diff options
| author | HiFiPhile <[email protected]> | 2026-01-06 18:42:58 +0100 |
|---|---|---|
| committer | HiFiPhile <[email protected]> | 2026-01-06 18:42:58 +0100 |
| commit | 60154a128795bcd6ca36042cac1b8bebf1a2764e (patch) | |
| tree | 0557ab4b2a01c64de109c54662ba6f3a58147bc9 /src/host | |
| parent | c5f91ec6f54509d03529e276ffd42b0d9abab695 (diff) | |
| parent | 593dce28349cf5b1299c2cf1193312cb783574f3 (diff) | |
Merge remote-tracking branch 'tinyusb/master' into ep0_direct
Diffstat (limited to 'src/host')
| -rw-r--r-- | src/host/usbh.c | 99 | ||||
| -rw-r--r-- | src/host/usbh.h | 11 |
2 files changed, 72 insertions, 38 deletions
diff --git a/src/host/usbh.c b/src/host/usbh.c index 5b14a15cb..a725b7c8b 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -312,7 +312,8 @@ TU_ATTR_ALWAYS_INLINE static inline usbh_class_driver_t const *get_driver(uint8_ // Function Inline and Prototypes //--------------------------------------------------------------------+ static bool enum_new_device(hcd_event_t* event); -static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port); +static void process_remove_event(hcd_event_t *event); +static void remove_device_tree(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port); static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size); static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); @@ -540,8 +541,8 @@ bool tuh_deinit(uint8_t rhport) { hcd_deinit(rhport); _usbh_data.controller_id = TUSB_INDEX_INVALID_8; - // "unplug" all devices on this rhport (hub_addr = 0, hub_port = 0) - process_removed_device(rhport, 0, 0); + // remove all devices on this rhport (hub_addr = 0, hub_port = 0) + remove_device_tree(rhport, 0, 0); // deinit host stack if no controller is active if (!tuh_inited()) { @@ -605,6 +606,11 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) { switch (event.event_id) { case HCD_EVENT_DEVICE_ATTACH: + // Should we miss the hub detach event due to high traffic, Or due to physical debouncing, some devices can + // cause multiple attaches (actually reset) without detach event. + // Force remove currently mounted with the same bus info (rhport, hub addr, hub port) if exists + process_remove_event(&event); + // due to the shared control buffer, we must fully complete enumerating one device first. // TODO better to have an separated queue for newly attached devices if (_usbh_data.enumerating_daddr == TUSB_INDEX_INVALID_8) { @@ -625,15 +631,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) { case HCD_EVENT_DEVICE_REMOVE: TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port); - if (_usbh_data.enumerating_daddr == 0 && - event.rhport == _usbh_data.dev0_bus.rhport && - event.connection.hub_addr == _usbh_data.dev0_bus.hub_addr && - event.connection.hub_port == _usbh_data.dev0_bus.hub_port) { - // dev0 is unplugged while enumerating (not yet assigned an address) - usbh_device_close(_usbh_data.dev0_bus.rhport, 0); - } else { - process_removed_device(event.rhport, event.connection.hub_addr, event.connection.hub_port); - } + process_remove_event(&event); break; case HCD_EVENT_XFER_COMPLETE: { @@ -1321,8 +1319,22 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt, //--------------------------------------------------------------------+ // Detaching //--------------------------------------------------------------------+ -// a device unplugged from rhport:hub_addr:hub_port -static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) { + +// process detach event from rhport:hub_addr:hub_port +static void process_remove_event(hcd_event_t *event) { + if (_usbh_data.enumerating_daddr == 0 && + event->rhport == _usbh_data.dev0_bus.rhport && + event->connection.hub_addr == _usbh_data.dev0_bus.hub_addr && + event->connection.hub_port == _usbh_data.dev0_bus.hub_port) { + // dev0 is unplugged while enumerating (not yet assigned an address) + usbh_device_close(_usbh_data.dev0_bus.rhport, 0); + } else { + remove_device_tree(event->rhport, event->connection.hub_addr, event->connection.hub_port); + } +} + +// remove a device at rhport:hub_addr:hub_port and all of its downstream +static void remove_device_tree(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) { // Find the all devices (star-network) under port that is unplugged #if CFG_TUH_HUB uint8_t removing_hubs[CFG_TUH_HUB] = { 0 }; @@ -1393,12 +1405,13 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub // NOTE: due to the shared control buffer, we must complete enumerating // one device before enumerating another one. //--------------------------------------------------------------------+ -enum { // USB 2.0 specs 7.1.7 for timing - ENUM_DEBOUNCING_DELAY_MS = 150, // T(ATTDB) minimum 100 ms for stable connection - ENUM_RESET_ROOT_DELAY_MS = 50, // T(DRSTr) minimum 50 ms for reset from root port - ENUM_RESET_HUB_DELAY_MS = 20, // T(DRST) 10-20 ms for hub reset - ENUM_RESET_RECOVERY_DELAY_MS = 10, // T(RSTRCY) minimum 10 ms for reset recovery - ENUM_SET_ADDRESS_RECOVERY_DELAY_MS = 2, // USB 2.0 Spec 9.2.6.3 min is 2 ms +enum { // USB 2.0 specs 7.1.7 for timing + ENUM_DEBOUNCING_DELAY_MS = 150, // T(ATTDB) minimum 100 ms for stable connection + ENUM_RESET_ROOT_DELAY_MS = 50, // T(DRSTr) minimum 50 ms for reset from root port + ENUM_RESET_ROOT_POST_DELAY_MS = 2, // 2 ms delay after root port reset before getting speed/status + ENUM_RESET_HUB_DELAY_MS = 20, // T(DRST) 10-20 ms for hub reset + ENUM_RESET_RECOVERY_DELAY_MS = 10, // T(RSTRCY) minimum 10 ms for reset recovery + ENUM_SET_ADDRESS_RECOVERY_DELAY_MS = 2, // USB 2.0 Spec 9.2.6.3 min is 2 ms }; enum { @@ -1427,7 +1440,7 @@ enum { static uint8_t enum_get_new_address(bool is_hub); static bool enum_parse_configuration_desc (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); -static void enum_full_complete(void); +static void enum_full_complete(bool success); static void process_enumeration(tuh_xfer_t* xfer); // start a new enumeration process @@ -1449,7 +1462,7 @@ static bool enum_new_device(hcd_event_t* event) { if (!hcd_port_connect_status(dev0_bus->rhport)) { TU_LOG_USBH("Device unplugged while debouncing\r\n"); - enum_full_complete(); + enum_full_complete(false); return true; } @@ -1457,10 +1470,11 @@ static bool enum_new_device(hcd_event_t* event) { hcd_port_reset(dev0_bus->rhport); tusb_time_delay_ms_api(ENUM_RESET_ROOT_DELAY_MS); hcd_port_reset_end(dev0_bus->rhport); + tusb_time_delay_ms_api(ENUM_RESET_ROOT_POST_DELAY_MS); if (!hcd_port_connect_status(dev0_bus->rhport)) { // device unplugged while delaying - enum_full_complete(); + enum_full_complete(false); return true; } @@ -1506,7 +1520,7 @@ static void process_enumeration(tuh_xfer_t* xfer) { } if (!retry) { - enum_full_complete(); // complete as failed + enum_full_complete(false); // complete as failed } return; } @@ -1529,7 +1543,7 @@ static void process_enumeration(tuh_xfer_t* xfer) { if (0 == port_status.status.connection) { TU_LOG_USBH("Device unplugged from hub while debouncing\r\n"); - enum_full_complete(); + enum_full_complete(false); return; } @@ -1566,7 +1580,7 @@ static void process_enumeration(tuh_xfer_t* xfer) { if (0 == port_status.status.connection) { TU_LOG_USBH("Device unplugged from hub (not addressed yet)\r\n"); - enum_full_complete(); + enum_full_complete(false); return; } @@ -1582,7 +1596,11 @@ static void process_enumeration(tuh_xfer_t* xfer) { // TODO probably doesn't need to open/close each enumeration uint8_t const addr0 = 0; - TU_ASSERT(usbh_edpt_control_open(addr0, 8),); + if (!usbh_edpt_control_open(addr0, 8)) { + // Stop enumeration gracefully + enum_full_complete(false); + TU_ASSERT(false,); + } // Get first 8 bytes of device descriptor for control endpoint size TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n"); @@ -1592,10 +1610,6 @@ static void process_enumeration(tuh_xfer_t* xfer) { } case ENUM_SET_ADDR: { - // Due to physical debouncing, some devices can cause multiple attaches (actually reset) without detach event - // Force remove currently mounted with the same bus info (rhport, hub addr, hub port) if exists - process_removed_device(dev0_bus->rhport, dev0_bus->hub_addr, dev0_bus->hub_port); - const tusb_desc_device_t *desc_device = (const tusb_desc_device_t *) _usbh_epbuf.ctrl; const uint8_t new_addr = enum_get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB); TU_ASSERT(new_addr != 0,); @@ -1620,7 +1634,12 @@ static void process_enumeration(tuh_xfer_t* xfer) { usbh_device_close(dev0_bus->rhport, 0); // close dev0 - TU_ASSERT(usbh_edpt_control_open(new_addr, new_dev->bMaxPacketSize0),); // open new control endpoint + if (!usbh_edpt_control_open(new_addr, new_dev->bMaxPacketSize0)) { // open new control endpoint + // Stop enumeration gracefully + clear_device(new_dev); + enum_full_complete(false); + TU_ASSERT(false,); + } TU_LOG_USBH("Get Device Descriptor\r\n"); TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_epbuf.ctrl, sizeof(tusb_desc_device_t), @@ -1774,6 +1793,12 @@ static void process_enumeration(tuh_xfer_t* xfer) { TU_LOG_USBH("Device configured\r\n"); dev->configured = 1; + #if CFG_TUH_HUB + if (_usbh_data.dev0_bus.hub_addr != 0) { + hub_edpt_status_xfer(_usbh_data.dev0_bus.hub_addr); // get next hub status + } + #endif + // Parse configuration & set up drivers // driver_open() must not make any usb transfer TU_ASSERT(enum_parse_configuration_desc(daddr, (tusb_desc_configuration_t*) _usbh_epbuf.ctrl),); @@ -1787,7 +1812,7 @@ static void process_enumeration(tuh_xfer_t* xfer) { } default: - enum_full_complete(); // stop enumeration if unknown state + enum_full_complete(false); // stop enumeration if unknown state break; } } @@ -1890,7 +1915,7 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) { // all interface are configured if (itf_num == CFG_TUH_INTERFACE_MAX) { - enum_full_complete(); + enum_full_complete(true); if (is_hub_addr(dev_addr)) { TU_LOG_USBH("HUB address = %u is mounted\r\n", dev_addr); @@ -1901,12 +1926,14 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) { } } -static void enum_full_complete(void) { +static void enum_full_complete(bool success) { + (void)success; // mark enumeration as complete _usbh_data.enumerating_daddr = TUSB_INDEX_INVALID_8; #if CFG_TUH_HUB - if (_usbh_data.dev0_bus.hub_addr != 0) { + // Hub status is already requested in case of successful enumeration + if (_usbh_data.dev0_bus.hub_addr != 0 && !success) { hub_edpt_status_xfer(_usbh_data.dev0_bus.hub_addr); // get next hub status } #endif diff --git a/src/host/usbh.h b/src/host/usbh.h index 4b6747848..d86efbcb2 100644 --- a/src/host/usbh.h +++ b/src/host/usbh.h @@ -95,18 +95,23 @@ enum { TUH_CFGID_INVALID = 0, TUH_CFGID_RPI_PIO_USB_CONFIGURATION = 100, // cfg_param: pio_usb_configuration_t TUH_CFGID_MAX3421 = 200, + TUH_CFGID_FSDEV = 300, }; typedef struct { - uint8_t max_nak; // max NAK per endpoint per frame to save CPU/SPI bus usage + uint8_t max_nak; // max NAK per endpoint per frame to save CPU/SPI bus usage (0=unlimited) uint8_t cpuctl; // R16: CPU Control Register uint8_t pinctl; // R17: Pin Control Register. FDUPSPI bit is ignored } tuh_configure_max3421_t; +typedef struct { + uint8_t max_nak; // max NAK per endpoint per frame to save CPU usage (0=unlimited) +} tuh_configure_fsdev_t; + typedef union { // For TUH_CFGID_RPI_PIO_USB_CONFIGURATION use pio_usb_configuration_t - tuh_configure_max3421_t max3421; + tuh_configure_fsdev_t fsdev; } tuh_configure_param_t; //--------------------------------------------------------------------+ @@ -145,6 +150,7 @@ void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr); bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param); // New API to replace tuh_init() to init host stack on specific roothub port +// Must be called in the same task/context as tuh_task() if RTOS is used bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init); // Init host stack @@ -160,6 +166,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool tuh_init(uint8_t rhport) { } // Deinit host stack on rhport +// Must be called in the same task/context as tuh_task() if RTOS is used bool tuh_deinit(uint8_t rhport); // Check if host stack is already initialized with any roothub ports |
