Merge pull request #9701 from german77/common_protocol
input_common: joycon: Remove Magic numbers from common protocol
This commit is contained in:
commit
e74660673b
12 changed files with 264 additions and 185 deletions
|
@ -668,12 +668,10 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
|
||||||
return "Right Joycon";
|
return "Right Joycon";
|
||||||
case Joycon::ControllerType::Pro:
|
case Joycon::ControllerType::Pro:
|
||||||
return "Pro Controller";
|
return "Pro Controller";
|
||||||
case Joycon::ControllerType::Grip:
|
|
||||||
return "Grip Controller";
|
|
||||||
case Joycon::ControllerType::Dual:
|
case Joycon::ControllerType::Dual:
|
||||||
return "Dual Joycon";
|
return "Dual Joycon";
|
||||||
default:
|
default:
|
||||||
return "Unknown Joycon";
|
return "Unknown Switch Controller";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace InputCommon
|
} // namespace InputCommon
|
||||||
|
|
|
@ -15,7 +15,7 @@ using SerialNumber = std::array<u8, 15>;
|
||||||
struct Battery;
|
struct Battery;
|
||||||
struct Color;
|
struct Color;
|
||||||
struct MotionData;
|
struct MotionData;
|
||||||
enum class ControllerType;
|
enum class ControllerType : u8;
|
||||||
enum class DriverResult;
|
enum class DriverResult;
|
||||||
enum class IrsResolution;
|
enum class IrsResolution;
|
||||||
class JoyconDriver;
|
class JoyconDriver;
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(), sizeof(DeviceInfo));
|
device_info = output.device_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,20 +19,24 @@
|
||||||
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>;
|
||||||
using SerialNumber = std::array<u8, 15>;
|
using SerialNumber = std::array<u8, 15>;
|
||||||
|
|
||||||
enum class ControllerType {
|
enum class ControllerType : u8 {
|
||||||
None,
|
None = 0x00,
|
||||||
Left,
|
Left = 0x01,
|
||||||
Right,
|
Right = 0x02,
|
||||||
Pro,
|
Pro = 0x03,
|
||||||
Grip,
|
Dual = 0x05, // TODO: Verify this id
|
||||||
Dual,
|
LarkHvc1 = 0x07,
|
||||||
|
LarkHvc2 = 0x08,
|
||||||
|
LarkNesLeft = 0x09,
|
||||||
|
LarkNesRight = 0x0A,
|
||||||
|
Lucia = 0x0B,
|
||||||
|
Lagon = 0x0C,
|
||||||
|
Lager = 0x0D,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class PadAxes {
|
enum class PadAxes {
|
||||||
|
@ -99,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,
|
||||||
|
@ -143,9 +139,10 @@ enum class SubCommand : u8 {
|
||||||
ENABLE_VIBRATION = 0x48,
|
ENABLE_VIBRATION = 0x48,
|
||||||
GET_REGULATED_VOLTAGE = 0x50,
|
GET_REGULATED_VOLTAGE = 0x50,
|
||||||
SET_EXTERNAL_CONFIG = 0x58,
|
SET_EXTERNAL_CONFIG = 0x58,
|
||||||
UNKNOWN_RINGCON = 0x59,
|
GET_EXTERNAL_DEVICE_INFO = 0x59,
|
||||||
UNKNOWN_RINGCON2 = 0x5A,
|
ENABLE_EXTERNAL_POLLING = 0x5A,
|
||||||
UNKNOWN_RINGCON3 = 0x5C,
|
DISABLE_EXTERNAL_POLLING = 0x5B,
|
||||||
|
SET_EXTERNAL_FORMAT_CONFIG = 0x5C,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class UsbSubCommand : u8 {
|
enum class UsbSubCommand : u8 {
|
||||||
|
@ -164,20 +161,26 @@ enum class CalibrationMagic : u8 {
|
||||||
USR_MAGIC_1 = 0xA1,
|
USR_MAGIC_1 = 0xA1,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SpiAddress {
|
enum class SpiAddress : u16 {
|
||||||
SERIAL_NUMBER = 0X6000,
|
MAGIC = 0x0000,
|
||||||
DEVICE_TYPE = 0X6012,
|
MAC_ADDRESS = 0x0015,
|
||||||
COLOR_EXIST = 0X601B,
|
PAIRING_INFO = 0x2000,
|
||||||
FACT_LEFT_DATA = 0X603d,
|
SHIPMENT = 0x5000,
|
||||||
FACT_RIGHT_DATA = 0X6046,
|
SERIAL_NUMBER = 0x6000,
|
||||||
COLOR_DATA = 0X6050,
|
DEVICE_TYPE = 0x6012,
|
||||||
FACT_IMU_DATA = 0X6020,
|
FORMAT_VERSION = 0x601B,
|
||||||
USER_LEFT_MAGIC = 0X8010,
|
FACT_IMU_DATA = 0x6020,
|
||||||
USER_LEFT_DATA = 0X8012,
|
FACT_LEFT_DATA = 0x603d,
|
||||||
USER_RIGHT_MAGIC = 0X801B,
|
FACT_RIGHT_DATA = 0x6046,
|
||||||
USER_RIGHT_DATA = 0X801D,
|
COLOR_DATA = 0x6050,
|
||||||
USER_IMU_MAGIC = 0X8026,
|
DESIGN_VARIATION = 0x605C,
|
||||||
USER_IMU_DATA = 0X8028,
|
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 {
|
enum class ReportMode : u8 {
|
||||||
|
@ -185,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 {
|
||||||
|
@ -359,10 +364,16 @@ enum class IrRegistersAddress : u16 {
|
||||||
DenoiseColor = 0x6901,
|
DenoiseColor = 0x6901,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ExternalDeviceId : u16 {
|
||||||
|
RingController = 0x2000,
|
||||||
|
Starlink = 0x2800,
|
||||||
|
};
|
||||||
|
|
||||||
enum class DriverResult {
|
enum class DriverResult {
|
||||||
Success,
|
Success,
|
||||||
WrongReply,
|
WrongReply,
|
||||||
Timeout,
|
Timeout,
|
||||||
|
InvalidParameters,
|
||||||
UnsupportedControllerType,
|
UnsupportedControllerType,
|
||||||
HandleInUse,
|
HandleInUse,
|
||||||
ErrorReadingData,
|
ErrorReadingData,
|
||||||
|
@ -485,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;
|
||||||
|
@ -493,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;
|
||||||
|
@ -507,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;
|
||||||
|
@ -605,9 +616,11 @@ static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid siz
|
||||||
|
|
||||||
struct DeviceInfo {
|
struct DeviceInfo {
|
||||||
FirmwareVersion firmware;
|
FirmwareVersion firmware;
|
||||||
|
std::array<u8, 2> unknown_1;
|
||||||
MacAddress mac_address;
|
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 {
|
struct MotionStatus {
|
||||||
bool is_enabled;
|
bool is_enabled;
|
||||||
|
@ -623,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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,7 @@ void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& moti
|
||||||
case Joycon::ControllerType::Pro:
|
case Joycon::ControllerType::Pro:
|
||||||
UpdateActiveProPadInput(data, motion_status);
|
UpdateActiveProPadInput(data, motion_status);
|
||||||
break;
|
break;
|
||||||
case Joycon::ControllerType::Grip:
|
default:
|
||||||
case Joycon::ControllerType::Dual:
|
|
||||||
case Joycon::ControllerType::None:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,9 +56,7 @@ void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
|
||||||
case Joycon::ControllerType::Pro:
|
case Joycon::ControllerType::Pro:
|
||||||
UpdatePasiveProPadInput(data);
|
UpdatePasiveProPadInput(data);
|
||||||
break;
|
break;
|
||||||
case Joycon::ControllerType::Grip:
|
default:
|
||||||
case Joycon::ControllerType::Dual:
|
|
||||||
case Joycon::ControllerType::None:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,14 +70,12 @@ 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;
|
||||||
constexpr u8 ring_controller_id = 0x20;
|
SubCommandResponse output{};
|
||||||
std::vector<u8> output;
|
|
||||||
std::size_t tries = 0;
|
std::size_t tries = 0;
|
||||||
is_connected = false;
|
is_connected = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
std::array<u8, 1> empty_data{};
|
const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output);
|
||||||
const auto result = SendSubCommand(SubCommand::UNKNOWN_RINGCON, empty_data, output);
|
|
||||||
|
|
||||||
if (result != DriverResult::Success) {
|
if (result != DriverResult::Success) {
|
||||||
return result;
|
return result;
|
||||||
|
@ -86,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] != ring_controller_id);
|
} while (output.external_device_id != ExternalDeviceId::RingController);
|
||||||
|
|
||||||
is_connected = true;
|
is_connected = true;
|
||||||
return DriverResult::Success;
|
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, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
|
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) {
|
if (result != DriverResult::Success) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02};
|
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 {
|
bool RingConProtocol::IsEnabled() const {
|
||||||
|
|
Loading…
Reference in a new issue