Skip to content

Commit

Permalink
feat(cdc): Add support for two CDC ports at once (#10962)
Browse files Browse the repository at this point in the history
* feat(cdc): Add support for two CDC ports at once

* ci(pre-commit): Apply automatic fixes

---------

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
  • Loading branch information
me-no-dev and pre-commit-ci-lite[bot] authored Feb 13, 2025
1 parent 250c1ab commit 7b651b6
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 28 deletions.
68 changes: 51 additions & 17 deletions cores/esp32/USBCDC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ ESP_EVENT_DEFINE_BASE(ARDUINO_USB_CDC_EVENTS);
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);

#define MAX_USB_CDC_DEVICES 2
USBCDC *devices[MAX_USB_CDC_DEVICES] = {NULL, NULL};
USBCDC *devices[CFG_TUD_CDC];

static uint16_t load_cdc_descriptor(uint8_t *dst, uint8_t *itf) {
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC");
Expand All @@ -38,23 +37,43 @@ static uint16_t load_cdc_descriptor(uint8_t *dst, uint8_t *itf) {
return TUD_CDC_DESC_LEN;
}

static uint16_t load_cdc_descriptor2(uint8_t *dst, uint8_t *itf) {
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC2");
uint8_t ep_ntfy = tinyusb_get_free_in_endpoint();
TU_VERIFY(ep_ntfy != 0);
uint8_t ep_in = tinyusb_get_free_in_endpoint();
TU_VERIFY(ep_in != 0);
uint8_t ep_out = tinyusb_get_free_out_endpoint();
TU_VERIFY(ep_out != 0);
uint8_t descriptor[TUD_CDC_DESC_LEN] = {
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(*itf, str_index, (uint8_t)(0x80 | ep_ntfy), CFG_TUD_ENDOINT_SIZE, ep_out, (uint8_t)(0x80 | ep_in), CFG_TUD_ENDOINT_SIZE)
};
*itf += 2;
memcpy(dst, descriptor, TUD_CDC_DESC_LEN);
return TUD_CDC_DESC_LEN;
}

// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
if (itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL) {
//log_v("ITF: %u, DTR: %u, RTS: %u", itf, dtr, rts);
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
devices[itf]->_onLineState(dtr, rts);
}
}

// Invoked when line coding is change via SET_LINE_CODING
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding) {
if (itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL) {
//log_v("ITF: %u, BITRATE: %lu, STOP_BITS: %u, PARITY: %u, DATA_BITS: %u", itf, p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
devices[itf]->_onLineCoding(p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
}
}

// Invoked when received new data
void tud_cdc_rx_cb(uint8_t itf) {
if (itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL) {
//log_v("ITF: %u", itf);
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
devices[itf]->_onRX();
}
}
Expand All @@ -66,13 +85,13 @@ void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) {

// Invoked when space becomes available in TX buffer
void tud_cdc_tx_complete_cb(uint8_t itf) {
if (itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL) {
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
devices[itf]->_onTX();
}
}

static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
if (devices[0] != NULL) {
if (CFG_TUD_CDC && devices[0] != NULL) {
tud_cdc_n_write_char(0, c);
}
}
Expand All @@ -84,9 +103,15 @@ static void usb_unplugged_cb(void *arg, esp_event_base_t event_base, int32_t eve
USBCDC::USBCDC(uint8_t itfn)
: itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_lock(NULL),
tx_timeout_ms(250) {
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
if (itf < MAX_USB_CDC_DEVICES) {
if (itf < CFG_TUD_CDC) {
if (itf == 0) {
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
} else {
tinyusb_enable_interface(USB_INTERFACE_CDC2, TUD_CDC_DESC_LEN, load_cdc_descriptor2);
}
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
} else {
log_e("Maximum of %u CDC devices are supported", CFG_TUD_CDC);
}
}

Expand Down Expand Up @@ -142,6 +167,9 @@ size_t USBCDC::setRxBufferSize(size_t rx_queue_len) {
}

void USBCDC::begin(unsigned long baud) {
if (itf >= CFG_TUD_CDC) {
return;
}
if (tx_lock == NULL) {
tx_lock = xSemaphoreCreateMutex();
}
Expand All @@ -153,6 +181,9 @@ void USBCDC::begin(unsigned long baud) {
}

void USBCDC::end() {
if (itf >= CFG_TUD_CDC) {
return;
}
connected = false;
devices[itf] = NULL;
setRxBufferSize(0);
Expand Down Expand Up @@ -298,14 +329,14 @@ bool USBCDC::rebootEnabled(void) {
}

int USBCDC::available(void) {
if (itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL) {
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
return -1;
}
return uxQueueMessagesWaiting(rx_queue);
}

int USBCDC::peek(void) {
if (itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL) {
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
return -1;
}
uint8_t c;
Expand All @@ -316,7 +347,7 @@ int USBCDC::peek(void) {
}

int USBCDC::read(void) {
if (itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL) {
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
return -1;
}
uint8_t c = 0;
Expand All @@ -327,7 +358,7 @@ int USBCDC::read(void) {
}

size_t USBCDC::read(uint8_t *buffer, size_t size) {
if (itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL) {
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
return -1;
}
uint8_t c = 0;
Expand All @@ -339,7 +370,7 @@ size_t USBCDC::read(uint8_t *buffer, size_t size) {
}

void USBCDC::flush(void) {
if (itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || !tud_cdc_n_connected(itf)) {
if (itf >= CFG_TUD_CDC || tx_lock == NULL || !tud_cdc_n_connected(itf)) {
return;
}
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
Expand All @@ -350,7 +381,7 @@ void USBCDC::flush(void) {
}

int USBCDC::availableForWrite(void) {
if (itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || !tud_cdc_n_connected(itf)) {
if (itf >= CFG_TUD_CDC || tx_lock == NULL || !tud_cdc_n_connected(itf)) {
return 0;
}
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
Expand All @@ -362,7 +393,7 @@ int USBCDC::availableForWrite(void) {
}

size_t USBCDC::write(const uint8_t *buffer, size_t size) {
if (itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || buffer == NULL || size == 0 || !tud_cdc_n_connected(itf)) {
if (itf >= CFG_TUD_CDC || tx_lock == NULL || buffer == NULL || size == 0 || !tud_cdc_n_connected(itf)) {
return 0;
}
if (xPortInIsrContext()) {
Expand Down Expand Up @@ -415,6 +446,9 @@ uint32_t USBCDC::baudRate() {
}

void USBCDC::setDebugOutput(bool en) {
if (itf) {
return;
}
if (en) {
uartSetDebug(NULL);
ets_install_putc2((void (*)(char)) & cdc0_write_char);
Expand All @@ -424,7 +458,7 @@ void USBCDC::setDebugOutput(bool en) {
}

USBCDC::operator bool() const {
if (itf >= MAX_USB_CDC_DEVICES) {
if (itf >= CFG_TUD_CDC) {
return false;
}
return connected;
Expand Down
24 changes: 13 additions & 11 deletions cores/esp32/esp32-hal-tinyusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,27 +616,29 @@ void usb_persist_restart(restart_type_t mode) {
}

static bool tinyusb_reserve_in_endpoint(uint8_t endpoint) {
if (endpoint > 6 || (tinyusb_endpoints.in & BIT(endpoint)) != 0) {
if (endpoint > CFG_TUD_NUM_EPS || (tinyusb_endpoints.in & BIT(endpoint)) != 0) {
return false;
}
tinyusb_endpoints.in |= BIT(endpoint);
return true;
}

static bool tinyusb_reserve_out_endpoint(uint8_t endpoint) {
if (endpoint > 6 || (tinyusb_endpoints.out & BIT(endpoint)) != 0) {
if (endpoint > CFG_TUD_NUM_EPS || (tinyusb_endpoints.out & BIT(endpoint)) != 0) {
return false;
}
tinyusb_endpoints.out |= BIT(endpoint);
return true;
}

static bool tinyusb_has_available_fifos(void) {
uint8_t max_endpoints = 4, active_endpoints = 0;
uint8_t max_endpoints = CFG_TUD_NUM_IN_EPS - 1, active_endpoints = 0;
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
if (tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) {
max_endpoints = 5; //CDC endpoint 0x85 is actually not linked to FIFO and not used
max_endpoints = CFG_TUD_NUM_IN_EPS; //CDC endpoint 0x85 is actually not linked to FIFO and not used
}
for (uint8_t i = 1; i < 7; i++) {
#endif
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
if ((tinyusb_endpoints.in & BIT(i)) != 0) {
active_endpoints++;
}
Expand Down Expand Up @@ -771,7 +773,7 @@ static void usb_device_task(void *param) {
* PUBLIC API
* */
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "MIDI", "CUSTOM"};
const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "CDC2", "MIDI", "CUSTOM"};
#endif
static bool tinyusb_is_initialized = false;

Expand Down Expand Up @@ -862,7 +864,7 @@ uint8_t tinyusb_get_free_duplex_endpoint(void) {
log_e("No available IN endpoints");
return 0;
}
for (uint8_t i = 1; i < 7; i++) {
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
if ((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) == 0) {
tinyusb_endpoints.in |= BIT(i);
tinyusb_endpoints.out |= BIT(i);
Expand All @@ -878,13 +880,13 @@ uint8_t tinyusb_get_free_in_endpoint(void) {
log_e("No available IN endpoints");
return 0;
}
for (uint8_t i = 1; i < 7; i++) {
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
if ((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) != 0) {
tinyusb_endpoints.in |= BIT(i);
return i;
}
}
for (uint8_t i = 1; i < 7; i++) {
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
if ((tinyusb_endpoints.in & BIT(i)) == 0) {
tinyusb_endpoints.in |= BIT(i);
return i;
Expand All @@ -894,13 +896,13 @@ uint8_t tinyusb_get_free_in_endpoint(void) {
}

uint8_t tinyusb_get_free_out_endpoint(void) {
for (uint8_t i = 1; i < 7; i++) {
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
if ((tinyusb_endpoints.out & BIT(i)) == 0 && (tinyusb_endpoints.in & BIT(i)) != 0) {
tinyusb_endpoints.out |= BIT(i);
return i;
}
}
for (uint8_t i = 1; i < 7; i++) {
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
if ((tinyusb_endpoints.out & BIT(i)) == 0) {
tinyusb_endpoints.out |= BIT(i);
return i;
Expand Down
8 changes: 8 additions & 0 deletions cores/esp32/esp32-hal-tinyusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ extern "C" {
#define CFG_TUD_ENDOINT_SIZE 64
#endif
#endif
#if CONFIG_IDF_TARGET_ESP32P4
#define CFG_TUD_NUM_EPS 15
#define CFG_TUD_NUM_IN_EPS 8
#else
#define CFG_TUD_NUM_EPS 6
#define CFG_TUD_NUM_IN_EPS 5
#endif

typedef struct {
uint16_t vid;
Expand Down Expand Up @@ -88,6 +95,7 @@ typedef enum {
USB_INTERFACE_HID,
USB_INTERFACE_VENDOR,
USB_INTERFACE_CDC,
USB_INTERFACE_CDC2,
USB_INTERFACE_MIDI,
USB_INTERFACE_CUSTOM,
USB_INTERFACE_MAX
Expand Down

0 comments on commit 7b651b6

Please sign in to comment.