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