usb: Extend CDC device with second CDC channel

Closes https://github.com/espressif/esp-idf/issues/7020
Closes https://github.com/espressif/esp-idf/pull/8011
This commit is contained in:
Tomas Rezucha 2021-12-16 07:50:08 +01:00
parent fa8364a544
commit 6330d5d312
29 changed files with 115 additions and 164 deletions

View File

@ -116,6 +116,7 @@ menu "TinyUSB Stack"
menu "Massive Storage Class (MSC)"
config TINYUSB_MSC_ENABLED
depends on TINYUSB_CDC_COUNT < 2
bool "Enable TinyUSB MSC feature"
default n
help
@ -136,6 +137,14 @@ menu "TinyUSB Stack"
help
Enable TinyUSB CDC feature.
config TINYUSB_CDC_COUNT
int "CDC Channel Count"
default 1
range 1 2
depends on TINYUSB_CDC_ENABLED
help
Number of independent serial ports.
config TINYUSB_CDC_RX_BUFSIZE
depends on TINYUSB_CDC_ENABLED
int "CDC FIFO size of RX channel"

View File

@ -21,9 +21,11 @@ extern "C" {
/**
* @brief CDC ports available to setup
*/
typedef enum{
TINYUSB_CDC_ACM_0 = 0x0
}tinyusb_cdcacm_itf_t;
typedef enum {
TINYUSB_CDC_ACM_0 = 0x0,
TINYUSB_CDC_ACM_1,
TINYUSB_CDC_ACM_MAX
} tinyusb_cdcacm_itf_t;
/* Callbacks and events
********************************************************************* */

View File

@ -86,7 +86,11 @@ extern "C" {
#define CFG_TUD_HID_BUFSIZE CONFIG_TINYUSB_HID_BUFSIZE
// Enabled device class driver
#define CFG_TUD_CDC CONFIG_TINYUSB_CDC_ENABLED
#if defined(CONFIG_TINYUSB_CDC_COUNT)
#define CFG_TUD_CDC CONFIG_TINYUSB_CDC_COUNT
#else
#define CFG_TUD_CDC 0
#endif
#define CFG_TUD_MSC CONFIG_TINYUSB_MSC_ENABLED
#define CFG_TUD_HID CONFIG_TINYUSB_HID_ENABLED
#define CFG_TUD_MIDI CONFIG_TINYUSB_MIDI_ENABLED

View File

@ -69,14 +69,6 @@ esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg);
esp_err_t tinyusb_cdc_deinit(int itf);
/**
* @brief Checks if the CDC initialized and ready to interaction
*
* @return true or false
*/
bool tinyusb_cdc_initialized(int itf);
/**
* @brief Return interface of a CDC device
*

View File

@ -9,15 +9,6 @@
#include <string.h>
#include "usb_descriptors.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define EPNUM_MSC 0x03
#ifdef __cplusplus
extern "C" {
#endif
@ -36,6 +27,11 @@ enum {
ITF_NUM_CDC_DATA,
# endif
# if CFG_TUD_CDC > 1
ITF_NUM_CDC1,
ITF_NUM_CDC1_DATA,
# endif
# if CFG_TUD_MSC
ITF_NUM_MSC,
# endif

View File

@ -10,65 +10,29 @@
#include "esp_log.h"
#include "tusb.h"
#include "cdc.h"
#include "sdkconfig.h"
static const char *TAG = "tusb_cdc";
#define CDC_INTF_NUM CFG_TUD_CDC // number of cdc blocks
static esp_tusb_cdc_t *cdc_obj[CDC_INTF_NUM] = {};
/* Common CDC functions
********************************************************************* */
bool tinyusb_cdc_initialized(int itf)
{
return (cdc_obj[itf] != NULL);
}
static esp_err_t cdc_interface_check(int itf)
{
if (tinyusb_cdc_initialized(itf)) {
return ESP_OK;
} else {
ESP_LOGE(TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization");
return ESP_ERR_INVALID_STATE;
}
}
/**
* @brief
*
* @param itf
* @param expected_inited
* @param expected_type use -1 if you don't care
* @return esp_err_t
*/
static esp_err_t cdc_obj_check(int itf, bool expected_inited, tusb_class_code_t expected_type)
{
bool inited = (cdc_obj[itf] != NULL);
if (expected_inited != inited) {
ESP_LOGE(TAG, "Wrong state of the interface. Expected state: %s",
expected_inited ? "initialized" : "not initialized");
return ESP_ERR_INVALID_STATE;
}
if (inited && (expected_type != -1) && !(cdc_obj[itf]->type == expected_type)) {
ESP_LOGE(TAG, "Wrong type of the interface. Should be : 0x%x (tusb_class_code_t)", expected_type);
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
static const char *TAG = "tusb_cdc";
esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num)
{
if (cdc_interface_check(itf_num) != ESP_OK) {
if (itf_num >= CDC_INTF_NUM || itf_num < 0) {
return NULL;
}
return cdc_obj[itf_num];
}
/*********************************************************************** Common CDC functions*/
/* CDC class funcs
********************************************************************* */
static esp_err_t cdc_obj_check(int itf, bool expected_inited, tusb_class_code_t expected_type)
{
esp_tusb_cdc_t *this_itf = tinyusb_cdc_get_intf(itf);
bool inited = (this_itf != NULL);
ESP_RETURN_ON_FALSE(expected_inited == inited, ESP_ERR_INVALID_STATE, TAG, "Wrong state of the interface. Expected state: %s", expected_inited ? "initialized" : "not initialized");
ESP_RETURN_ON_FALSE(!(inited && (expected_type != -1) && !(this_itf->type == expected_type)), ESP_ERR_INVALID_STATE, TAG, "Wrong type of the interface. Should be : 0x%x (tusb_class_code_t)", expected_type);
return ESP_OK;
}
static esp_err_t tusb_cdc_comm_init(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1), TAG, "cdc_obj_check failed");
@ -112,16 +76,12 @@ static esp_err_t tusb_cdc_deinit_data(int itf)
cdc_obj[itf] = NULL;
return ESP_OK;
}
/*********************************************************************** CDC class funcs*/
/* CDC initialization
********************************************************************* */
esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg)
{
ESP_LOGD(TAG, "CDC initialization...");
if (itf != 0) {
ESP_LOGE(TAG, "There is not CDC no.%d", itf);
return ESP_ERR_INVALID_ARG;
}
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1), TAG, "cdc_obj_check failed");
ESP_LOGD(TAG, "Init CDC %d", itf);
if (cfg->cdc_class == TUSB_CLASS_CDC) {
ESP_RETURN_ON_ERROR(tusb_cdc_comm_init(itf), TAG, "tusb_cdc_comm_init failed");
cdc_obj[itf]->cdc_subclass.comm_subclass = cfg->cdc_subclass.comm_subclass;
@ -133,13 +93,11 @@ esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg)
return ESP_OK;
}
esp_err_t tinyusb_cdc_deinit(int itf)
{
if (itf != 0) {
ESP_LOGE(TAG, "There is not CDC no.%d", itf);
return ESP_ERR_INVALID_ARG;
}
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, -1), TAG, "cdc_obj_check failed");
ESP_LOGD(TAG, "Deinit CDC %d", itf);
if (cdc_obj[itf]->type == TUSB_CLASS_CDC) {
ESP_RETURN_ON_ERROR(tusb_cdc_deinit_comm(itf), TAG, "tusb_cdc_deinit_comm failed");
} else if (cdc_obj[itf]->type == TUSB_CLASS_CDC_DATA) {
@ -147,7 +105,5 @@ esp_err_t tinyusb_cdc_deinit(int itf)
} else {
return ESP_ERR_INVALID_ARG;
}
ESP_LOGD(TAG, "De-initialized");
return ESP_OK;
}
/*********************************************************************** CDC initialization*/

View File

@ -12,6 +12,9 @@ static tusb_desc_device_t s_descriptor;
static char *s_str_descriptor[USB_STRING_DESCRIPTOR_ARRAY_SIZE];
#define MAX_DESC_BUF_SIZE 32
#define EPNUM_MSC ((CFG_TUD_CDC * 2) + 1)
#define EPNUM_HID (EPNUM_MSC + 1)
#if CFG_TUD_HID //HID Report Descriptor
uint8_t const desc_hid_report[] = {
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD), ),
@ -27,13 +30,20 @@ uint8_t const desc_configuration[] = {
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, 0x02, 0x82, 64),
# endif
# if CFG_TUD_CDC > 1
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC1, 4, 0x83, 8, 0x04, 0x84, 64),
# endif
# if CFG_TUD_MSC
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512
# endif
# if CFG_TUD_HID
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_NONE, sizeof(desc_hid_report), 0x84, 16, 10)
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_NONE, sizeof(desc_hid_report), 0x80 | EPNUM_HID, 16, 10)
# endif
};

View File

@ -12,6 +12,7 @@
#include "tusb_console.h"
#include "tinyusb.h"
#include "vfs_tinyusb.h"
#include "esp_check.h"
#define STRINGIFY(s) STRINGIFY2(s)
#define STRINGIFY2(s) #s
@ -100,35 +101,15 @@ static esp_err_t restore_std_streams(FILE **f_in, FILE **f_out, FILE **f_err)
esp_err_t esp_tusb_init_console(int cdc_intf)
{
if (!tinyusb_cdc_initialized(cdc_intf)) {
ESP_LOGE(TAG, "Can't init the console because TinyUSB's CDC is not initialized!");
return ESP_ERR_INVALID_STATE;
}
/* Registering TUSB at VFS */
int res = esp_vfs_tusb_cdc_register(cdc_intf, NULL);
if (res != ESP_OK) {
return res;
}
res = redirect_std_streams_to(&con.in, &con.out, &con.err, "/dev/tusb_cdc");
if (res != ESP_OK) {
return res;
}
ESP_RETURN_ON_ERROR(esp_vfs_tusb_cdc_register(cdc_intf, NULL), TAG, "");
ESP_RETURN_ON_ERROR(redirect_std_streams_to(&con.in, &con.out, &con.err, "/dev/tusb_cdc"), TAG, "Failed to redirect STD streams");
return ESP_OK;
}
esp_err_t esp_tusb_deinit_console(int cdc_intf)
{
if (!tinyusb_cdc_initialized(cdc_intf)) {
ESP_LOGE(TAG, "Can't deinit the console because TinyUSB's CDC is not initialized!");
return ESP_ERR_INVALID_STATE;
}
int res = restore_std_streams(&con.in, &con.out, &con.err);
if (res != ESP_OK) {
return res;
}
ESP_RETURN_ON_ERROR(restore_std_streams(&con.in, &con.out, &con.err), TAG, "Failed to restore STD streams");
esp_vfs_tusb_cdc_unregister(NULL);
return ESP_OK;
}

View File

@ -7,6 +7,13 @@
#include "usb_descriptors.h"
#include "sdkconfig.h"
/*
* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3))
/**** TinyUSB default ****/

View File

@ -128,11 +128,11 @@ The table below describes the code examples available in the directory :example:
* - Code Example
- Description
* - :example:`peripherals/usb/tusb_console`
* - :example:`peripherals/usb/device/tusb_console`
- How to set up {IDF_TARGET_NAME} chip to get log output via Serial Device connection
* - :example:`peripherals/usb/tusb_sample_descriptor`
* - :example:`peripherals/usb/device/tusb_sample_descriptor`
- How to set up {IDF_TARGET_NAME} chip to work as a Generic USB Device with a user-defined descriptor
* - :example:`peripherals/usb/tusb_serial_device`
* - :example:`peripherals/usb/device/tusb_serial_device`
- How to set up {IDF_TARGET_NAME} chip to work as a USB Serial Device

View File

@ -17,7 +17,7 @@ Any ESP boards that have USB-OTG supported.
#### Pin Assignment
See common pin assignments for USB Device examples from [upper level](../README.md#common-pin-assignments).
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments).
### Build and Flash

View File

@ -1,11 +1,8 @@
/* USB Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
// DESCRIPTION:
// This example contains minimal code to make ESP32-S2 based device

View File

@ -17,7 +17,7 @@ Any ESP boards that have USB-OTG supported.
#### Pin Assignment
See common pin assignments for USB Device examples from [upper level](../README.md#common-pin-assignments).
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments).
### Configure the project

View File

@ -1,11 +1,8 @@
/* USB Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdlib.h>
#include "esp_log.h"

View File

@ -1,7 +1,7 @@
| Supported Targets | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- |
# TinyUSB Sample Descriptor
# TinyUSB Serial Device Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
@ -11,13 +11,18 @@ As a USB stack, a TinyUSB component is used.
## How to use example
This example can also be configured to act as double serial device.
Run `idf.py menuconfig` and in `Component config → TinyUSB Stack → Communication Device Class (CDC) → CDC Channel Count` select number of serial devices you want to implement.
### Hardware Required
Any ESP boards that have USB-OTG supported.
Any ESP board that have USB-OTG supported.
#### Pin Assignment
See common pin assignments for USB Device examples from [upper level](../README.md#common-pin-assignments).
_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below.
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments).
### Build and Flash
@ -35,7 +40,7 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
## Example Output
After the flashing you should see the output:
After the flashing you should see this output (for single CDC channel):
```
I (285) example: USB initialization
@ -69,19 +74,12 @@ I (455) TinyUSB: TinyUSB Driver installed
I (465) example: USB initialization DONE
```
Connect to the serial port (e.g. on Linux, it should be `/dev/ttyACM0`) by any terminal application (e.g. `picocom /dev/ttyACM0`), typing a string "espressif" and you will get the exactly same string returned.
Connect to the serial port (e.g. on Linux, it should be `/dev/ttyACM0`) by any terminal application (e.g. `picocom /dev/ttyACM0`).
Now you can send data strings to the device, the device will echo back the same data string.
The monitor tool will also print the communication process:
```
I (146186) example: Line state changed! dtr:1, rst:1
I (147936) example: Got data (1 bytes): e
I (148136) example: Got data (1 bytes): s
I (148336) example: Got data (1 bytes): p
I (148416) example: Got data (1 bytes): r
I (148446) example: Got data (1 bytes): e
I (148676) example: Got data (1 bytes): s
I (148836) example: Got data (1 bytes): s
I (148956) example: Got data (1 bytes): i
I (149066) example: Got data (1 bytes): f
I (12438) example: Data from channel 0:
I (12438) example: 0x3ffbfea0 45 73 70 72 65 73 73 69 66 0d |Espressif.|
```

View File

@ -1,15 +1,8 @@
/* USB Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
// DESCRIPTION:
// This example contains minimal code to make ESP32-S2 based device
// recognizable by USB-host devices as a USB Serial Device.
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdint.h>
#include "esp_log.h"
@ -30,8 +23,8 @@ void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event)
/* read */
esp_err_t ret = tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
if (ret == ESP_OK) {
buf[rx_size] = '\0';
ESP_LOGI(TAG, "Got data (%d bytes): %s", rx_size, buf);
ESP_LOGI(TAG, "Data from channel %d:", itf);
ESP_LOG_BUFFER_HEXDUMP(TAG, buf, rx_size, ESP_LOG_INFO);
} else {
ESP_LOGE(TAG, "Read error");
}
@ -44,8 +37,8 @@ void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event)
void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event)
{
int dtr = event->line_state_changed_data.dtr;
int rst = event->line_state_changed_data.rts;
ESP_LOGI(TAG, "Line state changed! dtr:%d, rst:%d", dtr, rst);
int rts = event->line_state_changed_data.rts;
ESP_LOGI(TAG, "Line state changed on channel %d: DTR:%d, RTS:%d", itf, dtr, rts);
}
void app_main(void)
@ -70,5 +63,15 @@ void app_main(void)
TINYUSB_CDC_ACM_0,
CDC_EVENT_LINE_STATE_CHANGED,
&tinyusb_cdc_line_state_changed_callback));
#if (CONFIG_TINYUSB_CDC_COUNT > 1)
amc_cfg.cdc_port = TINYUSB_CDC_ACM_1;
ESP_ERROR_CHECK(tusb_cdc_acm_init(&amc_cfg));
ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback(
TINYUSB_CDC_ACM_1,
CDC_EVENT_LINE_STATE_CHANGED,
&tinyusb_cdc_line_state_changed_callback));
#endif
ESP_LOGI(TAG, "USB initialization DONE");
}

View File

@ -0,0 +1 @@
CONFIG_TINYUSB_CDC_COUNT=2

View File

@ -20,7 +20,7 @@ See common pin assignments for USB Device examples from [upper level](../../../R
### Build and Flash
1. Build and flash [tusb_serial_device example](../../../tusb_serial_device) to USB device board.
1. Build and flash [tusb_serial_device example](../../../device/tusb_serial_device) to USB device board.
2. Build this project and flash it to the USB host board, then run monitor tool to view serial output:
```bash

View File

@ -2096,6 +2096,7 @@ components/tcp_transport/transport_utils.c
components/tcpip_adapter/include/tcpip_adapter.h
components/tcpip_adapter/include/tcpip_adapter_compatible/tcpip_adapter_compat.h
components/tcpip_adapter/include/tcpip_adapter_types.h
components/tinyusb/additions/include/tusb_config.h
components/touch_element/include/touch_element/touch_button.h
components/touch_element/include/touch_element/touch_element.h
components/touch_element/include/touch_element/touch_element_private.h
@ -2687,9 +2688,6 @@ examples/peripherals/uart/uart_events/main/uart_events_example_main.c
examples/peripherals/uart/uart_repl/main/uart_repl_example_main.c
examples/peripherals/uart/uart_select/main/uart_select_example_main.c
examples/peripherals/usb/host/msc/components/msc/test/msc_device.c
examples/peripherals/usb/tusb_console/main/tusb_console_main.c
examples/peripherals/usb/tusb_sample_descriptor/main/tusb_sample_descriptor_main.c
examples/peripherals/usb/tusb_serial_device/main/tusb_serial_device_main.c
examples/peripherals/wave_gen/main/wave_gen_example_main.c
examples/protocols/asio/asio_chat/example_test.py
examples/protocols/asio/asio_chat/main/asio_chat.cpp