Fix XUSB binding after reconnect
This commit is contained in:
parent
e68cdcd40f
commit
1e36571770
11 changed files with 74 additions and 11 deletions
|
|
@ -101,15 +101,28 @@ 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) {
|
||||
if (stage != CONTROL_STAGE_SETUP || request == nullptr ||
|
||||
if (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) {
|
||||
return false;
|
||||
}
|
||||
const bool status_request =
|
||||
request->bRequest == kStatusRequest &&
|
||||
request->wIndex == kStatusIndex;
|
||||
const bool compatible_id_request =
|
||||
request->bRequest == XInput::kMsVendorRequest &&
|
||||
request->wIndex == XInput::kMsCompatIdIndex &&
|
||||
(g_mode == AdapterUsbMode::kSwitchProbe ||
|
||||
g_mode == AdapterUsbMode::kXInput);
|
||||
if (!status_request && !compatible_id_request) {
|
||||
return false;
|
||||
}
|
||||
if (stage != CONTROL_STAGE_SETUP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request->bRequest == kStatusRequest &&
|
||||
request->wIndex == kStatusIndex) {
|
||||
if (status_request) {
|
||||
const uint32_t current_time = now_ms();
|
||||
g_status_response[0] =
|
||||
g_mode == AdapterUsbMode::kXInput ? 1 : 0;
|
||||
|
|
@ -121,10 +134,6 @@ bool adapter_host_probe_vendor_control(uint8_t rhport, uint8_t stage,
|
|||
sizeof(g_status_response));
|
||||
}
|
||||
|
||||
if (request->bRequest != XInput::kMsVendorRequest ||
|
||||
request->wIndex != XInput::kMsCompatIdIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_mode == AdapterUsbMode::kSwitchProbe) {
|
||||
g_probe.note_ms_compat_id_request(now_ms());
|
||||
|
|
|
|||
|
|
@ -312,6 +312,13 @@ AdapterRequestedMode adapter_mode_controller_requested_mode() {
|
|||
return g_requested_mode;
|
||||
}
|
||||
|
||||
void adapter_mode_controller_on_usb_unmounted() {
|
||||
if (g_requested_mode == AdapterRequestedMode::kAuto &&
|
||||
adapter_host_probe_mode() == AdapterUsbMode::kXInput) {
|
||||
reboot_now();
|
||||
}
|
||||
}
|
||||
|
||||
void adapter_mode_controller_process_input(
|
||||
uint8_t slot_index, bool active, uint32_t connection_generation,
|
||||
uint16_t* pre_hotkey_button_mask, uint32_t now_ms,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ constexpr uint16_t ADAPTER_MODE_CHORD_BUTTON_MASK =
|
|||
void adapter_mode_controller_initialize_usb();
|
||||
AdapterRequestedMode adapter_mode_controller_requested_mode();
|
||||
|
||||
// Auto mode's XInput selection is valid for one USB attachment. Reboot to the
|
||||
// Switch probe after it unmounts so the next host attachment is detected
|
||||
// afresh. Persistent manual XInput mode is intentionally unaffected.
|
||||
void adapter_mode_controller_on_usb_unmounted();
|
||||
|
||||
// Observes the physical pre-hotkey mask and removes the mode chord from both
|
||||
// that mask and the state before profile processing. Slot state is isolated by
|
||||
// connection generation and all time comparisons are uint32-wrap safe.
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -63,7 +63,7 @@ bool valid_identity(const ControllerIdentity& identity) {
|
|||
}
|
||||
|
||||
void refresh_list_locked() {
|
||||
g_list = {};
|
||||
g_list = ProfileServiceListSnapshot{};
|
||||
g_list.metadata = g_metadata;
|
||||
g_list.count = 1;
|
||||
g_list.rows[0].identity = controller_identity_global();
|
||||
|
|
@ -183,7 +183,7 @@ void profile_service_prepare() {
|
|||
critical_section_init(&g_lock);
|
||||
g_metadata = {};
|
||||
__atomic_store_n(&g_published_generation, 0, __ATOMIC_RELAXED);
|
||||
g_list = {};
|
||||
g_list = ProfileServiceListSnapshot{};
|
||||
g_selected = {};
|
||||
g_active_profiles[0] = {};
|
||||
g_active_profile_count = 0;
|
||||
|
|
|
|||
|
|
@ -181,6 +181,11 @@ static void log_usb_state() {
|
|||
if (mounted != g_last_mounted) {
|
||||
g_last_mounted = mounted;
|
||||
LOG_PRINTF("[USB] %s\n", mounted ? "mounted" : "unmounted");
|
||||
#ifdef SWITCH_PICO_BLUEPAD32
|
||||
if (!mounted) {
|
||||
adapter_mode_controller_on_usb_unmounted();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SWITCH_PICO_BLUEPAD32
|
||||
|
|
|
|||
|
|
@ -115,6 +115,12 @@ void test_manual_modes_bypass_and_consume_probe_state() {
|
|||
control_length) == 0 &&
|
||||
alarm_count == 0,
|
||||
"manual XInput did not serve XInput descriptors without probing");
|
||||
require(adapter_host_probe_vendor_control(
|
||||
0, CONTROL_STAGE_DATA, &request) &&
|
||||
adapter_host_probe_vendor_control(
|
||||
0, CONTROL_STAGE_ACK, &request) &&
|
||||
control_count == 1,
|
||||
"manual XInput did not retain the compatible-ID transfer");
|
||||
|
||||
reset_harness(kXInputBootMagic);
|
||||
adapter_host_probe_init(AdapterRequestedMode::kDInput);
|
||||
|
|
@ -154,6 +160,12 @@ void test_probe_transition_resets_before_watchdog() {
|
|||
alarm_count == 1 && pending_alarm_delay_ms == 100 &&
|
||||
reboot_count == 0,
|
||||
"auto probe did not queue the exact delayed transition");
|
||||
require(adapter_host_probe_vendor_control(
|
||||
0, CONTROL_STAGE_DATA, &request) &&
|
||||
adapter_host_probe_vendor_control(
|
||||
0, CONTROL_STAGE_ACK, &request) &&
|
||||
control_count == 1 && alarm_count == 1,
|
||||
"auto probe did not retain the compatible-ID transfer");
|
||||
|
||||
pending_alarm(1, pending_alarm_user_data);
|
||||
require(reset_count == 1 && reboot_count == 1 &&
|
||||
|
|
|
|||
|
|
@ -532,6 +532,27 @@ void test_failed_recovery_never_reboots() {
|
|||
"or failed recovery acknowledged, released ownership, or retried");
|
||||
}
|
||||
|
||||
void test_auto_xinput_disconnect_reboots_to_probe() {
|
||||
reset_harness(AdapterRequestedMode::kAuto);
|
||||
probed_active_mode = AdapterUsbMode::kXInput;
|
||||
adapter_mode_controller_on_usb_unmounted();
|
||||
adapter_mode_controller_on_usb_unmounted();
|
||||
require(runtime_reset_count == 1 && reboot_count == 1,
|
||||
"Auto XInput unmount did not schedule exactly one probe reboot");
|
||||
|
||||
reset_harness(AdapterRequestedMode::kXInput);
|
||||
probed_active_mode = AdapterUsbMode::kXInput;
|
||||
adapter_mode_controller_on_usb_unmounted();
|
||||
require(runtime_reset_count == 0 && reboot_count == 0,
|
||||
"manual XInput mode rebooted after USB unmount");
|
||||
|
||||
reset_harness(AdapterRequestedMode::kAuto);
|
||||
probed_active_mode = AdapterUsbMode::kSwitchProbe;
|
||||
adapter_mode_controller_on_usb_unmounted();
|
||||
require(runtime_reset_count == 0 && reboot_count == 0,
|
||||
"Auto Switch probe rebooted after USB unmount");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void configuration_service_initialize_pre_usb() {
|
||||
|
|
@ -659,5 +680,6 @@ int main() {
|
|||
test_configuration_failure_and_correlated_reboot();
|
||||
test_recovery_auto_wins_host_mode_interleaving();
|
||||
test_failed_recovery_never_reboots();
|
||||
test_auto_xinput_disconnect_reboots_to_probe();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,9 +146,12 @@ void test_microsoft_compatible_id_descriptor() {
|
|||
"Microsoft function count mismatch");
|
||||
for (uint8_t instance = 0;
|
||||
instance < SWITCH_PICO_HID_INSTANCE_COUNT; ++instance) {
|
||||
const uint8_t *function = &kMsCompatIdDescriptor[16 + instance * 24];
|
||||
const uint8_t* function =
|
||||
&kMsCompatIdDescriptor[16 + instance * 24];
|
||||
expect(function[0] == instance,
|
||||
"Microsoft descriptor interface mismatch");
|
||||
expect(function[1] == 1,
|
||||
"XUSB function must own exactly one interface");
|
||||
expect(std::memcmp(&function[2], "XUSB10", 6) == 0,
|
||||
"XUSB10 compatible ID missing");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace XInput {
|
|||
|
||||
constexpr uint16_t kDevelopmentVendorId = 0xcafe;
|
||||
constexpr uint16_t kDevelopmentProductId = 0x4010;
|
||||
constexpr uint16_t kDevelopmentDeviceRevision = 0x0101;
|
||||
constexpr uint16_t kDevelopmentDeviceRevision = 0x0105;
|
||||
constexpr uint8_t kInterfaceDescriptorSize = 39;
|
||||
constexpr uint16_t kConfigurationDescriptorSize =
|
||||
9 + SWITCH_PICO_HID_INSTANCE_COUNT * kInterfaceDescriptorSize;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue