mirror of
https://github.com/ndeadly/MissionControl
synced 2025-02-16 13:18:27 +00:00
mc.mitm: implement enough of nfc state machine to respond correctly with device not ready status when queried
This commit is contained in:
parent
43f405e3e6
commit
6ce89a7f54
3 changed files with 237 additions and 22 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue