summaryrefslogtreecommitdiff
path: root/src/hid_core/hidbus/ringcon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/hid_core/hidbus/ringcon.cpp')
-rw-r--r--src/hid_core/hidbus/ringcon.cpp292
1 files changed, 292 insertions, 0 deletions
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