From 8ad024e51bbc663678d8a357f91021bd39222246 Mon Sep 17 00:00:00 2001 From: Ivo Popov Date: Sun, 9 Apr 2023 19:10:01 -0400 Subject: [PATCH 1/3] Even when we get an empty "status change" interrupt from the hub, schedule another interrupt poll. During enumeration, when there are multiple devices attached to the hub as it's plugged into the Pi Pico, enumeration hangs, because we get a "status change" callback with value zero. With this patch, we retry several times on "zero" status change callbacks, until eventually we succeed. This is the cheapo hub that exhibits this behavior, but I assume it's not the only one: https://www.amazon.com/gp/product/B083RQMC7S. While debugging this, I consulted the implementation in the Linux kernel. There, hub setup explicitly checks each port individually, before starting to depend on "status change" interrupts: https://elixir.bootlin.com/linux/latest/source/drivers/usb/core/hub.c#L1133. We probably should do something like that here, but it's a much bigger change. --- src/host/hub.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/host/hub.c b/src/host/hub.c index 85bf22b3e..5b2db547d 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -361,6 +361,13 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32 break; } } + + // The status change event was neither for the hub, nor for any of + // its ports. (For example `p_hub->status_change == 0`.) This + // shouldn't happen, but it does with some devices. Initiate the + // next interrupt poll here, because we've scheduled no other work + // whose completion can initiate it. + hub_edpt_status_xfer(dev_addr); } // NOTE: next status transfer is queued by usbh.c after handling this request From 5c428d35a61f7d877cba19d9e094bd519ae91997 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 29 May 2023 13:27:20 +0700 Subject: [PATCH 2/3] check status_change is not zero first --- src/host/hub.c | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/host/hub.c b/src/host/hub.c index 5b2db547d..0807c2023 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -327,51 +327,45 @@ static void connection_clear_conn_change_complete (tuh_xfer_t* xfer); static void connection_port_reset_complete (tuh_xfer_t* xfer); // callback as response of interrupt endpoint polling -bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) -{ +bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { (void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports (void) ep_addr; TU_ASSERT(result == XFER_RESULT_SUCCESS); hub_interface_t* p_hub = get_itf(dev_addr); - TU_LOG2(" Hub Status Change = 0x%02X\r\n", p_hub->status_change); + uint8_t const status_change = p_hub->status_change; + TU_LOG2(" Hub Status Change = 0x%02X\r\n", status_change); - // Hub bit 0 is for the hub device events - if (tu_bit_test(p_hub->status_change, 0)) - { - if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false) - { + if ( status_change == 0 ) { + // The status change event was neither for the hub, nor for any of its ports. + // This shouldn't happen, but it does with some devices. + // Initiate the next interrupt poll here. + hub_edpt_status_xfer(dev_addr); + return true; + } + + if (tu_bit_test(status_change, 0)) { + // Hub bit 0 is for the hub device events + if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false) { //Hub status control transfer failed, retry hub_edpt_status_xfer(dev_addr); } } - else - { + else { // Hub bits 1 to n are hub port events - for (uint8_t port=1; port <= p_hub->port_count; port++) - { - if ( tu_bit_test(p_hub->status_change, port) ) - { - if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false) - { + for (uint8_t port=1; port <= p_hub->port_count; port++) { + if ( tu_bit_test(status_change, port) ) { + if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false) { //Hub status control transfer failed, retry hub_edpt_status_xfer(dev_addr); } break; } } - - // The status change event was neither for the hub, nor for any of - // its ports. (For example `p_hub->status_change == 0`.) This - // shouldn't happen, but it does with some devices. Initiate the - // next interrupt poll here, because we've scheduled no other work - // whose completion can initiate it. - hub_edpt_status_xfer(dev_addr); } // NOTE: next status transfer is queued by usbh.c after handling this request - return true; } From 20ef6c4ef7f7fa7130352ae0c0984692a9719b35 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 29 May 2023 13:29:11 +0700 Subject: [PATCH 3/3] slightly clean up --- src/host/hub.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/host/hub.c b/src/host/hub.c index 0807c2023..182bd6ce8 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -341,8 +341,7 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32 // The status change event was neither for the hub, nor for any of its ports. // This shouldn't happen, but it does with some devices. // Initiate the next interrupt poll here. - hub_edpt_status_xfer(dev_addr); - return true; + return hub_edpt_status_xfer(dev_addr); } if (tu_bit_test(status_change, 0)) {