mirror of
https://github.com/esphome/esphome.git
synced 2025-09-26 07:02:21 +01:00
lock free
This commit is contained in:
@@ -7,8 +7,8 @@
|
|||||||
#include "usb/usb_host.h"
|
#include "usb/usb_host.h"
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
#include <freertos/semphr.h>
|
#include "esphome/core/lock_free_queue.h"
|
||||||
#include <freertos/queue.h>
|
#include "esphome/core/event_pool.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@@ -79,6 +79,9 @@ struct UsbEvent {
|
|||||||
TransferRequest *trq;
|
TransferRequest *trq;
|
||||||
} transfer;
|
} transfer;
|
||||||
} data;
|
} data;
|
||||||
|
|
||||||
|
// Required for EventPool - no cleanup needed for POD types
|
||||||
|
void release() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// callback function type.
|
// callback function type.
|
||||||
@@ -115,7 +118,11 @@ class USBClient : public Component {
|
|||||||
void release_trq(TransferRequest *trq);
|
void release_trq(TransferRequest *trq);
|
||||||
bool control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index, const transfer_cb_t &callback,
|
bool control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index, const transfer_cb_t &callback,
|
||||||
const std::vector<uint8_t> &data = {});
|
const std::vector<uint8_t> &data = {});
|
||||||
QueueHandle_t get_event_queue() { return event_queue_; }
|
|
||||||
|
// Lock-free event queue and pool for USB task to main loop communication
|
||||||
|
// Must be public for access from static callbacks
|
||||||
|
LockFreeQueue<UsbEvent, USB_EVENT_QUEUE_SIZE> event_queue;
|
||||||
|
EventPool<UsbEvent, USB_EVENT_QUEUE_SIZE> event_pool;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool register_();
|
bool register_();
|
||||||
@@ -129,7 +136,6 @@ class USBClient : public Component {
|
|||||||
void usb_task_loop();
|
void usb_task_loop();
|
||||||
|
|
||||||
TaskHandle_t usb_task_handle_{nullptr};
|
TaskHandle_t usb_task_handle_{nullptr};
|
||||||
QueueHandle_t event_queue_{nullptr}; // Queue of UsbEvent structs
|
|
||||||
|
|
||||||
usb_host_client_handle_t handle_{};
|
usb_host_client_handle_t handle_{};
|
||||||
usb_device_handle_t device_handle_{};
|
usb_device_handle_t device_handle_{};
|
||||||
|
@@ -142,28 +142,37 @@ static std::string get_descriptor_string(const usb_str_desc_t *desc) {
|
|||||||
// CALLBACK CONTEXT: USB task (called from usb_host_client_handle_events in USB task)
|
// CALLBACK CONTEXT: USB task (called from usb_host_client_handle_events in USB task)
|
||||||
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *ptr) {
|
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *ptr) {
|
||||||
auto *client = static_cast<USBClient *>(ptr);
|
auto *client = static_cast<USBClient *>(ptr);
|
||||||
UsbEvent event;
|
|
||||||
|
// Allocate event from pool
|
||||||
|
UsbEvent *event = client->event_pool.allocate();
|
||||||
|
if (event == nullptr) {
|
||||||
|
// No events available - increment counter for periodic logging
|
||||||
|
client->event_queue.increment_dropped_count();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Queue events to be processed in main loop
|
// Queue events to be processed in main loop
|
||||||
switch (event_msg->event) {
|
switch (event_msg->event) {
|
||||||
case USB_HOST_CLIENT_EVENT_NEW_DEV: {
|
case USB_HOST_CLIENT_EVENT_NEW_DEV: {
|
||||||
ESP_LOGD(TAG, "New device %d", event_msg->new_dev.address);
|
ESP_LOGD(TAG, "New device %d", event_msg->new_dev.address);
|
||||||
event.type = EVENT_DEVICE_NEW;
|
event->type = EVENT_DEVICE_NEW;
|
||||||
event.data.device_new.address = event_msg->new_dev.address;
|
event->data.device_new.address = event_msg->new_dev.address;
|
||||||
xQueueSend(client->get_event_queue(), &event, portMAX_DELAY);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case USB_HOST_CLIENT_EVENT_DEV_GONE: {
|
case USB_HOST_CLIENT_EVENT_DEV_GONE: {
|
||||||
ESP_LOGD(TAG, "Device gone");
|
ESP_LOGD(TAG, "Device gone");
|
||||||
event.type = EVENT_DEVICE_GONE;
|
event->type = EVENT_DEVICE_GONE;
|
||||||
event.data.device_gone.handle = event_msg->dev_gone.dev_hdl;
|
event->data.device_gone.handle = event_msg->dev_gone.dev_hdl;
|
||||||
xQueueSend(client->get_event_queue(), &event, portMAX_DELAY);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
ESP_LOGD(TAG, "Unknown event %d", event_msg->event);
|
ESP_LOGD(TAG, "Unknown event %d", event_msg->event);
|
||||||
break;
|
client->event_pool.release(event);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Push to lock-free queue (always succeeds since pool size == queue size)
|
||||||
|
client->event_queue.push(event);
|
||||||
}
|
}
|
||||||
void USBClient::setup() {
|
void USBClient::setup() {
|
||||||
usb_host_client_config_t config{.is_synchronous = false,
|
usb_host_client_config_t config{.is_synchronous = false,
|
||||||
@@ -181,14 +190,6 @@ void USBClient::setup() {
|
|||||||
trq->client = this;
|
trq->client = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create event queue for communication between USB task and main loop
|
|
||||||
this->event_queue_ = xQueueCreate(USB_EVENT_QUEUE_SIZE, sizeof(UsbEvent));
|
|
||||||
if (this->event_queue_ == nullptr) {
|
|
||||||
ESP_LOGE(TAG, "Failed to create event queue");
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and start USB task
|
// Create and start USB task
|
||||||
xTaskCreatePinnedToCore(usb_task_fn, "usb_task",
|
xTaskCreatePinnedToCore(usb_task_fn, "usb_task",
|
||||||
USB_TASK_STACK_SIZE, // Stack size
|
USB_TASK_STACK_SIZE, // Stack size
|
||||||
@@ -219,23 +220,31 @@ void USBClient::usb_task_loop() {
|
|||||||
|
|
||||||
void USBClient::loop() {
|
void USBClient::loop() {
|
||||||
// Process any events from the USB task
|
// Process any events from the USB task
|
||||||
UsbEvent event;
|
UsbEvent *event;
|
||||||
while (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) {
|
while ((event = this->event_queue.pop()) != nullptr) {
|
||||||
switch (event.type) {
|
switch (event->type) {
|
||||||
case EVENT_DEVICE_NEW:
|
case EVENT_DEVICE_NEW:
|
||||||
this->on_opened(event.data.device_new.address);
|
this->on_opened(event->data.device_new.address);
|
||||||
break;
|
break;
|
||||||
case EVENT_DEVICE_GONE:
|
case EVENT_DEVICE_GONE:
|
||||||
this->on_removed(event.data.device_gone.handle);
|
this->on_removed(event->data.device_gone.handle);
|
||||||
break;
|
break;
|
||||||
case EVENT_TRANSFER_COMPLETE:
|
case EVENT_TRANSFER_COMPLETE:
|
||||||
case EVENT_CONTROL_COMPLETE: {
|
case EVENT_CONTROL_COMPLETE: {
|
||||||
auto *trq = event.data.transfer.trq;
|
auto *trq = event->data.transfer.trq;
|
||||||
// Callback was already executed in USB task, just cleanup
|
// Callback was already executed in USB task, just cleanup
|
||||||
this->release_trq(trq);
|
this->release_trq(trq);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Return event to pool for reuse
|
||||||
|
this->event_pool.release(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log dropped events periodically
|
||||||
|
uint16_t dropped = this->event_queue.get_and_reset_dropped_count();
|
||||||
|
if (dropped > 0) {
|
||||||
|
ESP_LOGW(TAG, "Dropped %u USB events due to queue overflow", dropped);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this->state_) {
|
switch (this->state_) {
|
||||||
@@ -309,10 +318,21 @@ void USBClient::on_removed(usb_device_handle_t handle) {
|
|||||||
|
|
||||||
// Helper to queue transfer cleanup to main loop
|
// Helper to queue transfer cleanup to main loop
|
||||||
static void queue_transfer_cleanup(TransferRequest *trq, EventType type) {
|
static void queue_transfer_cleanup(TransferRequest *trq, EventType type) {
|
||||||
UsbEvent event;
|
auto *client = trq->client;
|
||||||
event.type = type;
|
|
||||||
event.data.transfer.trq = trq;
|
// Allocate event from pool
|
||||||
xQueueSend(trq->client->get_event_queue(), &event, portMAX_DELAY);
|
UsbEvent *event = client->event_pool.allocate();
|
||||||
|
if (event == nullptr) {
|
||||||
|
// No events available - increment counter for periodic logging
|
||||||
|
client->event_queue.increment_dropped_count();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event->type = type;
|
||||||
|
event->data.transfer.trq = trq;
|
||||||
|
|
||||||
|
// Push to lock-free queue (always succeeds since pool size == queue size)
|
||||||
|
client->event_queue.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALLBACK CONTEXT: USB task (called from usb_host_client_handle_events in USB task)
|
// CALLBACK CONTEXT: USB task (called from usb_host_client_handle_events in USB task)
|
||||||
|
Reference in New Issue
Block a user