arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioBackendTinyUSB.h
Go to the documentation of this file.
1#pragma once
3
4extern "C" {
5#include "device/usbd.h"
6#include "device/usbd_pvt.h"
7#include "tusb.h"
8}
9
10namespace audio_tools {
11
12// TinyUSB >= 0.15 (ESP32 IDF v5+, current Adafruit TinyUSB) added an is_isr
13// parameter to usbd_edpt_xfer(); older TinyUSB (bundled with some RP2040 /
14// SAMD / STM32 cores) does not have it. Detect the new API by the presence
15// of the renamed UAC2 IAD-length macro, exactly as USBAudioDeviceBase.h used
16// to before this file existed.
17#ifdef TUD_AUDIO20_DESC_IAD_LEN
18#define TU_AUDIO_EDPT_XFER(rp, ep, buf, sz) usbd_edpt_xfer(rp, ep, buf, sz, false)
19#else
20#define TU_AUDIO_EDPT_XFER(rp, ep, buf, sz) usbd_edpt_xfer(rp, ep, buf, sz)
21#endif
22
23// Same story for usbd_sof_enable(): newer TinyUSB added a per-consumer enum
24// parameter (sof_consumer_t, e.g. SOF_CONSUMER_AUDIO) so multiple class
25// drivers can each enable/disable the SOF interrupt independently; older
26// TinyUSB (e.g. the version bundled with Arduino's Renesas UNO R4 core) only
27// has the plain two-argument form. SOF_CONSUMER_AUDIO is an *enum value*,
28// not a macro, so #ifdef can never detect it (a prior version of this file
29// tried that and always silently fell through to the 2-arg form, which
30// doesn't compile against the 3-arg signature -- this went unnoticed until
31// ESP32/RP2040 were recompiled). Use the same TUSB_VERSION_NUMBER threshold
32// as the usbd_class_driver_t::deinit detection below instead.
33#if TUSB_VERSION_NUMBER >= 1600
34#define TU_SOF_ENABLE(rp, en) usbd_sof_enable(rp, SOF_CONSUMER_AUDIO, en)
35#else
36#define TU_SOF_ENABLE(rp, en) usbd_sof_enable(rp, en)
37#endif
38
49 public:
50 bool openEndpoint(uint8_t rhport, const UsbEndpointDescriptorView& ep) override {
51 tusb_desc_endpoint_t d = toTusbDescriptor(ep);
52 return usbd_edpt_open(rhport, &d);
53 }
54
55 bool closeEndpoint(uint8_t rhport, uint8_t ep_addr) override {
56 usbd_edpt_close(rhport, ep_addr);
57 return true;
58 }
59
60 bool clearStall(uint8_t rhport, uint8_t ep_addr) override {
61 usbd_edpt_clear_stall(rhport, ep_addr);
62 return true;
63 }
64
65 bool usesIsoAlloc() const override {
66#ifdef TUP_DCD_EDPT_ISO_ALLOC
67 return true;
68#else
69 return false;
70#endif
71 }
72
73 bool isoAllocEndpoint(uint8_t rhport, uint8_t ep_addr,
74 uint16_t max_packet_size) override {
75 return usbd_edpt_iso_alloc(rhport, ep_addr, max_packet_size);
76 }
77
78 bool isoActivateEndpoint(uint8_t rhport,
79 const UsbEndpointDescriptorView& ep) override {
80 tusb_desc_endpoint_t d = toTusbDescriptor(ep);
81 return usbd_edpt_iso_activate(rhport, &d);
82 }
83
84 bool releaseEndpoint(uint8_t rhport, uint8_t ep_addr) override {
85 return usbd_edpt_release(rhport, ep_addr);
86 }
87
88 bool claimEndpoint(uint8_t rhport, uint8_t ep_addr) override {
89 return usbd_edpt_claim(rhport, ep_addr);
90 }
91
92 bool transfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer,
93 uint16_t length) override {
94 return TU_AUDIO_EDPT_XFER(rhport, ep_addr, buffer, length);
95 }
96
97 bool controlTransfer(uint8_t rhport, const UsbSetupPacket& request,
98 uint8_t* buffer, uint16_t length) override {
99 tusb_control_request_t r = toTusbRequest(request);
100 return tud_control_xfer(rhport, &r, buffer, length);
101 }
102
103 bool controlStatus(uint8_t rhport, const UsbSetupPacket& request) override {
104 tusb_control_request_t r = toTusbRequest(request);
105 return tud_control_status(rhport, &r);
106 }
107
108 UsbSpeed speed() const override {
109 switch (tud_speed_get()) {
110 case TUSB_SPEED_FULL:
111 return UsbSpeed::Full;
112 case TUSB_SPEED_HIGH:
113 return UsbSpeed::High;
114 case TUSB_SPEED_LOW:
115 return UsbSpeed::Low;
116 default:
117 return UsbSpeed::Unknown;
118 }
119 }
120
121 bool mounted() const override { return tud_mounted(); }
122
123 void enableSof(uint8_t rhport, bool enable) override {
124 TU_SOF_ENABLE(rhport, enable);
125 }
126
127 // ── The only two places in the whole backend that touch TinyUSB's raw
128 // struct layout directly ────────────────────────────────────────────
129
130 static tusb_desc_endpoint_t toTusbDescriptor(const UsbEndpointDescriptorView& ep) {
131 tusb_desc_endpoint_t d{};
132 d.bLength = sizeof(tusb_desc_endpoint_t);
133 d.bDescriptorType = TUSB_DESC_ENDPOINT;
134 d.bEndpointAddress = ep.bEndpointAddress;
135 d.bmAttributes.xfer = (uint8_t)ep.xferType;
136 d.bmAttributes.sync = ep.sync;
137 d.bmAttributes.usage = ep.usage;
138 d.wMaxPacketSize = ep.wMaxPacketSize;
139 d.bInterval = ep.bInterval;
140 return d;
141 }
142
143 static UsbSetupPacket fromTusbRequest(tusb_control_request_t const* r) {
145 p.bmRequestType = r->bmRequestType;
146 p.bRequest = r->bRequest;
147 p.wValue = r->wValue;
148 p.wIndex = r->wIndex;
149 p.wLength = r->wLength;
150 return p;
151 }
152
153 static tusb_control_request_t toTusbRequest(const UsbSetupPacket& p) {
154 tusb_control_request_t r{};
155 r.bmRequestType = p.bmRequestType;
156 r.bRequest = p.bRequest;
157 r.wValue = p.wValue;
158 r.wIndex = p.wIndex;
159 r.wLength = p.wLength;
160 return r;
161 }
162
163 static UsbInterfaceDescriptorView fromTusbInterface(tusb_desc_interface_t const* d) {
165 v.bInterfaceNumber = d->bInterfaceNumber;
166 v.bAlternateSetting = d->bAlternateSetting;
167 v.bNumEndpoints = d->bNumEndpoints;
168 v.bInterfaceClass = d->bInterfaceClass;
169 v.bInterfaceSubClass = d->bInterfaceSubClass;
170 v.bInterfaceProtocol = d->bInterfaceProtocol;
171 return v;
172 }
173};
174
175} // namespace audio_tools
176
177// ── TinyUSB class-driver registration ───────────────────────────────────────
178// This is included after USBAudioDeviceBase.h everywhere it's used, so the
179// full class definition (audiod_init/audiod_open/... methods) is available.
181
182namespace audio_tools {
183
184inline usbd_class_driver_t const* usbAudioGetClassDriver(uint8_t* count) {
185 static usbd_class_driver_t driver;
186 // usbd_class_driver_t's `name` field is unconditional on every TinyUSB
187 // version actually in use here (ESP32, RP2040/Adafruit, and Renesas as of
188 // its 0.21.0 upgrade all have it unguarded). It was only ever compiled out
189 // behind a `#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL` guard on the old
190 // pre-0.16-era struct layout (TUSB_VERSION_NUMBER < 1600), so gate on that
191 // instead of the debug-level macros -- those default to a combination
192 // (CFG_TUSB_DEBUG=0, CFG_TUD_LOG_LEVEL=2) that's false on every version,
193 // which would silently skip this assignment even on versions where the
194 // field is always present and always safe to set.
195#if TUSB_VERSION_NUMBER >= 1600
196 driver.name = "AUDIO";
197#endif
198 driver.init = []() { USBAudioDeviceBase::activeInstance().audiod_init(); };
199#if TUSB_VERSION_NUMBER >= 1600
200 // usbd_class_driver_t only gained a `deinit` member around TinyUSB 0.16 --
201 // the older TinyUSB bundled with Arduino's Renesas UNO R4 core (0.15)
202 // doesn't have this field at all, so referencing it there is a compile
203 // error. TUSB_VERSION_NUMBER (from tusb_option.h) is TinyUSB's own
204 // major*10000+minor*100+revision version macro, so this stays correct
205 // automatically as bundled TinyUSB versions change.
206 driver.deinit = []() {
208 };
209#endif
210 driver.reset = [](uint8_t rhport) {
212 };
213 driver.open = [](uint8_t rhport, tusb_desc_interface_t const* itf_desc,
214 uint16_t max_len) {
218 rhport, view, (uint8_t const*)itf_desc, max_len);
219 };
220 driver.control_xfer_cb = [](uint8_t rhport, uint8_t stage,
221 tusb_control_request_t const* request) {
224 rhport, stage, req);
225 };
226 driver.xfer_cb = [](uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
227 uint32_t xferred_bytes) {
228 UsbXferResult r = (result == XFER_RESULT_SUCCESS) ? UsbXferResult::Success
229 : (result == XFER_RESULT_STALLED) ? UsbXferResult::Stalled
232 rhport, ep_addr, r, xferred_bytes);
233 };
234 driver.sof = [](uint8_t rhport, uint32_t frame_count) {
236 };
237
238 *count = 1;
239 return &driver;
240}
241
242} // namespace audio_tools
243
244// Custom driver registration — routes to whichever USBAudioDeviceBase
245// subclass was constructed (base or derived; set via s_active_ in the
246// constructor).
247//
248// Deliberately NOT marked `inline`: TinyUSB's own internal default
249// implementation of this function is a weak symbol, and this override must
250// win at link time by being a *strong* symbol. Marking a function `inline`
251// in C++ causes the compiler to emit it with weak linkage too (for ODR
252// merging across translation units) -- two weak symbols racing lets the
253// linker pick either one, and it was silently picking TinyUSB's built-in
254// default (which registers zero class drivers), so this driver never got
255// attached: every control request targeting our interfaces stalled and no
256// audio ever streamed. This header is only ever included from one
257// translation unit in practice (the platform-specific USB device header
258// pulled in by the sketch), so plain external linkage is correct and safe.
259extern "C" usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* count) {
261}
#define TU_AUDIO_EDPT_XFER(rp, ep, buf, sz)
Definition USBAudioBackendTinyUSB.h:20
#define TU_SOF_ENABLE(rp, en)
Definition USBAudioBackendTinyUSB.h:36
usbd_class_driver_t const * usbd_app_driver_get_cb(uint8_t *count)
Definition USBAudioBackendTinyUSB.h:259
Abstract seam for every raw USB-stack call the UAC2 driver (USBAudioDeviceBase) needs....
Definition USBAudioBackend.h:160
USBAudioBackend implementation wrapping the real TinyUSB device stack. Injected by the ESP32,...
Definition USBAudioBackendTinyUSB.h:48
bool usesIsoAlloc() const override
Definition USBAudioBackendTinyUSB.h:65
bool isoAllocEndpoint(uint8_t rhport, uint8_t ep_addr, uint16_t max_packet_size) override
Definition USBAudioBackendTinyUSB.h:73
static UsbSetupPacket fromTusbRequest(tusb_control_request_t const *r)
Definition USBAudioBackendTinyUSB.h:143
static tusb_desc_endpoint_t toTusbDescriptor(const UsbEndpointDescriptorView &ep)
Definition USBAudioBackendTinyUSB.h:130
bool mounted() const override
True once the device has completed USB enumeration.
Definition USBAudioBackendTinyUSB.h:121
UsbSpeed speed() const override
Current negotiated bus speed.
Definition USBAudioBackendTinyUSB.h:108
bool claimEndpoint(uint8_t rhport, uint8_t ep_addr) override
Definition USBAudioBackendTinyUSB.h:88
static tusb_control_request_t toTusbRequest(const UsbSetupPacket &p)
Definition USBAudioBackendTinyUSB.h:153
bool releaseEndpoint(uint8_t rhport, uint8_t ep_addr) override
Definition USBAudioBackendTinyUSB.h:84
bool closeEndpoint(uint8_t rhport, uint8_t ep_addr) override
Definition USBAudioBackendTinyUSB.h:55
static UsbInterfaceDescriptorView fromTusbInterface(tusb_desc_interface_t const *d)
Definition USBAudioBackendTinyUSB.h:163
bool openEndpoint(uint8_t rhport, const UsbEndpointDescriptorView &ep) override
Definition USBAudioBackendTinyUSB.h:50
void enableSof(uint8_t rhport, bool enable) override
Definition USBAudioBackendTinyUSB.h:123
bool clearStall(uint8_t rhport, uint8_t ep_addr) override
Clear a halted/stalled condition on an endpoint.
Definition USBAudioBackendTinyUSB.h:60
bool controlStatus(uint8_t rhport, const UsbSetupPacket &request) override
Send a zero-length-packet status acknowledgement.
Definition USBAudioBackendTinyUSB.h:103
bool transfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t length) override
Definition USBAudioBackendTinyUSB.h:92
bool controlTransfer(uint8_t rhport, const UsbSetupPacket &request, uint8_t *buffer, uint16_t length) override
Complete a GET/SET control transfer's data stage with buffer.
Definition USBAudioBackendTinyUSB.h:97
bool isoActivateEndpoint(uint8_t rhport, const UsbEndpointDescriptorView &ep) override
Definition USBAudioBackendTinyUSB.h:78
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, UsbSetupPacket const &request)
Definition USBAudioDeviceBase.h:1317
void audiod_sof_isr(uint8_t rhport, uint32_t frame_count)
Definition USBAudioDeviceBase.h:1458
uint16_t audiod_open(uint8_t rhport, UsbInterfaceDescriptorView const &itf_desc, uint8_t const *raw_desc, uint16_t max_len)
Definition USBAudioDeviceBase.h:1175
void audiod_init(void)
Definition USBAudioDeviceBase.h:1125
bool audiod_deinit(void)
Definition USBAudioDeviceBase.h:1157
void audiod_reset(uint8_t rhport)
Definition USBAudioDeviceBase.h:1161
static USBAudioDeviceBase & activeInstance()
Returns the most-recently-constructed instance (base or subclass).
Definition USBAudioDeviceBase.h:384
bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, UsbXferResult result, uint32_t xferred_bytes)
TODO refactor control request handling to separate function and reduce nesting.
Definition USBAudioDeviceBase.h:1404
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
UsbXferResult
Definition USBAudioBackend.h:24
usbd_class_driver_t const * usbAudioGetClassDriver(uint8_t *count)
Definition USBAudioBackendTinyUSB.h:184
UsbSpeed
Device enumerated bus speed, mirrors tusb_speed_t's relevant values.
Definition USBAudioBackend.h:19
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
UsbXferType xferType
Definition USBAudioBackend.h:88
uint16_t wMaxPacketSize
Definition USBAudioBackend.h:92
uint8_t sync
Definition USBAudioBackend.h:89
uint8_t usage
Definition USBAudioBackend.h:90
uint8_t bInterval
Definition USBAudioBackend.h:91
Backend-agnostic mirror of tusb_desc_interface_t — only the fields the UAC2 driver reads (USB 2....
Definition USBAudioBackend.h:66
uint8_t bInterfaceSubClass
Definition USBAudioBackend.h:71
uint8_t bInterfaceNumber
Definition USBAudioBackend.h:67
uint8_t bAlternateSetting
Definition USBAudioBackend.h:68
uint8_t bInterfaceProtocol
Definition USBAudioBackend.h:72
uint8_t bNumEndpoints
Definition USBAudioBackend.h:69
uint8_t bInterfaceClass
Definition USBAudioBackend.h:70
Backend-agnostic mirror of the standard 8-byte USB SETUP packet (TinyUSB's tusb_control_request_t)....
Definition USBAudioBackend.h:34
uint8_t bRequest
Definition USBAudioBackend.h:36
uint16_t wLength
Definition USBAudioBackend.h:39
uint16_t wValue
Definition USBAudioBackend.h:37
uint8_t bmRequestType
Definition USBAudioBackend.h:35
uint16_t wIndex
Definition USBAudioBackend.h:38