Skip to content

Commit 51cfae6

Browse files
authored
Merge pull request #3007 from hathach/fix-8bitdo-enum
Fix(host) enumeration with 8bitdo devices
2 parents a29e114 + 5f447b7 commit 51cfae6

File tree

7 files changed

+150
-95
lines changed

7 files changed

+150
-95
lines changed

examples/host/cdc_msc_hid/src/hid_app.c

+32-48
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,16 @@
3939
static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII };
4040

4141
// Each HID instance can has multiple reports
42-
static struct
43-
{
42+
static struct {
4443
uint8_t report_count;
4544
tuh_hid_report_info_t report_info[MAX_REPORT];
46-
}hid_info[CFG_TUH_HID];
45+
} hid_info[CFG_TUH_HID];
4746

4847
static void process_kbd_report(hid_keyboard_report_t const *report);
4948
static void process_mouse_report(hid_mouse_report_t const * report);
5049
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
5150

52-
void hid_app_task(void)
53-
{
51+
void hid_app_task(void) {
5452
// nothing to do
5553
}
5654

@@ -63,64 +61,57 @@ void hid_app_task(void)
6361
// can be used to parse common/simple enough descriptor.
6462
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
6563
// therefore report_desc = NULL, desc_len = 0
66-
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
67-
{
64+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
6865
printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
6966

7067
// Interface protocol (hid_interface_protocol_enum_t)
71-
const char* protocol_str[] = { "None", "Keyboard", "Mouse" };
68+
const char *protocol_str[] = {"None", "Keyboard", "Mouse"};
7269
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
7370

7471
printf("HID Interface Protocol = %s\r\n", protocol_str[itf_protocol]);
7572

7673
// By default host stack will use activate boot protocol on supported interface.
7774
// Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser)
78-
if ( itf_protocol == HID_ITF_PROTOCOL_NONE )
79-
{
75+
if (itf_protocol == HID_ITF_PROTOCOL_NONE) {
8076
hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len);
8177
printf("HID has %u reports \r\n", hid_info[instance].report_count);
8278
}
8379

8480
// request to receive report
8581
// tuh_hid_report_received_cb() will be invoked when report is available
86-
if ( !tuh_hid_receive_report(dev_addr, instance) )
87-
{
82+
if (!tuh_hid_receive_report(dev_addr, instance)) {
8883
printf("Error: cannot request to receive report\r\n");
8984
}
9085
}
9186

9287
// Invoked when device with hid interface is un-mounted
93-
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
94-
{
88+
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
9589
printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
9690
}
9791

9892
// Invoked when received report from device via interrupt endpoint
99-
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
100-
{
93+
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
10194
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
10295

103-
switch (itf_protocol)
104-
{
96+
switch (itf_protocol) {
10597
case HID_ITF_PROTOCOL_KEYBOARD:
10698
TU_LOG2("HID receive boot keyboard report\r\n");
107-
process_kbd_report( (hid_keyboard_report_t const*) report );
108-
break;
99+
process_kbd_report((hid_keyboard_report_t const *) report);
100+
break;
109101

110102
case HID_ITF_PROTOCOL_MOUSE:
111103
TU_LOG2("HID receive boot mouse report\r\n");
112-
process_mouse_report( (hid_mouse_report_t const*) report );
113-
break;
104+
process_mouse_report((hid_mouse_report_t const *) report);
105+
break;
114106

115107
default:
116108
// Generic report requires matching ReportID and contents with previous parsed report info
117109
process_generic_report(dev_addr, instance, report, len);
118-
break;
110+
break;
119111
}
120112

121113
// continue to request to receive report
122-
if ( !tuh_hid_receive_report(dev_addr, instance) )
123-
{
114+
if (!tuh_hid_receive_report(dev_addr, instance)) {
124115
printf("Error: cannot request to receive report\r\n");
125116
}
126117
}
@@ -231,29 +222,24 @@ static void process_mouse_report(hid_mouse_report_t const * report)
231222
//--------------------------------------------------------------------+
232223
// Generic Report
233224
//--------------------------------------------------------------------+
234-
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
235-
{
225+
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
236226
(void) dev_addr;
237227
(void) len;
238228

239229
uint8_t const rpt_count = hid_info[instance].report_count;
240-
tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info;
241-
tuh_hid_report_info_t* rpt_info = NULL;
230+
tuh_hid_report_info_t *rpt_info_arr = hid_info[instance].report_info;
231+
tuh_hid_report_info_t *rpt_info = NULL;
242232

243-
if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0)
244-
{
233+
if (rpt_count == 1 && rpt_info_arr[0].report_id == 0) {
245234
// Simple report without report ID as 1st byte
246235
rpt_info = &rpt_info_arr[0];
247-
}else
248-
{
236+
} else {
249237
// Composite report, 1st byte is report ID, data starts from 2nd byte
250238
uint8_t const rpt_id = report[0];
251239

252240
// Find report id in the array
253-
for(uint8_t i=0; i<rpt_count; i++)
254-
{
255-
if (rpt_id == rpt_info_arr[i].report_id )
256-
{
241+
for (uint8_t i = 0; i < rpt_count; i++) {
242+
if (rpt_id == rpt_info_arr[i].report_id) {
257243
rpt_info = &rpt_info_arr[i];
258244
break;
259245
}
@@ -263,8 +249,7 @@ static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t c
263249
len--;
264250
}
265251

266-
if (!rpt_info)
267-
{
252+
if (!rpt_info) {
268253
printf("Couldn't find report info !\r\n");
269254
return;
270255
}
@@ -276,23 +261,22 @@ static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t c
276261
// - Consumer Control (Media Key) : Consumer, Consumer Control
277262
// - System Control (Power key) : Desktop, System Control
278263
// - Generic (vendor) : 0xFFxx, xx
279-
if ( rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP )
280-
{
281-
switch (rpt_info->usage)
282-
{
264+
if (rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP) {
265+
switch (rpt_info->usage) {
283266
case HID_USAGE_DESKTOP_KEYBOARD:
284267
TU_LOG1("HID receive keyboard report\r\n");
285268
// Assume keyboard follow boot report layout
286-
process_kbd_report( (hid_keyboard_report_t const*) report );
287-
break;
269+
process_kbd_report((hid_keyboard_report_t const *) report);
270+
break;
288271

289272
case HID_USAGE_DESKTOP_MOUSE:
290273
TU_LOG1("HID receive mouse report\r\n");
291274
// Assume mouse follow boot report layout
292-
process_mouse_report( (hid_mouse_report_t const*) report );
293-
break;
275+
process_mouse_report((hid_mouse_report_t const *) report);
276+
break;
294277

295-
default: break;
278+
default:
279+
break;
296280
}
297281
}
298282
}

src/common/tusb_types.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8, "size is not correct
462462
typedef struct TU_ATTR_PACKED {
463463
uint8_t bLength ; ///< Size of this descriptor in bytes
464464
uint8_t bDescriptorType ; ///< Descriptor Type
465-
uint16_t unicode_string[];
465+
uint16_t utf16le[];
466466
} tusb_desc_string_t;
467467

468468
// USB Binary Device Object Store (BOS)

src/host/hub.c

+2-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ typedef struct {
4646

4747
// from hub descriptor
4848
uint8_t bNbrPorts;
49-
uint8_t bPwrOn2PwrGood; // port power on to good, in 2ms unit
49+
uint8_t bPwrOn2PwrGood_2ms; // port power on to good, in 2ms unit
5050
// uint16_t wHubCharacteristics;
5151

5252
hub_port_status_response_t port_status;
@@ -279,7 +279,7 @@ static void config_set_port_power (tuh_xfer_t* xfer) {
279279
// only use number of ports in hub descriptor
280280
hub_desc_cs_t const* desc_hub = (hub_desc_cs_t const*) p_epbuf->ctrl_buf;
281281
p_hub->bNbrPorts = desc_hub->bNbrPorts;
282-
p_hub->bPwrOn2PwrGood = desc_hub->bPwrOn2PwrGood;
282+
p_hub->bPwrOn2PwrGood_2ms = desc_hub->bPwrOn2PwrGood;
283283

284284
// May need to GET_STATUS
285285

@@ -301,7 +301,6 @@ static void config_port_power_complete (tuh_xfer_t* xfer) {
301301
TU_MESS_FAILED();
302302
TU_BREAKPOINT();
303303
}
304-
// delay bPwrOn2PwrGood * 2 ms before set configuration complete
305304
usbh_driver_set_config_complete(daddr, p_hub->itf_num);
306305
} else {
307306
// power next port

src/host/usbh.c

+75-20
Original file line numberDiff line numberDiff line change
@@ -1282,13 +1282,17 @@ static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hu
12821282
removing_hubs |= TU_BIT(dev_id - CFG_TUH_DEVICE_MAX);
12831283
} else {
12841284
// Invoke callback before closing driver (maybe call it later ?)
1285-
if (tuh_umount_cb) tuh_umount_cb(daddr);
1285+
if (tuh_umount_cb) {
1286+
tuh_umount_cb(daddr);
1287+
}
12861288
}
12871289

12881290
// Close class driver
12891291
for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
12901292
usbh_class_driver_t const* driver = get_driver(drv_id);
1291-
if (driver) driver->close(daddr);
1293+
if (driver) {
1294+
driver->close(daddr);
1295+
}
12921296
}
12931297

12941298
hcd_device_close(rhport, daddr);
@@ -1345,8 +1349,11 @@ enum {
13451349
ENUM_HUB_GET_STATUS_2,
13461350
ENUM_HUB_CLEAR_RESET_2,
13471351
ENUM_SET_ADDR,
1348-
13491352
ENUM_GET_DEVICE_DESC,
1353+
ENUM_GET_STRING_LANGUAGE_ID,
1354+
ENUM_GET_STRING_MANUFACTURER,
1355+
ENUM_GET_STRING_PRODUCT,
1356+
ENUM_GET_STRING_SERIAL,
13501357
ENUM_GET_9BYTE_CONFIG_DESC,
13511358
ENUM_GET_FULL_CONFIG_DESC,
13521359
ENUM_SET_CONFIG,
@@ -1359,25 +1366,25 @@ static void enum_full_complete(void);
13591366

13601367
// process device enumeration
13611368
static void process_enumeration(tuh_xfer_t* xfer) {
1362-
// Retry a few times with transfers in enumeration since device can be unstable when starting up
1363-
enum {
1364-
ATTEMPT_COUNT_MAX = 3,
1365-
ATTEMPT_DELAY_MS = 100
1366-
};
1369+
// Retry a few times while enumerating since device can be unstable when starting up
13671370
static uint8_t failed_count = 0;
1371+
if (XFER_RESULT_FAILED == xfer->result) {
1372+
enum {
1373+
ATTEMPT_COUNT_MAX = 3,
1374+
ATTEMPT_DELAY_MS = 100
1375+
};
13681376

1369-
if (XFER_RESULT_SUCCESS != xfer->result) {
13701377
// retry if not reaching max attempt
1378+
failed_count++;
13711379
bool retry = _dev0.enumerating && (failed_count < ATTEMPT_COUNT_MAX);
1372-
if ( retry ) {
1373-
failed_count++;
1380+
if (retry) {
13741381
tusb_time_delay_ms_api(ATTEMPT_DELAY_MS); // delay a bit
1375-
TU_LOG1("Enumeration attempt %u\r\n", failed_count);
1382+
TU_LOG1("Enumeration attempt %u/%u\r\n", failed_count+1, ATTEMPT_COUNT_MAX);
13761383
retry = tuh_control_xfer(xfer);
13771384
}
13781385

13791386
if (!retry) {
1380-
enum_full_complete();
1387+
enum_full_complete(); // complete as failed
13811388
}
13821389

13831390
return;
@@ -1386,6 +1393,8 @@ static void process_enumeration(tuh_xfer_t* xfer) {
13861393

13871394
uint8_t const daddr = xfer->daddr;
13881395
uintptr_t const state = xfer->user_data;
1396+
usbh_device_t* dev = get_device(daddr);
1397+
uint16_t langid = 0x0409; // default is English
13891398

13901399
switch (state) {
13911400
#if CFG_TUH_HUB
@@ -1476,7 +1485,6 @@ static void process_enumeration(tuh_xfer_t* xfer) {
14761485
tusb_time_delay_ms_api(2);
14771486

14781487
const uint8_t new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
1479-
14801488
usbh_device_t* new_dev = get_device(new_addr);
14811489
TU_ASSERT(new_dev,);
14821490
new_dev->addressed = 1;
@@ -1490,21 +1498,69 @@ static void process_enumeration(tuh_xfer_t* xfer) {
14901498
// Get full device descriptor
14911499
TU_LOG_USBH("Get Device Descriptor\r\n");
14921500
TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_epbuf.ctrl, sizeof(tusb_desc_device_t),
1493-
process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC),);
1501+
process_enumeration, ENUM_GET_STRING_LANGUAGE_ID),);
14941502
break;
14951503
}
14961504

1497-
case ENUM_GET_9BYTE_CONFIG_DESC: {
1498-
tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_epbuf.ctrl;
1499-
usbh_device_t* dev = get_device(daddr);
1505+
case ENUM_GET_STRING_LANGUAGE_ID: {
1506+
// save the received device descriptor
15001507
TU_ASSERT(dev,);
1501-
1508+
tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_epbuf.ctrl;
15021509
dev->vid = desc_device->idVendor;
15031510
dev->pid = desc_device->idProduct;
15041511
dev->i_manufacturer = desc_device->iManufacturer;
15051512
dev->i_product = desc_device->iProduct;
15061513
dev->i_serial = desc_device->iSerialNumber;
15071514

1515+
tuh_descriptor_get_string_langid(daddr, _usbh_epbuf.ctrl, CFG_TUH_ENUMERATION_BUFSIZE,
1516+
process_enumeration, ENUM_GET_STRING_MANUFACTURER);
1517+
break;
1518+
}
1519+
1520+
case ENUM_GET_STRING_MANUFACTURER: {
1521+
TU_ASSERT(dev,);
1522+
const tusb_desc_string_t* desc_langid = (tusb_desc_string_t const*) _usbh_epbuf.ctrl;
1523+
if (desc_langid->bLength >= 4) {
1524+
langid = tu_le16toh(desc_langid->utf16le[0]);
1525+
}
1526+
if (dev->i_manufacturer != 0) {
1527+
tuh_descriptor_get_string(daddr, dev->i_manufacturer, langid, _usbh_epbuf.ctrl, CFG_TUH_ENUMERATION_BUFSIZE,
1528+
process_enumeration, ENUM_GET_STRING_PRODUCT);
1529+
break;
1530+
} else {
1531+
TU_ATTR_FALLTHROUGH;
1532+
}
1533+
}
1534+
1535+
case ENUM_GET_STRING_PRODUCT: {
1536+
TU_ASSERT(dev,);
1537+
if (state == ENUM_GET_STRING_PRODUCT) {
1538+
langid = tu_le16toh(xfer->setup->wIndex); // if not fall through, get langid from previous setup packet
1539+
}
1540+
if (dev->i_product != 0) {
1541+
tuh_descriptor_get_string(daddr, dev->i_product, 0x0409, _usbh_epbuf.ctrl, CFG_TUH_ENUMERATION_BUFSIZE,
1542+
process_enumeration, ENUM_GET_STRING_SERIAL);
1543+
break;
1544+
} else {
1545+
TU_ATTR_FALLTHROUGH;
1546+
}
1547+
}
1548+
1549+
case ENUM_GET_STRING_SERIAL: {
1550+
TU_ASSERT(dev,);
1551+
if (state == ENUM_GET_STRING_SERIAL) {
1552+
langid = tu_le16toh(xfer->setup->wIndex); // if not fall through, get langid from previous setup packet
1553+
}
1554+
if (dev->i_serial != 0) {
1555+
tuh_descriptor_get_string(daddr, dev->i_serial, langid, _usbh_epbuf.ctrl, CFG_TUH_ENUMERATION_BUFSIZE,
1556+
process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC);
1557+
break;
1558+
} else {
1559+
TU_ATTR_FALLTHROUGH;
1560+
}
1561+
}
1562+
1563+
case ENUM_GET_9BYTE_CONFIG_DESC: {
15081564
// Get 9-byte for total length
15091565
uint8_t const config_idx = CONFIG_NUM - 1;
15101566
TU_LOG_USBH("Get Configuration[0] Descriptor (9 bytes)\r\n");
@@ -1537,7 +1593,6 @@ static void process_enumeration(tuh_xfer_t* xfer) {
15371593

15381594
case ENUM_CONFIG_DRIVER: {
15391595
TU_LOG_USBH("Device configured\r\n");
1540-
usbh_device_t* dev = get_device(daddr);
15411596
TU_ASSERT(dev,);
15421597

15431598
dev->configured = 1;

0 commit comments

Comments
 (0)