Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cdc): Add support for two CDC ports at once #10962

Merged
merged 3 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading