summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/joycon_driver.cpp
diff options
context:
space:
mode:
authorGravatar Narr the Reg2022-12-20 11:34:33 -0600
committerGravatar Narr the Reg2023-01-19 18:05:20 -0600
commitd80e6c399bf8196646cca5ac1265d122638bb96b (patch)
tree328254642e4edcd5e0aadfe9190f3f133d34708e /src/input_common/helpers/joycon_driver.cpp
parentMerge pull request #9556 from vonchenplus/draw_texture (diff)
downloadyuzu-d80e6c399bf8196646cca5ac1265d122638bb96b.tar.gz
yuzu-d80e6c399bf8196646cca5ac1265d122638bb96b.tar.xz
yuzu-d80e6c399bf8196646cca5ac1265d122638bb96b.zip
input_common: Initial skeleton for custom joycon driver
Diffstat (limited to 'src/input_common/helpers/joycon_driver.cpp')
-rw-r--r--src/input_common/helpers/joycon_driver.cpp382
1 files changed, 382 insertions, 0 deletions
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
new file mode 100644
index 000000000..a0a2a180b
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -0,0 +1,382 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "common/swap.h"
6#include "common/thread.h"
7#include "input_common/helpers/joycon_driver.h"
8
9namespace InputCommon::Joycon {
10JoyconDriver::JoyconDriver(std::size_t port_) : port{port_} {
11 hidapi_handle = std::make_shared<JoyconHandle>();
12}
13
14JoyconDriver::~JoyconDriver() {
15 Stop();
16}
17
18void JoyconDriver::Stop() {
19 is_connected = false;
20 input_thread = {};
21}
22
23DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
24 std::scoped_lock lock{mutex};
25
26 handle_device_type = ControllerType::None;
27 GetDeviceType(device_info, handle_device_type);
28 if (handle_device_type == ControllerType::None) {
29 return DriverResult::UnsupportedControllerType;
30 }
31
32 hidapi_handle->handle =
33 SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
34 std::memcpy(&handle_serial_number, device_info->serial_number, 15);
35 if (!hidapi_handle->handle) {
36 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
37 device_info->vendor_id, device_info->product_id);
38 return DriverResult::HandleInUse;
39 }
40 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
41 return DriverResult::Success;
42}
43
44DriverResult JoyconDriver::InitializeDevice() {
45 if (!hidapi_handle->handle) {
46 return DriverResult::InvalidHandle;
47 }
48 std::scoped_lock lock{mutex};
49 disable_input_thread = true;
50
51 // Reset Counters
52 error_counter = 0;
53 hidapi_handle->packet_counter = 0;
54
55 // Set HW default configuration
56 vibration_enabled = true;
57 motion_enabled = true;
58 hidbus_enabled = false;
59 nfc_enabled = false;
60 passive_enabled = false;
61 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
62 gyro_performance = Joycon::GyroPerformance::HZ833;
63 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
64 accelerometer_performance = Joycon::AccelerometerPerformance::HZ100;
65
66 // Initialize HW Protocols
67
68 // Get fixed joycon info
69 supported_features = GetSupportedFeatures();
70
71 // Get Calibration data
72
73 // Set led status
74
75 // Apply HW configuration
76 SetPollingMode();
77
78 // Start pooling for data
79 is_connected = true;
80 if (!input_thread_running) {
81 input_thread =
82 std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
83 }
84
85 disable_input_thread = false;
86 return DriverResult::Success;
87}
88
89void JoyconDriver::InputThread(std::stop_token stop_token) {
90 LOG_INFO(Input, "JC Adapter input thread started");
91 Common::SetCurrentThreadName("JoyconInput");
92 input_thread_running = true;
93
94 // Max update rate is 5ms, ensure we are always able to read a bit faster
95 constexpr int ThreadDelay = 2;
96 std::vector<u8> buffer(MaxBufferSize);
97
98 while (!stop_token.stop_requested()) {
99 int status = 0;
100
101 if (!IsInputThreadValid()) {
102 input_thread.request_stop();
103 continue;
104 }
105
106 // By disabling the input thread we can ensure custom commands will succeed as no package is
107 // skipped
108 if (!disable_input_thread) {
109 status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
110 ThreadDelay);
111 } else {
112 std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay));
113 }
114
115 if (IsPayloadCorrect(status, buffer)) {
116 OnNewData(buffer);
117 }
118
119 std::this_thread::yield();
120 }
121
122 is_connected = false;
123 input_thread_running = false;
124 LOG_INFO(Input, "JC Adapter input thread stopped");
125}
126
127void JoyconDriver::OnNewData(std::span<u8> buffer) {
128 const auto report_mode = static_cast<InputReport>(buffer[0]);
129
130 switch (report_mode) {
131 case InputReport::STANDARD_FULL_60HZ:
132 ReadActiveMode(buffer);
133 break;
134 case InputReport::NFC_IR_MODE_60HZ:
135 ReadNfcIRMode(buffer);
136 break;
137 case InputReport::SIMPLE_HID_MODE:
138 ReadPassiveMode(buffer);
139 break;
140 default:
141 LOG_ERROR(Input, "Report mode not Implemented {}", report_mode);
142 break;
143 }
144}
145
146void JoyconDriver::SetPollingMode() {
147 disable_input_thread = true;
148 disable_input_thread = false;
149}
150
151JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
152 SupportedFeatures features{
153 .passive = true,
154 .motion = true,
155 .vibration = true,
156 };
157
158 if (device_type == ControllerType::Right) {
159 features.nfc = true;
160 features.irs = true;
161 features.hidbus = true;
162 }
163
164 if (device_type == ControllerType::Pro) {
165 features.nfc = true;
166 }
167 return features;
168}
169
170void JoyconDriver::ReadActiveMode(std::span<u8> buffer) {
171 InputReportActive data{};
172 memcpy(&data, buffer.data(), sizeof(InputReportActive));
173
174 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion
175 // experience
176 const auto now = std::chrono::steady_clock::now();
177 const auto new_delta_time =
178 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count();
179 delta_time = static_cast<u64>((delta_time * 0.8f) + (new_delta_time * 0.2));
180 last_update = now;
181
182 switch (device_type) {
183 case Joycon::ControllerType::Left:
184 break;
185 case Joycon::ControllerType::Right:
186 break;
187 case Joycon::ControllerType::Pro:
188 break;
189 case Joycon::ControllerType::Grip:
190 case Joycon::ControllerType::Dual:
191 case Joycon::ControllerType::None:
192 break;
193 }
194
195 on_battery_data(data.battery_status);
196 on_color_data(color);
197}
198
199void JoyconDriver::ReadPassiveMode(std::span<u8> buffer) {
200 InputReportPassive data{};
201 memcpy(&data, buffer.data(), sizeof(InputReportPassive));
202
203 switch (device_type) {
204 case Joycon::ControllerType::Left:
205 break;
206 case Joycon::ControllerType::Right:
207 break;
208 case Joycon::ControllerType::Pro:
209 break;
210 case Joycon::ControllerType::Grip:
211 case Joycon::ControllerType::Dual:
212 case Joycon::ControllerType::None:
213 break;
214 }
215}
216
217void JoyconDriver::ReadNfcIRMode(std::span<u8> buffer) {
218 // This mode is compatible with the active mode
219 ReadActiveMode(buffer);
220
221 if (!nfc_enabled) {
222 return;
223 }
224}
225
226bool JoyconDriver::IsInputThreadValid() const {
227 if (!is_connected) {
228 return false;
229 }
230 if (hidapi_handle->handle == nullptr) {
231 return false;
232 }
233 // Controller is not responding. Terminate connection
234 if (error_counter > MaxErrorCount) {
235 return false;
236 }
237 return true;
238}
239
240bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
241 if (status <= -1) {
242 error_counter++;
243 return false;
244 }
245 // There's no new data
246 if (status == 0) {
247 return false;
248 }
249 // No reply ever starts with zero
250 if (buffer[0] == 0x00) {
251 error_counter++;
252 return false;
253 }
254 error_counter = 0;
255 return true;
256}
257
258DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
259 std::scoped_lock lock{mutex};
260 return DriverResult::NotSupported;
261}
262
263DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
264 std::scoped_lock lock{mutex};
265 return DriverResult::NotSupported;
266}
267
268DriverResult JoyconDriver::SetPasiveMode() {
269 motion_enabled = false;
270 hidbus_enabled = false;
271 nfc_enabled = false;
272 passive_enabled = true;
273 SetPollingMode();
274 return DriverResult::Success;
275}
276
277DriverResult JoyconDriver::SetActiveMode() {
278 motion_enabled = false;
279 hidbus_enabled = false;
280 nfc_enabled = false;
281 passive_enabled = false;
282 SetPollingMode();
283 return DriverResult::Success;
284}
285
286DriverResult JoyconDriver::SetNfcMode() {
287 motion_enabled = false;
288 hidbus_enabled = false;
289 nfc_enabled = true;
290 passive_enabled = false;
291 SetPollingMode();
292 return DriverResult::Success;
293}
294
295DriverResult JoyconDriver::SetRingConMode() {
296 motion_enabled = true;
297 hidbus_enabled = true;
298 nfc_enabled = false;
299 passive_enabled = false;
300 SetPollingMode();
301 return DriverResult::Success;
302}
303
304bool JoyconDriver::IsConnected() const {
305 std::scoped_lock lock{mutex};
306 return is_connected;
307}
308
309bool JoyconDriver::IsVibrationEnabled() const {
310 std::scoped_lock lock{mutex};
311 return vibration_enabled;
312}
313
314FirmwareVersion JoyconDriver::GetDeviceVersion() const {
315 std::scoped_lock lock{mutex};
316 return version;
317}
318
319Color JoyconDriver::GetDeviceColor() const {
320 std::scoped_lock lock{mutex};
321 return color;
322}
323
324std::size_t JoyconDriver::GetDevicePort() const {
325 std::scoped_lock lock{mutex};
326 return port;
327}
328
329ControllerType JoyconDriver::GetDeviceType() const {
330 std::scoped_lock lock{mutex};
331 return handle_device_type;
332}
333
334ControllerType JoyconDriver::GetHandleDeviceType() const {
335 std::scoped_lock lock{mutex};
336 return handle_device_type;
337}
338
339SerialNumber JoyconDriver::GetSerialNumber() const {
340 std::scoped_lock lock{mutex};
341 return serial_number;
342}
343
344SerialNumber JoyconDriver::GetHandleSerialNumber() const {
345 std::scoped_lock lock{mutex};
346 return handle_serial_number;
347}
348
349Joycon::DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
350 ControllerType& controller_type) {
351 std::array<std::pair<u32, Joycon::ControllerType>, 4> supported_devices{
352 std::pair<u32, Joycon::ControllerType>{0x2006, Joycon::ControllerType::Left},
353 {0x2007, Joycon::ControllerType::Right},
354 {0x2009, Joycon::ControllerType::Pro},
355 {0x200E, Joycon::ControllerType::Grip},
356 };
357 constexpr u16 nintendo_vendor_id = 0x057e;
358
359 controller_type = Joycon::ControllerType::None;
360 if (device_info->vendor_id != nintendo_vendor_id) {
361 return Joycon::DriverResult::UnsupportedControllerType;
362 }
363
364 for (const auto& [product_id, type] : supported_devices) {
365 if (device_info->product_id == static_cast<u16>(product_id)) {
366 controller_type = type;
367 return Joycon::DriverResult::Success;
368 }
369 }
370 return Joycon::DriverResult::UnsupportedControllerType;
371}
372
373Joycon::DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info,
374 Joycon::SerialNumber& serial_number) {
375 if (device_info->serial_number == nullptr) {
376 return Joycon::DriverResult::Unknown;
377 }
378 std::memcpy(&serial_number, device_info->serial_number, 15);
379 return Joycon::DriverResult::Success;
380}
381
382} // namespace InputCommon::Joycon