ESPHome 2025.10.0-dev
Loading...
Searching...
No Matches
usb_host.h
Go to the documentation of this file.
1#pragma once
2
3// Should not be needed, but it's required to pass CI clang-tidy checks
4#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32P4)
6#include <vector>
7#include "usb/usb_host.h"
8#include <freertos/FreeRTOS.h>
9#include <freertos/task.h>
12#include <atomic>
13
14namespace esphome {
15namespace usb_host {
16
17// THREADING MODEL:
18// This component uses a dedicated USB task for event processing to prevent data loss.
19// - USB Task (high priority): Handles USB events, executes transfer callbacks
20// - Main Loop Task: Initiates transfers, processes completion events
21//
22// Thread-safe communication:
23// - Lock-free queues for USB task -> main loop events (SPSC pattern)
24// - Lock-free TransferRequest pool using atomic bitmask (MCSP pattern)
25//
26// TransferRequest pool access pattern:
27// - get_trq_() [allocate]: Called from BOTH USB task and main loop threads
28// * USB task: via USB UART input callbacks that restart transfers immediately
29// * Main loop: for output transfers and flow-controlled input restarts
30// - release_trq() [deallocate]: Called from main loop thread only
31//
32// The multi-threaded allocation is intentional for performance:
33// - USB task can immediately restart input transfers without context switching
34// - Main loop controls backpressure by deciding when to restart after consuming data
35// The atomic bitmask ensures thread-safe allocation without mutex blocking.
36
37static const char *const TAG = "usb_host";
38
39// Forward declarations
40struct TransferRequest;
41class USBClient;
42
43// constants for setup packet type
44static const uint8_t USB_RECIP_DEVICE = 0;
45static const uint8_t USB_RECIP_INTERFACE = 1;
46static const uint8_t USB_RECIP_ENDPOINT = 2;
47static const uint8_t USB_TYPE_STANDARD = 0 << 5;
48static const uint8_t USB_TYPE_CLASS = 1 << 5;
49static const uint8_t USB_TYPE_VENDOR = 2 << 5;
50static const uint8_t USB_DIR_MASK = 1 << 7;
51static const uint8_t USB_DIR_IN = 1 << 7;
52static const uint8_t USB_DIR_OUT = 0;
53static const size_t SETUP_PACKET_SIZE = 8;
54
55static const size_t MAX_REQUESTS = 16; // maximum number of outstanding requests possible.
56static_assert(MAX_REQUESTS <= 16, "MAX_REQUESTS must be <= 16 to fit in uint16_t bitmask");
57static constexpr size_t USB_EVENT_QUEUE_SIZE = 32; // Size of event queue between USB task and main loop
58static constexpr size_t USB_TASK_STACK_SIZE = 4096; // Stack size for USB task (same as ESP-IDF USB examples)
59static constexpr UBaseType_t USB_TASK_PRIORITY = 5; // Higher priority than main loop (tskIDLE_PRIORITY + 5)
60
61// used to report a transfer status
63 bool success;
64 uint16_t error_code;
65 uint8_t *data;
66 size_t data_len;
67 uint8_t endpoint;
68 void *user_data;
69};
70
71using transfer_cb_t = std::function<void(const TransferStatus &)>;
72
73class USBClient;
74
75// struct used to capture all data needed for a transfer
82
89
90struct UsbEvent {
92 union {
93 struct {
94 uint8_t address;
96 struct {
97 usb_device_handle_t handle;
99 struct {
103
104 // Required for EventPool - no cleanup needed for POD types
105 void release() {}
106};
107
108// callback function type.
109
118class USBClient : public Component {
119 friend class USBHost;
120
121 public:
122 USBClient(uint16_t vid, uint16_t pid) : vid_(vid), pid_(pid), trq_in_use_(0) {}
123 void setup() override;
124 void loop() override;
125 // setup must happen after the host bus has been setup
126 float get_setup_priority() const override { return setup_priority::IO; }
127 void on_opened(uint8_t addr);
128 void on_removed(usb_device_handle_t handle);
129 void control_transfer_callback(const usb_transfer_t *xfer) const;
130 void transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length);
131 void transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length);
132 void dump_config() override;
133 void release_trq(TransferRequest *trq);
134 bool control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index, const transfer_cb_t &callback,
135 const std::vector<uint8_t> &data = {});
136
137 // Lock-free event queue and pool for USB task to main loop communication
138 // Must be public for access from static callbacks
141
142 protected:
143 bool register_();
144 TransferRequest *get_trq_(); // Lock-free allocation using atomic bitmask (multi-consumer safe)
145 virtual void disconnect();
146 virtual void on_connected() {}
147 virtual void on_disconnected() {
148 // Reset all requests to available (all bits to 0)
149 this->trq_in_use_.store(0);
150 }
151
152 // USB task management
153 static void usb_task_fn(void *arg);
154 void usb_task_loop();
155
156 TaskHandle_t usb_task_handle_{nullptr};
157
158 usb_host_client_handle_t handle_{};
159 usb_device_handle_t device_handle_{};
162 uint16_t vid_{};
163 uint16_t pid_{};
164 // Lock-free pool management using atomic bitmask (no dynamic allocation)
165 // Bit i = 1: requests_[i] is in use, Bit i = 0: requests_[i] is available
166 // Supports multiple concurrent consumers (both threads can allocate)
167 // Single producer for deallocation (main loop only)
168 // Limited to 16 slots by uint16_t size (enforced by static_assert)
169 std::atomic<uint16_t> trq_in_use_;
170 TransferRequest requests_[MAX_REQUESTS]{};
171};
172class USBHost : public Component {
173 public:
174 float get_setup_priority() const override { return setup_priority::BUS; }
175 void loop() override;
176 void setup() override;
177
178 protected:
179 std::vector<USBClient *> clients_{};
180};
181
182} // namespace usb_host
183} // namespace esphome
184
185#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
USBClient(uint16_t vid, uint16_t pid)
Definition usb_host.h:122
usb_host_client_handle_t handle_
Definition usb_host.h:158
TransferRequest requests_[MAX_REQUESTS]
Definition usb_host.h:170
void transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length)
Performs a transfer input operation.
void transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length)
Performs an output transfer operation.
float get_setup_priority() const override
Definition usb_host.h:126
void control_transfer_callback(const usb_transfer_t *xfer) const
void release_trq(TransferRequest *trq)
std::atomic< uint16_t > trq_in_use_
Definition usb_host.h:169
static void usb_task_fn(void *arg)
virtual void on_connected()
Definition usb_host.h:146
TaskHandle_t usb_task_handle_
Definition usb_host.h:156
EventPool< UsbEvent, USB_EVENT_QUEUE_SIZE > event_pool
Definition usb_host.h:140
virtual void on_disconnected()
Definition usb_host.h:147
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={})
void on_removed(usb_device_handle_t handle)
usb_device_handle_t device_handle_
Definition usb_host.h:159
LockFreeQueue< UsbEvent, USB_EVENT_QUEUE_SIZE > event_queue
Definition usb_host.h:139
std::vector< USBClient * > clients_
Definition usb_host.h:179
float get_setup_priority() const override
Definition usb_host.h:174
uint16_t type
const float BUS
For communication buses like i2c/spi.
Definition component.cpp:46
const float IO
For components that represent GPIO pins like PCF8573.
Definition component.cpp:47
std::function< void(const TransferStatus &)> transfer_cb_t
Definition usb_host.h:71
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
struct esphome::usb_host::UsbEvent::@152::@155 transfer
usb_device_handle_t handle
Definition usb_host.h:97
struct esphome::usb_host::UsbEvent::@152::@153 device_new
union esphome::usb_host::UsbEvent::@152 data
struct esphome::usb_host::UsbEvent::@152::@154 device_gone
TransferRequest * trq
Definition usb_host.h:100
uint16_t length
Definition tt21100.cpp:0