17static const char *TAG =
"usb_cdc_acm";
19static constexpr size_t USB_TX_TASK_STACK_SIZE = 4096;
20static constexpr size_t USB_TX_TASK_STACK_SIZE_VV = 8192;
32static void tinyusb_cdc_rx_callback(
int itf, cdcacm_event_t *event) {
33 USBCDCACMInstance *instance = get_instance_by_itf(itf);
34 if (instance ==
nullptr) {
35 ESP_LOGE(TAG,
"RX callback: invalid interface %d", itf);
40 static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE] = {0};
44 tinyusb_cdcacm_read(
static_cast<tinyusb_cdcacm_itf_t
>(itf), rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
45 ESP_LOGV(TAG,
"tinyusb_cdc_rx_callback itf=%d (size: %u)", itf, rx_size);
48 if (ret == ESP_OK && rx_size > 0) {
49 RingbufHandle_t rx_ringbuf = instance->get_rx_ringbuf();
50 if (rx_ringbuf !=
nullptr) {
51 BaseType_t send_res = xRingbufferSend(rx_ringbuf, rx_buf, rx_size, 0);
52 if (send_res != pdTRUE) {
53 ESP_LOGE(TAG,
"USB RX itf=%d: buffer full, %u bytes lost", itf, rx_size);
55 ESP_LOGV(TAG,
"USB RX itf=%d: queued %u bytes", itf, rx_size);
61static void tinyusb_cdc_line_state_changed_callback(
int itf, cdcacm_event_t *event) {
62 USBCDCACMInstance *instance = get_instance_by_itf(itf);
63 if (instance ==
nullptr) {
64 ESP_LOGE(TAG,
"Line state callback: invalid interface %d", itf);
68 int dtr =
event->line_state_changed_data.dtr;
69 int rts =
event->line_state_changed_data.rts;
70 ESP_LOGV(TAG,
"Line state itf=%d: DTR=%d, RTS=%d", itf, dtr, rts);
73 instance->queue_line_state_event(dtr != 0, rts != 0);
76static void tinyusb_cdc_line_coding_changed_callback(
int itf, cdcacm_event_t *event) {
77 USBCDCACMInstance *instance = get_instance_by_itf(itf);
78 if (instance ==
nullptr) {
79 ESP_LOGE(TAG,
"Line coding callback: invalid interface %d", itf);
83 uint32_t bit_rate =
event->line_coding_changed_data.p_line_coding->bit_rate;
84 uint8_t stop_bits =
event->line_coding_changed_data.p_line_coding->stop_bits;
85 uint8_t parity =
event->line_coding_changed_data.p_line_coding->parity;
86 uint8_t data_bits =
event->line_coding_changed_data.p_line_coding->data_bits;
87 ESP_LOGV(TAG,
"Line coding itf=%d: bit_rate=%" PRIu32
" stop_bits=%u parity=%u data_bits=%u", itf, bit_rate,
88 stop_bits, parity, data_bits);
91 instance->queue_line_coding_event(bit_rate, stop_bits, parity, data_bits);
94static esp_err_t ringbuf_read_bytes(RingbufHandle_t ring_buf, uint8_t *out_buf,
size_t out_buf_sz,
size_t *rx_data_size,
95 TickType_t xTicksToWait) {
97 uint8_t *buf =
static_cast<uint8_t *
>(xRingbufferReceiveUpTo(ring_buf, &read_sz, xTicksToWait, out_buf_sz));
103 memcpy(out_buf, buf, read_sz);
104 vRingbufferReturnItem(ring_buf, (
void *) buf);
105 *rx_data_size = read_sz;
108 buf =
static_cast<uint8_t *
>(xRingbufferReceiveUpTo(ring_buf, &read_sz, 0, out_buf_sz - *rx_data_size));
109 if (buf !=
nullptr) {
110 memcpy(out_buf + *rx_data_size, buf, read_sz);
111 vRingbufferReturnItem(ring_buf, (
void *) buf);
112 *rx_data_size += read_sz;
123 this->
usb_tx_ringbuf_ = xRingbufferCreate(CONFIG_TINYUSB_CDC_TX_BUFSIZE, RINGBUF_TYPE_BYTEBUF);
125 ESP_LOGE(TAG,
"USB TX buffer creation error for itf %d", this->
itf_);
130 this->
usb_rx_ringbuf_ = xRingbufferCreate(CONFIG_TINYUSB_CDC_RX_BUFSIZE, RINGBUF_TYPE_BYTEBUF);
132 ESP_LOGE(TAG,
"USB RX buffer creation error for itf %d", this->
itf_);
138 const tinyusb_config_cdcacm_t acm_cfg = {
139 .usb_dev = TINYUSB_USBDEV_0,
140 .cdc_port = this->
itf_,
141 .callback_rx = &tinyusb_cdc_rx_callback,
142 .callback_rx_wanted_char = NULL,
143 .callback_line_state_changed = &tinyusb_cdc_line_state_changed_callback,
144 .callback_line_coding_changed = &tinyusb_cdc_line_coding_changed_callback,
147 esp_err_t result = tusb_cdc_acm_init(&acm_cfg);
148 if (result != ESP_OK) {
149 ESP_LOGE(TAG,
"tusb_cdc_acm_init failed: %d", result);
155 const size_t stack_size = esp_log_level_get(TAG) > ESP_LOG_DEBUG ? USB_TX_TASK_STACK_SIZE_VV : USB_TX_TASK_STACK_SIZE;
158 char task_name[] =
"usb_tx_0";
163 ESP_LOGE(TAG,
"Failed to create USB TX task for itf %d", this->
itf_);
177 if (event ==
nullptr) {
178 ESP_LOGW(TAG,
"Event pool exhausted, line state event dropped (itf=%d)", this->
itf_);
183 event->data.line_state.dtr = dtr;
184 event->data.line_state.rts = rts;
187 ESP_LOGW(TAG,
"Event queue full, line state event dropped (itf=%d)", this->
itf_);
192#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
202 if (event ==
nullptr) {
203 ESP_LOGW(TAG,
"Event pool exhausted, line coding event dropped (itf=%d)", this->
itf_);
208 event->data.line_coding.bit_rate = bit_rate;
209 event->data.line_coding.stop_bits = stop_bits;
210 event->data.line_coding.parity = parity;
211 event->data.line_coding.data_bits = data_bits;
214 ESP_LOGW(TAG,
"Event queue full, line coding event dropped (itf=%d)", this->
itf_);
219#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
229 switch (event->
type) {
231 bool dtr =
event->data.line_state.dtr;
232 bool rts =
event->data.line_state.rts;
241 uint32_t bit_rate =
event->data.line_coding.bit_rate;
242 uint8_t stop_bits =
event->data.line_coding.stop_bits;
243 uint8_t parity =
event->data.line_coding.parity;
244 uint8_t data_bits =
event->data.line_coding.data_bits;
252 this->
stop_bits_ = (stop_bits == 0) ? 1 : (stop_bits == 1) ? 1 : 2;
290 uint8_t data[CONFIG_TINYUSB_CDC_TX_BUFSIZE] = {0};
291 size_t tx_data_size = 0;
295 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
298 esp_err_t ret = ringbuf_read_bytes(this->
usb_tx_ringbuf_, data, CONFIG_TINYUSB_CDC_TX_BUFSIZE, &tx_data_size, 0);
301 ESP_LOGE(TAG,
"USB TX itf=%d: RingBuf read failed", this->
itf_);
303 }
else if (tx_data_size == 0) {
304 ESP_LOGD(TAG,
"USB TX itf=%d: RingBuf empty, skipping", this->
itf_);
308 ESP_LOGV(TAG,
"USB TX itf=%d: Read %d bytes from buffer", this->
itf_, tx_data_size);
313 uint8_t *data_head = &data[0];
315 while (tx_data_size > 0) {
316 size_t queued = tinyusb_cdcacm_write_queue(this->
itf_, data_head, tx_data_size);
317 ESP_LOGV(TAG,
"USB TX itf=%d: enqueued: size=%d, queued=%u", this->
itf_, tx_data_size, queued);
319 tx_data_size -= queued;
322 ESP_LOGV(TAG,
"USB TX itf=%d: waiting 10ms for flush", this->
itf_);
323 esp_err_t flush_ret = tinyusb_cdcacm_write_flush(this->
itf_, pdMS_TO_TICKS(10));
325 if (flush_ret != ESP_OK) {
326 ESP_LOGE(TAG,
"USB TX itf=%d: flush failed", this->
itf_);
327 tud_cdc_n_write_clear(this->
itf_);
345 if (send_res != pdTRUE) {
346 ESP_LOGW(TAG,
"USB TX itf=%d: buffer full, %u bytes dropped", this->
itf_, len);
376 size_t original_len =
len;
377 size_t bytes_read = 0;
392 uint8_t *buf =
static_cast<uint8_t *
>(xRingbufferReceiveUpTo(this->
usb_rx_ringbuf_, &rx_size, 0,
len));
393 if (buf ==
nullptr) {
397 memcpy(data, buf, rx_size);
399 bytes_read += rx_size;
407 buf =
static_cast<uint8_t *
>(xRingbufferReceiveUpTo(this->
usb_rx_ringbuf_, &rx_size, 0,
len));
408 if (buf ==
nullptr) {
412 memcpy(data, buf, rx_size);
414 bytes_read += rx_size;
416 return bytes_read == original_len;
420 UBaseType_t waiting = 0;
422 vRingbufferGetInfo(this->
usb_rx_ringbuf_,
nullptr,
nullptr,
nullptr,
nullptr, &waiting);
424 return static_cast<int>(waiting) + (this->
has_peek_ ? 1 : 0);
433 UBaseType_t waiting = 1;
434 while (waiting > 0) {
435 vRingbufferGetInfo(this->
usb_tx_ringbuf_,
nullptr,
nullptr,
nullptr,
nullptr, &waiting);
437 vTaskDelay(pdMS_TO_TICKS(1));
442 tinyusb_cdcacm_write_flush(this->
itf_, pdMS_TO_TICKS(100));
454 if (interface !=
nullptr) {
463 if (interface !=
nullptr) {
472 " Number of Interfaces: %d",
473 this->
interfaces_[MAX_USB_CDC_INSTANCES - 1] !=
nullptr ? MAX_USB_CDC_INSTANCES : 1);
477 uint8_t itf_num =
static_cast<uint8_t
>(interface->
get_itf());
478 if (itf_num < MAX_USB_CDC_INSTANCES) {
481 ESP_LOGE(TAG,
"Interface number must be less than %u", MAX_USB_CDC_INSTANCES);
487 if ((interface !=
nullptr) && (interface->get_itf() ==
static_cast<tinyusb_cdcacm_itf_t
>(itf))) {
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.