1
0
Fork 0
forked from suyu/suyu

input_common: joycon: Remove Magic numbers from common protocol

This commit is contained in:
Narr the Reg 2023-01-27 22:30:44 -06:00 committed by german77
parent 11fea5deea
commit c318a4c80b
9 changed files with 216 additions and 149 deletions

View file

@ -162,14 +162,14 @@ void JoyconDriver::InputThread(std::stop_token stop_token) {
} }
void JoyconDriver::OnNewData(std::span<u8> buffer) { 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 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion
// experience // experience
switch (report_mode) { switch (report_mode) {
case InputReport::STANDARD_FULL_60HZ: case ReportMode::STANDARD_FULL_60HZ:
case InputReport::NFC_IR_MODE_60HZ: case ReportMode::NFC_IR_MODE_60HZ:
case InputReport::SIMPLE_HID_MODE: { case ReportMode::SIMPLE_HID_MODE: {
const auto now = std::chrono::steady_clock::now(); const auto now = std::chrono::steady_clock::now();
const auto new_delta_time = static_cast<u64>( const auto new_delta_time = static_cast<u64>(
std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); 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 // 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{}; InputReportActive data{};
memcpy(&data, buffer.data(), sizeof(InputReportActive)); memcpy(&data, buffer.data(), sizeof(InputReportActive));
calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input); calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input);
@ -228,16 +228,16 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
} }
switch (report_mode) { switch (report_mode) {
case InputReport::STANDARD_FULL_60HZ: case ReportMode::STANDARD_FULL_60HZ:
joycon_poller->ReadActiveMode(buffer, motion_status, ring_status); joycon_poller->ReadActiveMode(buffer, motion_status, ring_status);
break; break;
case InputReport::NFC_IR_MODE_60HZ: case ReportMode::NFC_IR_MODE_60HZ:
joycon_poller->ReadNfcIRMode(buffer, motion_status); joycon_poller->ReadNfcIRMode(buffer, motion_status);
break; break;
case InputReport::SIMPLE_HID_MODE: case ReportMode::SIMPLE_HID_MODE:
joycon_poller->ReadPassiveMode(buffer); joycon_poller->ReadPassiveMode(buffer);
break; break;
case InputReport::SUBCMD_REPLY: case ReportMode::SUBCMD_REPLY:
LOG_DEBUG(Input, "Unhandled command reply"); LOG_DEBUG(Input, "Unhandled command reply");
break; break;
default: default:

View file

@ -22,12 +22,9 @@ void JoyconCommonProtocol::SetNonBlocking() {
} }
DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
std::array<u8, 1> buffer{}; const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type);
const auto result = ReadRawSPI(SpiAddress::DEVICE_TYPE, buffer);
controller_type = ControllerType::None;
if (result == DriverResult::Success) { if (result == DriverResult::Success) {
controller_type = static_cast<ControllerType>(buffer[0]);
// Fallback to 3rd party pro controllers // Fallback to 3rd party pro controllers
if (controller_type == ControllerType::None) { if (controller_type == ControllerType::None) {
controller_type = ControllerType::Pro; controller_type = ControllerType::Pro;
@ -40,6 +37,7 @@ DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type
DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) { DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) {
ControllerType controller_type{ControllerType::None}; ControllerType controller_type{ControllerType::None};
const auto result = GetDeviceType(controller_type); const auto result = GetDeviceType(controller_type);
if (result != DriverResult::Success || controller_type == ControllerType::None) { if (result != DriverResult::Success || controller_type == ControllerType::None) {
return DriverResult::UnsupportedControllerType; return DriverResult::UnsupportedControllerType;
} }
@ -62,7 +60,7 @@ DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer); 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()); const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
if (result == -1) { if (result == -1) {
@ -72,15 +70,15 @@ DriverResult JoyconCommonProtocol::SendData(std::span<const u8> buffer) {
return DriverResult::Success; 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 timeout_mili = 66;
constexpr int MaxTries = 15; constexpr int MaxTries = 15;
int tries = 0; int tries = 0;
output.resize(MaxSubCommandResponseSize);
do { do {
int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
MaxSubCommandResponseSize, timeout_mili); sizeof(SubCommandResponse), timeout_mili);
if (result < 1) { if (result < 1) {
LOG_ERROR(Input, "No response from joycon"); LOG_ERROR(Input, "No response from joycon");
@ -88,27 +86,28 @@ DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vec
if (tries++ > MaxTries) { if (tries++ > MaxTries) {
return DriverResult::Timeout; return DriverResult::Timeout;
} }
} while (output[0] != 0x21 && output[14] != static_cast<u8>(sc)); } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY &&
output.sub_command != sc);
if (output[0] != 0x21 && output[14] != static_cast<u8>(sc)) {
return DriverResult::WrongReply;
}
return DriverResult::Success; return DriverResult::Success;
} }
DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer, DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer,
std::vector<u8>& output) { SubCommandResponse& output) {
std::vector<u8> local_buffer(MaxResponseSize); SubCommandPacket packet{
.output_report = OutputReport::RUMBLE_AND_SUBCMD,
.packet_counter = GetCounter(),
.sub_command = sc,
.command_data = {},
};
local_buffer[0] = static_cast<u8>(OutputReport::RUMBLE_AND_SUBCMD); if (buffer.size() > packet.command_data.size()) {
local_buffer[1] = GetCounter(); return DriverResult::InvalidParameters;
local_buffer[10] = static_cast<u8>(sc);
for (std::size_t i = 0; i < buffer.size(); ++i) {
local_buffer[11 + i] = buffer[i];
} }
auto result = SendData(local_buffer); memcpy(packet.command_data.data(), buffer.data(), buffer.size());
auto result = SendData(packet);
if (result != DriverResult::Success) { if (result != DriverResult::Success) {
return result; return result;
@ -120,46 +119,57 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const
} }
DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
std::vector<u8> output; SubCommandResponse output{};
return SendSubCommand(sc, buffer, output); return SendSubCommand(sc, buffer, output);
} }
DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) { DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) {
std::vector<u8> local_buffer(MaxResponseSize); SubCommandPacket packet{
.output_report = OutputReport::MCU_DATA,
.packet_counter = GetCounter(),
.sub_command = sc,
.command_data = {},
};
local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); if (buffer.size() > packet.command_data.size()) {
local_buffer[1] = GetCounter(); return DriverResult::InvalidParameters;
local_buffer[10] = static_cast<u8>(sc);
for (std::size_t i = 0; i < buffer.size(); ++i) {
local_buffer[11 + i] = buffer[i];
} }
return SendData(local_buffer); memcpy(packet.command_data.data(), buffer.data(), buffer.size());
return SendData(packet);
} }
DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) { DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
std::vector<u8> local_buffer(MaxResponseSize); VibrationPacket packet{
.output_report = OutputReport::RUMBLE_ONLY,
.packet_counter = GetCounter(),
.vibration_data = {},
};
local_buffer[0] = static_cast<u8>(Joycon::OutputReport::RUMBLE_ONLY); if (buffer.size() > packet.vibration_data.size()) {
local_buffer[1] = GetCounter(); 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) { 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; constexpr std::size_t MaxTries = 10;
const auto size = output.size();
std::size_t tries = 0; std::size_t tries = 0;
std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, static_cast<u8>(size)}; SubCommandResponse response{};
std::vector<u8> local_buffer{}; std::array<u8, sizeof(ReadSpiPacket)> buffer{};
const ReadSpiPacket packet_data{
.spi_address = addr,
.size = static_cast<u8>(output.size()),
};
buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF); memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket));
buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8);
do { 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) { if (result != DriverResult::Success) {
return result; return result;
} }
@ -167,14 +177,14 @@ DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> out
if (tries++ > MaxTries) { if (tries++ > MaxTries) {
return DriverResult::Timeout; 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; return DriverResult::WrongReply;
} }
// Remove header from output // 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; return DriverResult::Success;
} }
@ -183,7 +193,7 @@ DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state); const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
if (result != DriverResult::Success) { if (result != DriverResult::Success) {
LOG_ERROR(Input, "SendMCUData failed with error {}", result); LOG_ERROR(Input, "Failed with error {}", result);
} }
return result; return result;
@ -198,22 +208,21 @@ DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer); const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
if (result != DriverResult::Success) { if (result != DriverResult::Success) {
LOG_ERROR(Input, "Set MCU config failed with error {}", result); LOG_ERROR(Input, "Failed with error {}", result);
} }
return result; return result;
} }
DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_, DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
std::vector<u8>& output) { MCUCommandResponse& output) {
const int report_mode = static_cast<u8>(report_mode_);
constexpr int TimeoutMili = 200; constexpr int TimeoutMili = 200;
constexpr int MaxTries = 9; constexpr int MaxTries = 9;
int tries = 0; int tries = 0;
output.resize(0x170);
do { 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) { if (result < 1) {
LOG_ERROR(Input, "No response from joycon attempt {}", tries); LOG_ERROR(Input, "No response from joycon attempt {}", tries);
@ -221,28 +230,29 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_,
if (tries++ > MaxTries) { if (tries++ > MaxTries) {
return DriverResult::Timeout; return DriverResult::Timeout;
} }
} while (output[0] != report_mode || output[49] == 0xFF); } while (output.input_report.report_mode != report_mode ||
output.mcu_report == MCUReport::EmptyAwaitingCmd);
if (output[0] != report_mode || output[49] == 0xFF) {
return DriverResult::WrongReply;
}
return DriverResult::Success; return DriverResult::Success;
} }
DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc, DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
std::span<const u8> buffer, std::span<const u8> buffer,
std::vector<u8>& output) { MCUCommandResponse& output) {
std::vector<u8> local_buffer(MaxResponseSize); SubCommandPacket packet{
.output_report = OutputReport::MCU_DATA,
.packet_counter = GetCounter(),
.sub_command = sc,
.command_data = {},
};
local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); if (buffer.size() > packet.command_data.size()) {
local_buffer[1] = GetCounter(); return DriverResult::InvalidParameters;
local_buffer[9] = static_cast<u8>(sc);
for (std::size_t i = 0; i < buffer.size(); ++i) {
local_buffer[10 + i] = buffer[i];
} }
auto result = SendData(local_buffer); memcpy(packet.command_data.data(), buffer.data(), buffer.size());
auto result = SendData(packet);
if (result != DriverResult::Success) { if (result != DriverResult::Success) {
return result; return result;
@ -254,7 +264,7 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubComman
} }
DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) { DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
std::vector<u8> output; MCUCommandResponse output{};
constexpr std::size_t MaxTries{8}; constexpr std::size_t MaxTries{8};
std::size_t tries{}; std::size_t tries{};
@ -269,7 +279,8 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod
if (tries++ > MaxTries) { if (tries++ > MaxTries) {
return DriverResult::WrongReply; 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; return DriverResult::Success;
} }

View file

@ -57,22 +57,31 @@ public:
* Sends data to the joycon device * Sends data to the joycon device
* @param buffer data to be send * @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 * Waits for incoming data of the joycon device that matchs the subcommand
* @param sub_command type of data to be returned * @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 * Sends a sub command to the device and waits for it's reply
* @param sc sub command to be send * @param sc sub command to be send
* @param buffer data 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 * 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 * Reads the SPI memory stored on the joycon
* @param Initial address location * @param Initial address location
* @returns output buffer containing the responce * @returns output buffer containing the response
*/ */
DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output); DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
/** /**
* Reads the SPI memory stored on the joycon * Reads the SPI memory stored on the joycon
* @param Initial address location * @param Initial address location
* @returns output object containing the responce * @returns output object containing the response
*/ */
template <typename Output> template <typename Output>
requires std::is_trivially_copyable_v<Output> requires std::is_trivially_copyable_v<Output>
@ -136,19 +145,19 @@ public:
/** /**
* Waits until there's MCU data available. On timeout returns error * Waits until there's MCU data available. On timeout returns error
* @param report mode of the expected reply * @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 * Sends data to the MCU chip and waits for it's reply
* @param report mode of the expected reply * @param report mode of the expected reply
* @param sub command to be send * @param sub command to be send
* @param buffer data 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, 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 * Wait's until the MCU chip is on the specified mode

View file

@ -32,13 +32,13 @@ DriverResult GenericProtocol::TriggersElapsed() {
DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
ScopedSetBlocking sb(this); ScopedSetBlocking sb(this);
std::vector<u8> output; SubCommandResponse output{};
const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
device_info = {}; device_info = {};
if (result == DriverResult::Success) { if (result == DriverResult::Success) {
memcpy(&device_info, output.data() + 15, sizeof(DeviceInfo)); device_info = output.device_info;
} }
return result; return result;

View file

@ -132,7 +132,7 @@ DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
DriverResult IrsProtocol::ConfigureIrs() { DriverResult IrsProtocol::ConfigureIrs() {
LOG_DEBUG(Input, "Configure IRS"); LOG_DEBUG(Input, "Configure IRS");
constexpr std::size_t max_tries = 28; constexpr std::size_t max_tries = 28;
std::vector<u8> output; SubCommandResponse output{};
std::size_t tries = 0; std::size_t tries = 0;
const IrsConfigure irs_configuration{ const IrsConfigure irs_configuration{
@ -158,7 +158,7 @@ DriverResult IrsProtocol::ConfigureIrs() {
if (tries++ >= max_tries) { if (tries++ >= max_tries) {
return DriverResult::WrongReply; return DriverResult::WrongReply;
} }
} while (output[15] != 0x0b); } while (output.command_data[0] != 0x0b);
return DriverResult::Success; return DriverResult::Success;
} }
@ -167,7 +167,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
LOG_DEBUG(Input, "WriteRegistersStep1"); LOG_DEBUG(Input, "WriteRegistersStep1");
DriverResult result{DriverResult::Success}; DriverResult result{DriverResult::Success};
constexpr std::size_t max_tries = 28; constexpr std::size_t max_tries = 28;
std::vector<u8> output; SubCommandResponse output{};
std::size_t tries = 0; std::size_t tries = 0;
const IrsWriteRegisters irs_registers{ const IrsWriteRegisters irs_registers{
@ -218,7 +218,8 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
if (tries++ >= max_tries) { if (tries++ >= max_tries) {
return DriverResult::WrongReply; 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; return DriverResult::Success;
} }
@ -226,7 +227,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
DriverResult IrsProtocol::WriteRegistersStep2() { DriverResult IrsProtocol::WriteRegistersStep2() {
LOG_DEBUG(Input, "WriteRegistersStep2"); LOG_DEBUG(Input, "WriteRegistersStep2");
constexpr std::size_t max_tries = 28; constexpr std::size_t max_tries = 28;
std::vector<u8> output; SubCommandResponse output{};
std::size_t tries = 0; std::size_t tries = 0;
const IrsWriteRegisters irs_registers{ const IrsWriteRegisters irs_registers{
@ -260,7 +261,7 @@ DriverResult IrsProtocol::WriteRegistersStep2() {
if (tries++ >= max_tries) { if (tries++ >= max_tries) {
return DriverResult::WrongReply; return DriverResult::WrongReply;
} }
} while (output[15] != 0x13 && output[15] != 0x23); } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23);
return DriverResult::Success; return DriverResult::Success;
} }

View file

@ -19,8 +19,6 @@
namespace InputCommon::Joycon { namespace InputCommon::Joycon {
constexpr u32 MaxErrorCount = 50; constexpr u32 MaxErrorCount = 50;
constexpr u32 MaxBufferSize = 368; 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}; constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
using MacAddress = std::array<u8, 6>; using MacAddress = std::array<u8, 6>;
@ -105,14 +103,6 @@ enum class OutputReport : u8 {
USB_CMD = 0x80, 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 { enum class FeatureReport : u8 {
Last_SUBCMD = 0x02, Last_SUBCMD = 0x02,
OTA_GW_UPGRADE = 0x70, OTA_GW_UPGRADE = 0x70,
@ -171,7 +161,7 @@ enum class CalibrationMagic : u8 {
USR_MAGIC_1 = 0xA1, USR_MAGIC_1 = 0xA1,
}; };
enum class SpiAddress { enum class SpiAddress : u16 {
MAGIC = 0x0000, MAGIC = 0x0000,
MAC_ADDRESS = 0x0015, MAC_ADDRESS = 0x0015,
PAIRING_INFO = 0x2000, PAIRING_INFO = 0x2000,
@ -198,10 +188,12 @@ enum class ReportMode : u8 {
ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01, ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02, ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
ACTIVE_POLLING_IR_CAMERA_DATA = 0x03, ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
SUBCMD_REPLY = 0x21,
MCU_UPDATE_STATE = 0x23, MCU_UPDATE_STATE = 0x23,
STANDARD_FULL_60HZ = 0x30, STANDARD_FULL_60HZ = 0x30,
NFC_IR_MODE_60HZ = 0x31, NFC_IR_MODE_60HZ = 0x31,
SIMPLE_HID_MODE = 0x3F, SIMPLE_HID_MODE = 0x3F,
INPUT_USB_RESPONSE = 0x81,
}; };
enum class GyroSensitivity : u8 { enum class GyroSensitivity : u8 {
@ -372,15 +364,16 @@ enum class IrRegistersAddress : u16 {
DenoiseColor = 0x6901, DenoiseColor = 0x6901,
}; };
enum class ExternalDeviceId : u8 { enum class ExternalDeviceId : u16 {
RingController = 0x20, RingController = 0x2000,
Starlink = 0x28, Starlink = 0x2800,
}; };
enum class DriverResult { enum class DriverResult {
Success, Success,
WrongReply, WrongReply,
Timeout, Timeout,
InvalidParameters,
UnsupportedControllerType, UnsupportedControllerType,
HandleInUse, HandleInUse,
ErrorReadingData, ErrorReadingData,
@ -503,7 +496,7 @@ static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size");
#pragma pack(push, 1) #pragma pack(push, 1)
struct InputReportPassive { struct InputReportPassive {
InputReport report_mode; ReportMode report_mode;
u16 button_input; u16 button_input;
u8 stick_state; u8 stick_state;
std::array<u8, 10> unknown_data; std::array<u8, 10> unknown_data;
@ -511,7 +504,7 @@ struct InputReportPassive {
static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size"); static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size");
struct InputReportActive { struct InputReportActive {
InputReport report_mode; ReportMode report_mode;
u8 packet_id; u8 packet_id;
Battery battery_status; Battery battery_status;
std::array<u8, 3> button_input; std::array<u8, 3> button_input;
@ -525,7 +518,7 @@ struct InputReportActive {
static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size"); static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size");
struct InputReportNfcIr { struct InputReportNfcIr {
InputReport report_mode; ReportMode report_mode;
u8 packet_id; u8 packet_id;
Battery battery_status; Battery battery_status;
std::array<u8, 3> button_input; std::array<u8, 3> button_input;
@ -643,6 +636,53 @@ struct RingStatus {
s16 min_value; 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 { struct JoyconCallbacks {
std::function<void(Battery)> on_battery_data; std::function<void(Battery)> on_battery_data;
std::function<void(Color)> on_color_data; std::function<void(Color)> on_color_data;

View file

@ -110,7 +110,7 @@ bool NfcProtocol::HasAmiibo() {
DriverResult NfcProtocol::WaitUntilNfcIsReady() { DriverResult NfcProtocol::WaitUntilNfcIsReady() {
constexpr std::size_t timeout_limit = 10; constexpr std::size_t timeout_limit = 10;
std::vector<u8> output; MCUCommandResponse output{};
std::size_t tries = 0; std::size_t tries = 0;
do { do {
@ -122,8 +122,9 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
if (tries++ > timeout_limit) { if (tries++ > timeout_limit) {
return DriverResult::Timeout; return DriverResult::Timeout;
} }
} while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[55] != 0x31 || } while (output.mcu_report != MCUReport::NFCState ||
output[56] != 0x00); (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
return DriverResult::Success; return DriverResult::Success;
} }
@ -131,7 +132,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
DriverResult NfcProtocol::StartPolling(TagFoundData& data) { DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
LOG_DEBUG(Input, "Start Polling for tag"); LOG_DEBUG(Input, "Start Polling for tag");
constexpr std::size_t timeout_limit = 7; constexpr std::size_t timeout_limit = 7;
std::vector<u8> output; MCUCommandResponse output{};
std::size_t tries = 0; std::size_t tries = 0;
do { do {
@ -142,18 +143,20 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
if (tries++ > timeout_limit) { if (tries++ > timeout_limit) {
return DriverResult::Timeout; 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.type = output.mcu_data[12];
data.uuid.resize(output[64]); data.uuid.resize(output.mcu_data[14]);
memcpy(data.uuid.data(), output.data() + 65, data.uuid.size()); memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
return DriverResult::Success; return DriverResult::Success;
} }
DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
constexpr std::size_t timeout_limit = 10; constexpr std::size_t timeout_limit = 10;
std::vector<u8> output; MCUCommandResponse output{};
std::size_t tries = 0; std::size_t tries = 0;
std::string uuid_string; std::string uuid_string;
@ -168,23 +171,24 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
// Read Tag data // Read Tag data
while (true) { while (true) {
auto result = SendReadAmiiboRequest(output, ntag_pages); auto result = SendReadAmiiboRequest(output, ntag_pages);
const auto mcu_report = static_cast<MCUReport>(output[49]); const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
const auto nfc_status = static_cast<NFCStatus>(output[56]);
if (result != DriverResult::Success) { if (result != DriverResult::Success) {
return result; 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) { nfc_status == NFCStatus::TagLost) {
return DriverResult::ErrorReadingData; 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) { if (data.type != 2) {
continue; continue;
} }
switch (output[74]) { switch (output.mcu_data[24]) {
case 0: case 0:
ntag_pages = NFCPages::Block135; ntag_pages = NFCPages::Block135;
break; break;
@ -200,14 +204,14 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
continue; continue;
} }
if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
// finished // finished
SendStopPollingRequest(output); SendStopPollingRequest(output);
return DriverResult::Success; return DriverResult::Success;
} }
// Ignore other state reports // Ignore other state reports
if (mcu_report == MCUReport::NFCState) { if (output.mcu_report == MCUReport::NFCState) {
continue; continue;
} }
@ -221,7 +225,7 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
constexpr std::size_t timeout_limit = 10; constexpr std::size_t timeout_limit = 10;
std::vector<u8> output; MCUCommandResponse output{};
std::size_t tries = 0; std::size_t tries = 0;
NFCPages ntag_pages = NFCPages::Block135; NFCPages ntag_pages = NFCPages::Block135;
@ -229,36 +233,38 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
// Read Tag data // Read Tag data
while (true) { while (true) {
auto result = SendReadAmiiboRequest(output, ntag_pages); auto result = SendReadAmiiboRequest(output, ntag_pages);
const auto mcu_report = static_cast<MCUReport>(output[49]); const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
const auto nfc_status = static_cast<NFCStatus>(output[56]);
if (result != DriverResult::Success) { if (result != DriverResult::Success) {
return result; 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) { nfc_status == NFCStatus::TagLost) {
return DriverResult::ErrorReadingData; return DriverResult::ErrorReadingData;
} }
if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07) { if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF; std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
if (output[52] == 0x01) { if (output.mcu_data[2] == 0x01) {
memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, payload_size - 60); memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
payload_size - 60);
ntag_buffer_pos += payload_size - 60; ntag_buffer_pos += payload_size - 60;
} else { } 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; 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"); LOG_INFO(Input, "Finished reading amiibo");
return DriverResult::Success; return DriverResult::Success;
} }
// Ignore other state reports // Ignore other state reports
if (mcu_report == MCUReport::NFCState) { if (output.mcu_report == MCUReport::NFCState) {
continue; continue;
} }
@ -270,7 +276,7 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
return DriverResult::Success; return DriverResult::Success;
} }
DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) { DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
NFCRequestState request{ NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode, .sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StartPolling, .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); 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{ NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode, .sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StopPolling, .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); 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{ NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode, .sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StartWaitingRecieve, .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); 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{ NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode, .sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::Ntag, .command_argument = NFCReadCommand::Ntag,

View file

@ -45,13 +45,13 @@ private:
DriverResult GetAmiiboData(std::vector<u8>& data); 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; NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;

View file

@ -70,7 +70,7 @@ DriverResult RingConProtocol::StartRingconPolling() {
DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
LOG_DEBUG(Input, "IsRingConnected"); LOG_DEBUG(Input, "IsRingConnected");
constexpr std::size_t max_tries = 28; constexpr std::size_t max_tries = 28;
std::vector<u8> output; SubCommandResponse output{};
std::size_t tries = 0; std::size_t tries = 0;
is_connected = false; is_connected = false;
@ -84,7 +84,7 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
if (tries++ >= max_tries) { if (tries++ >= max_tries) {
return DriverResult::NoDeviceDetected; return DriverResult::NoDeviceDetected;
} }
} while (output[16] != static_cast<u8>(ExternalDeviceId::RingController)); } while (output.external_device_id != ExternalDeviceId::RingController);
is_connected = true; is_connected = true;
return DriverResult::Success; return DriverResult::Success;