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