Skip to content

Commit 5618d9c

Browse files
authored
Merge pull request #525 from adafruit/fix-sigma-mouse
fix sigma mouse in adafruit store cause multiple attach connection
2 parents 72cd810 + 46d2c6c commit 5618d9c

File tree

2 files changed

+128
-129
lines changed

2 files changed

+128
-129
lines changed

src/host/hub.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,8 @@ static void process_new_status(tuh_xfer_t* xfer) {
459459
}
460460
};
461461
hcd_event_handler(&event, false);
462-
processed = true; // usbh queue status after handled this in (de)enumeration
462+
// skip status for attach event, usbh will do it after handled this enumeration
463+
processed = (event.event_id == HCD_EVENT_DEVICE_ATTACH);
463464
break;
464465
}
465466

src/host/usbh.c

Lines changed: 126 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
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
//--------------------------------------------------------------------+
9494
typedef 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 -------------//
294297
TU_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

304306
TU_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-
314346
bool 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
644659
bool 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
12721282
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
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

Comments
 (0)