diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 2453086a91..397235fb0c 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -411,8 +411,8 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); if (Settings::values.enable_sdl_joycons) { - // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and - // not a generic one + // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController + // and not a generic one SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); } diff --git a/src/input_common/helpers/joycon_protocol.cpp b/src/input_common/helpers/joycon_protocol.cpp index 48d121b099..6131058d8f 100644 --- a/src/input_common/helpers/joycon_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol.cpp @@ -44,7 +44,7 @@ f32 EncodeRumbleAmplification(f32 amplification) { void SetVibration(JoyconHandle& joycon_handle, VibrationValue vibration) { std::vector buffer(Joycon::max_resp_size); - buffer[0] = static_cast(Joycon::Output::RUMBLE_ONLY); + buffer[0] = static_cast(Joycon::OutputReport::RUMBLE_ONLY); buffer[1] = GetCounter(joycon_handle); if (vibration.high_amplitude == 0.0f && vibration.low_amplitude == 0.0f) { @@ -87,21 +87,21 @@ void SetImuConfig(JoyconHandle& joycon_handle, GyroSensitivity gsen, GyroPerform const std::vector buffer{static_cast(gsen), static_cast(asen), static_cast(gfrec), static_cast(afrec)}; SDL_hid_set_nonblocking(joycon_handle.handle, 0); - SendSubCommand(joycon_handle, SubCommand::SET_IMU_SENSITIVITY, buffer, 4); + SendSubCommand(joycon_handle, SubCommand::SET_IMU_SENSITIVITY, buffer, buffer.size()); SDL_hid_set_nonblocking(joycon_handle.handle, 1); } void SetLedConfig(JoyconHandle& joycon_handle, u8 leds) { const std::vector buffer{leds}; SDL_hid_set_nonblocking(joycon_handle.handle, 0); - SendSubCommand(joycon_handle, SubCommand::SET_PLAYER_LIGHTS, buffer, 1); + SendSubCommand(joycon_handle, SubCommand::SET_PLAYER_LIGHTS, buffer, buffer.size()); SDL_hid_set_nonblocking(joycon_handle.handle, 1); } void SetReportMode(JoyconHandle& joycon_handle, ReportMode report_mode) { const std::vector buffer{static_cast(report_mode)}; SDL_hid_set_nonblocking(joycon_handle.handle, 0); - SendSubCommand(joycon_handle, SubCommand::SET_REPORT_MODE, buffer, 1); + SendSubCommand(joycon_handle, SubCommand::SET_REPORT_MODE, buffer, buffer.size()); SDL_hid_set_nonblocking(joycon_handle.handle, 1); } @@ -293,14 +293,14 @@ CalibrationData GetFactoryCalibrationData(JoyconHandle& joycon_handle, void EnableImu(JoyconHandle& joycon_handle, bool enable) { SDL_hid_set_nonblocking(joycon_handle.handle, 0); const std::vector buffer{static_cast(enable ? 1 : 0)}; - SendSubCommand(joycon_handle, SubCommand::ENABLE_IMU, buffer, 1); + SendSubCommand(joycon_handle, SubCommand::ENABLE_IMU, buffer, buffer.size()); SDL_hid_set_nonblocking(joycon_handle.handle, 1); } void EnableRumble(JoyconHandle& joycon_handle, bool enable) { SDL_hid_set_nonblocking(joycon_handle.handle, 0); const std::vector buffer{static_cast(enable ? 1 : 0)}; - SendSubCommand(joycon_handle, SubCommand::ENABLE_VIBRATION, buffer, 1); + SendSubCommand(joycon_handle, SubCommand::ENABLE_VIBRATION, buffer, buffer.size()); SDL_hid_set_nonblocking(joycon_handle.handle, 1); } @@ -309,10 +309,12 @@ void SendData(JoyconHandle& joycon_handle, std::span buffer, std::size } std::vector GetResponse(JoyconHandle& joycon_handle, SubCommand sc) { + constexpr int timeout_mili = 100; int tries = 0; std::vector buffer(max_resp_size); do { - int result = SDL_hid_read_timeout(joycon_handle.handle, buffer.data(), max_resp_size, 100); + int result = + SDL_hid_read_timeout(joycon_handle.handle, buffer.data(), max_resp_size, timeout_time); if (result < 1) { LOG_ERROR(Input, "No response from joycon"); } @@ -325,7 +327,7 @@ std::vector SendSubCommand(JoyconHandle& joycon_handle, SubCommand sc, std::span buffer, std::size_t size) { std::vector local_buffer(size + 11); - local_buffer[0] = static_cast(Output::RUMBLE_AND_SUBCMD); + local_buffer[0] = static_cast(OutputReport::RUMBLE_AND_SUBCMD); local_buffer[1] = GetCounter(joycon_handle); for (std::size_t i = 0; i < 8; ++i) { local_buffer[i + 2] = default_buffer[i]; @@ -340,13 +342,16 @@ std::vector SendSubCommand(JoyconHandle& joycon_handle, SubCommand sc, } std::vector ReadSPI(JoyconHandle& joycon_handle, CalAddr addr, u8 size) { + constexpr std::size_t max_tries = 10; std::vector buffer = {0x00, 0x00, 0x00, 0x00, size}; std::vector local_buffer(size + 20); buffer[0] = static_cast(static_cast(addr) & 0x00FF); buffer[1] = static_cast((static_cast(addr) & 0xFF00) >> 8); - for (std::size_t i = 0; i < 100; ++i) { - local_buffer = SendSubCommand(joycon_handle, SubCommand::SPI_FLASH_READ, buffer, 5); + for (std::size_t i = 0; i < max_tries; ++i) { + local_buffer = + SendSubCommand(joycon_handle, SubCommand::SPI_FLASH_READ, buffer, buffer.size()); + // Recieved packed has to match with the requested address if (local_buffer[15] == buffer[0] && local_buffer[16] == buffer[1]) { break; } diff --git a/src/input_common/helpers/joycon_protocol.h b/src/input_common/helpers/joycon_protocol.h index 6883bf8352..063fbe3ac4 100644 --- a/src/input_common/helpers/joycon_protocol.h +++ b/src/input_common/helpers/joycon_protocol.h @@ -2,6 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included +// Based on dkms-hid-nintendo implementation and dekuNukem reverse engineering +// https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + #pragma once #include @@ -46,7 +50,7 @@ enum class PadButton : u32 { Capture = 0x200000, }; -enum class Output { +enum class OutputReport { RUMBLE_AND_SUBCMD = 0x01, FW_UPDATE_PKT = 0x03, RUMBLE_ONLY = 0x10, @@ -54,6 +58,24 @@ enum class Output { USB_CMD = 0x80, }; +enum class InputReport { + BUTTON_EVENT = 0x3F, + SUBCMD_REPLY = 0x21, + IMU_DATA = 0x30, + MCU_DATA = 0x31, + INPUT_USB_RESPONSE = 0x81, +}; + +enum class FeatureReport { + Last_SUBCMD = 0x02, + OTA_GW_UPGRADE = 0x70, + SETUP_MEM_READ = 0x71, + MEM_READ = 0x72, + ERASE_MEM_SECTOR = 0x73, + MEM_WRITE = 0x74, + LAUNCH = 0x75, +}; + enum class SubCommand { STATE = 0x00, MANUAL_BT_PAIRING = 0x01, @@ -80,6 +102,23 @@ enum class SubCommand { GET_REGULATED_VOLTAGE = 0x50, }; +enum class UsbSubCommand { + CONN_STATUS = 0x01, + HADSHAKE = 0x02, + BAUDRATE_3M = 0x03, + NO_TIMEOUT = 0x04, + EN_TIMEOUT = 0x05, + RESET = 0x06, + PRE_HANDSHAKE = 0x91, + SEND_UART = 0x92, +}; + +enum class CalMagic { + USR_MAGIC_0 = 0xB2, + USR_MAGIC_1 = 0xA1, + USRR_MAGI_SIZE = 2, +}; + enum class CalAddr { USER_LEFT_MAGIC = 0X8010, USER_LEFT_DATA = 0X8012, @@ -111,11 +150,11 @@ enum class GyroSensitivity { DPS250, DPS500, DPS1000, - DPS2000, + DPS2000, // Default }; enum class AccelerometerSensitivity { - G8, + G8, // Default G4, G2, G16, @@ -123,12 +162,12 @@ enum class AccelerometerSensitivity { enum class GyroPerformance { HZ833, - HZ208, + HZ208, // Default }; enum class AccelerometerPerformance { HZ200, - HZ100, + HZ100, // Default }; struct ImuSensorCalibration {