#pragma once #if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) #include "esphome/core/component.h" #include "esphome/core/event_pool.h" #include "esphome/core/lock_free_queue.h" #include "esphome/components/uart/uart_component.h" #include #include "freertos/ringbuf.h" #include "tinyusb_cdc_acm.h" namespace esphome::usb_cdc_acm { static const uint8_t EVENT_QUEUE_SIZE = 12; // Callback types for line coding and line state changes using LineCodingCallback = std::function; using LineStateCallback = std::function; // Event types enum CDCEventType : uint8_t { CDC_EVENT_LINE_STATE_CHANGED, CDC_EVENT_LINE_CODING_CHANGED, }; // Event structure for the queue struct CDCEvent { CDCEventType type; union { struct { bool dtr; bool rts; } line_state; struct { uint32_t bit_rate; uint8_t stop_bits; uint8_t parity; uint8_t data_bits; } line_coding; } data; // Required by EventPool - called before returning to pool void release() { // No dynamic memory to clean up, data is stored inline } }; // Forward declaration class USBCDCACMComponent; /// Represents a single CDC ACM interface instance class USBCDCACMInstance : public uart::UARTComponent, public Parented { public: void setup(); void loop(); void dump_config(); void set_interface_number(uint8_t itf) { this->itf_ = itf; } // Get the CDC port number for this instance uint8_t get_itf() const { return this->itf_; } // Ring buffer accessors for bridge components RingbufHandle_t get_tx_ringbuf() const { return this->usb_tx_ringbuf_; } RingbufHandle_t get_rx_ringbuf() const { return this->usb_rx_ringbuf_; } // Task handle accessor for notifying TX task TaskHandle_t get_tx_task_handle() const { return this->usb_tx_task_handle_; } // Callback registration for line coding and line state changes void set_line_coding_callback(LineCodingCallback callback) { this->line_coding_callback_ = std::move(callback); } void set_line_state_callback(LineStateCallback callback) { this->line_state_callback_ = std::move(callback); } // Called from USB core task context queues event for processing in main loop void queue_line_coding_event(uint32_t bit_rate, uint8_t stop_bits, uint8_t parity, uint8_t data_bits); void queue_line_state_event(bool dtr, bool rts); static void usb_tx_task_fn(void *arg); void usb_tx_task(); // UARTComponent interface implementation void write_array(const uint8_t *data, size_t len) override; bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; size_t available() override; uart::UARTFlushResult flush() override; protected: void check_logger_conflict() override; // Process queued events and invoke callbacks (called from main loop) void process_events_(); TaskHandle_t usb_tx_task_handle_{nullptr}; RingbufHandle_t usb_tx_ringbuf_{nullptr}; RingbufHandle_t usb_rx_ringbuf_{nullptr}; // RX buffer for peek functionality uint8_t peek_buffer_{0}; bool has_peek_{false}; uint8_t itf_{0}; // User-registered callbacks (called from main loop) LineCodingCallback line_coding_callback_{nullptr}; LineStateCallback line_state_callback_{nullptr}; // Lock-free queue and event pool for cross-task event passing // Pool sized to queue capacity (SIZE-1) because LockFreeQueue is a ring // buffer that holds N-1 elements. This guarantees allocate() returns nullptr // before push() can fail, preventing both a pool slot leak and an SPSC // violation on the pool's internal free list. EventPool event_pool_; LockFreeQueue event_queue_; }; /// Main USB CDC ACM component that manages the USB device and all CDC interfaces class USBCDCACMComponent : public Component { public: USBCDCACMComponent(); void setup() override; void loop() override; void dump_config() override; float get_setup_priority() const override { return setup_priority::IO; } // Interface management void add_interface(USBCDCACMInstance *interface); USBCDCACMInstance *get_interface_by_number(uint8_t itf); protected: std::array interfaces_{}; }; extern USBCDCACMComponent *global_usb_cdc_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esphome::usb_cdc_acm #endif