1 /*
2 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "sdkconfig.h"
8 #include <stdlib.h>
9 #include <stdbool.h>
10 #include <string.h>
11 #include "freertos/FreeRTOS.h"
12 #include "freertos/portmacro.h"
13 #include "esp_err.h"
14 #include "esp_heap_caps.h"
15 #include "esp_log.h"
16 #include "usb_private.h"
17 #include "hcd.h"
18 #include "hub.h"
19 #include "usb/usb_helpers.h"
20
21 /*
22 Implementation of the HUB driver that only supports the Root Hub with a single port. Therefore, we currently don't
23 implement the bare minimum to control the root HCD port.
24 */
25
26 #define HUB_ROOT_PORT_NUM 1 //HCD only supports one port
27 #ifdef CONFIG_USB_HOST_HW_BUFFER_BIAS_IN
28 #define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_RX
29 #elif CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT
30 #define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_PTX
31 #else //CONFIG_USB_HOST_HW_BUFFER_BIAS_BALANCED
32 #define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_BALANCED
33 #endif
34
35 #define ENUM_CTRL_TRANSFER_MAX_DATA_LEN CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE
36 #define ENUM_DEV_ADDR 1 //Device address used in enumeration
37 #define ENUM_CONFIG_INDEX 0 //Index of the first configuration of the device
38 #define ENUM_SHORT_DESC_REQ_LEN 8 //Number of bytes to request when getting a short descriptor (just enough to get bMaxPacketSize0 or wTotalLength)
39 #define ENUM_WORST_CASE_MPS_LS 8 //The worst case MPS of EP0 for a LS device
40 #define ENUM_WORST_CASE_MPS_FS 64 //The worst case MPS of EP0 for a FS device
41 #define ENUM_LOW_SPEED_MPS 8 //Worst case MPS for the default endpoint of a low-speed device
42 #define ENUM_FULL_SPEED_MPS 64 //Worst case MPS for the default endpoint of a full-speed device
43 #define ENUM_LANGID 0x409 //Current enumeration only supports English (United States) string descriptors
44
45 //Hub driver action flags. LISTED IN THE ORDER THEY SHOULD BE HANDLED IN within hub_process(). Some actions are mutually exclusive
46 #define HUB_DRIVER_FLAG_ACTION_ROOT_EVENT 0x01
47 #define HUB_DRIVER_FLAG_ACTION_PORT_DISABLE 0x02
48 #define HUB_DRIVER_FLAG_ACTION_PORT_RECOVER 0x04
49 #define HUB_DRIVER_FLAG_ACTION_ENUM_EVENT 0x08
50
51 /**
52 * @brief Hub driver states
53 *
54 * These states represent a Hub driver that only has a single port (the root port)
55 */
56 typedef enum {
57 HUB_DRIVER_STATE_INSTALLED, /**< Hub driver is installed. Root port is not powered */
58 HUB_DRIVER_STATE_ROOT_POWERED, /**< Root port is powered, is not connected */
59 HUB_DRIVER_STATE_ROOT_ENUM, /**< A device has connected to the root port and is undergoing enumeration */
60 HUB_DRIVER_STATE_ROOT_ENUM_FAILED, /**< Enumeration of a connect device has failed. Waiting for that device to disconnect */
61 HUB_DRIVER_STATE_ROOT_ACTIVE, /**< The connected device is enumerated */
62 HUB_DRIVER_STATE_ROOT_RECOVERY, /**< Root port encountered an error and needs to be recovered */
63 } hub_driver_state_t;
64
65 /**
66 * @brief Stages of device enumeration listed in their order of execution
67 *
68 * - These stages MUST BE LISTED IN THE ORDER OF THEIR EXECUTION as the enumeration will simply increment the current stage
69 * - If an error occurs at any stage, ENUM_STAGE_CLEANUP_FAILED acts as a common exit stage on failure
70 * - Must start with 0 as enum is also used as an index
71 * - The short descriptor stages are used to fetch the start particular descriptors that don't have a fixed length in order to determine the full descriptors length
72 */
73 typedef enum {
74 ENUM_STAGE_NONE = 0, /**< There is no device awaiting enumeration. Start requires device connection and first reset. */
75 ENUM_STAGE_START, /**< A device has connected and has already been reset once. Allocate a device object in USBH */
76 //Basic device enumeration
77 ENUM_STAGE_GET_SHORT_DEV_DESC, /**< Getting short dev desc (wLength is ENUM_SHORT_DESC_REQ_LEN) */
78 ENUM_STAGE_CHECK_SHORT_DEV_DESC, /**< Save bMaxPacketSize0 from the short dev desc. Update the MPS of the enum pipe */
79 ENUM_STAGE_SECOND_RESET, /**< Reset the device again (Workaround for old USB devices that get confused by the previous short dev desc request). */
80 ENUM_STAGE_SET_ADDR, /**< Send SET_ADDRESS request */
81 ENUM_STAGE_CHECK_ADDR, /**< Update the enum pipe's target address */
82 ENUM_STAGE_GET_FULL_DEV_DESC, /**< Get the full dev desc */
83 ENUM_STAGE_CHECK_FULL_DEV_DESC, /**< Check the full dev desc, fill it into the device object in USBH. Save the string descriptor indexes*/
84 ENUM_STAGE_GET_SHORT_CONFIG_DESC, /**< Getting a short config desc (wLength is ENUM_SHORT_DESC_REQ_LEN) */
85 ENUM_STAGE_CHECK_SHORT_CONFIG_DESC, /**< Save wTotalLength of the short config desc */
86 ENUM_STAGE_GET_FULL_CONFIG_DESC, /**< Get the full config desc (wLength is the saved wTotalLength) */
87 ENUM_STAGE_CHECK_FULL_CONFIG_DESC, /**< Check the full config desc, fill it into the device object in USBH */
88 ENUM_STAGE_SET_CONFIG, /**< Send SET_CONFIGURATION request */
89 ENUM_STAGE_CHECK_CONFIG, /**< Check that SET_CONFIGURATION request was successful */
90 //Get string descriptors
91 ENUM_STAGE_GET_SHORT_LANGID_TABLE, /**< Get the header of the LANGID table string descriptor */
92 ENUM_STAGE_CHECK_SHORT_LANGID_TABLE, /**< Save the bLength of the LANGID table string descriptor */
93 ENUM_STAGE_GET_FULL_LANGID_TABLE, /**< Get the full LANGID table string descriptor */
94 ENUM_STAGE_CHECK_FULL_LANGID_TABLE, /**< Check whether ENUM_LANGID is in the LANGID table */
95 ENUM_STAGE_GET_SHORT_MANU_STR_DESC, /**< Get the header of the iManufacturer string descriptor */
96 ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC, /**< Save the bLength of the iManufacturer string descriptor */
97 ENUM_STAGE_GET_FULL_MANU_STR_DESC, /**< Get the full iManufacturer string descriptor */
98 ENUM_STAGE_CHECK_FULL_MANU_STR_DESC, /**< Check and fill the full iManufacturer string descriptor */
99 ENUM_STAGE_GET_SHORT_PROD_STR_DESC, /**< Get the header of the string descriptor at index iProduct */
100 ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC, /**< Save the bLength of the iProduct string descriptor */
101 ENUM_STAGE_GET_FULL_PROD_STR_DESC, /**< Get the full iProduct string descriptor */
102 ENUM_STAGE_CHECK_FULL_PROD_STR_DESC, /**< Check and fill the full iProduct string descriptor */
103 ENUM_STAGE_GET_SHORT_SER_STR_DESC, /**< Get the header of the string descriptor at index iSerialNumber */
104 ENUM_STAGE_CHECK_SHORT_SER_STR_DESC, /**< Save the bLength of the iSerialNumber string descriptor */
105 ENUM_STAGE_GET_FULL_SER_STR_DESC, /**< Get the full iSerialNumber string descriptor */
106 ENUM_STAGE_CHECK_FULL_SER_STR_DESC, /**< Check and fill the full iSerialNumber string descriptor */
107 //Cleanup
108 ENUM_STAGE_CLEANUP, /**< Clean up after successful enumeration. Adds enumerated device to USBH */
109 ENUM_STAGE_CLEANUP_FAILED, /**< Cleanup failed enumeration. Free device resources */
110 } enum_stage_t;
111
112 const char *const enum_stage_strings[] = {
113 "NONE",
114 "START",
115 "GET_SHORT_DEV_DESC",
116 "CHECK_SHORT_DEV_DESC",
117 "SECOND_RESET",
118 "SET_ADDR",
119 "CHECK_ADDR",
120 "GET_FULL_DEV_DESC",
121 "CHECK_FULL_DEV_DESC",
122 "GET_SHORT_CONFIG_DESC",
123 "CHECK_SHORT_CONFIG_DESC",
124 "GET_FULL_CONFIG_DESC",
125 "CHECK_FULL_CONFIG_DESC",
126 "SET_CONFIG",
127 "CHECK_CONFIG",
128 "GET_SHORT_LANGID_TABLE",
129 "CHECK_SHORT_LANGID_TABLE",
130 "GET_FULL_LANGID_TABLE",
131 "CHECK_FULL_LANGID_TABLE",
132 "GET_SHORT_MANU_STR_DESC",
133 "CHECK_SHORT_MANU_STR_DESC",
134 "GET_FULL_MANU_STR_DESC",
135 "CHECK_FULL_MANU_STR_DESC",
136 "GET_SHORT_PROD_STR_DESC",
137 "CHECK_SHORT_PROD_STR_DESC",
138 "GET_FULL_PROD_STR_DESC",
139 "CHECK_FULL_PROD_STR_DESC",
140 "GET_SHORT_SER_STR_DESC",
141 "CHECK_SHORT_SER_STR_DESC",
142 "GET_FULL_SER_STR_DESC",
143 "CHECK_FULL_SER_STR_DESC",
144 "CLEANUP",
145 "CLEANUP_FAILED",
146 };
147
148 typedef struct {
149 //Constant
150 urb_t *urb; /**< URB used for enumeration control transfers. Max data length of ENUM_CTRL_TRANSFER_MAX_DATA_LEN */
151 //Initialized at start of a particular enumeration
152 usb_device_handle_t dev_hdl; /**< Handle of device being enumerated */
153 hcd_pipe_handle_t pipe; /**< Default pipe handle of the device being enumerated */
154 //Updated during enumeration
155 enum_stage_t stage; /**< Current enumeration stage */
156 int expect_num_bytes; /**< Expected number of bytes for IN transfers stages. Set to 0 for OUT transfer */
157 uint8_t bMaxPacketSize0; /**< Max packet size of the device's EP0. Read from bMaxPacketSize0 field of device descriptor */
158 uint16_t wTotalLength; /**< Total length of device's configuration descriptor. Read from wTotalLength field of config descriptor */
159 uint8_t iManufacturer; /**< Index of the Manufacturer string descriptor */
160 uint8_t iProduct; /**< Index of the Product string descriptor */
161 uint8_t iSerialNumber; /**< Index of the Serial Number string descriptor */
162 uint8_t str_desc_bLength; /**< Saved bLength from getting a short string descriptor */
163 } enum_ctrl_t;
164
165 typedef struct {
166 //Dynamic members require a critical section
167 struct {
168 union {
169 struct {
170 uint32_t actions: 8;
171 uint32_t reserved24: 24;
172 };
173 uint32_t val;
174 } flags;
175 hub_driver_state_t driver_state;
176 } dynamic;
177 //Single thread members don't require a critical section so long as they are never accessed from multiple threads
178 struct {
179 usb_device_handle_t root_dev_hdl; //Indicates if an enumerated device is connected to the root port
180 enum_ctrl_t enum_ctrl;
181 } single_thread;
182 //Constant members do no change after installation thus do not require a critical section
183 struct {
184 hcd_port_handle_t root_port_hdl;
185 usb_notif_cb_t notif_cb;
186 void *notif_cb_arg;
187 } constant;
188 } hub_driver_t;
189
190 static hub_driver_t *p_hub_driver_obj = NULL;
191 static portMUX_TYPE hub_driver_lock = portMUX_INITIALIZER_UNLOCKED;
192
193 const char *HUB_DRIVER_TAG = "HUB";
194
195 #define HUB_DRIVER_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&hub_driver_lock)
196 #define HUB_DRIVER_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&hub_driver_lock)
197 #define HUB_DRIVER_ENTER_CRITICAL() portENTER_CRITICAL(&hub_driver_lock)
198 #define HUB_DRIVER_EXIT_CRITICAL() portEXIT_CRITICAL(&hub_driver_lock)
199 #define HUB_DRIVER_ENTER_CRITICAL_SAFE() portENTER_CRITICAL_SAFE(&hub_driver_lock)
200 #define HUB_DRIVER_EXIT_CRITICAL_SAFE() portEXIT_CRITICAL_SAFE(&hub_driver_lock)
201
202 #define HUB_DRIVER_CHECK(cond, ret_val) ({ \
203 if (!(cond)) { \
204 return (ret_val); \
205 } \
206 })
207 #define HUB_DRIVER_CHECK_FROM_CRIT(cond, ret_val) ({ \
208 if (!(cond)) { \
209 HUB_DRIVER_EXIT_CRITICAL(); \
210 return ret_val; \
211 } \
212 })
213
214 // ------------------------------------------------- Forward Declare ---------------------------------------------------
215
216 /**
217 * @brief HCD port callback for the root port
218 *
219 * - This callback is called from the context of the HCD, so any event handling should be deferred to hub_process()
220 * - Under the current HCD implementation, this callback should only be ever be called in an ISR
221 * - This callback needs to call the notification to ensure hub_process() gets a chance to run
222 *
223 * @param port_hdl HCD port handle
224 * @param port_event HCD port event
225 * @param user_arg Callback argument
226 * @param in_isr Whether callback is in an ISR context
227 * @return Whether a yield is required
228 */
229 static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port_event, void *user_arg, bool in_isr);
230
231 /**
232 * @brief HCD pipe callback for the default pipe of the device under enumeration
233 *
234 * - This callback is called from the context of the HCD, so any event handling should be deferred to hub_process()
235 * - This callback needs to call the notification to ensure hub_process() gets a chance to run
236 *
237 * @param pipe_hdl HCD pipe handle
238 * @param pipe_event Pipe event
239 * @param user_arg Callback argument
240 * @param in_isr Whether callback is in an ISR context
241 * @return Whether a yield is required
242 */
243 static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr);
244
245 /**
246 * @brief USBH Hub driver request callback
247 *
248 * - This callback is called from the context of the USBH, so so any event handling should be deferred to hub_process()
249 * - This callback needs to call the notification to ensure hub_process() gets a chance to run
250 *
251 * @param port_hdl HCD port handle
252 * @param hub_req Hub driver request
253 * @param arg Callback argument
254 */
255 static void usbh_hub_req_callback(hcd_port_handle_t port_hdl, usbh_hub_req_t hub_req, void *arg);
256
257 // ------------------------------------------------- Enum Functions ----------------------------------------------------
258
enum_stage_start(enum_ctrl_t * enum_ctrl)259 static bool enum_stage_start(enum_ctrl_t *enum_ctrl)
260 {
261 //Get the speed of the device, and set the enum MPS to the worst case size for now
262 usb_speed_t speed;
263 if (hcd_port_get_speed(p_hub_driver_obj->constant.root_port_hdl, &speed) != ESP_OK) {
264 return false;
265 }
266 enum_ctrl->bMaxPacketSize0 = (speed == USB_SPEED_LOW) ? ENUM_WORST_CASE_MPS_LS : ENUM_WORST_CASE_MPS_FS;
267 //Try to add the device to USBH
268 usb_device_handle_t enum_dev_hdl;
269 hcd_pipe_handle_t enum_dflt_pipe_hdl;
270 //We use NULL as the parent device to indicate the Root Hub port 1. We currently only support a single device
271 if (usbh_hub_add_dev(p_hub_driver_obj->constant.root_port_hdl, speed, &enum_dev_hdl, &enum_dflt_pipe_hdl) != ESP_OK) {
272 return false;
273 }
274 //Set our own default pipe callback
275 ESP_ERROR_CHECK(hcd_pipe_update_callback(enum_dflt_pipe_hdl, enum_dflt_pipe_callback, NULL));
276 enum_ctrl->dev_hdl = enum_dev_hdl;
277 enum_ctrl->pipe = enum_dflt_pipe_hdl;
278 return true;
279 }
280
enum_stage_second_reset(enum_ctrl_t * enum_ctrl)281 static bool enum_stage_second_reset(enum_ctrl_t *enum_ctrl)
282 {
283 ESP_ERROR_CHECK(hcd_pipe_set_persist_reset(enum_ctrl->pipe)); //Persist the default pipe through the reset
284 if (hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_RESET) != ESP_OK) {
285 ESP_LOGE(HUB_DRIVER_TAG, "Failed to issue second reset");
286 return false;
287 }
288 return true;
289 }
290
get_string_desc_index(enum_ctrl_t * enum_ctrl)291 static uint8_t get_string_desc_index(enum_ctrl_t *enum_ctrl)
292 {
293 uint8_t index;
294 switch (enum_ctrl->stage) {
295 case ENUM_STAGE_GET_SHORT_LANGID_TABLE:
296 case ENUM_STAGE_GET_FULL_LANGID_TABLE:
297 index = 0; //The LANGID table uses an index of 0
298 break;
299 case ENUM_STAGE_GET_SHORT_MANU_STR_DESC:
300 case ENUM_STAGE_GET_FULL_MANU_STR_DESC:
301 index = enum_ctrl->iManufacturer;
302 break;
303 case ENUM_STAGE_GET_SHORT_PROD_STR_DESC:
304 case ENUM_STAGE_GET_FULL_PROD_STR_DESC:
305 index = enum_ctrl->iProduct;
306 break;
307 case ENUM_STAGE_GET_SHORT_SER_STR_DESC:
308 case ENUM_STAGE_GET_FULL_SER_STR_DESC:
309 index = enum_ctrl->iSerialNumber;
310 break;
311 default:
312 //Should not occur
313 index = 0;
314 abort();
315 break;
316 }
317 return index;
318 }
319
enum_stage_transfer(enum_ctrl_t * enum_ctrl)320 static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl)
321 {
322 usb_transfer_t *transfer = &enum_ctrl->urb->transfer;
323 switch (enum_ctrl->stage) {
324 case ENUM_STAGE_GET_SHORT_DEV_DESC: {
325 //Initialize a short device descriptor request
326 USB_SETUP_PACKET_INIT_GET_DEVICE_DESC((usb_setup_packet_t *)transfer->data_buffer);
327 ((usb_setup_packet_t *)transfer->data_buffer)->wLength = ENUM_SHORT_DESC_REQ_LEN;
328 transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(ENUM_SHORT_DESC_REQ_LEN, enum_ctrl->bMaxPacketSize0);
329 //IN data stage should return exactly ENUM_SHORT_DESC_REQ_LEN bytes
330 enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + ENUM_SHORT_DESC_REQ_LEN;
331 break;
332 }
333 case ENUM_STAGE_SET_ADDR: {
334 USB_SETUP_PACKET_INIT_SET_ADDR((usb_setup_packet_t *)transfer->data_buffer, ENUM_DEV_ADDR);
335 transfer->num_bytes = sizeof(usb_setup_packet_t); //No data stage
336 enum_ctrl->expect_num_bytes = 0; //OUT transfer. No need to check number of bytes returned
337 break;
338 }
339 case ENUM_STAGE_GET_FULL_DEV_DESC: {
340 USB_SETUP_PACKET_INIT_GET_DEVICE_DESC((usb_setup_packet_t *)transfer->data_buffer);
341 transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(sizeof(usb_device_desc_t), enum_ctrl->bMaxPacketSize0);
342 //IN data stage should return exactly sizeof(usb_device_desc_t) bytes
343 enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_device_desc_t);
344 break;
345 }
346 case ENUM_STAGE_GET_SHORT_CONFIG_DESC: {
347 //Get a short config descriptor at index 0
348 USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, ENUM_CONFIG_INDEX, ENUM_SHORT_DESC_REQ_LEN);
349 transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(ENUM_SHORT_DESC_REQ_LEN, enum_ctrl->bMaxPacketSize0);
350 //IN data stage should return exactly ENUM_SHORT_DESC_REQ_LEN bytes
351 enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + ENUM_SHORT_DESC_REQ_LEN;
352 break;
353 }
354 case ENUM_STAGE_GET_FULL_CONFIG_DESC: {
355 //Get the full configuration descriptor at index 0, requesting its exact length.
356 USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, ENUM_CONFIG_INDEX, enum_ctrl->wTotalLength);
357 transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(enum_ctrl->wTotalLength, enum_ctrl->bMaxPacketSize0);
358 //IN data stage should return exactly wTotalLength bytes
359 enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + enum_ctrl->wTotalLength;
360 break;
361 }
362 case ENUM_STAGE_SET_CONFIG: {
363 USB_SETUP_PACKET_INIT_SET_CONFIG((usb_setup_packet_t *)transfer->data_buffer, ENUM_CONFIG_INDEX + 1);
364 transfer->num_bytes = sizeof(usb_setup_packet_t); //No data stage
365 enum_ctrl->expect_num_bytes = 0; //OUT transfer. No need to check number of bytes returned
366 break;
367 }
368 case ENUM_STAGE_GET_SHORT_LANGID_TABLE:
369 case ENUM_STAGE_GET_SHORT_MANU_STR_DESC:
370 case ENUM_STAGE_GET_SHORT_PROD_STR_DESC:
371 case ENUM_STAGE_GET_SHORT_SER_STR_DESC: {
372 uint8_t index = get_string_desc_index(enum_ctrl);
373 //Get only the header of the string descriptor
374 USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer,
375 index,
376 ENUM_LANGID,
377 sizeof(usb_str_desc_t));
378 transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(sizeof(usb_str_desc_t), enum_ctrl->bMaxPacketSize0);
379 //IN data stage should return exactly sizeof(usb_str_desc_t) bytes
380 enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_str_desc_t);
381 break;
382 }
383 case ENUM_STAGE_GET_FULL_LANGID_TABLE:
384 case ENUM_STAGE_GET_FULL_MANU_STR_DESC:
385 case ENUM_STAGE_GET_FULL_PROD_STR_DESC:
386 case ENUM_STAGE_GET_FULL_SER_STR_DESC: {
387 uint8_t index = get_string_desc_index(enum_ctrl);
388 //Get the full string descriptor at a particular index, requesting the descriptors exact length
389 USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer,
390 index,
391 ENUM_LANGID,
392 enum_ctrl->str_desc_bLength);
393 transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(enum_ctrl->str_desc_bLength, enum_ctrl->bMaxPacketSize0);
394 //IN data stage should return exactly str_desc_bLength bytes
395 enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + enum_ctrl->str_desc_bLength;
396 break;
397 }
398 default: //Should never occur
399 abort();
400 break;
401 }
402 if (hcd_urb_enqueue(enum_ctrl->pipe, enum_ctrl->urb) != ESP_OK) {
403 ESP_LOGE(HUB_DRIVER_TAG, "Failed to submit: %s", enum_stage_strings[enum_ctrl->stage]);
404 return false;
405 }
406 return true;
407 }
408
enum_stage_transfer_check(enum_ctrl_t * enum_ctrl)409 static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
410 {
411 //Dequeue the URB
412 urb_t *dequeued_enum_urb = hcd_urb_dequeue(enum_ctrl->pipe);
413 assert(dequeued_enum_urb == enum_ctrl->urb);
414
415 //Check transfer status
416 usb_transfer_t *transfer = &dequeued_enum_urb->transfer;
417 if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) {
418 ESP_LOGE(HUB_DRIVER_TAG, "Bad transfer status: %s", enum_stage_strings[enum_ctrl->stage]);
419 return false;
420 }
421 //Check IN transfer returned the expected correct number of bytes
422 if (enum_ctrl->expect_num_bytes != 0 && enum_ctrl->expect_num_bytes != transfer->actual_num_bytes) {
423 ESP_LOGE(HUB_DRIVER_TAG, "Incorrect number of bytes returned: %s", enum_stage_strings[enum_ctrl->stage]);
424 return false;
425 }
426
427 //Stage specific checks and updates
428 bool ret;
429 switch (enum_ctrl->stage) {
430 case ENUM_STAGE_CHECK_SHORT_DEV_DESC: {
431 const usb_device_desc_t *device_desc = (usb_device_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t));
432 //Check if the returned descriptor is corrupted
433 if (device_desc->bDescriptorType != USB_B_DESCRIPTOR_TYPE_DEVICE) {
434 ESP_LOGE(HUB_DRIVER_TAG, "Short dev desc corrupt");
435 ret = false;
436 break;
437 }
438 //Update and save the MPS of the default pipe
439 if (hcd_pipe_update_mps(enum_ctrl->pipe, device_desc->bMaxPacketSize0) != ESP_OK) {
440 ESP_LOGE(HUB_DRIVER_TAG, "Failed to update MPS");
441 ret = false;
442 break;
443 }
444 //Save the actual MPS of EP0
445 enum_ctrl->bMaxPacketSize0 = device_desc->bMaxPacketSize0;
446 ret = true;
447 break;
448 }
449 case ENUM_STAGE_CHECK_ADDR: {
450 //Update the pipe and device's address, and fill the address into the device object
451 ESP_ERROR_CHECK(hcd_pipe_update_dev_addr(enum_ctrl->pipe, ENUM_DEV_ADDR));
452 ESP_ERROR_CHECK(usbh_hub_enum_fill_dev_addr(enum_ctrl->dev_hdl, ENUM_DEV_ADDR));
453 ret = true;
454 break;
455 }
456 case ENUM_STAGE_CHECK_FULL_DEV_DESC: {
457 //Fill device descriptor into the device object
458 const usb_device_desc_t *device_desc = (const usb_device_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t));
459 ESP_ERROR_CHECK(usbh_hub_enum_fill_dev_desc(enum_ctrl->dev_hdl, device_desc));
460 enum_ctrl->iManufacturer = device_desc->iManufacturer;
461 enum_ctrl->iProduct = device_desc->iProduct;
462 enum_ctrl->iSerialNumber = device_desc->iSerialNumber;
463 ret = true;
464 break;
465 }
466 case ENUM_STAGE_CHECK_SHORT_CONFIG_DESC: {
467 const usb_config_desc_t *config_desc = (usb_config_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t));
468 //Check if the returned descriptor is corrupted
469 if (config_desc->bDescriptorType != USB_B_DESCRIPTOR_TYPE_CONFIGURATION) {
470 ESP_LOGE(HUB_DRIVER_TAG, "Short config desc corrupt");
471 ret = false;
472 break;
473 }
474 #if (ENUM_CTRL_TRANSFER_MAX_DATA_LEN < UINT16_MAX) //Suppress -Wtype-limits warning due to uint16_t wTotalLength
475 //Check if the descriptor is too long to be supported
476 if (config_desc->wTotalLength > ENUM_CTRL_TRANSFER_MAX_DATA_LEN) {
477 ESP_LOGE(HUB_DRIVER_TAG, "Configuration descriptor larger than control transfer max length");
478 ret = false;
479 break;
480 }
481 #endif
482 //Save the configuration descriptors full length
483 enum_ctrl->wTotalLength = config_desc->wTotalLength;
484 ret = true;
485 break;
486 }
487 case ENUM_STAGE_CHECK_FULL_CONFIG_DESC: {
488 //Fill configuration descriptor into the device object
489 const usb_config_desc_t *config_desc = (usb_config_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t));
490 ESP_ERROR_CHECK(usbh_hub_enum_fill_config_desc(enum_ctrl->dev_hdl, config_desc));
491 ret = true;
492 break;
493 }
494 case ENUM_STAGE_CHECK_CONFIG: {
495 ret = true;
496 //Nothing to do
497 break;
498 }
499 case ENUM_STAGE_CHECK_SHORT_LANGID_TABLE:
500 case ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC:
501 case ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC:
502 case ENUM_STAGE_CHECK_SHORT_SER_STR_DESC: {
503 const usb_str_desc_t *str_desc = (usb_str_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t));
504 //Check if the returned descriptor is supported or corrupted
505 if (str_desc->bDescriptorType == 0) {
506 ESP_LOGW(HUB_DRIVER_TAG, "String desc not supported");
507 ret = false;
508 break;
509 } else if (str_desc->bDescriptorType != USB_B_DESCRIPTOR_TYPE_STRING) {
510 ESP_LOGE(HUB_DRIVER_TAG, "Full string desc corrupt");
511 ret = false;
512 break;
513 }
514 #if (ENUM_CTRL_TRANSFER_MAX_DATA_LEN < UINT8_MAX) //Suppress -Wtype-limits warning due to uint8_t bLength
515 //Check if the descriptor is too long to be supported
516 if (str_desc->bLength > (uint32_t)ENUM_CTRL_TRANSFER_MAX_DATA_LEN) {
517 ESP_LOGE(HUB_DRIVER_TAG, "String descriptor larger than control transfer max length");
518 ret = false;
519 break;
520 }
521 #endif
522 //Save the descriptors full length
523 enum_ctrl->str_desc_bLength = str_desc->bLength;
524 ret = true;
525 break;
526 }
527 case ENUM_STAGE_CHECK_FULL_LANGID_TABLE:
528 case ENUM_STAGE_CHECK_FULL_MANU_STR_DESC:
529 case ENUM_STAGE_CHECK_FULL_PROD_STR_DESC:
530 case ENUM_STAGE_CHECK_FULL_SER_STR_DESC: {
531 const usb_str_desc_t *str_desc = (usb_str_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t));
532 //Check if the returned descriptor is supported or corrupted
533 if (str_desc->bDescriptorType == 0) {
534 ESP_LOGW(HUB_DRIVER_TAG, "String desc not supported");
535 ret = false;
536 break;
537 } else if (str_desc->bDescriptorType != USB_B_DESCRIPTOR_TYPE_STRING) {
538 ESP_LOGE(HUB_DRIVER_TAG, "Full string desc corrupt");
539 ret = false;
540 break;
541 }
542 if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_LANGID_TABLE) {
543 //Scan the LANGID table for our target LANGID
544 bool target_langid_found = false;
545 int langid_table_num_entries = (str_desc->bLength - sizeof(usb_str_desc_t))/2; //Each LANGID is 2 bytes
546 for (int i = 0; i < langid_table_num_entries; i++) { //Each LANGID is 2 bytes
547 if (str_desc->wData[i] == ENUM_LANGID) {
548 target_langid_found = true;
549 break;
550 }
551 }
552 if (!target_langid_found) {
553 ESP_LOGE(HUB_DRIVER_TAG, "LANGID 0x%x not found", ENUM_LANGID);
554 }
555 ret = target_langid_found;
556 break;
557 } else {
558 //Fill the string descriptor into the device object
559 int select;
560 if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_MANU_STR_DESC) {
561 select = 0;
562 } else if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_PROD_STR_DESC) {
563 select = 1;
564 } else { //ENUM_STAGE_CHECK_FULL_PROD_STR_DESC
565 select = 2;
566 }
567 ESP_ERROR_CHECK(usbh_hub_enum_fill_str_desc(enum_ctrl->dev_hdl, str_desc, select));
568 ret = true;
569 break;
570 }
571 }
572 default: //Should never occur
573 ret = false;
574 abort();
575 break;
576 }
577 return ret;
578 }
579
enum_stage_cleanup(enum_ctrl_t * enum_ctrl)580 static void enum_stage_cleanup(enum_ctrl_t *enum_ctrl)
581 {
582 //We currently only support a single device connected to the root port. Move the device handle from enum to root
583 HUB_DRIVER_ENTER_CRITICAL();
584 p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_ACTIVE;
585 HUB_DRIVER_EXIT_CRITICAL();
586 p_hub_driver_obj->single_thread.root_dev_hdl = enum_ctrl->dev_hdl;
587 usb_device_handle_t dev_hdl = enum_ctrl->dev_hdl;
588 //Clear values in enum_ctrl
589 enum_ctrl->dev_hdl = NULL;
590 enum_ctrl->pipe = NULL;
591 //Update device object after enumeration is done
592 ESP_ERROR_CHECK(usbh_hub_enum_done(dev_hdl));
593 }
594
enum_stage_cleanup_failed(enum_ctrl_t * enum_ctrl)595 static void enum_stage_cleanup_failed(enum_ctrl_t *enum_ctrl)
596 {
597 //Enumeration failed. Clear the enum device handle and pipe
598 if (enum_ctrl->dev_hdl) {
599 //If enumeration failed due to a port event, we need to Halt, flush, and dequeue enum default pipe in case there
600 //was an in-flight URB.
601 ESP_ERROR_CHECK(hcd_pipe_command(enum_ctrl->pipe, HCD_PIPE_CMD_HALT));
602 ESP_ERROR_CHECK(hcd_pipe_command(enum_ctrl->pipe, HCD_PIPE_CMD_FLUSH));
603 hcd_urb_dequeue(enum_ctrl->pipe); //This could return NULL if there
604 ESP_ERROR_CHECK(usbh_hub_enum_failed(enum_ctrl->dev_hdl)); //Free the underlying device object first before recovering the port
605 }
606 //Clear values in enum_ctrl
607 enum_ctrl->dev_hdl = NULL;
608 enum_ctrl->pipe = NULL;
609 HUB_DRIVER_ENTER_CRITICAL();
610 //Enum could have failed due to a port error. If so, we need to trigger a port recovery
611 if (p_hub_driver_obj->dynamic.driver_state == HUB_DRIVER_STATE_ROOT_RECOVERY) {
612 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_RECOVER;
613 } else {
614 //Otherwise, we move to the enum failed state and wait for the device to disconnect
615 p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_ENUM_FAILED;
616 }
617 HUB_DRIVER_EXIT_CRITICAL();
618 }
619
enum_set_next_stage(enum_ctrl_t * enum_ctrl,bool last_stage_pass)620 static void enum_set_next_stage(enum_ctrl_t *enum_ctrl, bool last_stage_pass)
621 {
622 //Set next stage
623 if (last_stage_pass) {
624 if (enum_ctrl->stage != ENUM_STAGE_NONE &&
625 enum_ctrl->stage != ENUM_STAGE_CLEANUP &&
626 enum_ctrl->stage != ENUM_STAGE_CLEANUP_FAILED) {
627 enum_ctrl->stage++; //Go to next stage
628 } else {
629 enum_ctrl->stage = ENUM_STAGE_NONE;
630 }
631 } else {
632 switch (enum_ctrl->stage) {
633 case ENUM_STAGE_START:
634 //Stage failed but clean up not required
635 enum_ctrl->stage = ENUM_STAGE_NONE;
636 break;
637 case ENUM_STAGE_GET_SHORT_LANGID_TABLE:
638 case ENUM_STAGE_CHECK_SHORT_LANGID_TABLE:
639 case ENUM_STAGE_GET_FULL_LANGID_TABLE:
640 case ENUM_STAGE_CHECK_FULL_LANGID_TABLE:
641 case ENUM_STAGE_GET_SHORT_MANU_STR_DESC:
642 case ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC:
643 case ENUM_STAGE_GET_FULL_MANU_STR_DESC:
644 case ENUM_STAGE_CHECK_FULL_MANU_STR_DESC:
645 case ENUM_STAGE_GET_SHORT_PROD_STR_DESC:
646 case ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC:
647 case ENUM_STAGE_GET_FULL_PROD_STR_DESC:
648 case ENUM_STAGE_CHECK_FULL_PROD_STR_DESC:
649 case ENUM_STAGE_GET_SHORT_SER_STR_DESC:
650 case ENUM_STAGE_CHECK_SHORT_SER_STR_DESC:
651 case ENUM_STAGE_GET_FULL_SER_STR_DESC:
652 case ENUM_STAGE_CHECK_FULL_SER_STR_DESC:
653 //String descriptor stages are allow to fail. We just don't fetch them and treat enumeration as successful
654 enum_ctrl->stage = ENUM_STAGE_CLEANUP;
655 break;
656 default:
657 //Enumeration failed. Go to failure clean up
658 enum_ctrl->stage = ENUM_STAGE_CLEANUP_FAILED;
659 break;
660 }
661 }
662
663 //These stages are not waiting for a callback, so we need to re-trigger the enum event
664 bool re_trigger;
665 switch (enum_ctrl->stage) {
666 case ENUM_STAGE_GET_SHORT_DEV_DESC:
667 case ENUM_STAGE_SECOND_RESET:
668 case ENUM_STAGE_SET_ADDR:
669 case ENUM_STAGE_GET_FULL_DEV_DESC:
670 case ENUM_STAGE_GET_SHORT_CONFIG_DESC:
671 case ENUM_STAGE_GET_FULL_CONFIG_DESC:
672 case ENUM_STAGE_SET_CONFIG:
673 case ENUM_STAGE_GET_SHORT_LANGID_TABLE:
674 case ENUM_STAGE_GET_FULL_LANGID_TABLE:
675 case ENUM_STAGE_GET_SHORT_MANU_STR_DESC:
676 case ENUM_STAGE_GET_FULL_MANU_STR_DESC:
677 case ENUM_STAGE_GET_SHORT_PROD_STR_DESC:
678 case ENUM_STAGE_GET_FULL_PROD_STR_DESC:
679 case ENUM_STAGE_GET_SHORT_SER_STR_DESC:
680 case ENUM_STAGE_GET_FULL_SER_STR_DESC:
681 case ENUM_STAGE_CLEANUP:
682 case ENUM_STAGE_CLEANUP_FAILED:
683 re_trigger = true;
684 break;
685 default:
686 re_trigger = false;
687 break;
688 }
689 if (re_trigger) {
690 HUB_DRIVER_ENTER_CRITICAL();
691 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
692 HUB_DRIVER_EXIT_CRITICAL();
693 }
694 }
695
696 // ------------------------------------------------- Event Handling ----------------------------------------------------
697
698 // ---------------------- Callbacks ------------------------
699
root_port_callback(hcd_port_handle_t port_hdl,hcd_port_event_t port_event,void * user_arg,bool in_isr)700 static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port_event, void *user_arg, bool in_isr)
701 {
702 HUB_DRIVER_ENTER_CRITICAL_SAFE();
703 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ROOT_EVENT;
704 HUB_DRIVER_EXIT_CRITICAL_SAFE();
705 assert(in_isr); //Currently, this callback should only ever be called from an ISR context
706 return p_hub_driver_obj->constant.notif_cb(USB_NOTIF_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.notif_cb_arg);;
707 }
708
enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl,hcd_pipe_event_t pipe_event,void * user_arg,bool in_isr)709 static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
710 {
711 //Note: This callback may have triggered when a failed enumeration is already cleaned up (e.g., due to a failed port reset)
712 HUB_DRIVER_ENTER_CRITICAL_SAFE();
713 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
714 HUB_DRIVER_EXIT_CRITICAL_SAFE();
715 return p_hub_driver_obj->constant.notif_cb(USB_NOTIF_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.notif_cb_arg);
716 }
717
usbh_hub_req_callback(hcd_port_handle_t port_hdl,usbh_hub_req_t hub_req,void * arg)718 static void usbh_hub_req_callback(hcd_port_handle_t port_hdl, usbh_hub_req_t hub_req, void *arg)
719 {
720 //We currently only support the root port, so the port_hdl should match the root port
721 assert(port_hdl == p_hub_driver_obj->constant.root_port_hdl);
722
723 HUB_DRIVER_ENTER_CRITICAL();
724 switch (hub_req) {
725 case USBH_HUB_REQ_PORT_DISABLE:
726 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_DISABLE;
727 break;
728 case USBH_HUB_REQ_PORT_RECOVER:
729 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_RECOVER;
730 break;
731 default:
732 //Should never occur
733 abort();
734 break;
735 }
736 HUB_DRIVER_EXIT_CRITICAL();
737
738 p_hub_driver_obj->constant.notif_cb(USB_NOTIF_SOURCE_HUB, false, p_hub_driver_obj->constant.notif_cb_arg);
739 }
740
741 // ---------------------- Handlers -------------------------
742
root_port_handle_events(hcd_port_handle_t root_port_hdl)743 static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
744 {
745 hcd_port_event_t port_event = hcd_port_handle_event(root_port_hdl);
746 switch (port_event) {
747 case HCD_PORT_EVENT_NONE:
748 //Nothing to do
749 break;
750 case HCD_PORT_EVENT_CONNECTION: {
751 if (hcd_port_command(root_port_hdl, HCD_PORT_CMD_RESET) == ESP_OK) {
752 ESP_LOGD(HUB_DRIVER_TAG, "Root port reset");
753 //Start enumeration
754 HUB_DRIVER_ENTER_CRITICAL();
755 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
756 p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_ENUM;
757 HUB_DRIVER_EXIT_CRITICAL();
758 p_hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_START;
759 } else {
760 ESP_LOGE(HUB_DRIVER_TAG, "Root port reset failed");
761 }
762 break;
763 }
764 case HCD_PORT_EVENT_DISCONNECTION:
765 case HCD_PORT_EVENT_ERROR:
766 case HCD_PORT_EVENT_OVERCURRENT: {
767 bool pass_event_to_usbh = false;
768 HUB_DRIVER_ENTER_CRITICAL();
769 switch (p_hub_driver_obj->dynamic.driver_state) {
770 case HUB_DRIVER_STATE_ROOT_POWERED: //This occurred before enumeration
771 case HUB_DRIVER_STATE_ROOT_ENUM_FAILED: //This occurred after a failed enumeration.
772 //Therefore, there's no device and we can go straight to port recovery
773 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_RECOVER;
774 break;
775 case HUB_DRIVER_STATE_ROOT_ENUM:
776 //This occurred during enumeration. Therefore, we need to recover the failed enumeration
777 p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
778 p_hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_CLEANUP_FAILED;
779 break;
780 case HUB_DRIVER_STATE_ROOT_ACTIVE:
781 //There was an enumerated device. We need to indicate to USBH that the device is gone
782 pass_event_to_usbh = true;
783 break;
784 default:
785 abort(); //Should never occur
786 break;
787 }
788 p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_RECOVERY;
789 HUB_DRIVER_EXIT_CRITICAL();
790 if (pass_event_to_usbh) {
791 assert(p_hub_driver_obj->single_thread.root_dev_hdl);
792 ESP_ERROR_CHECK(usbh_hub_pass_event(p_hub_driver_obj->single_thread.root_dev_hdl, USBH_HUB_EVENT_PORT_ERROR));
793 }
794 break;
795 }
796 default:
797 abort(); //Should never occur
798 break;
799 }
800 }
801
enum_handle_events(void)802 static void enum_handle_events(void)
803 {
804 bool stage_pass;
805 enum_ctrl_t *enum_ctrl = &p_hub_driver_obj->single_thread.enum_ctrl;
806 switch (enum_ctrl->stage) {
807 case ENUM_STAGE_START:
808 stage_pass = enum_stage_start(enum_ctrl);
809 break;
810 case ENUM_STAGE_SECOND_RESET:
811 stage_pass = enum_stage_second_reset(enum_ctrl);
812 break;
813 //Transfer submission stages
814 case ENUM_STAGE_GET_SHORT_DEV_DESC:
815 case ENUM_STAGE_SET_ADDR:
816 case ENUM_STAGE_GET_FULL_DEV_DESC:
817 case ENUM_STAGE_GET_SHORT_CONFIG_DESC:
818 case ENUM_STAGE_GET_FULL_CONFIG_DESC:
819 case ENUM_STAGE_SET_CONFIG:
820 case ENUM_STAGE_GET_SHORT_LANGID_TABLE:
821 case ENUM_STAGE_GET_FULL_LANGID_TABLE:
822 case ENUM_STAGE_GET_SHORT_MANU_STR_DESC:
823 case ENUM_STAGE_GET_FULL_MANU_STR_DESC:
824 case ENUM_STAGE_GET_SHORT_PROD_STR_DESC:
825 case ENUM_STAGE_GET_FULL_PROD_STR_DESC:
826 case ENUM_STAGE_GET_SHORT_SER_STR_DESC:
827 case ENUM_STAGE_GET_FULL_SER_STR_DESC:
828 stage_pass = enum_stage_transfer(enum_ctrl);
829 break;
830 //Transfer check stages
831 case ENUM_STAGE_CHECK_SHORT_DEV_DESC:
832 case ENUM_STAGE_CHECK_ADDR:
833 case ENUM_STAGE_CHECK_FULL_DEV_DESC:
834 case ENUM_STAGE_CHECK_SHORT_CONFIG_DESC:
835 case ENUM_STAGE_CHECK_FULL_CONFIG_DESC:
836 case ENUM_STAGE_CHECK_CONFIG:
837 case ENUM_STAGE_CHECK_SHORT_LANGID_TABLE:
838 case ENUM_STAGE_CHECK_FULL_LANGID_TABLE:
839 case ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC:
840 case ENUM_STAGE_CHECK_FULL_MANU_STR_DESC:
841 case ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC:
842 case ENUM_STAGE_CHECK_FULL_PROD_STR_DESC:
843 case ENUM_STAGE_CHECK_SHORT_SER_STR_DESC:
844 case ENUM_STAGE_CHECK_FULL_SER_STR_DESC:
845 stage_pass = enum_stage_transfer_check(enum_ctrl);
846 break;
847 case ENUM_STAGE_CLEANUP:
848 enum_stage_cleanup(enum_ctrl);
849 stage_pass = true;
850 break;
851 case ENUM_STAGE_CLEANUP_FAILED:
852 enum_stage_cleanup_failed(enum_ctrl);
853 stage_pass = true;
854 break;
855 default:
856 //Note: Don't abort here. The enum_dflt_pipe_callback() can trigger a HUB_DRIVER_FLAG_ACTION_ENUM_EVENT after a cleanup.
857 stage_pass = true;
858 break;
859 }
860 if (stage_pass) {
861 ESP_LOGD(HUB_DRIVER_TAG, "Stage done: %s", enum_stage_strings[enum_ctrl->stage]);
862 } else {
863 ESP_LOGE(HUB_DRIVER_TAG, "Stage failed: %s", enum_stage_strings[enum_ctrl->stage]);
864 }
865 enum_set_next_stage(enum_ctrl, stage_pass);
866 }
867
868 // ---------------------------------------------- Hub Driver Functions -------------------------------------------------
869
hub_install(hub_config_t * hub_config)870 esp_err_t hub_install(hub_config_t *hub_config)
871 {
872 HUB_DRIVER_ENTER_CRITICAL();
873 HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj == NULL, ESP_ERR_INVALID_STATE);
874 HUB_DRIVER_EXIT_CRITICAL();
875 //Allocate Hub driver object
876 hub_driver_t *hub_driver_obj = heap_caps_calloc(1, sizeof(hub_driver_t), MALLOC_CAP_DEFAULT);
877 urb_t *enum_urb = urb_alloc(sizeof(usb_setup_packet_t) + ENUM_CTRL_TRANSFER_MAX_DATA_LEN, 0, 0);
878 if (hub_driver_obj == NULL || enum_urb == NULL) {
879 return ESP_ERR_NO_MEM;
880 }
881 esp_err_t ret;
882 //Install HCD port
883 hcd_port_config_t port_config = {
884 .fifo_bias = HUB_ROOT_HCD_PORT_FIFO_BIAS,
885 .callback = root_port_callback,
886 .callback_arg = NULL,
887 .context = NULL,
888 };
889 hcd_port_handle_t port_hdl;
890 ret = hcd_port_init(HUB_ROOT_PORT_NUM, &port_config, &port_hdl);
891 if (ret != ESP_OK) {
892 goto err;
893 }
894 //Initialize Hub driver object
895 hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_INSTALLED;
896 hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_NONE;
897 hub_driver_obj->single_thread.enum_ctrl.urb = enum_urb;
898 hub_driver_obj->constant.root_port_hdl = port_hdl;
899 hub_driver_obj->constant.notif_cb = hub_config->notif_cb;
900 hub_driver_obj->constant.notif_cb_arg = hub_config->notif_cb_arg;
901 HUB_DRIVER_ENTER_CRITICAL();
902 if (p_hub_driver_obj != NULL) {
903 HUB_DRIVER_EXIT_CRITICAL();
904 ret = ESP_ERR_INVALID_STATE;
905 goto assign_err;
906 }
907 p_hub_driver_obj = hub_driver_obj;
908 HUB_DRIVER_EXIT_CRITICAL();
909 //Indicate to USBH that the hub is installed
910 ESP_ERROR_CHECK(usbh_hub_is_installed(usbh_hub_req_callback, NULL));
911 ret = ESP_OK;
912 return ret;
913
914 assign_err:
915 ESP_ERROR_CHECK(hcd_port_deinit(port_hdl));
916 err:
917 urb_free(enum_urb);
918 heap_caps_free(hub_driver_obj);
919 return ret;
920 }
921
hub_uninstall(void)922 esp_err_t hub_uninstall(void)
923 {
924 HUB_DRIVER_ENTER_CRITICAL();
925 HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
926 HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj->dynamic.driver_state == HUB_DRIVER_STATE_INSTALLED, ESP_ERR_INVALID_STATE);
927 hub_driver_t *hub_driver_obj = p_hub_driver_obj;
928 p_hub_driver_obj = NULL;
929 HUB_DRIVER_EXIT_CRITICAL();
930
931 ESP_ERROR_CHECK(hcd_port_deinit(hub_driver_obj->constant.root_port_hdl));
932 //Free Hub driver resources
933 urb_free(hub_driver_obj->single_thread.enum_ctrl.urb);
934 heap_caps_free(hub_driver_obj);
935 return ESP_OK;
936 }
937
hub_root_start(void)938 esp_err_t hub_root_start(void)
939 {
940 HUB_DRIVER_ENTER_CRITICAL();
941 HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
942 HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj->dynamic.driver_state == HUB_DRIVER_STATE_INSTALLED, ESP_ERR_INVALID_STATE);
943 HUB_DRIVER_EXIT_CRITICAL();
944 //Power ON the root port
945 esp_err_t ret;
946 ret = hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_ON);
947 if (ret == ESP_OK) {
948 HUB_DRIVER_ENTER_CRITICAL();
949 p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_POWERED;
950 HUB_DRIVER_EXIT_CRITICAL();
951 }
952 return ret;
953 }
954
hub_root_stop(void)955 esp_err_t hub_root_stop(void)
956 {
957 HUB_DRIVER_ENTER_CRITICAL();
958 HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
959 HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj->dynamic.driver_state != HUB_DRIVER_STATE_INSTALLED, ESP_ERR_INVALID_STATE);
960 HUB_DRIVER_EXIT_CRITICAL();
961 esp_err_t ret;
962 ret = hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_OFF);
963 if (ret == ESP_OK) {
964 HUB_DRIVER_ENTER_CRITICAL();
965 p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_INSTALLED;
966 HUB_DRIVER_EXIT_CRITICAL();
967 }
968 return ret;
969 }
970
hub_process(void)971 esp_err_t hub_process(void)
972 {
973 HUB_DRIVER_ENTER_CRITICAL();
974 uint32_t action_flags = p_hub_driver_obj->dynamic.flags.actions;
975 p_hub_driver_obj->dynamic.flags.actions = 0;
976 HUB_DRIVER_EXIT_CRITICAL();
977
978 while (action_flags) {
979 /*
980 Mutually exclude Root event and Port disable:
981 If a device was waiting for its port to be disabled, and a port error occurs in that time, the root event
982 handler will send a USBH_HUB_EVENT_PORT_ERROR to the USBH already, thus freeing the device and canceling the
983 waiting of port disable.
984 */
985 if (action_flags & HUB_DRIVER_FLAG_ACTION_ROOT_EVENT) {
986 root_port_handle_events(p_hub_driver_obj->constant.root_port_hdl);
987 } else if (action_flags & HUB_DRIVER_FLAG_ACTION_PORT_DISABLE) {
988 ESP_LOGD(HUB_DRIVER_TAG, "Disabling root port");
989 hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_DISABLE);
990 ESP_ERROR_CHECK(usbh_hub_pass_event(p_hub_driver_obj->single_thread.root_dev_hdl, USBH_HUB_EVENT_PORT_DISABLED));
991 //The root port has been disabled, so the root_dev_hdl is no longer valid
992 p_hub_driver_obj->single_thread.root_dev_hdl = NULL;
993 }
994
995 if (action_flags & HUB_DRIVER_FLAG_ACTION_PORT_RECOVER) {
996 ESP_LOGD(HUB_DRIVER_TAG, "Recovering root port");
997 ESP_ERROR_CHECK(hcd_port_recover(p_hub_driver_obj->constant.root_port_hdl));
998 ESP_ERROR_CHECK(hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_ON));
999 HUB_DRIVER_ENTER_CRITICAL();
1000 p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_POWERED;
1001 HUB_DRIVER_EXIT_CRITICAL();
1002 //USBH requesting a port recovery means the device has already been freed. Clear root_dev_hdl
1003 p_hub_driver_obj->single_thread.root_dev_hdl = NULL;
1004 }
1005
1006 if (action_flags & HUB_DRIVER_FLAG_ACTION_ENUM_EVENT) {
1007 enum_handle_events();
1008 }
1009
1010 HUB_DRIVER_ENTER_CRITICAL();
1011 action_flags = p_hub_driver_obj->dynamic.flags.actions;
1012 p_hub_driver_obj->dynamic.flags.actions = 0;
1013 HUB_DRIVER_EXIT_CRITICAL();
1014 }
1015 return ESP_OK;
1016 }
1017