1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 15:12:06 +00:00

Merge branch 'integration' into memory_api

This commit is contained in:
J. Nick Koston
2025-10-29 18:13:22 -05:00
2 changed files with 2 additions and 112 deletions

View File

@@ -169,98 +169,6 @@ bool USBUartChannel::read_array(uint8_t *data, size_t len) {
this->parent_->start_input(this); this->parent_->start_input(this);
return status; 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::setup() { USBClient::setup(); }
void USBUartComponent::loop() { void USBUartComponent::loop() {
USBClient::loop(); USBClient::loop();
@@ -308,12 +216,6 @@ void USBUartComponent::dump_config() {
void USBUartComponent::start_input(USBUartChannel *channel) { void USBUartComponent::start_input(USBUartChannel *channel) {
if (!channel->initialised_.load()) if (!channel->initialised_.load())
return; 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 // THREAD CONTEXT: Called from both USB task and main loop threads
// - USB task: Immediate restart after successful transfer for continuous data flow // - USB task: Immediate restart after successful transfer for continuous data flow
// - Main loop: Controlled restart after consuming data (backpressure mechanism) // - 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 // The underlying transfer_in() uses lock-free atomic allocation from the
// TransferRequest pool, making this multi-threaded access safe // 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 // 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() // 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)) { if (!this->transfer_in(ep->bEndpointAddress, callback, ep->wMaxPacketSize)) {
channel->input_started_.store(false); channel->input_started_.store(false);
} }
>>>>>>> clydebarrow/usb-uart
} }
void USBUartComponent::start_output(USBUartChannel *channel) { void USBUartComponent::start_output(USBUartChannel *channel) {
@@ -482,7 +378,7 @@ void USBUartTypeCdcAcm::enable_channels() {
for (auto *channel : this->channels_) { for (auto *channel : this->channels_) {
if (!channel->initialised_.load()) if (!channel->initialised_.load())
continue; continue;
this->reset_input_state_(channel); channel->input_started_.store(false);
channel->output_started_.store(false); channel->output_started_.store(false);
this->start_input(channel); this->start_input(channel);
} }

View File

@@ -111,11 +111,10 @@ class USBUartChannel : public uart::UARTComponent, public Parented<USBUartCompon
CdcEps cdc_dev_{}; CdcEps cdc_dev_{};
// Enum (likely 4 bytes) // Enum (likely 4 bytes)
UARTParityOptions parity_{UART_CONFIG_PARITY_NONE}; UARTParityOptions parity_{UART_CONFIG_PARITY_NONE};
// Group atomics together // Group atomics together (each 1 byte)
std::atomic<bool> input_started_{true}; std::atomic<bool> input_started_{true};
std::atomic<bool> output_started_{true}; std::atomic<bool> output_started_{true};
std::atomic<bool> initialised_{false}; std::atomic<bool> initialised_{false};
std::atomic<uint8_t> input_retry_count_{0};
// Group regular bytes together to minimize padding // Group regular bytes together to minimize padding
const uint8_t index_; const uint8_t index_;
bool debug_{}; bool debug_{};
@@ -141,11 +140,6 @@ class USBUartComponent : public usb_host::USBClient {
EventPool<UsbDataChunk, USB_DATA_QUEUE_SIZE> chunk_pool_; EventPool<UsbDataChunk, USB_DATA_QUEUE_SIZE> chunk_pool_;
protected: 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<USBUartChannel *> channels_{}; std::vector<USBUartChannel *> channels_{};
}; };