Skip to content

Commit

Permalink
Merge pull request #2310 from hathach/fix-usbh-enumeration-removal-race
Browse files Browse the repository at this point in the history
Fix usbh enumeration removal race
  • Loading branch information
hathach committed Nov 3, 2023
2 parents f3eaf06 + 9377fd6 commit f84eafc
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 42 deletions.
74 changes: 40 additions & 34 deletions src/host/usbh.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,12 @@ typedef struct
uint8_t rhport;
uint8_t hub_addr;
uint8_t hub_port;
uint8_t speed;

// enumeration is in progress, done when all interfaces are configured
volatile uint8_t enumerating;

// struct TU_ATTR_PACKED {
// uint8_t speed : 4; // packed speed to save footprint
// volatile uint8_t enumerating : 1;
// uint8_t TU_RESERVED : 3;
// };
struct TU_ATTR_PACKED {
uint8_t speed : 4; // packed speed to save footprint
volatile uint8_t enumerating : 1; // enumeration is in progress, false if not connected or all interfaces are configured
uint8_t TU_RESERVED : 3;
};
} usbh_dev0_t;

typedef struct {
Expand Down Expand Up @@ -431,8 +427,8 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
switch (event.event_id)
{
case HCD_EVENT_DEVICE_ATTACH:
// due to the shared _usbh_ctrl_buf, we must complete enumerating
// one device before enumerating another one.
// due to the shared _usbh_ctrl_buf, we must complete enumerating one device before enumerating another one.
// TODO better to have an separated queue for newly attached devices
if ( _dev0.enumerating ) {
TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport);

Expand Down Expand Up @@ -556,11 +552,17 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
// EP0 with setup packet
TU_VERIFY(xfer->ep_addr == 0 && xfer->setup);

// pre-check to help reducing mutex lock
TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE);

// Check if device is still connected (enumerating for dev0)
uint8_t const daddr = xfer->daddr;
if ( daddr == 0 ) {
if (!_dev0.enumerating) return false;
} else {
usbh_device_t const* dev = get_device(daddr);
if (dev && dev->connected == 0) return false;
}

// pre-check to help reducing mutex lock
TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
(void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);

bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
Expand Down Expand Up @@ -917,19 +919,23 @@ void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info)
}
}

TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
{
switch (event->event_id)
{
// case HCD_EVENT_DEVICE_REMOVE:
// // FIXME device remove from a hub need an HCD API for hcd to free up endpoint
// // mark device as removing to prevent further xfer before the event is processed in usbh task
// break;
TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) {
switch (event->event_id) {
case HCD_EVENT_DEVICE_REMOVE:
// FIXME device remove from a hub need an HCD API for hcd to free up endpoint
// mark device as removing to prevent further xfer before the event is processed in usbh task

default:
osal_queue_send(_usbh_q, event, in_isr);
break;
// Check if dev0 is removed
if ((event->rhport == _dev0.rhport) && (event->connection.hub_addr == _dev0.hub_addr) &&
(event->connection.hub_port == _dev0.hub_port)) {
_dev0.enumerating = 0;
}
break;

default: break;
}

osal_queue_send(_usbh_q, event, in_isr);
}

//--------------------------------------------------------------------+
Expand Down Expand Up @@ -1294,28 +1300,28 @@ static bool _parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configu
static void enum_full_complete(void);

// process device enumeration
static void process_enumeration(tuh_xfer_t* xfer)
{
static void process_enumeration(tuh_xfer_t* xfer) {
// Retry a few times with transfers in enumeration since device can be unstable when starting up
enum {
ATTEMPT_COUNT_MAX = 3,
ATTEMPT_DELAY_MS = 100
};
static uint8_t failed_count = 0;

if (XFER_RESULT_SUCCESS != xfer->result)
{
if (XFER_RESULT_SUCCESS != xfer->result) {
// retry if not reaching max attempt
if ( failed_count < ATTEMPT_COUNT_MAX )
{
bool retry = _dev0.enumerating && (failed_count < ATTEMPT_COUNT_MAX);
if ( retry ) {
failed_count++;
osal_task_delay(ATTEMPT_DELAY_MS); // delay a bit
TU_LOG1("Enumeration attempt %u\r\n", failed_count);
TU_ASSERT(tuh_control_xfer(xfer), );
}else
{
retry = tuh_control_xfer(xfer);
}

if (!retry) {
enum_full_complete();
}

return;
}
failed_count = 0;
Expand Down
16 changes: 8 additions & 8 deletions src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,6 @@ void __no_inline_not_in_flash_func(pio_usb_host_irq_handler)(uint8_t root_id) {
root_port_t *rport = PIO_USB_ROOT_PORT(root_id);
uint32_t const ints = rport->ints;

if ( ints & PIO_USB_INTS_CONNECT_BITS ) {
hcd_event_device_attach(tu_rhport, true);
}

if ( ints & PIO_USB_INTS_DISCONNECT_BITS ) {
hcd_event_device_remove(tu_rhport, true);
}

if ( ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS ) {
handle_endpoint_irq(rport, XFER_RESULT_SUCCESS, &rport->ep_complete);
}
Expand All @@ -202,6 +194,14 @@ void __no_inline_not_in_flash_func(pio_usb_host_irq_handler)(uint8_t root_id) {
handle_endpoint_irq(rport, XFER_RESULT_FAILED, &rport->ep_error);
}

if ( ints & PIO_USB_INTS_CONNECT_BITS ) {
hcd_event_device_attach(tu_rhport, true);
}

if ( ints & PIO_USB_INTS_DISCONNECT_BITS ) {
hcd_event_device_remove(tu_rhport, true);
}

// clear all
rport->ints &= ~ints;
}
Expand Down

0 comments on commit f84eafc

Please sign in to comment.