add example code to pio test projet

This commit is contained in:
Clement_B 2025-01-28 12:11:56 +01:00
parent 61fce0f5da
commit 88207a7632
23 changed files with 3092 additions and 3545 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
managed_components

View File

@ -1,27 +0,0 @@
## 2.0.3
- Added `cdc_acm_host_cdc_desc_get()` function that enables users to get CDC functional descriptors of the device
- Fixed closing of a CDC device from multiple threads
- Fixed reaction on TX transfer timeout (https://github.com/espressif/esp-protocols/issues/514)
## 2.0.2
- Return an error if the selected interface does not have IN and OUT bulk endpoints
## 2.0.1
- Added support for USB "triple null" devices, which use USB interface association descriptors, but have Device Class, Device Subclass, and Device Protocol all set to 0x00, instead of 0xEF, 0x02, and 0x01 respectively. USB Standard reference: https://www.usb.org/defined-class-codes, Base Class 00h (Device) section.
## 2.0.0
- New function `cdc_acm_host_register_new_dev_callback`. This allows you to get New Device notifications even if you use the default driver.
- Receive buffer has configurable size. This is useful if you expect data transfers larger then Maximum Packet Size.
- Receive buffer has 'append' function. In the Data Received callback you can signal that you wait for more data and the current data were not yet processed. In this case, the CDC driver appends new data to the already received data. This is especially useful if the upper layer messages consist of multiple USB transfers and you don't want to waste more RAM and CPU copying the data around.
## 1.0.4
- C++ methods are now virtual to allow derived classes to override them.
## 1.0.0
- Initial version

View File

@ -1,15 +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}
)

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,49 +0,0 @@
# USB Host CDC-ACM Class Driver
[![Component Registry](https://components.espressif.com/components/espressif/usb_host_cdc_acm/badge.svg)](https://components.espressif.com/components/espressif/usb_host_cdc_acm)
This component 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 or cellular modems)
### 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 or time-out
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](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc/cdc_acm_host)
- For an example with Virtual COM devices, refer to [cdc_acm_vcp](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc/cdc_acm_vcp)
- For examples with [esp_modem](https://components.espressif.com/components/espressif/esp_modem), refer to [esp_modem examples](https://github.com/espressif/esp-protocols/tree/master/components/esp_modem/examples)

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
dependencies:
idf:
version: '>=4.4'
description: USB Host CDC-ACM driver
repository: git://github.com/espressif/esp-usb.git
repository_info:
commit_sha: d938736c500397f92fc8fe8881539e33a9edc93b
path: host/class/cdc/usb_host_cdc_acm
tags:
- usb
- usb_host
- cdc
targets:
- esp32s2
- esp32s3
- esp32p4
url: https://github.com/espressif/esp-usb/tree/master/host/class/cdc/usb_host_cdc_acm
version: 2.0.3

View File

@ -1,375 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#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
*
* @param[in] data Pointer to received data
* @param[in] data_len Length of received data in bytes
* @param[in] user_arg User's argument passed to open function
* @return true Received data was processed -> Flush RX buffer
* @return false Received data was NOT processed -> Append new data to the buffer
*/
typedef bool (*cdc_acm_data_callback_t)(const uint8_t *data, size_t data_len, void *user_arg);
/**
* @brief Device event callback type
*
* @param[in] event Event structure
* @param[in] user_arg User's argument passed to open function
*/
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 */
size_t in_buffer_size; /**< Maximum size of USB bulk in transfer */
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 Register new USB device callback
*
* The callback will be called for every new USB device, not just CDC-ACM class.
*
* @param[in] new_dev_cb New device callback function
* @return esp_err_t
*/
esp_err_t cdc_acm_host_register_new_dev_callback(cdc_acm_new_dev_callback_t new_dev_cb);
/**
* @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[in] cdc_hdl CDC handle obtained from cdc_acm_host_open()
* @return
* - ESP_OK: Success - device closed
* - ESP_ERR_INVALID_STATE: cdc_hdl is NULL or the CDC driver is not installed
*/
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 Get CDC functional descriptor
*
* @param cdc_hdl CDC handle obtained from cdc_acm_host_open()
* @param[in] desc_type Type of functional descriptor
* @param[out] desc_out Pointer to the required descriptor
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_ARG: Invalid device or descriptor type
* - ESP_ERR_NOT_FOUND: The required descriptor is not present in the device
*/
esp_err_t cdc_acm_host_cdc_desc_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_desc_subtype_t desc_type, const usb_standard_desc_t **desc_out);
/**
* @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) {};
virtual ~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()
{
const esp_err_t err = cdc_acm_host_close(this->cdc_hdl);
if (err == ESP_OK) {
this->cdc_hdl = NULL;
}
return err;
}
virtual inline esp_err_t line_coding_get(cdc_acm_line_coding_t *line_coding) const
{
return cdc_acm_host_line_coding_get(this->cdc_hdl, line_coding);
}
virtual 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);
}
virtual 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);
}
virtual 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 &operator= (const CdcAcmDevice &Copy);
bool operator== (const CdcAcmDevice &param) const;
bool operator!= (const CdcAcmDevice &param) const;
cdc_acm_dev_hdl_t cdc_hdl;
};
#endif

View File

@ -1,248 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <inttypes.h>
/**
* @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
USB_CDC_DESC_SUBTYPE_MAX
} __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;
/**
* @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;

View File

@ -1,20 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS
../../esp_modem_usb_dte
../../usb_host_cdc_acm
../../usb_host_ch34x_vcp
../../usb_host_cp210x_vcp
../../usb_host_ftdi_vcp
../../usb_host_vcp
)
# Set the components to include the tests for.
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
list(APPEND EXTRA_COMPONENT_DIRS ../../../../../device/esp_tinyusb)
endif()
project(test_app_usb_host_cdc)

View File

@ -1,15 +0,0 @@
| Supported Targets | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- |
# USB: CDC Class test application
## CDC-ACM driver
It tests basic functionality of the driver like open/close/read/write operations,
advanced features like CDC control request, multi-threaded or multi-device access,
as well as reaction to sudden disconnection and other error states.
### Hardware Required
This test expects that TinyUSB dual CDC device with VID = 0x303A and PID = 0x4002
is connected to the USB host.

View File

@ -1,23 +0,0 @@
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
set (TINYUSB_LIB)
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
set(TINYUSB_LIB "esp_tinyusb")
else()
set(TINYUSB_LIB "tinyusb")
endif()
# TODO: once IDF_v4.4 is at the EOL support, use WHOLE_ARCHIVE
idf_component_register(SRCS "test_cdc_acm_host.c" "usb_device.c" "test_app_main.c"
INCLUDE_DIRS "."
REQUIRES usb_host_cdc_acm unity ${TINYUSB_LIB})
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
# Due to the backward compatibility to IDFv4.4 (in which WHOLE_ARCHIVE is not implemented) we use following approach:
# Any non-static function test_app/main/*.c (apart from test_app_main.c) file is added as an undefined symbol
# because otherwise the linker will ignore test_app/main/*.c as it has no other files depending on any
# symbols in it.
# force-link test_cdc_acm_host.c
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u usb_lib_task")

View File

@ -1,57 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "unity.h"
#include "esp_heap_caps.h"
static size_t before_free_8bit;
static size_t before_free_32bit;
#define TEST_MEMORY_LEAK_THRESHOLD (-530)
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void app_main(void)
{
// ____ ___ ___________________ __ __
// | | \/ _____/\______ \ _/ |_ ____ _______/ |_
// | | /\_____ \ | | _/ \ __\/ __ \ / ___/\ __\.
// | | / / \ | | \ | | \ ___/ \___ \ | |
// |______/ /_______ / |______ / |__| \___ >____ > |__|
// \/ \/ \/ \/
printf(" ____ ___ ___________________ __ __ \r\n");
printf("| | \\/ _____/\\______ \\ _/ |_ ____ _______/ |_ \r\n");
printf("| | /\\_____ \\ | | _/ \\ __\\/ __ \\ / ___/\\ __\\\r\n");
printf("| | / / \\ | | \\ | | \\ ___/ \\___ \\ | | \r\n");
printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n");
printf(" \\/ \\/ \\/ \\/ \r\n");
UNITY_BEGIN();
unity_run_menu();
UNITY_END();
}
/* setUp runs before every test */
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
/* tearDown runs after every test */
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}

View File

@ -1,645 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc_caps.h"
#if SOC_USB_OTG_SUPPORTED
#include <stdio.h>
#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 <string.h>
#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 bool new_dev_cb_called = false;
static bool rx_overflow = false;
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
};
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 bool handle_rx(const 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);
return true;
}
static bool handle_rx2(const 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);
return true;
}
static bool handle_rx_advanced(const uint8_t *data, size_t data_len, void *arg)
{
bool *process_data = (bool *)arg;
return *process_data;
}
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:
if (event->data.serial_state.bOverRun) {
rx_overflow = true;
}
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 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(10);
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, even though 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_1", "[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);
// Option 1: Register callback during driver install
printf("Installing CDC-ACM driver\n");
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(50);
TEST_ASSERT_TRUE_MESSAGE(new_dev_cb_called, "New device callback was not called\n");
new_dev_cb_called = false;
// Clean-up
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall());
vTaskDelay(20);
}
TEST_CASE("new_device_connection_2", "[cdc_acm]")
{
test_install_cdc_driver();
// Option 2: Register callback after driver install
force_conn_state(false, 0);
vTaskDelay(50);
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_register_new_dev_callback(new_dev_cb));
force_conn_state(true, 0);
vTaskDelay(50);
TEST_ASSERT_TRUE_MESSAGE(new_dev_cb_called, "New device callback was not called\n");
new_dev_cb_called = false;
// Clean-up
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall());
vTaskDelay(20);
}
TEST_CASE("rx_buffer", "[cdc_acm]")
{
test_install_cdc_driver();
bool process_data = true; // This variable will determine return value of data_cb
cdc_acm_dev_hdl_t cdc_dev;
const cdc_acm_host_device_config_t dev_config = {
.connection_timeout_ms = 500,
.out_buffer_size = 64,
.in_buffer_size = 512,
.event_cb = notif_cb,
.data_cb = handle_rx_advanced,
.user_arg = &process_data,
};
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev));
TEST_ASSERT_NOT_NULL(cdc_dev);
// 1. Send > in_buffer_size bytes of data in normal operation: Expect no error
uint8_t tx_data[64] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
for (int i = 0; i < 10; i++) {
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_data, sizeof(tx_data), 1000));
vTaskDelay(5);
}
TEST_ASSERT_FALSE_MESSAGE(rx_overflow, "RX overflowed");
rx_overflow = false;
// 2. Send < (in_buffer_size - IN_MPS) bytes of data in 'not processed' mode: Expect no error
process_data = false;
for (int i = 0; i < 7; i++) {
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_data, sizeof(tx_data), 1000));
vTaskDelay(5);
}
TEST_ASSERT_FALSE_MESSAGE(rx_overflow, "RX overflowed");
rx_overflow = false;
// 3. Send >= (in_buffer_size - IN_MPS) bytes of data in 'not processed' mode: Expect error
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_data, sizeof(tx_data), 1000));
vTaskDelay(5);
TEST_ASSERT_TRUE_MESSAGE(rx_overflow, "RX did not overflow");
rx_overflow = false;
// 4. Send more data to the EP: Expect no error
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_data, sizeof(tx_data), 1000));
vTaskDelay(5);
TEST_ASSERT_FALSE_MESSAGE(rx_overflow, "RX overflowed");
rx_overflow = false;
// 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("functional_descriptor", "[cdc_acm]")
{
test_install_cdc_driver();
cdc_acm_dev_hdl_t cdc_dev;
const cdc_acm_host_device_config_t dev_config = {
.connection_timeout_ms = 500,
.out_buffer_size = 64,
.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);
// Request various CDC functional descriptors
// Following are present in the TinyUSB CDC device: Header, Call management, ACM, Union
const cdc_header_desc_t *header_desc;
const cdc_acm_call_desc_t *call_desc;
const cdc_acm_acm_desc_t *acm_desc;
const cdc_union_desc_t *union_desc;
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_cdc_desc_get(cdc_dev, USB_CDC_DESC_SUBTYPE_HEADER, (const usb_standard_desc_t **)&header_desc));
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_cdc_desc_get(cdc_dev, USB_CDC_DESC_SUBTYPE_CALL, (const usb_standard_desc_t **)&call_desc));
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_cdc_desc_get(cdc_dev, USB_CDC_DESC_SUBTYPE_ACM, (const usb_standard_desc_t **)&acm_desc));
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_cdc_desc_get(cdc_dev, USB_CDC_DESC_SUBTYPE_UNION, (const usb_standard_desc_t **)&union_desc));
TEST_ASSERT_NOT_NULL(header_desc);
TEST_ASSERT_NOT_NULL(call_desc);
TEST_ASSERT_NOT_NULL(acm_desc);
TEST_ASSERT_NOT_NULL(union_desc);
TEST_ASSERT_EQUAL(USB_CDC_DESC_SUBTYPE_HEADER, header_desc->bDescriptorSubtype);
TEST_ASSERT_EQUAL(USB_CDC_DESC_SUBTYPE_CALL, call_desc->bDescriptorSubtype);
TEST_ASSERT_EQUAL(USB_CDC_DESC_SUBTYPE_ACM, acm_desc->bDescriptorSubtype);
TEST_ASSERT_EQUAL(USB_CDC_DESC_SUBTYPE_UNION, union_desc->bDescriptorSubtype);
// Check few errors
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, cdc_acm_host_cdc_desc_get(cdc_dev, USB_CDC_DESC_SUBTYPE_OBEX, (const usb_standard_desc_t **)&header_desc));
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, cdc_acm_host_cdc_desc_get(cdc_dev, USB_CDC_DESC_SUBTYPE_MAX, (const usb_standard_desc_t **)&header_desc));
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, cdc_acm_host_cdc_desc_get(NULL, USB_CDC_DESC_SUBTYPE_HEADER, (const usb_standard_desc_t **)&header_desc));
// Clean-up
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev));
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall());
vTaskDelay(20);
}
/**
* @brief Closing procedure test
*
* -# Close already closed device
*/
TEST_CASE("closing", "[cdc_acm]")
{
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);
vTaskDelay(10);
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev));
printf("Closing already closed device \n");
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
}
/* Basic test to check CDC driver reaction to TX timeout */
TEST_CASE("tx_timeout", "[cdc_acm]")
{
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);
vTaskDelay(10);
// TX some data with timeout_ms=0. This will cause a timeout
TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 0));
vTaskDelay(100); // Wait before trying new TX
// TX some data again with greater timeout. This will check normal operation
TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000));
vTaskDelay(100);
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
}
/* 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 // SOC_USB_OTG_SUPPORTED

View File

@ -1,76 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "sdkconfig.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#include "esp_idf_version.h"
static uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
static 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);
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 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
};
static const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * 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),
};
#endif
void run_usb_dual_cdc_device(void)
{
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
.device_descriptor = &cdc_device_descriptor,
.configuration_descriptor = cdc_desc_configuration
#endif
};
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
tinyusb_config_cdcacm_t amc_cfg = {
.usb_dev = TINYUSB_USBDEV_0,
.cdc_port = TINYUSB_CDC_ACM_0,
.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(&amc_cfg));
#if (CONFIG_TINYUSB_CDC_COUNT > 1)
amc_cfg.cdc_port = TINYUSB_CDC_ACM_1;
ESP_ERROR_CHECK(tusb_cdc_acm_init(&amc_cfg));
#endif
printf("USB initialization DONE\n");
}

View File

@ -1,26 +0,0 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
from typing import Tuple
import pytest
from pytest_embedded_idf.dut import IdfDut
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.usb_host
@pytest.mark.parametrize('count', [
2,
], indirect=True)
def test_usb_host(dut: Tuple[IdfDut, IdfDut]) -> None:
device = dut[0]
host = dut[1]
# 1.1 Prepare USB device for CDC test
device.expect_exact('Press ENTER to see the list of tests.')
device.write('[cdc_acm_device]')
device.expect_exact('USB initialization DONE')
# 1.2 Run CDC test
host.run_all_single_board_cases(group='cdc_acm')

View File

@ -1,19 +0,0 @@
# Configure TinyUSB, it will be used to mock USB devices
CONFIG_TINYUSB=y
CONFIG_TINYUSB_MSC_ENABLED=n
CONFIG_TINYUSB_CDC_ENABLED=y
CONFIG_TINYUSB_CDC_COUNT=2
CONFIG_TINYUSB_HID_COUNT=0
# Disable watchdogs, they'd get triggered during unity interactive menu
CONFIG_ESP_INT_WDT=n
CONFIG_ESP_TASK_WDT=n
# Run-time checks of Heap and Stack
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
CONFIG_COMPILER_STACK_CHECK=y
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
CONFIG_COMPILER_CXX_EXCEPTIONS=y

63
dependencies.lock Normal file
View File

@ -0,0 +1,63 @@
dependencies:
espressif/esp-dsp:
component_hash: fa7fe74305df6da25867437ebcd4213e047cbfc0556cf92067ab657fce537c6e
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.5.2
espressif/esp_modem:
component_hash: 681d7c8417ce0f2a11a562c6998b7b379a4847be41d84673c11c0c7a6b00e918
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.3.0
espressif/mdns:
component_hash: 4e21149422be01c24d4b1d4dec64fa07d136d8c5d234e931f3ebf375cbde51a0
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.5.2
espressif/usb_host_cdc_acm:
component_hash: f76e1283e7ef0e7970e8a708c4ee443ff0aa112572e25319d30b7ec555cc0aee
source:
service_url: https://api.components.espressif.com/
type: service
version: 2.0.6
espressif/usb_host_ch34x_vcp:
component_hash: c7305154c452531950d8e4ee9838c474b5e4b84fdd12c40b2cae3f6f938752cb
source:
service_url: https://api.components.espressif.com/
type: service
version: 2.0.0
espressif/usb_host_cp210x_vcp:
component_hash: 6a36fd6179b2264a7d8854aa643d872f0d6408dd8f67a23d96e8cb8e49dfd087
source:
service_url: https://api.components.espressif.com/
type: service
version: 2.0.0
espressif/usb_host_ftdi_vcp:
component_hash: a24180724b8d7fdf01b920b6baebd01057d52d1a4ae5bf7ce009963cc24ebb42
source:
service_url: https://api.components.espressif.com/
type: service
version: 2.0.0
espressif/usb_host_vcp:
component_hash: 99b3ad314fa966cb32970e468625672994e259c69c7c668d9c0a13d65993a0e7
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.0.0~5
idf:
component_hash: null
source:
type: idf
version: 5.1.4
joltwallet/littlefs:
component_hash: cc23c607cbc8671bc03b1b1b39ae13d8f5e8e23771e0b1c66599d04ecd0a3527
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.16.2
manifest_hash: d59f1a31e1a9f7946c11e32fc4ec7bdda4ba5718dd49dcca5d2b59dfdd37483c
target: esp32s3
version: 1.0.0

View File

@ -12,4 +12,5 @@
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino, espidf
monitor_speed = 115200
; lib_deps = https://github.com/espressif/arduino-esp32#2f423afc4eb481bcc1e31c795643f009d184bf4f

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

10
src/idf_component.yml Normal file
View File

@ -0,0 +1,10 @@
dependencies:
usb_host_ch34x_vcp: "^2"
usb_host_cp210x_vcp: "^2"
usb_host_ftdi_vcp: "^2"
usb_host_vcp: "^1"
idf: ">=5.1.0"
# espressif/arduino-esp32:
# pre_release: true
# version: '*'

View File

@ -1,17 +1,172 @@
// #include "usb/vcp_cp210x.hpp"
#include "Arduino.h"
// extern "C" void app_main() {
#include <stdio.h>
#include <string.h>
// }
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include <Arduino.h>
#include "usb/cdc_acm_host.h"
#include "usb/vcp_ch34x.hpp"
#include "usb/vcp_cp210x.hpp"
#include "usb/vcp_ftdi.hpp"
#include "usb/vcp.hpp"
#include "usb/usb_host.h"
void setup() {
using namespace esp_usb;
// Change these values to match your needs
#define EXAMPLE_BAUDRATE (115200)
#define EXAMPLE_STOP_BITS (0) // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits
#define EXAMPLE_PARITY (0) // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space
#define EXAMPLE_DATA_BITS (8)
namespace {
static const char *TAG = "VCP example";
static SemaphoreHandle_t device_disconnected_sem;
/**
* @brief Data received callback
*
* Just pass received data to stdout
*
* @param[in] data Pointer to received data
* @param[in] data_len Length of received data in bytes
* @param[in] arg Argument we passed to the device open function
* @return
* true: We have processed the received data
* false: We expect more data
*/
static bool handle_rx(const uint8_t *data, size_t data_len, void *arg)
{
printf("%.*s", data_len, data);
return true;
}
/**
* @brief Device event callback
*
* Apart from handling device disconnection it doesn't do anything useful
*
* @param[in] event Device event type and data
* @param[in] user_ctx Argument we passed to the device open function
*/
static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx)
{
switch (event->type) {
case CDC_ACM_HOST_ERROR:
ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %d", event->data.error);
break;
case CDC_ACM_HOST_DEVICE_DISCONNECTED:
ESP_LOGI(TAG, "Device suddenly disconnected");
xSemaphoreGive(device_disconnected_sem);
break;
case CDC_ACM_HOST_SERIAL_STATE:
ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val);
break;
case CDC_ACM_HOST_NETWORK_CONNECTION:
default: break;
}
}
/**
* @brief USB Host library handling task
*
* @param arg Unused
*/
static void usb_lib_task(void *arg)
{
while (1) {
// 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) {
ESP_ERROR_CHECK(usb_host_device_free_all());
}
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
ESP_LOGI(TAG, "USB: All devices freed");
// Continue handling USB events to allow device reconnection
}
}
}
}
void loop() {
}
void setup() {
device_disconnected_sem = xSemaphoreCreateBinary();
assert(device_disconnected_sem);
// Install USB Host driver. Should only be called once in entire application
ESP_LOGI(TAG, "Installing USB Host");
const usb_host_config_t host_config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1,
};
ESP_ERROR_CHECK(usb_host_install(&host_config));
// Create a task that will handle USB library events
BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, NULL);
assert(task_created == pdTRUE);
ESP_LOGI(TAG, "Installing CDC-ACM driver");
ESP_ERROR_CHECK(cdc_acm_host_install(NULL));
// Register VCP drivers to VCP service
VCP::register_driver<FT23x>();
VCP::register_driver<CP210x>();
VCP::register_driver<CH34x>();
}
void loop() {
const cdc_acm_host_device_config_t dev_config = {
.connection_timeout_ms = 5000, // 5 seconds, enough time to plug the device in or experiment with timeout
.out_buffer_size = 512,
.in_buffer_size = 512,
.event_cb = handle_event,
.data_cb = handle_rx,
.user_arg = NULL,
};
// You don't need to know the device's VID and PID. Just plug in any device and the VCP service will load correct (already registered) driver for the device
ESP_LOGI(TAG, "Opening any VCP device...");
auto vcp = std::unique_ptr<CdcAcmDevice>(VCP::open(&dev_config));
if (vcp == nullptr) {
ESP_LOGI(TAG, "Failed to open VCP device");
return;
// continue;
}
vTaskDelay(10);
ESP_LOGI(TAG, "Setting up line coding");
cdc_acm_line_coding_t line_coding = {
.dwDTERate = EXAMPLE_BAUDRATE,
.bCharFormat = EXAMPLE_STOP_BITS,
.bParityType = EXAMPLE_PARITY,
.bDataBits = EXAMPLE_DATA_BITS,
};
ESP_ERROR_CHECK(vcp->line_coding_set(&line_coding));
/*
Now the USB-to-UART converter is configured and receiving data.
You can use standard CDC-ACM API to interact with it. E.g.
ESP_ERROR_CHECK(vcp->set_control_line_state(false, true));
ESP_ERROR_CHECK(vcp->tx_blocking((uint8_t *)"Test string", 12));
*/
// Send some dummy data
ESP_LOGI(TAG, "Sending data through CdcAcmDevice");
uint8_t data[] = "test_string";
ESP_ERROR_CHECK(vcp->tx_blocking(data, sizeof(data)));
ESP_ERROR_CHECK(vcp->set_control_line_state(true, true));
// We are done. Wait for device disconnection and start over
ESP_LOGI(TAG, "Done. You can reconnect the VCP device to run again.");
xSemaphoreTake(device_disconnected_sem, portMAX_DELAY);
}