arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioHostBackendTinyUSB.h
Go to the documentation of this file.
1#pragma once
2#include <Adafruit_TinyUSB.h>
3
4#include <cstring>
5#include <vector>
6
9
10extern "C" {
11#include "host/hcd.h"
12#include "host/usbh.h"
13#include "host/usbh_pvt.h"
14#include "tusb.h"
15}
16
17#if !CFG_TUH_ENABLED
18#error "USB Audio Host: host support is not enabled in the Adafruit TinyUSB Library (CFG_TUH_ENABLED); e.g. on RP2040 select the USB Stack 'Adafruit TinyUSB Host'"
19#endif
20
21#if TUSB_VERSION_NUMBER < 1700
22#error "USB Audio Host requires TinyUSB >= 0.17 (a current Adafruit TinyUSB Library)"
23#endif
24
25namespace audio_tools {
26
52 public:
54
57 USBAudioHostBackendTinyUSB(Adafruit_USBH_Host& host) : p_host_(&host) {}
58
60 void setHost(Adafruit_USBH_Host& host) { p_host_ = &host; }
61
62 bool begin(USBAudioHostEvents* events, const USBAudioConfig& cfg) override {
63 events_ = events;
64 s_instance_ = this;
65 rhport_ = cfg.host_rhport;
66#if CFG_TUH_ENUMERATION_BUFSIZE < 512
67 LOGW(
68 "CFG_TUH_ENUMERATION_BUFSIZE is %d: devices with a larger "
69 "configuration descriptor are not detected",
70 (int)CFG_TUH_ENUMERATION_BUFSIZE);
71#endif
72 if (tuh_rhport_is_active(rhport_)) return true;
73 return host().begin(rhport_);
74 }
75
76 void end() override {
77 for (auto& s : streams_) {
78 if (s.open) closeStream(s.daddr, s.ep_addr);
79 }
80 }
81
82 void task() override {
83 host().task(0, false);
84 // resubmit the transfers which were deferred to the next frame
85 for (auto& s : streams_) {
86 if (s.open && s.due && frameAdvanced(s)) submit(s);
87 }
88 }
89
90 bool control(uint8_t daddr, const UsbSetupPacket& setup,
91 uint8_t* data) override {
92 request_.bmRequestType = setup.bmRequestType;
93 request_.bRequest = setup.bRequest;
94 request_.wValue = tu_htole16(setup.wValue);
95 request_.wIndex = tu_htole16(setup.wIndex);
96 request_.wLength = tu_htole16(setup.wLength);
97 tuh_xfer_t xfer;
98 memset(&xfer, 0, sizeof(xfer));
99 xfer.daddr = daddr;
100 xfer.ep_addr = 0;
101 xfer.setup = &request_;
102 xfer.buffer = setup.wLength > 0 ? data : nullptr;
103 xfer.complete_cb = controlComplete;
104 xfer.user_data = 0;
105 return tuh_control_xfer(&xfer);
106 }
107
108 bool setInterface(uint8_t daddr, uint8_t itf, uint8_t alt) override {
110 setup.bmRequestType = 0x01; // standard, host to device, interface
111 setup.bRequest = kUsbReqSetInterface;
112 setup.wValue = alt;
113 setup.wIndex = itf;
114 setup.wLength = 0;
115 return control(daddr, setup, nullptr);
116 }
117
118 bool openStream(uint8_t daddr, const UsbEndpointDescriptorView& ep,
119 UsbHostStreamRole role) override {
120 tusb_desc_endpoint_t desc;
121 memset(&desc, 0, sizeof(desc));
122 desc.bLength = sizeof(tusb_desc_endpoint_t);
123 desc.bDescriptorType = TUSB_DESC_ENDPOINT;
124 desc.bEndpointAddress = ep.bEndpointAddress;
125 desc.bmAttributes.xfer = TUSB_XFER_ISOCHRONOUS;
126 desc.bmAttributes.sync = ep.sync;
127 desc.bmAttributes.usage = ep.usage;
128 desc.wMaxPacketSize = tu_htole16(ep.wMaxPacketSize);
129 desc.bInterval = ep.bInterval;
130 if (!tuh_edpt_open(daddr, &desc)) {
131 LOGE("tuh_edpt_open 0x%x failed", ep.bEndpointAddress);
132 return false;
133 }
134 Stream* s = find(daddr, ep.bEndpointAddress);
135 if (s == nullptr) {
136 streams_.push_back(Stream());
137 s = &streams_.back();
138 }
139 s->daddr = daddr;
141 s->role = role;
142 s->max_packet = ep.packetSize();
143 s->buffer.resize(s->max_packet);
144 s->open = true;
145 s->due = false;
146 s->last_frame = frameNumber() - 1;
147 submit(*s);
148 return true;
149 }
150
151 void closeStream(uint8_t daddr, uint8_t ep_addr) override {
152 Stream* s = find(daddr, ep_addr);
153 if (s == nullptr || !s->open) return;
154 s->open = false;
155 s->due = false;
156 tuh_edpt_abort_xfer(daddr, ep_addr);
157 tuh_edpt_close(daddr, ep_addr);
158 }
159
160 UsbSpeed speed() const override {
161 tusb_speed_t sp = tuh_speed_get(daddr_);
162 if (sp == TUSB_SPEED_HIGH) return UsbSpeed::High;
163 if (sp == TUSB_SPEED_LOW) return UsbSpeed::Low;
164 return UsbSpeed::Full;
165 }
166
167 uint32_t millis() const override { return tusb_time_millis_api(); }
168
169 // ── usbh_class_driver_t trampolines ──────────────────────────────────────
170
171 static bool driverInit() { return true; }
172
173 static bool driverDeinit() { return true; }
174
175 static uint16_t driverOpen(uint8_t rhport, uint8_t daddr,
176 tusb_desc_interface_t const* itf_desc,
177 uint16_t max_len) {
179 if (self == nullptr || self->events_ == nullptr) return 0;
180 if (rhport != self->rhport_) return 0;
181 uint16_t vid = 0, pid = 0;
182 tuh_vid_pid_get(daddr, &vid, &pid);
183 const uint8_t* desc = (const uint8_t*)itf_desc;
184 uint16_t len = self->events_->onAttach(daddr, vid, pid, desc, max_len);
185 if (len < sizeof(tusb_desc_interface_t)) return 0;
186 // remember the last interface which belongs to the audio function
187 self->daddr_ = daddr;
188 self->last_itf_ = itf_desc->bInterfaceNumber;
189 for (const uint8_t* p = desc; p < desc + len && p[0] > 0; p += p[0]) {
190 if (p[1] == TUSB_DESC_INTERFACE && p[2] > self->last_itf_)
191 self->last_itf_ = p[2];
192 }
193 return len;
194 }
195
196 static bool driverSetConfig(uint8_t daddr, uint8_t itf_num) {
198 if (self && self->events_ && daddr == self->daddr_) {
199 self->events_->onConfigured(daddr);
200 usbh_driver_set_config_complete(daddr, self->last_itf_);
201 } else {
202 usbh_driver_set_config_complete(daddr, itf_num);
203 }
204 return true;
205 }
206
207 static bool driverXfer(uint8_t daddr, uint8_t ep_addr, xfer_result_t result,
208 uint32_t xferred_bytes) {
210 if (self == nullptr) return false;
211 Stream* s = self->find(daddr, ep_addr);
212 if (s == nullptr || !s->open) return true;
213 if (result == XFER_RESULT_SUCCESS && s->role != UsbHostStreamRole::TxData &&
214 self->events_) {
215 uint16_t len = xferred_bytes > s->max_packet ? s->max_packet
216 : (uint16_t)xferred_bytes;
217 self->events_->onIsoIn(s->role, s->buffer.data(), len);
218 }
219 if (!s->open) return true; // closed by the callback
220 if (self->frameAdvanced(*s)) {
221 self->submit(*s);
222 } else {
223 s->due = true;
224 }
225 return true;
226 }
227
228 static void driverClose(uint8_t daddr) {
230 if (self == nullptr) return;
231 for (auto& s : self->streams_) {
232 if (s.daddr == daddr) {
233 s.open = false;
234 s.due = false;
235 }
236 }
237 if (daddr == self->daddr_) self->daddr_ = 0;
238 if (self->events_) self->events_->onDetach(daddr);
239 }
240
241 protected:
242 struct Stream {
243 uint8_t daddr = 0;
244 uint8_t ep_addr = 0;
246 uint16_t max_packet = 0;
247 std::vector<uint8_t> buffer;
248 bool open = false;
249 bool due = false;
250 uint32_t last_frame = 0;
251 };
252
253 inline static USBAudioHostBackendTinyUSB* s_instance_ = nullptr;
254 Adafruit_USBH_Host* p_host_ = nullptr;
256 uint8_t rhport_ = 0;
257 uint8_t daddr_ = 0;
258 uint8_t last_itf_ = 0;
259 tusb_control_request_t request_;
260 std::vector<Stream> streams_;
261
262 Adafruit_USBH_Host& host() {
263 if (p_host_ == nullptr) {
264 static Adafruit_USBH_Host default_host;
265 p_host_ = &default_host;
266 }
267 return *p_host_;
268 }
269
270 Stream* find(uint8_t daddr, uint8_t ep_addr) {
271 for (auto& s : streams_) {
272 if (s.daddr == daddr && s.ep_addr == ep_addr) return &s;
273 }
274 return nullptr;
275 }
276
277 uint32_t frameNumber() { return hcd_frame_number(rhport_); }
278
279 bool frameAdvanced(const Stream& s) { return frameNumber() != s.last_frame; }
280
281 void submit(Stream& s) {
282 s.due = false;
284 uint16_t len = s.max_packet;
286 len = events_ ? events_->onIsoOut(s.buffer.data(), s.max_packet) : 0;
287 }
288 if (!usbh_edpt_xfer(s.daddr, s.ep_addr, s.buffer.data(), len)) {
289 // e.g. still busy: try again with the next frame
290 s.due = true;
291 }
292 }
293
294 static void controlComplete(tuh_xfer_t* xfer) {
296 if (self == nullptr || self->events_ == nullptr) return;
297 self->events_->onControlComplete(xfer->result == XFER_RESULT_SUCCESS,
298 (uint16_t)xfer->actual_len);
299 }
300};
301
302} // namespace audio_tools
303
304// Custom host class driver registration. Deliberately NOT `inline`: it must
305// be a strong symbol which overrides TinyUSB's weak default (see the same
306// comment for usbd_app_driver_get_cb() in USBAudioBackendTinyUSB.h).
307extern "C" usbh_class_driver_t const* usbh_app_driver_get_cb(
308 uint8_t* driver_count) {
310 static usbh_class_driver_t const driver = {
311 "AUDIO",
312 USBAudioHostBackendTinyUSB::driverInit,
313 USBAudioHostBackendTinyUSB::driverDeinit,
314 USBAudioHostBackendTinyUSB::driverOpen,
315 USBAudioHostBackendTinyUSB::driverSetConfig,
316 USBAudioHostBackendTinyUSB::driverXfer,
317 USBAudioHostBackendTinyUSB::driverClose,
318 };
319 *driver_count = 1;
320 return &driver;
321}
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGE(...)
Definition AudioLoggerIDF.h:30
void setup()
usbh_class_driver_t const * usbh_app_driver_get_cb(uint8_t *driver_count)
Definition USBAudioHostBackendTinyUSB.h:307
Abstract seam for every raw USB host stack call the UAC host class logic (USBAudioHostBase) needs....
Definition USBAudioHostBackend.h:73
USBAudioHostBackend for the TinyUSB host stack provided by the Adafruit TinyUSB Library (RP2040/RP235...
Definition USBAudioHostBackendTinyUSB.h:51
uint32_t millis() const override
Milliseconds since start (for control request timeouts).
Definition USBAudioHostBackendTinyUSB.h:167
static bool driverDeinit()
Definition USBAudioHostBackendTinyUSB.h:173
uint8_t last_itf_
Definition USBAudioHostBackendTinyUSB.h:258
bool begin(USBAudioHostEvents *events, const USBAudioConfig &cfg) override
Start the host stack and register the event receiver.
Definition USBAudioHostBackendTinyUSB.h:62
bool control(uint8_t daddr, const UsbSetupPacket &setup, uint8_t *data) override
Definition USBAudioHostBackendTinyUSB.h:90
static bool driverXfer(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
Definition USBAudioHostBackendTinyUSB.h:207
uint8_t rhport_
Definition USBAudioHostBackendTinyUSB.h:256
static void controlComplete(tuh_xfer_t *xfer)
Definition USBAudioHostBackendTinyUSB.h:294
UsbSpeed speed() const override
Bus speed of the attached device.
Definition USBAudioHostBackendTinyUSB.h:160
Adafruit_USBH_Host * p_host_
Definition USBAudioHostBackendTinyUSB.h:254
void end() override
Stop all streams (the host stack itself keeps running).
Definition USBAudioHostBackendTinyUSB.h:76
static void driverClose(uint8_t daddr)
Definition USBAudioHostBackendTinyUSB.h:228
bool frameAdvanced(const Stream &s)
Definition USBAudioHostBackendTinyUSB.h:279
static bool driverSetConfig(uint8_t daddr, uint8_t itf_num)
Definition USBAudioHostBackendTinyUSB.h:196
void task() override
Pump the host stack on platforms where the application drives it.
Definition USBAudioHostBackendTinyUSB.h:82
bool openStream(uint8_t daddr, const UsbEndpointDescriptorView &ep, UsbHostStreamRole role) override
Definition USBAudioHostBackendTinyUSB.h:118
USBAudioHostEvents * events_
Definition USBAudioHostBackendTinyUSB.h:255
std::vector< Stream > streams_
Definition USBAudioHostBackendTinyUSB.h:260
static USBAudioHostBackendTinyUSB * s_instance_
Definition USBAudioHostBackendTinyUSB.h:253
void closeStream(uint8_t daddr, uint8_t ep_addr) override
Stop streaming the indicated endpoint.
Definition USBAudioHostBackendTinyUSB.h:151
uint8_t daddr_
Definition USBAudioHostBackendTinyUSB.h:257
tusb_control_request_t request_
Definition USBAudioHostBackendTinyUSB.h:259
Adafruit_USBH_Host & host()
Definition USBAudioHostBackendTinyUSB.h:262
void submit(Stream &s)
Definition USBAudioHostBackendTinyUSB.h:281
static uint16_t driverOpen(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
Definition USBAudioHostBackendTinyUSB.h:175
uint32_t frameNumber()
Definition USBAudioHostBackendTinyUSB.h:277
bool setInterface(uint8_t daddr, uint8_t itf, uint8_t alt) override
Definition USBAudioHostBackendTinyUSB.h:108
static bool driverInit()
Definition USBAudioHostBackendTinyUSB.h:171
USBAudioHostBackendTinyUSB(Adafruit_USBH_Host &host)
Definition USBAudioHostBackendTinyUSB.h:57
void setHost(Adafruit_USBH_Host &host)
Defines the Adafruit_USBH_Host (call before begin())
Definition USBAudioHostBackendTinyUSB.h:60
Stream * find(uint8_t daddr, uint8_t ep_addr)
Definition USBAudioHostBackendTinyUSB.h:270
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
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.
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
uint8_t host_rhport
TinyUSB root hub port used for host mode (e.g. 1 for Pico-PIO-USB).
Definition USBAudioConfig.h:244
Definition USBAudioHostBackendTinyUSB.h:242
uint32_t last_frame
Definition USBAudioHostBackendTinyUSB.h:250
UsbHostStreamRole role
Definition USBAudioHostBackendTinyUSB.h:245
std::vector< uint8_t > buffer
Definition USBAudioHostBackendTinyUSB.h:247
uint8_t ep_addr
Definition USBAudioHostBackendTinyUSB.h:244
uint16_t max_packet
Definition USBAudioHostBackendTinyUSB.h:246
uint8_t daddr
Definition USBAudioHostBackendTinyUSB.h:243
bool open
Definition USBAudioHostBackendTinyUSB.h:248
bool due
Definition USBAudioHostBackendTinyUSB.h:249
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 wMaxPacketSize
Definition USBAudioBackend.h:92
uint8_t sync
Definition USBAudioBackend.h:89
uint16_t packetSize() const
Definition USBAudioBackend.h:97
uint8_t usage
Definition USBAudioBackend.h:90
uint8_t bInterval
Definition USBAudioBackend.h:91
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