diff options
Diffstat (limited to '')
13 files changed, 485 insertions, 322 deletions
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 3775e2d35..8f94c9f45 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<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: diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp index f6e7e97d5..d8f040f75 100644 --- a/src/input_common/helpers/joycon_protocol/calibration.cpp +++ b/src/input_common/helpers/joycon_protocol/calibration.cpp @@ -13,33 +13,33 @@ CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle) DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { ScopedSetBlocking sb(this); - std::vector<u8> buffer; DriverResult result{DriverResult::Success}; + JoystickLeftSpiCalibration spi_calibration{}; + bool has_user_calibration = false; calibration = {}; - result = ReadSPI(CalAddr::USER_LEFT_MAGIC, sizeof(u16), buffer); - if (result == DriverResult::Success) { - const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; - if (has_user_calibration) { - result = ReadSPI(CalAddr::USER_LEFT_DATA, 9, buffer); - } else { - result = ReadSPI(CalAddr::FACT_LEFT_DATA, 9, buffer); - } + result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration); } - if (result == DriverResult::Success) { - calibration.x.max = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); - calibration.y.max = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); - calibration.x.center = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); - calibration.y.center = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); - calibration.x.min = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); - calibration.y.min = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); + // Read User defined calibration + if (result == DriverResult::Success && has_user_calibration) { + result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration); } - // Nintendo fix for drifting stick - // result = ReadSPI(0x60, 0x86 ,buffer, 16); - // calibration.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); + // Read Factory calibration + if (result == DriverResult::Success && !has_user_calibration) { + result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration); + } + + if (result == DriverResult::Success) { + calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center); + calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center); + calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min); + calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min); + calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max); + calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max); + } // Set a valid default calibration if data is missing ValidateCalibration(calibration); @@ -49,33 +49,33 @@ DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { ScopedSetBlocking sb(this); - std::vector<u8> buffer; DriverResult result{DriverResult::Success}; + JoystickRightSpiCalibration spi_calibration{}; + bool has_user_calibration = false; calibration = {}; - result = ReadSPI(CalAddr::USER_RIGHT_MAGIC, sizeof(u16), buffer); - if (result == DriverResult::Success) { - const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; - if (has_user_calibration) { - result = ReadSPI(CalAddr::USER_RIGHT_DATA, 9, buffer); - } else { - result = ReadSPI(CalAddr::FACT_RIGHT_DATA, 9, buffer); - } + result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration); } - if (result == DriverResult::Success) { - calibration.x.center = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); - calibration.y.center = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); - calibration.x.min = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); - calibration.y.min = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); - calibration.x.max = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); - calibration.y.max = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); + // Read User defined calibration + if (result == DriverResult::Success && has_user_calibration) { + result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration); + } + + // Read Factory calibration + if (result == DriverResult::Success && !has_user_calibration) { + result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration); } - // Nintendo fix for drifting stick - // buffer = ReadSPI(0x60, 0x98 , 16); - // joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); + if (result == DriverResult::Success) { + calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center); + calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center); + calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min); + calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min); + calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max); + calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max); + } // Set a valid default calibration if data is missing ValidateCalibration(calibration); @@ -85,39 +85,41 @@ DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibratio DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { ScopedSetBlocking sb(this); - std::vector<u8> buffer; DriverResult result{DriverResult::Success}; + ImuSpiCalibration spi_calibration{}; + bool has_user_calibration = false; calibration = {}; - result = ReadSPI(CalAddr::USER_IMU_MAGIC, sizeof(u16), buffer); - if (result == DriverResult::Success) { - const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; - if (has_user_calibration) { - result = ReadSPI(CalAddr::USER_IMU_DATA, sizeof(IMUCalibration), buffer); - } else { - result = ReadSPI(CalAddr::FACT_IMU_DATA, sizeof(IMUCalibration), buffer); - } + result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration); + } + + // Read User defined calibration + if (result == DriverResult::Success && has_user_calibration) { + result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration); + } + + // Read Factory calibration + if (result == DriverResult::Success && !has_user_calibration) { + result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration); } if (result == DriverResult::Success) { - IMUCalibration device_calibration{}; - memcpy(&device_calibration, buffer.data(), sizeof(IMUCalibration)); - calibration.accelerometer[0].offset = device_calibration.accelerometer_offset[0]; - calibration.accelerometer[1].offset = device_calibration.accelerometer_offset[1]; - calibration.accelerometer[2].offset = device_calibration.accelerometer_offset[2]; + calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0]; + calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1]; + calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2]; - calibration.accelerometer[0].scale = device_calibration.accelerometer_scale[0]; - calibration.accelerometer[1].scale = device_calibration.accelerometer_scale[1]; - calibration.accelerometer[2].scale = device_calibration.accelerometer_scale[2]; + calibration.accelerometer[0].scale = spi_calibration.accelerometer_scale[0]; + calibration.accelerometer[1].scale = spi_calibration.accelerometer_scale[1]; + calibration.accelerometer[2].scale = spi_calibration.accelerometer_scale[2]; - calibration.gyro[0].offset = device_calibration.gyroscope_offset[0]; - calibration.gyro[1].offset = device_calibration.gyroscope_offset[1]; - calibration.gyro[2].offset = device_calibration.gyroscope_offset[2]; + calibration.gyro[0].offset = spi_calibration.gyroscope_offset[0]; + calibration.gyro[1].offset = spi_calibration.gyroscope_offset[1]; + calibration.gyro[2].offset = spi_calibration.gyroscope_offset[2]; - calibration.gyro[0].scale = device_calibration.gyroscope_scale[0]; - calibration.gyro[1].scale = device_calibration.gyroscope_scale[1]; - calibration.gyro[2].scale = device_calibration.gyroscope_scale[2]; + calibration.gyro[0].scale = spi_calibration.gyroscope_scale[0]; + calibration.gyro[1].scale = spi_calibration.gyroscope_scale[1]; + calibration.gyro[2].scale = spi_calibration.gyroscope_scale[2]; } ValidateCalibration(calibration); @@ -127,10 +129,12 @@ DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibrati DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, s16 current_value) { + constexpr s16 DefaultRingRange{800}; + // TODO: Get default calibration form ring itself if (ring_data_max == 0 && ring_data_min == 0) { - ring_data_max = current_value + 800; - ring_data_min = current_value - 800; + ring_data_max = current_value + DefaultRingRange; + ring_data_min = current_value - DefaultRingRange; ring_data_default = current_value; } ring_data_max = std::max(ring_data_max, current_value); @@ -143,42 +147,72 @@ DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibratio return DriverResult::Success; } +DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address, + bool& has_user_calibration) { + MagicSpiCalibration spi_magic{}; + const DriverResult result{ReadSPI(address, spi_magic)}; + has_user_calibration = false; + if (result == DriverResult::Success) { + has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 && + spi_magic.second == CalibrationMagic::USR_MAGIC_1; + } + return result; +} + +u16 CalibrationProtocol::GetXAxisCalibrationValue(std::span<u8> block) const { + return static_cast<u16>(((block[1] & 0x0F) << 8) | block[0]); +} + +u16 CalibrationProtocol::GetYAxisCalibrationValue(std::span<u8> block) const { + return static_cast<u16>((block[2] << 4) | (block[1] >> 4)); +} + void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) { - constexpr u16 DefaultStickCenter{2048}; - constexpr u16 DefaultStickRange{1740}; + constexpr u16 DefaultStickCenter{0x800}; + constexpr u16 DefaultStickRange{0x6cc}; - if (calibration.x.center == 0xFFF || calibration.x.center == 0) { - calibration.x.center = DefaultStickCenter; - } - if (calibration.x.max == 0xFFF || calibration.x.max == 0) { - calibration.x.max = DefaultStickRange; + calibration.x.center = ValidateValue(calibration.x.center, DefaultStickCenter); + calibration.x.max = ValidateValue(calibration.x.max, DefaultStickRange); + calibration.x.min = ValidateValue(calibration.x.min, DefaultStickRange); + + calibration.y.center = ValidateValue(calibration.y.center, DefaultStickCenter); + calibration.y.max = ValidateValue(calibration.y.max, DefaultStickRange); + calibration.y.min = ValidateValue(calibration.y.min, DefaultStickRange); +} + +void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) { + constexpr s16 DefaultAccelerometerScale{0x4000}; + constexpr s16 DefaultGyroScale{0x3be7}; + constexpr s16 DefaultOffset{0}; + + for (auto& sensor : calibration.accelerometer) { + sensor.scale = ValidateValue(sensor.scale, DefaultAccelerometerScale); + sensor.offset = ValidateValue(sensor.offset, DefaultOffset); } - if (calibration.x.min == 0xFFF || calibration.x.min == 0) { - calibration.x.min = DefaultStickRange; + for (auto& sensor : calibration.gyro) { + sensor.scale = ValidateValue(sensor.scale, DefaultGyroScale); + sensor.offset = ValidateValue(sensor.offset, DefaultOffset); } +} - if (calibration.y.center == 0xFFF || calibration.y.center == 0) { - calibration.y.center = DefaultStickCenter; - } - if (calibration.y.max == 0xFFF || calibration.y.max == 0) { - calibration.y.max = DefaultStickRange; +u16 CalibrationProtocol::ValidateValue(u16 value, u16 default_value) const { + if (value == 0) { + return default_value; } - if (calibration.y.min == 0xFFF || calibration.y.min == 0) { - calibration.y.min = DefaultStickRange; + if (value == 0xFFF) { + return default_value; } + return value; } -void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) { - for (auto& sensor : calibration.accelerometer) { - if (sensor.scale == 0) { - sensor.scale = 0x4000; - } +s16 CalibrationProtocol::ValidateValue(s16 value, s16 default_value) const { + if (value == 0) { + return default_value; } - for (auto& sensor : calibration.gyro) { - if (sensor.scale == 0) { - sensor.scale = 0x3be7; - } + if (value == 0xFFF) { + return default_value; } + return value; } } // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h index afb52a36a..c6fd0f729 100644 --- a/src/input_common/helpers/joycon_protocol/calibration.h +++ b/src/input_common/helpers/joycon_protocol/calibration.h @@ -53,9 +53,27 @@ public: DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); private: + /// Returns true if the specified address corresponds to the magic value of user calibration + DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration); + + /// Converts a raw calibration block to an u16 value containing the x axis value + u16 GetXAxisCalibrationValue(std::span<u8> block) const; + + /// Converts a raw calibration block to an u16 value containing the y axis value + u16 GetYAxisCalibrationValue(std::span<u8> block) const; + + /// Ensures that all joystick calibration values are set void ValidateCalibration(JoyStickCalibration& calibration); + + /// Ensures that all motion calibration values are set void ValidateCalibration(MotionCalibration& calibration); + /// Returns the default value if the value is either zero or 0xFFF + u16 ValidateValue(u16 value, u16 default_value) const; + + /// Returns the default value if the value is either zero or 0xFFF + s16 ValidateValue(s16 value, s16 default_value) const; + s16 ring_data_max = 0; s16 ring_data_default = 0; s16 ring_data_min = 0; diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index 417d0dcc5..2b42a4555 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::vector<u8> buffer; - const auto result = ReadSPI(CalAddr::DEVICE_TYPE, 1, 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,44 +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::ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output) { +DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { + constexpr std::size_t HeaderSize = 5; constexpr std::size_t MaxTries = 10; std::size_t tries = 0; - std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, size}; - std::vector<u8> local_buffer(size + 20); - - 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; } @@ -165,10 +177,14 @@ DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8 if (tries++ > MaxTries) { return DriverResult::Timeout; } - } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]); + } while (response.spi_address != addr); + + if (response.command_data.size() < packet_data.size + HeaderSize) { + return DriverResult::WrongReply; + } // Remove header from output - output = std::vector<u8>(local_buffer.begin() + 20, local_buffer.begin() + 20 + size); + memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size); return DriverResult::Success; } @@ -177,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; @@ -192,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); @@ -215,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; @@ -248,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{}; @@ -263,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; } diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h index 903bcf402..f44f73ba4 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<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,10 +106,29 @@ public: /** * Reads the SPI memory stored on the joycon * @param Initial address location - * @param size in bytes to be read - * @returns output buffer containing the responce + * @returns output buffer containing the response */ - DriverResult ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output); + 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 response + */ + template <typename Output> + requires std::is_trivially_copyable_v<Output> + DriverResult ReadSPI(SpiAddress addr, Output& output) { + std::array<u8, sizeof(Output)> buffer; + output = {}; + + const auto result = ReadRawSPI(addr, buffer); + if (result != DriverResult::Success) { + return result; + } + + std::memcpy(&output, buffer.data(), sizeof(Output)); + return DriverResult::Success; + } /** * Enables MCU chip on the joycon @@ -117,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 diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp index 63cfb1369..548a4b9e3 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<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; @@ -71,8 +71,8 @@ DriverResult GenericProtocol::GetBattery(u32& battery_level) { DriverResult GenericProtocol::GetColor(Color& color) { ScopedSetBlocking sb(this); - std::vector<u8> buffer; - const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer); + std::array<u8, 12> buffer{}; + const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer); color = {}; if (result == DriverResult::Success) { @@ -87,8 +87,8 @@ DriverResult GenericProtocol::GetColor(Color& color) { DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { ScopedSetBlocking sb(this); - std::vector<u8> buffer; - const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer); + std::array<u8, 16> buffer{}; + const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer); serial_number = {}; if (result == DriverResult::Success) { diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp index 09e17bc5b..731fd5981 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<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; } diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 182d2c15b..b91934990 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/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 { @@ -159,26 +156,31 @@ enum class UsbSubCommand : u8 { SEND_UART = 0x92, }; -enum class CalMagic : u8 { +enum class CalibrationMagic : u8 { USR_MAGIC_0 = 0xB2, USR_MAGIC_1 = 0xA1, - USRR_MAGI_SIZE = 2, }; -enum class CalAddr { - 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 { @@ -186,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 { @@ -360,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, @@ -396,10 +406,35 @@ struct MotionData { u64 delta_timestamp{}; }; +// Output from SPI read command containing user calibration magic +struct MagicSpiCalibration { + CalibrationMagic first; + CalibrationMagic second; +}; +static_assert(sizeof(MagicSpiCalibration) == 0x2, "MagicSpiCalibration is an invalid size"); + +// Output from SPI read command containing left joystick calibration +struct JoystickLeftSpiCalibration { + std::array<u8, 3> max; + std::array<u8, 3> center; + std::array<u8, 3> min; +}; +static_assert(sizeof(JoystickLeftSpiCalibration) == 0x9, + "JoystickLeftSpiCalibration is an invalid size"); + +// Output from SPI read command containing right joystick calibration +struct JoystickRightSpiCalibration { + std::array<u8, 3> center; + std::array<u8, 3> min; + std::array<u8, 3> max; +}; +static_assert(sizeof(JoystickRightSpiCalibration) == 0x9, + "JoystickRightSpiCalibration is an invalid size"); + struct JoyStickAxisCalibration { - u16 max{1}; - u16 min{1}; - u16 center{0}; + u16 max; + u16 min; + u16 center; }; struct JoyStickCalibration { @@ -407,6 +442,14 @@ struct JoyStickCalibration { JoyStickAxisCalibration y; }; +struct ImuSpiCalibration { + std::array<s16, 3> accelerometer_offset; + std::array<s16, 3> accelerometer_scale; + std::array<s16, 3> gyroscope_offset; + std::array<s16, 3> gyroscope_scale; +}; +static_assert(sizeof(ImuSpiCalibration) == 0x18, "ImuSpiCalibration is an invalid size"); + struct RingCalibration { s16 default_value; s16 max_value; @@ -453,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; @@ -461,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; @@ -475,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; @@ -488,14 +531,6 @@ struct InputReportNfcIr { static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size"); #pragma pack(pop) -struct IMUCalibration { - std::array<s16, 3> accelerometer_offset; - std::array<s16, 3> accelerometer_scale; - std::array<s16, 3> gyroscope_offset; - std::array<s16, 3> gyroscope_scale; -}; -static_assert(sizeof(IMUCalibration) == 0x18, "IMUCalibration is an invalid size"); - struct NFCReadBlock { u8 start; u8 end; @@ -581,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; @@ -599,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; diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index 5c0f71722..eeba82986 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<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, diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index e63665aa9..11e263e07 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<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; diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index 7f8e093fa..9bb15e935 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<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; } } diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp index 12f81309e..190cef812 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<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 { diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp index 096c23b07..a6be6dac1 100644 --- a/src/input_common/helpers/stick_from_buttons.cpp +++ b/src/input_common/helpers/stick_from_buttons.cpp @@ -15,6 +15,9 @@ public: // do not play nicely with the theoretical maximum range. // Using a value one lower from the maximum emulates real stick behavior. static constexpr float MAX_RANGE = 32766.0f / 32767.0f; + static constexpr float TAU = Common::PI * 2.0f; + // Use wider angle to ease the transition. + static constexpr float APERTURE = TAU * 0.15f; using Button = std::unique_ptr<Common::Input::InputDevice>; @@ -61,30 +64,23 @@ public: } bool IsAngleGreater(float old_angle, float new_angle) const { - constexpr float TAU = Common::PI * 2.0f; - // Use wider angle to ease the transition. - constexpr float aperture = TAU * 0.15f; - const float top_limit = new_angle + aperture; + const float top_limit = new_angle + APERTURE; return (old_angle > new_angle && old_angle <= top_limit) || (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); } bool IsAngleSmaller(float old_angle, float new_angle) const { - constexpr float TAU = Common::PI * 2.0f; - // Use wider angle to ease the transition. - constexpr float aperture = TAU * 0.15f; - const float bottom_limit = new_angle - aperture; + const float bottom_limit = new_angle - APERTURE; return (old_angle >= bottom_limit && old_angle < new_angle) || (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); } float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { - constexpr float TAU = Common::PI * 2.0f; float new_angle = angle; auto time_difference = static_cast<float>( - std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); - time_difference /= 1000.0f * 1000.0f; + std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count()); + time_difference /= 1000.0f; if (time_difference > 0.5f) { time_difference = 0.5f; } @@ -201,8 +197,6 @@ public: } void UpdateStatus() { - const float coef = modifier_status.value ? modifier_scale : MAX_RANGE; - bool r = right_status; bool l = left_status; bool u = up_status; @@ -220,7 +214,7 @@ public: // Move if a key is pressed if (r || l || u || d) { - amplitude = coef; + amplitude = modifier_status.value ? modifier_scale : MAX_RANGE; } else { amplitude = 0; } @@ -274,30 +268,17 @@ public: Common::Input::StickStatus status{}; status.x.properties = properties; status.y.properties = properties; + if (Settings::values.emulate_analog_keyboard) { const auto now = std::chrono::steady_clock::now(); - float angle_ = GetAngle(now); + const float angle_ = GetAngle(now); status.x.raw_value = std::cos(angle_) * amplitude; status.y.raw_value = std::sin(angle_) * amplitude; return status; } - constexpr float SQRT_HALF = 0.707106781f; - int x = 0, y = 0; - if (right_status) { - ++x; - } - if (left_status) { - --x; - } - if (up_status) { - ++y; - } - if (down_status) { - --y; - } - const float coef = modifier_status.value ? modifier_scale : MAX_RANGE; - status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); - status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); + + status.x.raw_value = std::cos(goal_angle) * amplitude; + status.y.raw_value = std::sin(goal_angle) * amplitude; return status; } |