arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioHostBackendESP32.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstring>
4#include <vector>
5
6#include "AudioLogger.h"
9#include "esp_intr_alloc.h"
10#include "esp_timer.h"
11#include "freertos/FreeRTOS.h"
12#include "freertos/task.h"
13#include "usb/usb_host.h"
14
15namespace audio_tools {
16
35 public:
37 static constexpr int kPacketsPerTransfer = 4;
39 static constexpr int kTransfersInFlight = 2;
40
42 // the USB host library stays installed: other clients might use it
43 if (client_ != nullptr) {
45 usb_host_client_deregister(client_);
46 client_ = nullptr;
47 }
48 }
49
50 bool begin(USBAudioHostEvents* events, const USBAudioConfig& cfg) override {
51 events_ = events;
52 (void)cfg;
53 if (!installLibrary()) return false;
54 if (client_ == nullptr) {
55 usb_host_client_config_t client_cfg;
56 memset(&client_cfg, 0, sizeof(client_cfg));
57 client_cfg.is_synchronous = false;
58 client_cfg.max_num_event_msg = 5;
59 client_cfg.async.client_event_callback = clientEvent;
60 client_cfg.async.callback_arg = this;
61 esp_err_t rc = usb_host_client_register(&client_cfg, &client_);
62 if (rc != ESP_OK) {
63 LOGE("usb_host_client_register: %s", esp_err_to_name(rc));
64 client_ = nullptr;
65 return false;
66 }
67 }
68 if (ctrl_xfer_ == nullptr) {
69 esp_err_t rc =
70 usb_host_transfer_alloc(kCtrlXferSize, 0, &ctrl_xfer_);
71 if (rc != ESP_OK) {
72 LOGE("usb_host_transfer_alloc: %s", esp_err_to_name(rc));
73 return false;
74 }
75 }
76 return true;
77 }
78
79 void end() override {
80 for (auto& s : streams_) {
81 if (s.open) closeStream(daddr_, s.ep_addr);
82 }
83 }
84
85 void task() override {
86 if (client_ == nullptr) {
87 vTaskDelay(1);
88 return;
89 }
90 // processes client events and transfer completions (callbacks)
91 usb_host_client_handle_events(client_, 1);
92 }
93
94 bool control(uint8_t daddr, const UsbSetupPacket& setup,
95 uint8_t* data) override {
96 if (dev_ == nullptr || daddr != daddr_ || ctrl_xfer_ == nullptr)
97 return false;
98 if (setup.wLength + 8 > kCtrlXferSize) return false;
99 usb_setup_packet_t* pkt = (usb_setup_packet_t*)ctrl_xfer_->data_buffer;
100 pkt->bmRequestType = setup.bmRequestType;
101 pkt->bRequest = setup.bRequest;
102 pkt->wValue = setup.wValue;
103 pkt->wIndex = setup.wIndex;
104 pkt->wLength = setup.wLength;
105 bool is_in = (setup.bmRequestType & 0x80) != 0;
106 if (!is_in && setup.wLength > 0 && data != nullptr)
107 memcpy(ctrl_xfer_->data_buffer + 8, data, setup.wLength);
108 ctrl_xfer_->num_bytes = 8 + setup.wLength;
109 ctrl_xfer_->device_handle = dev_;
110 ctrl_xfer_->bEndpointAddress = 0;
111 ctrl_xfer_->callback = controlComplete;
112 ctrl_xfer_->context = this;
113 ctrl_xfer_->timeout_ms = 1000;
114 ctrl_data_ = is_in ? data : nullptr;
115 ctrl_len_ = setup.wLength;
116 esp_err_t rc = usb_host_transfer_submit_control(client_, ctrl_xfer_);
117 if (rc != ESP_OK) {
118 LOGW("usb_host_transfer_submit_control: %s", esp_err_to_name(rc));
119 return false;
120 }
121 return true;
122 }
123
126 bool setInterface(uint8_t daddr, uint8_t itf, uint8_t alt) override {
127 if (dev_ == nullptr || daddr != daddr_) return false;
128 Interface* claimed = findInterface(itf);
129 if (claimed != nullptr && claimed->claimed && claimed->alt != alt) {
130 if (!releaseInterface(*claimed)) return false; // retry later
131 }
132 if (alt != 0 && (claimed == nullptr || !claimed->claimed)) {
133 esp_err_t rc = usb_host_interface_claim(client_, dev_, itf, alt);
134 if (rc != ESP_OK) {
135 LOGE("usb_host_interface_claim %d/%d: %s", itf, alt,
136 esp_err_to_name(rc));
137 return false;
138 }
139 if (claimed == nullptr) {
140 interfaces_.push_back(Interface());
141 claimed = &interfaces_.back();
142 }
143 claimed->itf = itf;
144 claimed->alt = alt;
145 claimed->claimed = true;
146 }
148 setup.bmRequestType = 0x01; // standard, host to device, interface
149 setup.bRequest = kUsbReqSetInterface;
150 setup.wValue = alt;
151 setup.wIndex = itf;
152 setup.wLength = 0;
153 return control(daddr, setup, nullptr);
154 }
155
156 bool openStream(uint8_t daddr, const UsbEndpointDescriptorView& ep,
157 UsbHostStreamRole role) override {
158 if (dev_ == nullptr || daddr != daddr_) return false;
160 if (s == nullptr) {
161 streams_.push_back(Stream());
162 s = &streams_.back();
163 }
164 if (s->in_flight > 0) {
165 LOGE("Endpoint 0x%x is still busy", ep.bEndpointAddress);
166 return false;
167 }
168 freeTransfers(*s);
170 s->role = role;
171 s->max_packet = ep.packetSize();
173 for (int j = 0; j < kTransfersInFlight; j++) {
174 usb_transfer_t* xfer = nullptr;
175 esp_err_t rc = usb_host_transfer_alloc(
176 (size_t)s->max_packet * s->packets, s->packets, &xfer);
177 if (rc != ESP_OK) {
178 LOGE("usb_host_transfer_alloc: %s", esp_err_to_name(rc));
179 freeTransfers(*s);
180 return false;
181 }
182 xfer->device_handle = dev_;
183 xfer->bEndpointAddress = ep.bEndpointAddress;
184 xfer->callback = isoComplete;
185 xfer->context = this;
186 s->transfers.push_back(xfer);
187 }
188 s->open = true;
189 for (auto xfer : s->transfers) submit(*s, xfer);
190 return true;
191 }
192
193 void closeStream(uint8_t daddr, uint8_t ep_addr) override {
194 (void)daddr;
195 Stream* s = findStream(ep_addr);
196 if (s == nullptr || !s->open) return;
197 s->open = false;
198 if (dev_ == nullptr) return;
199 // cancels the transfers in flight: they are returned via isoComplete()
200 usb_host_endpoint_halt(dev_, ep_addr);
201 usb_host_endpoint_flush(dev_, ep_addr);
202 usb_host_endpoint_clear(dev_, ep_addr);
203 }
204
205 UsbSpeed speed() const override {
206 if (dev_ == nullptr) return UsbSpeed::Full;
207 usb_device_info_t info;
208 if (usb_host_device_info(dev_, &info) != ESP_OK) return UsbSpeed::Full;
209 if (info.speed == USB_SPEED_HIGH) return UsbSpeed::High;
210 if (info.speed == USB_SPEED_LOW) return UsbSpeed::Low;
211 return UsbSpeed::Full;
212 }
213
214 uint32_t millis() const override {
215 return (uint32_t)(esp_timer_get_time() / 1000);
216 }
217
218 protected:
219 static constexpr size_t kCtrlXferSize = 8 + 512;
220
221 struct Stream {
222 uint8_t ep_addr = 0;
224 uint16_t max_packet = 0;
225 int packets = 1;
226 bool open = false;
227 int in_flight = 0;
228 std::vector<usb_transfer_t*> transfers;
229 };
230
231 struct Interface {
232 uint8_t itf = 0;
233 uint8_t alt = 0;
234 bool claimed = false;
235 };
236
237 inline static bool s_library_installed_ = false;
238 inline static TaskHandle_t s_library_task_ = nullptr;
240 usb_host_client_handle_t client_ = nullptr;
241 usb_device_handle_t dev_ = nullptr;
242 uint8_t daddr_ = 0;
243 usb_transfer_t* ctrl_xfer_ = nullptr;
244 uint8_t* ctrl_data_ = nullptr;
245 uint16_t ctrl_len_ = 0;
246 std::vector<Stream> streams_;
247 std::vector<Interface> interfaces_;
248
251 static bool installLibrary() {
252 if (s_library_installed_) return true;
253 usb_host_config_t host_cfg;
254 memset(&host_cfg, 0, sizeof(host_cfg));
255 host_cfg.skip_phy_setup = false;
256 host_cfg.intr_flags = ESP_INTR_FLAG_LEVEL1;
257 esp_err_t rc = usb_host_install(&host_cfg);
258 if (rc == ESP_ERR_INVALID_STATE) {
259 LOGI("USB host library is already installed");
260 } else if (rc != ESP_OK) {
261 LOGE("usb_host_install: %s", esp_err_to_name(rc));
262 return false;
263 } else {
264 xTaskCreate(libraryTask, "usb_host_lib", 4096, nullptr, 5,
266 }
268 return true;
269 }
270
271 static void libraryTask(void*) {
272 while (true) {
273 uint32_t flags = 0;
274 usb_host_lib_handle_events(portMAX_DELAY, &flags);
275 if (flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
276 usb_host_device_free_all();
277 }
278 }
279 }
280
281 static void clientEvent(const usb_host_client_event_msg_t* msg, void* arg) {
283 switch (msg->event) {
284 case USB_HOST_CLIENT_EVENT_NEW_DEV:
285 self->openDevice(msg->new_dev.address);
286 break;
287 case USB_HOST_CLIENT_EVENT_DEV_GONE:
288 if (msg->dev_gone.dev_hdl == self->dev_) {
289 uint8_t daddr = self->daddr_;
290 self->closeDevice();
291 if (self->events_) self->events_->onDetach(daddr);
292 }
293 break;
294 default:
295 break;
296 }
297 }
298
301 void openDevice(uint8_t address) {
302 if (dev_ != nullptr || events_ == nullptr) return;
303 usb_device_handle_t dev = nullptr;
304 if (usb_host_device_open(client_, address, &dev) != ESP_OK) return;
305 const usb_device_desc_t* dev_desc = nullptr;
306 const usb_config_desc_t* cfg_desc = nullptr;
307 if (usb_host_get_device_descriptor(dev, &dev_desc) != ESP_OK ||
308 usb_host_get_active_config_descriptor(dev, &cfg_desc) != ESP_OK) {
309 usb_host_device_close(client_, dev);
310 return;
311 }
312 const uint8_t* start = (const uint8_t*)cfg_desc;
313 const uint8_t* end = start + cfg_desc->wTotalLength;
314 // the class logic sends control requests to dev_
315 dev_ = dev;
316 daddr_ = address;
317 bool accepted = false;
318 for (const uint8_t* p = start; p + 2 <= end && p[0] >= 2; p += p[0]) {
319 if (p[1] != kUsbDescTypeInterface || p[0] < 9) continue;
320 if (p[5] != kUsbClassAudio || p[6] != kAudioSubclassControl) continue;
321 uint16_t len = events_->onAttach(address, dev_desc->idVendor,
322 dev_desc->idProduct, p,
323 (uint16_t)(end - p));
324 if (len > 0) {
325 accepted = true;
326 break;
327 }
328 }
329 if (!accepted) {
330 dev_ = nullptr;
331 daddr_ = 0;
332 usb_host_device_close(client_, dev);
333 return;
334 }
335 // ESP-IDF has already selected the configuration
336 events_->onConfigured(address);
337 }
338
339 void closeDevice() {
340 if (dev_ == nullptr) return;
341 for (auto& s : streams_) {
342 if (s.open) closeStream(daddr_, s.ep_addr);
343 }
344 for (auto& itf : interfaces_) {
345 if (itf.claimed) usb_host_interface_release(client_, dev_, itf.itf);
346 itf.claimed = false;
347 }
348 for (auto& s : streams_) {
349 if (s.in_flight == 0) freeTransfers(s);
350 }
351 usb_host_device_close(client_, dev_);
352 dev_ = nullptr;
353 daddr_ = 0;
354 }
355
356 Stream* findStream(uint8_t ep_addr) {
357 for (auto& s : streams_) {
358 if (s.ep_addr == ep_addr) return &s;
359 }
360 return nullptr;
361 }
362
363 Stream* findStream(usb_transfer_t* xfer) {
364 for (auto& s : streams_) {
365 for (auto t : s.transfers) {
366 if (t == xfer) return &s;
367 }
368 }
369 return nullptr;
370 }
371
372 Interface* findInterface(uint8_t itf) {
373 for (auto& i : interfaces_) {
374 if (i.itf == itf) return &i;
375 }
376 return nullptr;
377 }
378
383 esp_err_t rc = usb_host_interface_release(client_, dev_, itf.itf);
384 if (rc != ESP_OK) {
385 LOGI("usb_host_interface_release %d: %s", itf.itf, esp_err_to_name(rc));
386 return false;
387 }
388 itf.claimed = false;
389 return true;
390 }
391
393 for (auto t : s.transfers) usb_host_transfer_free(t);
394 s.transfers.clear();
395 }
396
397 void submit(Stream& s, usb_transfer_t* xfer) {
398 int total = 0;
399 for (int j = 0; j < s.packets; j++) {
400 int len = s.max_packet;
402 len = events_ ? events_->onIsoOut(xfer->data_buffer + total,
403 s.max_packet)
404 : 0;
405 }
406 xfer->isoc_packet_desc[j].num_bytes = len;
407 total += len;
408 }
409 xfer->num_bytes = total;
410 if (usb_host_transfer_submit(xfer) == ESP_OK) {
411 s.in_flight++;
412 } else {
413 LOGW("usb_host_transfer_submit 0x%x failed", s.ep_addr);
414 }
415 }
416
417 static void isoComplete(usb_transfer_t* xfer) {
419 Stream* s = self->findStream(xfer);
420 if (s == nullptr) return;
421 s->in_flight--;
422 if (!s->open || self->dev_ == nullptr) {
423 if (s->in_flight == 0 && self->dev_ == nullptr) self->freeTransfers(*s);
424 return;
425 }
426 if (s->role != UsbHostStreamRole::TxData && self->events_ &&
427 xfer->status == USB_TRANSFER_STATUS_COMPLETED) {
428 // IN: packet j starts at j * max_packet
429 for (int j = 0; j < s->packets; j++) {
430 usb_isoc_packet_desc_t& pd = xfer->isoc_packet_desc[j];
431 if (pd.status != USB_TRANSFER_STATUS_COMPLETED) continue;
432 self->events_->onIsoIn(s->role, xfer->data_buffer + j * s->max_packet,
433 (uint16_t)pd.actual_num_bytes);
434 }
435 }
436 if (xfer->status == USB_TRANSFER_STATUS_NO_DEVICE) return;
437 self->submit(*s, xfer);
438 }
439
440 static void controlComplete(usb_transfer_t* xfer) {
442 bool ok = xfer->status == USB_TRANSFER_STATUS_COMPLETED;
443 uint16_t len = 0;
444 if (ok && xfer->actual_num_bytes > 8) {
445 len = (uint16_t)(xfer->actual_num_bytes - 8);
446 if (len > self->ctrl_len_) len = self->ctrl_len_;
447 if (self->ctrl_data_) memcpy(self->ctrl_data_, xfer->data_buffer + 8, len);
448 }
449 if (self->events_) self->events_->onControlComplete(ok, len);
450 }
451};
452
453} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define portMAX_DELAY
Definition BufferBlocking.h:14
void setup()
USBAudioHostBackend for the ESP-IDF usb_host library which is part of arduino-esp32 (ESP32-S2,...
Definition USBAudioHostBackendESP32.h:34
uint32_t millis() const override
Milliseconds since start (for control request timeouts).
Definition USBAudioHostBackendESP32.h:214
Interface * findInterface(uint8_t itf)
Definition USBAudioHostBackendESP32.h:372
usb_host_client_handle_t client_
Definition USBAudioHostBackendESP32.h:240
void submit(Stream &s, usb_transfer_t *xfer)
Definition USBAudioHostBackendESP32.h:397
bool begin(USBAudioHostEvents *events, const USBAudioConfig &cfg) override
Start the host stack and register the event receiver.
Definition USBAudioHostBackendESP32.h:50
bool control(uint8_t daddr, const UsbSetupPacket &setup, uint8_t *data) override
Definition USBAudioHostBackendESP32.h:94
static bool installLibrary()
Definition USBAudioHostBackendESP32.h:251
void closeDevice()
Definition USBAudioHostBackendESP32.h:339
static void libraryTask(void *)
Definition USBAudioHostBackendESP32.h:271
~USBAudioHostBackendESP32()
Definition USBAudioHostBackendESP32.h:41
static constexpr int kPacketsPerTransfer
Number of isochronous packets (1 ms at full speed) per transfer.
Definition USBAudioHostBackendESP32.h:37
UsbSpeed speed() const override
Bus speed of the attached device.
Definition USBAudioHostBackendESP32.h:205
std::vector< Interface > interfaces_
Definition USBAudioHostBackendESP32.h:247
void end() override
Stop all streams (the host stack itself keeps running).
Definition USBAudioHostBackendESP32.h:79
void openDevice(uint8_t address)
Definition USBAudioHostBackendESP32.h:301
Stream * findStream(usb_transfer_t *xfer)
Definition USBAudioHostBackendESP32.h:363
static constexpr size_t kCtrlXferSize
Definition USBAudioHostBackendESP32.h:219
usb_device_handle_t dev_
Definition USBAudioHostBackendESP32.h:241
static TaskHandle_t s_library_task_
Definition USBAudioHostBackendESP32.h:238
static void isoComplete(usb_transfer_t *xfer)
Definition USBAudioHostBackendESP32.h:417
void task() override
Pump the host stack on platforms where the application drives it.
Definition USBAudioHostBackendESP32.h:85
bool openStream(uint8_t daddr, const UsbEndpointDescriptorView &ep, UsbHostStreamRole role) override
Definition USBAudioHostBackendESP32.h:156
USBAudioHostEvents * events_
Definition USBAudioHostBackendESP32.h:239
static void controlComplete(usb_transfer_t *xfer)
Definition USBAudioHostBackendESP32.h:440
usb_transfer_t * ctrl_xfer_
Definition USBAudioHostBackendESP32.h:243
std::vector< Stream > streams_
Definition USBAudioHostBackendESP32.h:246
static constexpr int kTransfersInFlight
Number of transfers which are kept in flight per endpoint.
Definition USBAudioHostBackendESP32.h:39
void closeStream(uint8_t daddr, uint8_t ep_addr) override
Stop streaming the indicated endpoint.
Definition USBAudioHostBackendESP32.h:193
bool releaseInterface(Interface &itf)
Definition USBAudioHostBackendESP32.h:382
uint8_t daddr_
Definition USBAudioHostBackendESP32.h:242
static void clientEvent(const usb_host_client_event_msg_t *msg, void *arg)
Definition USBAudioHostBackendESP32.h:281
uint8_t * ctrl_data_
Definition USBAudioHostBackendESP32.h:244
static bool s_library_installed_
Definition USBAudioHostBackendESP32.h:237
bool setInterface(uint8_t daddr, uint8_t itf, uint8_t alt) override
Definition USBAudioHostBackendESP32.h:126
uint16_t ctrl_len_
Definition USBAudioHostBackendESP32.h:245
void freeTransfers(Stream &s)
Definition USBAudioHostBackendESP32.h:392
Stream * findStream(uint8_t ep_addr)
Definition USBAudioHostBackendESP32.h:356
Abstract seam for every raw USB host stack call the UAC host class logic (USBAudioHostBase) needs....
Definition USBAudioHostBackend.h:73
Upcalls from a USB host backend into the stack independent UAC host class logic (USBAudioHostBase)....
Definition USBAudioHostBackend.h:26
virtual void onDetach(uint8_t daddr)=0
The device was removed.
virtual uint16_t onIsoOut(uint8_t *data, uint16_t max)=0
virtual void onConfigured(uint8_t daddr)=0
virtual void onIsoIn(UsbHostStreamRole role, const uint8_t *data, uint16_t len)=0
One isochronous IN packet was received (RxData or Feedback).
virtual void onControlComplete(bool ok, uint16_t len)=0
virtual uint16_t onAttach(uint8_t daddr, uint16_t vid, uint16_t pid, const uint8_t *desc, uint16_t len)=0
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
static constexpr uint8_t kAudioSubclassControl
Definition USBAudioConstants.h:22
static constexpr uint8_t kUsbClassAudio
Definition USBAudioConstants.h:21
UsbHostStreamRole
Role of an isochronous endpoint which is streamed by a host backend.
Definition USBAudioHostBackend.h:11
@ TxData
Audio data to the attached device (speaker): ISO OUT.
@ RxData
Audio data from the attached device (microphone): ISO IN.
@ Feedback
Explicit feedback from the attached speaker: ISO IN.
k_tid_t TaskHandle_t
Definition Task.h:10
static constexpr uint8_t kUsbDescTypeInterface
Definition USBAudioConstants.h:14
UsbSpeed
Device enumerated bus speed, mirrors tusb_speed_t's relevant values.
Definition USBAudioBackend.h:19
static constexpr uint8_t kUsbReqSetInterface
Definition USBAudioConstants.h:58
Configuration for USB Audio (inherits sample_rate / channels / bits_per_sample from AudioInfo).
Definition USBAudioConfig.h:117
Definition USBAudioHostBackendESP32.h:231
uint8_t alt
Definition USBAudioHostBackendESP32.h:233
uint8_t itf
Definition USBAudioHostBackendESP32.h:232
bool claimed
Definition USBAudioHostBackendESP32.h:234
Definition USBAudioHostBackendESP32.h:221
UsbHostStreamRole role
Definition USBAudioHostBackendESP32.h:223
uint8_t ep_addr
Definition USBAudioHostBackendESP32.h:222
uint16_t max_packet
Definition USBAudioHostBackendESP32.h:224
int in_flight
Definition USBAudioHostBackendESP32.h:227
std::vector< usb_transfer_t * > transfers
Definition USBAudioHostBackendESP32.h:228
bool open
Definition USBAudioHostBackendESP32.h:226
int packets
Definition USBAudioHostBackendESP32.h:225
Backend-agnostic mirror of tusb_desc_endpoint_t (USB 2.0 spec Table 9-13). wMaxPacketSize is kept in ...
Definition USBAudioBackend.h:86
uint8_t bEndpointAddress
Definition USBAudioBackend.h:87
uint16_t packetSize() const
Definition USBAudioBackend.h:97
Backend-agnostic mirror of the standard 8-byte USB SETUP packet (TinyUSB's tusb_control_request_t)....
Definition USBAudioBackend.h:34
uint8_t bmRequestType
Definition USBAudioBackend.h:35