From 11fea5deea6dd72badbed521ac5639a48d591dc6 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Fri, 27 Jan 2023 20:35:51 -0600 Subject: [PATCH 1/2] input_common: joycon: Fill missing enum data --- src/input_common/drivers/joycon.cpp | 4 +- src/input_common/drivers/joycon.h | 2 +- .../joycon_protocol/generic_functions.cpp | 2 +- .../helpers/joycon_protocol/joycon_types.h | 68 ++++++++++++------- .../helpers/joycon_protocol/poller.cpp | 8 +-- .../helpers/joycon_protocol/ringcon.cpp | 10 ++- 6 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index cedc94e639..4fcfb45108 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -668,12 +668,10 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const { return "Right Joycon"; case Joycon::ControllerType::Pro: return "Pro Controller"; - case Joycon::ControllerType::Grip: - return "Grip Controller"; case Joycon::ControllerType::Dual: return "Dual Joycon"; default: - return "Unknown Joycon"; + return "Unknown Switch Controller"; } } } // namespace InputCommon diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index 316d383d88..2149ab7fdc 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -15,7 +15,7 @@ using SerialNumber = std::array; struct Battery; struct Color; struct MotionData; -enum class ControllerType; +enum class ControllerType : u8; enum class DriverResult; enum class IrsResolution; class JoyconDriver; diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp index 484c208e6a..f3fefd5b6b 100644 --- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp +++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp @@ -38,7 +38,7 @@ DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { device_info = {}; if (result == DriverResult::Success) { - memcpy(&device_info, output.data(), sizeof(DeviceInfo)); + memcpy(&device_info, output.data() + 15, sizeof(DeviceInfo)); } return result; diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 14b07bfb52..ddb48a5533 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -26,13 +26,19 @@ constexpr std::array DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x using MacAddress = std::array; using SerialNumber = std::array; -enum class ControllerType { - None, - Left, - Right, - Pro, - Grip, - Dual, +enum class ControllerType : u8 { + None = 0x00, + Left = 0x01, + Right = 0x02, + Pro = 0x03, + Dual = 0x05, // TODO: Verify this id + LarkHvc1 = 0x07, + LarkHvc2 = 0x08, + LarkNesLeft = 0x09, + LarkNesRight = 0x0A, + Lucia = 0x0B, + Lagon = 0x0C, + Lager = 0x0D, }; enum class PadAxes { @@ -143,9 +149,10 @@ enum class SubCommand : u8 { ENABLE_VIBRATION = 0x48, GET_REGULATED_VOLTAGE = 0x50, SET_EXTERNAL_CONFIG = 0x58, - UNKNOWN_RINGCON = 0x59, - UNKNOWN_RINGCON2 = 0x5A, - UNKNOWN_RINGCON3 = 0x5C, + GET_EXTERNAL_DEVICE_INFO = 0x59, + ENABLE_EXTERNAL_POLLING = 0x5A, + DISABLE_EXTERNAL_POLLING = 0x5B, + SET_EXTERNAL_FORMAT_CONFIG = 0x5C, }; enum class UsbSubCommand : u8 { @@ -165,19 +172,25 @@ enum class CalibrationMagic : u8 { }; enum class SpiAddress { - SERIAL_NUMBER = 0X6000, - DEVICE_TYPE = 0X6012, - COLOR_EXIST = 0X601B, - FACT_LEFT_DATA = 0X603d, - FACT_RIGHT_DATA = 0X6046, - COLOR_DATA = 0X6050, - FACT_IMU_DATA = 0X6020, - USER_LEFT_MAGIC = 0X8010, - USER_LEFT_DATA = 0X8012, - USER_RIGHT_MAGIC = 0X801B, - USER_RIGHT_DATA = 0X801D, - USER_IMU_MAGIC = 0X8026, - USER_IMU_DATA = 0X8028, + MAGIC = 0x0000, + MAC_ADDRESS = 0x0015, + PAIRING_INFO = 0x2000, + SHIPMENT = 0x5000, + SERIAL_NUMBER = 0x6000, + DEVICE_TYPE = 0x6012, + FORMAT_VERSION = 0x601B, + FACT_IMU_DATA = 0x6020, + FACT_LEFT_DATA = 0x603d, + FACT_RIGHT_DATA = 0x6046, + COLOR_DATA = 0x6050, + DESIGN_VARIATION = 0x605C, + SENSOR_DATA = 0x6080, + USER_LEFT_MAGIC = 0x8010, + USER_LEFT_DATA = 0x8012, + USER_RIGHT_MAGIC = 0x801B, + USER_RIGHT_DATA = 0x801D, + USER_IMU_MAGIC = 0x8026, + USER_IMU_DATA = 0x8028, }; enum class ReportMode : u8 { @@ -359,6 +372,11 @@ enum class IrRegistersAddress : u16 { DenoiseColor = 0x6901, }; +enum class ExternalDeviceId : u8 { + RingController = 0x20, + Starlink = 0x28, +}; + enum class DriverResult { Success, WrongReply, @@ -605,9 +623,11 @@ static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid siz struct DeviceInfo { FirmwareVersion firmware; + std::array unknown_1; MacAddress mac_address; + std::array unknown_2; }; -static_assert(sizeof(DeviceInfo) == 0x8, "DeviceInfo is an invalid size"); +static_assert(sizeof(DeviceInfo) == 0xC, "DeviceInfo is an invalid size"); struct MotionStatus { bool is_enabled; diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index 7f8e093faf..9bb15e9353 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -31,9 +31,7 @@ void JoyconPoller::ReadActiveMode(std::span buffer, const MotionStatus& moti case Joycon::ControllerType::Pro: UpdateActiveProPadInput(data, motion_status); break; - case Joycon::ControllerType::Grip: - case Joycon::ControllerType::Dual: - case Joycon::ControllerType::None: + default: break; } @@ -58,9 +56,7 @@ void JoyconPoller::ReadPassiveMode(std::span buffer) { case Joycon::ControllerType::Pro: UpdatePasiveProPadInput(data); break; - case Joycon::ControllerType::Grip: - case Joycon::ControllerType::Dual: - case Joycon::ControllerType::None: + default: break; } } diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp index 12f81309e1..3c2ca4379f 100644 --- a/src/input_common/helpers/joycon_protocol/ringcon.cpp +++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp @@ -70,14 +70,12 @@ DriverResult RingConProtocol::StartRingconPolling() { DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { LOG_DEBUG(Input, "IsRingConnected"); constexpr std::size_t max_tries = 28; - constexpr u8 ring_controller_id = 0x20; std::vector output; std::size_t tries = 0; is_connected = false; do { - std::array empty_data{}; - const auto result = SendSubCommand(SubCommand::UNKNOWN_RINGCON, empty_data, output); + const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output); if (result != DriverResult::Success) { return result; @@ -86,7 +84,7 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { if (tries++ >= max_tries) { return DriverResult::NoDeviceDetected; } - } while (output[16] != ring_controller_id); + } while (output[16] != static_cast(ExternalDeviceId::RingController)); is_connected = true; return DriverResult::Success; @@ -100,14 +98,14 @@ DriverResult RingConProtocol::ConfigureRing() { 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36}; - const DriverResult result = SendSubCommand(SubCommand::UNKNOWN_RINGCON3, ring_config); + const DriverResult result = SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config); if (result != DriverResult::Success) { return result; } static constexpr std::array ringcon_data{0x04, 0x01, 0x01, 0x02}; - return SendSubCommand(SubCommand::UNKNOWN_RINGCON2, ringcon_data); + return SendSubCommand(SubCommand::ENABLE_EXTERNAL_POLLING, ringcon_data); } bool RingConProtocol::IsEnabled() const { From c318a4c80b4b6eef9f8020f452573ef5c80f6716 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Fri, 27 Jan 2023 22:30:44 -0600 Subject: [PATCH 2/2] input_common: joycon: Remove Magic numbers from common protocol --- src/input_common/helpers/joycon_driver.cpp | 18 +-- .../joycon_protocol/common_protocol.cpp | 145 ++++++++++-------- .../helpers/joycon_protocol/common_protocol.h | 31 ++-- .../joycon_protocol/generic_functions.cpp | 4 +- .../helpers/joycon_protocol/irs.cpp | 13 +- .../helpers/joycon_protocol/joycon_types.h | 74 +++++++-- .../helpers/joycon_protocol/nfc.cpp | 68 ++++---- .../helpers/joycon_protocol/nfc.h | 8 +- .../helpers/joycon_protocol/ringcon.cpp | 4 +- 9 files changed, 216 insertions(+), 149 deletions(-) diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 3775e2d358..8f94c9f45d 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -162,14 +162,14 @@ void JoyconDriver::InputThread(std::stop_token stop_token) { } void JoyconDriver::OnNewData(std::span buffer) { - const auto report_mode = static_cast(buffer[0]); + const auto report_mode = static_cast(buffer[0]); // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion // experience switch (report_mode) { - case InputReport::STANDARD_FULL_60HZ: - case InputReport::NFC_IR_MODE_60HZ: - case InputReport::SIMPLE_HID_MODE: { + case ReportMode::STANDARD_FULL_60HZ: + case ReportMode::NFC_IR_MODE_60HZ: + case ReportMode::SIMPLE_HID_MODE: { const auto now = std::chrono::steady_clock::now(); const auto new_delta_time = static_cast( std::chrono::duration_cast(now - last_update).count()); @@ -190,7 +190,7 @@ void JoyconDriver::OnNewData(std::span buffer) { }; // TODO: Remove this when calibration is properly loaded and not calculated - if (ring_connected && report_mode == InputReport::STANDARD_FULL_60HZ) { + if (ring_connected && report_mode == ReportMode::STANDARD_FULL_60HZ) { InputReportActive data{}; memcpy(&data, buffer.data(), sizeof(InputReportActive)); calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input); @@ -228,16 +228,16 @@ void JoyconDriver::OnNewData(std::span buffer) { } switch (report_mode) { - case InputReport::STANDARD_FULL_60HZ: + case ReportMode::STANDARD_FULL_60HZ: joycon_poller->ReadActiveMode(buffer, motion_status, ring_status); break; - case InputReport::NFC_IR_MODE_60HZ: + case ReportMode::NFC_IR_MODE_60HZ: joycon_poller->ReadNfcIRMode(buffer, motion_status); break; - case InputReport::SIMPLE_HID_MODE: + case ReportMode::SIMPLE_HID_MODE: joycon_poller->ReadPassiveMode(buffer); break; - case InputReport::SUBCMD_REPLY: + case ReportMode::SUBCMD_REPLY: LOG_DEBUG(Input, "Unhandled command reply"); break; default: diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index 0ef2403440..2b42a4555a 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp @@ -22,12 +22,9 @@ void JoyconCommonProtocol::SetNonBlocking() { } DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { - std::array buffer{}; - const auto result = ReadRawSPI(SpiAddress::DEVICE_TYPE, buffer); - controller_type = ControllerType::None; + const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type); if (result == DriverResult::Success) { - controller_type = static_cast(buffer[0]); // Fallback to 3rd party pro controllers if (controller_type == ControllerType::None) { controller_type = ControllerType::Pro; @@ -40,6 +37,7 @@ DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) { ControllerType controller_type{ControllerType::None}; const auto result = GetDeviceType(controller_type); + if (result != DriverResult::Success || controller_type == ControllerType::None) { return DriverResult::UnsupportedControllerType; } @@ -62,7 +60,7 @@ DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) { return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer); } -DriverResult JoyconCommonProtocol::SendData(std::span buffer) { +DriverResult JoyconCommonProtocol::SendRawData(std::span buffer) { const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size()); if (result == -1) { @@ -72,15 +70,15 @@ DriverResult JoyconCommonProtocol::SendData(std::span buffer) { return DriverResult::Success; } -DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vector& output) { +DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, + SubCommandResponse& output) { constexpr int timeout_mili = 66; constexpr int MaxTries = 15; int tries = 0; - output.resize(MaxSubCommandResponseSize); do { - int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), - MaxSubCommandResponseSize, timeout_mili); + int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast(&output), + sizeof(SubCommandResponse), timeout_mili); if (result < 1) { LOG_ERROR(Input, "No response from joycon"); @@ -88,27 +86,28 @@ DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vec if (tries++ > MaxTries) { return DriverResult::Timeout; } - } while (output[0] != 0x21 && output[14] != static_cast(sc)); - - if (output[0] != 0x21 && output[14] != static_cast(sc)) { - return DriverResult::WrongReply; - } + } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY && + output.sub_command != sc); return DriverResult::Success; } DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span buffer, - std::vector& output) { - std::vector local_buffer(MaxResponseSize); + SubCommandResponse& output) { + SubCommandPacket packet{ + .output_report = OutputReport::RUMBLE_AND_SUBCMD, + .packet_counter = GetCounter(), + .sub_command = sc, + .command_data = {}, + }; - local_buffer[0] = static_cast(OutputReport::RUMBLE_AND_SUBCMD); - local_buffer[1] = GetCounter(); - local_buffer[10] = static_cast(sc); - for (std::size_t i = 0; i < buffer.size(); ++i) { - local_buffer[11 + i] = buffer[i]; + if (buffer.size() > packet.command_data.size()) { + return DriverResult::InvalidParameters; } - auto result = SendData(local_buffer); + memcpy(packet.command_data.data(), buffer.data(), buffer.size()); + + auto result = SendData(packet); if (result != DriverResult::Success) { return result; @@ -120,46 +119,57 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span buffer) { - std::vector output; + SubCommandResponse output{}; return SendSubCommand(sc, buffer, output); } DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span buffer) { - std::vector local_buffer(MaxResponseSize); + SubCommandPacket packet{ + .output_report = OutputReport::MCU_DATA, + .packet_counter = GetCounter(), + .sub_command = sc, + .command_data = {}, + }; - local_buffer[0] = static_cast(OutputReport::MCU_DATA); - local_buffer[1] = GetCounter(); - local_buffer[10] = static_cast(sc); - for (std::size_t i = 0; i < buffer.size(); ++i) { - local_buffer[11 + i] = buffer[i]; + if (buffer.size() > packet.command_data.size()) { + return DriverResult::InvalidParameters; } - return SendData(local_buffer); + memcpy(packet.command_data.data(), buffer.data(), buffer.size()); + + return SendData(packet); } DriverResult JoyconCommonProtocol::SendVibrationReport(std::span buffer) { - std::vector local_buffer(MaxResponseSize); + VibrationPacket packet{ + .output_report = OutputReport::RUMBLE_ONLY, + .packet_counter = GetCounter(), + .vibration_data = {}, + }; - local_buffer[0] = static_cast(Joycon::OutputReport::RUMBLE_ONLY); - local_buffer[1] = GetCounter(); + if (buffer.size() > packet.vibration_data.size()) { + return DriverResult::InvalidParameters; + } - memcpy(local_buffer.data() + 2, buffer.data(), buffer.size()); + memcpy(packet.vibration_data.data(), buffer.data(), buffer.size()); - return SendData(local_buffer); + return SendData(packet); } DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span output) { - constexpr std::size_t HeaderSize = 20; + constexpr std::size_t HeaderSize = 5; constexpr std::size_t MaxTries = 10; - const auto size = output.size(); std::size_t tries = 0; - std::array buffer = {0x00, 0x00, 0x00, 0x00, static_cast(size)}; - std::vector local_buffer{}; + SubCommandResponse response{}; + std::array buffer{}; + const ReadSpiPacket packet_data{ + .spi_address = addr, + .size = static_cast(output.size()), + }; - buffer[0] = static_cast(static_cast(addr) & 0x00FF); - buffer[1] = static_cast((static_cast(addr) & 0xFF00) >> 8); + memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket)); do { - const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, local_buffer); + const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response); if (result != DriverResult::Success) { return result; } @@ -167,14 +177,14 @@ DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span out if (tries++ > MaxTries) { return DriverResult::Timeout; } - } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]); + } while (response.spi_address != addr); - if (local_buffer.size() < size + HeaderSize) { + if (response.command_data.size() < packet_data.size + HeaderSize) { return DriverResult::WrongReply; } // Remove header from output - memcpy(output.data(), local_buffer.data() + HeaderSize, size); + memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size); return DriverResult::Success; } @@ -183,7 +193,7 @@ DriverResult JoyconCommonProtocol::EnableMCU(bool enable) { const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state); if (result != DriverResult::Success) { - LOG_ERROR(Input, "SendMCUData failed with error {}", result); + LOG_ERROR(Input, "Failed with error {}", result); } return result; @@ -198,22 +208,21 @@ DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) { const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer); if (result != DriverResult::Success) { - LOG_ERROR(Input, "Set MCU config failed with error {}", result); + LOG_ERROR(Input, "Failed with error {}", result); } return result; } -DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_, - std::vector& output) { - const int report_mode = static_cast(report_mode_); +DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode, + MCUCommandResponse& output) { constexpr int TimeoutMili = 200; constexpr int MaxTries = 9; int tries = 0; - output.resize(0x170); do { - int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), 0x170, TimeoutMili); + int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast(&output), + sizeof(MCUCommandResponse), TimeoutMili); if (result < 1) { LOG_ERROR(Input, "No response from joycon attempt {}", tries); @@ -221,28 +230,29 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_, if (tries++ > MaxTries) { return DriverResult::Timeout; } - } while (output[0] != report_mode || output[49] == 0xFF); - - if (output[0] != report_mode || output[49] == 0xFF) { - return DriverResult::WrongReply; - } + } while (output.input_report.report_mode != report_mode || + output.mcu_report == MCUReport::EmptyAwaitingCmd); return DriverResult::Success; } DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc, std::span buffer, - std::vector& output) { - std::vector local_buffer(MaxResponseSize); + MCUCommandResponse& output) { + SubCommandPacket packet{ + .output_report = OutputReport::MCU_DATA, + .packet_counter = GetCounter(), + .sub_command = sc, + .command_data = {}, + }; - local_buffer[0] = static_cast(OutputReport::MCU_DATA); - local_buffer[1] = GetCounter(); - local_buffer[9] = static_cast(sc); - for (std::size_t i = 0; i < buffer.size(); ++i) { - local_buffer[10 + i] = buffer[i]; + if (buffer.size() > packet.command_data.size()) { + return DriverResult::InvalidParameters; } - auto result = SendData(local_buffer); + memcpy(packet.command_data.data(), buffer.data(), buffer.size()); + + auto result = SendData(packet); if (result != DriverResult::Success) { return result; @@ -254,7 +264,7 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubComman } DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) { - std::vector output; + MCUCommandResponse output{}; constexpr std::size_t MaxTries{8}; std::size_t tries{}; @@ -269,7 +279,8 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod if (tries++ > MaxTries) { return DriverResult::WrongReply; } - } while (output[49] != 1 || output[56] != static_cast(mode)); + } while (output.mcu_report != MCUReport::StateReport || + output.mcu_data[6] != static_cast(mode)); return DriverResult::Success; } diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h index 188f6ecfa6..f44f73ba4e 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.h +++ b/src/input_common/helpers/joycon_protocol/common_protocol.h @@ -57,22 +57,31 @@ public: * Sends data to the joycon device * @param buffer data to be send */ - DriverResult SendData(std::span buffer); + DriverResult SendRawData(std::span buffer); + + template + requires std::is_trivially_copyable_v + DriverResult SendData(const Output& output) { + std::array buffer; + std::memcpy(buffer.data(), &output, sizeof(Output)); + return SendRawData(buffer); + } /** * Waits for incoming data of the joycon device that matchs the subcommand * @param sub_command type of data to be returned - * @returns a buffer containing the responce + * @returns a buffer containing the response */ - DriverResult GetSubCommandResponse(SubCommand sub_command, std::vector& output); + DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output); /** * Sends a sub command to the device and waits for it's reply * @param sc sub command to be send * @param buffer data to be send - * @returns output buffer containing the responce + * @returns output buffer containing the response */ - DriverResult SendSubCommand(SubCommand sc, std::span buffer, std::vector& output); + DriverResult SendSubCommand(SubCommand sc, std::span buffer, + SubCommandResponse& output); /** * Sends a sub command to the device and waits for it's reply and ignores the output @@ -97,14 +106,14 @@ public: /** * Reads the SPI memory stored on the joycon * @param Initial address location - * @returns output buffer containing the responce + * @returns output buffer containing the response */ DriverResult ReadRawSPI(SpiAddress addr, std::span output); /** * Reads the SPI memory stored on the joycon * @param Initial address location - * @returns output object containing the responce + * @returns output object containing the response */ template requires std::is_trivially_copyable_v @@ -136,19 +145,19 @@ public: /** * Waits until there's MCU data available. On timeout returns error * @param report mode of the expected reply - * @returns a buffer containing the responce + * @returns a buffer containing the response */ - DriverResult GetMCUDataResponse(ReportMode report_mode_, std::vector& output); + DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output); /** * Sends data to the MCU chip and waits for it's reply * @param report mode of the expected reply * @param sub command to be send * @param buffer data to be send - * @returns output buffer containing the responce + * @returns output buffer containing the response */ DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span buffer, - std::vector& output); + MCUCommandResponse& output); /** * Wait's until the MCU chip is on the specified mode diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp index f3fefd5b6b..548a4b9e35 100644 --- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp +++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp @@ -32,13 +32,13 @@ DriverResult GenericProtocol::TriggersElapsed() { DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { ScopedSetBlocking sb(this); - std::vector output; + SubCommandResponse output{}; const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); device_info = {}; if (result == DriverResult::Success) { - memcpy(&device_info, output.data() + 15, sizeof(DeviceInfo)); + device_info = output.device_info; } return result; diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp index 09e17bc5b6..731fd59810 100644 --- a/src/input_common/helpers/joycon_protocol/irs.cpp +++ b/src/input_common/helpers/joycon_protocol/irs.cpp @@ -132,7 +132,7 @@ DriverResult IrsProtocol::RequestImage(std::span buffer) { DriverResult IrsProtocol::ConfigureIrs() { LOG_DEBUG(Input, "Configure IRS"); constexpr std::size_t max_tries = 28; - std::vector output; + SubCommandResponse output{}; std::size_t tries = 0; const IrsConfigure irs_configuration{ @@ -158,7 +158,7 @@ DriverResult IrsProtocol::ConfigureIrs() { if (tries++ >= max_tries) { return DriverResult::WrongReply; } - } while (output[15] != 0x0b); + } while (output.command_data[0] != 0x0b); return DriverResult::Success; } @@ -167,7 +167,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() { LOG_DEBUG(Input, "WriteRegistersStep1"); DriverResult result{DriverResult::Success}; constexpr std::size_t max_tries = 28; - std::vector output; + SubCommandResponse output{}; std::size_t tries = 0; const IrsWriteRegisters irs_registers{ @@ -218,7 +218,8 @@ DriverResult IrsProtocol::WriteRegistersStep1() { if (tries++ >= max_tries) { return DriverResult::WrongReply; } - } while (!(output[15] == 0x13 && output[17] == 0x07) && output[15] != 0x23); + } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) && + output.command_data[0] != 0x23); return DriverResult::Success; } @@ -226,7 +227,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() { DriverResult IrsProtocol::WriteRegistersStep2() { LOG_DEBUG(Input, "WriteRegistersStep2"); constexpr std::size_t max_tries = 28; - std::vector output; + SubCommandResponse output{}; std::size_t tries = 0; const IrsWriteRegisters irs_registers{ @@ -260,7 +261,7 @@ DriverResult IrsProtocol::WriteRegistersStep2() { if (tries++ >= max_tries) { return DriverResult::WrongReply; } - } while (output[15] != 0x13 && output[15] != 0x23); + } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23); return DriverResult::Success; } diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index ddb48a5533..b919349900 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -19,8 +19,6 @@ namespace InputCommon::Joycon { constexpr u32 MaxErrorCount = 50; constexpr u32 MaxBufferSize = 368; -constexpr u32 MaxResponseSize = 49; -constexpr u32 MaxSubCommandResponseSize = 64; constexpr std::array DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40}; using MacAddress = std::array; @@ -105,14 +103,6 @@ enum class OutputReport : u8 { USB_CMD = 0x80, }; -enum class InputReport : u8 { - SUBCMD_REPLY = 0x21, - STANDARD_FULL_60HZ = 0x30, - NFC_IR_MODE_60HZ = 0x31, - SIMPLE_HID_MODE = 0x3F, - INPUT_USB_RESPONSE = 0x81, -}; - enum class FeatureReport : u8 { Last_SUBCMD = 0x02, OTA_GW_UPGRADE = 0x70, @@ -171,7 +161,7 @@ enum class CalibrationMagic : u8 { USR_MAGIC_1 = 0xA1, }; -enum class SpiAddress { +enum class SpiAddress : u16 { MAGIC = 0x0000, MAC_ADDRESS = 0x0015, PAIRING_INFO = 0x2000, @@ -198,10 +188,12 @@ enum class ReportMode : u8 { ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01, ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02, ACTIVE_POLLING_IR_CAMERA_DATA = 0x03, + SUBCMD_REPLY = 0x21, MCU_UPDATE_STATE = 0x23, STANDARD_FULL_60HZ = 0x30, NFC_IR_MODE_60HZ = 0x31, SIMPLE_HID_MODE = 0x3F, + INPUT_USB_RESPONSE = 0x81, }; enum class GyroSensitivity : u8 { @@ -372,15 +364,16 @@ enum class IrRegistersAddress : u16 { DenoiseColor = 0x6901, }; -enum class ExternalDeviceId : u8 { - RingController = 0x20, - Starlink = 0x28, +enum class ExternalDeviceId : u16 { + RingController = 0x2000, + Starlink = 0x2800, }; enum class DriverResult { Success, WrongReply, Timeout, + InvalidParameters, UnsupportedControllerType, HandleInUse, ErrorReadingData, @@ -503,7 +496,7 @@ static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size"); #pragma pack(push, 1) struct InputReportPassive { - InputReport report_mode; + ReportMode report_mode; u16 button_input; u8 stick_state; std::array unknown_data; @@ -511,7 +504,7 @@ struct InputReportPassive { static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size"); struct InputReportActive { - InputReport report_mode; + ReportMode report_mode; u8 packet_id; Battery battery_status; std::array button_input; @@ -525,7 +518,7 @@ struct InputReportActive { static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size"); struct InputReportNfcIr { - InputReport report_mode; + ReportMode report_mode; u8 packet_id; Battery battery_status; std::array button_input; @@ -643,6 +636,53 @@ struct RingStatus { s16 min_value; }; +struct VibrationPacket { + OutputReport output_report; + u8 packet_counter; + std::array vibration_data; +}; +static_assert(sizeof(VibrationPacket) == 0xA, "VibrationPacket is an invalid size"); + +struct SubCommandPacket { + OutputReport output_report; + u8 packet_counter; + INSERT_PADDING_BYTES(0x8); // This contains vibration data + SubCommand sub_command; + std::array command_data; +}; +static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size"); + +#pragma pack(push, 1) +struct ReadSpiPacket { + SpiAddress spi_address; + INSERT_PADDING_BYTES(0x2); + u8 size; +}; +static_assert(sizeof(ReadSpiPacket) == 0x5, "ReadSpiPacket is an invalid size"); + +struct SubCommandResponse { + InputReportPassive input_report; + SubCommand sub_command; + union { + std::array command_data; + SpiAddress spi_address; // Reply from SPI_FLASH_READ subcommand + ExternalDeviceId external_device_id; // Reply from GET_EXTERNAL_DEVICE_INFO subcommand + DeviceInfo device_info; // Reply from REQ_DEV_INFO subcommand + }; + u8 crc; // This is never used +}; +static_assert(sizeof(SubCommandResponse) == 0x40, "SubCommandResponse is an invalid size"); +#pragma pack(pop) + +struct MCUCommandResponse { + InputReportNfcIr input_report; + INSERT_PADDING_BYTES(0x8); + MCUReport mcu_report; + std::array mcu_data; + u8 crc; +}; +static_assert(sizeof(MCUCommandResponse) == 0x170, "MCUCommandResponse is an invalid size"); + struct JoyconCallbacks { std::function on_battery_data; std::function on_color_data; diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index 5c0f717228..eeba829860 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.cpp +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp @@ -110,7 +110,7 @@ bool NfcProtocol::HasAmiibo() { DriverResult NfcProtocol::WaitUntilNfcIsReady() { constexpr std::size_t timeout_limit = 10; - std::vector output; + MCUCommandResponse output{}; std::size_t tries = 0; do { @@ -122,8 +122,9 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() { if (tries++ > timeout_limit) { return DriverResult::Timeout; } - } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[55] != 0x31 || - output[56] != 0x00); + } while (output.mcu_report != MCUReport::NFCState || + (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || + output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00); return DriverResult::Success; } @@ -131,7 +132,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() { DriverResult NfcProtocol::StartPolling(TagFoundData& data) { LOG_DEBUG(Input, "Start Polling for tag"); constexpr std::size_t timeout_limit = 7; - std::vector output; + MCUCommandResponse output{}; std::size_t tries = 0; do { @@ -142,18 +143,20 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data) { if (tries++ > timeout_limit) { return DriverResult::Timeout; } - } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[56] != 0x09); + } while (output.mcu_report != MCUReport::NFCState || + (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || + output.mcu_data[6] != 0x09); - data.type = output[62]; - data.uuid.resize(output[64]); - memcpy(data.uuid.data(), output.data() + 65, data.uuid.size()); + data.type = output.mcu_data[12]; + data.uuid.resize(output.mcu_data[14]); + memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size()); return DriverResult::Success; } DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { constexpr std::size_t timeout_limit = 10; - std::vector output; + MCUCommandResponse output{}; std::size_t tries = 0; std::string uuid_string; @@ -168,23 +171,24 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { // Read Tag data while (true) { auto result = SendReadAmiiboRequest(output, ntag_pages); - const auto mcu_report = static_cast(output[49]); - const auto nfc_status = static_cast(output[56]); + const auto nfc_status = static_cast(output.mcu_data[6]); if (result != DriverResult::Success) { return result; } - if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && + if ((output.mcu_report == MCUReport::NFCReadData || + output.mcu_report == MCUReport::NFCState) && nfc_status == NFCStatus::TagLost) { return DriverResult::ErrorReadingData; } - if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07 && output[52] == 0x01) { + if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 && + output.mcu_data[2] == 0x01) { if (data.type != 2) { continue; } - switch (output[74]) { + switch (output.mcu_data[24]) { case 0: ntag_pages = NFCPages::Block135; break; @@ -200,14 +204,14 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { continue; } - if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { + if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { // finished SendStopPollingRequest(output); return DriverResult::Success; } // Ignore other state reports - if (mcu_report == MCUReport::NFCState) { + if (output.mcu_report == MCUReport::NFCState) { continue; } @@ -221,7 +225,7 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { DriverResult NfcProtocol::GetAmiiboData(std::vector& ntag_data) { constexpr std::size_t timeout_limit = 10; - std::vector output; + MCUCommandResponse output{}; std::size_t tries = 0; NFCPages ntag_pages = NFCPages::Block135; @@ -229,36 +233,38 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector& ntag_data) { // Read Tag data while (true) { auto result = SendReadAmiiboRequest(output, ntag_pages); - const auto mcu_report = static_cast(output[49]); - const auto nfc_status = static_cast(output[56]); + const auto nfc_status = static_cast(output.mcu_data[6]); if (result != DriverResult::Success) { return result; } - if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && + if ((output.mcu_report == MCUReport::NFCReadData || + output.mcu_report == MCUReport::NFCState) && nfc_status == NFCStatus::TagLost) { return DriverResult::ErrorReadingData; } - if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07) { - std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF; - if (output[52] == 0x01) { - memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, payload_size - 60); + if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) { + std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF; + if (output.mcu_data[2] == 0x01) { + memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66, + payload_size - 60); ntag_buffer_pos += payload_size - 60; } else { - memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size); + memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6, + payload_size); } continue; } - if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { + if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { LOG_INFO(Input, "Finished reading amiibo"); return DriverResult::Success; } // Ignore other state reports - if (mcu_report == MCUReport::NFCState) { + if (output.mcu_report == MCUReport::NFCState) { continue; } @@ -270,7 +276,7 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector& ntag_data) { return DriverResult::Success; } -DriverResult NfcProtocol::SendStartPollingRequest(std::vector& output) { +DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) { NFCRequestState request{ .sub_command = MCUSubCommand::ReadDeviceMode, .command_argument = NFCReadCommand::StartPolling, @@ -294,7 +300,7 @@ DriverResult NfcProtocol::SendStartPollingRequest(std::vector& output) { return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); } -DriverResult NfcProtocol::SendStopPollingRequest(std::vector& output) { +DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) { NFCRequestState request{ .sub_command = MCUSubCommand::ReadDeviceMode, .command_argument = NFCReadCommand::StopPolling, @@ -311,7 +317,7 @@ DriverResult NfcProtocol::SendStopPollingRequest(std::vector& output) { return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); } -DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector& output) { +DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) { NFCRequestState request{ .sub_command = MCUSubCommand::ReadDeviceMode, .command_argument = NFCReadCommand::StartWaitingRecieve, @@ -328,7 +334,7 @@ DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector& output return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); } -DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector& output, NFCPages ntag_pages) { +DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) { NFCRequestState request{ .sub_command = MCUSubCommand::ReadDeviceMode, .command_argument = NFCReadCommand::Ntag, diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index e63665aa97..11e263e077 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.h +++ b/src/input_common/helpers/joycon_protocol/nfc.h @@ -45,13 +45,13 @@ private: DriverResult GetAmiiboData(std::vector& data); - DriverResult SendStartPollingRequest(std::vector& output); + DriverResult SendStartPollingRequest(MCUCommandResponse& output); - DriverResult SendStopPollingRequest(std::vector& output); + DriverResult SendStopPollingRequest(MCUCommandResponse& output); - DriverResult SendStartWaitingRecieveRequest(std::vector& output); + DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output); - DriverResult SendReadAmiiboRequest(std::vector& output, NFCPages ntag_pages); + DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages); NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp index 3c2ca4379f..190cef8129 100644 --- a/src/input_common/helpers/joycon_protocol/ringcon.cpp +++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp @@ -70,7 +70,7 @@ DriverResult RingConProtocol::StartRingconPolling() { DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { LOG_DEBUG(Input, "IsRingConnected"); constexpr std::size_t max_tries = 28; - std::vector output; + SubCommandResponse output{}; std::size_t tries = 0; is_connected = false; @@ -84,7 +84,7 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { if (tries++ >= max_tries) { return DriverResult::NoDeviceDetected; } - } while (output[16] != static_cast(ExternalDeviceId::RingController)); + } while (output.external_device_id != ExternalDeviceId::RingController); is_connected = true; return DriverResult::Success;