1 /*
2  * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "esp_log.h"
8 #include <stdio.h>
9 #include <string.h>
10 #include <sys/queue.h>
11 #include "usb/usb_host.h"
12 #include "usb/cdc_acm_host.h"
13 #include "freertos/FreeRTOS.h"
14 #include "freertos/task.h"
15 #include "freertos/semphr.h"
16 #include "freertos/event_groups.h"
17 #include "esp_check.h"
18 #include "esp_system.h"
19 
20 #define TAG "cdc_acm"
21 
22 // CDC devices often implement Interface Association Descriptor (IAD). Parse IAD only when
23 // bDeviceClass = 0xEF (Miscellaneous Device Class), bDeviceSubClass = 0x02 (Common Class), bDeviceProtocol = 0x01 (Interface Association Descriptor)
24 // @see USB Interface Association Descriptor: Device Class Code and Use Model rev 1.0, Table 1-1
25 #define USB_SUBCLASS_COMMON        0x02
26 #define USB_DEVICE_PROTOCOL_IAD    0x01
27 
28 // CDC-ACM spinlock
29 static portMUX_TYPE cdc_acm_lock = portMUX_INITIALIZER_UNLOCKED;
30 #define CDC_ACM_ENTER_CRITICAL()   portENTER_CRITICAL(&cdc_acm_lock)
31 #define CDC_ACM_EXIT_CRITICAL()    portEXIT_CRITICAL(&cdc_acm_lock)
32 
33 // CDC-ACM events
34 #define CDC_ACM_TEARDOWN          BIT0
35 #define CDC_ACM_TEARDOWN_COMPLETE BIT1
36 
37 // CDC-ACM check macros
38 #define CDC_ACM_CHECK(cond, ret_val) ({                                     \
39             if (!(cond)) {                                                  \
40                 return (ret_val);                                           \
41             }                                                               \
42 })
43 
44 #define CDC_ACM_CHECK_FROM_CRIT(cond, ret_val) ({                           \
45             if (!(cond)) {                                                  \
46                 CDC_ACM_EXIT_CRITICAL();                                    \
47                 return ret_val;                                             \
48             }                                                               \
49 })
50 
51 // CDC-ACM driver object
52 typedef struct {
53     usb_host_client_handle_t cdc_acm_client_hdl;        /*!< USB Host handle reused for all CDC-ACM devices in the system */
54     SemaphoreHandle_t open_close_mutex;
55     EventGroupHandle_t event_group;
56     SLIST_HEAD(list_dev, cdc_dev_s) cdc_devices_list;   /*!< List of open pseudo devices */
57 } cdc_acm_obj_t;
58 
59 static cdc_acm_obj_t *p_cdc_acm_obj = NULL;
60 
61 /**
62  * @brief Default CDC-ACM driver configuration
63  *
64  * This configuration is used when user passes NULL to config pointer during device open.
65  */
66 static const cdc_acm_host_driver_config_t cdc_acm_driver_config_default = {
67     .driver_task_stack_size = 4096,
68     .driver_task_priority = 10,
69     .xCoreID = 0
70 };
71 
72 /**
73  * @brief USB CDC PSTN Call Descriptor
74  *
75  * @see Table 3, USB CDC-PSTN specification rev. 1.2
76  */
77 typedef struct {
78     uint8_t bFunctionLength;
79     const uint8_t bDescriptorType;
80     const cdc_desc_subtype_t bDescriptorSubtype;
81     union {
82         struct {
83             uint8_t call_management:   1; // Device handles call management itself
84             uint8_t call_over_data_if: 1; // Device sends/receives call management information over Data Class interface
85             uint8_t reserved: 6;
86         };
87         uint8_t val;
88     } bmCapabilities;
89     uint8_t bDataInterface; // Interface number of Data Class interface optionally used for call management
90 } __attribute__((packed)) cdc_acm_call_desc_t;
91 
92 /**
93  * @brief USB CDC PSTN Abstract Control Model Descriptor
94  *
95  * @see Table 4, USB CDC-PSTN specification rev. 1.2
96  */
97 typedef struct {
98     uint8_t bFunctionLength;
99     const uint8_t bDescriptorType;
100     const cdc_desc_subtype_t bDescriptorSubtype;
101     union {
102         struct {
103             uint8_t feature:    1; // Device supports Set/Clear/Get_Comm_Feature requests
104             uint8_t serial:     1; // Device supports Set/Get_Line_Coding, Set_Control_Line_State and Serial_State request and notifications
105             uint8_t send_break: 1; // Device supports Send_Break request
106             uint8_t network:    1; // Device supports Network_Connection notification
107             uint8_t reserved:   4;
108         };
109         uint8_t val;
110     } bmCapabilities;
111 } __attribute__((packed)) cdc_acm_acm_desc_t;
112 
113 typedef struct cdc_dev_s cdc_dev_t;
114 struct cdc_dev_s{
115     usb_device_handle_t dev_hdl;          // USB device handle
116     void *cb_arg;                         // Common argument for user's callbacks (data IN and Notification)
117     struct {
118         usb_transfer_t *out_xfer;         // OUT data transfer
119         usb_transfer_t *in_xfer;          // IN data transfer
120         cdc_acm_data_callback_t in_cb;    // User's callback for async (non-blocking) data IN
121         const usb_intf_desc_t *intf_desc; // Pointer to data interface descriptor
122         SemaphoreHandle_t out_mux;        // OUT mutex
123     } data;
124 
125     struct {
126         usb_transfer_t *xfer;             // IN notification transfer
127         const usb_intf_desc_t *intf_desc; // Pointer to notification interface descriptor, can be NULL if there is no notification channel in the device
128         cdc_acm_host_dev_callback_t cb;   // User's callback for device events
129     } notif;                              // Structure with Notif pipe data
130 
131     usb_transfer_t *ctrl_transfer;        // CTRL (endpoint 0) transfer
132     SemaphoreHandle_t ctrl_mux;           // CTRL mutex
133     cdc_acm_uart_state_t serial_state;    // Serial State
134     cdc_comm_protocol_t comm_protocol;
135     cdc_data_protocol_t data_protocol;
136     int             num_cdc_intf_desc;    // Number of CDC Interface descriptors in following array
137     const usb_standard_desc_t **cdc_intf_desc;   // CDC Interface descriptors
138     SLIST_ENTRY(cdc_dev_s) list_entry;
139 };
140 
141 /**
142  * @brief Notification received callback
143  *
144  * Notification (interrupt) IN transfer is submitted at the end of this function to ensure periodic poll of IN endpoint.
145  *
146  * @param[in] transfer Transfer that triggered the callback
147  */
148 static void notif_xfer_cb(usb_transfer_t *transfer);
149 
150 /**
151  * @brief Data received callback
152  *
153  * Data (bulk) IN transfer is submitted at the end of this function to ensure continuous poll of IN endpoint.
154  *
155  * @param[in] transfer Transfer that triggered the callback
156  */
157 static void in_xfer_cb(usb_transfer_t *transfer);
158 
159 /**
160  * @brief Data send callback
161  *
162  * Reused for bulk OUT and CTRL transfers
163  *
164  * @param[in] transfer Transfer that triggered the callback
165  */
166 static void out_xfer_cb(usb_transfer_t *transfer);
167 
168 /**
169  * @brief USB Host Client event callback
170  *
171  * Handling of USB device connection/disconnection to/from root HUB.
172  *
173  * @param[in] event_msg Event message type
174  * @param[in] arg Caller's argument (not used in this driver)
175  */
176 static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg);
177 
178 /**
179  * @brief Send CDC specific request
180  *
181  * Helper function that will send CDC specific request to default endpoint.
182  * Both IN and OUT requests are sent through this API, depending on the in_transfer parameter.
183  *
184  * @see  Chapter 6.2, USB CDC specification rev. 1.2
185  * @note CDC specific requests are only supported by devices that have dedicated management element.
186  *
187  * @param[in] cdc_dev Pointer to CDC device
188  * @param[in] in_transfer Direction of data phase. true: IN, false: OUT
189  * @param[in] request CDC request code
190  * @param[inout] data Pointer to data buffer. Input for OUT transfers, output for IN transfers.
191  * @param[in] data_len Length of data buffer
192  * @param[in] value Value to be set in bValue of Setup packet
193  * @return esp_err_t
194  */
195 static esp_err_t send_cdc_request(cdc_dev_t *cdc_dev, bool in_transfer, cdc_request_code_t request, uint8_t *data, uint16_t data_len, uint16_t value);
196 
197 /**
198  * @brief CDC-ACM driver handling task
199  *
200  * USB host client registration and deregistration is handled here.
201  *
202  * @param[in] arg User's argument. Handle of a task that started this task.
203  */
cdc_acm_client_task(void * arg)204 static void cdc_acm_client_task(void *arg)
205 {
206     vTaskSuspend(NULL); // Task will be resumed from cdc_acm_host_install()
207     cdc_acm_obj_t *cdc_acm_obj = p_cdc_acm_obj; // Make local copy of the driver's handle
208     assert(cdc_acm_obj->cdc_acm_client_hdl);
209 
210     // Start handling client's events
211     while (1) {
212         usb_host_client_handle_events(cdc_acm_obj->cdc_acm_client_hdl, portMAX_DELAY);
213         EventBits_t events = xEventGroupGetBits(cdc_acm_obj->event_group);
214         if (events & CDC_ACM_TEARDOWN) {
215             break;
216         }
217     }
218 
219     ESP_LOGD(TAG, "Deregistering client");
220     ESP_ERROR_CHECK(usb_host_client_deregister(cdc_acm_obj->cdc_acm_client_hdl));
221     xEventGroupSetBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN_COMPLETE);
222     vTaskDelete(NULL);
223 }
224 
225 /**
226  * @brief Cancel transfer and reset endpoint
227  *
228  * This function will cancel ongoing transfer a reset its endpoint to ready state.
229  *
230  * @param[in] dev_hdl USB device handle
231  * @param[in] transfer Transfer to be cancelled
232  * @return esp_err_t
233  */
cdc_acm_reset_transfer_endpoint(usb_device_handle_t dev_hdl,usb_transfer_t * transfer)234 static esp_err_t cdc_acm_reset_transfer_endpoint(usb_device_handle_t dev_hdl, usb_transfer_t *transfer)
235 {
236     assert(dev_hdl);
237     assert(transfer);
238 
239     ESP_RETURN_ON_ERROR(usb_host_endpoint_halt(dev_hdl, transfer->bEndpointAddress), TAG,);
240     ESP_RETURN_ON_ERROR(usb_host_endpoint_flush(dev_hdl, transfer->bEndpointAddress), TAG,);
241     usb_host_endpoint_clear(dev_hdl, transfer->bEndpointAddress);
242     return ESP_OK;
243 }
244 
245 /**
246  * @brief Start CDC device
247  *
248  * After this call, USB host peripheral will continuously poll IN endpoints.
249  *
250  * @param cdc_dev
251  * @param[in] event_cb  Device event callback
252  * @param[in] in_cb     Data received callback
253  * @param[in] user_arg  Optional user's argument, that will be passed to the callbacks
254  * @return esp_err_t
255  */
cdc_acm_start(cdc_dev_t * cdc_dev,cdc_acm_host_dev_callback_t event_cb,cdc_acm_data_callback_t in_cb,void * user_arg)256 static esp_err_t cdc_acm_start(cdc_dev_t *cdc_dev, cdc_acm_host_dev_callback_t event_cb, cdc_acm_data_callback_t in_cb, void *user_arg)
257 {
258     esp_err_t ret = ESP_OK;
259     assert(cdc_dev);
260 
261     CDC_ACM_ENTER_CRITICAL();
262     cdc_dev->notif.cb = event_cb;
263     cdc_dev->data.in_cb = in_cb;
264     cdc_dev->cb_arg = user_arg;
265     CDC_ACM_EXIT_CRITICAL();
266 
267     // Claim data interface and start polling its IN endpoint
268     ESP_GOTO_ON_ERROR(usb_host_interface_claim(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber, 0), err, TAG,);
269     ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer");
270     ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->data.in_xfer));
271 
272     // If notification are supported, claim its interface and start polling its IN endpoint
273     if (cdc_dev->notif.intf_desc != NULL) {
274         if (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc) {
275             ESP_GOTO_ON_ERROR(usb_host_interface_claim(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl,
276                                                      cdc_dev->notif.intf_desc->bInterfaceNumber, 0), err, TAG,);
277         }
278         ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer");
279         ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->notif.xfer));
280     }
281 
282     // Everything OK, add the device into list and return
283     CDC_ACM_ENTER_CRITICAL();
284     SLIST_INSERT_HEAD(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, list_entry);
285     CDC_ACM_EXIT_CRITICAL();
286     return ret;
287 
288 err:
289     usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber);
290     if (cdc_dev->notif.intf_desc != NULL) {
291         usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber);
292     }
293     return ret;
294 }
295 
296 static void cdc_acm_transfers_free(cdc_dev_t *cdc_dev);
297 /**
298  * @brief Helper function that releases resources claimed by CDC device
299  *
300  * Close underlying USB device, free device driver memory
301  *
302  * @note All interfaces claimed by this device must be release before calling this function
303  * @param cdc_dev CDC device handle to be removed
304  */
cdc_acm_device_remove(cdc_dev_t * cdc_dev)305 static void cdc_acm_device_remove(cdc_dev_t *cdc_dev)
306 {
307     assert(cdc_dev);
308     cdc_acm_transfers_free(cdc_dev);
309     free(cdc_dev->cdc_intf_desc);
310     // We don't check the error code of usb_host_device_close, as the close might fail, if someone else is still using the device (not all interfaces are released)
311     usb_host_device_close(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl); // Gracefully continue on error
312     free(cdc_dev);
313 }
314 
315 /**
316  * @brief Open USB device with requested VID/PID
317  *
318  * This function has two regular return paths:
319  * 1. USB device with matching VID/PID is already opened by this driver: allocate new CDC device on top of the already opened USB device.
320  * 2. USB device with matching VID/PID is NOT opened by this driver yet: poll USB connected devices until it is found.
321  *
322  * @note This function will block for timeout_ms, if the device is not enumerated at the moment of calling this function.
323  * @param[in] vid Vendor ID
324  * @param[in] pid Product ID
325  * @param[in] timeout_ms Connection timeout [ms]
326  * @param[out] dev CDC-ACM device
327  * @return esp_err_t
328  */
cdc_acm_find_and_open_usb_device(uint16_t vid,uint16_t pid,int timeout_ms,cdc_dev_t ** dev)329 static esp_err_t cdc_acm_find_and_open_usb_device(uint16_t vid, uint16_t pid, int timeout_ms, cdc_dev_t **dev)
330 {
331     assert(p_cdc_acm_obj);
332     assert(dev);
333 
334     *dev = calloc(1, sizeof(cdc_dev_t));
335     if (*dev == NULL) {
336         return ESP_ERR_NO_MEM;
337     }
338 
339     // First, check list of already opened CDC devices
340     ESP_LOGD(TAG, "Checking list of opened USB devices");
341     cdc_dev_t *cdc_dev;
342     SLIST_FOREACH(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry) {
343         const usb_device_desc_t *device_desc;
344         ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc));
345         if (device_desc->idVendor == vid && device_desc->idProduct == pid) {
346             // Return path 1:
347             (*dev)->dev_hdl = cdc_dev->dev_hdl;
348             return ESP_OK;
349         }
350     }
351 
352     // Second, poll connected devices until new device is connected or timeout
353     TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
354     TimeOut_t connection_timeout;
355     vTaskSetTimeOutState(&connection_timeout);
356 
357     while (true) {
358         ESP_LOGD(TAG, "Checking list of connected USB devices");
359         uint8_t dev_addr_list[10];
360         int num_of_devices;
361         ESP_ERROR_CHECK(usb_host_device_addr_list_fill(sizeof(dev_addr_list), dev_addr_list, &num_of_devices));
362 
363         // Go through device address list and find the one we are looking for
364         for (int i = 0; i < num_of_devices; i++) {
365             usb_device_handle_t current_device;
366             // Open USB device
367             if (usb_host_device_open(p_cdc_acm_obj->cdc_acm_client_hdl, dev_addr_list[i], &current_device) != ESP_OK) {
368                 continue; // In case we failed to open this device, continue with next one in the list
369             }
370             assert(current_device);
371             const usb_device_desc_t *device_desc;
372             ESP_ERROR_CHECK(usb_host_get_device_descriptor(current_device, &device_desc));
373             if (device_desc->idVendor == vid && device_desc->idProduct == pid) {
374                 // Return path 2:
375                 (*dev)->dev_hdl = current_device;
376                 return ESP_OK;
377             }
378             usb_host_device_close(p_cdc_acm_obj->cdc_acm_client_hdl, current_device);
379         }
380 
381         if (xTaskCheckForTimeOut(&connection_timeout, &timeout_ticks) != pdFALSE) {
382             break; // Timeout elapsed and the device is not connected
383         }
384         vTaskDelay(pdMS_TO_TICKS(50));
385     }
386 
387     // Timeout was reached, clean-up
388     free(*dev);
389     *dev = NULL;
390     return ESP_ERR_NOT_FOUND;
391 }
392 
cdc_acm_host_install(const cdc_acm_host_driver_config_t * driver_config)393 esp_err_t cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config)
394 {
395     CDC_ACM_CHECK(!p_cdc_acm_obj, ESP_ERR_INVALID_STATE);
396 
397     // Check driver configuration, use default if NULL is passed
398     if (driver_config == NULL) {
399         driver_config = &cdc_acm_driver_config_default;
400     }
401 
402     // Allocate all we need for this driver
403     esp_err_t ret;
404     cdc_acm_obj_t *cdc_acm_obj = heap_caps_calloc(1, sizeof(cdc_acm_obj_t), MALLOC_CAP_DEFAULT);
405     EventGroupHandle_t event_group = xEventGroupCreate();
406     SemaphoreHandle_t mutex = xSemaphoreCreateMutex();
407     TaskHandle_t driver_task_h = NULL;
408     xTaskCreatePinnedToCore(
409             cdc_acm_client_task, "USB-CDC", driver_config->driver_task_stack_size, NULL,
410             driver_config->driver_task_priority, &driver_task_h, driver_config->xCoreID);
411 
412     if (cdc_acm_obj == NULL || driver_task_h == NULL || event_group == NULL || mutex == NULL) {
413         ret = ESP_ERR_NO_MEM;
414         goto err;
415     }
416 
417     // Register USB Host client
418     usb_host_client_handle_t usb_client = NULL;
419     const usb_host_client_config_t client_config = {
420         .is_synchronous = false,
421         .max_num_event_msg = 3,
422         .async.client_event_callback = usb_event_cb,
423         .async.callback_arg = NULL
424     };
425     ESP_GOTO_ON_ERROR(usb_host_client_register(&client_config, &usb_client), err, TAG, "Failed to register USB host client");
426 
427     // Initialize CDC-ACM driver structure
428     SLIST_INIT(&(cdc_acm_obj->cdc_devices_list));
429     cdc_acm_obj->event_group = event_group;
430     cdc_acm_obj->open_close_mutex = mutex;
431     cdc_acm_obj->cdc_acm_client_hdl = usb_client;
432 
433     // Between 1st call of this function and following section, another task might try to install this driver:
434     // Make sure that there is only one instance of this driver in the system
435     CDC_ACM_ENTER_CRITICAL();
436     if (p_cdc_acm_obj) {
437         // Already created
438         ret = ESP_ERR_INVALID_STATE;
439         CDC_ACM_EXIT_CRITICAL();
440         goto client_err;
441     } else {
442         p_cdc_acm_obj = cdc_acm_obj;
443     }
444     CDC_ACM_EXIT_CRITICAL();
445 
446     // Everything OK: Start CDC-Driver task and return
447     vTaskResume(driver_task_h);
448     return ESP_OK;
449 
450 client_err:
451     usb_host_client_deregister(usb_client);
452 err: // Clean-up
453     free(cdc_acm_obj);
454     if (event_group) {
455         vEventGroupDelete(event_group);
456     }
457     if (driver_task_h) {
458         vTaskDelete(driver_task_h);
459     }
460     if (mutex) {
461         vSemaphoreDelete(mutex);
462     }
463     return ret;
464 }
465 
cdc_acm_host_uninstall()466 esp_err_t cdc_acm_host_uninstall()
467 {
468     esp_err_t ret;
469 
470     CDC_ACM_ENTER_CRITICAL();
471     CDC_ACM_CHECK_FROM_CRIT(p_cdc_acm_obj, ESP_ERR_INVALID_STATE);
472     cdc_acm_obj_t *cdc_acm_obj = p_cdc_acm_obj; // Save Driver's handle to temporary handle
473     CDC_ACM_EXIT_CRITICAL();
474 
475     xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); // Wait for all open/close calls to finish
476 
477     CDC_ACM_ENTER_CRITICAL();
478     if (SLIST_EMPTY(&p_cdc_acm_obj->cdc_devices_list)) { // Check that device list is empty (all devices closed)
479         p_cdc_acm_obj = NULL; // NULL static driver pointer: No open/close calls form this point
480     } else {
481         ret = ESP_ERR_INVALID_STATE;
482         CDC_ACM_EXIT_CRITICAL();
483         goto unblock;
484     }
485     CDC_ACM_EXIT_CRITICAL();
486 
487     // Signal to CDC task to stop, unblock it and wait for its deletion
488     xEventGroupSetBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN);
489     usb_host_client_unblock(cdc_acm_obj->cdc_acm_client_hdl);
490     ESP_GOTO_ON_FALSE(
491         xEventGroupWaitBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN_COMPLETE, pdFALSE, pdFALSE, pdMS_TO_TICKS(100)),
492         ESP_ERR_NOT_FINISHED, unblock, TAG,);
493 
494     // Free remaining resources and return
495     vEventGroupDelete(cdc_acm_obj->event_group);
496     xSemaphoreGive(cdc_acm_obj->open_close_mutex);
497     vSemaphoreDelete(cdc_acm_obj->open_close_mutex);
498     free(cdc_acm_obj);
499     return ESP_OK;
500 
501 unblock:
502     xSemaphoreGive(cdc_acm_obj->open_close_mutex);
503     return ret;
504 }
505 
506 /**
507  * @brief Free USB transfers used by this device
508  *
509  * @note There can be no transfers in flight, at the moment of calling this function.
510  * @param[in] cdc_dev Pointer to CDC device
511  */
cdc_acm_transfers_free(cdc_dev_t * cdc_dev)512 static void cdc_acm_transfers_free(cdc_dev_t *cdc_dev)
513 {
514     assert(cdc_dev);
515     usb_host_transfer_free(cdc_dev->notif.xfer);
516     usb_host_transfer_free(cdc_dev->data.in_xfer);
517     if (cdc_dev->data.out_xfer != NULL) {
518         if (cdc_dev->data.out_xfer->context != NULL) {
519             vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->data.out_xfer->context);
520         }
521         if (cdc_dev->data.out_mux != NULL) {
522             vSemaphoreDelete(cdc_dev->data.out_mux);
523         }
524         usb_host_transfer_free(cdc_dev->data.out_xfer);
525     }
526     if (cdc_dev->ctrl_transfer != NULL) {
527         if (cdc_dev->ctrl_transfer->context != NULL) {
528             vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context);
529         }
530         if (cdc_dev->ctrl_mux != NULL) {
531             vSemaphoreDelete(cdc_dev->ctrl_mux);
532         }
533         usb_host_transfer_free(cdc_dev->ctrl_transfer);
534     }
535 }
536 
537 /**
538  * @brief Allocate CDC transfers
539  *
540  * @param[in] cdc_dev       Pointer to CDC device
541  * @param[in] notif_ep_desc Pointer to notification EP descriptor
542  * @param[in] in_ep_desc-   Pointer to data IN EP descriptor
543  * @param[in] out_ep_desc   Pointer to data OUT EP descriptor
544  * @param[in] out_buf_len   Length of data OUT buffer
545  * @return esp_err_t
546  */
cdc_acm_transfers_allocate(cdc_dev_t * cdc_dev,const usb_ep_desc_t * notif_ep_desc,const usb_ep_desc_t * in_ep_desc,const usb_ep_desc_t * out_ep_desc,size_t out_buf_len)547 static esp_err_t cdc_acm_transfers_allocate(cdc_dev_t *cdc_dev, const usb_ep_desc_t *notif_ep_desc, const usb_ep_desc_t *in_ep_desc, const usb_ep_desc_t *out_ep_desc, size_t out_buf_len)
548 {
549     esp_err_t ret;
550 
551     // 1. Setup notification and control transfers if they are supported
552     if (notif_ep_desc) {
553         ESP_GOTO_ON_ERROR(
554             usb_host_transfer_alloc(USB_EP_DESC_GET_MPS(notif_ep_desc), 0, &cdc_dev->notif.xfer),
555             err, TAG,);
556         cdc_dev->notif.xfer->device_handle = cdc_dev->dev_hdl;
557         cdc_dev->notif.xfer->bEndpointAddress = notif_ep_desc->bEndpointAddress;
558         cdc_dev->notif.xfer->callback = notif_xfer_cb;
559         cdc_dev->notif.xfer->context = cdc_dev;
560         cdc_dev->notif.xfer->num_bytes = USB_EP_DESC_GET_MPS(notif_ep_desc);
561 
562         usb_device_info_t dev_info;
563         ESP_ERROR_CHECK(usb_host_device_info(cdc_dev->dev_hdl, &dev_info));
564         ESP_GOTO_ON_ERROR(
565             usb_host_transfer_alloc(dev_info.bMaxPacketSize0, 0, &cdc_dev->ctrl_transfer),
566             err, TAG,);
567         cdc_dev->ctrl_transfer->timeout_ms = 1000;
568         cdc_dev->ctrl_transfer->bEndpointAddress = 0;
569         cdc_dev->ctrl_transfer->device_handle = cdc_dev->dev_hdl;
570         cdc_dev->ctrl_transfer->context = cdc_dev;
571         cdc_dev->ctrl_transfer->callback = out_xfer_cb;
572         cdc_dev->ctrl_transfer->context = xSemaphoreCreateBinary();
573         ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->context, ESP_ERR_NO_MEM, err, TAG,);
574         cdc_dev->ctrl_mux = xSemaphoreCreateMutex();
575         ESP_GOTO_ON_FALSE(cdc_dev->ctrl_mux, ESP_ERR_NO_MEM, err, TAG,);
576     }
577 
578     // 2. Setup IN data transfer
579     ESP_GOTO_ON_ERROR(
580         usb_host_transfer_alloc(USB_EP_DESC_GET_MPS(in_ep_desc), 0, &cdc_dev->data.in_xfer),
581         err, TAG,
582     );
583     assert(cdc_dev->data.in_xfer);
584     cdc_dev->data.in_xfer->callback = in_xfer_cb;
585     cdc_dev->data.in_xfer->num_bytes = USB_EP_DESC_GET_MPS(in_ep_desc);
586     cdc_dev->data.in_xfer->bEndpointAddress = in_ep_desc->bEndpointAddress;
587     cdc_dev->data.in_xfer->device_handle = cdc_dev->dev_hdl;
588     cdc_dev->data.in_xfer->context = cdc_dev;
589 
590     // 3. Setup OUT bulk transfer (if it is required (out_buf_len > 0))
591     if (out_buf_len != 0) {
592         ESP_GOTO_ON_ERROR(
593             usb_host_transfer_alloc(out_buf_len, 0, &cdc_dev->data.out_xfer),
594             err, TAG,
595         );
596         assert(cdc_dev->data.out_xfer);
597         cdc_dev->data.out_xfer->device_handle = cdc_dev->dev_hdl;
598         cdc_dev->data.out_xfer->context = xSemaphoreCreateBinary();
599         ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->context, ESP_ERR_NO_MEM, err, TAG,);
600         cdc_dev->data.out_mux = xSemaphoreCreateMutex();
601         ESP_GOTO_ON_FALSE(cdc_dev->data.out_mux, ESP_ERR_NO_MEM, err, TAG,);
602         cdc_dev->data.out_xfer->bEndpointAddress = out_ep_desc->bEndpointAddress;
603         cdc_dev->data.out_xfer->callback = out_xfer_cb;
604     }
605     return ESP_OK;
606 
607 err:
608     cdc_acm_transfers_free(cdc_dev);
609     return ret;
610 }
611 
612 /**
613  * @brief Find CDC interface descriptor and its endpoint descriptors
614  *
615  * @note This function is called in open procedure of CDC compliant devices only.
616  * @param[in]  cdc_dev  Pointer to CDC device
617  * @param[in]  intf_idx Index of CDC interface that should be used for this device
618  * @param[out] notif_ep Pointer to notification EP descriptor
619  * @param[out] in_ep    Pointer to data IN EP descriptor
620  * @param[out] out_ep   Pointer to data OUT EP descriptor
621  * @return esp_err_t
622  */
cdc_acm_find_intf_and_ep_desc(cdc_dev_t * cdc_dev,uint8_t intf_idx,const usb_ep_desc_t ** notif_ep,const usb_ep_desc_t ** in_ep,const usb_ep_desc_t ** out_ep)623 static esp_err_t cdc_acm_find_intf_and_ep_desc(cdc_dev_t *cdc_dev, uint8_t intf_idx, const usb_ep_desc_t **notif_ep, const usb_ep_desc_t **in_ep, const usb_ep_desc_t **out_ep)
624 {
625     bool interface_found = false;
626     const usb_config_desc_t *config_desc;
627     const usb_device_desc_t *device_desc;
628     int data_intf_idx, notif_intf_idx;
629     int desc_offset = 0;
630 
631     // Get required descriptors
632     ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc));
633     ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc));
634 
635     if ((device_desc->bDeviceClass == USB_CLASS_MISC) && (device_desc->bDeviceSubClass == USB_SUBCLASS_COMMON) &&
636         (device_desc->bDeviceProtocol == USB_DEVICE_PROTOCOL_IAD)) {
637         // This is a composite device, that uses Interface Association Descriptor
638         const usb_standard_desc_t *this_desc = (const usb_standard_desc_t *)config_desc;
639         do {
640             this_desc = usb_parse_next_descriptor_of_type(
641                 this_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, &desc_offset);
642 
643             if (this_desc == NULL)
644                 break; // Reached end of configuration descriptor
645 
646             const usb_iad_desc_t *iad_desc = (const usb_iad_desc_t *)this_desc;
647             if (iad_desc->bFirstInterface == intf_idx) {
648                 // IAD with correct interface number was found: Check Class/Subclass codes, save Interface indexes
649                 assert(iad_desc->bInterfaceCount == 2);
650                 assert(iad_desc->bFunctionClass == USB_CLASS_COMM);
651                 assert(iad_desc->bFunctionSubClass == CDC_SUBCLASS_ACM);
652                 notif_intf_idx = iad_desc->bFirstInterface;
653                 data_intf_idx = iad_desc->bFirstInterface + 1;
654                 interface_found = true;
655             }
656         } while (!interface_found);
657     } else if ((device_desc->bDeviceClass == USB_CLASS_COMM) && (intf_idx == 0)) {
658         // This is a Communication Device Class
659         notif_intf_idx = 0;
660         data_intf_idx = 1;
661         interface_found = true;
662     }
663 
664     // Save found interfaces descriptors:
665     if (interface_found) {
666         // Notification IF and EP
667         cdc_dev->notif.intf_desc = usb_parse_interface_descriptor(config_desc, notif_intf_idx, 0, &desc_offset);
668         assert(cdc_dev->notif.intf_desc);
669 
670         // CDC specific descriptors should be right after CDC-Communication interface descriptor
671         // Note: That's why we use usb_parse_next_descriptor instead of usb_parse_next_descriptor_of_type.
672         // The latter could return CDC specific descriptors that don't belong to this interface
673         const usb_standard_desc_t *cdc_desc = (usb_standard_desc_t *)cdc_dev->notif.intf_desc;
674         do {
675             cdc_desc = usb_parse_next_descriptor(cdc_desc, config_desc->wTotalLength, &desc_offset);
676             if ((cdc_desc == NULL) || (cdc_desc->bDescriptorType != ((USB_CLASS_COMM << 4) | USB_W_VALUE_DT_INTERFACE)))
677                 break; // We found all CDC specific descriptors
678             cdc_dev->num_cdc_intf_desc++;
679             cdc_dev->cdc_intf_desc =
680                 realloc(cdc_dev->cdc_intf_desc, cdc_dev->num_cdc_intf_desc * (sizeof(usb_standard_desc_t *)));
681             assert(cdc_dev->cdc_intf_desc);
682             cdc_dev->cdc_intf_desc[cdc_dev->num_cdc_intf_desc - 1] = cdc_desc;
683         } while (1);
684         *notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->notif.intf_desc, 0, config_desc->wTotalLength, &desc_offset);
685         assert(notif_ep);
686 
687         // Data IF and EP
688         cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, data_intf_idx, 0, &desc_offset);
689         assert(cdc_dev->data.intf_desc);
690         int temp_offset = desc_offset;
691         for (int i = 0; i < 2; i++) {
692             const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset);
693             assert(this_ep);
694             if (USB_EP_DESC_GET_EP_DIR(this_ep)) {
695                 *in_ep = this_ep;
696             } else {
697                 *out_ep = this_ep;
698             }
699             desc_offset = temp_offset;
700         }
701         return ESP_OK;
702     }
703     return ESP_ERR_NOT_FOUND;
704 }
705 
cdc_acm_host_open(uint16_t vid,uint16_t pid,uint8_t interface_idx,const cdc_acm_host_device_config_t * dev_config,cdc_acm_dev_hdl_t * cdc_hdl_ret)706 esp_err_t cdc_acm_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret)
707 {
708     esp_err_t ret;
709     CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE);
710     CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG);
711     CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG);
712 
713     xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY);
714     // Find underlying USB device
715     cdc_dev_t *cdc_dev;
716     ESP_GOTO_ON_ERROR(
717         cdc_acm_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev),
718         exit, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", vid, pid);
719 
720     // Find and save relevant interface and endpoint descriptors
721     const usb_ep_desc_t *notif_ep = NULL;
722     const usb_ep_desc_t *in_ep = NULL;
723     const usb_ep_desc_t *out_ep = NULL;
724     ESP_GOTO_ON_ERROR(
725         cdc_acm_find_intf_and_ep_desc(cdc_dev, interface_idx, &notif_ep, &in_ep, &out_ep),
726         err, TAG, "Could not find required interface");
727 
728     // Check whether found Interfaces are really CDC-ACM
729     assert(cdc_dev->notif.intf_desc->bInterfaceClass == USB_CLASS_COMM);
730     assert(cdc_dev->notif.intf_desc->bInterfaceSubClass == CDC_SUBCLASS_ACM);
731     assert(cdc_dev->notif.intf_desc->bNumEndpoints == 1);
732     assert(cdc_dev->data.intf_desc->bInterfaceClass == USB_CLASS_CDC_DATA);
733     assert(cdc_dev->data.intf_desc->bNumEndpoints == 2);
734 
735     // Save Communication and Data protocols
736     cdc_dev->comm_protocol = (cdc_comm_protocol_t)cdc_dev->notif.intf_desc->bInterfaceProtocol;
737     cdc_dev->data_protocol = (cdc_data_protocol_t)cdc_dev->data.intf_desc->bInterfaceProtocol;
738 
739     // Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle
740     ESP_GOTO_ON_ERROR(cdc_acm_transfers_allocate(cdc_dev, notif_ep, in_ep, out_ep, dev_config->out_buffer_size), err, TAG,);
741     ESP_GOTO_ON_ERROR(cdc_acm_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG,);
742     *cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev;
743     xSemaphoreGive(p_cdc_acm_obj->open_close_mutex);
744     return ESP_OK;
745 
746 err:
747     cdc_acm_device_remove(cdc_dev);
748 exit:
749     xSemaphoreGive(p_cdc_acm_obj->open_close_mutex);
750     *cdc_hdl_ret = NULL;
751     return ret;
752 }
753 
cdc_acm_host_open_vendor_specific(uint16_t vid,uint16_t pid,uint8_t interface_num,const cdc_acm_host_device_config_t * dev_config,cdc_acm_dev_hdl_t * cdc_hdl_ret)754 esp_err_t cdc_acm_host_open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_num, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret)
755 {
756     esp_err_t ret;
757     CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE);
758     CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG);
759     CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG);
760 
761     xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY);
762 
763     // Find underlying USB device
764     cdc_dev_t *cdc_dev;
765     ESP_GOTO_ON_ERROR(
766         cdc_acm_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev),
767         exit, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", vid, pid);
768 
769     // Open procedure for CDC-ACM non-compliant devices:
770     const usb_config_desc_t *config_desc;
771     int desc_offset;
772     ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc));
773     cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, interface_num, 0, &desc_offset);
774     const int temp_offset = desc_offset; // Save this offset for later
775     assert(cdc_dev->data.intf_desc);
776 
777     // The interface can have 2-3 endpoints. 2 for data and 1 optional for notifications
778     const usb_ep_desc_t *in_ep = NULL;
779     const usb_ep_desc_t *out_ep = NULL;
780     const usb_ep_desc_t *notif_ep = NULL;
781     int ep_idx = 0;
782     if (cdc_dev->data.intf_desc->bNumEndpoints == 3) {
783         // Notification channel does not have its dedicated interface (data and notif interface is the same)
784         // First endpoint of this interface is used as notification channel
785         cdc_dev->notif.intf_desc = cdc_dev->data.intf_desc;
786         notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, 0, config_desc->wTotalLength, &desc_offset);
787         desc_offset = temp_offset;
788         ep_idx++;
789     }
790 
791     for (int i = ep_idx; i < ep_idx + 2; i++) {
792         const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset);
793         assert(this_ep);
794         if (USB_EP_DESC_GET_EP_DIR(this_ep)) {
795             in_ep = this_ep;
796         } else {
797             out_ep = this_ep;
798         }
799         desc_offset = temp_offset;
800     }
801 
802     // Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle
803     ESP_GOTO_ON_ERROR(cdc_acm_transfers_allocate(cdc_dev, notif_ep, in_ep, out_ep, dev_config->out_buffer_size), err, TAG, );
804     ESP_GOTO_ON_ERROR(cdc_acm_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG,);
805     *cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev;
806     xSemaphoreGive(p_cdc_acm_obj->open_close_mutex);
807     return ESP_OK;
808 err:
809     cdc_acm_device_remove(cdc_dev);
810 exit:
811     xSemaphoreGive(p_cdc_acm_obj->open_close_mutex);
812     return ret;
813 }
814 
cdc_acm_host_close(cdc_acm_dev_hdl_t cdc_hdl)815 esp_err_t cdc_acm_host_close(cdc_acm_dev_hdl_t cdc_hdl)
816 {
817     CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE);
818     CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG);
819 
820     xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY);
821 
822     cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl;
823 
824     // Cancel polling of BULK IN and INTERRUPT IN endpoints
825     cdc_dev->notif.cb = NULL;
826     cdc_dev->data.in_cb = NULL;
827     ESP_ERROR_CHECK(cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.in_xfer));
828     if (cdc_dev->notif.intf_desc != NULL) {
829         ESP_ERROR_CHECK(cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->notif.xfer));
830     }
831 
832     // Release all interfaces
833     ESP_ERROR_CHECK(usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber));
834     if ((cdc_dev->notif.intf_desc != NULL) && (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc)) {
835         ESP_ERROR_CHECK(usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber));
836     }
837 
838     CDC_ACM_ENTER_CRITICAL();
839     SLIST_REMOVE(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, cdc_dev_s, list_entry);
840     CDC_ACM_EXIT_CRITICAL();
841 
842     cdc_acm_device_remove(cdc_dev);
843     xSemaphoreGive(p_cdc_acm_obj->open_close_mutex);
844     return ESP_OK;
845 }
846 
cdc_acm_host_desc_print(cdc_acm_dev_hdl_t cdc_hdl)847 void cdc_acm_host_desc_print(cdc_acm_dev_hdl_t cdc_hdl)
848 {
849     assert(cdc_hdl);
850     cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl;
851 
852     ESP_RETURN_ON_FALSE(cdc_dev->num_cdc_intf_desc > 0,, TAG, "No CDC-ACM specific descriptors found");
853 
854     for (int i = 0; i < cdc_dev->num_cdc_intf_desc; i++) {
855         switch (((cdc_header_desc_t *)cdc_dev->cdc_intf_desc[i])->bDescriptorSubtype) {
856         case CDC_DESC_SUBTYPE_HEADER: {
857             cdc_header_desc_t *desc = (cdc_header_desc_t *)cdc_dev->cdc_intf_desc[i];
858             printf("CDC Header Descriptor:\n");
859             printf("\tbcdCDC: %d.%d0\n", ((desc->bcdCDC >> 8) & 0xF), ((desc->bcdCDC >> 4) & 0xF));
860             break;
861         }
862         case CDC_DESC_SUBTYPE_CALL: {
863             cdc_acm_call_desc_t *desc = (cdc_acm_call_desc_t *)cdc_dev->cdc_intf_desc[i];
864             printf("CDC Call Descriptor:\n");
865             printf("\tbmCapabilities: 0x%02X\n", desc->bmCapabilities.val);
866             printf("\tbDataInterface: %d\n", desc->bDataInterface);
867             break;
868         }
869         case CDC_DESC_SUBTYPE_ACM: {
870             cdc_acm_acm_desc_t *desc = (cdc_acm_acm_desc_t *)cdc_dev->cdc_intf_desc[i];
871             printf("CDC ACM Descriptor:\n");
872             printf("\tbmCapabilities: 0x%02X\n", desc->bmCapabilities.val);
873             break;
874         }
875         case CDC_DESC_SUBTYPE_UNION: {
876             cdc_union_desc_t *desc = (cdc_union_desc_t *)cdc_dev->cdc_intf_desc[i];
877             printf("CDC Union Descriptor:\n");
878             printf("\tbControlInterface: %d\n", desc->bControlInterface);
879             printf("\tbSubordinateInterface[0]: %d\n", desc->bSubordinateInterface[0]);
880             break;
881         }
882         default:
883             ESP_LOGW(TAG, "Unsupported CDC specific descriptor");
884             break;
885         }
886     }
887 }
888 
889 /**
890  * @brief Check finished transfer status
891  *
892  * Return to on transfer completed OK.
893  * Cancel the transfer and issue user's callback in case of an error.
894  *
895  * @param[in] transfer Transfer to be checked
896  * @return true Transfer completed
897  * @return false Transfer NOT completed
898  */
cdc_acm_is_transfer_completed(usb_transfer_t * transfer)899 static bool cdc_acm_is_transfer_completed(usb_transfer_t *transfer)
900 {
901     cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context;
902     bool completed = false;
903 
904     switch (transfer->status) {
905     case USB_TRANSFER_STATUS_COMPLETED:
906         completed = true;
907         break;
908     case USB_TRANSFER_STATUS_NO_DEVICE: // User is notified about device disconnection from usb_event_cb
909     case USB_TRANSFER_STATUS_CANCELED:
910         break;
911     case USB_TRANSFER_STATUS_ERROR:
912     case USB_TRANSFER_STATUS_TIMED_OUT:
913     case USB_TRANSFER_STATUS_STALL:
914     case USB_TRANSFER_STATUS_OVERFLOW:
915     case USB_TRANSFER_STATUS_SKIPPED:
916     default:
917         // Transfer was not completed or cancelled by user. Inform user about this
918         if (cdc_dev->notif.cb) {
919             const cdc_acm_host_dev_event_data_t error_event = {
920                 .type = CDC_ACM_HOST_ERROR,
921                 .data.error = (int) transfer->status
922             };
923             cdc_dev->notif.cb((cdc_acm_dev_hdl_t) cdc_dev, &error_event, cdc_dev->cb_arg);
924         }
925     }
926     return completed;
927 }
928 
in_xfer_cb(usb_transfer_t * transfer)929 static void in_xfer_cb(usb_transfer_t *transfer)
930 {
931     ESP_LOGD("CDC_ACM", "in xfer cb");
932     cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context;
933 
934     if (cdc_acm_is_transfer_completed(transfer)) {
935         if (cdc_dev->data.in_cb) {
936             cdc_dev->data.in_cb(transfer->data_buffer, transfer->actual_num_bytes, cdc_dev->cb_arg);
937         }
938 
939         ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer");
940         usb_host_transfer_submit(cdc_dev->data.in_xfer);
941     }
942 }
943 
notif_xfer_cb(usb_transfer_t * transfer)944 static void notif_xfer_cb(usb_transfer_t *transfer)
945 {
946     ESP_LOGD("CDC_ACM", "notif xfer cb");
947     cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context;
948 
949     if (cdc_acm_is_transfer_completed(transfer)) {
950         cdc_notification_t *notif = (cdc_notification_t *)transfer->data_buffer;
951         switch (notif->bNotificationCode) {
952         case CDC_NOTIF_NETWORK_CONNECTION: {
953             if (cdc_dev->notif.cb) {
954                 const cdc_acm_host_dev_event_data_t net_conn_event = {
955                     .type = CDC_ACM_HOST_NETWORK_CONNECTION,
956                     .data.network_connected = (bool) notif->wValue
957                 };
958                 cdc_dev->notif.cb((cdc_acm_dev_hdl_t) cdc_dev, &net_conn_event, cdc_dev->cb_arg);
959             }
960             break;
961         }
962         case CDC_NOTIF_SERIAL_STATE: {
963             cdc_dev->serial_state.val = *((uint16_t *)notif->Data);
964             if (cdc_dev->notif.cb) {
965                 const cdc_acm_host_dev_event_data_t serial_state_event = {
966                     .type = CDC_ACM_HOST_SERIAL_STATE,
967                     .data.serial_state = cdc_dev->serial_state
968                 };
969                 cdc_dev->notif.cb((cdc_acm_dev_hdl_t) cdc_dev, &serial_state_event, cdc_dev->cb_arg);
970             }
971             break;
972         }
973         case CDC_NOTIF_RESPONSE_AVAILABLE: // Encapsulated commands not implemented - fallthrough
974         default:
975             ESP_LOGW("CDC_ACM", "Unsupported notification type 0x%02X", notif->bNotificationCode);
976             ESP_LOG_BUFFER_HEX("CDC_ACM", transfer->data_buffer, transfer->actual_num_bytes);
977             break;
978         }
979 
980         // Start polling for new data again
981         ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer");
982         usb_host_transfer_submit(cdc_dev->notif.xfer);
983     }
984 }
985 
out_xfer_cb(usb_transfer_t * transfer)986 static void out_xfer_cb(usb_transfer_t *transfer)
987 {
988     ESP_LOGD("CDC_ACM", "out/ctrl xfer cb");
989     assert(transfer->context);
990     xSemaphoreGive((SemaphoreHandle_t)transfer->context);
991 }
992 
usb_event_cb(const usb_host_client_event_msg_t * event_msg,void * arg)993 static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
994 {
995     switch (event_msg->event) {
996     case USB_HOST_CLIENT_EVENT_NEW_DEV:
997         ESP_LOGD(TAG, "New device connected");
998         break;
999     case USB_HOST_CLIENT_EVENT_DEV_GONE: {
1000         ESP_LOGD(TAG, "Device suddenly disconnected");
1001         // Find CDC pseudo-devices associated with this USB device and close them
1002         cdc_dev_t *cdc_dev;
1003         cdc_dev_t *tcdc_dev;
1004         // We are using 'SAFE' version of 'SLIST_FOREACH' which enables user to close the disconnected device in the callback
1005         SLIST_FOREACH_SAFE(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry, tcdc_dev) {
1006             if (cdc_dev->dev_hdl == event_msg->dev_gone.dev_hdl && cdc_dev->notif.cb) {
1007                 // The suddenly disconnected device was opened by this driver: inform user about this
1008                 const cdc_acm_host_dev_event_data_t disconn_event = {
1009                     .type = CDC_ACM_HOST_DEVICE_DISCONNECTED,
1010                 };
1011                 cdc_dev->notif.cb((cdc_acm_dev_hdl_t) cdc_dev, &disconn_event, cdc_dev->cb_arg);
1012             }
1013         }
1014         break;
1015     }
1016     default:
1017         assert(false);
1018         break;
1019     }
1020 }
1021 
cdc_acm_host_data_tx_blocking(cdc_acm_dev_hdl_t cdc_hdl,const uint8_t * data,size_t data_len,uint32_t timeout_ms)1022 esp_err_t cdc_acm_host_data_tx_blocking(cdc_acm_dev_hdl_t cdc_hdl, const uint8_t *data, size_t data_len, uint32_t timeout_ms)
1023 {
1024     esp_err_t ret;
1025     CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG);
1026     cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl;
1027     CDC_ACM_CHECK(data && (data_len > 0), ESP_ERR_INVALID_ARG);
1028     CDC_ACM_CHECK(cdc_dev->data.out_xfer, ESP_ERR_NOT_SUPPORTED); // Device was opened as read-only.
1029     CDC_ACM_CHECK(data_len <= cdc_dev->data.out_xfer->data_buffer_size, ESP_ERR_INVALID_SIZE);
1030 
1031     // Take OUT mutex and fill the OUT transfer
1032     BaseType_t taken = xSemaphoreTake(cdc_dev->data.out_mux, pdMS_TO_TICKS(timeout_ms));
1033     if (taken != pdTRUE) {
1034         return ESP_ERR_TIMEOUT;
1035     }
1036 
1037     ESP_LOGD("CDC_ACM", "Submitting BULK OUT transfer");
1038     memcpy(cdc_dev->data.out_xfer->data_buffer, data, data_len);
1039     cdc_dev->data.out_xfer->num_bytes = data_len;
1040     cdc_dev->data.out_xfer->timeout_ms = timeout_ms;
1041     ESP_GOTO_ON_ERROR(usb_host_transfer_submit(cdc_dev->data.out_xfer), unblock, TAG,);
1042 
1043     // Wait for OUT transfer completion
1044     taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->data.out_xfer->context, pdMS_TO_TICKS(timeout_ms));
1045     if (!taken) {
1046         // Reset the endpoint
1047         cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.out_xfer);
1048         ret = ESP_ERR_TIMEOUT;
1049         goto unblock;
1050     }
1051 
1052     ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Bulk OUT transfer error");
1053     ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->actual_num_bytes == data_len, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred");
1054     ret = ESP_OK;
1055 
1056 unblock:
1057     xSemaphoreGive(cdc_dev->data.out_mux);
1058     return ret;
1059 }
1060 
cdc_acm_host_line_coding_get(cdc_acm_dev_hdl_t cdc_hdl,cdc_acm_line_coding_t * line_coding)1061 esp_err_t cdc_acm_host_line_coding_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_acm_line_coding_t *line_coding)
1062 {
1063     CDC_ACM_CHECK(cdc_hdl && line_coding, ESP_ERR_INVALID_ARG);
1064 
1065     ESP_RETURN_ON_ERROR(
1066         send_cdc_request((cdc_dev_t *)cdc_hdl, true, CDC_REQ_GET_LINE_CODING, (uint8_t *)line_coding, sizeof(cdc_acm_line_coding_t), 0),
1067         TAG,);
1068     ESP_LOGD(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding->dwDTERate,
1069              line_coding->bCharFormat, line_coding->bParityType, line_coding->bDataBits);
1070     return ESP_OK;
1071 }
1072 
cdc_acm_host_line_coding_set(cdc_acm_dev_hdl_t cdc_hdl,const cdc_acm_line_coding_t * line_coding)1073 esp_err_t cdc_acm_host_line_coding_set(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_line_coding_t *line_coding)
1074 {
1075     CDC_ACM_CHECK(cdc_hdl && line_coding, ESP_ERR_INVALID_ARG);
1076 
1077     ESP_RETURN_ON_ERROR(
1078         send_cdc_request((cdc_dev_t *)cdc_hdl, false, CDC_REQ_SET_LINE_CODING, (uint8_t *)line_coding, sizeof(cdc_acm_line_coding_t), 0),
1079         TAG,);
1080     ESP_LOGD(TAG, "Line Set: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding->dwDTERate,
1081              line_coding->bCharFormat, line_coding->bParityType, line_coding->bDataBits);
1082     return ESP_OK;
1083 }
1084 
cdc_acm_host_set_control_line_state(cdc_acm_dev_hdl_t cdc_hdl,bool dtr,bool rts)1085 esp_err_t cdc_acm_host_set_control_line_state(cdc_acm_dev_hdl_t cdc_hdl, bool dtr, bool rts)
1086 {
1087     CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG);
1088 
1089     const uint16_t ctrl_bitmap = (uint16_t)dtr | ((uint16_t)rts << 1);
1090 
1091     ESP_RETURN_ON_ERROR(
1092         send_cdc_request((cdc_dev_t *)cdc_hdl, false, CDC_REQ_SET_CONTROL_LINE_STATE, NULL, 0, ctrl_bitmap),
1093         TAG,);
1094     ESP_LOGD(TAG, "Control Line Set: DTR: %d, RTS: %d", dtr, rts);
1095     return ESP_OK;
1096 }
1097 
cdc_acm_host_send_break(cdc_acm_dev_hdl_t cdc_hdl,uint16_t duration_ms)1098 esp_err_t cdc_acm_host_send_break(cdc_acm_dev_hdl_t cdc_hdl, uint16_t duration_ms)
1099 {
1100     CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG);
1101 
1102     ESP_RETURN_ON_ERROR(
1103         send_cdc_request((cdc_dev_t *)cdc_hdl, false, CDC_REQ_SEND_BREAK, NULL, 0, duration_ms),
1104         TAG,);
1105 
1106     // Block until break is deasserted
1107     vTaskDelay(pdMS_TO_TICKS(duration_ms + 1));
1108     return ESP_OK;
1109 }
1110 
send_cdc_request(cdc_dev_t * cdc_dev,bool in_transfer,cdc_request_code_t request,uint8_t * data,uint16_t data_len,uint16_t value)1111 static esp_err_t send_cdc_request(cdc_dev_t *cdc_dev, bool in_transfer, cdc_request_code_t request, uint8_t *data, uint16_t data_len, uint16_t value)
1112 {
1113     esp_err_t ret;
1114     CDC_ACM_CHECK(cdc_dev->ctrl_transfer, ESP_ERR_NOT_SUPPORTED);
1115     CDC_ACM_CHECK(cdc_dev->ctrl_transfer->data_buffer_size >= data_len, ESP_ERR_INVALID_SIZE);
1116 
1117     // Take Mutex and fill the CTRL request
1118     BaseType_t taken = xSemaphoreTake(cdc_dev->ctrl_mux, pdMS_TO_TICKS(1000));
1119     if (!taken) {
1120         return ESP_ERR_TIMEOUT;
1121     }
1122     usb_setup_packet_t *req = (usb_setup_packet_t *)(cdc_dev->ctrl_transfer->data_buffer);
1123     uint8_t *start_of_data = (uint8_t *)req + sizeof(usb_setup_packet_t);
1124     req->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE;
1125     req->bRequest = request;
1126     req->wValue = value;
1127     req->wIndex = cdc_dev->notif.intf_desc->bInterfaceNumber;
1128     req->wLength = data_len;
1129 
1130     if (in_transfer) {
1131         req->bmRequestType |= USB_BM_REQUEST_TYPE_DIR_IN;
1132     } else {
1133         memcpy(start_of_data, data, data_len);
1134     }
1135 
1136     cdc_dev->ctrl_transfer->num_bytes = data_len + sizeof(usb_setup_packet_t);
1137     ESP_GOTO_ON_ERROR(
1138         usb_host_transfer_submit_control(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->ctrl_transfer),
1139         unblock, TAG, "CTRL transfer failed");
1140 
1141     taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context, pdMS_TO_TICKS(1000)); // This is a fixed timeout. Every CDC device should be able to respond to CTRL transfer in 1 second
1142     if (!taken) {
1143         // Transfer was not finished, error in USB LIB. Reset the endpoint
1144         cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->ctrl_transfer);
1145         ret = ESP_ERR_TIMEOUT;
1146         goto unblock;
1147     }
1148 
1149     ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Control transfer error");
1150     ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->actual_num_bytes == cdc_dev->ctrl_transfer->num_bytes, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred");
1151 
1152     if (in_transfer) {
1153         memcpy(data, start_of_data, data_len);
1154     }
1155     ret = ESP_OK;
1156 
1157 unblock:
1158     xSemaphoreGive(cdc_dev->ctrl_mux);
1159     return ret;
1160 }
1161 
cdc_acm_host_protocols_get(cdc_acm_dev_hdl_t cdc_hdl,cdc_comm_protocol_t * comm,cdc_data_protocol_t * data)1162 esp_err_t cdc_acm_host_protocols_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_comm_protocol_t *comm, cdc_data_protocol_t *data)
1163 {
1164     CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG);
1165     cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl;
1166 
1167     if (comm != NULL) *comm = cdc_dev->comm_protocol;
1168     if (data != NULL) *data = cdc_dev->data_protocol;
1169     return ESP_OK;
1170 }
1171