Эх сурвалжийг харах

Merge pull request #9701 from german77/common_protocol

input_common: joycon: Remove Magic numbers from common protocol
liamwhite 3 жил өмнө
parent
commit
e74660673b

+ 1 - 3
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

+ 1 - 1
src/input_common/drivers/joycon.h

@@ -15,7 +15,7 @@ using SerialNumber = std::array<u8, 15>;
 struct Battery;
 struct Color;
 struct MotionData;
-enum class ControllerType;
+enum class ControllerType : u8;
 enum class DriverResult;
 enum class IrsResolution;
 class JoyconDriver;

+ 9 - 9
src/input_common/helpers/joycon_driver.cpp

@@ -162,14 +162,14 @@ void JoyconDriver::InputThread(std::stop_token stop_token) {
 }
 
 void JoyconDriver::OnNewData(std::span<u8> buffer) {
-    const auto report_mode = static_cast<InputReport>(buffer[0]);
+    const auto report_mode = static_cast<ReportMode>(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<u64>(
             std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
@@ -190,7 +190,7 @@ void JoyconDriver::OnNewData(std::span<u8> 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<u8> 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:

+ 83 - 72
src/input_common/helpers/joycon_protocol/common_protocol.cpp

@@ -22,12 +22,9 @@ void JoyconCommonProtocol::SetNonBlocking() {
 }
 
 DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
-    std::array<u8, 1> 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<ControllerType>(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<const u8> buffer) {
+DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> 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<const u8> buffer) {
     return DriverResult::Success;
 }
 
-DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vector<u8>& 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<u8*>(&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<u8>(sc));
-
-    if (output[0] != 0x21 && output[14] != static_cast<u8>(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<const u8> buffer,
-                                                  std::vector<u8>& output) {
-    std::vector<u8> local_buffer(MaxResponseSize);
-
-    local_buffer[0] = static_cast<u8>(OutputReport::RUMBLE_AND_SUBCMD);
-    local_buffer[1] = GetCounter();
-    local_buffer[10] = static_cast<u8>(sc);
-    for (std::size_t i = 0; i < buffer.size(); ++i) {
-        local_buffer[11 + i] = buffer[i];
+                                                  SubCommandResponse& output) {
+    SubCommandPacket packet{
+        .output_report = OutputReport::RUMBLE_AND_SUBCMD,
+        .packet_counter = GetCounter(),
+        .sub_command = sc,
+        .command_data = {},
+    };
+
+    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<const
 }
 
 DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
-    std::vector<u8> output;
+    SubCommandResponse output{};
     return SendSubCommand(sc, buffer, output);
 }
 
 DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) {
-    std::vector<u8> local_buffer(MaxResponseSize);
-
-    local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA);
-    local_buffer[1] = GetCounter();
-    local_buffer[10] = static_cast<u8>(sc);
-    for (std::size_t i = 0; i < buffer.size(); ++i) {
-        local_buffer[11 + i] = buffer[i];
+    SubCommandPacket packet{
+        .output_report = OutputReport::MCU_DATA,
+        .packet_counter = GetCounter(),
+        .sub_command = sc,
+        .command_data = {},
+    };
+
+    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<const u8> buffer) {
-    std::vector<u8> local_buffer(MaxResponseSize);
-
-    local_buffer[0] = static_cast<u8>(Joycon::OutputReport::RUMBLE_ONLY);
-    local_buffer[1] = GetCounter();
+    VibrationPacket packet{
+        .output_report = OutputReport::RUMBLE_ONLY,
+        .packet_counter = GetCounter(),
+        .vibration_data = {},
+    };
+
+    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<u8> 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<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, static_cast<u8>(size)};
-    std::vector<u8> local_buffer{};
-
-    buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF);
-    buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8);
+    SubCommandResponse response{};
+    std::array<u8, sizeof(ReadSpiPacket)> buffer{};
+    const ReadSpiPacket packet_data{
+        .spi_address = addr,
+        .size = static_cast<u8>(output.size()),
+    };
+
+    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<u8> 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<u8>& output) {
-    const int report_mode = static_cast<u8>(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<u8*>(&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<const u8> buffer,
-                                               std::vector<u8>& output) {
-    std::vector<u8> local_buffer(MaxResponseSize);
-
-    local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA);
-    local_buffer[1] = GetCounter();
-    local_buffer[9] = static_cast<u8>(sc);
-    for (std::size_t i = 0; i < buffer.size(); ++i) {
-        local_buffer[10 + i] = buffer[i];
+                                               MCUCommandResponse& output) {
+    SubCommandPacket packet{
+        .output_report = OutputReport::MCU_DATA,
+        .packet_counter = GetCounter(),
+        .sub_command = sc,
+        .command_data = {},
+    };
+
+    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<u8> 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<u8>(mode));
+    } while (output.mcu_report != MCUReport::StateReport ||
+             output.mcu_data[6] != static_cast<u8>(mode));
 
     return DriverResult::Success;
 }

+ 20 - 11
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<const u8> buffer);
+    DriverResult SendRawData(std::span<const u8> buffer);
+
+    template <typename Output>
+        requires std::is_trivially_copyable_v<Output>
+    DriverResult SendData(const Output& output) {
+        std::array<u8, sizeof(Output)> 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<u8>& 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<const u8> buffer, std::vector<u8>& output);
+    DriverResult SendSubCommand(SubCommand sc, std::span<const u8> 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<u8> 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 <typename Output>
         requires std::is_trivially_copyable_v<Output>
@@ -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<u8>& 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<const u8> buffer,
-                             std::vector<u8>& output);
+                             MCUCommandResponse& output);
 
     /**
      * Wait's until the MCU chip is on the specified mode

+ 2 - 2
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<u8> output;
+    SubCommandResponse output{};
 
     const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
 
     device_info = {};
     if (result == DriverResult::Success) {
-        memcpy(&device_info, output.data(), sizeof(DeviceInfo));
+        device_info = output.device_info;
     }
 
     return result;

+ 7 - 6
src/input_common/helpers/joycon_protocol/irs.cpp

@@ -132,7 +132,7 @@ DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
 DriverResult IrsProtocol::ConfigureIrs() {
     LOG_DEBUG(Input, "Configure IRS");
     constexpr std::size_t max_tries = 28;
-    std::vector<u8> 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<u8> 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<u8> 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;
 }

+ 98 - 38
src/input_common/helpers/joycon_protocol/joycon_types.h

@@ -19,20 +19,24 @@
 namespace InputCommon::Joycon {
 constexpr u32 MaxErrorCount = 50;
 constexpr u32 MaxBufferSize = 368;
-constexpr u32 MaxResponseSize = 49;
-constexpr u32 MaxSubCommandResponseSize = 64;
 constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
 
 using MacAddress = std::array<u8, 6>;
 using SerialNumber = std::array<u8, 15>;
 
-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 {
@@ -99,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,
@@ -143,9 +139,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 {
@@ -164,20 +161,26 @@ enum class CalibrationMagic : u8 {
     USR_MAGIC_1 = 0xA1,
 };
 
-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,
+enum class SpiAddress : u16 {
+    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 {
@@ -185,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 {
@@ -359,10 +364,16 @@ enum class IrRegistersAddress : u16 {
     DenoiseColor = 0x6901,
 };
 
+enum class ExternalDeviceId : u16 {
+    RingController = 0x2000,
+    Starlink = 0x2800,
+};
+
 enum class DriverResult {
     Success,
     WrongReply,
     Timeout,
+    InvalidParameters,
     UnsupportedControllerType,
     HandleInUse,
     ErrorReadingData,
@@ -485,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<u8, 10> unknown_data;
@@ -493,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<u8, 3> button_input;
@@ -507,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<u8, 3> button_input;
@@ -605,9 +616,11 @@ static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid siz
 
 struct DeviceInfo {
     FirmwareVersion firmware;
+    std::array<u8, 2> unknown_1;
     MacAddress mac_address;
+    std::array<u8, 2> 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;
@@ -623,6 +636,53 @@ struct RingStatus {
     s16 min_value;
 };
 
+struct VibrationPacket {
+    OutputReport output_report;
+    u8 packet_counter;
+    std::array<u8, 0x8> 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<u8, 0x26> 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<u8, 0x30> 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<u8, 0x13D> mcu_data;
+    u8 crc;
+};
+static_assert(sizeof(MCUCommandResponse) == 0x170, "MCUCommandResponse is an invalid size");
+
 struct JoyconCallbacks {
     std::function<void(Battery)> on_battery_data;
     std::function<void(Color)> on_color_data;

+ 37 - 31
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<u8> 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<u8> 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<u8> 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<MCUReport>(output[49]);
-        const auto nfc_status = static_cast<NFCStatus>(output[56]);
+        const auto nfc_status = static_cast<NFCStatus>(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<u8>& ntag_data) {
     constexpr std::size_t timeout_limit = 10;
-    std::vector<u8> output;
+    MCUCommandResponse output{};
     std::size_t tries = 0;
 
     NFCPages ntag_pages = NFCPages::Block135;
@@ -229,36 +233,38 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
     // Read Tag data
     while (true) {
         auto result = SendReadAmiiboRequest(output, ntag_pages);
-        const auto mcu_report = static_cast<MCUReport>(output[49]);
-        const auto nfc_status = static_cast<NFCStatus>(output[56]);
+        const auto nfc_status = static_cast<NFCStatus>(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<u8>& ntag_data) {
     return DriverResult::Success;
 }
 
-DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& 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<u8>& output) {
     return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
 }
 
-DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& 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<u8>& output) {
     return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
 }
 
-DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& 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<u8>& output
     return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
 }
 
-DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages) {
+DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
     NFCRequestState request{
         .sub_command = MCUSubCommand::ReadDeviceMode,
         .command_argument = NFCReadCommand::Ntag,

+ 4 - 4
src/input_common/helpers/joycon_protocol/nfc.h

@@ -45,13 +45,13 @@ private:
 
     DriverResult GetAmiiboData(std::vector<u8>& data);
 
-    DriverResult SendStartPollingRequest(std::vector<u8>& output);
+    DriverResult SendStartPollingRequest(MCUCommandResponse& output);
 
-    DriverResult SendStopPollingRequest(std::vector<u8>& output);
+    DriverResult SendStopPollingRequest(MCUCommandResponse& output);
 
-    DriverResult SendStartWaitingRecieveRequest(std::vector<u8>& output);
+    DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
 
-    DriverResult SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages);
+    DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
 
     NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
 

+ 2 - 6
src/input_common/helpers/joycon_protocol/poller.cpp

@@ -31,9 +31,7 @@ void JoyconPoller::ReadActiveMode(std::span<u8> 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<u8> buffer) {
     case Joycon::ControllerType::Pro:
         UpdatePasiveProPadInput(data);
         break;
-    case Joycon::ControllerType::Grip:
-    case Joycon::ControllerType::Dual:
-    case Joycon::ControllerType::None:
+    default:
         break;
     }
 }

+ 5 - 7
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<u8> output;
+    SubCommandResponse output{};
     std::size_t tries = 0;
     is_connected = false;
 
     do {
-        std::array<u8, 1> 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.external_device_id != 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<u8, 4> 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 {