summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/joycon_driver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/helpers/joycon_driver.cpp')
-rw-r--r--src/input_common/helpers/joycon_driver.cpp710
1 files changed, 710 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..ec984a647
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -0,0 +1,710 @@
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/scope_exit.h"
6#include "common/swap.h"
7#include "common/thread.h"
8#include "input_common/helpers/joycon_driver.h"
9#include "input_common/helpers/joycon_protocol/calibration.h"
10#include "input_common/helpers/joycon_protocol/generic_functions.h"
11#include "input_common/helpers/joycon_protocol/irs.h"
12#include "input_common/helpers/joycon_protocol/nfc.h"
13#include "input_common/helpers/joycon_protocol/poller.h"
14#include "input_common/helpers/joycon_protocol/ringcon.h"
15#include "input_common/helpers/joycon_protocol/rumble.h"
16
17namespace InputCommon::Joycon {
18JoyconDriver::JoyconDriver(std::size_t port_) : port{port_} {
19 hidapi_handle = std::make_shared<JoyconHandle>();
20}
21
22JoyconDriver::~JoyconDriver() {
23 Stop();
24}
25
26void JoyconDriver::Stop() {
27 is_connected = false;
28 input_thread = {};
29}
30
31DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
32 std::scoped_lock lock{mutex};
33
34 handle_device_type = ControllerType::None;
35 GetDeviceType(device_info, handle_device_type);
36 if (handle_device_type == ControllerType::None) {
37 return DriverResult::UnsupportedControllerType;
38 }
39
40 hidapi_handle->handle =
41 SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
42 std::memcpy(&handle_serial_number, device_info->serial_number, 15);
43 if (!hidapi_handle->handle) {
44 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
45 device_info->vendor_id, device_info->product_id);
46 return DriverResult::HandleInUse;
47 }
48 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
49 return DriverResult::Success;
50}
51
52DriverResult JoyconDriver::InitializeDevice() {
53 if (!hidapi_handle->handle) {
54 return DriverResult::InvalidHandle;
55 }
56 std::scoped_lock lock{mutex};
57 disable_input_thread = true;
58
59 // Reset Counters
60 error_counter = 0;
61 hidapi_handle->packet_counter = 0;
62
63 // Reset external device status
64 starlink_connected = false;
65 ring_connected = false;
66 amiibo_detected = false;
67
68 // Set HW default configuration
69 vibration_enabled = true;
70 motion_enabled = true;
71 hidbus_enabled = false;
72 nfc_enabled = false;
73 passive_enabled = false;
74 irs_enabled = false;
75 input_only_device = false;
76 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
77 gyro_performance = Joycon::GyroPerformance::HZ833;
78 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
79 accelerometer_performance = Joycon::AccelerometerPerformance::HZ100;
80
81 // Initialize HW Protocols
82 calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle);
83 generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle);
84 irs_protocol = std::make_unique<IrsProtocol>(hidapi_handle);
85 nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle);
86 ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle);
87 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
88
89 // Get fixed joycon info
90 if (generic_protocol->GetVersionNumber(version) != DriverResult::Success) {
91 // If this command fails the device doesn't accept configuration commands
92 input_only_device = true;
93 }
94
95 if (!input_only_device) {
96 generic_protocol->SetLowPowerMode(false);
97 generic_protocol->GetColor(color);
98 if (handle_device_type == ControllerType::Pro) {
99 // Some 3rd party controllers aren't pro controllers
100 generic_protocol->GetControllerType(device_type);
101 } else {
102 device_type = handle_device_type;
103 }
104 generic_protocol->GetSerialNumber(serial_number);
105 }
106
107 supported_features = GetSupportedFeatures();
108
109 // Get Calibration data
110 calibration_protocol->GetLeftJoyStickCalibration(left_stick_calibration);
111 calibration_protocol->GetRightJoyStickCalibration(right_stick_calibration);
112 calibration_protocol->GetImuCalibration(motion_calibration);
113
114 // Set led status
115 generic_protocol->SetLedBlinkPattern(static_cast<u8>(1 + port));
116
117 // Apply HW configuration
118 SetPollingMode();
119
120 // Initialize joycon poller
121 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
122 right_stick_calibration, motion_calibration);
123
124 // Start polling for data
125 is_connected = true;
126 if (!input_thread_running) {
127 input_thread =
128 std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
129 }
130
131 disable_input_thread = false;
132 return DriverResult::Success;
133}
134
135void JoyconDriver::InputThread(std::stop_token stop_token) {
136 LOG_INFO(Input, "Joycon Adapter input thread started");
137 Common::SetCurrentThreadName("JoyconInput");
138 input_thread_running = true;
139
140 // Max update rate is 5ms, ensure we are always able to read a bit faster
141 constexpr int ThreadDelay = 2;
142 std::vector<u8> buffer(MaxBufferSize);
143
144 while (!stop_token.stop_requested()) {
145 int status = 0;
146
147 if (!IsInputThreadValid()) {
148 input_thread.request_stop();
149 continue;
150 }
151
152 // By disabling the input thread we can ensure custom commands will succeed as no package is
153 // skipped
154 if (!disable_input_thread) {
155 status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
156 ThreadDelay);
157 } else {
158 std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay));
159 }
160
161 if (IsPayloadCorrect(status, buffer)) {
162 OnNewData(buffer);
163 }
164
165 std::this_thread::yield();
166 }
167
168 is_connected = false;
169 input_thread_running = false;
170 LOG_INFO(Input, "Joycon Adapter input thread stopped");
171}
172
173void JoyconDriver::OnNewData(std::span<u8> buffer) {
174 const auto report_mode = static_cast<ReportMode>(buffer[0]);
175
176 // Packages can be a little bit inconsistent. Average the delta time to provide a smoother
177 // motion experience
178 switch (report_mode) {
179 case ReportMode::STANDARD_FULL_60HZ:
180 case ReportMode::NFC_IR_MODE_60HZ:
181 case ReportMode::SIMPLE_HID_MODE: {
182 const auto now = std::chrono::steady_clock::now();
183 const auto new_delta_time = static_cast<u64>(
184 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
185 delta_time = ((delta_time * 8) + (new_delta_time * 2)) / 10;
186 last_update = now;
187 joycon_poller->UpdateColor(color);
188 break;
189 }
190 default:
191 break;
192 }
193
194 const MotionStatus motion_status{
195 .is_enabled = motion_enabled,
196 .delta_time = delta_time,
197 .gyro_sensitivity = gyro_sensitivity,
198 .accelerometer_sensitivity = accelerometer_sensitivity,
199 };
200
201 // TODO: Remove this when calibration is properly loaded and not calculated
202 if (ring_connected && report_mode == ReportMode::STANDARD_FULL_60HZ) {
203 InputReportActive data{};
204 memcpy(&data, buffer.data(), sizeof(InputReportActive));
205 calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input);
206 }
207
208 const RingStatus ring_status{
209 .is_enabled = ring_connected,
210 .default_value = ring_calibration.default_value,
211 .max_value = ring_calibration.max_value,
212 .min_value = ring_calibration.min_value,
213 };
214
215 if (irs_protocol->IsEnabled()) {
216 irs_protocol->RequestImage(buffer);
217 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
218 }
219
220 if (nfc_protocol->IsPolling()) {
221 if (amiibo_detected) {
222 if (!nfc_protocol->HasAmiibo()) {
223 joycon_poller->UpdateAmiibo({});
224 amiibo_detected = false;
225 return;
226 }
227 }
228
229 if (!amiibo_detected) {
230 Joycon::TagInfo tag_info;
231 const auto result = nfc_protocol->GetTagInfo(tag_info);
232 if (result == DriverResult::Success) {
233 joycon_poller->UpdateAmiibo(tag_info);
234 amiibo_detected = true;
235 }
236 }
237 }
238
239 switch (report_mode) {
240 case ReportMode::STANDARD_FULL_60HZ:
241 joycon_poller->ReadActiveMode(buffer, motion_status, ring_status);
242 break;
243 case ReportMode::NFC_IR_MODE_60HZ:
244 joycon_poller->ReadNfcIRMode(buffer, motion_status);
245 break;
246 case ReportMode::SIMPLE_HID_MODE:
247 joycon_poller->ReadPassiveMode(buffer);
248 break;
249 case ReportMode::SUBCMD_REPLY:
250 LOG_DEBUG(Input, "Unhandled command reply");
251 break;
252 default:
253 LOG_ERROR(Input, "Report mode not Implemented {}", report_mode);
254 break;
255 }
256}
257
258DriverResult JoyconDriver::SetPollingMode() {
259 SCOPE_EXIT({ disable_input_thread = false; });
260 disable_input_thread = true;
261
262 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
263
264 if (motion_enabled && supported_features.motion) {
265 generic_protocol->EnableImu(true);
266 generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance,
267 accelerometer_sensitivity, accelerometer_performance);
268 } else {
269 generic_protocol->EnableImu(false);
270 }
271
272 if (input_only_device) {
273 return DriverResult::NotSupported;
274 }
275
276 if (irs_protocol->IsEnabled()) {
277 irs_protocol->DisableIrs();
278 }
279
280 if (nfc_protocol->IsEnabled()) {
281 amiibo_detected = false;
282 nfc_protocol->DisableNfc();
283 }
284
285 if (ring_protocol->IsEnabled()) {
286 ring_connected = false;
287 ring_protocol->DisableRingCon();
288 }
289
290 if (irs_enabled && supported_features.irs) {
291 auto result = irs_protocol->EnableIrs();
292 if (result == DriverResult::Success) {
293 return result;
294 }
295 irs_protocol->DisableIrs();
296 LOG_ERROR(Input, "Error enabling IRS");
297 return result;
298 }
299
300 if (nfc_enabled && supported_features.nfc) {
301 auto result = nfc_protocol->EnableNfc();
302 if (result == DriverResult::Success) {
303 return result;
304 }
305 nfc_protocol->DisableNfc();
306 LOG_ERROR(Input, "Error enabling NFC");
307 return result;
308 }
309
310 if (hidbus_enabled && supported_features.hidbus) {
311 auto result = ring_protocol->EnableRingCon();
312 if (result == DriverResult::Success) {
313 result = ring_protocol->StartRingconPolling();
314 }
315 if (result == DriverResult::Success) {
316 ring_connected = true;
317 return result;
318 }
319 ring_connected = false;
320 ring_protocol->DisableRingCon();
321 LOG_ERROR(Input, "Error enabling Ringcon");
322 return result;
323 }
324
325 if (passive_enabled && supported_features.passive) {
326 const auto result = generic_protocol->EnablePassiveMode();
327 if (result == DriverResult::Success) {
328 return result;
329 }
330 LOG_ERROR(Input, "Error enabling passive mode");
331 }
332
333 // Default Mode
334 const auto result = generic_protocol->EnableActiveMode();
335 if (result != DriverResult::Success) {
336 LOG_ERROR(Input, "Error enabling active mode");
337 }
338 // Switch calls this function after enabling active mode
339 generic_protocol->TriggersElapsed();
340
341 return result;
342}
343
344JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
345 SupportedFeatures features{
346 .passive = true,
347 .motion = true,
348 .vibration = true,
349 };
350
351 if (input_only_device) {
352 return features;
353 }
354
355 if (device_type == ControllerType::Right) {
356 features.nfc = true;
357 features.irs = true;
358 features.hidbus = true;
359 }
360
361 if (device_type == ControllerType::Pro) {
362 features.nfc = true;
363 }
364 return features;
365}
366
367bool JoyconDriver::IsInputThreadValid() const {
368 if (!is_connected.load()) {
369 return false;
370 }
371 if (hidapi_handle->handle == nullptr) {
372 return false;
373 }
374 // Controller is not responding. Terminate connection
375 if (error_counter > MaxErrorCount) {
376 return false;
377 }
378 return true;
379}
380
381bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
382 if (status <= -1) {
383 error_counter++;
384 return false;
385 }
386 // There's no new data
387 if (status == 0) {
388 return false;
389 }
390 // No reply ever starts with zero
391 if (buffer[0] == 0x00) {
392 error_counter++;
393 return false;
394 }
395 error_counter = 0;
396 return true;
397}
398
399DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
400 std::scoped_lock lock{mutex};
401 if (disable_input_thread) {
402 return DriverResult::HandleInUse;
403 }
404 return rumble_protocol->SendVibration(vibration);
405}
406
407DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
408 std::scoped_lock lock{mutex};
409 if (disable_input_thread) {
410 return DriverResult::HandleInUse;
411 }
412 return generic_protocol->SetLedPattern(led_pattern);
413}
414
415DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
416 std::scoped_lock lock{mutex};
417 if (disable_input_thread) {
418 return DriverResult::HandleInUse;
419 }
420 disable_input_thread = true;
421 const auto result = irs_protocol->SetIrsConfig(mode_, format_);
422 disable_input_thread = false;
423 return result;
424}
425
426DriverResult JoyconDriver::SetPassiveMode() {
427 std::scoped_lock lock{mutex};
428 motion_enabled = false;
429 hidbus_enabled = false;
430 nfc_enabled = false;
431 passive_enabled = true;
432 irs_enabled = false;
433 return SetPollingMode();
434}
435
436DriverResult JoyconDriver::SetActiveMode() {
437 if (is_ring_disabled_by_irs) {
438 is_ring_disabled_by_irs = false;
439 SetActiveMode();
440 return SetRingConMode();
441 }
442
443 std::scoped_lock lock{mutex};
444 motion_enabled = true;
445 hidbus_enabled = false;
446 nfc_enabled = false;
447 passive_enabled = false;
448 irs_enabled = false;
449 return SetPollingMode();
450}
451
452DriverResult JoyconDriver::SetIrMode() {
453 std::scoped_lock lock{mutex};
454
455 if (!supported_features.irs) {
456 return DriverResult::NotSupported;
457 }
458
459 if (ring_connected) {
460 is_ring_disabled_by_irs = true;
461 }
462
463 motion_enabled = false;
464 hidbus_enabled = false;
465 nfc_enabled = false;
466 passive_enabled = false;
467 irs_enabled = true;
468 return SetPollingMode();
469}
470
471DriverResult JoyconDriver::SetNfcMode() {
472 std::scoped_lock lock{mutex};
473
474 if (!supported_features.nfc) {
475 return DriverResult::NotSupported;
476 }
477
478 motion_enabled = true;
479 hidbus_enabled = false;
480 nfc_enabled = true;
481 passive_enabled = false;
482 irs_enabled = false;
483 return SetPollingMode();
484}
485
486DriverResult JoyconDriver::SetRingConMode() {
487 std::scoped_lock lock{mutex};
488
489 if (!supported_features.hidbus) {
490 return DriverResult::NotSupported;
491 }
492
493 motion_enabled = true;
494 hidbus_enabled = true;
495 nfc_enabled = false;
496 passive_enabled = false;
497 irs_enabled = false;
498
499 const auto result = SetPollingMode();
500
501 if (!ring_connected) {
502 return DriverResult::NoDeviceDetected;
503 }
504
505 return result;
506}
507
508DriverResult JoyconDriver::StartNfcPolling() {
509 std::scoped_lock lock{mutex};
510
511 if (!supported_features.nfc) {
512 return DriverResult::NotSupported;
513 }
514 if (!nfc_protocol->IsEnabled()) {
515 return DriverResult::Disabled;
516 }
517
518 disable_input_thread = true;
519 const auto result = nfc_protocol->StartNFCPollingMode();
520 disable_input_thread = false;
521
522 return result;
523}
524
525DriverResult JoyconDriver::StopNfcPolling() {
526 std::scoped_lock lock{mutex};
527
528 if (!supported_features.nfc) {
529 return DriverResult::NotSupported;
530 }
531 if (!nfc_protocol->IsEnabled()) {
532 return DriverResult::Disabled;
533 }
534
535 disable_input_thread = true;
536 const auto result = nfc_protocol->StopNFCPollingMode();
537 disable_input_thread = false;
538
539 if (amiibo_detected) {
540 amiibo_detected = false;
541 joycon_poller->UpdateAmiibo({});
542 }
543
544 return result;
545}
546
547DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) {
548 std::scoped_lock lock{mutex};
549
550 if (!supported_features.nfc) {
551 return DriverResult::NotSupported;
552 }
553 if (!nfc_protocol->IsEnabled()) {
554 return DriverResult::Disabled;
555 }
556 if (!amiibo_detected) {
557 return DriverResult::ErrorWritingData;
558 }
559
560 out_data.resize(0x21C);
561 disable_input_thread = true;
562 const auto result = nfc_protocol->ReadAmiibo(out_data);
563 disable_input_thread = false;
564
565 return result;
566}
567
568DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
569 std::scoped_lock lock{mutex};
570
571 if (!supported_features.nfc) {
572 return DriverResult::NotSupported;
573 }
574 if (!nfc_protocol->IsEnabled()) {
575 return DriverResult::Disabled;
576 }
577 if (!amiibo_detected) {
578 return DriverResult::ErrorWritingData;
579 }
580
581 disable_input_thread = true;
582 const auto result = nfc_protocol->WriteAmiibo(data);
583 disable_input_thread = false;
584
585 return result;
586}
587
588DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
589 std::span<MifareReadData> out_data) {
590 std::scoped_lock lock{mutex};
591
592 if (!supported_features.nfc) {
593 return DriverResult::NotSupported;
594 }
595 if (!nfc_protocol->IsEnabled()) {
596 return DriverResult::Disabled;
597 }
598 if (!amiibo_detected) {
599 return DriverResult::ErrorWritingData;
600 }
601
602 disable_input_thread = true;
603 const auto result = nfc_protocol->ReadMifare(data, out_data);
604 disable_input_thread = false;
605
606 return result;
607}
608
609DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
610 std::scoped_lock lock{mutex};
611
612 if (!supported_features.nfc) {
613 return DriverResult::NotSupported;
614 }
615 if (!nfc_protocol->IsEnabled()) {
616 return DriverResult::Disabled;
617 }
618 if (!amiibo_detected) {
619 return DriverResult::ErrorWritingData;
620 }
621
622 disable_input_thread = true;
623 const auto result = nfc_protocol->WriteMifare(data);
624 disable_input_thread = false;
625
626 return result;
627}
628
629bool JoyconDriver::IsConnected() const {
630 std::scoped_lock lock{mutex};
631 return is_connected.load();
632}
633
634bool JoyconDriver::IsVibrationEnabled() const {
635 std::scoped_lock lock{mutex};
636 return vibration_enabled;
637}
638
639FirmwareVersion JoyconDriver::GetDeviceVersion() const {
640 std::scoped_lock lock{mutex};
641 return version;
642}
643
644Color JoyconDriver::GetDeviceColor() const {
645 std::scoped_lock lock{mutex};
646 return color;
647}
648
649std::size_t JoyconDriver::GetDevicePort() const {
650 std::scoped_lock lock{mutex};
651 return port;
652}
653
654ControllerType JoyconDriver::GetDeviceType() const {
655 std::scoped_lock lock{mutex};
656 return device_type;
657}
658
659ControllerType JoyconDriver::GetHandleDeviceType() const {
660 std::scoped_lock lock{mutex};
661 return handle_device_type;
662}
663
664SerialNumber JoyconDriver::GetSerialNumber() const {
665 std::scoped_lock lock{mutex};
666 return serial_number;
667}
668
669SerialNumber JoyconDriver::GetHandleSerialNumber() const {
670 std::scoped_lock lock{mutex};
671 return handle_serial_number;
672}
673
674void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) {
675 joycon_poller->SetCallbacks(callbacks);
676}
677
678DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
679 ControllerType& controller_type) {
680 static constexpr std::array<std::pair<u32, ControllerType>, 6> supported_devices{
681 std::pair<u32, ControllerType>{0x2006, ControllerType::Left},
682 {0x2007, ControllerType::Right},
683 {0x2009, ControllerType::Pro},
684 };
685 constexpr u16 nintendo_vendor_id = 0x057e;
686
687 controller_type = ControllerType::None;
688 if (device_info->vendor_id != nintendo_vendor_id) {
689 return DriverResult::UnsupportedControllerType;
690 }
691
692 for (const auto& [product_id, type] : supported_devices) {
693 if (device_info->product_id == static_cast<u16>(product_id)) {
694 controller_type = type;
695 return Joycon::DriverResult::Success;
696 }
697 }
698 return Joycon::DriverResult::UnsupportedControllerType;
699}
700
701DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info,
702 SerialNumber& serial_number) {
703 if (device_info->serial_number == nullptr) {
704 return DriverResult::Unknown;
705 }
706 std::memcpy(&serial_number, device_info->serial_number, 15);
707 return Joycon::DriverResult::Success;
708}
709
710} // namespace InputCommon::Joycon