3434#include "hub.h"
3535
3636//--------------------------------------------------------------------+
37- // USBH Configuration
37+ // Configuration
3838//--------------------------------------------------------------------+
3939#ifndef CFG_TUH_TASK_QUEUE_SZ
4040 #define CFG_TUH_TASK_QUEUE_SZ 16
@@ -89,7 +89,7 @@ TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_si
8989}
9090
9191//--------------------------------------------------------------------+
92- // USBH-HCD common data structure
92+ // Data Structure
9393//--------------------------------------------------------------------+
9494typedef struct {
9595 tuh_bus_info_t bus_info ;
@@ -131,8 +131,60 @@ typedef struct {
131131
132132} usbh_device_t ;
133133
134+ // sum of end device + hub
135+ #define TOTAL_DEVICES (CFG_TUH_DEVICE_MAX + CFG_TUH_HUB)
136+
137+ // all devices excluding zero-address
138+ // hub address start from CFG_TUH_DEVICE_MAX+1
139+ // TODO: hub can has its own simpler struct to save memory
140+ static usbh_device_t _usbh_devices [TOTAL_DEVICES ];
141+
142+ // Mutex for claiming endpoint
143+ #if OSAL_MUTEX_REQUIRED
144+ static osal_mutex_def_t _usbh_mutexdef ;
145+ static osal_mutex_t _usbh_mutex ;
146+ #else
147+ #define _usbh_mutex NULL
148+ #endif
149+
150+ // Event queue: usbh_int_set() is used as mutex in OS NONE config
151+ OSAL_QUEUE_DEF (usbh_int_set , _usbh_qdef , CFG_TUH_TASK_QUEUE_SZ , hcd_event_t );
152+ static osal_queue_t _usbh_q ;
153+
154+ // Control transfers: since most controllers do not support multiple control transfers
155+ // on multiple devices concurrently and control transfers are not used much except for
156+ // enumeration, we will only execute control transfers one at a time.
157+ typedef struct {
158+ uint8_t * buffer ;
159+ tuh_xfer_cb_t complete_cb ;
160+ uintptr_t user_data ;
161+
162+ volatile uint8_t stage ;
163+ uint8_t daddr ;
164+ volatile uint16_t actual_len ;
165+ uint8_t failed_count ;
166+ } usbh_ctrl_xfer_info_t ;
167+
168+ typedef struct {
169+ uint8_t controller_id ; // controller ID
170+ uint8_t enumerating_daddr ; // device address of the device being enumerated
171+ uint8_t attach_debouncing_bm ; // bitmask for roothub port attach debouncing
172+ tuh_bus_info_t dev0_bus ; // bus info for dev0 in enumeration
173+ usbh_ctrl_xfer_info_t ctrl_xfer_info ; // control transfer
174+ } usbh_data_t ;
175+
176+ static usbh_data_t _usbh_data = {
177+ .controller_id = TUSB_INDEX_INVALID_8 ,
178+ };
179+
180+ typedef struct {
181+ TUH_EPBUF_TYPE_DEF (tusb_control_request_t , request );
182+ TUH_EPBUF_DEF (ctrl , CFG_TUH_ENUMERATION_BUFSIZE );
183+ } usbh_epbuf_t ;
184+ CFG_TUH_MEM_SECTION static usbh_epbuf_t _usbh_epbuf ;
185+
134186//--------------------------------------------------------------------+
135- // MACRO CONSTANT TYPEDEF
187+ // Class Driver
136188//--------------------------------------------------------------------+
137189#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
138190 #define DRIVER_NAME (_name ) _name
@@ -235,82 +287,62 @@ static inline usbh_class_driver_t const *get_driver(uint8_t drv_id) {
235287}
236288
237289//--------------------------------------------------------------------+
238- // INTERNAL OBJECT & FUNCTION DECLARATION
290+ // Function Inline and Prototypes
239291//--------------------------------------------------------------------+
292+ static bool enum_new_device (hcd_event_t * event );
293+ static void process_removed_device (uint8_t rhport , uint8_t hub_addr , uint8_t hub_port );
294+ static bool usbh_edpt_control_open (uint8_t dev_addr , uint8_t max_packet_size );
295+ static bool usbh_control_xfer_cb (uint8_t daddr , uint8_t ep_addr , xfer_result_t result , uint32_t xferred_bytes );
240296
241- // sum of end device + hub
242- #define TOTAL_DEVICES (CFG_TUH_DEVICE_MAX + CFG_TUH_HUB)
243-
244- // all devices excluding zero-address
245- // hub address start from CFG_TUH_DEVICE_MAX+1
246- // TODO: hub can has its own simpler struct to save memory
247- static usbh_device_t _usbh_devices [TOTAL_DEVICES ];
248-
249- // Mutex for claiming endpoint
250- #if OSAL_MUTEX_REQUIRED
251- static osal_mutex_def_t _usbh_mutexdef ;
252- static osal_mutex_t _usbh_mutex ;
253- #else
254- #define _usbh_mutex NULL
255- #endif
256-
257- // Event queue: usbh_int_set() is used as mutex in OS NONE config
258- OSAL_QUEUE_DEF (usbh_int_set , _usbh_qdef , CFG_TUH_TASK_QUEUE_SZ , hcd_event_t );
259- static osal_queue_t _usbh_q ;
260-
261- // Control transfers: since most controllers do not support multiple control transfers
262- // on multiple devices concurrently and control transfers are not used much except for
263- // enumeration, we will only execute control transfers one at a time.
264- typedef struct {
265- uint8_t * buffer ;
266- tuh_xfer_cb_t complete_cb ;
267- uintptr_t user_data ;
268-
269- volatile uint8_t stage ;
270- uint8_t daddr ;
271- volatile uint16_t actual_len ;
272- uint8_t failed_count ;
273- } usbh_ctrl_xfer_info_t ;
274-
275- typedef struct {
276- uint8_t controller_id ; // controller ID
277- uint8_t enumerating_daddr ; // device address of the device being enumerated
278- uint8_t attach_debouncing_bm ; // bitmask for roothub port attach debouncing
279- tuh_bus_info_t dev0_bus ; // bus info for dev0 in enumeration
280- usbh_ctrl_xfer_info_t ctrl_xfer_info ; // control transfer
281- } usbh_data_t ;
282-
283- static usbh_data_t _usbh_data = {
284- .controller_id = TUSB_INDEX_INVALID_8 ,
285- };
286-
287- typedef struct {
288- TUH_EPBUF_TYPE_DEF (tusb_control_request_t , request );
289- TUH_EPBUF_DEF (ctrl , CFG_TUH_ENUMERATION_BUFSIZE );
290- } usbh_epbuf_t ;
291- CFG_TUH_MEM_SECTION static usbh_epbuf_t _usbh_epbuf ;
292-
293- //------------- Helper Function -------------//
294297TU_ATTR_ALWAYS_INLINE static inline usbh_device_t * get_device (uint8_t dev_addr ) {
295298 TU_VERIFY (dev_addr > 0 && dev_addr <= TOTAL_DEVICES , NULL );
296299 return & _usbh_devices [dev_addr - 1 ];
297300}
298301
299- static bool enum_new_device (hcd_event_t * event );
300- static void process_removed_device (uint8_t rhport , uint8_t hub_addr , uint8_t hub_port );
301- static bool usbh_edpt_control_open (uint8_t dev_addr , uint8_t max_packet_size );
302- static bool usbh_control_xfer_cb (uint8_t daddr , uint8_t ep_addr , xfer_result_t result , uint32_t xferred_bytes );
302+ TU_ATTR_ALWAYS_INLINE static inline bool is_hub_addr (uint8_t daddr ) {
303+ return (CFG_TUH_HUB > 0 ) && (daddr > CFG_TUH_DEVICE_MAX );
304+ }
303305
304306TU_ATTR_ALWAYS_INLINE static inline bool queue_event (hcd_event_t const * event , bool in_isr ) {
305307 TU_ASSERT (osal_queue_send (_usbh_q , event , in_isr ));
306308 tuh_event_hook_cb (event -> rhport , event -> event_id , in_isr );
307309 return true;
308310}
309311
312+ TU_ATTR_ALWAYS_INLINE static inline void _control_set_xfer_stage (uint8_t stage ) {
313+ if (_usbh_data .ctrl_xfer_info .stage != stage ) {
314+ (void ) osal_mutex_lock (_usbh_mutex , OSAL_TIMEOUT_WAIT_FOREVER );
315+ _usbh_data .ctrl_xfer_info .stage = stage ;
316+ (void ) osal_mutex_unlock (_usbh_mutex );
317+ }
318+ }
319+
320+ TU_ATTR_ALWAYS_INLINE static inline bool usbh_setup_send (uint8_t daddr , const uint8_t setup_packet [8 ]) {
321+ const uint8_t rhport = usbh_get_rhport (daddr );
322+ const bool ret = hcd_setup_send (rhport , daddr , setup_packet );
323+ if (!ret ) {
324+ _control_set_xfer_stage (CONTROL_STAGE_IDLE );
325+ }
326+ return ret ;
327+ }
328+
329+ TU_ATTR_ALWAYS_INLINE static inline void usbh_device_close (uint8_t rhport , uint8_t daddr ) {
330+ hcd_device_close (rhport , daddr );
331+
332+ // abort any ongoing control transfer
333+ if (daddr == _usbh_data .ctrl_xfer_info .daddr ) {
334+ _control_set_xfer_stage (CONTROL_STAGE_IDLE );
335+ }
336+
337+ // invalidate if enumerating
338+ if (daddr == _usbh_data .enumerating_daddr ) {
339+ _usbh_data .enumerating_daddr = TUSB_INDEX_INVALID_8 ;
340+ }
341+ }
342+
310343//--------------------------------------------------------------------+
311344// Device API
312345//--------------------------------------------------------------------+
313-
314346bool tuh_mounted (uint8_t dev_addr ) {
315347 usbh_device_t * dev = get_device (dev_addr );
316348 TU_VERIFY (dev );
@@ -530,16 +562,16 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
530562 break ;
531563
532564 case HCD_EVENT_DEVICE_REMOVE :
533- TU_LOG_USBH ("[%u:%u:%u] USBH DEVICE REMOVED\r\n" , event .rhport , event .connection .hub_addr , event .connection .hub_port );
534- process_removed_device (event .rhport , event .connection .hub_addr , event .connection .hub_port );
535-
536- #if CFG_TUH_HUB
537- // TODO remove
538- if (event .connection .hub_addr != 0 && event .connection .hub_port != 0 ) {
539- // done with hub, waiting for next data on status pipe
540- (void ) hub_edpt_status_xfer (event .connection .hub_addr );
565+ TU_LOG1 ("[%u:%u:%u] USBH DEVICE REMOVED\r\n" , event .rhport , event .connection .hub_addr , event .connection .hub_port );
566+ if (_usbh_data .enumerating_daddr == 0 &&
567+ event .rhport == _usbh_data .dev0_bus .rhport &&
568+ event .connection .hub_addr == _usbh_data .dev0_bus .hub_addr &&
569+ event .connection .hub_port == _usbh_data .dev0_bus .hub_port ) {
570+ // dev0 is unplugged while enumerating (not yet assigned an address)
571+ usbh_device_close (_usbh_data .dev0_bus .rhport , 0 );
572+ } else {
573+ process_removed_device (event .rhport , event .connection .hub_addr , event .connection .hub_port );
541574 }
542- #endif
543575 break ;
544576
545577 case HCD_EVENT_XFER_COMPLETE : {
@@ -623,23 +655,6 @@ static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
623655 * ((xfer_result_t * ) xfer -> user_data ) = xfer -> result ;
624656}
625657
626- TU_ATTR_ALWAYS_INLINE static inline void _control_set_xfer_stage (uint8_t stage ) {
627- if (_usbh_data .ctrl_xfer_info .stage != stage ) {
628- (void ) osal_mutex_lock (_usbh_mutex , OSAL_TIMEOUT_WAIT_FOREVER );
629- _usbh_data .ctrl_xfer_info .stage = stage ;
630- (void ) osal_mutex_unlock (_usbh_mutex );
631- }
632- }
633-
634- TU_ATTR_ALWAYS_INLINE static inline bool usbh_setup_send (uint8_t daddr , const uint8_t setup_packet [8 ]) {
635- const uint8_t rhport = usbh_get_rhport (daddr );
636- const bool ret = hcd_setup_send (rhport , daddr , setup_packet );
637- if (!ret ) {
638- _control_set_xfer_stage (CONTROL_STAGE_IDLE );
639- }
640- return ret ;
641- }
642-
643658// TODO timeout_ms is not supported yet
644659bool tuh_control_xfer (tuh_xfer_t * xfer ) {
645660 TU_VERIFY (xfer -> ep_addr == 0 && xfer -> setup ); // EP0 with setup packet
@@ -1263,28 +1278,13 @@ uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_i
12631278//--------------------------------------------------------------------+
12641279// Detaching
12651280//--------------------------------------------------------------------+
1266-
1267- TU_ATTR_ALWAYS_INLINE static inline bool is_hub_addr (uint8_t daddr ) {
1268- return (CFG_TUH_HUB > 0 ) && (daddr > CFG_TUH_DEVICE_MAX );
1269- }
1270-
12711281// a device unplugged from rhport:hub_addr:hub_port
12721282static void process_removed_device (uint8_t rhport , uint8_t hub_addr , uint8_t hub_port ) {
1273- // if dev0 is unplugged while enumerating (not yet assigned an address)
1274- if (_usbh_data .enumerating_daddr == 0 ) {
1275- const tuh_bus_info_t * dev0_bus = & _usbh_data .dev0_bus ;
1276- if ((rhport == dev0_bus -> rhport ) && (hub_addr == dev0_bus -> hub_addr ) && (hub_port == dev0_bus -> hub_port )) {
1277- hcd_device_close (dev0_bus -> rhport , 0 );
1278- if (_usbh_data .ctrl_xfer_info .daddr == 0 ) {
1279- _control_set_xfer_stage (CONTROL_STAGE_IDLE );
1280- }
1281- _usbh_data .enumerating_daddr = TUSB_INDEX_INVALID_8 ;
1282- return ;
1283- }
1284- }
1283+ // Find the all devices (star-network) under port that is unplugged
1284+ #if CFG_TUH_HUB
1285+ uint8_t removing_hubs [CFG_TUH_HUB ] = { 0 };
1286+ #endif
12851287
1286- //------------- find the all devices (star-network) under port that is unplugged -------------//
1287- uint32_t removing_hubs = 0 ;
12881288 do {
12891289 for (uint8_t dev_id = 0 ; dev_id < TOTAL_DEVICES ; dev_id ++ ) {
12901290 usbh_device_t * dev = & _usbh_devices [dev_id ];
@@ -1296,10 +1296,13 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub
12961296 (hub_port == 0 || dev -> bus_info .hub_port == hub_port )) {
12971297 TU_LOG_USBH ("[%u:%u:%u] unplugged address = %u\r\n" , rhport , hub_addr , hub_port , daddr );
12981298
1299+ #if CFG_TUH_HUB
12991300 if (is_hub_addr (daddr )) {
13001301 TU_LOG_USBH (" is a HUB device %u\r\n" , daddr );
1301- removing_hubs |= TU_BIT (dev_id - CFG_TUH_DEVICE_MAX );
1302- } else {
1302+ removing_hubs [dev_id - CFG_TUH_DEVICE_MAX ] = 1 ;
1303+ } else
1304+ #endif
1305+ {
13031306 // Invoke callback before closing driver (maybe call it later ?)
13041307 if (tuh_umount_cb ) {
13051308 tuh_umount_cb (daddr );
@@ -1314,41 +1317,32 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub
13141317 }
13151318 }
13161319
1317- hcd_device_close (rhport , daddr );
1320+ usbh_device_close (rhport , daddr );
13181321 clear_device (dev );
1319-
1320- // abort ongoing control xfer on this device if any
1321- if (daddr == _usbh_data .ctrl_xfer_info .daddr ) {
1322- _control_set_xfer_stage (CONTROL_STAGE_IDLE );
1323- }
1324-
1325- if (daddr == _usbh_data .enumerating_daddr ) {
1326- _usbh_data .enumerating_daddr = TUSB_INDEX_INVALID_8 ;
1327- }
13281322 }
13291323 }
13301324
1331- // if removing a hub, we need to remove all of its downstream devices
1332- # if CFG_TUH_HUB
1333- if (removing_hubs == 0 ) {
1325+ # if CFG_TUH_HUB
1326+ // if a hub is removed, we need to remove all of its downstream devices
1327+ if (tu_mem_is_zero ( removing_hubs , CFG_TUH_HUB ) ) {
13341328 break ;
13351329 }
13361330
13371331 // find a marked hub to process
13381332 for (uint8_t h_id = 0 ; h_id < CFG_TUH_HUB ; h_id ++ ) {
1339- if (tu_bit_test ( removing_hubs , h_id ) ) {
1340- removing_hubs &= ~ TU_BIT ( h_id ) ;
1333+ if (removing_hubs [ h_id ] ) {
1334+ removing_hubs [ h_id ] = 0 ;
13411335
13421336 // update hub_addr and hub_port for next loop
13431337 hub_addr = h_id + 1 + CFG_TUH_DEVICE_MAX ;
13441338 hub_port = 0 ;
13451339 break ;
13461340 }
13471341 }
1348- #else
1349- (void ) removing_hubs ;
1342+ #else
13501343 break ;
1351- #endif
1344+ #endif
1345+
13521346 } while (1 );
13531347}
13541348
@@ -1560,6 +1554,10 @@ static void process_enumeration(tuh_xfer_t* xfer) {
15601554 }
15611555
15621556 case ENUM_SET_ADDR : {
1557+ // Due to physical debouncing, some devices can cause multiple attaches (actually reset) without detach event
1558+ // Force remove currently mounted with the same bus info (rhport, hub addr, hub port) if exists
1559+ process_removed_device (dev0_bus -> rhport , dev0_bus -> hub_addr , dev0_bus -> hub_port );
1560+
15631561 const tusb_desc_device_t * desc_device = (const tusb_desc_device_t * ) _usbh_epbuf .ctrl ;
15641562 const uint8_t new_addr = enum_get_new_address (desc_device -> bDeviceClass == TUSB_CLASS_HUB );
15651563 TU_ASSERT (new_addr != 0 ,);
@@ -1582,7 +1580,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
15821580 new_dev -> addressed = 1 ;
15831581 _usbh_data .enumerating_daddr = new_addr ;
15841582
1585- hcd_device_close (dev0_bus -> rhport , 0 ); // close dev0
1583+ usbh_device_close (dev0_bus -> rhport , 0 ); // close dev0
15861584
15871585 TU_ASSERT (usbh_edpt_control_open (new_addr , new_dev -> ep0_size ),); // open new control endpoint
15881586
0 commit comments