1#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
8#include "freertos/FreeRTOS.h"
9#include "freertos/ringbuf.h"
10#include "freertos/task.h"
14#include "tusb_cdc_acm.h"
18static const char *
const TAG =
"usb_cdc_acm";
21static constexpr size_t USB_CDC_MAX_LOG_BYTES = 168;
23static constexpr size_t USB_TX_TASK_STACK_SIZE = 4096;
24static constexpr size_t USB_TX_TASK_STACK_SIZE_VV = 8192;
26static USBCDCACMInstance *get_instance_by_itf(
int itf) {
33static void tinyusb_cdc_rx_callback(
int itf, cdcacm_event_t *event) {
34 USBCDCACMInstance *instance = get_instance_by_itf(itf);
35 if (instance ==
nullptr) {
36 ESP_LOGE(TAG,
"RX callback: invalid interface %d", itf);
41 static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE] = {0};
45 tinyusb_cdcacm_read(
static_cast<tinyusb_cdcacm_itf_t
>(itf), rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
46 ESP_LOGV(TAG,
"tinyusb_cdc_rx_callback itf=%d (size: %u)", itf, rx_size);
47#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
52 if (ret == ESP_OK && rx_size > 0) {
53 RingbufHandle_t rx_ringbuf = instance->get_rx_ringbuf();
54 if (rx_ringbuf !=
nullptr) {
55 BaseType_t send_res = xRingbufferSend(rx_ringbuf, rx_buf, rx_size, 0);
56 if (send_res != pdTRUE) {
57 ESP_LOGE(TAG,
"USB RX itf=%d: buffer full, %u bytes lost", itf, rx_size);
59 ESP_LOGV(TAG,
"USB RX itf=%d: queued %u bytes", itf, rx_size);
65static void tinyusb_cdc_line_state_changed_callback(
int itf, cdcacm_event_t *event) {
66 USBCDCACMInstance *instance = get_instance_by_itf(itf);
67 if (instance ==
nullptr) {
68 ESP_LOGE(TAG,
"Line state callback: invalid interface %d", itf);
72 int dtr =
event->line_state_changed_data.dtr;
73 int rts =
event->line_state_changed_data.rts;
74 ESP_LOGV(TAG,
"Line state itf=%d: DTR=%d, RTS=%d", itf, dtr, rts);
77 instance->queue_line_state_event(dtr != 0, rts != 0);
80static void tinyusb_cdc_line_coding_changed_callback(
int itf, cdcacm_event_t *event) {
81 USBCDCACMInstance *instance = get_instance_by_itf(itf);
82 if (instance ==
nullptr) {
83 ESP_LOGE(TAG,
"Line coding callback: invalid interface %d", itf);
87 uint32_t bit_rate =
event->line_coding_changed_data.p_line_coding->bit_rate;
88 uint8_t stop_bits =
event->line_coding_changed_data.p_line_coding->stop_bits;
89 uint8_t parity =
event->line_coding_changed_data.p_line_coding->parity;
90 uint8_t data_bits =
event->line_coding_changed_data.p_line_coding->data_bits;
91 ESP_LOGV(TAG,
"Line coding itf=%d: bit_rate=%" PRIu32
" stop_bits=%u parity=%u data_bits=%u", itf, bit_rate,
92 stop_bits, parity, data_bits);
95 instance->queue_line_coding_event(bit_rate, stop_bits, parity, data_bits);
98static esp_err_t ringbuf_read_bytes(RingbufHandle_t ring_buf, uint8_t *out_buf,
size_t out_buf_sz,
size_t *rx_data_size,
99 TickType_t xTicksToWait) {
101 uint8_t *buf =
static_cast<uint8_t *
>(xRingbufferReceiveUpTo(ring_buf, &read_sz, xTicksToWait, out_buf_sz));
103 if (buf ==
nullptr) {
107 memcpy(out_buf, buf, read_sz);
108 vRingbufferReturnItem(ring_buf, (
void *) buf);
109 *rx_data_size = read_sz;
112 buf =
static_cast<uint8_t *
>(xRingbufferReceiveUpTo(ring_buf, &read_sz, 0, out_buf_sz - *rx_data_size));
113 if (buf !=
nullptr) {
114 memcpy(out_buf + *rx_data_size, buf, read_sz);
115 vRingbufferReturnItem(ring_buf, (
void *) buf);
116 *rx_data_size += read_sz;
127 this->
usb_tx_ringbuf_ = xRingbufferCreate(CONFIG_TINYUSB_CDC_TX_BUFSIZE, RINGBUF_TYPE_BYTEBUF);
129 ESP_LOGE(TAG,
"USB TX buffer creation error for itf %d", this->
itf_);
134 this->
usb_rx_ringbuf_ = xRingbufferCreate(CONFIG_TINYUSB_CDC_RX_BUFSIZE, RINGBUF_TYPE_BYTEBUF);
136 ESP_LOGE(TAG,
"USB RX buffer creation error for itf %d", this->
itf_);
142 const tinyusb_config_cdcacm_t acm_cfg = {
143 .usb_dev = TINYUSB_USBDEV_0,
144 .cdc_port =
static_cast<tinyusb_cdcacm_itf_t
>(this->
itf_),
145 .callback_rx = &tinyusb_cdc_rx_callback,
146 .callback_rx_wanted_char = NULL,
147 .callback_line_state_changed = &tinyusb_cdc_line_state_changed_callback,
148 .callback_line_coding_changed = &tinyusb_cdc_line_coding_changed_callback,
151 esp_err_t result = tusb_cdc_acm_init(&acm_cfg);
152 if (result != ESP_OK) {
153 ESP_LOGE(TAG,
"tusb_cdc_acm_init failed: %d", result);
159 const size_t stack_size = esp_log_level_get(TAG) > ESP_LOG_DEBUG ? USB_TX_TASK_STACK_SIZE_VV : USB_TX_TASK_STACK_SIZE;
162 char task_name[] =
"usb_tx_0";
167 ESP_LOGE(TAG,
"Failed to create USB TX task for itf %d", this->
itf_);
186 uint8_t data[CONFIG_TINYUSB_CDC_TX_BUFSIZE] = {0};
187 size_t tx_data_size = 0;
191 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
194 esp_err_t ret = ringbuf_read_bytes(this->
usb_tx_ringbuf_, data, CONFIG_TINYUSB_CDC_TX_BUFSIZE, &tx_data_size, 0);
197 ESP_LOGE(TAG,
"USB TX itf=%d: RingBuf read failed", this->
itf_);
199 }
else if (tx_data_size == 0) {
200 ESP_LOGD(TAG,
"USB TX itf=%d: RingBuf empty, skipping", this->
itf_);
204 ESP_LOGV(TAG,
"USB TX itf=%d: Read %d bytes from buffer", this->
itf_, tx_data_size);
205#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
212 uint8_t *data_head = &data[0];
214 while (tx_data_size > 0) {
216 tinyusb_cdcacm_write_queue(
static_cast<tinyusb_cdcacm_itf_t
>(this->
itf_), data_head, tx_data_size);
217 ESP_LOGV(TAG,
"USB TX itf=%d: enqueued: size=%d, queued=%u", this->
itf_, tx_data_size, queued);
219 tx_data_size -= queued;
222 ESP_LOGV(TAG,
"USB TX itf=%d: waiting 10ms for flush", this->
itf_);
223 esp_err_t flush_ret =
224 tinyusb_cdcacm_write_flush(
static_cast<tinyusb_cdcacm_itf_t
>(this->
itf_), pdMS_TO_TICKS(10));
226 if (flush_ret != ESP_OK) {
227 ESP_LOGE(TAG,
"USB TX itf=%d: flush failed", this->
itf_);
228 tud_cdc_n_write_clear(this->
itf_);
246 if (send_res != pdTRUE) {
247 ESP_LOGW(TAG,
"USB TX itf=%d: buffer full, %u bytes dropped", this->
itf_, len);
277 size_t original_len =
len;
278 size_t bytes_read = 0;
293 uint8_t *buf =
static_cast<uint8_t *
>(xRingbufferReceiveUpTo(this->
usb_rx_ringbuf_, &rx_size, 0,
len));
294 if (buf ==
nullptr) {
298 memcpy(data, buf, rx_size);
300 bytes_read += rx_size;
308 buf =
static_cast<uint8_t *
>(xRingbufferReceiveUpTo(this->
usb_rx_ringbuf_, &rx_size, 0,
len));
309 if (buf ==
nullptr) {
313 memcpy(data, buf, rx_size);
315 bytes_read += rx_size;
317 return bytes_read == original_len;
321 UBaseType_t waiting = 0;
323 vRingbufferGetInfo(this->
usb_rx_ringbuf_,
nullptr,
nullptr,
nullptr,
nullptr, &waiting);
325 return static_cast<int>(waiting) + (this->
has_peek_ ? 1 : 0);
334 UBaseType_t waiting = 1;
335 while (waiting > 0) {
336 vRingbufferGetInfo(this->
usb_tx_ringbuf_,
nullptr,
nullptr,
nullptr,
nullptr, &waiting);
338 vTaskDelay(pdMS_TO_TICKS(1));
343 tinyusb_cdcacm_write_flush(
static_cast<tinyusb_cdcacm_itf_t
>(this->
itf_), pdMS_TO_TICKS(100));
USBCDCACMComponent * parent_
bool read_byte(uint8_t *data)
USBCDCACMInstance * get_interface_by_number(uint8_t itf)
Represents a single CDC ACM interface instance.
TaskHandle_t usb_tx_task_handle_
bool read_array(uint8_t *data, size_t len) override
bool peek_byte(uint8_t *data) override
RingbufHandle_t usb_tx_ringbuf_
void check_logger_conflict() override
static void usb_tx_task_fn(void *arg)
void write_array(const uint8_t *data, size_t len) override
RingbufHandle_t usb_rx_ringbuf_
USBCDCACMComponent * global_usb_cdc_component
char format_hex_char(uint8_t v, char base)
Convert a nibble (0-15) to hex char with specified base ('a' for lowercase, 'A' for uppercase)
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".