mc.mitm: implement enough of nfc state machine to respond correctly with device not ready status when queried

This commit is contained in:
ndeadly 2024-10-10 19:03:42 +02:00
parent 43f405e3e6
commit 6ce89a7f54
3 changed files with 237 additions and 22 deletions

View file

@ -36,7 +36,8 @@ namespace ams::controller {
, m_led_pattern(0)
, m_gyro_sensitivity(2000)
, m_acc_sensitivity(8000)
, m_input_report_mode(0x30) {
, m_input_report_mode(0x30)
, m_mcu_mode(McuMode_Suspended) {
this->ClearControllerState();
auto config = mitm::GetGlobalConfig();
@ -68,7 +69,7 @@ namespace ams::controller {
this->ProcessInputData(report);
auto input_report = reinterpret_cast<SwitchInputReport *>(m_input_report.data);
input_report->id = 0x30;
input_report->id = m_input_report_mode;
input_report->timer = (input_report->timer + 1) & 0xff;
input_report->conn_info = (0 << 1) | m_ext_power;
input_report->battery = m_battery | m_charging;
@ -76,8 +77,23 @@ namespace ams::controller {
input_report->left_stick = m_left_stick;
input_report->right_stick = m_right_stick;
std::memcpy(&input_report->type0x30.motion_data, &m_motion_data, sizeof(m_motion_data));
m_input_report.size = offsetof(SwitchInputReport, type0x30) + sizeof(input_report->type0x30);
const SwitchMcuResponse empty_mcu_response = {
.command = McuCommand_EmptyAwaitingCmd,
.data = {},
};
switch (m_input_report_mode) {
case 0x31:
std::memcpy(&input_report->type0x31.motion_data, &m_motion_data, sizeof(m_motion_data));
std::memcpy(&input_report->type0x31.mcu_response, &empty_mcu_response, sizeof(empty_mcu_response));
input_report->type0x31.crc = ComputeCrc8(&empty_mcu_response, sizeof(SwitchMcuResponse));
m_input_report.size = offsetof(SwitchInputReport, type0x31) + sizeof(input_report->type0x31);
break;
default:
std::memcpy(&input_report->type0x30.motion_data, &m_motion_data, sizeof(m_motion_data));
m_input_report.size = offsetof(SwitchInputReport, type0x30) + sizeof(input_report->type0x30);
break;
}
}
Result EmulatedSwitchController::HandleOutputDataReport(const bluetooth::HidReport *report) {
@ -93,7 +109,7 @@ namespace ams::controller {
break;
case 0x11:
R_TRY(this->HandleRumbleData(&output_report->enc_motor_data));
//R_TRY(this->HandleNfcIrData(output_report->type0x11.nfc_ir_data));
R_TRY(this->HandleMcuCommand(&output_report->type0x11.mcu_command));
break;
default:
break;
@ -346,11 +362,18 @@ namespace ams::controller {
}
Result EmulatedSwitchController::HandleHidCommandMcuWrite(const SwitchHidCommand *command) {
switch (command->mcu_write.command){
case McuCommand_ConfigureMcu:
return this->HandleHidCommandConfigureMcu(command);
default:
break;
}
const SwitchHidCommandResponse response = {
.ack = 0xa0,
.id = command->id,
.data = {
.raw = {
.raw = {// This looks a lot like mcu get status
0x01, 0x00, 0xff, 0x00, 0x03, 0x00, 0x05, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -362,8 +385,57 @@ namespace ams::controller {
R_RETURN(this->FakeHidCommandResponse(&response));
}
Result EmulatedSwitchController::HandleHidCommandConfigureMcu(const SwitchHidCommand *command) {
if (m_mcu_mode == McuMode_Suspended || m_mcu_mode == McuMode_Busy) {
const SwitchHidCommandResponse response = {
.ack = 0xa0,
.id = command->id,
.data = {
.raw = {// This looks a lot like mcu get status
0x01, 0x00, 0xff, 0x00, 0x08, 0x00, 0x1b, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf6
}
}
};
R_RETURN(this->FakeHidCommandResponse(&response));
}
if (m_mcu_mode == McuMode_Standby){
m_mcu_mode = command->mcu_write.data.configure_mcu.mode;
}
const SwitchHidCommandResponse response = {
.ack = 0xa0,
.id = command->id,
.data = {
.raw = {// This looks a lot like mcu get status
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1b, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xef
}
}
};
R_RETURN(this->FakeHidCommandResponse(&response));
}
Result EmulatedSwitchController::HandleHidCommandMcuResume(const SwitchHidCommand *command) {
if(command->mcu_resume.enabled && m_mcu_mode == McuMode_Suspended){
m_mcu_mode = McuMode_Standby;
}
if (!command->mcu_resume.enabled){
m_mcu_mode = McuMode_Suspended;
}
const SwitchHidCommandResponse response = {
.ack = 0x80,
.id = command->id
@ -481,18 +553,78 @@ namespace ams::controller {
R_RETURN(bluetooth::hid::report::WriteHidDataReport(m_address, &m_input_report));
}
Result EmulatedSwitchController::HandleNfcIrData(const u8 *nfc_ir) {
AMS_UNUSED(nfc_ir);
Result EmulatedSwitchController::HandleMcuCommand(const SwitchMcuCommand *command) {
switch (command->sub_command) {
case McuSubCommand_SetMcuMode:
R_TRY(this->HandleMcuCommandSetMcuMode());
break;
case McuSubCommand_GetMcuMode:
R_TRY(this->HandleMcuCommandGetMcuMode());
break;
case McuSubCommand_ReadDeviceMode:
R_TRY(this->HandleMcuCommandReadDeviceMode());
break;
case McuSubCommand_WriteDeviceRegisters:
//R_TRY(this->HandleMcuCommandWriteDeviceRegisters(command));
break;
default: {
// Send device not ready response for now
const SwitchMcuResponse response = {
.command = McuCommand_EmptyAwaitingCmd,
.data = {
.get_mcu_mode = {
.mode = m_mcu_mode
}
}
};
SwitchNfcIrResponse response = {};
R_RETURN(this->FakeMcuResponse(&response));
}
}
// Send device not ready response for now
response.data[0] = 0xff;
R_RETURN(this->FakeNfcIrResponse(&response));
R_SUCCEED();
}
Result EmulatedSwitchController::FakeNfcIrResponse(const SwitchNfcIrResponse *response) {
Result EmulatedSwitchController::HandleMcuCommandSetMcuMode() {
const SwitchMcuResponse response = {
.command = McuCommand_EmptyAwaitingCmd
};
R_RETURN(this->FakeMcuResponse(&response));
}
Result EmulatedSwitchController::HandleMcuCommandGetMcuMode() {
const SwitchMcuResponse response = {
.command = McuCommand_StateReport,
.data = {
.get_mcu_mode = {
.unknown_1 = 0x08,
.unknown_2 = 0x1b,
.mode = m_mcu_mode
}
}
};
R_RETURN(this->FakeMcuResponse(&response));
}
Result EmulatedSwitchController::HandleMcuCommandReadDeviceMode() {
const SwitchMcuResponse response = {
.command = McuCommand_NfcState,
.data = {
.read_device_mode = {
.unknown_1 = 0x05,
.unknown_2 = 0x09,
.unknown_3 = 0x31,
.is_ready = 0x01
}
}
};
R_RETURN(this->FakeMcuResponse(&response));
}
Result EmulatedSwitchController::FakeMcuResponse(const SwitchMcuResponse *response) {
std::scoped_lock lk(m_input_mutex);
auto input_report = reinterpret_cast<SwitchInputReport *>(m_input_report.data);
@ -506,8 +638,8 @@ namespace ams::controller {
input_report->vibrator = 0;
std::memcpy(&input_report->type0x31.motion_data, &m_motion_data, sizeof(m_motion_data));
std::memcpy(&input_report->type0x31.nfc_ir_response, response, sizeof(SwitchNfcIrResponse));
input_report->type0x31.crc = ComputeCrc8(response, sizeof(SwitchNfcIrResponse));
std::memcpy(&input_report->type0x31.mcu_response, response, sizeof(SwitchMcuResponse));
input_report->type0x31.crc = ComputeCrc8(response, sizeof(SwitchMcuResponse));
m_input_report.size = offsetof(SwitchInputReport, type0x31) + sizeof(input_report->type0x31);
// Write a fake response into the report buffer

View file

@ -41,7 +41,7 @@ namespace ams::controller {
Result HandleRumbleData(const SwitchEncodedMotorData *enc_motor_data);
Result HandleHidCommand(const SwitchHidCommand *command);
Result HandleNfcIrData(const u8 *nfc_ir);
Result HandleMcuCommand(const SwitchMcuCommand *command);
Result HandleHidCommandGetDeviceInfo(const SwitchHidCommand *command);
Result HandleHidCommandSetDataFormat(const SwitchHidCommand *command);
@ -52,6 +52,7 @@ namespace ams::controller {
Result HandleHidCommandSerialFlashWrite(const SwitchHidCommand *command);
Result HandleHidCommandSerialFlashSectorErase(const SwitchHidCommand *command);
Result HandleHidCommandMcuWrite(const SwitchHidCommand *command);
Result HandleHidCommandConfigureMcu(const SwitchHidCommand *command);
Result HandleHidCommandMcuResume(const SwitchHidCommand *command);
Result HandleHidCommandMcuPollingEnable(const SwitchHidCommand *command);
Result HandleHidCommandMcuPollingDisable(const SwitchHidCommand *command);
@ -62,8 +63,12 @@ namespace ams::controller {
Result HandleHidCommandSensorConfig(const SwitchHidCommand *command);
Result HandleHidCommandMotorEnable(const SwitchHidCommand *command);
Result HandleMcuCommandSetMcuMode();
Result HandleMcuCommandGetMcuMode();
Result HandleMcuCommandReadDeviceMode();
Result FakeHidCommandResponse(const SwitchHidCommandResponse *response);
Result FakeNfcIrResponse(const SwitchNfcIrResponse *response);
Result FakeMcuResponse(const SwitchMcuResponse *response);
bool m_charging;
bool m_ext_power;
@ -87,6 +92,8 @@ namespace ams::controller {
float m_trigger_threshold;
McuModeType m_mcu_mode;
VirtualSpiFlash m_virtual_memory;
};

View file

@ -157,6 +157,36 @@ namespace ams::controller {
HidCommand_ReadChargeSetting = 0x52,
};
enum McuCommandType : u8 {
McuCommand_Invalid = 0x00,
McuCommand_StateReport = 0x01,
McuCommand_IrData = 0x03,
McuCommand_BusyInitializing = 0x0b,
McuCommand_IrStatus = 0x13,
McuCommand_IrRegisters = 0x1b,
McuCommand_ConfigureMcu = 0x21,
McuCommand_ConfigureIr= 0x23,
McuCommand_NfcState = 0x2a,
McuCommand_NfcReadData = 0x3a,
McuCommand_EmptyAwaitingCmd = 0xff,
};
enum McuSubCommandType : u8 {
McuSubCommand_SetMcuMode = 0x00,
McuSubCommand_GetMcuMode = 0x01,
McuSubCommand_ReadDeviceMode = 0x02,
McuSubCommand_WriteDeviceRegisters = 0x04,
};
enum McuModeType : u8 {
McuMode_Suspended = 0,
McuMode_Standby = 1,
McuMode_Ringcon = 3,
McuMode_Nfc = 4,
McuMode_Ir = 5,
McuMode_Busy = 6,
};
struct SwitchHidCommand {
u8 id;
union {
@ -206,6 +236,21 @@ namespace ams::controller {
struct {
bool enabled;
} motor_enable;
struct {
McuCommandType command;
union {
u8 raw[0x25];
struct {
u8 pad;
McuModeType mode;
} configure_mcu;
} data;
} mcu_write;
struct {
bool enabled;
} mcu_resume;
};
} PACKED;
@ -258,8 +303,39 @@ namespace ams::controller {
} data;
} PACKED;
struct SwitchNfcIrResponse {
u8 data[0x138];
struct SwitchMcuCommand {
McuSubCommandType sub_command;
union {
u8 raw[0x26];
struct {
McuModeType mode;
} set_mcu_mode;
} data;
} PACKED;
struct SwitchMcuResponse {
McuCommandType command;
union {
u8 raw[0x137];
struct {
u8 pad[3];
u8 unknown_1;
u8 pad2;
u8 unknown_2;
McuModeType mode;
} get_mcu_mode;
struct {
u8 pad;
u8 unknown_1;
u8 pad2[2];
u8 unknown_2;
u8 unknown_3;
u8 is_ready;
} read_device_mode;
} data;
} PACKED;
struct SwitchInputReport {
@ -287,7 +363,7 @@ namespace ams::controller {
struct {
Switch6AxisData motion_data[3]; // IMU samples at 0, 5 and 10ms
SwitchNfcIrResponse nfc_ir_response;
SwitchMcuResponse mcu_response;
u8 crc;
} type0x31;
};
@ -304,7 +380,7 @@ namespace ams::controller {
} type0x01;
struct {
u8 nfc_ir_data[0x16];
SwitchMcuCommand mcu_command;
} type0x11;
};
} PACKED;