summaryrefslogtreecommitdiff
path: root/src/hid_core/hidbus
diff options
context:
space:
mode:
Diffstat (limited to 'src/hid_core/hidbus')
-rw-r--r--src/hid_core/hidbus/hidbus_base.cpp73
-rw-r--r--src/hid_core/hidbus/hidbus_base.h183
-rw-r--r--src/hid_core/hidbus/ringcon.cpp292
-rw-r--r--src/hid_core/hidbus/ringcon.h253
-rw-r--r--src/hid_core/hidbus/starlink.cpp50
-rw-r--r--src/hid_core/hidbus/starlink.h37
-rw-r--r--src/hid_core/hidbus/stubbed.cpp50
-rw-r--r--src/hid_core/hidbus/stubbed.h37
8 files changed, 975 insertions, 0 deletions
diff --git a/src/hid_core/hidbus/hidbus_base.cpp b/src/hid_core/hidbus/hidbus_base.cpp
new file mode 100644
index 000000000..632bb173b
--- /dev/null
+++ b/src/hid_core/hidbus/hidbus_base.cpp
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_event.h"
5#include "core/hle/kernel/k_readable_event.h"
6#include "core/hle/service/kernel_helpers.h"
7#include "hid_core/hid_core.h"
8#include "hid_core/hidbus/hidbus_base.h"
9
10namespace Service::HID {
11
12HidbusBase::HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
13 : system(system_), service_context(service_context_) {
14 send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
15}
16
17HidbusBase::~HidbusBase() {
18 service_context.CloseEvent(send_command_async_event);
19};
20
21void HidbusBase::ActivateDevice() {
22 if (is_activated) {
23 return;
24 }
25 is_activated = true;
26 OnInit();
27}
28
29void HidbusBase::DeactivateDevice() {
30 if (is_activated) {
31 OnRelease();
32 }
33 is_activated = false;
34}
35
36bool HidbusBase::IsDeviceActivated() const {
37 return is_activated;
38}
39
40void HidbusBase::Enable(bool enable) {
41 device_enabled = enable;
42}
43
44bool HidbusBase::IsEnabled() const {
45 return device_enabled;
46}
47
48bool HidbusBase::IsPollingMode() const {
49 return polling_mode_enabled;
50}
51
52JoyPollingMode HidbusBase::GetPollingMode() const {
53 return polling_mode;
54}
55
56void HidbusBase::SetPollingMode(JoyPollingMode mode) {
57 polling_mode = mode;
58 polling_mode_enabled = true;
59}
60
61void HidbusBase::DisablePollingMode() {
62 polling_mode_enabled = false;
63}
64
65void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
66 transfer_memory = t_mem;
67}
68
69Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
70 return send_command_async_event->GetReadableEvent();
71}
72
73} // namespace Service::HID
diff --git a/src/hid_core/hidbus/hidbus_base.h b/src/hid_core/hidbus/hidbus_base.h
new file mode 100644
index 000000000..ec41684e1
--- /dev/null
+++ b/src/hid_core/hidbus/hidbus_base.h
@@ -0,0 +1,183 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8#include "common/typed_address.h"
9#include "core/hle/result.h"
10
11namespace Core {
12class System;
13}
14
15namespace Kernel {
16class KEvent;
17class KReadableEvent;
18} // namespace Kernel
19
20namespace Service::KernelHelpers {
21class ServiceContext;
22}
23
24namespace Service::HID {
25
26// This is nn::hidbus::JoyPollingMode
27enum class JoyPollingMode : u32 {
28 SixAxisSensorDisable,
29 SixAxisSensorEnable,
30 ButtonOnly,
31};
32
33struct DataAccessorHeader {
34 Result result{ResultUnknown};
35 INSERT_PADDING_WORDS(0x1);
36 std::array<u8, 0x18> unused{};
37 u64 latest_entry{};
38 u64 total_entries{};
39};
40static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
41
42struct JoyDisableSixAxisPollingData {
43 std::array<u8, 0x26> data;
44 u8 out_size;
45 INSERT_PADDING_BYTES(0x1);
46 u64 sampling_number;
47};
48static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
49 "JoyDisableSixAxisPollingData is an invalid size");
50
51struct JoyEnableSixAxisPollingData {
52 std::array<u8, 0x8> data;
53 u8 out_size;
54 INSERT_PADDING_BYTES(0x7);
55 u64 sampling_number;
56};
57static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
58 "JoyEnableSixAxisPollingData is an invalid size");
59
60struct JoyButtonOnlyPollingData {
61 std::array<u8, 0x2c> data;
62 u8 out_size;
63 INSERT_PADDING_BYTES(0x3);
64 u64 sampling_number;
65};
66static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
67 "JoyButtonOnlyPollingData is an invalid size");
68
69struct JoyDisableSixAxisPollingEntry {
70 u64 sampling_number;
71 JoyDisableSixAxisPollingData polling_data;
72};
73static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
74 "JoyDisableSixAxisPollingEntry is an invalid size");
75
76struct JoyEnableSixAxisPollingEntry {
77 u64 sampling_number;
78 JoyEnableSixAxisPollingData polling_data;
79};
80static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
81 "JoyEnableSixAxisPollingEntry is an invalid size");
82
83struct JoyButtonOnlyPollingEntry {
84 u64 sampling_number;
85 JoyButtonOnlyPollingData polling_data;
86};
87static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
88 "JoyButtonOnlyPollingEntry is an invalid size");
89
90struct JoyDisableSixAxisDataAccessor {
91 DataAccessorHeader header{};
92 std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
93};
94static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
95 "JoyDisableSixAxisDataAccessor is an invalid size");
96
97struct JoyEnableSixAxisDataAccessor {
98 DataAccessorHeader header{};
99 std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
100};
101static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
102 "JoyEnableSixAxisDataAccessor is an invalid size");
103
104struct ButtonOnlyPollingDataAccessor {
105 DataAccessorHeader header;
106 std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
107};
108static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
109 "ButtonOnlyPollingDataAccessor is an invalid size");
110
111class HidbusBase {
112public:
113 explicit HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
114 virtual ~HidbusBase();
115
116 void ActivateDevice();
117
118 void DeactivateDevice();
119
120 bool IsDeviceActivated() const;
121
122 // Enables/disables the device
123 void Enable(bool enable);
124
125 // returns true if device is enabled
126 bool IsEnabled() const;
127
128 // returns true if polling mode is enabled
129 bool IsPollingMode() const;
130
131 // returns polling mode
132 JoyPollingMode GetPollingMode() const;
133
134 // Sets and enables JoyPollingMode
135 void SetPollingMode(JoyPollingMode mode);
136
137 // Disables JoyPollingMode
138 void DisablePollingMode();
139
140 // Called on EnableJoyPollingReceiveMode
141 void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
142
143 Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
144
145 virtual void OnInit() {}
146
147 virtual void OnRelease() {}
148
149 // Updates device transfer memory
150 virtual void OnUpdate() {}
151
152 // Returns the device ID of the joycon
153 virtual u8 GetDeviceId() const {
154 return {};
155 }
156
157 // Assigns a command from data
158 virtual bool SetCommand(std::span<const u8> data) {
159 return {};
160 }
161
162 // Returns a reply from a command
163 virtual std::vector<u8> GetReply() const {
164 return {};
165 }
166
167protected:
168 bool is_activated{};
169 bool device_enabled{};
170 bool polling_mode_enabled{};
171 JoyPollingMode polling_mode = {};
172 // TODO(German77): All data accessors need to be replaced with a ring lifo object
173 JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
174 JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
175 ButtonOnlyPollingDataAccessor button_only_data{};
176
177 Common::ProcessAddress transfer_memory{};
178
179 Core::System& system;
180 Kernel::KEvent* send_command_async_event;
181 KernelHelpers::ServiceContext& service_context;
182};
183} // namespace Service::HID
diff --git a/src/hid_core/hidbus/ringcon.cpp b/src/hid_core/hidbus/ringcon.cpp
new file mode 100644
index 000000000..cedf25c16
--- /dev/null
+++ b/src/hid_core/hidbus/ringcon.cpp
@@ -0,0 +1,292 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_event.h"
6#include "core/hle/kernel/k_readable_event.h"
7#include "core/memory.h"
8#include "hid_core/frontend/emulated_controller.h"
9#include "hid_core/hid_core.h"
10#include "hid_core/hidbus/ringcon.h"
11
12namespace Service::HID {
13
14RingController::RingController(Core::System& system_,
15 KernelHelpers::ServiceContext& service_context_)
16 : HidbusBase(system_, service_context_) {
17 input = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
18}
19
20RingController::~RingController() = default;
21
22void RingController::OnInit() {
23 input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
24 Common::Input::PollingMode::Ring);
25 return;
26}
27
28void RingController::OnRelease() {
29 input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
30 Common::Input::PollingMode::Active);
31 return;
32};
33
34void RingController::OnUpdate() {
35 if (!is_activated) {
36 return;
37 }
38
39 if (!device_enabled) {
40 return;
41 }
42
43 if (!polling_mode_enabled || transfer_memory == 0) {
44 return;
45 }
46
47 // TODO: Increment multitasking counters from motion and sensor data
48
49 switch (polling_mode) {
50 case JoyPollingMode::SixAxisSensorEnable: {
51 enable_sixaxis_data.header.total_entries = 10;
52 enable_sixaxis_data.header.result = ResultSuccess;
53 const auto& last_entry =
54 enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
55
56 enable_sixaxis_data.header.latest_entry =
57 (enable_sixaxis_data.header.latest_entry + 1) % 10;
58 auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
59
60 curr_entry.sampling_number = last_entry.sampling_number + 1;
61 curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
62
63 const RingConData ringcon_value = GetSensorValue();
64 curr_entry.polling_data.out_size = sizeof(ringcon_value);
65 std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
66
67 system.ApplicationMemory().WriteBlock(transfer_memory, &enable_sixaxis_data,
68 sizeof(enable_sixaxis_data));
69 break;
70 }
71 default:
72 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
73 break;
74 }
75}
76
77RingController::RingConData RingController::GetSensorValue() const {
78 RingConData ringcon_sensor_value{
79 .status = DataValid::Valid,
80 .data = 0,
81 };
82
83 const f32 force_value = input->GetRingSensorForce().force * range;
84 ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
85
86 return ringcon_sensor_value;
87}
88
89u8 RingController::GetDeviceId() const {
90 return device_id;
91}
92
93std::vector<u8> RingController::GetReply() const {
94 const RingConCommands current_command = command;
95
96 switch (current_command) {
97 case RingConCommands::GetFirmwareVersion:
98 return GetFirmwareVersionReply();
99 case RingConCommands::ReadId:
100 return GetReadIdReply();
101 case RingConCommands::c20105:
102 return GetC020105Reply();
103 case RingConCommands::ReadUnkCal:
104 return GetReadUnkCalReply();
105 case RingConCommands::ReadFactoryCal:
106 return GetReadFactoryCalReply();
107 case RingConCommands::ReadUserCal:
108 return GetReadUserCalReply();
109 case RingConCommands::ReadRepCount:
110 return GetReadRepCountReply();
111 case RingConCommands::ReadTotalPushCount:
112 return GetReadTotalPushCountReply();
113 case RingConCommands::ResetRepCount:
114 return GetResetRepCountReply();
115 case RingConCommands::SaveCalData:
116 return GetSaveDataReply();
117 default:
118 return GetErrorReply();
119 }
120}
121
122bool RingController::SetCommand(std::span<const u8> data) {
123 if (data.size() < 4) {
124 LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
125 command = RingConCommands::Error;
126 return false;
127 }
128
129 std::memcpy(&command, data.data(), sizeof(RingConCommands));
130
131 switch (command) {
132 case RingConCommands::GetFirmwareVersion:
133 case RingConCommands::ReadId:
134 case RingConCommands::c20105:
135 case RingConCommands::ReadUnkCal:
136 case RingConCommands::ReadFactoryCal:
137 case RingConCommands::ReadUserCal:
138 case RingConCommands::ReadRepCount:
139 case RingConCommands::ReadTotalPushCount:
140 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
141 send_command_async_event->Signal();
142 return true;
143 case RingConCommands::ResetRepCount:
144 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
145 total_rep_count = 0;
146 send_command_async_event->Signal();
147 return true;
148 case RingConCommands::SaveCalData: {
149 ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
150
151 SaveCalData save_info{};
152 std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
153 user_calibration = save_info.calibration;
154 send_command_async_event->Signal();
155 return true;
156 }
157 default:
158 LOG_ERROR(Service_HID, "Command not implemented {}", command);
159 command = RingConCommands::Error;
160 // Signal a reply to avoid softlocking the game
161 send_command_async_event->Signal();
162 return false;
163 }
164}
165
166std::vector<u8> RingController::GetFirmwareVersionReply() const {
167 const FirmwareVersionReply reply{
168 .status = DataValid::Valid,
169 .firmware = version,
170 };
171
172 return GetDataVector(reply);
173}
174
175std::vector<u8> RingController::GetReadIdReply() const {
176 // The values are hardcoded from a real joycon
177 const ReadIdReply reply{
178 .status = DataValid::Valid,
179 .id_l_x0 = 8,
180 .id_l_x0_2 = 41,
181 .id_l_x4 = 22294,
182 .id_h_x0 = 19777,
183 .id_h_x0_2 = 13621,
184 .id_h_x4 = 8245,
185 };
186
187 return GetDataVector(reply);
188}
189
190std::vector<u8> RingController::GetC020105Reply() const {
191 const Cmd020105Reply reply{
192 .status = DataValid::Valid,
193 .data = 1,
194 };
195
196 return GetDataVector(reply);
197}
198
199std::vector<u8> RingController::GetReadUnkCalReply() const {
200 const ReadUnkCalReply reply{
201 .status = DataValid::Valid,
202 .data = 0,
203 };
204
205 return GetDataVector(reply);
206}
207
208std::vector<u8> RingController::GetReadFactoryCalReply() const {
209 const ReadFactoryCalReply reply{
210 .status = DataValid::Valid,
211 .calibration = factory_calibration,
212 };
213
214 return GetDataVector(reply);
215}
216
217std::vector<u8> RingController::GetReadUserCalReply() const {
218 const ReadUserCalReply reply{
219 .status = DataValid::Valid,
220 .calibration = user_calibration,
221 };
222
223 return GetDataVector(reply);
224}
225
226std::vector<u8> RingController::GetReadRepCountReply() const {
227 const GetThreeByteReply reply{
228 .status = DataValid::Valid,
229 .data = {total_rep_count, 0, 0},
230 .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
231 };
232
233 return GetDataVector(reply);
234}
235
236std::vector<u8> RingController::GetReadTotalPushCountReply() const {
237 const GetThreeByteReply reply{
238 .status = DataValid::Valid,
239 .data = {total_push_count, 0, 0},
240 .crc = GetCrcValue({total_push_count, 0, 0, 0}),
241 };
242
243 return GetDataVector(reply);
244}
245
246std::vector<u8> RingController::GetResetRepCountReply() const {
247 return GetReadRepCountReply();
248}
249
250std::vector<u8> RingController::GetSaveDataReply() const {
251 const StatusReply reply{
252 .status = DataValid::Valid,
253 };
254
255 return GetDataVector(reply);
256}
257
258std::vector<u8> RingController::GetErrorReply() const {
259 const ErrorReply reply{
260 .status = DataValid::BadCRC,
261 };
262
263 return GetDataVector(reply);
264}
265
266u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
267 u8 crc = 0;
268 for (std::size_t index = 0; index < data.size(); index++) {
269 for (u8 i = 0x80; i > 0; i >>= 1) {
270 bool bit = (crc & 0x80) != 0;
271 if ((data[index] & i) != 0) {
272 bit = !bit;
273 }
274 crc <<= 1;
275 if (bit) {
276 crc ^= 0x8d;
277 }
278 }
279 }
280 return crc;
281}
282
283template <typename T>
284std::vector<u8> RingController::GetDataVector(const T& reply) const {
285 static_assert(std::is_trivially_copyable_v<T>);
286 std::vector<u8> data;
287 data.resize(sizeof(reply));
288 std::memcpy(data.data(), &reply, sizeof(reply));
289 return data;
290}
291
292} // namespace Service::HID
diff --git a/src/hid_core/hidbus/ringcon.h b/src/hid_core/hidbus/ringcon.h
new file mode 100644
index 000000000..0953e8100
--- /dev/null
+++ b/src/hid_core/hidbus/ringcon.h
@@ -0,0 +1,253 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8
9#include "common/common_types.h"
10#include "hid_core/hidbus/hidbus_base.h"
11
12namespace Core::HID {
13class EmulatedController;
14} // namespace Core::HID
15
16namespace Service::HID {
17
18class RingController final : public HidbusBase {
19public:
20 explicit RingController(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
21 ~RingController() override;
22
23 void OnInit() override;
24
25 void OnRelease() override;
26
27 // Updates ringcon transfer memory
28 void OnUpdate() override;
29
30 // Returns the device ID of the joycon
31 u8 GetDeviceId() const override;
32
33 // Assigns a command from data
34 bool SetCommand(std::span<const u8> data) override;
35
36 // Returns a reply from a command
37 std::vector<u8> GetReply() const override;
38
39private:
40 // These values are obtained from a real ring controller
41 static constexpr s16 idle_value = 2280;
42 static constexpr s16 idle_deadzone = 120;
43 static constexpr s16 range = 2500;
44
45 // Most missing command names are leftovers from other firmware versions
46 enum class RingConCommands : u32 {
47 GetFirmwareVersion = 0x00020000,
48 ReadId = 0x00020100,
49 JoyPolling = 0x00020101,
50 Unknown1 = 0x00020104,
51 c20105 = 0x00020105,
52 Unknown2 = 0x00020204,
53 Unknown3 = 0x00020304,
54 Unknown4 = 0x00020404,
55 ReadUnkCal = 0x00020504,
56 ReadFactoryCal = 0x00020A04,
57 Unknown5 = 0x00021104,
58 Unknown6 = 0x00021204,
59 Unknown7 = 0x00021304,
60 ReadUserCal = 0x00021A04,
61 ReadRepCount = 0x00023104,
62 ReadTotalPushCount = 0x00023204,
63 ResetRepCount = 0x04013104,
64 Unknown8 = 0x04011104,
65 Unknown9 = 0x04011204,
66 Unknown10 = 0x04011304,
67 SaveCalData = 0x10011A04,
68 Error = 0xFFFFFFFF,
69 };
70
71 enum class DataValid : u32 {
72 Valid,
73 BadCRC,
74 Cal,
75 };
76
77 struct FirmwareVersion {
78 u8 sub;
79 u8 main;
80 };
81 static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
82
83 struct FactoryCalibration {
84 s32_le os_max;
85 s32_le hk_max;
86 s32_le zero_min;
87 s32_le zero_max;
88 };
89 static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
90
91 struct CalibrationValue {
92 s16 value;
93 u16 crc;
94 };
95 static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
96
97 struct UserCalibration {
98 CalibrationValue os_max;
99 CalibrationValue hk_max;
100 CalibrationValue zero;
101 };
102 static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
103
104 struct SaveCalData {
105 RingConCommands command;
106 UserCalibration calibration;
107 INSERT_PADDING_BYTES_NOINIT(4);
108 };
109 static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
110 static_assert(std::is_trivially_copyable_v<SaveCalData>,
111 "SaveCalData must be trivially copyable");
112
113 struct FirmwareVersionReply {
114 DataValid status;
115 FirmwareVersion firmware;
116 INSERT_PADDING_BYTES(0x2);
117 };
118 static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
119
120 struct Cmd020105Reply {
121 DataValid status;
122 u8 data;
123 INSERT_PADDING_BYTES(0x3);
124 };
125 static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
126
127 struct StatusReply {
128 DataValid status;
129 };
130 static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
131
132 struct GetThreeByteReply {
133 DataValid status;
134 std::array<u8, 3> data;
135 u8 crc;
136 };
137 static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
138
139 struct ReadUnkCalReply {
140 DataValid status;
141 u16 data;
142 INSERT_PADDING_BYTES(0x2);
143 };
144 static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
145
146 struct ReadFactoryCalReply {
147 DataValid status;
148 FactoryCalibration calibration;
149 };
150 static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
151
152 struct ReadUserCalReply {
153 DataValid status;
154 UserCalibration calibration;
155 INSERT_PADDING_BYTES(0x4);
156 };
157 static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
158
159 struct ReadIdReply {
160 DataValid status;
161 u16 id_l_x0;
162 u16 id_l_x0_2;
163 u16 id_l_x4;
164 u16 id_h_x0;
165 u16 id_h_x0_2;
166 u16 id_h_x4;
167 };
168 static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
169
170 struct ErrorReply {
171 DataValid status;
172 INSERT_PADDING_BYTES(0x3);
173 };
174 static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
175
176 struct RingConData {
177 DataValid status;
178 s16_le data;
179 INSERT_PADDING_BYTES(0x2);
180 };
181 static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
182
183 // Returns RingConData struct with pressure sensor values
184 RingConData GetSensorValue() const;
185
186 // Returns 8 byte reply with firmware version
187 std::vector<u8> GetFirmwareVersionReply() const;
188
189 // Returns 16 byte reply with ID values
190 std::vector<u8> GetReadIdReply() const;
191
192 // (STUBBED) Returns 8 byte reply
193 std::vector<u8> GetC020105Reply() const;
194
195 // (STUBBED) Returns 8 byte empty reply
196 std::vector<u8> GetReadUnkCalReply() const;
197
198 // Returns 20 byte reply with factory calibration values
199 std::vector<u8> GetReadFactoryCalReply() const;
200
201 // Returns 20 byte reply with user calibration values
202 std::vector<u8> GetReadUserCalReply() const;
203
204 // Returns 8 byte reply
205 std::vector<u8> GetReadRepCountReply() const;
206
207 // Returns 8 byte reply
208 std::vector<u8> GetReadTotalPushCountReply() const;
209
210 // Returns 8 byte reply
211 std::vector<u8> GetResetRepCountReply() const;
212
213 // Returns 4 byte save data reply
214 std::vector<u8> GetSaveDataReply() const;
215
216 // Returns 8 byte error reply
217 std::vector<u8> GetErrorReply() const;
218
219 // Returns 8 bit redundancy check from provided data
220 u8 GetCrcValue(const std::vector<u8>& data) const;
221
222 // Converts structs to an u8 vector equivalent
223 template <typename T>
224 std::vector<u8> GetDataVector(const T& reply) const;
225
226 RingConCommands command{RingConCommands::Error};
227
228 // These counters are used in multitasking mode while the switch is sleeping
229 // Total steps taken
230 u8 total_rep_count = 0;
231 // Total times the ring was pushed
232 u8 total_push_count = 0;
233
234 const u8 device_id = 0x20;
235 const FirmwareVersion version = {
236 .sub = 0x0,
237 .main = 0x2c,
238 };
239 const FactoryCalibration factory_calibration = {
240 .os_max = idle_value + range + idle_deadzone,
241 .hk_max = idle_value - range - idle_deadzone,
242 .zero_min = idle_value - idle_deadzone,
243 .zero_max = idle_value + idle_deadzone,
244 };
245 UserCalibration user_calibration = {
246 .os_max = {.value = range, .crc = 228},
247 .hk_max = {.value = -range, .crc = 239},
248 .zero = {.value = idle_value, .crc = 225},
249 };
250
251 Core::HID::EmulatedController* input;
252};
253} // namespace Service::HID
diff --git a/src/hid_core/hidbus/starlink.cpp b/src/hid_core/hidbus/starlink.cpp
new file mode 100644
index 000000000..31b263aa1
--- /dev/null
+++ b/src/hid_core/hidbus/starlink.cpp
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "hid_core/frontend/emulated_controller.h"
5#include "hid_core/hid_core.h"
6#include "hid_core/hidbus/starlink.h"
7
8namespace Service::HID {
9constexpr u8 DEVICE_ID = 0x28;
10
11Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
12 : HidbusBase(system_, service_context_) {}
13Starlink::~Starlink() = default;
14
15void Starlink::OnInit() {
16 return;
17}
18
19void Starlink::OnRelease() {
20 return;
21};
22
23void Starlink::OnUpdate() {
24 if (!is_activated) {
25 return;
26 }
27 if (!device_enabled) {
28 return;
29 }
30 if (!polling_mode_enabled || transfer_memory == 0) {
31 return;
32 }
33
34 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
35}
36
37u8 Starlink::GetDeviceId() const {
38 return DEVICE_ID;
39}
40
41std::vector<u8> Starlink::GetReply() const {
42 return {};
43}
44
45bool Starlink::SetCommand(std::span<const u8> data) {
46 LOG_ERROR(Service_HID, "Command not implemented");
47 return false;
48}
49
50} // namespace Service::HID
diff --git a/src/hid_core/hidbus/starlink.h b/src/hid_core/hidbus/starlink.h
new file mode 100644
index 000000000..ee37763b4
--- /dev/null
+++ b/src/hid_core/hidbus/starlink.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "hid_core/hidbus/hidbus_base.h"
8
9namespace Core::HID {
10class EmulatedController;
11} // namespace Core::HID
12
13namespace Service::HID {
14
15class Starlink final : public HidbusBase {
16public:
17 explicit Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
18 ~Starlink() override;
19
20 void OnInit() override;
21
22 void OnRelease() override;
23
24 // Updates ringcon transfer memory
25 void OnUpdate() override;
26
27 // Returns the device ID of the joycon
28 u8 GetDeviceId() const override;
29
30 // Assigns a command from data
31 bool SetCommand(std::span<const u8> data) override;
32
33 // Returns a reply from a command
34 std::vector<u8> GetReply() const override;
35};
36
37} // namespace Service::HID
diff --git a/src/hid_core/hidbus/stubbed.cpp b/src/hid_core/hidbus/stubbed.cpp
new file mode 100644
index 000000000..f16051aa9
--- /dev/null
+++ b/src/hid_core/hidbus/stubbed.cpp
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "hid_core/frontend/emulated_controller.h"
5#include "hid_core/hid_core.h"
6#include "hid_core/hidbus/stubbed.h"
7
8namespace Service::HID {
9constexpr u8 DEVICE_ID = 0xFF;
10
11HidbusStubbed::HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
12 : HidbusBase(system_, service_context_) {}
13HidbusStubbed::~HidbusStubbed() = default;
14
15void HidbusStubbed::OnInit() {
16 return;
17}
18
19void HidbusStubbed::OnRelease() {
20 return;
21};
22
23void HidbusStubbed::OnUpdate() {
24 if (!is_activated) {
25 return;
26 }
27 if (!device_enabled) {
28 return;
29 }
30 if (!polling_mode_enabled || transfer_memory == 0) {
31 return;
32 }
33
34 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
35}
36
37u8 HidbusStubbed::GetDeviceId() const {
38 return DEVICE_ID;
39}
40
41std::vector<u8> HidbusStubbed::GetReply() const {
42 return {};
43}
44
45bool HidbusStubbed::SetCommand(std::span<const u8> data) {
46 LOG_ERROR(Service_HID, "Command not implemented");
47 return false;
48}
49
50} // namespace Service::HID
diff --git a/src/hid_core/hidbus/stubbed.h b/src/hid_core/hidbus/stubbed.h
new file mode 100644
index 000000000..7a711cea0
--- /dev/null
+++ b/src/hid_core/hidbus/stubbed.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "hid_core/hidbus/hidbus_base.h"
8
9namespace Core::HID {
10class EmulatedController;
11} // namespace Core::HID
12
13namespace Service::HID {
14
15class HidbusStubbed final : public HidbusBase {
16public:
17 explicit HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
18 ~HidbusStubbed() override;
19
20 void OnInit() override;
21
22 void OnRelease() override;
23
24 // Updates ringcon transfer memory
25 void OnUpdate() override;
26
27 // Returns the device ID of the joycon
28 u8 GetDeviceId() const override;
29
30 // Assigns a command from data
31 bool SetCommand(std::span<const u8> data) override;
32
33 // Returns a reply from a command
34 std::vector<u8> GetReply() const override;
35};
36
37} // namespace Service::HID