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 <stdint.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <sys/queue.h>
12 #include "freertos/FreeRTOS.h"
13 #include "freertos/portmacro.h"
14 #include "freertos/task.h"
15 #include "freertos/semphr.h"
16 #include "esp_err.h"
17 #include "esp_log.h"
18 #include "esp_heap_caps.h"
19 #include "hcd.h"
20 #include "usbh.h"
21 #include "usb/usb_helpers.h"
22 #include "usb/usb_types_ch9.h"
23 
24 //Device action flags. LISTED IN THE ORDER THEY SHOULD BE HANDLED IN within usbh_process(). Some actions are mutually exclusive
25 #define DEV_FLAG_ACTION_PIPE_HALT_AND_FLUSH         0x0001  //Halt all non-default pipes then flush them (called after a device gone is gone)
26 #define DEV_FLAG_ACTION_DEFAULT_PIPE_FLUSH          0x0002  //Retire all URBS in the default pipe
27 #define DEV_FLAG_ACTION_DEFAULT_PIPE_DEQUEUE        0x0004  //Dequeue all URBs from default pipe
28 #define DEV_FLAG_ACTION_DEFAULT_PIPE_CLEAR          0x0008  //Move the default pipe to the active state
29 #define DEV_FLAG_ACTION_SEND_GONE_EVENT             0x0010  //Send a USB_HOST_CLIENT_EVENT_DEV_GONE event
30 #define DEV_FLAG_ACTION_FREE                        0x0020  //Free the device object
31 #define DEV_FLAG_ACTION_FREE_AND_RECOVER            0x0040  //Free the device object, but send a USBH_HUB_REQ_PORT_RECOVER request afterwards.
32 #define DEV_FLAG_ACTION_PORT_DISABLE                0x0080  //Request the hub driver to disable the port of the device
33 #define DEV_FLAG_ACTION_SEND_NEW                    0x0100  //Send a new device event
34 
35 #define EP_NUM_MIN                                  1
36 #define EP_NUM_MAX                                  16
37 
38 typedef struct device_s device_t;
39 struct device_s {
40     //Dynamic members require a critical section
41     struct {
42         TAILQ_ENTRY(device_s) tailq_entry;
43         union {
44             struct {
45                 uint32_t in_pending_list: 1;
46                 uint32_t is_gone: 1;
47                 uint32_t waiting_close: 1;
48                 uint32_t waiting_port_disable: 1;
49                 uint32_t waiting_free: 1;
50                 uint32_t reserved27: 27;
51             };
52             uint32_t val;
53         } flags;
54         uint32_t action_flags;
55         int num_ctrl_xfers_inflight;
56         usb_device_state_t state;
57         uint32_t ref_count;
58     } dynamic;
59     //Mux protected members must be protected by the USBH mux_lock when accessed
60     struct {
61         hcd_pipe_handle_t ep_in[EP_NUM_MAX - 1];    //IN EP owner contexts. -1 to exclude the default endpoint
62         hcd_pipe_handle_t ep_out[EP_NUM_MAX - 1];   //OUT EP owner contexts. -1 to exclude the default endpoint
63     } mux_protected;
64     //Constant members do no change after device allocation and enumeration thus do not require a critical section
65     struct {
66         hcd_pipe_handle_t default_pipe;
67         hcd_port_handle_t port_hdl;
68         uint8_t address;
69         usb_speed_t speed;
70         const usb_device_desc_t *desc;
71         const usb_config_desc_t *config_desc;
72         const usb_str_desc_t *str_desc_manu;
73         const usb_str_desc_t *str_desc_product;
74         const usb_str_desc_t *str_desc_ser_num;
75     } constant;
76 };
77 
78 typedef struct {
79     //Dynamic members require a critical section
80     struct {
81         TAILQ_HEAD(tailhead_devs, device_s) devs_idle_tailq;        //Tailq of all enum and configured devices
82         TAILQ_HEAD(tailhead_devs_cb, device_s) devs_pending_tailq;  //Tailq of devices that need to have their cb called
83     } dynamic;
84     //Mux protected members must be protected by the USBH mux_lock when accessed
85     struct {
86         uint8_t num_device;     //Number of enumerated devices
87     } mux_protected;
88     //Constant members do no change after installation thus do not require a critical section
89     struct {
90         usb_notif_cb_t notif_cb;
91         void *notif_cb_arg;
92         usbh_hub_req_cb_t hub_req_cb;
93         void *hub_req_cb_arg;
94         usbh_event_cb_t event_cb;
95         void *event_cb_arg;
96         usbh_ctrl_xfer_cb_t ctrl_xfer_cb;
97         void *ctrl_xfer_cb_arg;
98         SemaphoreHandle_t mux_lock;
99     } constant;
100 } usbh_t;
101 
102 static usbh_t *p_usbh_obj = NULL;
103 
104 static portMUX_TYPE usbh_lock = portMUX_INITIALIZER_UNLOCKED;
105 
106 const char *USBH_TAG = "USBH";
107 
108 #define USBH_ENTER_CRITICAL_ISR()       portENTER_CRITICAL_ISR(&usbh_lock)
109 #define USBH_EXIT_CRITICAL_ISR()        portEXIT_CRITICAL_ISR(&usbh_lock)
110 #define USBH_ENTER_CRITICAL()           portENTER_CRITICAL(&usbh_lock)
111 #define USBH_EXIT_CRITICAL()            portEXIT_CRITICAL(&usbh_lock)
112 #define USBH_ENTER_CRITICAL_SAFE()      portENTER_CRITICAL_SAFE(&usbh_lock)
113 #define USBH_EXIT_CRITICAL_SAFE()       portEXIT_CRITICAL_SAFE(&usbh_lock)
114 
115 #define USBH_CHECK(cond, ret_val) ({                                        \
116             if (!(cond)) {                                                  \
117                 return (ret_val);                                           \
118             }                                                               \
119 })
120 #define USBH_CHECK_FROM_CRIT(cond, ret_val) ({                              \
121             if (!(cond)) {                                                  \
122                 USBH_EXIT_CRITICAL();                                       \
123                 return ret_val;                                             \
124             }                                                               \
125 })
126 
127 // --------------------------------------------------- Allocation ------------------------------------------------------
128 
device_alloc(hcd_port_handle_t port_hdl,usb_speed_t speed,device_t ** dev_obj_ret)129 static esp_err_t device_alloc(hcd_port_handle_t port_hdl, usb_speed_t speed, device_t **dev_obj_ret)
130 {
131     esp_err_t ret;
132     device_t *dev_obj = heap_caps_calloc(1, sizeof(device_t), MALLOC_CAP_DEFAULT);
133     usb_device_desc_t *dev_desc = heap_caps_calloc(1, sizeof(usb_device_desc_t), MALLOC_CAP_DEFAULT);
134     if (dev_obj == NULL || dev_desc == NULL) {
135         ret = ESP_ERR_NO_MEM;
136         goto err;
137     }
138     //Allocate default pipe. We set the pipe callback to NULL for now
139     hcd_pipe_config_t pipe_config = {
140         .callback = NULL,
141         .callback_arg = NULL,
142         .context = (void *)dev_obj,
143         .ep_desc = NULL,    //No endpoint descriptor means we're allocating a default pipe
144         .dev_speed = speed,
145         .dev_addr = 0,
146     };
147     hcd_pipe_handle_t default_pipe_hdl;
148     ret = hcd_pipe_alloc(port_hdl, &pipe_config, &default_pipe_hdl);
149     if (ret != ESP_OK) {
150         goto err;
151     }
152     //Initialize device object
153     dev_obj->dynamic.state = USB_DEVICE_STATE_DEFAULT;
154     dev_obj->constant.default_pipe = default_pipe_hdl;
155     dev_obj->constant.port_hdl = port_hdl;
156     //Note: dev_obj->constant.address is assigned later during enumeration
157     dev_obj->constant.speed = speed;
158     dev_obj->constant.desc = dev_desc;
159     *dev_obj_ret = dev_obj;
160     ret = ESP_OK;
161     return ret;
162 
163 err:
164     heap_caps_free(dev_desc);
165     heap_caps_free(dev_obj);
166     return ret;
167 }
168 
device_free(device_t * dev_obj)169 static void device_free(device_t *dev_obj)
170 {
171     if (dev_obj == NULL) {
172         return;
173     }
174     //Configuration might not have been allocated (in case of early enumeration failure)
175     if (dev_obj->constant.config_desc) {
176         heap_caps_free((usb_config_desc_t *)dev_obj->constant.config_desc);
177     }
178     //String descriptors might not have been allocated (in case of early enumeration failure)
179     if (dev_obj->constant.str_desc_manu) {
180         heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_manu);
181     }
182     if (dev_obj->constant.str_desc_product) {
183         heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_product);
184     }
185     if (dev_obj->constant.str_desc_ser_num) {
186         heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_ser_num);
187     }
188     heap_caps_free((usb_device_desc_t *)dev_obj->constant.desc);
189     ESP_ERROR_CHECK(hcd_pipe_free(dev_obj->constant.default_pipe));
190     heap_caps_free(dev_obj);
191 }
192 
193 // -------------------------------------------------- Event Related ----------------------------------------------------
194 
_dev_set_actions(device_t * dev_obj,uint32_t action_flags)195 static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags)
196 {
197     if (action_flags == 0) {
198         return false;
199     }
200     bool call_notif_cb;
201     //Check if device is already on the callback list
202     if (!dev_obj->dynamic.flags.in_pending_list) {
203         //Move device form idle device list to callback device list
204         TAILQ_REMOVE(&p_usbh_obj->dynamic.devs_idle_tailq, dev_obj, dynamic.tailq_entry);
205         TAILQ_INSERT_TAIL(&p_usbh_obj->dynamic.devs_pending_tailq, dev_obj, dynamic.tailq_entry);
206         dev_obj->dynamic.action_flags |= action_flags;
207         dev_obj->dynamic.flags.in_pending_list = 1;
208         call_notif_cb = true;
209     } else {
210         call_notif_cb = false;
211     }
212     return call_notif_cb;
213 }
214 
default_pipe_callback(hcd_pipe_handle_t pipe_hdl,hcd_pipe_event_t pipe_event,void * user_arg,bool in_isr)215 static bool default_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
216 {
217     uint32_t action_flags;
218     device_t *dev_obj = (device_t *)user_arg;
219     switch (pipe_event) {
220         case HCD_PIPE_EVENT_URB_DONE:
221             //A control transfer completed on the default pipe. We need to dequeue it
222             action_flags = DEV_FLAG_ACTION_DEFAULT_PIPE_DEQUEUE;
223             break;
224         case HCD_PIPE_EVENT_ERROR_XFER:
225         case HCD_PIPE_EVENT_ERROR_URB_NOT_AVAIL:
226         case HCD_PIPE_EVENT_ERROR_OVERFLOW:
227             //The default pipe has encountered an error. We need to retire all URBs, dequeue them, then make the pipe active again
228             action_flags = DEV_FLAG_ACTION_DEFAULT_PIPE_FLUSH |
229                            DEV_FLAG_ACTION_DEFAULT_PIPE_DEQUEUE |
230                            DEV_FLAG_ACTION_DEFAULT_PIPE_CLEAR;
231             if (in_isr) {
232                 ESP_EARLY_LOGE(USBH_TAG, "Dev %d EP 0 Error", dev_obj->constant.address);
233             } else {
234                 ESP_LOGE(USBH_TAG, "Dev %d EP 0 Error", dev_obj->constant.address);
235             }
236             break;
237         case HCD_PIPE_EVENT_ERROR_STALL:
238             //The default pipe encountered a "protocol stall". We just need to dequeue URBs then make the pipe active again
239             action_flags = DEV_FLAG_ACTION_DEFAULT_PIPE_DEQUEUE | DEV_FLAG_ACTION_DEFAULT_PIPE_CLEAR;
240             if (in_isr) {
241                 ESP_EARLY_LOGE(USBH_TAG, "Dev %d EP 0 STALL", dev_obj->constant.address);
242             } else {
243                 ESP_LOGE(USBH_TAG, "Dev %d EP 0 STALL", dev_obj->constant.address);
244             }
245             break;
246         default:
247             action_flags = 0;
248             break;
249     }
250 
251     USBH_ENTER_CRITICAL_SAFE();
252     bool call_notif_cb = _dev_set_actions(dev_obj, action_flags);
253     USBH_EXIT_CRITICAL_SAFE();
254 
255     bool yield = false;
256     if (call_notif_cb) {
257         yield = p_usbh_obj->constant.notif_cb(USB_NOTIF_SOURCE_USBH, in_isr, p_usbh_obj->constant.notif_cb_arg);
258     }
259     return yield;
260 }
261 
handle_pipe_halt_and_flush(device_t * dev_obj)262 static void handle_pipe_halt_and_flush(device_t *dev_obj)
263 {
264     //We need to take the mux_lock to access mux_protected members
265     xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
266     //Halt then flush all non-default IN pipes
267     for (int i = 0; i < (EP_NUM_MAX - 1); i++) {
268         if (dev_obj->mux_protected.ep_in[i] != NULL) {
269             ESP_ERROR_CHECK(hcd_pipe_command(dev_obj->mux_protected.ep_in[i], HCD_PIPE_CMD_HALT));
270             ESP_ERROR_CHECK(hcd_pipe_command(dev_obj->mux_protected.ep_in[i], HCD_PIPE_CMD_FLUSH));
271         }
272         if (dev_obj->mux_protected.ep_out[i] != NULL) {
273             ESP_ERROR_CHECK(hcd_pipe_command(dev_obj->mux_protected.ep_out[i], HCD_PIPE_CMD_HALT));
274             ESP_ERROR_CHECK(hcd_pipe_command(dev_obj->mux_protected.ep_out[i], HCD_PIPE_CMD_FLUSH));
275         }
276     }
277     xSemaphoreGive(p_usbh_obj->constant.mux_lock);
278 }
279 
handle_dev_free(device_t * dev_obj)280 static bool handle_dev_free(device_t *dev_obj)
281 {
282     //We need to take the mux_lock to access mux_protected members
283     xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
284     USBH_ENTER_CRITICAL();
285     //Remove the device object for it's containing list
286     if (dev_obj->dynamic.flags.in_pending_list) {
287         dev_obj->dynamic.flags.in_pending_list = 0;
288         TAILQ_REMOVE(&p_usbh_obj->dynamic.devs_pending_tailq, dev_obj, dynamic.tailq_entry);
289     } else {
290         TAILQ_REMOVE(&p_usbh_obj->dynamic.devs_idle_tailq, dev_obj, dynamic.tailq_entry);
291     }
292     USBH_EXIT_CRITICAL();
293     p_usbh_obj->mux_protected.num_device--;
294     bool all_free = (p_usbh_obj->mux_protected.num_device == 0);
295     xSemaphoreGive(p_usbh_obj->constant.mux_lock);
296     device_free(dev_obj);
297     return all_free;
298 }
299 
300 // ------------------------------------------------- USBH Functions ----------------------------------------------------
301 
usbh_install(const usbh_config_t * usbh_config)302 esp_err_t usbh_install(const usbh_config_t *usbh_config)
303 {
304     USBH_CHECK(usbh_config != NULL, ESP_ERR_INVALID_ARG);
305     USBH_ENTER_CRITICAL();
306     USBH_CHECK_FROM_CRIT(p_usbh_obj == NULL, ESP_ERR_INVALID_STATE);
307     USBH_EXIT_CRITICAL();
308 
309     esp_err_t ret;
310     usbh_t *usbh_obj = heap_caps_calloc(1, sizeof(usbh_t), MALLOC_CAP_DEFAULT);
311     SemaphoreHandle_t mux_lock = xSemaphoreCreateMutex();
312     if (usbh_obj == NULL || mux_lock == NULL) {
313         ret = ESP_ERR_NO_MEM;
314         goto alloc_err;
315     }
316     //Install HCD
317     ret = hcd_install(&usbh_config->hcd_config);
318     if (ret != ESP_OK) {
319         goto hcd_install_err;
320     }
321     //Initialize usbh object
322     TAILQ_INIT(&usbh_obj->dynamic.devs_idle_tailq);
323     TAILQ_INIT(&usbh_obj->dynamic.devs_pending_tailq);
324     usbh_obj->constant.notif_cb = usbh_config->notif_cb;
325     usbh_obj->constant.notif_cb_arg = usbh_config->notif_cb_arg;
326     usbh_obj->constant.event_cb = usbh_config->event_cb;
327     usbh_obj->constant.event_cb_arg = usbh_config->event_cb_arg;
328     usbh_obj->constant.ctrl_xfer_cb = usbh_config->ctrl_xfer_cb;
329     usbh_obj->constant.ctrl_xfer_cb_arg = usbh_config->ctrl_xfer_cb_arg;
330     usbh_obj->constant.mux_lock = mux_lock;
331 
332     //Assign usbh object pointer
333     USBH_ENTER_CRITICAL();
334     if (p_usbh_obj != NULL) {
335         USBH_EXIT_CRITICAL();
336         ret = ESP_ERR_INVALID_STATE;
337         goto assign_err;
338     }
339     p_usbh_obj = usbh_obj;
340     USBH_EXIT_CRITICAL();
341 
342     ret = ESP_OK;
343     return ret;
344 
345 assign_err:
346     ESP_ERROR_CHECK(hcd_uninstall());
347 hcd_install_err:
348 alloc_err:
349     if (mux_lock != NULL) {
350         vSemaphoreDelete(mux_lock);
351     }
352     heap_caps_free(usbh_obj);
353     return ret;
354 }
355 
usbh_uninstall(void)356 esp_err_t usbh_uninstall(void)
357 {
358     //Check that USBH is in a state to be uninstalled
359     USBH_ENTER_CRITICAL();
360     USBH_CHECK_FROM_CRIT(p_usbh_obj != NULL, ESP_ERR_INVALID_STATE);
361     usbh_t *usbh_obj = p_usbh_obj;
362     USBH_EXIT_CRITICAL();
363 
364     esp_err_t ret;
365     //We need to take the mux_lock to access mux_protected members
366     xSemaphoreTake(usbh_obj->constant.mux_lock, portMAX_DELAY);
367     if (p_usbh_obj->mux_protected.num_device > 0) {
368         //There are still devices allocated. Can't uninstall right now.
369         ret = ESP_ERR_INVALID_STATE;
370         goto exit;
371     }
372     //Check again if we can uninstall
373     USBH_ENTER_CRITICAL();
374     assert(p_usbh_obj == usbh_obj);
375     p_usbh_obj = NULL;
376     USBH_EXIT_CRITICAL();
377     xSemaphoreGive(usbh_obj->constant.mux_lock);
378 
379     //Uninstall HCD, free resources
380     ESP_ERROR_CHECK(hcd_uninstall());
381     vSemaphoreDelete(usbh_obj->constant.mux_lock);
382     heap_caps_free(usbh_obj);
383     ret = ESP_OK;
384     return ret;
385 
386 exit:
387     xSemaphoreGive(p_usbh_obj->constant.mux_lock);
388     return ret;
389 }
390 
usbh_process(void)391 esp_err_t usbh_process(void)
392 {
393     USBH_ENTER_CRITICAL();
394     USBH_CHECK_FROM_CRIT(p_usbh_obj != NULL, ESP_ERR_INVALID_STATE);
395     //Keep clearing devices with events
396     while (!TAILQ_EMPTY(&p_usbh_obj->dynamic.devs_pending_tailq)){
397         //Move the device back into the idle device list,
398         device_t *dev_obj = TAILQ_FIRST(&p_usbh_obj->dynamic.devs_pending_tailq);
399         TAILQ_REMOVE(&p_usbh_obj->dynamic.devs_pending_tailq, dev_obj, dynamic.tailq_entry);
400         TAILQ_INSERT_TAIL(&p_usbh_obj->dynamic.devs_idle_tailq, dev_obj, dynamic.tailq_entry);
401         //Clear the device's flags
402         uint32_t action_flags = dev_obj->dynamic.action_flags;
403         dev_obj->dynamic.action_flags = 0;
404         dev_obj->dynamic.flags.in_pending_list = 0;
405 
406         /* ---------------------------------------------------------------------
407         Exit critical section to handle device action flags in their listed order
408         --------------------------------------------------------------------- */
409         USBH_EXIT_CRITICAL();
410         ESP_LOGD(USBH_TAG, "Processing actions 0x%x", action_flags);
411         //Sanity check. If the device is being freed, there must not be any other action flags set
412         assert(!(action_flags & DEV_FLAG_ACTION_FREE) || action_flags == DEV_FLAG_ACTION_FREE);
413 
414         if (action_flags & DEV_FLAG_ACTION_PIPE_HALT_AND_FLUSH) {
415             handle_pipe_halt_and_flush(dev_obj);
416         }
417         if (action_flags & DEV_FLAG_ACTION_DEFAULT_PIPE_FLUSH) {
418             ESP_ERROR_CHECK(hcd_pipe_command(dev_obj->constant.default_pipe, HCD_PIPE_CMD_HALT));
419             ESP_ERROR_CHECK(hcd_pipe_command(dev_obj->constant.default_pipe, HCD_PIPE_CMD_FLUSH));
420         }
421         if (action_flags & DEV_FLAG_ACTION_DEFAULT_PIPE_DEQUEUE) {
422             //Empty URBs from default pipe and trigger a control transfer callback
423             ESP_LOGD(USBH_TAG, "Default pipe device %d", dev_obj->constant.address);
424             int num_urbs = 0;
425             urb_t *urb = hcd_urb_dequeue(dev_obj->constant.default_pipe);
426             while (urb != NULL) {
427                 num_urbs++;
428                 p_usbh_obj->constant.ctrl_xfer_cb((usb_device_handle_t)dev_obj, urb, p_usbh_obj->constant.ctrl_xfer_cb_arg);
429                 urb = hcd_urb_dequeue(dev_obj->constant.default_pipe);
430             }
431             USBH_ENTER_CRITICAL();
432             dev_obj->dynamic.num_ctrl_xfers_inflight -= num_urbs;
433             USBH_EXIT_CRITICAL();
434         }
435         if (action_flags & DEV_FLAG_ACTION_DEFAULT_PIPE_CLEAR) {
436             //We allow the pipe command to fail just in case the pipe becomes invalid mid command
437             hcd_pipe_command(dev_obj->constant.default_pipe, HCD_PIPE_CMD_CLEAR);
438         }
439         if (action_flags & DEV_FLAG_ACTION_SEND_GONE_EVENT) {
440             //Flush the default pipe. Then do an event gone
441             ESP_LOGE(USBH_TAG, "Device %d gone", dev_obj->constant.address);
442             p_usbh_obj->constant.event_cb((usb_device_handle_t)dev_obj, USBH_EVENT_DEV_GONE, p_usbh_obj->constant.event_cb_arg);
443         }
444         /*
445         Note: We make these action flags mutually exclusive in case they happen in rapid succession. They are handled
446         in the order of precedence
447         For example
448         - New device event is requested followed immediately by a disconnection
449         - Port disable requested followed immediately by a disconnection
450         */
451         if (action_flags & (DEV_FLAG_ACTION_FREE | DEV_FLAG_ACTION_FREE_AND_RECOVER)) {
452             //Cache a copy of the port handle as we are about to free the device object
453             hcd_port_handle_t port_hdl = dev_obj->constant.port_hdl;
454             ESP_LOGD(USBH_TAG, "Freeing device %d", dev_obj->constant.address);
455             if (handle_dev_free(dev_obj)) {
456                 ESP_LOGD(USBH_TAG, "Device all free");
457                 p_usbh_obj->constant.event_cb((usb_device_handle_t)NULL, USBH_EVENT_DEV_ALL_FREE, p_usbh_obj->constant.event_cb_arg);
458             }
459             //Check if we need to recover the device's port
460             if (action_flags & DEV_FLAG_ACTION_FREE_AND_RECOVER) {
461                 p_usbh_obj->constant.hub_req_cb(port_hdl, USBH_HUB_REQ_PORT_RECOVER, p_usbh_obj->constant.hub_req_cb_arg);
462             }
463         } else if (action_flags & DEV_FLAG_ACTION_PORT_DISABLE) {
464             //Request that the HUB disables this device's port
465             ESP_LOGD(USBH_TAG, "Disable device port %d", dev_obj->constant.address);
466             p_usbh_obj->constant.hub_req_cb(dev_obj->constant.port_hdl, USBH_HUB_REQ_PORT_DISABLE, p_usbh_obj->constant.hub_req_cb_arg);
467         } else if (action_flags & DEV_FLAG_ACTION_SEND_NEW) {
468             ESP_LOGD(USBH_TAG, "New device %d", dev_obj->constant.address);
469             p_usbh_obj->constant.event_cb((usb_device_handle_t)dev_obj, USBH_EVENT_DEV_NEW, p_usbh_obj->constant.event_cb_arg);
470         }
471         USBH_ENTER_CRITICAL();
472         /* ---------------------------------------------------------------------
473         Re-enter critical sections. All device action flags should have been handled.
474         --------------------------------------------------------------------- */
475 
476     }
477     USBH_EXIT_CRITICAL();
478     return ESP_OK;
479 }
480 
usbh_num_devs(int * num_devs_ret)481 esp_err_t usbh_num_devs(int *num_devs_ret)
482 {
483     USBH_CHECK(num_devs_ret != NULL, ESP_ERR_INVALID_ARG);
484     xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
485     *num_devs_ret = p_usbh_obj->mux_protected.num_device;
486     xSemaphoreGive(p_usbh_obj->constant.mux_lock);
487     return ESP_OK;
488 }
489 
490 // ------------------------------------------------ Device Functions ---------------------------------------------------
491 
492 // --------------------- Device Pool -----------------------
493 
usbh_dev_addr_list_fill(int list_len,uint8_t * dev_addr_list,int * num_dev_ret)494 esp_err_t usbh_dev_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret)
495 {
496     USBH_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG);
497     USBH_ENTER_CRITICAL();
498     int num_filled = 0;
499     device_t *dev_obj;
500     //Fill list with devices from idle tailq
501     TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) {
502         if (num_filled < list_len) {
503             dev_addr_list[num_filled] = dev_obj->constant.address;
504             num_filled++;
505         } else {
506             break;
507         }
508     }
509     //Fill list with devices from pending tailq
510     TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_pending_tailq, dynamic.tailq_entry) {
511         if (num_filled < list_len) {
512             dev_addr_list[num_filled] = dev_obj->constant.address;
513             num_filled++;
514         } else {
515             break;
516         }
517     }
518     USBH_EXIT_CRITICAL();
519     //Write back number of devices filled
520     *num_dev_ret = num_filled;
521     return ESP_OK;
522 }
523 
usbh_dev_open(uint8_t dev_addr,usb_device_handle_t * dev_hdl)524 esp_err_t usbh_dev_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl)
525 {
526     USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
527     esp_err_t ret;
528 
529     USBH_ENTER_CRITICAL();
530     //Go through the device lists to find the device with the specified address
531     device_t *found_dev_obj = NULL;
532     device_t *dev_obj;
533     TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) {
534         if (dev_obj->constant.address == dev_addr) {
535             found_dev_obj = dev_obj;
536             goto exit;
537         }
538     }
539     TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) {
540         if (dev_obj->constant.address == dev_addr) {
541             found_dev_obj = dev_obj;
542             goto exit;
543         }
544     }
545 exit:
546     if (found_dev_obj != NULL) {
547         //The device is not in a state to be referenced
548         if (dev_obj->dynamic.flags.is_gone || dev_obj->dynamic.flags.waiting_port_disable || dev_obj->dynamic.flags.waiting_free) {
549             ret = ESP_ERR_INVALID_STATE;
550         } else {
551             dev_obj->dynamic.ref_count++;
552             *dev_hdl = (usb_device_handle_t)found_dev_obj;
553             ret = ESP_OK;
554         }
555     } else {
556         ret = ESP_ERR_NOT_FOUND;
557     }
558     USBH_EXIT_CRITICAL();
559 
560     return ret;
561 }
562 
usbh_dev_close(usb_device_handle_t dev_hdl)563 esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl)
564 {
565     USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
566     device_t *dev_obj = (device_t *)dev_hdl;
567 
568     USBH_ENTER_CRITICAL();
569     dev_obj->dynamic.ref_count--;
570     bool call_notif_cb = false;
571     if (dev_obj->dynamic.ref_count == 0) {
572         //Sanity check.
573         assert(dev_obj->dynamic.num_ctrl_xfers_inflight == 0);  //There cannot be any control transfer inflight
574         assert(!dev_obj->dynamic.flags.waiting_free);   //This can only be set when ref count reaches 0
575         if (dev_obj->dynamic.flags.is_gone) {
576             //Device is already gone so it's port is already disabled. Trigger the USBH process to free the device
577             dev_obj->dynamic.flags.waiting_free = 1;
578             call_notif_cb = _dev_set_actions(dev_obj, DEV_FLAG_ACTION_FREE_AND_RECOVER); //Port error occurred so we need to recover it
579         } else if (dev_obj->dynamic.flags.waiting_close) {
580             //Device is still connected but is no longer needed. Trigger the USBH process to request device's port be disabled
581             dev_obj->dynamic.flags.waiting_port_disable = 1;
582             call_notif_cb = _dev_set_actions(dev_obj, DEV_FLAG_ACTION_PORT_DISABLE);
583         }
584         //Else, there's nothing to do. Leave the device allocated
585     }
586     USBH_EXIT_CRITICAL();
587 
588     if (call_notif_cb) {
589         p_usbh_obj->constant.notif_cb(USB_NOTIF_SOURCE_USBH, false, p_usbh_obj->constant.notif_cb_arg);
590     }
591     return ESP_OK;
592 }
593 
usbh_dev_mark_all_free(void)594 esp_err_t usbh_dev_mark_all_free(void)
595 {
596     USBH_ENTER_CRITICAL();
597     /*
598     Go through the device list and mark each device as waiting to be closed. If the device is not opened at all, we can
599     disable it immediately.
600     Note: We manually traverse the list because we need to add/remove items while traversing
601     */
602     bool call_notif_cb = false;
603     bool wait_for_free = false;
604     for (int i = 0; i < 2; i++) {
605         device_t *dev_obj_cur;
606         device_t *dev_obj_next;
607         //Go through pending list first as it's more efficient
608         if (i == 0) {
609             dev_obj_cur = TAILQ_FIRST(&p_usbh_obj->dynamic.devs_pending_tailq);
610         } else {
611             dev_obj_cur = TAILQ_FIRST(&p_usbh_obj->dynamic.devs_idle_tailq);
612         }
613         while (dev_obj_cur != NULL) {
614             assert(!dev_obj_cur->dynamic.flags.waiting_close);  //Sanity check
615             //Keep a copy of the next item first in case we remove the current item
616             dev_obj_next = TAILQ_NEXT(dev_obj_cur, dynamic.tailq_entry);
617             if (dev_obj_cur->dynamic.ref_count == 0 && !dev_obj_cur->dynamic.flags.is_gone) {
618                 //Device is not opened as is not gone, so we can disable it now
619                 dev_obj_cur->dynamic.flags.waiting_port_disable = 1;
620                 call_notif_cb |= _dev_set_actions(dev_obj_cur, DEV_FLAG_ACTION_PORT_DISABLE);
621             } else {
622                 //Device is still opened. Just mark it as waiting to be closed
623                 dev_obj_cur->dynamic.flags.waiting_close = 1;
624             }
625             wait_for_free = true;   //As long as there is still a device, we need to wait for an event indicating it is freed
626             dev_obj_cur = dev_obj_next;
627         }
628     }
629     USBH_EXIT_CRITICAL();
630 
631     if (call_notif_cb) {
632         p_usbh_obj->constant.notif_cb(USB_NOTIF_SOURCE_USBH, false, p_usbh_obj->constant.notif_cb_arg);
633     }
634     return (wait_for_free) ? ESP_ERR_NOT_FINISHED : ESP_OK;
635 }
636 
637 // ------------------- Single Device  ----------------------
638 
usbh_dev_get_addr(usb_device_handle_t dev_hdl,uint8_t * dev_addr)639 esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr)
640 {
641     USBH_CHECK(dev_hdl != NULL && dev_addr != NULL, ESP_ERR_INVALID_ARG);
642     device_t *dev_obj = (device_t *)dev_hdl;
643 
644     USBH_ENTER_CRITICAL();
645     USBH_CHECK_FROM_CRIT(dev_obj->constant.address > 0, ESP_ERR_INVALID_STATE);
646     *dev_addr = dev_obj->constant.address;
647     USBH_EXIT_CRITICAL();
648 
649     return ESP_OK;
650 }
651 
usbh_dev_get_info(usb_device_handle_t dev_hdl,usb_device_info_t * dev_info)652 esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_info)
653 {
654     USBH_CHECK(dev_hdl != NULL && dev_info != NULL, ESP_ERR_INVALID_ARG);
655     device_t *dev_obj = (device_t *)dev_hdl;
656 
657     esp_err_t ret;
658     //Device must be configured, or not attached (if it suddenly disconnected)
659     USBH_ENTER_CRITICAL();
660     if (!(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED || dev_obj->dynamic.state == USB_DEVICE_STATE_NOT_ATTACHED)) {
661         USBH_EXIT_CRITICAL();
662         ret = ESP_ERR_INVALID_STATE;
663         goto exit;
664     }
665     //Critical section for the dynamic members
666     dev_info->speed = dev_obj->constant.speed;
667     dev_info->dev_addr = dev_obj->constant.address;
668     dev_info->bMaxPacketSize0 = dev_obj->constant.desc->bMaxPacketSize0;
669     USBH_EXIT_CRITICAL();
670     assert(dev_obj->constant.config_desc);
671     dev_info->bConfigurationValue = dev_obj->constant.config_desc->bConfigurationValue;
672     //String descriptors are allowed to be NULL as not all devices support them
673     dev_info->str_desc_manufacturer = dev_obj->constant.str_desc_manu;
674     dev_info->str_desc_product = dev_obj->constant.str_desc_product;
675     dev_info->str_desc_serial_num = dev_obj->constant.str_desc_ser_num;
676     ret = ESP_OK;
677 exit:
678     return ret;
679 }
680 
usbh_dev_get_desc(usb_device_handle_t dev_hdl,const usb_device_desc_t ** dev_desc_ret)681 esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t **dev_desc_ret)
682 {
683     USBH_CHECK(dev_hdl != NULL && dev_desc_ret != NULL, ESP_ERR_INVALID_ARG);
684     device_t *dev_obj = (device_t *)dev_hdl;
685 
686     USBH_ENTER_CRITICAL();
687     USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
688     USBH_EXIT_CRITICAL();
689 
690     *dev_desc_ret = dev_obj->constant.desc;
691     return ESP_OK;
692 }
693 
usbh_dev_get_config_desc(usb_device_handle_t dev_hdl,const usb_config_desc_t ** config_desc_ret)694 esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc_ret)
695 {
696     USBH_CHECK(dev_hdl != NULL && config_desc_ret != NULL, ESP_ERR_INVALID_ARG);
697     device_t *dev_obj = (device_t *)dev_hdl;
698 
699     esp_err_t ret;
700     //Device must be in the configured state
701     USBH_ENTER_CRITICAL();
702     if (dev_obj->dynamic.state != USB_DEVICE_STATE_CONFIGURED) {
703         USBH_EXIT_CRITICAL();
704         ret = ESP_ERR_INVALID_STATE;
705         goto exit;
706     }
707     USBH_EXIT_CRITICAL();
708     assert(dev_obj->constant.config_desc);
709     *config_desc_ret = dev_obj->constant.config_desc;
710     ret = ESP_OK;
711 exit:
712     return ret;
713 }
714 
usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl,urb_t * urb)715 esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb)
716 {
717     USBH_CHECK(dev_hdl != NULL && urb != NULL, ESP_ERR_INVALID_ARG);
718     device_t *dev_obj = (device_t *)dev_hdl;
719 
720     USBH_ENTER_CRITICAL();
721     USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
722     //Increment the control transfer count first
723     dev_obj->dynamic.num_ctrl_xfers_inflight++;
724     USBH_EXIT_CRITICAL();
725 
726     esp_err_t ret;
727     if (hcd_pipe_get_state(dev_obj->constant.default_pipe) != HCD_PIPE_STATE_ACTIVE) {
728         ret = ESP_ERR_INVALID_STATE;
729         goto hcd_err;
730     }
731     ret = hcd_urb_enqueue(dev_obj->constant.default_pipe, urb);
732     if (ret != ESP_OK) {
733         goto hcd_err;
734     }
735     ret = ESP_OK;
736     return ret;
737 
738 hcd_err:
739     USBH_ENTER_CRITICAL();
740     dev_obj->dynamic.num_ctrl_xfers_inflight--;
741     USBH_EXIT_CRITICAL();
742     return ret;
743 }
744 
745 // ----------------------------------------------- Interface Functions -------------------------------------------------
746 
usbh_ep_alloc(usb_device_handle_t dev_hdl,usbh_ep_config_t * ep_config,hcd_pipe_handle_t * pipe_hdl_ret)747 esp_err_t usbh_ep_alloc(usb_device_handle_t dev_hdl, usbh_ep_config_t *ep_config, hcd_pipe_handle_t *pipe_hdl_ret)
748 {
749     USBH_CHECK(dev_hdl != NULL && ep_config != NULL && pipe_hdl_ret != NULL, ESP_ERR_INVALID_ARG);
750     device_t *dev_obj = (device_t *)dev_hdl;
751     esp_err_t ret;
752 
753     //Allocate HCD pipe
754     hcd_pipe_config_t pipe_config = {
755         .callback = ep_config->pipe_cb,
756         .callback_arg = ep_config->pipe_cb_arg,
757         .context = ep_config->context,
758         .ep_desc = ep_config->ep_desc,
759         .dev_speed = dev_obj->constant.speed,
760         .dev_addr = dev_obj->constant.address,
761     };
762     hcd_pipe_handle_t pipe_hdl;
763     ret = hcd_pipe_alloc(dev_obj->constant.port_hdl, &pipe_config, &pipe_hdl);
764     if (ret != ESP_OK) {
765         goto pipe_alloc_err;
766     }
767 
768     bool is_in = ep_config->ep_desc->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK;
769     uint8_t addr = ep_config->ep_desc->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK;
770     bool assigned = false;
771 
772     //We need to take the mux_lock to access mux_protected members
773     xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
774     USBH_ENTER_CRITICAL();
775     //Check the device's state before we assign the pipes to the endpoint
776     if (dev_obj->dynamic.state != USB_DEVICE_STATE_CONFIGURED) {
777         USBH_EXIT_CRITICAL();
778         ret = ESP_ERR_INVALID_STATE;
779         goto assign_err;
780     }
781     USBH_EXIT_CRITICAL();
782     //Assign the allocated pipe to the correct endpoint
783     if (is_in && dev_obj->mux_protected.ep_in[addr - 1] == NULL) {  //Is an IN EP
784         dev_obj->mux_protected.ep_in[addr - 1] = pipe_hdl;
785         assigned = true;
786     } else if (dev_obj->mux_protected.ep_out[addr - 1] == NULL) {   //Is an OUT EP
787         dev_obj->mux_protected.ep_out[addr - 1] = pipe_hdl;
788         assigned = true;
789     }
790     xSemaphoreGive(p_usbh_obj->constant.mux_lock);
791 
792     if (!assigned) {
793         ret = ESP_ERR_INVALID_STATE;
794         goto assign_err;
795     }
796     //Write back output
797     *pipe_hdl_ret = pipe_hdl;
798     ret = ESP_OK;
799     return ret;
800 
801 assign_err:
802     ESP_ERROR_CHECK(hcd_pipe_free(pipe_hdl));
803 pipe_alloc_err:
804     return ret;
805 }
806 
usbh_ep_free(usb_device_handle_t dev_hdl,uint8_t bEndpointAddress)807 esp_err_t usbh_ep_free(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress)
808 {
809     USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
810     device_t *dev_obj = (device_t *)dev_hdl;
811 
812     esp_err_t ret;
813     bool is_in = bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK;
814     uint8_t addr = bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK;
815     hcd_pipe_handle_t pipe_hdl;
816 
817     //We need to take the mux_lock to access mux_protected members
818     xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
819     //Check if the EP was previously allocated. If so, set it to NULL
820     if (is_in) {
821         if (dev_obj->mux_protected.ep_in[addr - 1] != NULL) {
822             pipe_hdl = dev_obj->mux_protected.ep_in[addr - 1];
823             dev_obj->mux_protected.ep_in[addr - 1] = NULL;
824             ret = ESP_OK;
825         } else {
826             ret = ESP_ERR_INVALID_STATE;
827         }
828     } else {
829         //EP must have been previously allocated
830         if (dev_obj->mux_protected.ep_out[addr - 1] != NULL) {
831             pipe_hdl = dev_obj->mux_protected.ep_out[addr - 1];
832             dev_obj->mux_protected.ep_out[addr - 1] = NULL;
833             ret = ESP_OK;
834         } else {
835             ret = ESP_ERR_INVALID_STATE;
836         }
837     }
838     xSemaphoreGive(p_usbh_obj->constant.mux_lock);
839 
840     if (ret == ESP_OK) {
841         ESP_ERROR_CHECK(hcd_pipe_free(pipe_hdl));
842     }
843     return ret;
844 }
845 
usbh_ep_get_context(usb_device_handle_t dev_hdl,uint8_t bEndpointAddress,void ** context_ret)846 esp_err_t usbh_ep_get_context(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress, void **context_ret)
847 {
848     bool is_in = bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK;
849     uint8_t addr = bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK;
850     USBH_CHECK(dev_hdl != NULL &&
851                addr >= EP_NUM_MIN &&    //Control endpoints are owned by the USBH
852                addr <= EP_NUM_MAX &&
853                context_ret != NULL,
854                ESP_ERR_INVALID_ARG);
855 
856     esp_err_t ret;
857     device_t *dev_obj = (device_t *)dev_hdl;
858     hcd_pipe_handle_t pipe_hdl;
859 
860     //We need to take the mux_lock to access mux_protected members
861     xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
862     //Get the endpoint's corresponding pipe
863     if (is_in) {
864         pipe_hdl = dev_obj->mux_protected.ep_in[addr - 1];
865     } else {
866         pipe_hdl = dev_obj->mux_protected.ep_out[addr - 1];
867     }
868     //Check if the EP was allocated to begin with
869     if (pipe_hdl == NULL) {
870         ret = ESP_ERR_NOT_FOUND;
871         goto exit;
872     }
873     //Return the context of the pipe
874     void *context = hcd_pipe_get_context(pipe_hdl);
875     *context_ret = context;
876     ret = ESP_OK;
877 exit:
878     xSemaphoreGive(p_usbh_obj->constant.mux_lock);
879     return ret;
880 }
881 
882 // -------------------------------------------------- Hub Functions ----------------------------------------------------
883 
884 // ------------------- Device Related ----------------------
885 
usbh_hub_is_installed(usbh_hub_req_cb_t hub_req_callback,void * callback_arg)886 esp_err_t usbh_hub_is_installed(usbh_hub_req_cb_t hub_req_callback, void *callback_arg)
887 {
888     USBH_CHECK(hub_req_callback != NULL, ESP_ERR_INVALID_ARG);
889 
890     USBH_ENTER_CRITICAL();
891     //Check that USBH is already installed
892     USBH_CHECK_FROM_CRIT(p_usbh_obj != NULL, ESP_ERR_INVALID_STATE);
893     //Check that Hub has not be installed yet
894     USBH_CHECK_FROM_CRIT(p_usbh_obj->constant.hub_req_cb == NULL, ESP_ERR_INVALID_STATE);
895     p_usbh_obj->constant.hub_req_cb = hub_req_callback;
896     p_usbh_obj->constant.hub_req_cb_arg = callback_arg;
897     USBH_EXIT_CRITICAL();
898 
899     return ESP_OK;
900 }
901 
usbh_hub_add_dev(hcd_port_handle_t port_hdl,usb_speed_t dev_speed,usb_device_handle_t * new_dev_hdl,hcd_pipe_handle_t * default_pipe_hdl)902 esp_err_t usbh_hub_add_dev(hcd_port_handle_t port_hdl, usb_speed_t dev_speed, usb_device_handle_t *new_dev_hdl, hcd_pipe_handle_t *default_pipe_hdl)
903 {
904     //Note: Parent device handle can be NULL if it's connected to the root hub
905     USBH_CHECK(new_dev_hdl != NULL, ESP_ERR_INVALID_ARG);
906     esp_err_t ret;
907     device_t *dev_obj;
908     ret = device_alloc(port_hdl, dev_speed, &dev_obj);
909     if (ret != ESP_OK) {
910         return ret;
911     }
912     //Write-back device handle
913     *new_dev_hdl = (usb_device_handle_t)dev_obj;
914     *default_pipe_hdl = dev_obj->constant.default_pipe;
915     ret = ESP_OK;
916     return ret;
917 }
918 
usbh_hub_pass_event(usb_device_handle_t dev_hdl,usbh_hub_event_t hub_event)919 esp_err_t usbh_hub_pass_event(usb_device_handle_t dev_hdl, usbh_hub_event_t hub_event)
920 {
921     USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
922     device_t *dev_obj = (device_t *)dev_hdl;
923 
924     bool call_notif_cb;
925     switch (hub_event) {
926         case USBH_HUB_EVENT_PORT_ERROR: {
927             USBH_ENTER_CRITICAL();
928             dev_obj->dynamic.flags.is_gone = 1;
929             //Check if the device can be freed now
930             if (dev_obj->dynamic.ref_count == 0) {
931                 dev_obj->dynamic.flags.waiting_free = 1;
932                 //Device is already waiting free so none of it's pipes will be in use. Can free immediately.
933                 call_notif_cb = _dev_set_actions(dev_obj, DEV_FLAG_ACTION_FREE_AND_RECOVER); //Port error occurred so we need to recover it
934             } else {
935                 call_notif_cb = _dev_set_actions(dev_obj, DEV_FLAG_ACTION_PIPE_HALT_AND_FLUSH |
936                                                           DEV_FLAG_ACTION_DEFAULT_PIPE_FLUSH |
937                                                           DEV_FLAG_ACTION_DEFAULT_PIPE_DEQUEUE |
938                                                           DEV_FLAG_ACTION_SEND_GONE_EVENT);
939             }
940             USBH_EXIT_CRITICAL();
941             break;
942         }
943         case USBH_HUB_EVENT_PORT_DISABLED: {
944             USBH_ENTER_CRITICAL();
945             assert(dev_obj->dynamic.ref_count == 0);    //At this stage, the device should have been closed by all users
946             dev_obj->dynamic.flags.waiting_free = 1;
947             call_notif_cb = _dev_set_actions(dev_obj, DEV_FLAG_ACTION_FREE);
948             USBH_EXIT_CRITICAL();
949             break;
950         }
951         default:
952             return ESP_ERR_INVALID_ARG;
953     }
954 
955     if (call_notif_cb) {
956         p_usbh_obj->constant.notif_cb(USB_NOTIF_SOURCE_USBH, false, p_usbh_obj->constant.notif_cb_arg);
957     }
958     return ESP_OK;
959 }
960 
961 // ----------------- Enumeration Related -------------------
962 
usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl,uint8_t dev_addr)963 esp_err_t usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr)
964 {
965     USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
966     device_t *dev_obj = (device_t *)dev_hdl;
967 
968     USBH_ENTER_CRITICAL();
969     dev_obj->dynamic.state = USB_DEVICE_STATE_ADDRESS;
970     USBH_EXIT_CRITICAL();
971 
972     //We can modify the info members outside the critical section
973     dev_obj->constant.address = dev_addr;
974     return ESP_OK;
975 }
976 
usbh_hub_enum_fill_dev_desc(usb_device_handle_t dev_hdl,const usb_device_desc_t * device_desc)977 esp_err_t usbh_hub_enum_fill_dev_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t *device_desc)
978 {
979     USBH_CHECK(dev_hdl != NULL && device_desc != NULL, ESP_ERR_INVALID_ARG);
980     device_t *dev_obj = (device_t *)dev_hdl;
981     //We can modify the info members outside the critical section
982     memcpy((usb_device_desc_t *)dev_obj->constant.desc, device_desc, sizeof(usb_device_desc_t));
983     return ESP_OK;
984 }
985 
usbh_hub_enum_fill_config_desc(usb_device_handle_t dev_hdl,const usb_config_desc_t * config_desc_full)986 esp_err_t usbh_hub_enum_fill_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t *config_desc_full)
987 {
988     USBH_CHECK(dev_hdl != NULL && config_desc_full != NULL, ESP_ERR_INVALID_ARG);
989     device_t *dev_obj = (device_t *)dev_hdl;
990     //Allocate memory to store the configuration descriptor
991     usb_config_desc_t *config_desc = heap_caps_malloc(config_desc_full->wTotalLength, MALLOC_CAP_DEFAULT);  //Buffer to copy over full configuration descriptor (wTotalLength)
992     if (config_desc == NULL) {
993         return ESP_ERR_NO_MEM;
994     }
995     //Copy the configuration descriptor
996     memcpy(config_desc, config_desc_full, config_desc_full->wTotalLength);
997     //Assign the config desc to the device object
998     assert(dev_obj->constant.config_desc == NULL);
999     dev_obj->constant.config_desc = config_desc;
1000     return ESP_OK;
1001 }
1002 
usbh_hub_enum_fill_str_desc(usb_device_handle_t dev_hdl,const usb_str_desc_t * str_desc,int select)1003 esp_err_t usbh_hub_enum_fill_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select)
1004 {
1005     USBH_CHECK(dev_hdl != NULL && str_desc != NULL && (select >= 0 && select < 3), ESP_ERR_INVALID_ARG);
1006     device_t *dev_obj = (device_t *)dev_hdl;
1007     //Allocate memory to store the manufacturer string descriptor
1008     usb_str_desc_t *str_desc_fill = heap_caps_malloc(str_desc->bLength, MALLOC_CAP_DEFAULT);
1009     if (str_desc_fill == NULL) {
1010         return ESP_ERR_NO_MEM;
1011     }
1012     //Copy the string descriptor
1013     memcpy(str_desc_fill, str_desc, str_desc->bLength);
1014     //Assign filled string descriptor to the device object
1015     switch (select) {
1016         case 0:
1017             assert(dev_obj->constant.str_desc_manu == NULL);
1018             dev_obj->constant.str_desc_manu = str_desc_fill;
1019             break;
1020         case 1:
1021             assert(dev_obj->constant.str_desc_product == NULL);
1022             dev_obj->constant.str_desc_product = str_desc_fill;
1023             break;
1024         default:    //2
1025             assert(dev_obj->constant.str_desc_ser_num == NULL);
1026             dev_obj->constant.str_desc_ser_num = str_desc_fill;
1027             break;
1028     }
1029     return ESP_OK;
1030 }
1031 
usbh_hub_enum_done(usb_device_handle_t dev_hdl)1032 esp_err_t usbh_hub_enum_done(usb_device_handle_t dev_hdl)
1033 {
1034     USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
1035     device_t *dev_obj = (device_t *)dev_hdl;
1036 
1037     //We need to take the mux_lock to access mux_protected members
1038     xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
1039     USBH_ENTER_CRITICAL();
1040     dev_obj->dynamic.state = USB_DEVICE_STATE_CONFIGURED;
1041     //Add the device to list of devices, then trigger a device event
1042     TAILQ_INSERT_TAIL(&p_usbh_obj->dynamic.devs_idle_tailq, dev_obj, dynamic.tailq_entry);   //Add it to the idle device list first
1043     bool call_notif_cb = _dev_set_actions(dev_obj, DEV_FLAG_ACTION_SEND_NEW);
1044     USBH_EXIT_CRITICAL();
1045     p_usbh_obj->mux_protected.num_device++;
1046     xSemaphoreGive(p_usbh_obj->constant.mux_lock);
1047 
1048     //Update the default pipe callback
1049     ESP_ERROR_CHECK(hcd_pipe_update_callback(dev_obj->constant.default_pipe, default_pipe_callback, (void *)dev_obj));
1050     //Call the notification callback
1051     if (call_notif_cb) {
1052         p_usbh_obj->constant.notif_cb(USB_NOTIF_SOURCE_USBH, false, p_usbh_obj->constant.notif_cb_arg);
1053     }
1054     return ESP_OK;
1055 }
1056 
usbh_hub_enum_failed(usb_device_handle_t dev_hdl)1057 esp_err_t usbh_hub_enum_failed(usb_device_handle_t dev_hdl)
1058 {
1059     USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
1060     device_t *dev_obj = (device_t *)dev_hdl;
1061     device_free(dev_obj);
1062     return ESP_OK;
1063 }
1064