diff --git a/esphome/components/usb_uart/usb_uart.cpp b/esphome/components/usb_uart/usb_uart.cpp index dc9ba4af7a..c24fffb11d 100644 --- a/esphome/components/usb_uart/usb_uart.cpp +++ b/esphome/components/usb_uart/usb_uart.cpp @@ -169,98 +169,6 @@ bool USBUartChannel::read_array(uint8_t *data, size_t len) { this->parent_->start_input(this); return status; } -void USBUartComponent::reset_input_state_(USBUartChannel *channel) { - channel->input_retry_count_.store(0); - channel->input_started_.store(false); -} - -void USBUartComponent::restart_input_(USBUartChannel *channel) { - // Atomically verify it's still started (true) and keep it started - // This prevents the race window of toggling true->false->true - bool expected = true; - if (channel->input_started_.compare_exchange_strong(expected, true)) { - // Still started - do the actual restart work without toggling the flag - this->do_start_input_(channel); - } -} - -void USBUartComponent::input_transfer_callback_(USBUartChannel *channel, const usb_host::TransferStatus &status) { - // CALLBACK CONTEXT: This function is executed in USB task via transfer_callback - ESP_LOGV(TAG, "Transfer result: length: %u; status %X", status.data_len, status.error_code); - - if (!status.success) { - ESP_LOGE(TAG, "Control transfer failed, status=%s", esp_err_to_name(status.error_code)); - // Transfer failed, slot already released - // Reset state so normal operations can restart later - this->reset_input_state_(channel); - return; - } - - if (!channel->dummy_receiver_ && status.data_len > 0) { - // Allocate a chunk from the pool - UsbDataChunk *chunk = this->chunk_pool_.allocate(); - if (chunk == nullptr) { - // No chunks available - queue is full, data dropped, slot already released - this->usb_data_queue_.increment_dropped_count(); - // Reset state so normal operations can restart later - this->reset_input_state_(channel); - return; - } - - // Copy data to chunk (this is fast, happens in USB task) - memcpy(chunk->data, status.data, status.data_len); - chunk->length = status.data_len; - chunk->channel = channel; - - // Push to lock-free queue for main loop processing - // Push always succeeds because pool size == queue size - this->usb_data_queue_.push(chunk); - } - - // On success, reset retry count and restart input immediately from USB task for performance - // The lock-free queue will handle backpressure - channel->input_retry_count_.store(0); - channel->input_started_.store(false); - this->start_input(channel); -} - -void USBUartComponent::do_start_input_(USBUartChannel *channel) { - // This function does the actual work of starting input - // Caller must ensure input_started_ is already set to true - const auto *ep = channel->cdc_dev_.in_ep; - - // input_started_ already set to true by caller - auto result = this->transfer_in( - ep->bEndpointAddress, - [this, channel](const usb_host::TransferStatus &status) { this->input_transfer_callback_(channel, status); }, - ep->wMaxPacketSize); - - if (result == usb_host::TRANSFER_ERROR_NO_SLOTS) { - // No slots available - defer retry to main loop - this->defer_input_retry_(channel); - } else if (result != usb_host::TRANSFER_OK) { - // Other error (submit failed) - don't retry, just reset state - // Error already logged by transfer_in() - this->reset_input_state_(channel); - } -} - -void USBUartComponent::defer_input_retry_(USBUartChannel *channel) { - static constexpr uint8_t MAX_INPUT_RETRIES = 10; - - // Atomically increment and get the NEW value (previous + 1) - uint8_t new_retry_count = channel->input_retry_count_.fetch_add(1) + 1; - if (new_retry_count > MAX_INPUT_RETRIES) { - ESP_LOGE(TAG, "Input retry limit reached for channel %d, stopping retries", channel->index_); - this->reset_input_state_(channel); - return; - } - - // Keep input_started_ as true during defer to prevent multiple retries from queueing - // The deferred lambda will atomically restart - this->defer([this, channel] { this->restart_input_(channel); }); -} - void USBUartComponent::setup() { USBClient::setup(); } void USBUartComponent::loop() { USBClient::loop(); @@ -308,12 +216,6 @@ void USBUartComponent::dump_config() { void USBUartComponent::start_input(USBUartChannel *channel) { if (!channel->initialised_.load()) return; - - // Atomically check if not started and set to started in one operation - bool expected = false; - if (!channel->input_started_.compare_exchange_strong(expected, true)) - return; // Already started - prevents duplicate transfers from concurrent threads - // THREAD CONTEXT: Called from both USB task and main loop threads // - USB task: Immediate restart after successful transfer for continuous data flow // - Main loop: Controlled restart after consuming data (backpressure mechanism) @@ -324,11 +226,6 @@ void USBUartComponent::start_input(USBUartChannel *channel) { // // The underlying transfer_in() uses lock-free atomic allocation from the // TransferRequest pool, making this multi-threaded access safe -<<<<<<< HEAD - - // Do the actual work (input_started_ already set to true by CAS above) - this->do_start_input_(channel); -======= // if already started, don't restart. A spurious failure in compare_exchange_weak // is not a problem, as it will be retried on the next read_array() @@ -375,7 +272,6 @@ void USBUartComponent::start_input(USBUartChannel *channel) { if (!this->transfer_in(ep->bEndpointAddress, callback, ep->wMaxPacketSize)) { channel->input_started_.store(false); } ->>>>>>> clydebarrow/usb-uart } void USBUartComponent::start_output(USBUartChannel *channel) { @@ -482,7 +378,7 @@ void USBUartTypeCdcAcm::enable_channels() { for (auto *channel : this->channels_) { if (!channel->initialised_.load()) continue; - this->reset_input_state_(channel); + channel->input_started_.store(false); channel->output_started_.store(false); this->start_input(channel); } diff --git a/esphome/components/usb_uart/usb_uart.h b/esphome/components/usb_uart/usb_uart.h index ba7fc3ebe5..a5e7905ac5 100644 --- a/esphome/components/usb_uart/usb_uart.h +++ b/esphome/components/usb_uart/usb_uart.h @@ -111,11 +111,10 @@ class USBUartChannel : public uart::UARTComponent, public Parented input_started_{true}; std::atomic output_started_{true}; std::atomic initialised_{false}; - std::atomic input_retry_count_{0}; // Group regular bytes together to minimize padding const uint8_t index_; bool debug_{}; @@ -141,11 +140,6 @@ class USBUartComponent : public usb_host::USBClient { EventPool chunk_pool_; protected: - void defer_input_retry_(USBUartChannel *channel); - void reset_input_state_(USBUartChannel *channel); - void restart_input_(USBUartChannel *channel); - void do_start_input_(USBUartChannel *channel); - void input_transfer_callback_(USBUartChannel *channel, const usb_host::TransferStatus &status); std::vector channels_{}; };