From 2745e0b973bf4f2982ff3216be2e45d1e9bd4b4c Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sun, 27 Sep 2026 19:01:35 -0400 Subject: [PATCH] FS#14011: Handle usb hosts whose first request is SET_ADDRESS (Anthony Fletcher) Thanks for the SET_ADDRESS rework. I think it leaves one case uncovered: a host whose first request is SET_ADDRESS ends up with a device whose interfaces are all numbered 0. Two things still depend on the core seeing a request before the address arrives: 1. usb_core assigns interfaces and endpoints only on the first control request it handles in DEFAULT. When SET_ADDRESS comes first, the driver completes it and usb_core_set_address() moves the state to ADDRESS. So allocate_interfaces_and_endpoints() never runs. 2. Under USB_DETECT_BY_REQUEST, usb.c enables the class drivers only on a USB_TRANSFER_COMPLETION event. The SET_ADDRESS status stage now completes inside the driver, so the drivers are still disabled when the address arrives. Change-Id: Ifacb8b07cbdefb0dee3d414c2257a50a08759f3d --- firmware/usb.c | 9 +++++++++ firmware/usbstack/usb_core.c | 22 +++++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/firmware/usb.c b/firmware/usb.c index 3b73f8ec34..b5b556516c 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -431,6 +431,15 @@ static void NORETURN_ATTR usb_thread(void) #ifdef HAVE_USBSTACK case USB_NOTIFY_SET_ADDR: +#ifdef USB_DETECT_BY_REQUEST + /* first sign of a host that addresses before anything else; + * the class drivers must be on before interfaces are assigned */ + if(usb_state > USB_EXTRACTED) { + usb_state = USB_INSERTED; + usb_set_host_present(true); + } +#endif + /* intentional fallthrough */ case USB_NOTIFY_SET_CONFIG: case USB_NOTIFY_BUS_RESET: case USB_NOTIFY_CLASS_DRIVER: diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c index acccee21d2..78becb0fc4 100644 --- a/firmware/usbstack/usb_core.c +++ b/firmware/usbstack/usb_core.c @@ -863,9 +863,22 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, u } } +static void usb_core_init_interfaces(void) +{ + if(usb_state == DEFAULT) { + set_serial_descriptor(); + usb_core_set_serial_function_id(); + allocate_interfaces_and_endpoints(); + } +} + static void usb_core_set_address(uint8_t address) { logf("usb_core: SET_ADR %d", address); + /* A host may address the device before making any other request; + * the driver answers SET_ADDRESS itself, so nothing has been + * allocated yet. */ + usb_core_init_interfaces(); usb_address = address; usb_state = ADDRESS; } @@ -1134,12 +1147,7 @@ static void usb_core_control_request_handler(struct usb_ctrlrequest* req, uint8_ usb_charging_maxcurrent_change(usb_charging_maxcurrent()); } #endif - if(usb_state == DEFAULT) { - set_serial_descriptor(); - usb_core_set_serial_function_id(); - - allocate_interfaces_and_endpoints(); - } + usb_core_init_interfaces(); switch(req->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: @@ -1251,7 +1259,7 @@ void usb_core_transfer_complete(int ep, int dir, int status, int length) { /* Control packet handling */ switch(dir | ep0_state) { - /* EXPECT_TX_DATA_STATUS_COMP -(status comp)-> EXPECT_TX_DATA_COMP -(data comp)-> READY + /* EXPECT_TX_DATA_STATUS_COMP -(status comp)-> EXPECT_TX_DATA_COMP -(data comp)-> READY * -(data comp)-> EXPECT_TX_STATUS_COMP -(status comp)-> READY */ case USB_DIR_OUT | EP0_EXPECT_TX_DATA_STATUS_COMP: logf("usb_core: control-in done success=%d", status == 0 && length == 0);