From d95ba21328a9f2dba5041d6b0929eb0d0d95c59d Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Wed, 13 Jul 2022 10:27:56 +0200 Subject: [PATCH] usb_host: Use CDC driver from component registry --- .../usb/host/cdc/cdc_acm_bg96/CMakeLists.txt | 1 - .../cdc/cdc_acm_bg96/main/idf_component.yml | 2 + .../usb/host/cdc/cdc_acm_host/CMakeLists.txt | 1 - .../cdc/cdc_acm_host/main/idf_component.yml | 4 + .../usb/host/cdc/cdc_acm_vcp/CMakeLists.txt | 1 - .../cdc/cdc_acm_vcp/main/idf_component.yml | 4 + .../cdc/common/cdc_acm_host/CMakeLists.txt | 18 - .../host/cdc/common/cdc_acm_host/README.md | 46 - .../cdc/common/cdc_acm_host/cdc_acm_host.c | 1223 ----------------- .../cdc_acm_host/include/usb/cdc_acm_host.h | 343 ----- .../cdc_acm_host/include/usb/usb_types_cdc.h | 206 --- .../common/cdc_acm_host/test/CMakeLists.txt | 3 - .../cdc_acm_host/test/test_cdc_acm_host.c | 447 ------ .../cdc/common/cdc_acm_host/test/usb_device.c | 71 - 14 files changed, 10 insertions(+), 2360 deletions(-) create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/README.md delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/usb_types_cdc.h delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/usb_device.c diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt index 0955a50e2a..726b664235 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt @@ -4,6 +4,5 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/peripherals/usb/host/cdc/common) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(cdc_acm_host_bg96) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml index 7f91e1ab78..0455cf6acb 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml +++ b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml @@ -1,3 +1,5 @@ +## IDF Component Manager Manifest File dependencies: idf: ">=4.4" igrr/libnmea: "^0.1.1" + usb_host_cdc_acm: "1.*" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_host/CMakeLists.txt index eeca393d0c..067259a1fb 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/CMakeLists.txt @@ -4,6 +4,5 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/peripherals/usb/host/cdc/common) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(cdc_acm_host) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml new file mode 100644 index 0000000000..96533b1fbc --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + usb_host_cdc_acm: "1.*" + idf: ">=4.4" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/CMakeLists.txt index 18a0d1a6dd..daa5b7c53c 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/CMakeLists.txt @@ -4,6 +4,5 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/peripherals/usb/host/cdc/common) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(cdc_acm_vcp) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml new file mode 100644 index 0000000000..96533b1fbc --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + usb_host_cdc_acm: "1.*" + idf: ">=4.4" diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/CMakeLists.txt b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/CMakeLists.txt deleted file mode 100644 index 17093480c9..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -set(srcs) -set(include) -# As CONFIG_USB_OTG_SUPPORTED comes from Kconfig, it is not evaluated yet -# when components are being registered. -set(require usb) - -if(CONFIG_USB_OTG_SUPPORTED) - list(APPEND srcs "cdc_acm_host.c") - list(APPEND include "include") -endif() - -idf_component_register(SRCS ${srcs} - INCLUDE_DIRS ${include} - REQUIRES ${require} - ) -if(CONFIG_USB_OTG_SUPPORTED) - target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") -endif() diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/README.md b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/README.md deleted file mode 100644 index aeb28b107f..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# USB Host CDC-ACM Class Driver - -This directory contains an implementation of a USB CDC-ACM Host Class Driver that is implemented on top of the [USB Host Library](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html). - -## Supported Devices - -The CDC-ACM Host driver supports the following types of CDC devices: - -1. CDC-ACM devices -2. CDC-like vendor specific devices (usually found on USB to UART bridge devices) - -### CDC-ACM Devices - -The CDC-ACM Class driver supports CDC-ACM devices that meet the following requirements: -- The device class code must be set to the CDC class `0x02` or implement Interface Association Descriptor (IAD) -- The CDC-ACM must contain the following interfaces: - - A Communication Class Interface containing a management element (EP0) and may also contain a notification element (an interrupt endpoint). The driver will check this interface for CDC Functional Descriptors. - - A Data Class Interface with two BULK endpoints (IN and OUT). Other transfer types are not supported by the driver - -### CDC-Like Vendor Specific Devices - -The CDC-ACM Class driver supports CDC-like devices that meet the following requirements: -- The device class code must be set to the vendor specific class code `0xFF` -- The device needs to provide and interface containing the following endpoints: - - (Mandatory) Two Bulk endpoints (IN and OUT) for data - - (Optional) An interrupt endpoint (IN) for the notification element - -For CDC-like devices, users are responsible for ensuring that they only call APIs (e.g., `cdc_acm_host_send_break()`) that are supported by the target device. - - -## Usage - -The following steps outline the typical API call pattern of the CDC-ACM Class Driver - -1. Install the USB Host Library via `usb_host_install()` -2. Install the CDC-ACM driver via `cdc_acm_host_install()` -3. Call `cdc_acm_host_open()`/`cdc_acm_host_open_vendor_specific()` to open a target CDC-ACM/CDC-like device. These functions will block until the target device is connected -4. To transmit data, call `cdc_acm_host_data_tx_blocking()` -5. When data is received, the driver will automatically run the receive data callback -6. An opened device can be closed via `cdc_acm_host_close()` -7. The CDC-ACM driver can be uninstalled via `cdc_acm_host_uninstall()` - -## Examples - -- For an example with a CDC-ACM device, refer to [cdc_acm_host](../../cdc_acm_host) -- For an example with a CDC-like device, refer to [cdc_acm_host_bg96](../../cdc_acm_bg96) diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c deleted file mode 100644 index 4d8cfd3f9e..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c +++ /dev/null @@ -1,1223 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "esp_log.h" -#include -#include -#include -#include "usb/usb_host.h" -#include "usb/cdc_acm_host.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/event_groups.h" -#include "esp_check.h" -#include "esp_system.h" - -#define TAG "cdc_acm" - -// CDC devices often implement Interface Association Descriptor (IAD). Parse IAD only when -// bDeviceClass = 0xEF (Miscellaneous Device Class), bDeviceSubClass = 0x02 (Common Class), bDeviceProtocol = 0x01 (Interface Association Descriptor) -// @see USB Interface Association Descriptor: Device Class Code and Use Model rev 1.0, Table 1-1 -#define USB_SUBCLASS_COMMON 0x02 -#define USB_DEVICE_PROTOCOL_IAD 0x01 - -// CDC-ACM spinlock -static portMUX_TYPE cdc_acm_lock = portMUX_INITIALIZER_UNLOCKED; -#define CDC_ACM_ENTER_CRITICAL() portENTER_CRITICAL(&cdc_acm_lock) -#define CDC_ACM_EXIT_CRITICAL() portEXIT_CRITICAL(&cdc_acm_lock) - -// CDC-ACM events -#define CDC_ACM_TEARDOWN BIT0 -#define CDC_ACM_TEARDOWN_COMPLETE BIT1 - -// CDC-ACM check macros -#define CDC_ACM_CHECK(cond, ret_val) ({ \ - if (!(cond)) { \ - return (ret_val); \ - } \ -}) - -#define CDC_ACM_CHECK_FROM_CRIT(cond, ret_val) ({ \ - if (!(cond)) { \ - CDC_ACM_EXIT_CRITICAL(); \ - return ret_val; \ - } \ -}) - -// CDC-ACM driver object -typedef struct { - usb_host_client_handle_t cdc_acm_client_hdl; /*!< USB Host handle reused for all CDC-ACM devices in the system */ - SemaphoreHandle_t open_close_mutex; - EventGroupHandle_t event_group; - cdc_acm_new_dev_callback_t new_dev_cb; - SLIST_HEAD(list_dev, cdc_dev_s) cdc_devices_list; /*!< List of open pseudo devices */ -} cdc_acm_obj_t; - -static cdc_acm_obj_t *p_cdc_acm_obj = NULL; - -/** - * @brief Default CDC-ACM driver configuration - * - * This configuration is used when user passes NULL to config pointer during device open. - */ -static const cdc_acm_host_driver_config_t cdc_acm_driver_config_default = { - .driver_task_stack_size = 4096, - .driver_task_priority = 10, - .xCoreID = 0, - .new_dev_cb = NULL, -}; - -/** - * @brief USB CDC PSTN Call Descriptor - * - * @see Table 3, USB CDC-PSTN specification rev. 1.2 - */ -typedef struct { - uint8_t bFunctionLength; - const uint8_t bDescriptorType; - const cdc_desc_subtype_t bDescriptorSubtype; - union { - struct { - uint8_t call_management: 1; // Device handles call management itself - uint8_t call_over_data_if: 1; // Device sends/receives call management information over Data Class interface - uint8_t reserved: 6; - }; - uint8_t val; - } bmCapabilities; - uint8_t bDataInterface; // Interface number of Data Class interface optionally used for call management -} __attribute__((packed)) cdc_acm_call_desc_t; - -/** - * @brief USB CDC PSTN Abstract Control Model Descriptor - * - * @see Table 4, USB CDC-PSTN specification rev. 1.2 - */ -typedef struct { - uint8_t bFunctionLength; - const uint8_t bDescriptorType; - const cdc_desc_subtype_t bDescriptorSubtype; - union { - struct { - uint8_t feature: 1; // Device supports Set/Clear/Get_Comm_Feature requests - uint8_t serial: 1; // Device supports Set/Get_Line_Coding, Set_Control_Line_State and Serial_State request and notifications - uint8_t send_break: 1; // Device supports Send_Break request - uint8_t network: 1; // Device supports Network_Connection notification - uint8_t reserved: 4; - }; - uint8_t val; - } bmCapabilities; -} __attribute__((packed)) cdc_acm_acm_desc_t; - -typedef struct cdc_dev_s cdc_dev_t; -struct cdc_dev_s{ - usb_device_handle_t dev_hdl; // USB device handle - void *cb_arg; // Common argument for user's callbacks (data IN and Notification) - struct { - usb_transfer_t *out_xfer; // OUT data transfer - usb_transfer_t *in_xfer; // IN data transfer - cdc_acm_data_callback_t in_cb; // User's callback for async (non-blocking) data IN - const usb_intf_desc_t *intf_desc; // Pointer to data interface descriptor - SemaphoreHandle_t out_mux; // OUT mutex - } data; - - struct { - usb_transfer_t *xfer; // IN notification transfer - const usb_intf_desc_t *intf_desc; // Pointer to notification interface descriptor, can be NULL if there is no notification channel in the device - cdc_acm_host_dev_callback_t cb; // User's callback for device events - } notif; // Structure with Notif pipe data - - usb_transfer_t *ctrl_transfer; // CTRL (endpoint 0) transfer - SemaphoreHandle_t ctrl_mux; // CTRL mutex - cdc_acm_uart_state_t serial_state; // Serial State - cdc_comm_protocol_t comm_protocol; - cdc_data_protocol_t data_protocol; - int num_cdc_intf_desc; // Number of CDC Interface descriptors in following array - const usb_standard_desc_t **cdc_intf_desc; // CDC Interface descriptors - SLIST_ENTRY(cdc_dev_s) list_entry; -}; - -/** - * @brief Notification received callback - * - * Notification (interrupt) IN transfer is submitted at the end of this function to ensure periodic poll of IN endpoint. - * - * @param[in] transfer Transfer that triggered the callback - */ -static void notif_xfer_cb(usb_transfer_t *transfer); - -/** - * @brief Data received callback - * - * Data (bulk) IN transfer is submitted at the end of this function to ensure continuous poll of IN endpoint. - * - * @param[in] transfer Transfer that triggered the callback - */ -static void in_xfer_cb(usb_transfer_t *transfer); - -/** - * @brief Data send callback - * - * Reused for bulk OUT and CTRL transfers - * - * @param[in] transfer Transfer that triggered the callback - */ -static void out_xfer_cb(usb_transfer_t *transfer); - -/** - * @brief USB Host Client event callback - * - * Handling of USB device connection/disconnection to/from root HUB. - * - * @param[in] event_msg Event message type - * @param[in] arg Caller's argument (not used in this driver) - */ -static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg); - -/** - * @brief Send CDC specific request - * - * Helper function that will send CDC specific request to default endpoint. - * Both IN and OUT requests are sent through this API, depending on the in_transfer parameter. - * - * @see Chapter 6.2, USB CDC specification rev. 1.2 - * @note CDC specific requests are only supported by devices that have dedicated management element. - * - * @param[in] cdc_dev Pointer to CDC device - * @param[in] in_transfer Direction of data phase. true: IN, false: OUT - * @param[in] request CDC request code - * @param[inout] data Pointer to data buffer. Input for OUT transfers, output for IN transfers. - * @param[in] data_len Length of data buffer - * @param[in] value Value to be set in bValue of Setup packet - * @return esp_err_t - */ -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); - -/** - * @brief CDC-ACM driver handling task - * - * USB host client registration and deregistration is handled here. - * - * @param[in] arg User's argument. Handle of a task that started this task. - */ -static void cdc_acm_client_task(void *arg) -{ - vTaskSuspend(NULL); // Task will be resumed from cdc_acm_host_install() - cdc_acm_obj_t *cdc_acm_obj = p_cdc_acm_obj; // Make local copy of the driver's handle - assert(cdc_acm_obj->cdc_acm_client_hdl); - - // Start handling client's events - while (1) { - usb_host_client_handle_events(cdc_acm_obj->cdc_acm_client_hdl, portMAX_DELAY); - EventBits_t events = xEventGroupGetBits(cdc_acm_obj->event_group); - if (events & CDC_ACM_TEARDOWN) { - break; - } - } - - ESP_LOGD(TAG, "Deregistering client"); - ESP_ERROR_CHECK(usb_host_client_deregister(cdc_acm_obj->cdc_acm_client_hdl)); - xEventGroupSetBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN_COMPLETE); - vTaskDelete(NULL); -} - -/** - * @brief Cancel transfer and reset endpoint - * - * This function will cancel ongoing transfer a reset its endpoint to ready state. - * - * @param[in] dev_hdl USB device handle - * @param[in] transfer Transfer to be cancelled - * @return esp_err_t - */ -static esp_err_t cdc_acm_reset_transfer_endpoint(usb_device_handle_t dev_hdl, usb_transfer_t *transfer) -{ - assert(dev_hdl); - assert(transfer); - - ESP_RETURN_ON_ERROR(usb_host_endpoint_halt(dev_hdl, transfer->bEndpointAddress), TAG,); - ESP_RETURN_ON_ERROR(usb_host_endpoint_flush(dev_hdl, transfer->bEndpointAddress), TAG,); - usb_host_endpoint_clear(dev_hdl, transfer->bEndpointAddress); - return ESP_OK; -} - -/** - * @brief Start CDC device - * - * After this call, USB host peripheral will continuously poll IN endpoints. - * - * @param cdc_dev - * @param[in] event_cb Device event callback - * @param[in] in_cb Data received callback - * @param[in] user_arg Optional user's argument, that will be passed to the callbacks - * @return esp_err_t - */ -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) -{ - esp_err_t ret = ESP_OK; - assert(cdc_dev); - - CDC_ACM_ENTER_CRITICAL(); - cdc_dev->notif.cb = event_cb; - cdc_dev->data.in_cb = in_cb; - cdc_dev->cb_arg = user_arg; - CDC_ACM_EXIT_CRITICAL(); - - // Claim data interface and start polling its IN endpoint - 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,); - ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); - ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->data.in_xfer)); - - // If notification are supported, claim its interface and start polling its IN endpoint - if (cdc_dev->notif.intf_desc != NULL) { - if (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc) { - ESP_GOTO_ON_ERROR(usb_host_interface_claim(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, - cdc_dev->notif.intf_desc->bInterfaceNumber, 0), err, TAG,); - } - ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer"); - ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->notif.xfer)); - } - - // Everything OK, add the device into list and return - CDC_ACM_ENTER_CRITICAL(); - SLIST_INSERT_HEAD(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, list_entry); - CDC_ACM_EXIT_CRITICAL(); - return ret; - -err: - usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber); - if (cdc_dev->notif.intf_desc != NULL) { - usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber); - } - return ret; -} - -static void cdc_acm_transfers_free(cdc_dev_t *cdc_dev); -/** - * @brief Helper function that releases resources claimed by CDC device - * - * Close underlying USB device, free device driver memory - * - * @note All interfaces claimed by this device must be release before calling this function - * @param cdc_dev CDC device handle to be removed - */ -static void cdc_acm_device_remove(cdc_dev_t *cdc_dev) -{ - assert(cdc_dev); - cdc_acm_transfers_free(cdc_dev); - free(cdc_dev->cdc_intf_desc); - // 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) - usb_host_device_close(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl); // Gracefully continue on error - free(cdc_dev); -} - -/** - * @brief Open USB device with requested VID/PID - * - * This function has two regular return paths: - * 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. - * 2. USB device with matching VID/PID is NOT opened by this driver yet: poll USB connected devices until it is found. - * - * @note This function will block for timeout_ms, if the device is not enumerated at the moment of calling this function. - * @param[in] vid Vendor ID - * @param[in] pid Product ID - * @param[in] timeout_ms Connection timeout [ms] - * @param[out] dev CDC-ACM device - * @return esp_err_t - */ -static esp_err_t cdc_acm_find_and_open_usb_device(uint16_t vid, uint16_t pid, int timeout_ms, cdc_dev_t **dev) -{ - assert(p_cdc_acm_obj); - assert(dev); - - *dev = calloc(1, sizeof(cdc_dev_t)); - if (*dev == NULL) { - return ESP_ERR_NO_MEM; - } - - // First, check list of already opened CDC devices - ESP_LOGD(TAG, "Checking list of opened USB devices"); - cdc_dev_t *cdc_dev; - SLIST_FOREACH(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry) { - const usb_device_desc_t *device_desc; - ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); - if (device_desc->idVendor == vid && device_desc->idProduct == pid) { - // Return path 1: - (*dev)->dev_hdl = cdc_dev->dev_hdl; - return ESP_OK; - } - } - - // Second, poll connected devices until new device is connected or timeout - TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); - TimeOut_t connection_timeout; - vTaskSetTimeOutState(&connection_timeout); - - while (true) { - ESP_LOGD(TAG, "Checking list of connected USB devices"); - uint8_t dev_addr_list[10]; - int num_of_devices; - ESP_ERROR_CHECK(usb_host_device_addr_list_fill(sizeof(dev_addr_list), dev_addr_list, &num_of_devices)); - - // Go through device address list and find the one we are looking for - for (int i = 0; i < num_of_devices; i++) { - usb_device_handle_t current_device; - // Open USB device - if (usb_host_device_open(p_cdc_acm_obj->cdc_acm_client_hdl, dev_addr_list[i], ¤t_device) != ESP_OK) { - continue; // In case we failed to open this device, continue with next one in the list - } - assert(current_device); - const usb_device_desc_t *device_desc; - ESP_ERROR_CHECK(usb_host_get_device_descriptor(current_device, &device_desc)); - if (device_desc->idVendor == vid && device_desc->idProduct == pid) { - // Return path 2: - (*dev)->dev_hdl = current_device; - return ESP_OK; - } - usb_host_device_close(p_cdc_acm_obj->cdc_acm_client_hdl, current_device); - } - - if (xTaskCheckForTimeOut(&connection_timeout, &timeout_ticks) != pdFALSE) { - break; // Timeout elapsed and the device is not connected - } - vTaskDelay(pdMS_TO_TICKS(50)); - } - - // Timeout was reached, clean-up - free(*dev); - *dev = NULL; - return ESP_ERR_NOT_FOUND; -} - -esp_err_t cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config) -{ - CDC_ACM_CHECK(!p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - - // Check driver configuration, use default if NULL is passed - if (driver_config == NULL) { - driver_config = &cdc_acm_driver_config_default; - } - - // Allocate all we need for this driver - esp_err_t ret; - cdc_acm_obj_t *cdc_acm_obj = heap_caps_calloc(1, sizeof(cdc_acm_obj_t), MALLOC_CAP_DEFAULT); - EventGroupHandle_t event_group = xEventGroupCreate(); - SemaphoreHandle_t mutex = xSemaphoreCreateMutex(); - TaskHandle_t driver_task_h = NULL; - xTaskCreatePinnedToCore( - cdc_acm_client_task, "USB-CDC", driver_config->driver_task_stack_size, NULL, - driver_config->driver_task_priority, &driver_task_h, driver_config->xCoreID); - - if (cdc_acm_obj == NULL || driver_task_h == NULL || event_group == NULL || mutex == NULL) { - ret = ESP_ERR_NO_MEM; - goto err; - } - - // Register USB Host client - usb_host_client_handle_t usb_client = NULL; - const usb_host_client_config_t client_config = { - .is_synchronous = false, - .max_num_event_msg = 3, - .async.client_event_callback = usb_event_cb, - .async.callback_arg = NULL - }; - ESP_GOTO_ON_ERROR(usb_host_client_register(&client_config, &usb_client), err, TAG, "Failed to register USB host client"); - - // Initialize CDC-ACM driver structure - SLIST_INIT(&(cdc_acm_obj->cdc_devices_list)); - cdc_acm_obj->event_group = event_group; - cdc_acm_obj->open_close_mutex = mutex; - cdc_acm_obj->cdc_acm_client_hdl = usb_client; - cdc_acm_obj->new_dev_cb = driver_config->new_dev_cb; - - // Between 1st call of this function and following section, another task might try to install this driver: - // Make sure that there is only one instance of this driver in the system - CDC_ACM_ENTER_CRITICAL(); - if (p_cdc_acm_obj) { - // Already created - ret = ESP_ERR_INVALID_STATE; - CDC_ACM_EXIT_CRITICAL(); - goto client_err; - } else { - p_cdc_acm_obj = cdc_acm_obj; - } - CDC_ACM_EXIT_CRITICAL(); - - // Everything OK: Start CDC-Driver task and return - vTaskResume(driver_task_h); - return ESP_OK; - -client_err: - usb_host_client_deregister(usb_client); -err: // Clean-up - free(cdc_acm_obj); - if (event_group) { - vEventGroupDelete(event_group); - } - if (driver_task_h) { - vTaskDelete(driver_task_h); - } - if (mutex) { - vSemaphoreDelete(mutex); - } - return ret; -} - -esp_err_t cdc_acm_host_uninstall() -{ - esp_err_t ret; - - CDC_ACM_ENTER_CRITICAL(); - CDC_ACM_CHECK_FROM_CRIT(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - cdc_acm_obj_t *cdc_acm_obj = p_cdc_acm_obj; // Save Driver's handle to temporary handle - CDC_ACM_EXIT_CRITICAL(); - - xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); // Wait for all open/close calls to finish - - CDC_ACM_ENTER_CRITICAL(); - if (SLIST_EMPTY(&p_cdc_acm_obj->cdc_devices_list)) { // Check that device list is empty (all devices closed) - p_cdc_acm_obj = NULL; // NULL static driver pointer: No open/close calls form this point - } else { - ret = ESP_ERR_INVALID_STATE; - CDC_ACM_EXIT_CRITICAL(); - goto unblock; - } - CDC_ACM_EXIT_CRITICAL(); - - // Signal to CDC task to stop, unblock it and wait for its deletion - xEventGroupSetBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN); - usb_host_client_unblock(cdc_acm_obj->cdc_acm_client_hdl); - ESP_GOTO_ON_FALSE( - xEventGroupWaitBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN_COMPLETE, pdFALSE, pdFALSE, pdMS_TO_TICKS(100)), - ESP_ERR_NOT_FINISHED, unblock, TAG,); - - // Free remaining resources and return - vEventGroupDelete(cdc_acm_obj->event_group); - xSemaphoreGive(cdc_acm_obj->open_close_mutex); - vSemaphoreDelete(cdc_acm_obj->open_close_mutex); - free(cdc_acm_obj); - return ESP_OK; - -unblock: - xSemaphoreGive(cdc_acm_obj->open_close_mutex); - return ret; -} - -/** - * @brief Free USB transfers used by this device - * - * @note There can be no transfers in flight, at the moment of calling this function. - * @param[in] cdc_dev Pointer to CDC device - */ -static void cdc_acm_transfers_free(cdc_dev_t *cdc_dev) -{ - assert(cdc_dev); - usb_host_transfer_free(cdc_dev->notif.xfer); - usb_host_transfer_free(cdc_dev->data.in_xfer); - if (cdc_dev->data.out_xfer != NULL) { - if (cdc_dev->data.out_xfer->context != NULL) { - vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->data.out_xfer->context); - } - if (cdc_dev->data.out_mux != NULL) { - vSemaphoreDelete(cdc_dev->data.out_mux); - } - usb_host_transfer_free(cdc_dev->data.out_xfer); - } - if (cdc_dev->ctrl_transfer != NULL) { - if (cdc_dev->ctrl_transfer->context != NULL) { - vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context); - } - if (cdc_dev->ctrl_mux != NULL) { - vSemaphoreDelete(cdc_dev->ctrl_mux); - } - usb_host_transfer_free(cdc_dev->ctrl_transfer); - } -} - -/** - * @brief Allocate CDC transfers - * - * @param[in] cdc_dev Pointer to CDC device - * @param[in] notif_ep_desc Pointer to notification EP descriptor - * @param[in] in_ep_desc- Pointer to data IN EP descriptor - * @param[in] out_ep_desc Pointer to data OUT EP descriptor - * @param[in] out_buf_len Length of data OUT buffer - * @return esp_err_t - */ -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) -{ - esp_err_t ret; - - // 1. Setup notification transfer if it is supported - if (notif_ep_desc) { - ESP_GOTO_ON_ERROR( - usb_host_transfer_alloc(USB_EP_DESC_GET_MPS(notif_ep_desc), 0, &cdc_dev->notif.xfer), - err, TAG,); - cdc_dev->notif.xfer->device_handle = cdc_dev->dev_hdl; - cdc_dev->notif.xfer->bEndpointAddress = notif_ep_desc->bEndpointAddress; - cdc_dev->notif.xfer->callback = notif_xfer_cb; - cdc_dev->notif.xfer->context = cdc_dev; - cdc_dev->notif.xfer->num_bytes = USB_EP_DESC_GET_MPS(notif_ep_desc); - } - - // 2. Setup control transfer - usb_device_info_t dev_info; - ESP_ERROR_CHECK(usb_host_device_info(cdc_dev->dev_hdl, &dev_info)); - ESP_GOTO_ON_ERROR( - usb_host_transfer_alloc(dev_info.bMaxPacketSize0, 0, &cdc_dev->ctrl_transfer), - err, TAG,); - cdc_dev->ctrl_transfer->timeout_ms = 1000; - cdc_dev->ctrl_transfer->bEndpointAddress = 0; - cdc_dev->ctrl_transfer->device_handle = cdc_dev->dev_hdl; - cdc_dev->ctrl_transfer->context = cdc_dev; - cdc_dev->ctrl_transfer->callback = out_xfer_cb; - cdc_dev->ctrl_transfer->context = xSemaphoreCreateBinary(); - ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->context, ESP_ERR_NO_MEM, err, TAG,); - cdc_dev->ctrl_mux = xSemaphoreCreateMutex(); - ESP_GOTO_ON_FALSE(cdc_dev->ctrl_mux, ESP_ERR_NO_MEM, err, TAG,); - - // 3. Setup IN data transfer - ESP_GOTO_ON_ERROR( - usb_host_transfer_alloc(USB_EP_DESC_GET_MPS(in_ep_desc), 0, &cdc_dev->data.in_xfer), - err, TAG, - ); - assert(cdc_dev->data.in_xfer); - cdc_dev->data.in_xfer->callback = in_xfer_cb; - cdc_dev->data.in_xfer->num_bytes = USB_EP_DESC_GET_MPS(in_ep_desc); - cdc_dev->data.in_xfer->bEndpointAddress = in_ep_desc->bEndpointAddress; - cdc_dev->data.in_xfer->device_handle = cdc_dev->dev_hdl; - cdc_dev->data.in_xfer->context = cdc_dev; - - // 4. Setup OUT bulk transfer (if it is required (out_buf_len > 0)) - if (out_buf_len != 0) { - ESP_GOTO_ON_ERROR( - usb_host_transfer_alloc(out_buf_len, 0, &cdc_dev->data.out_xfer), - err, TAG, - ); - assert(cdc_dev->data.out_xfer); - cdc_dev->data.out_xfer->device_handle = cdc_dev->dev_hdl; - cdc_dev->data.out_xfer->context = xSemaphoreCreateBinary(); - ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->context, ESP_ERR_NO_MEM, err, TAG,); - cdc_dev->data.out_mux = xSemaphoreCreateMutex(); - ESP_GOTO_ON_FALSE(cdc_dev->data.out_mux, ESP_ERR_NO_MEM, err, TAG,); - cdc_dev->data.out_xfer->bEndpointAddress = out_ep_desc->bEndpointAddress; - cdc_dev->data.out_xfer->callback = out_xfer_cb; - } - return ESP_OK; - -err: - cdc_acm_transfers_free(cdc_dev); - return ret; -} - -/** - * @brief Find CDC interface descriptor and its endpoint descriptors - * - * @note This function is called in open procedure of CDC compliant devices only. - * @param[in] cdc_dev Pointer to CDC device - * @param[in] intf_idx Index of CDC interface that should be used for this device - * @param[out] notif_ep Pointer to notification EP descriptor - * @param[out] in_ep Pointer to data IN EP descriptor - * @param[out] out_ep Pointer to data OUT EP descriptor - * @return esp_err_t - */ -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) -{ - bool interface_found = false; - const usb_config_desc_t *config_desc; - const usb_device_desc_t *device_desc; - int data_intf_idx, notif_intf_idx; - int desc_offset = 0; - - // Get required descriptors - ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); - ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc)); - - if ((device_desc->bDeviceClass == USB_CLASS_MISC) && (device_desc->bDeviceSubClass == USB_SUBCLASS_COMMON) && - (device_desc->bDeviceProtocol == USB_DEVICE_PROTOCOL_IAD)) { - // This is a composite device, that uses Interface Association Descriptor - const usb_standard_desc_t *this_desc = (const usb_standard_desc_t *)config_desc; - do { - this_desc = usb_parse_next_descriptor_of_type( - this_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, &desc_offset); - - if (this_desc == NULL) - break; // Reached end of configuration descriptor - - const usb_iad_desc_t *iad_desc = (const usb_iad_desc_t *)this_desc; - if (iad_desc->bFirstInterface == intf_idx) { - // IAD with correct interface number was found: Check Class/Subclass codes, save Interface indexes - assert(iad_desc->bInterfaceCount == 2); - assert(iad_desc->bFunctionClass == USB_CLASS_COMM); - assert(iad_desc->bFunctionSubClass == USB_CDC_SUBCLASS_ACM); - notif_intf_idx = iad_desc->bFirstInterface; - data_intf_idx = iad_desc->bFirstInterface + 1; - interface_found = true; - } - } while (!interface_found); - } else if ((device_desc->bDeviceClass == USB_CLASS_COMM) && (intf_idx == 0)) { - // This is a Communication Device Class - notif_intf_idx = 0; - data_intf_idx = 1; - interface_found = true; - } - - // Save found interfaces descriptors: - if (interface_found) { - // Notification IF and EP - cdc_dev->notif.intf_desc = usb_parse_interface_descriptor(config_desc, notif_intf_idx, 0, &desc_offset); - assert(cdc_dev->notif.intf_desc); - - // CDC specific descriptors should be right after CDC-Communication interface descriptor - // Note: That's why we use usb_parse_next_descriptor instead of usb_parse_next_descriptor_of_type. - // The latter could return CDC specific descriptors that don't belong to this interface - const usb_standard_desc_t *cdc_desc = (usb_standard_desc_t *)cdc_dev->notif.intf_desc; - do { - cdc_desc = usb_parse_next_descriptor(cdc_desc, config_desc->wTotalLength, &desc_offset); - if ((cdc_desc == NULL) || (cdc_desc->bDescriptorType != ((USB_CLASS_COMM << 4) | USB_B_DESCRIPTOR_TYPE_INTERFACE ))) - break; // We found all CDC specific descriptors - cdc_dev->num_cdc_intf_desc++; - cdc_dev->cdc_intf_desc = - realloc(cdc_dev->cdc_intf_desc, cdc_dev->num_cdc_intf_desc * (sizeof(usb_standard_desc_t *))); - assert(cdc_dev->cdc_intf_desc); - cdc_dev->cdc_intf_desc[cdc_dev->num_cdc_intf_desc - 1] = cdc_desc; - } while (1); - *notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->notif.intf_desc, 0, config_desc->wTotalLength, &desc_offset); - assert(notif_ep); - - // Data IF and EP - cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, data_intf_idx, 0, &desc_offset); - assert(cdc_dev->data.intf_desc); - int temp_offset = desc_offset; - for (int i = 0; i < 2; i++) { - const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset); - assert(this_ep); - if (USB_EP_DESC_GET_EP_DIR(this_ep)) { - *in_ep = this_ep; - } else { - *out_ep = this_ep; - } - desc_offset = temp_offset; - } - return ESP_OK; - } - return ESP_ERR_NOT_FOUND; -} - -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) -{ - esp_err_t ret; - CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG); - CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG); - - xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); - // Find underlying USB device - cdc_dev_t *cdc_dev; - ESP_GOTO_ON_ERROR( - cdc_acm_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev), - exit, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", vid, pid); - - // Find and save relevant interface and endpoint descriptors - const usb_ep_desc_t *notif_ep = NULL; - const usb_ep_desc_t *in_ep = NULL; - const usb_ep_desc_t *out_ep = NULL; - ESP_GOTO_ON_ERROR( - cdc_acm_find_intf_and_ep_desc(cdc_dev, interface_idx, ¬if_ep, &in_ep, &out_ep), - err, TAG, "Could not find required interface"); - - // Check whether found Interfaces are really CDC-ACM - assert(cdc_dev->notif.intf_desc->bInterfaceClass == USB_CLASS_COMM); - assert(cdc_dev->notif.intf_desc->bInterfaceSubClass == USB_CDC_SUBCLASS_ACM); - assert(cdc_dev->notif.intf_desc->bNumEndpoints == 1); - assert(cdc_dev->data.intf_desc->bInterfaceClass == USB_CLASS_CDC_DATA); - assert(cdc_dev->data.intf_desc->bNumEndpoints == 2); - - // Save Communication and Data protocols - cdc_dev->comm_protocol = (cdc_comm_protocol_t)cdc_dev->notif.intf_desc->bInterfaceProtocol; - cdc_dev->data_protocol = (cdc_data_protocol_t)cdc_dev->data.intf_desc->bInterfaceProtocol; - - // Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle - ESP_GOTO_ON_ERROR(cdc_acm_transfers_allocate(cdc_dev, notif_ep, in_ep, out_ep, dev_config->out_buffer_size), err, TAG,); - ESP_GOTO_ON_ERROR(cdc_acm_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG,); - *cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev; - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - return ESP_OK; - -err: - cdc_acm_device_remove(cdc_dev); -exit: - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - *cdc_hdl_ret = NULL; - return ret; -} - -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) -{ - esp_err_t ret; - CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG); - CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG); - - xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); - - // Find underlying USB device - cdc_dev_t *cdc_dev; - ESP_GOTO_ON_ERROR( - cdc_acm_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev), - exit, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", vid, pid); - - // Open procedure for CDC-ACM non-compliant devices: - const usb_config_desc_t *config_desc; - int desc_offset; - ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc)); - cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, interface_num, 0, &desc_offset); - ESP_GOTO_ON_FALSE( - cdc_dev->data.intf_desc, - ESP_ERR_NOT_FOUND, err, TAG, "Required interfece no %d was not found.", interface_num); - const int temp_offset = desc_offset; // Save this offset for later - - // The interface can have 2-3 endpoints. 2 for data and 1 optional for notifications - const usb_ep_desc_t *in_ep = NULL; - const usb_ep_desc_t *out_ep = NULL; - const usb_ep_desc_t *notif_ep = NULL; - int ep_idx = 0; - if (cdc_dev->data.intf_desc->bNumEndpoints == 3) { - // Notification channel does not have its dedicated interface (data and notif interface is the same) - // First endpoint of this interface is used as notification channel - cdc_dev->notif.intf_desc = cdc_dev->data.intf_desc; - notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, 0, config_desc->wTotalLength, &desc_offset); - desc_offset = temp_offset; - ep_idx++; - } - - for (int i = ep_idx; i < ep_idx + 2; i++) { - const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset); - assert(this_ep); - if (USB_EP_DESC_GET_EP_DIR(this_ep)) { - in_ep = this_ep; - } else { - out_ep = this_ep; - } - desc_offset = temp_offset; - } - - // Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle - ESP_GOTO_ON_ERROR(cdc_acm_transfers_allocate(cdc_dev, notif_ep, in_ep, out_ep, dev_config->out_buffer_size), err, TAG, ); - ESP_GOTO_ON_ERROR(cdc_acm_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG,); - *cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev; - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - return ESP_OK; -err: - cdc_acm_device_remove(cdc_dev); -exit: - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - return ret; -} - -esp_err_t cdc_acm_host_close(cdc_acm_dev_hdl_t cdc_hdl) -{ - CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - - xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); - - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - - // Cancel polling of BULK IN and INTERRUPT IN endpoints - cdc_dev->notif.cb = NULL; - cdc_dev->data.in_cb = NULL; - ESP_ERROR_CHECK(cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.in_xfer)); - if (cdc_dev->notif.intf_desc != NULL) { - ESP_ERROR_CHECK(cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->notif.xfer)); - } - - // Release all interfaces - 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)); - if ((cdc_dev->notif.intf_desc != NULL) && (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc)) { - 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)); - } - - CDC_ACM_ENTER_CRITICAL(); - SLIST_REMOVE(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, cdc_dev_s, list_entry); - CDC_ACM_EXIT_CRITICAL(); - - cdc_acm_device_remove(cdc_dev); - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - return ESP_OK; -} - -/** - * @brief Print CDC specific descriptor in human readable form - * - * This is a callback function that is called from USB Host library, - * when it wants to print full configuration descriptor to stdout. - * - * @param[in] _desc CDC specific descriptor - */ -static void cdc_acm_print_desc(const usb_standard_desc_t *_desc) -{ - if (_desc->bDescriptorType != ((USB_CLASS_COMM << 4) | USB_B_DESCRIPTOR_TYPE_INTERFACE )) - { - // Quietly return in case that this descriptor is not CDC interface descriptor - return; - } - - switch (((cdc_header_desc_t *)_desc)->bDescriptorSubtype) { - case USB_CDC_DESC_SUBTYPE_HEADER: { - cdc_header_desc_t *desc = (cdc_header_desc_t *)_desc; - printf("\t*** CDC Header Descriptor ***\n"); - printf("\tbcdCDC: %d.%d0\n", ((desc->bcdCDC >> 8) & 0xF), ((desc->bcdCDC >> 4) & 0xF)); - break; - } - case USB_CDC_DESC_SUBTYPE_CALL: { - cdc_acm_call_desc_t *desc = (cdc_acm_call_desc_t *)_desc; - printf("\t*** CDC Call Descriptor ***\n"); - printf("\tbmCapabilities: 0x%02X\n", desc->bmCapabilities.val); - printf("\tbDataInterface: %d\n", desc->bDataInterface); - break; - } - case USB_CDC_DESC_SUBTYPE_ACM: { - cdc_acm_acm_desc_t *desc = (cdc_acm_acm_desc_t *)_desc; - printf("\t*** CDC ACM Descriptor ***\n"); - printf("\tbmCapabilities: 0x%02X\n", desc->bmCapabilities.val); - break; - } - case USB_CDC_DESC_SUBTYPE_UNION: { - cdc_union_desc_t *desc = (cdc_union_desc_t *)_desc; - printf("\t*** CDC Union Descriptor ***\n"); - printf("\tbControlInterface: %d\n", desc->bControlInterface); - printf("\tbSubordinateInterface[0]: %d\n", desc->bSubordinateInterface[0]); - break; - } - default: - ESP_LOGW(TAG, "Unsupported CDC specific descriptor"); - break; - } -} - -void cdc_acm_host_desc_print(cdc_acm_dev_hdl_t cdc_hdl) -{ - assert(cdc_hdl); - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - - const usb_device_desc_t *device_desc; - const usb_config_desc_t *config_desc; - ESP_ERROR_CHECK_WITHOUT_ABORT(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); - ESP_ERROR_CHECK_WITHOUT_ABORT(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc)); - usb_print_device_descriptor(device_desc); - usb_print_config_descriptor(config_desc, cdc_acm_print_desc); -} - -/** - * @brief Check finished transfer status - * - * Return to on transfer completed OK. - * Cancel the transfer and issue user's callback in case of an error. - * - * @param[in] transfer Transfer to be checked - * @return true Transfer completed - * @return false Transfer NOT completed - */ -static bool cdc_acm_is_transfer_completed(usb_transfer_t *transfer) -{ - cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; - bool completed = false; - - switch (transfer->status) { - case USB_TRANSFER_STATUS_COMPLETED: - completed = true; - break; - case USB_TRANSFER_STATUS_NO_DEVICE: // User is notified about device disconnection from usb_event_cb - case USB_TRANSFER_STATUS_CANCELED: - break; - case USB_TRANSFER_STATUS_ERROR: - case USB_TRANSFER_STATUS_TIMED_OUT: - case USB_TRANSFER_STATUS_STALL: - case USB_TRANSFER_STATUS_OVERFLOW: - case USB_TRANSFER_STATUS_SKIPPED: - default: - // Transfer was not completed or cancelled by user. Inform user about this - if (cdc_dev->notif.cb) { - const cdc_acm_host_dev_event_data_t error_event = { - .type = CDC_ACM_HOST_ERROR, - .data.error = (int) transfer->status - }; - cdc_dev->notif.cb(&error_event, cdc_dev->cb_arg); - } - } - return completed; -} - -static void in_xfer_cb(usb_transfer_t *transfer) -{ - ESP_LOGD("CDC_ACM", "in xfer cb"); - cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; - - if (cdc_acm_is_transfer_completed(transfer)) { - if (cdc_dev->data.in_cb) { - cdc_dev->data.in_cb(transfer->data_buffer, transfer->actual_num_bytes, cdc_dev->cb_arg); - } - - ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); - usb_host_transfer_submit(cdc_dev->data.in_xfer); - } -} - -static void notif_xfer_cb(usb_transfer_t *transfer) -{ - ESP_LOGD("CDC_ACM", "notif xfer cb"); - cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; - - if (cdc_acm_is_transfer_completed(transfer)) { - cdc_notification_t *notif = (cdc_notification_t *)transfer->data_buffer; - switch (notif->bNotificationCode) { - case USB_CDC_NOTIF_NETWORK_CONNECTION: { - if (cdc_dev->notif.cb) { - const cdc_acm_host_dev_event_data_t net_conn_event = { - .type = CDC_ACM_HOST_NETWORK_CONNECTION, - .data.network_connected = (bool) notif->wValue - }; - cdc_dev->notif.cb(&net_conn_event, cdc_dev->cb_arg); - } - break; - } - case USB_CDC_NOTIF_SERIAL_STATE: { - cdc_dev->serial_state.val = *((uint16_t *)notif->Data); - if (cdc_dev->notif.cb) { - const cdc_acm_host_dev_event_data_t serial_state_event = { - .type = CDC_ACM_HOST_SERIAL_STATE, - .data.serial_state = cdc_dev->serial_state - }; - cdc_dev->notif.cb(&serial_state_event, cdc_dev->cb_arg); - } - break; - } - case USB_CDC_NOTIF_RESPONSE_AVAILABLE: // Encapsulated commands not implemented - fallthrough - default: - ESP_LOGW("CDC_ACM", "Unsupported notification type 0x%02X", notif->bNotificationCode); - ESP_LOG_BUFFER_HEX("CDC_ACM", transfer->data_buffer, transfer->actual_num_bytes); - break; - } - - // Start polling for new data again - ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer"); - usb_host_transfer_submit(cdc_dev->notif.xfer); - } -} - -static void out_xfer_cb(usb_transfer_t *transfer) -{ - ESP_LOGD("CDC_ACM", "out/ctrl xfer cb"); - assert(transfer->context); - xSemaphoreGive((SemaphoreHandle_t)transfer->context); -} - -static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg) -{ - switch (event_msg->event) { - case USB_HOST_CLIENT_EVENT_NEW_DEV: - ESP_LOGD(TAG, "New device connected"); - if (p_cdc_acm_obj->new_dev_cb) { - usb_device_handle_t new_dev; - if (usb_host_device_open(p_cdc_acm_obj->cdc_acm_client_hdl, event_msg->new_dev.address, &new_dev) != ESP_OK) { - ESP_LOGW(TAG, "Couldn't open the new device"); - break; - } - assert(new_dev); - p_cdc_acm_obj->new_dev_cb(new_dev); - usb_host_device_close(p_cdc_acm_obj->cdc_acm_client_hdl, new_dev); - } - break; - case USB_HOST_CLIENT_EVENT_DEV_GONE: { - ESP_LOGD(TAG, "Device suddenly disconnected"); - // Find CDC pseudo-devices associated with this USB device and close them - cdc_dev_t *cdc_dev; - cdc_dev_t *tcdc_dev; - // We are using 'SAFE' version of 'SLIST_FOREACH' which enables user to close the disconnected device in the callback - SLIST_FOREACH_SAFE(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry, tcdc_dev) { - if (cdc_dev->dev_hdl == event_msg->dev_gone.dev_hdl && cdc_dev->notif.cb) { - // The suddenly disconnected device was opened by this driver: inform user about this - const cdc_acm_host_dev_event_data_t disconn_event = { - .type = CDC_ACM_HOST_DEVICE_DISCONNECTED, - .data.cdc_hdl = (cdc_acm_dev_hdl_t) cdc_dev, - }; - cdc_dev->notif.cb(&disconn_event, cdc_dev->cb_arg); - } - } - break; - } - default: - assert(false); - break; - } -} - -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) -{ - esp_err_t ret; - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - CDC_ACM_CHECK(data && (data_len > 0), ESP_ERR_INVALID_ARG); - CDC_ACM_CHECK(cdc_dev->data.out_xfer, ESP_ERR_NOT_SUPPORTED); // Device was opened as read-only. - CDC_ACM_CHECK(data_len <= cdc_dev->data.out_xfer->data_buffer_size, ESP_ERR_INVALID_SIZE); - - // Take OUT mutex and fill the OUT transfer - BaseType_t taken = xSemaphoreTake(cdc_dev->data.out_mux, pdMS_TO_TICKS(timeout_ms)); - if (taken != pdTRUE) { - return ESP_ERR_TIMEOUT; - } - - ESP_LOGD("CDC_ACM", "Submitting BULK OUT transfer"); - memcpy(cdc_dev->data.out_xfer->data_buffer, data, data_len); - cdc_dev->data.out_xfer->num_bytes = data_len; - cdc_dev->data.out_xfer->timeout_ms = timeout_ms; - ESP_GOTO_ON_ERROR(usb_host_transfer_submit(cdc_dev->data.out_xfer), unblock, TAG,); - - // Wait for OUT transfer completion - taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->data.out_xfer->context, pdMS_TO_TICKS(timeout_ms)); - if (!taken) { - // Reset the endpoint - cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.out_xfer); - ret = ESP_ERR_TIMEOUT; - goto unblock; - } - - ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Bulk OUT transfer error"); - 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"); - ret = ESP_OK; - -unblock: - xSemaphoreGive(cdc_dev->data.out_mux); - return ret; -} - -esp_err_t cdc_acm_host_line_coding_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_acm_line_coding_t *line_coding) -{ - CDC_ACM_CHECK(line_coding, ESP_ERR_INVALID_ARG); - - ESP_RETURN_ON_ERROR( - send_cdc_request((cdc_dev_t *)cdc_hdl, true, USB_CDC_REQ_GET_LINE_CODING, (uint8_t *)line_coding, sizeof(cdc_acm_line_coding_t), 0), - TAG,); - ESP_LOGD(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding->dwDTERate, - line_coding->bCharFormat, line_coding->bParityType, line_coding->bDataBits); - return ESP_OK; -} - -esp_err_t cdc_acm_host_line_coding_set(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_line_coding_t *line_coding) -{ - CDC_ACM_CHECK(line_coding, ESP_ERR_INVALID_ARG); - - ESP_RETURN_ON_ERROR( - send_cdc_request((cdc_dev_t *)cdc_hdl, false, USB_CDC_REQ_SET_LINE_CODING, (uint8_t *)line_coding, sizeof(cdc_acm_line_coding_t), 0), - TAG,); - ESP_LOGD(TAG, "Line Set: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding->dwDTERate, - line_coding->bCharFormat, line_coding->bParityType, line_coding->bDataBits); - return ESP_OK; -} - -esp_err_t cdc_acm_host_set_control_line_state(cdc_acm_dev_hdl_t cdc_hdl, bool dtr, bool rts) -{ - const uint16_t ctrl_bitmap = (uint16_t)dtr | ((uint16_t)rts << 1); - - ESP_RETURN_ON_ERROR( - send_cdc_request((cdc_dev_t *)cdc_hdl, false, USB_CDC_REQ_SET_CONTROL_LINE_STATE, NULL, 0, ctrl_bitmap), - TAG,); - ESP_LOGD(TAG, "Control Line Set: DTR: %d, RTS: %d", dtr, rts); - return ESP_OK; -} - -esp_err_t cdc_acm_host_send_break(cdc_acm_dev_hdl_t cdc_hdl, uint16_t duration_ms) -{ - ESP_RETURN_ON_ERROR( - send_cdc_request((cdc_dev_t *)cdc_hdl, false, USB_CDC_REQ_SEND_BREAK, NULL, 0, duration_ms), - TAG,); - - // Block until break is deasserted - vTaskDelay(pdMS_TO_TICKS(duration_ms + 1)); - return ESP_OK; -} - -esp_err_t cdc_acm_host_send_custom_request(cdc_acm_dev_hdl_t cdc_hdl, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *data) -{ - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - if (wLength > 0) { - CDC_ACM_CHECK(data, ESP_ERR_INVALID_ARG); - } - CDC_ACM_CHECK(cdc_dev->ctrl_transfer->data_buffer_size >= wLength, ESP_ERR_INVALID_SIZE); - - esp_err_t ret; - - // Take Mutex and fill the CTRL request - BaseType_t taken = xSemaphoreTake(cdc_dev->ctrl_mux, pdMS_TO_TICKS(5000)); - if (!taken) { - return ESP_ERR_TIMEOUT; - } - usb_setup_packet_t *req = (usb_setup_packet_t *)(cdc_dev->ctrl_transfer->data_buffer); - uint8_t *start_of_data = (uint8_t *)req + sizeof(usb_setup_packet_t); - req->bmRequestType = bmRequestType; - req->bRequest = bRequest; - req->wValue = wValue; - req->wIndex = wIndex; - req->wLength = wLength; - - // For IN transfers we must transfer data ownership to CDC driver - const bool in_transfer = bmRequestType & USB_BM_REQUEST_TYPE_DIR_IN; - if (!in_transfer) { - memcpy(start_of_data, data, wLength); - } - - cdc_dev->ctrl_transfer->num_bytes = wLength + sizeof(usb_setup_packet_t); - ESP_GOTO_ON_ERROR( - usb_host_transfer_submit_control(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->ctrl_transfer), - unblock, TAG, "CTRL transfer failed"); - - taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context, pdMS_TO_TICKS(5000)); // This is a fixed timeout. Every CDC device should be able to respond to CTRL transfer in 5 seconds - if (!taken) { - // Transfer was not finished, error in USB LIB. Reset the endpoint - cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->ctrl_transfer); - ret = ESP_ERR_TIMEOUT; - goto unblock; - } - - ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Control transfer error"); - 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"); - - // For OUT transfers, we must transfer data ownership to user - if (in_transfer) { - memcpy(data, start_of_data, wLength); - } - ret = ESP_OK; - -unblock: - xSemaphoreGive(cdc_dev->ctrl_mux); - return ret; -} - -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) -{ - CDC_ACM_CHECK(cdc_dev, ESP_ERR_INVALID_ARG); - CDC_ACM_CHECK(cdc_dev->notif.intf_desc, ESP_ERR_NOT_SUPPORTED); - - uint8_t req_type = USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; - if (in_transfer) { - req_type |= USB_BM_REQUEST_TYPE_DIR_IN; - } else { - req_type |= USB_BM_REQUEST_TYPE_DIR_OUT; - } - return cdc_acm_host_send_custom_request((cdc_acm_dev_hdl_t) cdc_dev, req_type, request, value, cdc_dev->notif.intf_desc->bInterfaceNumber, data_len, data); -} - -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) -{ - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - - if (comm != NULL) *comm = cdc_dev->comm_protocol; - if (data != NULL) *data = cdc_dev->data_protocol; - return ESP_OK; -} diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h deleted file mode 100644 index 952ecb85cb..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h +++ /dev/null @@ -1,343 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include "usb/usb_host.h" -#include "usb_types_cdc.h" -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct cdc_dev_s *cdc_acm_dev_hdl_t; - -/** - * @brief Line Coding structure - * @see Table 17, USB CDC-PSTN specification rev. 1.2 - */ -typedef struct { - uint32_t dwDTERate; // in bits per second - uint8_t bCharFormat; // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits - uint8_t bParityType; // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space - uint8_t bDataBits; // 5, 6, 7, 8 or 16 -} __attribute__((packed)) cdc_acm_line_coding_t; - -/** - * @brief UART State Bitmap - * @see Table 31, USB CDC-PSTN specification rev. 1.2 - */ -typedef union { - struct { - uint16_t bRxCarrier : 1; // State of receiver carrier detection mechanism of device. This signal corresponds to V.24 signal 109 and RS-232 signal DCD. - uint16_t bTxCarrier : 1; // State of transmission carrier. This signal corresponds to V.24 signal 106 and RS-232 signal DSR. - uint16_t bBreak : 1; // State of break detection mechanism of the device. - uint16_t bRingSignal : 1; // State of ring signal detection of the device. - uint16_t bFraming : 1; // A framing error has occurred. - uint16_t bParity : 1; // A parity error has occurred. - uint16_t bOverRun : 1; // Received data has been discarded due to overrun in the device. - uint16_t reserved : 9; - }; - uint16_t val; -} cdc_acm_uart_state_t; - -/** - * @brief CDC-ACM Device Event types to upper layer - * - */ -typedef enum { - CDC_ACM_HOST_ERROR, - CDC_ACM_HOST_SERIAL_STATE, - CDC_ACM_HOST_NETWORK_CONNECTION, - CDC_ACM_HOST_DEVICE_DISCONNECTED -} cdc_acm_host_dev_event_t; - -/** - * @brief CDC-ACM Device Event data structure - * - */ -typedef struct { - cdc_acm_host_dev_event_t type; - union { - int error; //!< Error code from USB Host - cdc_acm_uart_state_t serial_state; //!< Serial (UART) state - bool network_connected; //!< Network connection event - cdc_acm_dev_hdl_t cdc_hdl; //!< Disconnection event - } data; -} cdc_acm_host_dev_event_data_t; - -/** - * @brief New USB device callback - * - * Provides already opened usb_dev, that will be closed after this callback returns. - * This is useful for peeking device's descriptors, e.g. peeking VID/PID and loading proper driver. - * - * @attention This callback is called from USB Host context, so the CDC device can't be opened here. - */ -typedef void (*cdc_acm_new_dev_callback_t)(usb_device_handle_t usb_dev); - -/** - * @brief Data receive callback type - */ -typedef void (*cdc_acm_data_callback_t)(uint8_t* data, size_t data_len, void *user_arg); - -/** - * @brief Device event callback type - * @see cdc_acm_host_dev_event_data_t - */ -typedef void (*cdc_acm_host_dev_callback_t)(const cdc_acm_host_dev_event_data_t *event, void *user_ctx); - -/** - * @brief Configuration structure of USB Host CDC-ACM driver - * - */ -typedef struct { - size_t driver_task_stack_size; /**< Stack size of the driver's task */ - unsigned driver_task_priority; /**< Priority of the driver's task */ - int xCoreID; /**< Core affinity of the driver's task */ - cdc_acm_new_dev_callback_t new_dev_cb; /**< New USB device connected callback. Can be NULL. */ -} cdc_acm_host_driver_config_t; - -/** - * @brief Configuration structure of CDC-ACM device - * - */ -typedef struct { - uint32_t connection_timeout_ms; /**< Timeout for USB device connection in [ms] */ - size_t out_buffer_size; /**< Maximum size of USB bulk out transfer, set to 0 for read-only devices */ - cdc_acm_host_dev_callback_t event_cb; /**< Device's event callback function. Can be NULL */ - cdc_acm_data_callback_t data_cb; /**< Device's data RX callback function. Can be NULL for write-only devices */ - void *user_arg; /**< User's argument that will be passed to the callbacks */ -} cdc_acm_host_device_config_t; - -/** - * @brief Install CDC-ACM driver - * - * - USB Host Library must already be installed before calling this function (via usb_host_install()) - * - This function should be called before calling any other CDC driver functions - * - * @param[in] driver_config Driver configuration structure. If set to NULL, a default configuration will be used. - * @return esp_err_t - */ -esp_err_t cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config); - -/** - * @brief Uninstall CDC-ACM driver - * - * - Users must ensure that all CDC devices must be closed via cdc_acm_host_close() before calling this function - * - * @return esp_err_t - */ -esp_err_t cdc_acm_host_uninstall(void); - -/** - * @brief Open CDC-ACM compliant device - * - * CDC-ACM compliant device must contain either an Interface Association Descriptor or CDC-Union descriptor, - * which are used for the driver's configuration. - * - * @param[in] vid Device's Vendor ID - * @param[in] pid Device's Product ID - * @param[in] interface_idx Index of device's interface used for CDC-ACM communication - * @param[in] dev_config Configuration structure of the device - * @param[out] cdc_hdl_ret CDC device handle - * @return esp_err_t - */ -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); - -/** - * @brief Open CDC-ACM non-compliant device - * - * CDC-ACM non-compliant device acts as CDC-ACM device but doesn't support all its features. - * User must provide the interface index that will be used (zero for non-composite devices). - * - * @param[in] vid Device's Vendor ID - * @param[in] pid Device's Product ID - * @param[in] interface_idx Index of device's interface used for CDC-ACM like communication - * @param[in] dev_config Configuration structure of the device - * @param[out] cdc_hdl_ret CDC device handle - * @return esp_err_t - */ -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); - -/** - * @brief Close CDC device and release its resources - * - * @note All in-flight transfers will be prematurely canceled. - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @return esp_err_t - */ -esp_err_t cdc_acm_host_close(cdc_acm_dev_hdl_t cdc_hdl); - -/** - * @brief Transmit data - blocking mode - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] data Data to be sent - * @param[in] data_len Data length - * @param[in] timeout_ms Timeout in [ms] - * @return esp_err_t - */ -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); - -/** - * @brief SetLineCoding function - * - * @see Chapter 6.3.10, USB CDC-PSTN specification rev. 1.2 - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] line_coding Line Coding structure - * @return esp_err_t - */ -esp_err_t cdc_acm_host_line_coding_set(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_line_coding_t *line_coding); - -/** - * @brief GetLineCoding function - * - * @see Chapter 6.3.11, USB CDC-PSTN specification rev. 1.2 - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[out] line_coding Line Coding structure to be filled - * @return esp_err_t - */ -esp_err_t cdc_acm_host_line_coding_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_acm_line_coding_t *line_coding); - -/** - * @brief SetControlLineState function - * - * @see Chapter 6.3.12, USB CDC-PSTN specification rev. 1.2 - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] dtr Indicates to DCE if DTE is present or not. This signal corresponds to V.24 signal 108/2 and RS-232 signal Data Terminal Ready. - * @param[in] rts Carrier control for half duplex modems. This signal corresponds to V.24 signal 105 and RS-232 signal Request To Send. - * @return esp_err_t - */ -esp_err_t cdc_acm_host_set_control_line_state(cdc_acm_dev_hdl_t cdc_hdl, bool dtr, bool rts); - -/** - * @brief SendBreak function - * - * This function will block until the duration_ms has passed. - * - * @see Chapter 6.3.13, USB CDC-PSTN specification rev. 1.2 - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] duration_ms Duration of the Break signal in [ms] - * @return esp_err_t - */ -esp_err_t cdc_acm_host_send_break(cdc_acm_dev_hdl_t cdc_hdl, uint16_t duration_ms); - -/** - * @brief Print device's descriptors - * - * Device and full Configuration descriptors are printed in human readable format to stdout. - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - */ -void cdc_acm_host_desc_print(cdc_acm_dev_hdl_t cdc_hdl); - -/** - * @brief Get protocols defined in USB-CDC interface descriptors - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[out] comm Communication protocol - * @param[out] data Data protocol - * @return esp_err_t - */ -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); - -/** - * @brief Send command to CTRL endpoint - * - * Sends Control transfer as described in USB specification chapter 9. - * This function can be used by device drivers that use custom/vendor specific commands. - * These commands can either extend or replace commands defined in USB CDC-PSTN specification rev. 1.2. - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] bmRequestType Field of USB control request - * @param[in] bRequest Field of USB control request - * @param[in] wValue Field of USB control request - * @param[in] wIndex Field of USB control request - * @param[in] wLength Field of USB control request - * @param[inout] data Field of USB control request - * @return esp_err_t - */ -esp_err_t cdc_acm_host_send_custom_request(cdc_acm_dev_hdl_t cdc_hdl, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *data); - -#ifdef __cplusplus -} -class CdcAcmDevice -{ -public: - // Operators - CdcAcmDevice() : cdc_hdl(NULL){}; - ~CdcAcmDevice() - { - // Close CDC-ACM device, if it wasn't explicitly closed - if (this->cdc_hdl != NULL) { - this->close(); - } - } - - inline esp_err_t tx_blocking(uint8_t *data, size_t len, uint32_t timeout_ms = 100) - { - return cdc_acm_host_data_tx_blocking(this->cdc_hdl, data, len, timeout_ms); - } - - inline esp_err_t open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t* dev_config) - { - return cdc_acm_host_open(vid, pid, interface_idx, dev_config, &this->cdc_hdl); - } - - inline esp_err_t open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t* dev_config) - { - return cdc_acm_host_open_vendor_specific(vid, pid, interface_idx, dev_config, &this->cdc_hdl); - } - - inline esp_err_t close() - { - esp_err_t err = cdc_acm_host_close(this->cdc_hdl); - if (err == ESP_OK) { - this->cdc_hdl = NULL; - } - return err; - } - - inline esp_err_t line_coding_get(cdc_acm_line_coding_t *line_coding) - { - return cdc_acm_host_line_coding_get(this->cdc_hdl, line_coding); - } - - inline esp_err_t line_coding_set(cdc_acm_line_coding_t *line_coding) - { - return cdc_acm_host_line_coding_set(this->cdc_hdl, line_coding); - } - - inline esp_err_t set_control_line_state(bool dtr, bool rts) - { - return cdc_acm_host_set_control_line_state(this->cdc_hdl, dtr, rts); - } - - inline esp_err_t send_break(uint16_t duration_ms) - { - return cdc_acm_host_send_break(this->cdc_hdl, duration_ms); - } - - inline esp_err_t send_custom_request(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *data) - { - return cdc_acm_host_send_custom_request(this->cdc_hdl, bmRequestType, bRequest, wValue, wIndex, wLength, data); - } - -private: - CdcAcmDevice(const CdcAcmDevice &Copy); - CdcAcmDevice &operator= (const CdcAcmDevice &Copy); - bool operator== (const CdcAcmDevice ¶m) const; - bool operator!= (const CdcAcmDevice ¶m) const; - cdc_acm_dev_hdl_t cdc_hdl; -}; -#endif diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/usb_types_cdc.h b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/usb_types_cdc.h deleted file mode 100644 index bd8b1d07f7..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/usb_types_cdc.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once -#include - -/** - * @brief USB CDC Descriptor Subtypes - * - * @see Table 13, USB CDC specification rev. 1.2 - */ -typedef enum { - USB_CDC_DESC_SUBTYPE_HEADER = 0x00, // Header Functional Descriptor - USB_CDC_DESC_SUBTYPE_CALL = 0x01, // Call Management Functional Descriptor - USB_CDC_DESC_SUBTYPE_ACM = 0x02, // Abstract Control Management Functional Descriptor - USB_CDC_DESC_SUBTYPE_DLM = 0x03, // Direct Line Management Functional Descriptor - USB_CDC_DESC_SUBTYPE_TEL_RINGER = 0x04, // Telephone Ringer Functional Descriptor - USB_CDC_DESC_SUBTYPE_TEL_CLSR = 0x05, // Telephone Call and Line State Reporting Capabilities Functional Descriptor - USB_CDC_DESC_SUBTYPE_UNION = 0x06, // Union Functional Descriptor - USB_CDC_DESC_SUBTYPE_COUNTRY = 0x07, // Country Selection Functional Descriptor - USB_CDC_DESC_SUBTYPE_TEL_MODE = 0x08, // Telephone Operational Modes Functional Descriptor - USB_CDC_DESC_SUBTYPE_TERMINAL = 0x09, // USB Terminal - USB_CDC_DESC_SUBTYPE_NCHT = 0x0A, // Network Channel Terminal - USB_CDC_DESC_SUBTYPE_PROTOCOL = 0x08, // Protocol Unit - USB_CDC_DESC_SUBTYPE_EXTENSION = 0x0C, // Extension Unit - USB_CDC_DESC_SUBTYPE_MULTI_CHAN = 0x0D, // Multi-Channel Management Functional Descriptor - USB_CDC_DESC_SUBTYPE_CAPI = 0x0E, // CAPI Control - USB_CDC_DESC_SUBTYPE_ETH = 0x0F, // Ethernet Networking - USB_CDC_DESC_SUBTYPE_ATM = 0x10, // ATM Networking - USB_CDC_DESC_SUBTYPE_WHANDSET = 0x11, // Wireless Handset Control Model Functional Descriptor - USB_CDC_DESC_SUBTYPE_MDLM = 0x12, // Mobile Direct Line Model - USB_CDC_DESC_SUBTYPE_MDLM_DETAIL = 0x13, // MDLM Detail - USB_CDC_DESC_SUBTYPE_DMM = 0x14, // Device Management Model - USB_CDC_DESC_SUBTYPE_OBEX = 0x15, // OBEX Functional - USB_CDC_DESC_SUBTYPE_COMMAND_SET = 0x16, // Command Set - USB_CDC_DESC_SUBTYPE_COMMAND_SET_DETAIL = 0x17, // Command Set Detail Functional Descriptor - USB_CDC_DESC_SUBTYPE_TEL_CM = 0x18, // Telephone Control Model Functional Descriptor - USB_CDC_DESC_SUBTYPE_OBEX_SERVICE = 0x19, // OBEX Service Identifier Functional Descriptor - USB_CDC_DESC_SUBTYPE_NCM = 0x1A // NCM Functional Descriptor -} __attribute__((packed)) cdc_desc_subtype_t; - -/** - * @brief USB CDC Subclass codes - * - * @see Table 4, USB CDC specification rev. 1.2 - */ -typedef enum { - USB_CDC_SUBCLASS_DLCM = 0x01, // Direct Line Control Model - USB_CDC_SUBCLASS_ACM = 0x02, // Abstract Control Model - USB_CDC_SUBCLASS_TCM = 0x03, // Telephone Control Model - USB_CDC_SUBCLASS_MCHCM = 0x04, // Multi-Channel Control Model - USB_CDC_SUBCLASS_CAPI = 0x05, // CAPI Control Model - USB_CDC_SUBCLASS_ECM = 0x06, // Ethernet Networking Control Model - USB_CDC_SUBCLASS_ATM = 0x07, // ATM Networking Model - USB_CDC_SUBCLASS_HANDSET = 0x08, // Wireless Handset Control Model - USB_CDC_SUBCLASS_DEV_MAN = 0x09, // Device Management - USB_CDC_SUBCLASS_MOBILE = 0x0A, // Mobile Direct Line Model - USB_CDC_SUBCLASS_OBEX = 0x0B, // OBEX - USB_CDC_SUBCLASS_EEM = 0x0C, // Ethernet Emulation Model - USB_CDC_SUBCLASS_NCM = 0x0D // Network Control Model -} __attribute__((packed)) cdc_subclass_t; - -/** - * @brief USB CDC Communications Protocol Codes - * - * @see Table 5, USB CDC specification rev. 1.2 - */ -typedef enum { - USB_CDC_COMM_PROTOCOL_NONE = 0x00, // No class specific protocol required - USB_CDC_COMM_PROTOCOL_V250 = 0x01, // AT Commands: V.250 etc - USB_CDC_COMM_PROTOCOL_PCAA = 0x02, // AT Commands defined by PCCA-101 - USB_CDC_COMM_PROTOCOL_PCAA_A = 0x03, // AT Commands defined by PCAA-101 & Annex O - USB_CDC_COMM_PROTOCOL_GSM = 0x04, // AT Commands defined by GSM 07.07 - USB_CDC_COMM_PROTOCOL_3GPP = 0x05, // AT Commands defined by 3GPP 27.007 - USB_CDC_COMM_PROTOCOL_TIA = 0x06, // AT Commands defined by TIA for CDMA - USB_CDC_COMM_PROTOCOL_EEM = 0x07, // Ethernet Emulation Model - USB_CDC_COMM_PROTOCOL_EXT = 0xFE, // External Protocol: Commands defined by Command Set Functional Descriptor - USB_CDC_COMM_PROTOCOL_VENDOR = 0xFF // Vendor-specific -} __attribute__((packed)) cdc_comm_protocol_t; - -/** - * @brief USB CDC Data Protocol Codes - * - * @see Table 7, USB CDC specification rev. 1.2 - */ -typedef enum { - USB_CDC_DATA_PROTOCOL_NONE = 0x00, // No class specific protocol required - USB_CDC_DATA_PROTOCOL_NCM = 0x01, // Network Transfer Block - USB_CDC_DATA_PROTOCOL_I430 = 0x30, // Physical interface protocol for ISDN BRI - USB_CDC_DATA_PROTOCOL_HDLC = 0x31, // HDLC - USB_CDC_DATA_PROTOCOL_Q921M = 0x50, // Management protocol for Q.921 data link protocol - USB_CDC_DATA_PROTOCOL_Q921 = 0x51, // Data link protocol for Q.931 - USB_CDC_DATA_PROTOCOL_Q921TM = 0x52, // TEI-multiplexor for Q.921 data link protocol - USB_CDC_DATA_PROTOCOL_V42BIS = 0x90, // Data compression procedures - USB_CDC_DATA_PROTOCOL_Q931 = 0x91, // Euro-ISDN protocol control - USB_CDC_DATA_PROTOCOL_V120 = 0x92, // V.24 rate adaptation to ISDN - USB_CDC_DATA_PROTOCOL_CAPI = 0x93, // CAPI Commands - USB_CDC_DATA_PROTOCOL_VENDOR = 0xFF // Vendor-specific -} __attribute__((packed)) cdc_data_protocol_t; - -/** - * @brief USB CDC Request Codes - * - * @see Table 19, USB CDC specification rev. 1.2 - */ -typedef enum { - USB_CDC_REQ_SEND_ENCAPSULATED_COMMAND = 0x00, - USB_CDC_REQ_GET_ENCAPSULATED_RESPONSE = 0x01, - USB_CDC_REQ_SET_COMM_FEATURE = 0x02, - USB_CDC_REQ_GET_COMM_FEATURE = 0x03, - USB_CDC_REQ_CLEAR_COMM_FEATURE = 0x04, - USB_CDC_REQ_SET_AUX_LINE_STATE = 0x10, - USB_CDC_REQ_SET_HOOK_STATE = 0x11, - USB_CDC_REQ_PULSE_SETUP = 0x12, - USB_CDC_REQ_SEND_PULSE = 0x13, - USB_CDC_REQ_SET_PULSE_TIME = 0x14, - USB_CDC_REQ_RING_AUX_JACK = 0x15, - USB_CDC_REQ_SET_LINE_CODING = 0x20, - USB_CDC_REQ_GET_LINE_CODING = 0x21, - USB_CDC_REQ_SET_CONTROL_LINE_STATE = 0x22, - USB_CDC_REQ_SEND_BREAK = 0x23, - USB_CDC_REQ_SET_RINGER_PARMS = 0x30, - USB_CDC_REQ_GET_RINGER_PARMS = 0x31, - USB_CDC_REQ_SET_OPERATION_PARMS = 0x32, - USB_CDC_REQ_GET_OPERATION_PARMS = 0x33, - USB_CDC_REQ_SET_LINE_PARMS = 0x34, - USB_CDC_REQ_GET_LINE_PARMS = 0x35, - USB_CDC_REQ_DIAL_DIGITS = 0x36, - USB_CDC_REQ_SET_UNIT_PARAMETER = 0x37, - USB_CDC_REQ_GET_UNIT_PARAMETER = 0x38, - USB_CDC_REQ_CLEAR_UNIT_PARAMETER = 0x39, - USB_CDC_REQ_GET_PROFILE = 0x3A, - USB_CDC_REQ_SET_ETHERNET_MULTICAST_FILTERS = 0x40, - USB_CDC_REQ_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41, - USB_CDC_REQ_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42, - USB_CDC_REQ_SET_ETHERNET_PACKET_FILTER = 0x43, - USB_CDC_REQ_GET_ETHERNET_STATISTIC = 0x44, - USB_CDC_REQ_SET_ATM_DATA_FORMAT = 0x50, - USB_CDC_REQ_GET_ATM_DEVICE_STATISTICS = 0x51, - USB_CDC_REQ_SET_ATM_DEFAULT_VC = 0x52, - USB_CDC_REQ_GET_ATM_VC_STATISTICS = 0x53, - USB_CDC_REQ_GET_NTB_PARAMETERS = 0x80, - USB_CDC_REQ_GET_NET_ADDRESS = 0x81, - USB_CDC_REQ_SET_NET_ADDRESS = 0x82, - USB_CDC_REQ_GET_NTB_FORMAT = 0x83, - USB_CDC_REQ_SET_NTB_FORMAT = 0x84, - USB_CDC_REQ_GET_NTB_INPUT_SIZE = 0x85, - USB_CDC_REQ_SET_NTB_INPUT_SIZE = 0x86, - USB_CDC_REQ_GET_MAX_DATAGRAM_SIZE = 0x87, - USB_CDC_REQ_SET_MAX_DATAGRAM_SIZE = 0x88, - USB_CDC_REQ_GET_CRC_MODE = 0x89, - USB_CDC_REQ_SET_CRC_MODE = 0x8A -} __attribute__((packed)) cdc_request_code_t; - -/** - * @brief USB CDC Notification Codes - * - * @see Table 20, USB CDC specification rev. 1.2 - */ -typedef enum { - USB_CDC_NOTIF_NETWORK_CONNECTION = 0x00, - USB_CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, - USB_CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08, - USB_CDC_NOTIF_RING_DETECT = 0x09, - USB_CDC_NOTIF_SERIAL_STATE = 0x20, - USB_CDC_NOTIF_CALL_STATE_CHANGE = 0x28, - USB_CDC_NOTIF_LINE_STATE_CHANGE = 0x29, - USB_CDC_NOTIF_CONNECTION_SPEED_CHANGE = 0x2A -} __attribute__((packed)) cdc_notification_code_t; - -typedef struct { - uint8_t bmRequestType; - cdc_notification_code_t bNotificationCode; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; - uint8_t Data[]; -} __attribute__((packed)) cdc_notification_t; - -/** - * @brief USB CDC Header Functional Descriptor - * - * @see Table 15, USB CDC specification rev. 1.2 - */ -typedef struct { - uint8_t bFunctionLength; - const uint8_t bDescriptorType; // Upper nibble: CDC code 0x02, Lower nibble: intf/ep descriptor type 0x04/0x05 - const cdc_desc_subtype_t bDescriptorSubtype; - uint16_t bcdCDC; // CDC version as binary-coded decimal. This driver is written for version 1.2 -} __attribute__((packed)) cdc_header_desc_t; - -/** - * @brief USB CDC Union Functional Descriptor - * - * @see Table 16, USB CDC specification rev. 1.2 - */ -typedef struct { - uint8_t bFunctionLength; - const uint8_t bDescriptorType; // Upper nibble: CDC code 0x02, Lower nibble: intf/ep descriptor type 0x04/0x05 - const cdc_desc_subtype_t bDescriptorSubtype; - const uint8_t bControlInterface; // Master/controlling interface - uint8_t bSubordinateInterface[]; // Slave/subordinate interfaces -} __attribute__((packed)) cdc_union_desc_t; diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/CMakeLists.txt b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/CMakeLists.txt deleted file mode 100644 index 0345c55e76..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "test_cdc_acm_host.c" "usb_device.c" - INCLUDE_DIRS "." - REQUIRES cdc_acm_host tinyusb unity) diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c deleted file mode 100644 index 1ba2bcf231..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include "soc/soc_caps.h" -#if SOC_USB_OTG_SUPPORTED - -#include -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "esp_err.h" - -#include "esp_private/usb_phy.h" -#include "usb/usb_host.h" -#include "usb/cdc_acm_host.h" -#include - -#include "esp_intr_alloc.h" - -#include "unity.h" -#include "soc/usb_wrap_struct.h" - -static uint8_t tx_buf[] = "HELLO"; -static uint8_t tx_buf2[] = "WORLD"; -static int nb_of_responses; -static int nb_of_responses2; -static usb_phy_handle_t phy_hdl = NULL; - -static void force_conn_state(bool connected, TickType_t delay_ticks) -{ - TEST_ASSERT_NOT_EQUAL(NULL, phy_hdl); - if (delay_ticks > 0) { - //Delay of 0 ticks causes a yield. So skip if delay_ticks is 0. - vTaskDelay(delay_ticks); - } - ESP_ERROR_CHECK(usb_phy_action(phy_hdl, (connected) ? USB_PHY_ACTION_HOST_ALLOW_CONN : USB_PHY_ACTION_HOST_FORCE_DISCONN)); -} - -void usb_lib_task(void *arg) -{ - // Initialize the internal USB PHY to connect to the USB OTG peripheral. We manually install the USB PHY for testing - usb_phy_config_t phy_config = { - .controller = USB_PHY_CTRL_OTG, - .target = USB_PHY_TARGET_INT, - .otg_mode = USB_OTG_MODE_HOST, - .otg_speed = USB_PHY_SPEED_UNDEFINED, //In Host mode, the speed is determined by the connected device - .gpio_conf = NULL, - }; - TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_hdl)); - // Install USB Host driver. Should only be called once in entire application - const usb_host_config_t host_config = { - .skip_phy_setup = true, - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - TEST_ASSERT_EQUAL(ESP_OK, usb_host_install(&host_config)); - printf("USB Host installed\n"); - xTaskNotifyGive(arg); - - bool all_clients_gone = false; - bool all_dev_free = false; - while (!all_clients_gone || !all_dev_free) { - // Start handling system events - uint32_t event_flags; - usb_host_lib_handle_events(portMAX_DELAY, &event_flags); - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { - printf("No more clients\n"); - usb_host_device_free_all(); - all_clients_gone = true; - } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - printf("All devices freed\n"); - all_dev_free = true; - } - } - - // Clean up USB Host - vTaskDelay(10); // Short delay to allow clients clean-up - TEST_ASSERT_EQUAL(ESP_OK, usb_host_uninstall()); - TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_hdl)); //Tear down USB PHY - phy_hdl = NULL; - vTaskDelete(NULL); -} - -void test_install_cdc_driver(void) -{ - // Create a task that will handle USB library events - TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(usb_lib_task, "usb_lib", 4*4096, xTaskGetCurrentTaskHandle(), 10, NULL, 0)); - ulTaskNotifyTake(false, 1000); - - printf("Installing CDC-ACM driver\n"); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_install(NULL)); -} - -/* ------------------------------- Callbacks -------------------------------- */ -static void handle_rx(uint8_t *data, size_t data_len, void *arg) -{ - printf("Data received\n"); - nb_of_responses++; - TEST_ASSERT_EQUAL_STRING_LEN(data, arg, data_len); -} - -static void handle_rx2(uint8_t *data, size_t data_len, void *arg) -{ - printf("Data received 2\n"); - nb_of_responses2++; - TEST_ASSERT_EQUAL_STRING_LEN(data, arg, data_len); -} - -static void notif_cb(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) -{ - switch (event->type) { - case CDC_ACM_HOST_ERROR: - printf("Error event %d\n", event->data.error); - break; - case CDC_ACM_HOST_SERIAL_STATE: - break; - case CDC_ACM_HOST_NETWORK_CONNECTION: - break; - case CDC_ACM_HOST_DEVICE_DISCONNECTED: - printf("Disconnection event\n"); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(event->data.cdc_hdl)); - xTaskNotifyGive(user_ctx); - break; - default: - assert(false); - } -} - -static bool new_dev_cb_called = false; -static void new_dev_cb(usb_device_handle_t usb_dev) { - new_dev_cb_called = true; - const usb_config_desc_t *config_desc; - const usb_device_desc_t *device_desc; - - // Get descriptors - TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_device_descriptor(usb_dev, &device_desc)); - TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_active_config_descriptor(usb_dev, &config_desc)); - - printf("New device connected. VID = 0x%04X PID = %04X\n", device_desc->idVendor, device_desc->idProduct); -} - -/* Basic test to check CDC communication: - * open/read/write/close device - * CDC-ACM specific commands: set/get_line_coding, set_control_line_state */ -TEST_CASE("read_write", "[cdc_acm]") -{ - nb_of_responses = 0; - cdc_acm_dev_hdl_t cdc_dev = NULL; - - test_install_cdc_driver(); - - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 500, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx, - .user_arg = tx_buf, - }; - - printf("Opening CDC-ACM device\n"); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); // 0x303A:0x4002 (TinyUSB Dual CDC device) - TEST_ASSERT_NOT_NULL(cdc_dev); - cdc_acm_host_desc_print(cdc_dev); - vTaskDelay(100); - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000)); - vTaskDelay(100); // Wait until responses are processed - - // We sent two messages, should get two responses - TEST_ASSERT_EQUAL(2, nb_of_responses); - - cdc_acm_line_coding_t line_coding_get; - const cdc_acm_line_coding_t line_coding_set = { - .dwDTERate = 9600, - .bDataBits = 7, - .bParityType = 1, - .bCharFormat = 1, - }; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_line_coding_set(cdc_dev, &line_coding_set)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_line_coding_get(cdc_dev, &line_coding_get)); - TEST_ASSERT_EQUAL_MEMORY(&line_coding_set, &line_coding_get, sizeof(cdc_acm_line_coding_t)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_set_control_line_state(cdc_dev, true, false)); - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - - vTaskDelay(20); //Short delay to allow task to be cleaned up -} - -/* Test communication with multiple CDC-ACM devices from one thread */ -TEST_CASE("multiple_devices", "[cdc_acm]") -{ - nb_of_responses = 0; - nb_of_responses2 = 0; - - test_install_cdc_driver(); - - printf("Opening 2 CDC-ACM devices\n"); - cdc_acm_dev_hdl_t cdc_dev1, cdc_dev2; - cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 1000, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx, - .user_arg = tx_buf, - }; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev1)); // 0x303A:0x4002 (TinyUSB Dual CDC device) - dev_config.data_cb = handle_rx2; - dev_config.user_arg = tx_buf2; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 2, &dev_config, &cdc_dev2)); // 0x303A:0x4002 (TinyUSB Dual CDC device) - TEST_ASSERT_NOT_NULL(cdc_dev1); - TEST_ASSERT_NOT_NULL(cdc_dev2); - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev1, tx_buf, sizeof(tx_buf), 1000)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev2, tx_buf2, sizeof(tx_buf2), 1000)); - - vTaskDelay(100); // Wait for RX callbacks - - // We sent two messages, should get two responses - TEST_ASSERT_EQUAL(1, nb_of_responses); - TEST_ASSERT_EQUAL(1, nb_of_responses2); - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev1)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev2)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - - //Short delay to allow task to be cleaned up - vTaskDelay(20); -} - -#define MULTIPLE_THREADS_TRANSFERS_NUM 5 -#define MULTIPLE_THREADS_TASKS_NUM 4 -void tx_task(void *arg) -{ - cdc_acm_dev_hdl_t cdc_dev = (cdc_acm_dev_hdl_t) arg; - // Send multiple transfers to make sure that some of them will run at the same time - for (int i = 0; i < MULTIPLE_THREADS_TRANSFERS_NUM; i++) { - // BULK endpoints - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000)); - - // CTRL endpoints - cdc_acm_line_coding_t line_coding_get; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_line_coding_get(cdc_dev, &line_coding_get)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_set_control_line_state(cdc_dev, true, false)); - } - vTaskDelete(NULL); -} - -/** - * @brief Multiple threads test - * - * In this test, one CDC device is accessed from multiple threads. - * It has to be opened/closed just once, though. - */ -TEST_CASE("multiple_threads", "[cdc_acm]") -{ - nb_of_responses = 0; - cdc_acm_dev_hdl_t cdc_dev; - test_install_cdc_driver(); - - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 5000, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx, - .user_arg = tx_buf, - }; - - printf("Opening CDC-ACM device\n"); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); // 0x303A:0x4002 (TinyUSB Dual CDC device) - TEST_ASSERT_NOT_NULL(cdc_dev); - - // Create two tasks that will try to access cdc_dev - for (int i = 0; i < MULTIPLE_THREADS_TASKS_NUM; i++) { - TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(tx_task, "CDC TX", 4096, cdc_dev, i + 3, NULL)); - } - - // Wait until all tasks finish - vTaskDelay(pdMS_TO_TICKS(500)); - TEST_ASSERT_EQUAL(MULTIPLE_THREADS_TASKS_NUM * MULTIPLE_THREADS_TRANSFERS_NUM, nb_of_responses); - - // Clean-up - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - vTaskDelay(20); -} - -/* Test CDC driver reaction to USB device sudden disconnection */ -TEST_CASE("sudden_disconnection", "[cdc_acm]") -{ - test_install_cdc_driver(); - - cdc_acm_dev_hdl_t cdc_dev; - cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 1000, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx - }; - dev_config.user_arg = xTaskGetCurrentTaskHandle(); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - TEST_ASSERT_NOT_NULL(cdc_dev); - - force_conn_state(false, pdMS_TO_TICKS(10)); // Simulate device disconnection - TEST_ASSERT_EQUAL(1, ulTaskNotifyTake(false, pdMS_TO_TICKS(100))); // Notify will succeed only if CDC_ACM_HOST_DEVICE_DISCONNECTED notification was generated - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - vTaskDelay(20); //Short delay to allow task to be cleaned up -} - -/** - * @brief CDC-ACM error handling test - * - * There are multiple erroneous scenarios checked in this test: - * - * -# Install CDC-ACM driver without USB Host - * -# Open device without installed driver - * -# Uninstall driver before installing it - * -# Open non-existent device - * -# Open the same device twice - * -# Uninstall driver with open devices - * -# Send data that is too large - * -# Send unsupported CDC request - * -# Write to read-only device - */ -TEST_CASE("error_handling", "[cdc_acm]") -{ - cdc_acm_dev_hdl_t cdc_dev; - cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 500, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx - }; - - // Install CDC-ACM driver without USB Host - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_install(NULL)); - - // Open device without installed driver - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - - // Uninstall driver before installing it - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_uninstall()); - - // Properly install USB and CDC drivers - test_install_cdc_driver(); - - // Open non-existent device - TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, cdc_acm_host_open(0x303A, 0x1234, 0, &dev_config, &cdc_dev)); // 0x303A:0x1234 this device is not connected to USB Host - TEST_ASSERT_NULL(cdc_dev); - - // Open regular device - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - TEST_ASSERT_NOT_NULL(cdc_dev); - - // Open one CDC-ACM device twice - cdc_acm_dev_hdl_t cdc_dev_test; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev_test)); - TEST_ASSERT_NULL(cdc_dev_test); - - // Uninstall driver with open devices - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_uninstall()); - - // Send data that is too large and NULL data - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, 1024, 1000)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, cdc_acm_host_data_tx_blocking(cdc_dev, NULL, 10, 1000)); - - // Change mode to read-only and try to write to it - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - dev_config.out_buffer_size = 0; // Read-only device - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - TEST_ASSERT_NOT_NULL(cdc_dev); - TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000)); - - // Send unsupported CDC request (TinyUSB accepts SendBreak command, eventhough it doesn't support it) - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_send_break(cdc_dev, 100)); - - // Clean-up - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - vTaskDelay(20); -} - -TEST_CASE("custom_command", "[cdc_acm]") -{ - test_install_cdc_driver(); - - // Open device with only CTRL endpoint (endpoint no 0) - cdc_acm_dev_hdl_t cdc_dev; - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 500, - .out_buffer_size = 0, - .event_cb = notif_cb, - .data_cb = NULL - }; - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - TEST_ASSERT_NOT_NULL(cdc_dev); - - // Corresponds to command: Set Control Line State, DTR on, RTS off - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_send_custom_request(cdc_dev, 0x21, 34, 1, 0, 0, NULL)); - - // Clean-up - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - vTaskDelay(20); -} - -TEST_CASE("new_device_connection", "[cdc_acm]") -{ - // Create a task that will handle USB library events - TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(usb_lib_task, "usb_lib", 4*4096, xTaskGetCurrentTaskHandle(), 10, NULL, 0)); - ulTaskNotifyTake(false, 1000); - - printf("Installing CDC-ACM driver\n"); - const cdc_acm_host_driver_config_t driver_config = { - .driver_task_priority = 11, - .driver_task_stack_size = 2048, - .xCoreID = 0, - .new_dev_cb = new_dev_cb, - }; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_install(&driver_config)); - - vTaskDelay(80); - TEST_ASSERT_TRUE_MESSAGE(new_dev_cb_called, "New device callback was not called\n"); - - // Clean-up - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - vTaskDelay(20); -} - -/* Following test case implements dual CDC-ACM USB device that can be used as mock device for CDC-ACM Host tests */ -void run_usb_dual_cdc_device(void); -TEST_CASE("mock_device_app", "[cdc_acm_device][ignore]") -{ - run_usb_dual_cdc_device(); - while (1) { - vTaskDelay(10); - } -} - -#endif diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/usb_device.c b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/usb_device.c deleted file mode 100644 index 86c645e2c7..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/usb_device.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include "sdkconfig.h" -#include "tinyusb.h" -#include "tusb_cdc_acm.h" - -static uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; -void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event) -{ - size_t rx_size = 0; - /* read and write back */ - ESP_ERROR_CHECK(tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size)); - tinyusb_cdcacm_write_queue(itf, buf, rx_size); - tinyusb_cdcacm_write_flush(itf, 0); -} - -static const tusb_desc_device_t cdc_device_descriptor = { - .bLength = sizeof(cdc_device_descriptor), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = USB_ESPRESSIF_VID, - .idProduct = 0x4002, - .bcdDevice = 0x0100, - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - .bNumConfigurations = 0x01 -}; - -const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + 2 * TUD_CDC_DESC_LEN; -static const uint8_t cdc_desc_configuration[] = { - TUD_CONFIG_DESCRIPTOR(1, 4, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, 64), - TUD_CDC_DESCRIPTOR(2, 4, 0x83, 8, 0x04, 0x84, 64), -}; - -void run_usb_dual_cdc_device(void) -{ - const tinyusb_config_t tusb_cfg = { - .device_descriptor = &cdc_device_descriptor, - .configuration_descriptor = cdc_desc_configuration - }; - ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); - - tinyusb_config_cdcacm_t acm_cfg = { - .usb_dev = TINYUSB_USBDEV_0, - .cdc_port = TINYUSB_CDC_ACM_0, - .rx_unread_buf_sz = 64, - .callback_rx = &tinyusb_cdc_rx_callback, - .callback_rx_wanted_char = NULL, - .callback_line_state_changed = NULL, - .callback_line_coding_changed = NULL - }; - - ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); -#if (CONFIG_TINYUSB_CDC_COUNT > 1) - acm_cfg.cdc_port = TINYUSB_CDC_ACM_1; - ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); -#endif - - printf("USB initialization DONE\n"); -}