Verify automatic XInput mode switching
This commit is contained in:
parent
17b3d7399c
commit
7e3be8675f
5 changed files with 42 additions and 17 deletions
|
|
@ -18,13 +18,24 @@ namespace {
|
|||
|
||||
constexpr uint32_t kXInputBootMagic = 0x58494e50; // "XINP"
|
||||
constexpr uint8_t kModeScratchRegister = 0;
|
||||
constexpr uint8_t kStatusRequest = 0x21;
|
||||
constexpr uint16_t kStatusIndex = 0x0005;
|
||||
uint8_t g_status_response[4]{};
|
||||
|
||||
AdapterUsbMode g_mode = AdapterUsbMode::kSwitchProbe;
|
||||
AdapterHostProbeState g_probe;
|
||||
alarm_id_t g_reboot_alarm = 0;
|
||||
|
||||
uint32_t now_ms() {
|
||||
return static_cast<uint32_t>(to_ms_since_boot(get_absolute_time()));
|
||||
}
|
||||
int64_t reboot_to_xinput(alarm_id_t alarm_id, void *user_data) {
|
||||
(void)alarm_id;
|
||||
(void)user_data;
|
||||
watchdog_hw->scratch[kModeScratchRegister] = kXInputBootMagic;
|
||||
watchdog_reboot(0, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
|
@ -54,8 +65,24 @@ bool adapter_host_probe_vendor_control(uint8_t rhport, uint8_t stage,
|
|||
if (stage != CONTROL_STAGE_SETUP || request == nullptr ||
|
||||
request->bmRequestType_bit.direction != TUSB_DIR_IN ||
|
||||
request->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR ||
|
||||
request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_DEVICE ||
|
||||
request->bRequest != XInputFeasibility::kMsVendorRequest ||
|
||||
request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_DEVICE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request->bRequest == kStatusRequest &&
|
||||
request->wIndex == kStatusIndex) {
|
||||
const uint32_t current_time = now_ms();
|
||||
g_status_response[0] =
|
||||
g_mode == AdapterUsbMode::kXInput ? 1 : 0;
|
||||
g_status_response[1] = g_probe.saw_ms_os_string() ? 1 : 0;
|
||||
g_status_response[2] = g_probe.windows_confirmed() ? 1 : 0;
|
||||
g_status_response[3] =
|
||||
g_probe.should_reboot(current_time) ? 1 : 0;
|
||||
return tud_control_xfer(rhport, request, g_status_response,
|
||||
sizeof(g_status_response));
|
||||
}
|
||||
|
||||
if (request->bRequest != XInputFeasibility::kMsVendorRequest ||
|
||||
request->wIndex != XInputFeasibility::kMsCompatIdIndex) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -63,11 +90,19 @@ bool adapter_host_probe_vendor_control(uint8_t rhport, uint8_t stage,
|
|||
if (g_mode == AdapterUsbMode::kSwitchProbe) {
|
||||
g_probe.note_ms_compat_id_request(now_ms());
|
||||
PROBE_LOG("[HOST PROBE] Microsoft compatible-ID request confirmed\n");
|
||||
return tud_control_xfer(
|
||||
const bool queued = tud_control_xfer(
|
||||
rhport, request,
|
||||
const_cast<uint8_t *>(
|
||||
XInputFeasibility::kProbeMsCompatIdDescriptor),
|
||||
sizeof(XInputFeasibility::kProbeMsCompatIdDescriptor));
|
||||
if (queued && g_reboot_alarm == 0) {
|
||||
g_reboot_alarm = add_alarm_in_ms(
|
||||
AdapterHostProbeState::kRebootDelayMs,
|
||||
reboot_to_xinput, nullptr, true);
|
||||
PROBE_LOG("[HOST PROBE] XInput reboot alarm=%d\n",
|
||||
static_cast<int>(g_reboot_alarm));
|
||||
}
|
||||
return queued;
|
||||
}
|
||||
|
||||
return tud_control_xfer(
|
||||
|
|
@ -75,13 +110,3 @@ bool adapter_host_probe_vendor_control(uint8_t rhport, uint8_t stage,
|
|||
const_cast<uint8_t *>(XInputFeasibility::kMsCompatIdDescriptor),
|
||||
sizeof(XInputFeasibility::kMsCompatIdDescriptor));
|
||||
}
|
||||
|
||||
void adapter_host_probe_task() {
|
||||
if (g_mode != AdapterUsbMode::kSwitchProbe ||
|
||||
!g_probe.should_reboot(now_ms())) {
|
||||
return;
|
||||
}
|
||||
watchdog_hw->scratch[kModeScratchRegister] = kXInputBootMagic;
|
||||
PROBE_LOG("[HOST PROBE] rebooting once into XInput\n");
|
||||
watchdog_reboot(0, 0, 10);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,4 +14,3 @@ AdapterUsbMode adapter_host_probe_mode();
|
|||
void adapter_host_probe_note_string_descriptor(uint8_t index);
|
||||
bool adapter_host_probe_vendor_control(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const *request);
|
||||
void adapter_host_probe_task();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class AdapterHostProbeState {
|
|||
static constexpr uint32_t kRebootDelayMs = 100;
|
||||
|
||||
void note_ms_os_string() { saw_ms_os_string_ = true; }
|
||||
bool saw_ms_os_string() const { return saw_ms_os_string_; }
|
||||
|
||||
void note_ms_compat_id_request(uint32_t now_ms) {
|
||||
if (!saw_ms_os_string_) {
|
||||
|
|
|
|||
|
|
@ -278,9 +278,7 @@ int main() {
|
|||
|
||||
while (true) {
|
||||
tud_task(); // USB device tasks
|
||||
#ifdef SWITCH_PICO_ADAPTER_FEASIBILITY
|
||||
adapter_host_probe_task();
|
||||
#endif
|
||||
|
||||
#ifdef SWITCH_PICO_BLUEPAD32
|
||||
switch (bootsel_pairing_button_task()) {
|
||||
case BootselPairingButtonEvent::kOpenPairing:
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ void test_host_probe_sequence() {
|
|||
expect(!state.windows_confirmed(),
|
||||
"compatible-ID request without signature confirmed Windows");
|
||||
state.note_ms_os_string();
|
||||
expect(state.saw_ms_os_string(),
|
||||
"Microsoft OS string observation was not retained");
|
||||
state.note_ms_compat_id_request(20);
|
||||
expect(state.windows_confirmed(),
|
||||
"two-stage Windows signature not confirmed");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue