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
This commit is contained in:
Solomon Peachy 2026-09-27 19:01:35 -04:00
parent 583f27d5e5
commit 2745e0b973
2 changed files with 24 additions and 7 deletions

View file

@ -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:

View file

@ -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);