34
34
#include "hub.h"
35
35
36
36
//--------------------------------------------------------------------+
37
- // USBH Configuration
37
+ // Configuration
38
38
//--------------------------------------------------------------------+
39
39
#ifndef CFG_TUH_TASK_QUEUE_SZ
40
40
#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
89
89
}
90
90
91
91
//--------------------------------------------------------------------+
92
- // USBH-HCD common data structure
92
+ // Data Structure
93
93
//--------------------------------------------------------------------+
94
94
typedef struct {
95
95
tuh_bus_info_t bus_info ;
@@ -131,8 +131,60 @@ typedef struct {
131
131
132
132
} usbh_device_t ;
133
133
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
+
134
186
//--------------------------------------------------------------------+
135
- // MACRO CONSTANT TYPEDEF
187
+ // Class Driver
136
188
//--------------------------------------------------------------------+
137
189
#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
138
190
#define DRIVER_NAME (_name ) _name
@@ -235,82 +287,62 @@ static inline usbh_class_driver_t const *get_driver(uint8_t drv_id) {
235
287
}
236
288
237
289
//--------------------------------------------------------------------+
238
- // INTERNAL OBJECT & FUNCTION DECLARATION
290
+ // Function Inline and Prototypes
239
291
//--------------------------------------------------------------------+
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 );
240
296
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 -------------//
294
297
TU_ATTR_ALWAYS_INLINE static inline usbh_device_t * get_device (uint8_t dev_addr ) {
295
298
TU_VERIFY (dev_addr > 0 && dev_addr <= TOTAL_DEVICES , NULL );
296
299
return & _usbh_devices [dev_addr - 1 ];
297
300
}
298
301
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
+ }
303
305
304
306
TU_ATTR_ALWAYS_INLINE static inline bool queue_event (hcd_event_t const * event , bool in_isr ) {
305
307
TU_ASSERT (osal_queue_send (_usbh_q , event , in_isr ));
306
308
tuh_event_hook_cb (event -> rhport , event -> event_id , in_isr );
307
309
return true;
308
310
}
309
311
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
+
310
343
//--------------------------------------------------------------------+
311
344
// Device API
312
345
//--------------------------------------------------------------------+
313
-
314
346
bool tuh_mounted (uint8_t dev_addr ) {
315
347
usbh_device_t * dev = get_device (dev_addr );
316
348
TU_VERIFY (dev );
@@ -530,16 +562,16 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
530
562
break ;
531
563
532
564
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 );
541
574
}
542
- #endif
543
575
break ;
544
576
545
577
case HCD_EVENT_XFER_COMPLETE : {
@@ -623,23 +655,6 @@ static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
623
655
* ((xfer_result_t * ) xfer -> user_data ) = xfer -> result ;
624
656
}
625
657
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
-
643
658
// TODO timeout_ms is not supported yet
644
659
bool tuh_control_xfer (tuh_xfer_t * xfer ) {
645
660
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
1263
1278
//--------------------------------------------------------------------+
1264
1279
// Detaching
1265
1280
//--------------------------------------------------------------------+
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
-
1271
1281
// a device unplugged from rhport:hub_addr:hub_port
1272
1282
static 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
1285
1287
1286
- //------------- find the all devices (star-network) under port that is unplugged -------------//
1287
- uint32_t removing_hubs = 0 ;
1288
1288
do {
1289
1289
for (uint8_t dev_id = 0 ; dev_id < TOTAL_DEVICES ; dev_id ++ ) {
1290
1290
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
1296
1296
(hub_port == 0 || dev -> bus_info .hub_port == hub_port )) {
1297
1297
TU_LOG_USBH ("[%u:%u:%u] unplugged address = %u\r\n" , rhport , hub_addr , hub_port , daddr );
1298
1298
1299
+ #if CFG_TUH_HUB
1299
1300
if (is_hub_addr (daddr )) {
1300
1301
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
+ {
1303
1306
// Invoke callback before closing driver (maybe call it later ?)
1304
1307
if (tuh_umount_cb ) {
1305
1308
tuh_umount_cb (daddr );
@@ -1314,41 +1317,32 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub
1314
1317
}
1315
1318
}
1316
1319
1317
- hcd_device_close (rhport , daddr );
1320
+ usbh_device_close (rhport , daddr );
1318
1321
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
- }
1328
1322
}
1329
1323
}
1330
1324
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 ) ) {
1334
1328
break ;
1335
1329
}
1336
1330
1337
1331
// find a marked hub to process
1338
1332
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 ;
1341
1335
1342
1336
// update hub_addr and hub_port for next loop
1343
1337
hub_addr = h_id + 1 + CFG_TUH_DEVICE_MAX ;
1344
1338
hub_port = 0 ;
1345
1339
break ;
1346
1340
}
1347
1341
}
1348
- #else
1349
- (void ) removing_hubs ;
1342
+ #else
1350
1343
break ;
1351
- #endif
1344
+ #endif
1345
+
1352
1346
} while (1 );
1353
1347
}
1354
1348
@@ -1560,6 +1554,10 @@ static void process_enumeration(tuh_xfer_t* xfer) {
1560
1554
}
1561
1555
1562
1556
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
+
1563
1561
const tusb_desc_device_t * desc_device = (const tusb_desc_device_t * ) _usbh_epbuf .ctrl ;
1564
1562
const uint8_t new_addr = enum_get_new_address (desc_device -> bDeviceClass == TUSB_CLASS_HUB );
1565
1563
TU_ASSERT (new_addr != 0 ,);
@@ -1582,7 +1580,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
1582
1580
new_dev -> addressed = 1 ;
1583
1581
_usbh_data .enumerating_daddr = new_addr ;
1584
1582
1585
- hcd_device_close (dev0_bus -> rhport , 0 ); // close dev0
1583
+ usbh_device_close (dev0_bus -> rhport , 0 ); // close dev0
1586
1584
1587
1585
TU_ASSERT (usbh_edpt_control_open (new_addr , new_dev -> ep0_size ),); // open new control endpoint
1588
1586
0 commit comments