Skip to content

Commit f709e0f

Browse files
refactor(host/enum): Serialize device enumeration
1 parent a4d2095 commit f709e0f

File tree

4 files changed

+108
-13
lines changed

4 files changed

+108
-13
lines changed

host/usb/private_include/enum.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -109,6 +109,7 @@ esp_err_t enum_uninstall(void);
109109
* @return
110110
* - ESP_OK: Enumeration process started
111111
* - ESP_ERR_NOT_FOUND: No device at address 0
112+
* - ESP_ERR_NO_MEM: Pending enumeration queue is full
112113
*/
113114
esp_err_t enum_start(unsigned int uid);
114115

host/usb/private_include/usbh.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,23 @@ void usbh_devs_set_pm_actions_all(usbh_dev_ctrl_t device_ctrl);
336336
*/
337337
esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl);
338338

339+
/**
340+
* @brief Open a device by UID
341+
*
342+
* A device must be opened before it can be used
343+
*
344+
* @param[in] uid Unique ID assigned to the device
345+
* @param[out] dev_hdl Device handle
346+
*
347+
* @return
348+
* - ESP_OK: Device opened successfully
349+
* - ESP_ERR_INVALID_ARG: Invalid argument
350+
* - ESP_ERR_INVALID_STATE: Device is in invalid state, either already gone (disconnected), or waiting to be freed
351+
* - ESP_ERR_NOT_ALLOWED: It is not allowed to open the device, it is locked for the enumeration
352+
* - ESP_ERR_NOT_FOUND: Device with provided UID not found
353+
*/
354+
esp_err_t usbh_devs_open_uid(unsigned int uid, usb_device_handle_t *dev_hdl);
355+
339356
/**
340357
* @brief Trigger a USBH_EVENT_NEW_DEV event for the device
341358
*

host/usb/src/enum.c

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -26,6 +26,7 @@
2626
#define ENUM_WORST_CASE_MPS_FS_HS 64 // The worst case MPS of EP0 for a FS/HS device
2727
#define ENUM_LANGID 0x409 // Current enumeration only supports English (United States) string descriptors
2828
#define ENUM_MAX_ADDRESS (127) // Maximal device address value
29+
#define ENUM_PENDING_QUEUE_LEN (5)
2930

3031
/**
3132
* @brief Stages of device enumeration listed in their order of execution
@@ -139,6 +140,11 @@ typedef struct {
139140
enum_device_params_t dev_params; /**< Parameters of device under enumeration */
140141
int expect_num_bytes; /**< Expected number of bytes for IN transfers stages. Set to 0 for OUT transfer */
141142
uint8_t next_dev_addr; /**< Device address for device under enumeration */
143+
// Pending enumeration queue
144+
unsigned int pending_uids[ENUM_PENDING_QUEUE_LEN];
145+
uint16_t pending_head;
146+
uint16_t pending_tail;
147+
uint16_t pending_count;
142148
} single_thread; /**< Single thread members don't require a critical section so long as they are never accessed from multiple threads */
143149

144150
struct {
@@ -203,6 +209,30 @@ static uint8_t get_next_free_dev_addr(void)
203209
return new_dev_addr;
204210
}
205211

212+
static bool pending_uid_enqueue(unsigned int uid)
213+
{
214+
if (p_enum_driver->single_thread.pending_count >= ENUM_PENDING_QUEUE_LEN) {
215+
return false;
216+
}
217+
p_enum_driver->single_thread.pending_uids[p_enum_driver->single_thread.pending_tail] = uid;
218+
p_enum_driver->single_thread.pending_tail =
219+
(p_enum_driver->single_thread.pending_tail + 1) % ENUM_PENDING_QUEUE_LEN;
220+
p_enum_driver->single_thread.pending_count++;
221+
return true;
222+
}
223+
224+
static bool pending_uid_dequeue(unsigned int *uid)
225+
{
226+
if (p_enum_driver->single_thread.pending_count == 0) {
227+
return false;
228+
}
229+
*uid = p_enum_driver->single_thread.pending_uids[p_enum_driver->single_thread.pending_head];
230+
p_enum_driver->single_thread.pending_head =
231+
(p_enum_driver->single_thread.pending_head + 1) % ENUM_PENDING_QUEUE_LEN;
232+
p_enum_driver->single_thread.pending_count--;
233+
return true;
234+
}
235+
206236
/**
207237
* @brief Get Configuration descriptor index
208238
*
@@ -1161,11 +1191,19 @@ esp_err_t enum_start(unsigned int uid)
11611191
{
11621192
ENUM_CHECK(p_enum_driver != NULL, ESP_ERR_INVALID_STATE);
11631193

1194+
if (p_enum_driver->single_thread.stage != ENUM_STAGE_IDLE) {
1195+
1196+
// We are already enumerating a device, add the new device to the pending queue
1197+
if (!pending_uid_enqueue(uid)) {
1198+
return ESP_ERR_NO_MEM;
1199+
}
1200+
return ESP_OK;
1201+
}
11641202
esp_err_t ret = ESP_FAIL;
11651203

11661204
// Open device and lock it for enumeration process
11671205
usb_device_handle_t dev_hdl;
1168-
ret = usbh_devs_open(0, &dev_hdl);
1206+
ret = usbh_devs_open_uid(uid, &dev_hdl);
11691207
if (ret != ESP_OK) {
11701208
return ret;
11711209
}
@@ -1176,7 +1214,7 @@ esp_err_t enum_start(unsigned int uid)
11761214
ESP_ERROR_CHECK(usbh_dev_get_info(dev_hdl, &dev_info));
11771215

11781216
// Stage ENUM_STAGE_GET_SHORT_DEV_DESC
1179-
ESP_LOGD(ENUM_TAG, "Start processing device with address %d", 0);
1217+
ESP_LOGD(ENUM_TAG, "Start processing device with uid %d", uid);
11801218

11811219
p_enum_driver->single_thread.stage = ENUM_STAGE_GET_SHORT_DEV_DESC;
11821220
p_enum_driver->single_thread.node_uid = uid;
@@ -1322,5 +1360,17 @@ esp_err_t enum_process(void)
13221360
p_enum_driver->constant.proc_req_cb(USB_PROC_REQ_SOURCE_ENUM, false, p_enum_driver->constant.proc_req_cb_arg);
13231361
}
13241362

1363+
// If we are idle and there are pending devices, start the next one
1364+
while (p_enum_driver->single_thread.pending_count > 0 &&
1365+
p_enum_driver->single_thread.stage == ENUM_STAGE_IDLE) {
1366+
unsigned int next_uid = 0;
1367+
if (!pending_uid_dequeue(&next_uid)) {
1368+
break;
1369+
}
1370+
if (enum_start(next_uid) == ESP_OK) {
1371+
break;
1372+
}
1373+
}
1374+
13251375
return ESP_OK;
13261376
}

host/usb/src/usbh.c

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -940,8 +940,9 @@ esp_err_t usbh_devs_add(usbh_dev_params_t *params)
940940
ret = ESP_ERR_INVALID_ARG;
941941
goto exit;
942942
}
943-
// Check that there is not already a device currently with address 0
944-
if (_find_dev_from_addr(0) != NULL) {
943+
// Check that there is not already a device currently with address 0 (not enumerated) on the same port
944+
device_t *dev_with_addr_0 = _find_dev_from_addr(0);
945+
if (dev_with_addr_0 != NULL && dev_with_addr_0->constant.port_hdl == params->root_port_hdl) {
945946
ret = ESP_ERR_NOT_FINISHED;
946947
goto exit;
947948
}
@@ -1131,14 +1132,9 @@ void usbh_devs_set_pm_actions_all(usbh_dev_ctrl_t device_ctrl)
11311132
}
11321133
}
11331134

1134-
esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl)
1135+
esp_err_t _dev_open(device_t *dev_obj)
11351136
{
1136-
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
11371137
esp_err_t ret;
1138-
1139-
USBH_ENTER_CRITICAL();
1140-
// Go through the device lists to find the device with the specified address
1141-
device_t *dev_obj = _find_dev_from_addr(dev_addr);
11421138
if (dev_obj != NULL) {
11431139
// Check if the device is in a state to be opened
11441140
if (dev_obj->dynamic.flags.is_gone || // Device is already gone (disconnected)
@@ -1148,14 +1144,45 @@ esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl)
11481144
ret = ESP_ERR_NOT_ALLOWED;
11491145
} else {
11501146
dev_obj->dynamic.open_count++;
1151-
*dev_hdl = (usb_device_handle_t)dev_obj;
11521147
ret = ESP_OK;
11531148
}
11541149
} else {
11551150
ret = ESP_ERR_NOT_FOUND;
11561151
}
1152+
return ret;
1153+
}
1154+
1155+
esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl)
1156+
{
1157+
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
1158+
esp_err_t ret;
1159+
1160+
USBH_ENTER_CRITICAL();
1161+
// Go through the device lists to find the device with the specified address
1162+
device_t *dev_obj = _find_dev_from_addr(dev_addr);
1163+
ret = _dev_open(dev_obj);
1164+
USBH_EXIT_CRITICAL();
1165+
1166+
if (ret == ESP_OK) {
1167+
*dev_hdl = (usb_device_handle_t)dev_obj;
1168+
}
1169+
return ret;
1170+
}
1171+
1172+
esp_err_t usbh_devs_open_uid(unsigned int uid, usb_device_handle_t *dev_hdl)
1173+
{
1174+
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
1175+
esp_err_t ret;
1176+
1177+
USBH_ENTER_CRITICAL();
1178+
// Go through the device lists to find the device with the specified address
1179+
device_t *dev_obj = _find_dev_from_uid(uid);
1180+
ret = _dev_open(dev_obj);
11571181
USBH_EXIT_CRITICAL();
11581182

1183+
if (ret == ESP_OK) {
1184+
*dev_hdl = (usb_device_handle_t)dev_obj;
1185+
}
11591186
return ret;
11601187
}
11611188

0 commit comments

Comments
 (0)