arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioDeviceBase.h
Go to the documentation of this file.
1#pragma once
2
3// Some cores (e.g. Arduino's Renesas UNO R4 core, Arduino.h) #define abs(x)
4// as a function-like macro for AVR-era C compatibility. Since Arduino.h is
5// always included ahead of library headers in a .ino, that macro is already
6// active here and breaks libstdc++'s own templated abs(duration<...>)
7// overload inside <chrono> (pulled in by <mutex>) -- the preprocessor sees
8// the comma inside duration<_Rep, _Period> as a second macro argument.
9// Suppress the macro for just this header's STL includes, then restore it
10// so nothing else in the translation unit is affected.
11#pragma push_macro("abs")
12#undef abs
13
14#include <cmath>
15#include <cstddef>
16#include <cstdint>
17#include <cstring>
18#include <functional>
19#include <mutex>
20#include <vector>
21
22#pragma pop_macro("abs")
23
26
27#ifdef ESP32
28#ifndef ARDUINO_USB_MODE
29#error This Microcontroller has no Native USB interface
30#else
31#if ARDUINO_USB_MODE == 1
32#error This sketch should be used when USB is in OTG mode
33#endif
34#endif
35#else
36#if !defined(USE_TINYUSB) && !defined(ARDUINO_ARCH_STM32) && \
37 !defined(ARDUINO_ARCH_RENESAS) && !defined(IS_ZEPHYR)
38#error This Microcontroller has no Native USB interface
39#endif
40#endif
41
42#include "AudioLogger.h"
46
47#define USB_DESCR_MAX_LEN 512
48
49namespace audio_tools {
50
51// ── UAC2 / USB 2.0 spec constants ───────────────────────────────────────────
52// These are fixed byte values defined by the USB 2.0 and UAC2 specs, not
53// TinyUSB API surface (TinyUSB's own headers just give them names). Owned
54// here directly so USBAudioDeviceBase has zero dependency on any particular
55// USB stack's headers.
56
57// Descriptor types (USB 2.0 spec Table 9-5).
58static constexpr uint8_t kUsbDescTypeInterface = 0x04;
59static constexpr uint8_t kUsbDescTypeEndpoint = 0x05;
60static constexpr uint8_t kUsbDescTypeCsInterface = 0x24;
61
62// Audio class/subclass/protocol codes (UAC2 spec Appendix A).
63static constexpr uint8_t kUsbClassAudio = 0x01;
64static constexpr uint8_t kAudioSubclassControl = 0x01;
65static constexpr uint8_t kAudioIntProtocolCodeV2 = 0x20;
66
67// AC interface descriptor subtypes (UAC2 Table A-9).
68static constexpr uint8_t kAudioCsAcInputTerminal = 0x02;
69static constexpr uint8_t kAudioCsAcOutputTerminal = 0x03;
70// AS interface descriptor subtypes (UAC2 Table A-11).
71static constexpr uint8_t kAudioCsAsGeneral = 0x01;
72static constexpr uint8_t kAudioCsAsFormatType = 0x02;
73// Control request codes (UAC2 Table A-15).
74static constexpr uint8_t kAudioCsReqCur = 0x01;
75static constexpr uint8_t kAudioCsReqRange = 0x02;
76// Clock Source control selectors (UAC2 Table A-18).
77static constexpr uint8_t kAudioCsCtrlSamFreq = 0x01;
78static constexpr uint8_t kAudioCsCtrlClkValid = 0x02;
79// Feature Unit control selectors (UAC2 Table A-23).
80static constexpr uint8_t AUDIO_FU_CTRL_MUTE = 0x01;
81static constexpr uint8_t AUDIO_FU_CTRL_VOLUME = 0x02;
82// Terminal type: USB Streaming (UAC2 Table 2-1).
83static constexpr uint16_t kAudioTermTypeUsbStreaming = 0x0101;
84// Interface Association Descriptor length — fixed 8 bytes.
85static constexpr uint16_t kAudioDescIadLen = 8;
86
87// USB control-transfer state-machine stages (universal to any USB stack's
88// control-transfer handling, not TinyUSB-specific).
89static constexpr uint8_t kControlStageSetup = 1;
90static constexpr uint8_t kControlStageData = 2;
91
92static inline uint8_t u16Low(uint16_t v) { return (uint8_t)(v & 0xFF); }
93static inline uint8_t u16High(uint16_t v) { return (uint8_t)(v >> 8); }
94
110public:
121
135
143 uint8_t rhport;
144 uint8_t const* p_desc; // Pointer to Standard AC Interface Descriptor
145 // ep_in/ep_out are written from the USB interrupt handler (openEpIn/
146 // openEpOut/closeEpIn/closeEpOut, reached via audiod_set_interface() in
147 // ISR context) and read from normal application code (write()/
148 // readBytes()/isStreamingActiveTx()/isStreamingActiveRx(), called from
149 // loop()). Without a memory-visibility guarantee across that boundary, a
150 // compiler is free to cache a stale read indefinitely -- observed in
151 // practice on the arm-none-eabi-gcc 7.2.1 toolchain used by the Arduino
152 // Renesas UNO R4 core: isStreamingActiveTx() kept reporting false while
153 // real ISO-IN transfers were actively completing, so write() silently
154 // discarded all audio data (see the "disregard data" branch below) while
155 // still reporting success.
156 volatile uint8_t ep_in; // TX audio data EP.
157 uint16_t ep_in_sz; // Current size of TX EP
158 uint8_t
159 ep_in_as_intf_num; // Standard AS Interface Descriptor number for IN
160 volatile uint8_t ep_out; // RX audio data EP.
161 uint16_t ep_out_sz; // Current size of RX EP
162 uint8_t
163 ep_out_as_intf_num; // Standard AS Interface Descriptor number for OUT
164 uint8_t ep_fb; // Feedback EP.
165 uint8_t ep_int; // Audio control interrupt EP.
166 bool mounted; // Device opened
167 uint16_t desc_length; // Length of audio function descriptor
168 struct {
169 uint32_t value;
170 uint32_t min_value;
171 uint32_t max_value;
172 uint8_t frame_shift;
175 union {
176 uint8_t power_of_2;
178 struct {
179 uint32_t sample_freq;
180 uint32_t mclk_freq;
182 struct {
183 uint32_t nom_value;
184 uint32_t fifo_lvl_avg;
185 uint16_t fifo_lvl_thr;
186 uint16_t rate_const[2];
191 uint16_t packet_sz_tx[3];
193 uint8_t interval_tx;
197 // Fractional sample accumulator for IN-endpoint flow control. Carries the
198 // sub-frame remainder (e.g. the 0.1 sample/frame of 44100 Hz) so the
199 // long-term average packet size matches the real sample rate.
201 // Cached OUT endpoint descriptor (populated by audiod_open()'s descriptor
202 // scan) so closeEpOut() can re-activate the endpoint -- resetting its
203 // busy/claimed state for the next SET_INTERFACE -- without needing to
204 // re-walk the descriptor. See closeEpOut() for why this is needed.
206 // From this point, data is not cleared by bus reset
207 uint8_t ctrl_buf_sz;
208 std::vector<uint8_t> ctrl_buf;
209 std::vector<uint8_t> alt_setting;
210 std::vector<uint8_t> lin_buf_out;
211 std::vector<uint8_t> lin_buf_in;
212 std::vector<uint32_t> fb_buf;
213 };
214
222 uint8_t method;
223 uint32_t sample_freq; // sample frequency in Hz
224
225 union {
226 struct {
227 uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to
228 // which sample clock is based on
230 };
231 };
232
235
240
247 USBAudioConfig cfg;
248 switch (mode) {
249 case RX_MODE:
250 cfg.enable_ep_out = true;
251 cfg.enable_ep_in = false;
252 break;
253 case TX_MODE:
254 cfg.enable_ep_out = false;
255 cfg.enable_ep_in = true;
256 break;
257 case RXTX_MODE:
258 cfg.enable_ep_out = true;
259 cfg.enable_ep_in = true;
260 break;
261 default:
262 break;
263 }
264 return cfg;
265 }
266
268 void setAudioInfo(AudioInfo info) override {
270 LOGE("Unsupported bits_per_sample: %d (must be 16, 24, or 32)",
272 return;
273 }
274 // full flexibility when not started yet
275 if (!is_started_) {
279 return;
280 }
281
282 // when started only sample rate can be changed on the fly
284 if (config_.channels != info.channels) {
285 LOGE(
286 "Could not change channel count from %d to %d: channel count is "
287 "fixed at startup!",
289 }
291 LOGE(
292 "Could not change bits per sample from %d to %d: bits per sample is "
293 "fixed at startup!",
295 }
296 // notifiy subscribed entities
298 // notify host about sample rate change via control request callback;
300 }
301
310 bool begin(const USBAudioConfig& cfg) {
311 if (!is_started_) {
312 config_ = cfg;
313 } else if (!configChanged(cfg)) {
314 return true; // already running with same config
315 } else {
317 }
318 return begin();
319 }
320
327 bool begin() {
328 if (!is_started_) {
330 LOGE("Unsupported bits_per_sample: %d (must be 16, 24, or 32)",
332 return false;
333 }
334
336 LOGI("USB on core: %d", core);
337
338 // Resize platform buffers — virtual so each platform uses the right API.
340
341 const int n = getAudioCount();
342 // GET_RANGE response is 2 + count*12 bytes; 64 is the floor needed for
343 // everything else sharing this buffer (Feature Unit GET_RANGE, etc.).
344 uint16_t cb_sz = 64u;
346 uint16_t needed =
347 (uint16_t)(2 + config_.supported_sample_rates.size() * 12);
348 if (needed > cb_sz) cb_sz = needed;
349 }
350 ctrl_buf_sz_.assign(n, cb_sz);
351
352 uint8_t desc[USB_DESCR_MAX_LEN];
353 uint16_t desc_len = descr_builder.buildFullDescriptor(desc);
354 desc_len_.assign(n, desc_len);
355
356 // master (index 0) + one per channel
357 const size_t vol_sz = (size_t)config_.channels + 1;
358 volume_.assign(vol_sz, 1.0f);
359 mute_.assign(vol_sz, false);
360
361
362 audiod_init();
363
364 if (!beginUSB()) {
365 LOGE("beginUSB failed");
366 return false;
367 }
368 is_started_ = true;
369 rx_primed_ = false; // re-arm the one-shot startup prime for this session
370 }
371
372 // Push current state to the host. sendInterruptNotification()
373 // is a no-op when not yet mounted, so this is harmless on first boot.
375 for (uint8_t ch = 0; ch < (uint8_t)volume_.size(); ch++) {
376 setVolume(volume(ch), ch);
377 setMute(isMute(ch), ch);
378 }
379 is_active_ = true;
380 return true;
381 }
389
391 inline bool isEpInEnabled() const { return config_.enable_ep_in; }
392
394 inline bool isEpOutEnabled() const { return config_.enable_ep_out; }
395
400
407
408 // ── Volume / Mute / Sample-rate API ─────────────────────────────────────
409
411 float volume() override { return volume(0); }
412
414 bool setVolume(float volume) override { return setVolume(volume, 0); }
415
419 float volume(uint8_t channel) {
420 return (channel < volume_.size()) ? volume_[channel] : 0.0f;
421 }
422
427 bool setVolume(float vol, uint8_t channel) {
428 LOGW("setVolume %f channel: %d", vol, channel);
429 if (channel >= volume_.size()) return false;
430 volume_[channel] = vol;
431 if (volume_cb_) volume_cb_(vol, channel);
434 return true;
435 }
436
439 bool isMute(uint8_t channel = 0) const {
440 return (channel < mute_.size()) ? mute_[channel] : false;
441 }
442
447 bool setMute(bool m, uint8_t channel = 0) {
448 LOGW("setMute %s channel: %d", m ? "true" : "false", channel);
449 if (channel >= mute_.size()) return false;
450 mute_[channel] = m;
451 if (mute_cb_) mute_cb_(m, channel);
454 return true;
455 }
456
460 void setVolumeCallback(std::function<void(float, uint8_t)> cb) {
461 volume_cb_ = std::move(cb);
462 }
463
467 void setMuteCallback(std::function<void(bool, uint8_t)> cb) {
468 mute_cb_ = std::move(cb);
469 }
470
474 void setSampleRateCallback(std::function<void(uint32_t)> cb) {
475 sample_rate_cb_ = std::move(cb);
476 }
477
482 void setStreamingStateCallback(std::function<void(bool, bool)> cb) {
483 streaming_state_cb_ = std::move(cb);
484 }
485
487 bool isStreamingActive() const {
489 }
490
492 bool isStreamingActiveTx() const {
493 for (const auto& fct : audiod_fct_) {
494 if (fct.ep_in != 0) return true;
495 }
496 return false;
497 }
498
500 bool isStreamingActiveRx() const {
501 for (const auto& fct : audiod_fct_) {
502 if (fct.ep_out != 0) return true;
503 }
504 return false;
505 }
508
510 uint8_t getAudioCount() const { return 1; }
511
513 bool mounted() { return backend().mounted(); }
514
519 void setGetReqItfCallback(std::function<bool(USBAudioDeviceBase*, uint8_t,
520 UsbSetupPacket const&)>
521 cb) {
522 get_req_itf_cb_ = cb;
523 }
524
529 void setGetReqEpCallback(std::function<bool(USBAudioDeviceBase*, uint8_t,
530 UsbSetupPacket const&)>
531 cb) {
532 get_req_ep_cb_ = cb;
533 }
534
539 void setFbDoneCallback(std::function<void(USBAudioDeviceBase*, uint8_t)> cb) {
540 fb_done_cb_ = cb;
541 }
542
548 std::function<void(USBAudioDeviceBase*, uint8_t)> cb) {
549 int_done_cb_ = cb;
550 }
551
557 std::function<bool(USBAudioDeviceBase*, uint8_t, audiod_function_t*, uint16_t)>
558 cb) {
559 tx_done_cb_ = cb;
560 }
561
566 void setRxDoneCallback(std::function<bool(USBAudioDeviceBase*, uint8_t,
567 audiod_function_t*, uint16_t)>
568 cb) {
569 rx_done_cb_ = cb;
570 }
571
577 std::function<bool(USBAudioDeviceBase*, uint8_t)> cb) {
578 req_entity_cb_ = cb;
579 }
580
586 std::function<bool(USBAudioDeviceBase*, uint8_t,
587 UsbSetupPacket const&)>
588 cb) {
590 }
591
597 std::function<bool(USBAudioDeviceBase*, uint8_t,
598 UsbSetupPacket const&, uint8_t*)>
599 cb) {
601 }
602
608 std::function<bool(USBAudioDeviceBase*, uint8_t,
609 UsbSetupPacket const&, uint8_t*)>
610 cb) {
612 }
613
619 std::function<bool(USBAudioDeviceBase*, uint8_t,
620 UsbSetupPacket const&, uint8_t*)>
621 cb) {
623 }
624
629 void setItfCloseEpCallback(std::function<bool(USBAudioDeviceBase*, uint8_t,
630 UsbSetupPacket const&)>
631 cb) {
633 }
634
640 std::function<void(USBAudioDeviceBase*, uint8_t, uint8_t,
642 cb) {
644 }
645
654
670 void setFeedbackPercent(int percent) {
671 int normalized = percent;
672 if (normalized < 0) normalized = 0;
673 if (normalized > 100) normalized = 100;
674 manual_feedback_percent_ = normalized;
675 }
676
680
684 size_t write(const uint8_t* data, size_t len) {
685 if (!is_started_) return 0;
686 serviceUSB();
687
688 // disregard data if the host has not opened the capture device (alt=0)
689 if (!isStreamingActiveTx()) return len;
690
691 // update the volume
692 if (config_.volume_active) processVolume((uint8_t*)data, len);
693
694 // Write all data, retrying if the buffer is full. On single-core
695 // platforms (RP2040), serviceUSB() drains the buffer by running
696 // tud_task() → xfer_cb. On dual-core (ESP32), the USB task drains
697 // independently and the SynchronizedNBufferRTOS blocks internally.
698 size_t written = 0;
699 while (written < len) {
700 int n = bufferTx().writeArray(data + written, len - written);
701 written += n;
702 if (written < len) {
703 serviceUSB(); // drain buffer to make space
704 if (!isStreamingActiveTx()) break; // host stopped
705 }
706 }
707 return written;
708 }
709
715 size_t readBytes(uint8_t* buffer, size_t bufsize) {
716 if (!is_started_) return 0;
717 serviceUSB();
718
719 if (!rx_primed_) {
720 uint8_t start_fill = config_.rx_start_fill_percent > 100
721 ? 100
723 if (start_fill > 0 && bufferRx().levelPercent() < start_fill) return 0;
724 rx_primed_ = true;
725 }
726
727 // get the data from the buffer
728 size_t ret = bufferRx().readArray(buffer, bufsize);
729 // upate the volume
730 if (config_.volume_active) processVolume(buffer, ret);
731
732 return ret;
733 }
734
736 int available() override {
737 if (!is_started_) return 0;
738 return bufferRx().available();
739 }
740
742 int availableForWrite() override {
743 if (!is_started_) return 0;
744 return bufferTx().availableForWrite();
745 }
746
748 operator bool() override { return is_started_ && mounted(); }
749
751 void end() {
752 for (auto& audio : audiod_fct_) {
753 std::fill(audio.lin_buf_in.begin(), audio.lin_buf_in.end(), 0);
754 std::fill(audio.lin_buf_out.begin(), audio.lin_buf_out.end(), 0);
755 }
756 bufferTx().reset();
757 bufferRx().reset();
758 is_started_ = false;
759 }
760
763 uint16_t audioPacketSize() const { return packetSize(); }
764
776 uint16_t getDescriptor(uint8_t* desc) {
777 active_config_ = config_; // save active config
779 }
780
786 uint8_t numInterfaces() const {
787 return (uint8_t)(1 + (config_.enable_ep_out ? 1 : 0) +
788 (config_.enable_ep_in ? 1 : 0));
789 }
790
792 bool isTxXferArmed() const { return tx_xfer_armed_; }
793
795 uint32_t getTxXferCount() const { return xfer_cb_tx_count_; }
797 uint32_t getRxXferCount() const { return xfer_cb_rx_count_; }
799 uint32_t getRxTotalBytes() const { return rx_total_bytes_; }
803 uint32_t getRxDroppedBytes() const { return rx_dropped_bytes_; }
806 uint32_t getTxFifoReadTotal() const { return tx_fifo_read_total_; }
807
810 uint16_t getTxFrameBytesLast() const { return tx_frame_bytes_last_; }
813 uint32_t getTxXferredLast() const { return tx_xferred_last_; }
815 uint32_t getTxSampleRate() const {
816 return audiod_fct_.empty() ? 0 : audiod_fct_[0].sample_rate_tx;
817 }
819 uint8_t getTxChannels() const {
820 return audiod_fct_.empty() ? 0 : audiod_fct_[0].n_channels_tx;
821 }
823 uint8_t getTxBytesPerSample() const {
824 return audiod_fct_.empty() ? 0 : audiod_fct_[0].n_bytes_per_sample_tx;
825 }
827 uint8_t getTxInterval() const {
828 return audiod_fct_.empty() ? 0 : audiod_fct_[0].interval_tx;
829 }
830
832 virtual int getActualCore() const { return -1;}
833
834 protected:
835 bool is_started_ = false;
836 bool usb_task_active_ = false; // true while a dedicated tud_task() FreeRTOS task is running
837 bool tx_xfer_armed_ = false;
838 volatile uint32_t xfer_cb_tx_count_ = 0;
839 volatile uint32_t tx_fifo_read_total_ = 0;
840 volatile uint32_t xfer_cb_rx_count_ = 0;
841 volatile uint32_t rx_total_bytes_ = 0;
842 volatile uint32_t rx_dropped_bytes_ = 0;
843 volatile uint16_t tx_frame_bytes_last_ = 0;
844 volatile uint32_t tx_xferred_last_ = 0;
845 int core = -1;
846 bool rx_primed_ = false;
847
848 bool is_active_ = false;
849 // ── Volume / mute state (sized to channels+1 in begin()) ─────────────────
850 std::vector<float> volume_;
851 std::vector<bool> mute_;
852 std::function<void(float, uint8_t)> volume_cb_;
853 std::function<void(bool, uint8_t)> mute_cb_;
854 std::function<void(uint32_t)> sample_rate_cb_;
855 std::function<void(bool, bool)> streaming_state_cb_;
859 std::function<void(USBAudioDeviceBase*, uint8_t rhport)> int_done_cb_;
860 std::function<bool(USBAudioDeviceBase*, uint8_t rhport, audiod_function_t*, uint16_t bytes)>
862 std::function<bool(USBAudioDeviceBase*, uint8_t rhport, audiod_function_t*,
863 uint16_t xferred_bytes)>
865 // Callback for interface GET requests
866 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
867 UsbSetupPacket const&)>
869 // Callback for endpoint GET requests
870 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
871 UsbSetupPacket const&)>
873
874 // Callback for feedback done event
875 std::function<void(USBAudioDeviceBase*, uint8_t func_id)> fb_done_cb_;
876 std::function<bool(USBAudioDeviceBase*, uint8_t func_id)> req_entity_cb_;
877 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
878 UsbSetupPacket const& p_request)>
880 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
881 UsbSetupPacket const& p_request, uint8_t* pBuff)>
883
884 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
885 UsbSetupPacket const& p_request, uint8_t* pBuff)>
887
888 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
889 UsbSetupPacket const& p_request, uint8_t* pBuff)>
891
892 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
893 UsbSetupPacket const& p_request)>
895
896 std::function<void(USBAudioDeviceBase*, uint8_t func_id, uint8_t alt_itf,
897 audio_feedback_params_t* feedback_param)>
899
900 std::function<bool(USBAudioDeviceBase*, uint8_t func_id)>
902 uint8_t int_notify_buf_[6] = {};
903
904 // Manual feedback override: -1 means "automatic" (use the negotiated
905 // compute_method, e.g. AUDIO_FEEDBACK_METHOD_FIFO_COUNT); 0..100 means
906 // the caller is driving the feedback value directly via
907 // setFeedbackPercent(). See tud_audio_feedback_interval_isr().
909
910 // calculate!
911 std::vector<uint16_t> desc_len_;
912 // 64
913 std::vector<uint16_t> ctrl_buf_sz_;
914
915 std::vector<audiod_function_t> audiod_fct_;
916
917 // s_active_ lets the class-driver trampolines and the static process()
918 // trampoline reach the last-constructed instance without a singleton.
919 inline static USBAudioDeviceBase* s_active_ = nullptr;
920
922 void setConfig(const USBAudioConfig& cfg) { config_ = cfg; }
923
938 virtual void serviceUSB() = 0;
939
943 virtual USBAudioBackend& backend() = 0;
944
947
950
952 void processVolume(uint8_t* data, size_t len) {
953 switch (config_.bits_per_sample) {
954 case 8:
955 processVolume<int8_t>((int8_t*)data, len);
956 break;
957 case 16:
958 processVolume<int16_t>((int16_t*)data, len / 2);
959 break;
960 case 24:
961 processVolume<int24_3bytes_t>((int24_3bytes_t*)data, len / 3);
962 break;
963 case 32:
964 processVolume<int32_t>((int32_t*)data, len / 4);
965 break;
966 default:
967 // Unsupported bit depth; do nothing.
968 break;
969 }
970 }
971
974 float getVolumeExt(uint8_t channel) const {
975 if (volume_.empty()) return 1.0f;
976 if (mute_[0]) return 0.0f; // master mute
977 float master = volume_[0];
978 if (channel >= volume_.size()) return master; // no per-channel entry
979 if (mute_[channel]) return 0.0f; // per-channel mute
980 return master * volume_[channel];
981 }
982
983 template <typename T>
984 void processVolume(T* data, size_t sample_count) {
985 uint8_t ch_count = config_.channels;
986 for (size_t i = 0; i < sample_count; i++) {
987 uint8_t ch = (uint8_t)(i % ch_count) + 1; // 1-based per-channel index
988 float vol = getVolumeExt(ch);
989 data[i] = (T)(data[i] * vol);
990 }
991 }
992
1001 void setSampleRate(uint32_t rate) {
1002 bool rate_updated = rate != config_.sample_rate;
1003 config_.sample_rate = rate;
1004 LOGW("Sample rate changed to %u Hz", rate);
1005 if (rate_updated) {
1007 resizeBuffers();
1008 }
1009 for (auto& fct : audiod_fct_) fct.sample_rate_tx = rate;
1013 }
1014
1019 virtual bool beginUSB() = 0;
1020
1026 virtual void resizeBuffers() = 0;
1027
1030 static constexpr int16_t kVolumeMinDb256 = -25600; // -100 dB in 1/256 dB
1031
1034 static int16_t floatToUac2(float vol) {
1035 if (vol <= 0.0f) return (int16_t)0x8000;
1036 if (vol >= 1.0f) return 0;
1037 return (int16_t)((1.0f - vol) * kVolumeMinDb256);
1038 }
1039
1042 static float uac2ToFloat(int16_t v) {
1043 if (v == (int16_t)0x8000) return 0.0f;
1044 if (v >= 0) return 1.0f;
1045 if (v <= kVolumeMinDb256) return 0.0f;
1046 return 1.0f - (float)v / (float)kVolumeMinDb256;
1047 }
1048
1051 bool isFeatureUnit(uint8_t id) const {
1054 }
1055
1063 void sendInterruptNotification(uint8_t ctrlSel, uint8_t channel,
1064 uint8_t entityID) {
1065 if (!backend().mounted()) return;
1066 for (uint8_t i = 0; i < (uint8_t)audiod_fct_.size(); i++) {
1067 if (audiod_fct_[i].ep_int == 0) continue;
1068 int_notify_buf_[0] = 0x00; // bInfo: interface, not vendor
1069 int_notify_buf_[1] = kAudioCsReqCur; // bAttribute: CUR changed
1070 int_notify_buf_[2] = channel; // wValue low = CN
1071 int_notify_buf_[3] = ctrlSel; // wValue high = CS
1072 int_notify_buf_[4] = config_.itf_num_ac; // wIndex low = interface
1073 int_notify_buf_[5] = entityID; // wIndex high = entity ID
1074 (void)backend().transfer(0, audiod_fct_[i].ep_int, int_notify_buf_, 6);
1075 break;
1076 }
1077 }
1078
1079 bool configChanged(const USBAudioConfig& n) { return config_ != n; }
1080
1081 // Returns the control buffer size for a given function number
1082 uint16_t getCtrlBufSz(uint8_t fn) const {
1083 return (fn < ctrl_buf_sz_.size()) ? ctrl_buf_sz_[fn] : 64;
1084 }
1085
1086 // Returns the descriptor length for a given function number
1087 uint16_t getDescLen(uint8_t fn) const {
1088 return (fn < desc_len_.size()) ? desc_len_[fn] : 0;
1089 }
1090
1091 static bool isValidBitsPerSample(uint8_t bps) {
1092 return bps == 16 || bps == 24 || bps == 32;
1093 }
1094
1099
1100 // Max Bytes for one 1 ms isochronous USB packet.
1101 uint16_t packetSize() const { return descr_builder.calcMaxPacketSize(); }
1102
1103 // Returns the reset size for audiod_function_t up to and including
1104 // ctrl_buf_sz
1105 static constexpr size_t getResetSize() {
1106 return offsetof(audiod_function_t, ctrl_buf_sz) +
1107 sizeof(((audiod_function_t*)0)->ctrl_buf_sz);
1108 }
1109
1110 // Called by audiod_sof_isr() at the feedback interval.
1111 // Computes the current feedback value then claims the EP and sends it.
1113 uint32_t /*frame_count*/,
1114 uint8_t frame_shift) {
1115 audiod_function_t* audio = &audiod_fct_[func_id];
1116
1117 if (manual_feedback_percent_ >= 0) {
1118 // setFeedbackPercent() override: scale linearly across the negotiated
1119 // [min_value, max_value] range instead of running the automatic
1120 // compute_method below.
1121 uint32_t range = audio->feedback.max_value - audio->feedback.min_value;
1122 audio->feedback.value =
1123 audio->feedback.min_value +
1124 (range * (uint32_t)manual_feedback_percent_) / 100;
1125 } else {
1126 switch (audio->feedback.compute_method) {
1128 // In linear-buffer mode, audio data flows lin_buf_out → bufferRx(),
1129 // completely bypassing ep_out_ff. Reading ep_out_ff would always
1130 // return 0, driving the feedback to min_value and causing the host
1131 // to gradually reduce its send rate until the buffer drains
1132 // (audible periodic drop every 5-10 s). Use the platform RX ring
1133 // buffer level.
1134 uint32_t ff_count = (uint32_t)bufferRx().available();
1135 // Exponential weighted average keeps the level estimate stable
1138 (audio->feedback.compute.fifo_count.fifo_lvl_avg >> 8) +
1139 ((uint32_t)ff_count << 8);
1140 uint32_t avg = audio->feedback.compute.fifo_count.fifo_lvl_avg >> 8;
1141 uint32_t thr = audio->feedback.compute.fifo_count.fifo_lvl_thr;
1142 uint32_t nom = audio->feedback.compute.fifo_count.nom_value;
1143 if (avg > thr) {
1144 // Buffer fuller than the target level: ask the host to slow
1145 // down. rate_const[1] is scaled so avg==fifo_depth (full) maps
1146 // exactly to min_value.
1147 uint32_t drop =
1148 (uint32_t)audio->feedback.compute.fifo_count.rate_const[1] *
1149 (avg - thr);
1150 audio->feedback.value =
1151 (nom > drop) ? nom - drop : audio->feedback.min_value;
1152 } else {
1153 // Buffer emptier than the target level: ask the host to speed
1154 // up. rate_const[0] is scaled so avg==0 (empty) maps exactly to
1155 // max_value.
1156 audio->feedback.value =
1157 nom +
1158 (uint32_t)audio->feedback.compute.fifo_count.rate_const[0] *
1159 (thr - avg);
1160 }
1161 uint32_t clamped = audio->feedback.value < audio->feedback.min_value
1162 ? audio->feedback.min_value
1163 : audio->feedback.value;
1164 audio->feedback.value = clamped > audio->feedback.max_value
1165 ? audio->feedback.max_value
1166 : clamped;
1167 } break;
1168
1170 audio->feedback.value = 1UL << audio->feedback.compute.power_of_2;
1171 break;
1172
1174 audio->feedback.value =
1175 (uint32_t)(audio->feedback.compute.float_const *
1176 (float)(1UL << (16u - (frame_shift - 1u))));
1177 break;
1178
1180 uint32_t frame_div = backend().isFullSpeed() ? 1000u : 8000u;
1181 audio->feedback.value =
1182 (audio->feedback.compute.fixed.sample_freq << 16) / frame_div;
1183 } break;
1184
1185 default:
1186 break;
1187 }
1188 }
1189
1190 if (backend().claimEndpoint(audio->rhport, audio->ep_fb)) {
1191 audiod_fb_send(audio);
1192 }
1193 }
1194
1195 // USBD Driver API — public so a backend's class-driver registration glue
1196 // (e.g. USBAudioBackendTinyUSB.h's trampoline into TinyUSB's
1197 // usbd_class_driver_t) can call these without being a member of this
1198 // class. A native-HAL backend with no such registration concept would
1199 // instead call these directly from its own ISR/init code.
1200 public:
1201 void audiod_init(void) {
1202 audiod_fct_.resize(getAudioCount());
1203
1204 // Initialize control buffers
1205 for (uint8_t i = 0; i < getAudioCount(); i++) {
1206 audiod_function_t* audio = &audiod_fct_[i];
1207 // Initialize control buffers
1208 int size = getCtrlBufSz(i);
1209 audio->ctrl_buf.resize(size);
1210 audio->ctrl_buf_sz = size;
1211 // Initialize active alternate interface buffers
1213 // Initialize IN EP — lin_buf_in is the DMA staging buffer (one frame).
1214 // Audio data flows through bufferTx() (resized in begin()), not ep_in_ff.
1215 if (isEpInEnabled()) {
1216 // Max packet across all advertised rates (see supported_sample_rates).
1217 uint16_t max_pkt = descr_builder.calcPacketSizeForRate(
1219 audio->lin_buf_in.resize(max_pkt);
1220 }
1221 // Initialize OUT EP linear (DMA staging) buffer.
1222 if (isEpOutEnabled()) {
1223 uint16_t max_pkt = descr_builder.calcPacketSizeForRate(
1225 audio->lin_buf_out.resize(max_pkt);
1226 }
1227 if (isFeedbackEpEnabled()) {
1228 audio->fb_buf.resize(1); // one uint32_t = 4 bytes of feedback data
1229 }
1230 }
1231 }
1232
1233 bool audiod_deinit(void) {
1234 return false; // TODO not implemented yet
1235 }
1236
1237 void audiod_reset(uint8_t rhport) {
1238 (void)rhport;
1239 for (uint8_t i = 0; i < getAudioCount(); i++) {
1240 audiod_function_t* audio = &audiod_fct_[i];
1241 memset(audio, 0, getResetSize());
1242 if (isEpInEnabled()) {
1243 bufferTx().reset();
1244 }
1245 if (isEpOutEnabled()) {
1246 bufferRx().reset();
1247 }
1248 }
1249 }
1250
1251 uint16_t audiod_open(uint8_t rhport, UsbInterfaceDescriptorView const& itf_desc,
1252 uint8_t const* raw_desc, uint16_t max_len) {
1253 (void)max_len;
1254 if (!(kUsbClassAudio == itf_desc.bInterfaceClass &&
1256 return 0;
1257 if (itf_desc.bInterfaceProtocol != kAudioIntProtocolCodeV2) return 0;
1258 if (itf_desc.bNumEndpoints > 1) return 0;
1259 if (itf_desc.bNumEndpoints == 1 && !isInterruptEpEnabled()) return 0;
1260 if (itf_desc.bAlternateSetting != 0) return 0;
1261 uint8_t i;
1262 for (i = 0; i < getAudioCount(); i++) {
1263 if (!audiod_fct_[i].p_desc) {
1264 audiod_fct_[i].p_desc = raw_desc;
1265 audiod_fct_[i].rhport = rhport;
1266 audiod_fct_[i].desc_length = getDescLen(i);
1267 // audiod_reset() zeroes ctrl_buf_sz via memset — restore it so
1268 // controlTransfer() receives the correct buffer length.
1269 audiod_fct_[i].ctrl_buf_sz = getCtrlBufSz(i);
1271 uint8_t ep_in = 0, ep_out = 0, ep_fb = 0;
1272 uint16_t ep_in_size = 0, ep_out_size = 0;
1273 UsbEndpointDescriptorView desc_ep_out{};
1274 bool has_ep_out_view = false;
1275 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1276 uint8_t const* p_desc_end =
1277 p_desc + audiod_fct_[i].desc_length - kAudioDescIadLen;
1278 while (p_desc_end - p_desc > 0) {
1279 if (descType(p_desc) == kUsbDescTypeEndpoint) {
1280 UsbEndpointDescriptorView desc_ep = decodeEndpoint(p_desc);
1281 if (desc_ep.xferType == UsbXferType::Isochronous) {
1282 if (isFeedbackEpEnabled() && desc_ep.usage == 1) {
1283 ep_fb = desc_ep.bEndpointAddress;
1284 }
1285 if (desc_ep.usage == 0) {
1286 if (isEpInEnabled() && desc_ep.direction() == UsbDir::In) {
1287 ep_in = desc_ep.bEndpointAddress;
1288 uint16_t sz = desc_ep.packetSize();
1289 ep_in_size = sz > ep_in_size ? sz : ep_in_size;
1290 } else if (isEpOutEnabled() &&
1291 desc_ep.direction() == UsbDir::Out) {
1292 ep_out = desc_ep.bEndpointAddress;
1293 uint16_t sz = desc_ep.packetSize();
1294 ep_out_size = sz > ep_out_size ? sz : ep_out_size;
1295 desc_ep_out = desc_ep;
1296 has_ep_out_view = true;
1297 }
1298 }
1299 }
1300 }
1301 p_desc = descNext(p_desc);
1302 }
1303 if (isEpInEnabled() && ep_in) {
1304 bool alloc_ok = backend().isoAllocEndpoint(rhport, ep_in, ep_in_size);
1305 LOGD("iso_alloc IN ep=0x%02x sz=%u: %s", ep_in, ep_in_size,
1306 alloc_ok ? "OK" : "FAIL");
1307 }
1308 if (isEpOutEnabled() && ep_out) {
1309 bool alloc_ok = backend().isoAllocEndpoint(rhport, ep_out, ep_out_size);
1310 LOGD("iso_alloc OUT ep=0x%02x sz=%u: %s", ep_out, ep_out_size,
1311 alloc_ok ? "OK" : "FAIL");
1312 if (has_ep_out_view) audiod_fct_[i].ep_out_view = desc_ep_out;
1313 if (backend().usesIsoAlloc() && has_ep_out_view) {
1314 // Pre-activate during enumeration (no isochronous traffic).
1315 // Cannot be done in SET_INTERFACE because iso_activate blocks
1316 // on ESP32's DWC2 when the host is already sending. Activation
1317 // itself resets the endpoint's busy/claimed state to a known
1318 // (not busy) baseline, so nothing further is needed here for
1319 // the first open -- see closeEpOut() for why re-activation is
1320 // also needed on every subsequent re-open.
1321 backend().isoActivateEndpoint(rhport, desc_ep_out);
1322 LOGD("iso_activate OUT: done");
1323 }
1324 }
1325 if (isFeedbackEpEnabled() && ep_fb) {
1326 backend().isoAllocEndpoint(rhport, ep_fb, 4);
1327 }
1328 }
1329 // Scan for bclock_id_tx (clock entity referenced by the USB-streaming
1330 // terminal) and interval_tx. Runs in TX, RX, and RXTX mode so that
1331 // clock-validity/frequency GET requests always succeed.
1332 // TX/RXTX: Output Terminal type=USB_STREAMING → bCSourceID at [8]
1333 // RX: Input Terminal type=USB_STREAMING → bCSourceID at [7]
1334 // interval_tx is only meaningful for the ISO IN endpoint (TX/RXTX).
1335 if (isEpInEnabled() || isEpOutEnabled()) {
1336 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1337 uint8_t const* p_desc_end =
1338 p_desc + audiod_fct_[i].desc_length - kAudioDescIadLen;
1339 while (p_desc_end - p_desc > 0) {
1340 if (descType(p_desc) == kUsbDescTypeEndpoint) {
1341 if (isEpInEnabled()) {
1342 UsbEndpointDescriptorView desc_ep = decodeEndpoint(p_desc);
1343 if (desc_ep.xferType == UsbXferType::Isochronous &&
1344 desc_ep.usage == 0 && desc_ep.direction() == UsbDir::In) {
1345 audiod_fct_[i].interval_tx = desc_ep.bInterval;
1346 }
1347 }
1348 } else if (descType(p_desc) == kUsbDescTypeCsInterface) {
1349 if (descSubtype(p_desc) == kAudioCsAcOutputTerminal) {
1350 if (descU16(p_desc + 4) == kAudioTermTypeUsbStreaming) {
1351 audiod_fct_[i].bclock_id_tx = p_desc[8]; // OT bCSourceID
1352 }
1353 } else if (descSubtype(p_desc) == kAudioCsAcInputTerminal) {
1354 if (descU16(p_desc + 4) == kAudioTermTypeUsbStreaming) {
1355 audiod_fct_[i].bclock_id_tx = p_desc[7]; // IT bCSourceID
1356 }
1357 }
1358 }
1359 p_desc = descNext(p_desc);
1360 }
1361 }
1362
1363 if (isInterruptEpEnabled()) {
1364 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1365 uint8_t const* p_desc_end =
1366 p_desc + audiod_fct_[i].desc_length - kAudioDescIadLen;
1367 while (p_desc_end - p_desc > 0) {
1368 if (descType(p_desc) == kUsbDescTypeEndpoint) {
1369 UsbEndpointDescriptorView desc_ep = decodeEndpoint(p_desc);
1370 uint8_t const ep_addr = desc_ep.bEndpointAddress;
1371 if (desc_ep.direction() == UsbDir::In &&
1372 desc_ep.xferType == UsbXferType::Interrupt) {
1373 if (backend().openEndpoint(audiod_fct_[i].rhport, desc_ep)) {
1374 audiod_fct_[i].ep_int = ep_addr;
1375 } else {
1376 LOGE(" UAC2: interrupt EP 0x%02x open failed", ep_addr);
1377
1378 }
1379 }
1380 }
1381 p_desc = descNext(p_desc);
1382 }
1383 }
1384 audiod_fct_[i].mounted = true;
1385 break;
1386 }
1387 }
1388 if (i >= getAudioCount()) return 0;
1389 uint16_t drv_len = audiod_fct_[i].desc_length - kAudioDescIadLen;
1390 return drv_len;
1391 }
1392
1393 bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage,
1394 UsbSetupPacket const& request) {
1395 if (stage == kControlStageSetup) {
1396 return audiod_control_request(rhport, request);
1397 } else if (stage == kControlStageData) {
1398 return audiod_control_complete(rhport, request);
1399 }
1400 return true;
1401 }
1402 // Invoked when class request DATA stage is finished.
1403 // return false to stall control EP (e.g Host send non-sense DATA)
1404 bool audiod_control_complete(uint8_t rhport,
1405 UsbSetupPacket const& p_request) {
1406 // Handle audio class specific set requests
1407 if (p_request.type() == UsbSetupPacket::Type::Class &&
1408 p_request.direction() == UsbDir::Out) {
1409 uint8_t func_id;
1410
1411 switch (p_request.recipient()) {
1413 uint8_t itf = u16Low(p_request.wIndex);
1414 uint8_t entityID = u16High(p_request.wIndex);
1415
1416 if (entityID != 0) {
1417 func_id = 0;
1418 uint8_t ctrlSel = u16High(p_request.wValue);
1419 uint8_t* cb = audiod_fct_[func_id].ctrl_buf.data();
1420
1421 // ── Clock Source SET_CUR (sample rate) ──────────────
1423 ctrlSel == kAudioCsCtrlSamFreq &&
1424 p_request.bRequest == kAudioCsReqCur) {
1426 }
1427
1428 // ── Feature Unit SET_CUR (mute / volume) ────────────
1429 if (isFeatureUnit(entityID) &&
1430 p_request.bRequest == kAudioCsReqCur) {
1431 uint8_t channel = u16Low(p_request.wValue);
1432 if (ctrlSel == AUDIO_FU_CTRL_MUTE) {
1433 setMute(cb[0] != 0, channel);
1434 } else if (ctrlSel == AUDIO_FU_CTRL_VOLUME) {
1435 int16_t v;
1436 memcpy(&v, cb, 2);
1437 setVolume(uac2ToFloat(v), channel);
1438 }
1439 }
1440
1441 // Invoke callback
1443 return tud_audio_set_req_entity_cb_(this, rhport, p_request, cb);
1444 }
1445 } else {
1446 // Find index of audio driver structure and verify interface really
1447 // exists
1448 if (!audiod_verify_itf_exists(itf, &func_id)) return false;
1449
1450 // Invoke callback
1453 this, rhport, p_request,
1454 audiod_fct_[func_id].ctrl_buf.data());
1455 }
1456 }
1457 } break;
1458
1460 uint8_t ep = u16Low(p_request.wIndex);
1461
1462 // Check if entity is present and get corresponding driver index
1463 if (!audiod_verify_ep_exists(ep, &func_id)) return false;
1464
1465 // Invoke callback
1468 this, rhport, p_request, audiod_fct_[func_id].ctrl_buf.data());
1469 }
1470 } break;
1471 // Unknown/Unsupported recipient
1472 default:
1473 return false;
1474 }
1475 }
1476 return true;
1477 }
1478
1480 bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, UsbXferResult result,
1481 uint32_t xferred_bytes) {
1482 (void)result;
1483 (void)xferred_bytes;
1484 for (uint8_t func_id = 0; func_id < getAudioCount(); func_id++) {
1485 audiod_function_t* audio = &audiod_fct_[func_id];
1486 if (isInterruptEpEnabled() && audio->ep_int == ep_addr) {
1487 if (int_done_cb_) int_done_cb_(this, rhport);
1488 return true;
1489 }
1490 if (isEpInEnabled() && audio->ep_in == ep_addr &&
1491 audio->alt_setting.size() != 0) {
1493
1494 uint16_t frame_bytes = isEpInFlowControlEnabled()
1496 : audio->ep_in_sz;
1497 if (frame_bytes > audio->ep_in_sz) frame_bytes = audio->ep_in_sz;
1498 if (tx_done_cb_) tx_done_cb_(this, rhport, audio, frame_bytes);
1499
1500 tx_frame_bytes_last_ = frame_bytes;
1501 tx_xferred_last_ = xferred_bytes;
1502
1503 // Drain platform buffer into lin_buf_in, zero-pad, send via DMA.
1504 {
1505 uint8_t* dst = audio->lin_buf_in.data();
1506 uint16_t n = (uint16_t)bufferTx().readArray(dst, frame_bytes);
1508 if (n < frame_bytes) memset(dst + n, 0, frame_bytes - n);
1509 (void)backend().transfer(rhport, audio->ep_in, dst, frame_bytes);
1510 }
1511 return true;
1512 }
1513 if (isEpOutEnabled() && audio->ep_out == ep_addr) {
1515 rx_total_bytes_ += xferred_bytes;
1516 // Copy DMA-received data into the platform buffer, re-arm DMA.
1517 if (xferred_bytes > 0) {
1518 int written = bufferRx().writeArray(audio->lin_buf_out.data(),
1519 (int)xferred_bytes);
1520 if ((uint32_t)written < xferred_bytes)
1521 rx_dropped_bytes_ += xferred_bytes - (uint32_t)written;
1522 }
1523 // Re-arm the OUT endpoint before invoking the callback: rx_done_cb_
1524 // is user code (e.g. it may push the packet through a resampler)
1525 // and its duration is out of our control. If the next transfer
1526 // isn't queued until after it returns, a slow callback delays
1527 // re-arming past the next isochronous OUT window and the host's
1528 // packet for that frame is lost - audible as clicks/dropouts,
1529 // and more likely to happen the larger each packet is (e.g. at
1530 // 44.1/48kHz vs. 32kHz).
1531 (void)backend().transfer(rhport, audio->ep_out,
1532 audio->lin_buf_out.data(), audio->ep_out_sz);
1533 if (rx_done_cb_)
1534 rx_done_cb_(this, rhport, audio, (uint16_t)xferred_bytes);
1535 return true;
1536 }
1537 if (isFeedbackEpEnabled() && audio->ep_fb == ep_addr) {
1538 // SOF ISR owns re-sending; just notify the application.
1539 if (fb_done_cb_) fb_done_cb_(this, func_id);
1540 return true;
1541 }
1542 }
1543 return false;
1544 }
1545
1546 void audiod_sof_isr(uint8_t rhport, uint32_t frame_count) {
1547 (void)rhport;
1548 (void)frame_count;
1550 for (uint8_t i = 0; i < getAudioCount(); i++) {
1551 audiod_function_t* audio = &audiod_fct_[i];
1552 if (audio->ep_fb != 0) {
1553 uint8_t const hs_adjust = backend().isHighSpeed() ? 3 : 0;
1554 uint32_t const interval =
1555 1UL << (audio->feedback.frame_shift - hs_adjust);
1556 if (0 == (frame_count & (interval - 1))) {
1557 tud_audio_feedback_interval_isr(i, frame_count,
1558 audio->feedback.frame_shift);
1559 }
1560 }
1561 }
1562 }
1563 }
1564
1565 protected:
1566 // ── Clock Source GET handler ────────────────────────────────────────────
1567 bool handleClockSourceGet(uint8_t rhport,
1568 UsbSetupPacket const& p_request,
1569 uint8_t* cb) {
1570 uint8_t ctrlSel = u16High(p_request.wValue);
1571 if (ctrlSel == kAudioCsCtrlClkValid &&
1572 p_request.bRequest == kAudioCsReqCur) {
1573 cb[0] = 1;
1574 return backend().controlTransfer(rhport, p_request, cb, 1);
1575 }
1576 if (ctrlSel == kAudioCsCtrlSamFreq) {
1577 uint32_t rate = (uint32_t)config_.sample_rate;
1578 if (p_request.bRequest == kAudioCsReqCur) {
1579 memcpy(cb, &rate, 4);
1580 return backend().controlTransfer(rhport, p_request, cb, 4);
1581 }
1582 if (p_request.bRequest == kAudioCsReqRange) {
1584 // List all supported discrete rates
1585 const auto& rates = config_.supported_sample_rates;
1586 uint16_t cnt = (uint16_t)rates.size();
1587 memcpy(cb, &cnt, 2);
1588 for (size_t i = 0; i < rates.size(); i++) {
1589 uint32_t r = rates[i];
1590 uint32_t z = 0;
1591 memcpy(cb + 2 + i * 12, &r, 4);
1592 memcpy(cb + 2 + i * 12 + 4, &r, 4);
1593 memcpy(cb + 2 + i * 12 + 8, &z, 4);
1594 }
1595 return backend().controlTransfer(
1596 rhport, p_request, cb,
1597 (uint16_t)(2 + rates.size() * 12));
1598 } else {
1599 // Single fixed rate from config
1600 uint16_t cnt = 1;
1601 uint32_t z = 0;
1602 memcpy(cb, &cnt, 2);
1603 memcpy(cb + 2, &rate, 4); // dMIN
1604 memcpy(cb + 6, &rate, 4); // dMAX
1605 memcpy(cb + 10, &z, 4); // dRES = 0 (fixed)
1606 return backend().controlTransfer(rhport, p_request, cb, 14);
1607 }
1608 }
1609 }
1610 return false;
1611 }
1612
1613 // ── Feature Unit GET handler ──────────────────────────────────────────
1614 bool handleFeatureUnitGet(uint8_t rhport,
1615 UsbSetupPacket const& p_request,
1616 uint8_t* cb) {
1617 uint8_t ctrlSel = u16High(p_request.wValue);
1618 uint8_t channel = u16Low(p_request.wValue);
1619 if (ctrlSel == AUDIO_FU_CTRL_MUTE &&
1620 p_request.bRequest == kAudioCsReqCur) {
1621 cb[0] = isMute(channel) ? 1 : 0;
1622 return backend().controlTransfer(rhport, p_request, cb, 1);
1623 }
1624 if (ctrlSel == AUDIO_FU_CTRL_VOLUME) {
1625 if (p_request.bRequest == kAudioCsReqCur) {
1626 int16_t v = floatToUac2(volume(channel));
1627 memcpy(cb, &v, 2);
1628 return backend().controlTransfer(rhport, p_request, cb, 2);
1629 }
1630 if (p_request.bRequest == kAudioCsReqRange) {
1631 uint16_t cnt = 1;
1632 int16_t vmin = -25600, vmax = 0, vres = 256;
1633 memcpy(cb + 0, &cnt, 2);
1634 memcpy(cb + 2, &vmin, 2);
1635 memcpy(cb + 4, &vmax, 2);
1636 memcpy(cb + 6, &vres, 2);
1637 return backend().controlTransfer(rhport, p_request, cb, 8);
1638 }
1639 }
1640 return false;
1641 }
1642
1643 // ── Entity request handler (Clock Source + Feature Unit) ──────────────
1644 bool handleEntityRequest(uint8_t rhport,
1645 UsbSetupPacket const& p_request,
1646 uint8_t entityID) {
1647 uint8_t func_id = 0;
1648 uint8_t* cb = audiod_fct_[func_id].ctrl_buf.data();
1649 bool is_get = (p_request.direction() == UsbDir::In);
1650
1652 if (is_get && handleClockSourceGet(rhport, p_request, cb)) return true;
1653 // SET — schedule data receive for audiod_control_complete()
1654 return backend().controlTransfer(rhport, p_request, cb,
1655 audiod_fct_[func_id].ctrl_buf_sz);
1656 }
1657
1658 if (isFeatureUnit(entityID)) {
1659 if (is_get && handleFeatureUnitGet(rhport, p_request, cb)) return true;
1660 // SET — schedule data receive
1661 return backend().controlTransfer(rhport, p_request, cb,
1662 audiod_fct_[func_id].ctrl_buf_sz);
1663 }
1664
1665 // Unknown entity — try generic verify
1666 uint8_t itf = u16Low(p_request.wIndex);
1667 if (!audiod_verify_entity_exists(itf, entityID, &func_id)) {
1668 backend().controlStatus(rhport, p_request);
1669 return true;
1670 }
1671 if (is_get && req_entity_cb_) return req_entity_cb_(this, func_id);
1672 return backend().controlTransfer(rhport, p_request,
1673 audiod_fct_[func_id].ctrl_buf.data(),
1674 audiod_fct_[func_id].ctrl_buf_sz);
1675 }
1676
1677 // ── Interface request handler (entityID == 0) ─────────────────────────
1678 bool handleInterfaceRequest(uint8_t rhport,
1679 UsbSetupPacket const& p_request) {
1680 uint8_t itf = u16Low(p_request.wIndex);
1681 uint8_t func_id;
1682 if (!audiod_verify_itf_exists(itf, &func_id)) return false;
1683 if (p_request.direction() == UsbDir::In) {
1684 if (get_req_itf_cb_) return get_req_itf_cb_(this, rhport, p_request);
1685 return false;
1686 }
1687 return backend().controlTransfer(rhport, p_request,
1688 audiod_fct_[func_id].ctrl_buf.data(),
1689 audiod_fct_[func_id].ctrl_buf_sz);
1690 }
1691
1692 // ── Endpoint request handler ──────────────────────────────────────────
1693 bool handleEndpointRequest(uint8_t rhport,
1694 UsbSetupPacket const& p_request) {
1695 uint8_t ep = u16Low(p_request.wIndex);
1696 uint8_t func_id;
1697 if (!audiod_verify_ep_exists(ep, &func_id)) return false;
1698 if (p_request.direction() == UsbDir::In) {
1699 if (get_req_ep_cb_) return get_req_ep_cb_(this, rhport, p_request);
1700 return false;
1701 }
1702 return backend().controlTransfer(rhport, p_request,
1703 audiod_fct_[func_id].ctrl_buf.data(),
1704 audiod_fct_[func_id].ctrl_buf_sz);
1705 }
1706
1707 // ── Main control request dispatcher ───────────────────────────────────
1708 bool audiod_control_request(uint8_t rhport,
1709 UsbSetupPacket const& p_request) {
1710 if (p_request.type() == UsbSetupPacket::Type::Standard) {
1711 switch (p_request.bRequest) {
1712 case (uint8_t)UsbStdRequest::GetInterface:
1713 return audiod_get_interface(rhport, p_request);
1714 case (uint8_t)UsbStdRequest::SetInterface:
1715 return audiod_set_interface(rhport, p_request);
1716 case (uint8_t)UsbStdRequest::ClearFeature:
1717 return true;
1718 default:
1719 return false;
1720 }
1721 }
1722
1723 if (p_request.type() == UsbSetupPacket::Type::Class) {
1724 switch (p_request.recipient()) {
1726 uint8_t entityID = u16High(p_request.wIndex);
1727 return (entityID != 0)
1728 ? handleEntityRequest(rhport, p_request, entityID)
1729 : handleInterfaceRequest(rhport, p_request);
1730 }
1732 return handleEndpointRequest(rhport, p_request);
1733 default:
1734 return false;
1735 }
1736 }
1737
1738 return false;
1739 }
1740
1741 // Verify an entity with the given ID exists and returns also the
1742 // corresponding driver index
1743 bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID,
1744 uint8_t* func_id) {
1745 uint8_t i;
1746 for (i = 0; i < getAudioCount(); i++) {
1747 // Look for the correct driver by checking if the unique standard AC
1748 // interface number fits
1749 if (audiod_fct_[i].p_desc &&
1750 decodeInterface(audiod_fct_[i].p_desc).bInterfaceNumber == itf) {
1751 // Get pointers after class specific AC descriptors and end of AC
1752 // descriptors - entities are defined in between
1753 uint8_t const* p_desc =
1754 descNext(audiod_fct_[i].p_desc); // Points to CS AC descriptor
1755 // AC interface header wTotalLength lives at byte offset 6 (UAC2
1756 // Table 4-5: bLength,bDescriptorType,bDescriptorSubType,bcdADC(2),
1757 // bCategory,wTotalLength(2),bmControls).
1758 uint8_t const* p_desc_end = descU16(p_desc + 6) + p_desc;
1759 p_desc = descNext(p_desc); // Get past CS AC descriptor
1760
1761 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1762 // strict-overflow warning
1763 while (p_desc_end - p_desc > 0) {
1764 if (p_desc[3] == entityID) // Entity IDs are always at offset 3
1765 {
1766 *func_id = i;
1767 return true;
1768 }
1769 p_desc = descNext(p_desc);
1770 }
1771 }
1772 }
1773 return false;
1774 }
1775
1776 bool audiod_verify_ep_exists(uint8_t ep, uint8_t* func_id) {
1777 uint8_t i;
1778 for (i = 0; i < getAudioCount(); i++) {
1779 if (audiod_fct_[i].p_desc) {
1780 // Get pointer at end
1781 uint8_t const* p_desc_end =
1782 audiod_fct_[i].p_desc + audiod_fct_[i].desc_length;
1783
1784 // Advance past AC descriptors - EP we look for are streaming EPs
1785 uint8_t const* p_desc = descNext(audiod_fct_[i].p_desc);
1786 p_desc += descU16(p_desc + 6); // AC header wTotalLength, see above
1787
1788 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1789 // strict-overflow warning
1790 while (p_desc_end - p_desc > 0) {
1791 if (descType(p_desc) == kUsbDescTypeEndpoint &&
1792 decodeEndpoint(p_desc).bEndpointAddress == ep) {
1793 *func_id = i;
1794 return true;
1795 }
1796 p_desc = descNext(p_desc);
1797 }
1798 }
1799 }
1800 return false;
1801 }
1802
1803 bool audiod_verify_itf_exists(uint8_t itf, uint8_t* func_id) {
1804 uint8_t i;
1805 for (i = 0; i < getAudioCount(); i++) {
1806 if (audiod_fct_[i].p_desc) {
1807 // Get pointer at beginning and end
1808 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1809 uint8_t const* p_desc_end = audiod_fct_[i].p_desc +
1810 audiod_fct_[i].desc_length -
1812 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1813 // strict-overflow warning
1814 while (p_desc_end - p_desc > 0) {
1815 if (descType(p_desc) == kUsbDescTypeInterface &&
1816 decodeInterface(audiod_fct_[i].p_desc).bInterfaceNumber == itf) {
1817 *func_id = i;
1818 return true;
1819 }
1820 p_desc = descNext(p_desc);
1821 }
1822 }
1823 }
1824 return false;
1825 }
1826
1828 uint8_t const* p_desc) {
1829 // Seed the TX sample rate from the configured AudioInfo so packet-size
1830 // calculation works even when the host never issues a SET_CUR(SAM_FREQ)
1831 // request (typical for single-frequency clock sources). The host may still
1832 // override this later via audiod_control_complete().
1833 if (audio->sample_rate_tx == 0)
1834 audio->sample_rate_tx = (uint32_t)config_.sample_rate;
1835
1836 p_desc = descNext(p_desc); // Exclude standard AS interface descriptor
1837 // of current alternate interface descriptor
1838
1839 // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format
1840 // type and format and also to get number of physical channels.
1841 // Layout (UAC2 Table 4-27): bLength,bDescriptorType,bDescriptorSubType,
1842 // bTerminalLink,bmControls,bFormatType[5],bmFormats(4),bNrChannels[10],...
1843 if (descType(p_desc) == kUsbDescTypeCsInterface &&
1844 descSubtype(p_desc) == kAudioCsAsGeneral) {
1845 audio->n_channels_tx = p_desc[10];
1846 audio->format_type_tx = (audio_format_type_t)p_desc[5];
1847 // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
1848 // Layout: bLength,bDescriptorType,bDescriptorSubType,bFormatType,
1849 // bSubslotSize[4],bBitResolution.
1850 p_desc = descNext(p_desc);
1851 if (descType(p_desc) == kUsbDescTypeCsInterface &&
1852 descSubtype(p_desc) == kAudioCsAsFormatType &&
1853 p_desc[3] == AUDIO_FORMAT_TYPE_I) {
1854 audio->n_bytes_per_sample_tx = p_desc[4];
1855 }
1856 }
1857
1858 // Fallback from config if descriptor parsing missed any field.
1859 // The descriptor struct layout may differ across TinyUSB versions.
1860 if (audio->n_channels_tx == 0) audio->n_channels_tx = config_.channels;
1861 if (audio->n_bytes_per_sample_tx == 0)
1863 if (audio->format_type_tx == 0) audio->format_type_tx = AUDIO_FORMAT_TYPE_I;
1864 }
1865
1866 // This helper function finds for a given audio function and AS interface
1867 // number the index of the attached driver structure, the index of the
1868 // interface in the audio function
1869 // (e.g. the std. AS interface with interface number 15 is the first AS
1870 // interface for the given audio function and thus gets index zero), and
1871 // finally a pointer to the std. AS interface, where the pointer always points
1872 // to the first alternate setting i.e. alternate interface zero.
1874 uint8_t* idxItf,
1875 uint8_t const** pp_desc_int) {
1876 if (audio->p_desc) {
1877 // Get pointer at end
1878 uint8_t const* p_desc_end =
1879 audio->p_desc + audio->desc_length - kAudioDescIadLen;
1880
1881 // Advance past AC descriptors
1882 uint8_t const* p_desc = descNext(audio->p_desc);
1883 p_desc += descU16(p_desc + 6); // AC header wTotalLength
1884
1885 uint8_t tmp = 0;
1886 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1887 // strict-overflow warning
1888 while (p_desc_end - p_desc > 0) {
1889 // We assume the number of alternate settings is increasing thus we
1890 // return the index of alternate setting zero!
1891 if (descType(p_desc) == kUsbDescTypeInterface &&
1892 decodeInterface(p_desc).bAlternateSetting == 0) {
1893 if (decodeInterface(p_desc).bInterfaceNumber == itf) {
1894 *idxItf = tmp;
1895 *pp_desc_int = p_desc;
1896 return true;
1897 }
1898 // Increase index, bytes read, and pointer
1899 tmp++;
1900 }
1901 p_desc = descNext(p_desc);
1902 }
1903 }
1904 return false;
1905 }
1906
1907 // This helper function finds for a given AS interface number the index of the
1908 // attached driver structure, the index of the interface in the audio function
1909 // (e.g. the std. AS interface with interface number 15 is the first AS
1910 // interface for the given audio function and thus gets index zero), and
1911 // finally a pointer to the std. AS interface, where the pointer always points
1912 // to the first alternate setting i.e. alternate interface zero.
1913 bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t* func_id,
1914 uint8_t* idxItf,
1915 uint8_t const** pp_desc_int) {
1916 // Loop over audio driver interfaces
1917 uint8_t i;
1918 for (i = 0; i < getAudioCount(); i++) {
1919 if (audiod_get_AS_interface_index(itf, &audiod_fct_[i], idxItf,
1920 pp_desc_int)) {
1921 *func_id = i;
1922 return true;
1923 }
1924 }
1925
1926 return false;
1927 }
1928
1929 bool audiod_get_interface(uint8_t rhport,
1930 UsbSetupPacket const& p_request) {
1931 uint8_t const itf = u16Low(p_request.wIndex);
1932
1933 // Find index of audio streaming interface
1934 uint8_t func_id, idxItf;
1935 uint8_t const* dummy;
1936
1937 if (!audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy))
1938 return false;
1939 if (!backend().controlTransfer(
1940 rhport, p_request, &audiod_fct_[func_id].alt_setting[idxItf], 1))
1941 return false;
1942
1943 LOGI(" Get itf: %u - current alt: %u", itf,
1944 audiod_fct_[func_id].alt_setting[idxItf]);
1945
1946 return true;
1947 }
1948
1950 bool apply_correction =
1952 // Format the feedback value
1953 if (apply_correction) {
1954 uint8_t* fb = (uint8_t*)audio->fb_buf.data();
1955
1956 // For FS format is 10.14
1957 *(fb++) = (audio->feedback.value >> 2) & 0xFF;
1958 *(fb++) = (audio->feedback.value >> 10) & 0xFF;
1959 *(fb++) = (audio->feedback.value >> 18) & 0xFF;
1960 *fb = 0;
1961 } else {
1962 audio->fb_buf[0] = audio->feedback.value;
1963 }
1964
1965 // About feedback format on FS
1966 //
1967 // 3 variables: Format | packetSize | sendSize | Working OS:
1968 // 16.16 4 4 Linux, Windows
1969 // 16.16 4 3 Linux
1970 // 16.16 3 4 Linux
1971 // 16.16 3 3 Linux
1972 // 10.14 4 4 Linux
1973 // 10.14 4 3 Linux
1974 // 10.14 3 4 Linux, OSX
1975 // 10.14 3 3 Linux, OSX
1976 //
1977 // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty
1978 // ugly
1979 return backend().transfer(audio->rhport, audio->ep_fb,
1980 (uint8_t*)audio->fb_buf.data(),
1981 apply_correction ? 3 : 4);
1982 }
1983
1984 // ── Close existing EPs for this interface ──────────────────────────────
1985 void closeEpIn(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
1986 UsbSetupPacket const& p_request) {
1987 if (!isEpInEnabled() || audio->ep_in_as_intf_num != itf) return;
1988 audio->ep_in_as_intf_num = 0;
1989 if (!backend().usesIsoAlloc()) backend().closeEndpoint(rhport, audio->ep_in);
1990 bufferTx().reset();
1992 tud_audio_set_itf_close_EP_cb_(this, rhport, p_request);
1993 audio->ep_in = 0;
1995 audio->packet_sz_tx[0] = 0;
1996 audio->packet_sz_tx[1] = 0;
1997 audio->packet_sz_tx[2] = 0;
1998 }
2000 }
2001
2002 void closeEpOut(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
2003 UsbSetupPacket const& p_request) {
2004 if (!isEpOutEnabled() || audio->ep_out_as_intf_num != itf) return;
2005 audio->ep_out_as_intf_num = 0;
2006 if (!backend().usesIsoAlloc()) {
2007 backend().closeEndpoint(rhport, audio->ep_out);
2008 } else {
2009 // usesIsoAlloc() backends never close the OUT endpoint between
2010 // sessions (DPRAM stays allocated), so nothing resets the DCD's
2011 // internal busy/claimed bookkeeping for it. If the transfer armed by
2012 // the *previous* openEpOut() never completed (no host data arrived,
2013 // or the session was torn down mid-flight), that endpoint stays
2014 // "busy" forever and every later usbd_edpt_xfer() call in openEpOut()
2015 // silently fails, permanently breaking RX after the first
2016 // open/close cycle. Re-activating here -- while the host is quiet
2017 // (it just asked to close the stream) rather than right as a new
2018 // session starts -- resets busy/claimed to a known-good state for
2019 // the next open, matching the reasoning in audiod_open() for why
2020 // this can't be done at SET_INTERFACE(alt=1) time instead.
2021 backend().isoActivateEndpoint(rhport, audio->ep_out_view);
2022 }
2024 tud_audio_set_itf_close_EP_cb_(this, rhport, p_request);
2025 audio->ep_out = 0;
2026 if (isFeedbackEpEnabled()) {
2027 audio->ep_fb = 0;
2028 memset(&audio->feedback, 0, sizeof(audio->feedback));
2029 }
2031 }
2032
2033 // ── Activate a single endpoint found in the descriptor ────────────────
2034 bool activateEndpoint(uint8_t rhport, const UsbEndpointDescriptorView& desc_ep,
2035 UsbDir dir = UsbDir::In) {
2036 if (backend().usesIsoAlloc()) {
2037 // Skip iso_activate for isochronous OUT — on ESP32's DWC2 it blocks
2038 // for the entire playback duration. The endpoint DPRAM was already
2039 // allocated by iso_alloc in audiod_open(). The XFER call in
2040 // openEpOut will configure the DCD to receive.
2041 if (dir == UsbDir::Out && desc_ep.xferType == UsbXferType::Isochronous)
2042 return true;
2043 return backend().isoActivateEndpoint(rhport, desc_ep);
2044 }
2045 (void)dir;
2046 return backend().openEndpoint(rhport, desc_ep);
2047 }
2048
2049 // ── Open the IN (TX) data endpoint ────────────────────────────────────
2050 void openEpIn(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
2051 const UsbEndpointDescriptorView& desc_ep,
2052 uint8_t const* p_desc_for_params) {
2053 audio->ep_in = desc_ep.bEndpointAddress;
2054 audio->ep_in_as_intf_num = itf;
2055 audio->ep_in_sz = desc_ep.packetSize();
2056 if (audio->ep_in_sz == 0) return;
2057
2059 audiod_parse_flow_control_params(audio, p_desc_for_params);
2060
2061 // Arm initial transfer (silence — copier fills the buffer).
2062 uint16_t first_pkt = packetSize();
2063 if (first_pkt > audio->ep_in_sz) first_pkt = audio->ep_in_sz;
2064 audio->lin_buf_in.assign(audio->ep_in_sz, 0);
2065 tx_xfer_armed_ = backend().transfer(rhport, audio->ep_in,
2066 audio->lin_buf_in.data(), first_pkt);
2068 }
2069
2070 // ── Open the OUT (RX) data endpoint ───────────────────────────────────
2071 void openEpOut(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
2072 const UsbEndpointDescriptorView& desc_ep) {
2073 audio->ep_out = desc_ep.bEndpointAddress;
2074 audio->ep_out_as_intf_num = itf;
2075 audio->ep_out_sz = desc_ep.packetSize();
2076 if (audio->ep_out_sz == 0) return;
2077
2078 // iso_activate was done in audiod_open() (no traffic, instant).
2079 // Just arm the transfer here.
2080 if (audio->lin_buf_out.size() < audio->ep_out_sz)
2081 audio->lin_buf_out.assign(audio->ep_out_sz, 0);
2082 backend().transfer(rhport, audio->ep_out, audio->lin_buf_out.data(),
2083 audio->ep_out_sz);
2085 }
2086
2087 // ── Open the explicit feedback endpoint ───────────────────────────────
2089 const UsbEndpointDescriptorView& desc_ep) {
2090 audio->ep_fb = desc_ep.bEndpointAddress;
2091 audio->feedback.frame_shift = desc_ep.bInterval - 1;
2092 }
2093
2094 // ── Configure feedback computation parameters ─────────────────────────
2095 void setupFeedback(audiod_function_t* audio, uint8_t func_id, uint8_t alt) {
2096 if (!isFeedbackEpEnabled() || audio->ep_fb == 0) return;
2097
2098 audio_feedback_params_t fb_param = {};
2100 fb_param.sample_freq = config_.sample_rate;
2102 tud_audio_feedback_params_cb_(this, func_id, alt, &fb_param);
2103 audio->feedback.compute_method = fb_param.method;
2104
2108
2109 uint32_t const frame_div = backend().isFullSpeed() ? 1000 : 8000;
2110 audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
2111 audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
2112
2113 switch (fb_param.method) {
2117 audiod_set_fb_params_freq(audio, fb_param.sample_freq,
2118 fb_param.frequency.mclk_freq);
2119 break;
2121 // Use bufferRx() size — ep_out_ff may be uninitialized in linear buffer mode
2122 uint16_t fifo_depth = bufferRx().size();
2123 if (fifo_depth == 0) fifo_depth = 1; // guard against div-by-zero
2124 uint16_t fifo_lvl_thr = fifo_depth / 2;
2125 audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
2126 // fifo_lvl_avg is a Q8 exponential moving average (see the update
2127 // formula in tud_audio_feedback_interval_isr: `avg = fifo_lvl_avg
2128 // >> 8`, fed by `ff_count << 8` each tick) -- seed it in the same
2129 // Q8 scale. It was previously seeded as `fifo_lvl_thr << 16` (Q16,
2130 // 256x too large), which pinned the very first ~1400 feedback
2131 // updates (~1.4s at 1kHz) at feedback.max_value before decaying
2132 // into a sane range.
2134 ((uint32_t)fifo_lvl_thr) << 8;
2135 uint32_t nominal =
2136 ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
2137 audio->feedback.compute.fifo_count.nom_value = nominal;
2139 (uint16_t)((audio->feedback.max_value - nominal) / fifo_lvl_thr);
2141 (uint16_t)((nominal - audio->feedback.min_value) / fifo_lvl_thr);
2142 if (backend().isHighSpeed()) {
2143 audio->feedback.compute.fifo_count.rate_const[0] /= 8;
2144 audio->feedback.compute.fifo_count.rate_const[1] /= 8;
2145 }
2146 } break;
2147 default:
2148 break;
2149 }
2150 }
2151
2152 // ── Scan descriptor for endpoints and open them ───────────────────────
2154 uint8_t func_id, uint8_t itf, uint8_t alt) {
2155 uint8_t const* p_desc = audio->p_desc;
2156 uint8_t const* p_desc_end =
2157 p_desc + audio->desc_length - kAudioDescIadLen;
2158 LOGD(" openEPs: p_desc=%p end=%p len=%u itf=%u alt=%u",
2159 p_desc, p_desc_end, audio->desc_length, itf, alt);
2160
2161 while (p_desc_end - p_desc > 0) {
2162 if (descType(p_desc) == kUsbDescTypeInterface &&
2163 decodeInterface(p_desc).bInterfaceNumber == itf &&
2164 decodeInterface(p_desc).bAlternateSetting == alt) {
2165 uint8_t const* p_desc_for_params =
2166 (isEpInEnabled() && isEpInFlowControlEnabled()) ? p_desc : nullptr;
2167 uint8_t foundEPs = 0;
2168 uint8_t nEps = decodeInterface(p_desc).bNumEndpoints;
2169 LOGD(" matched itf=%u alt=%u nEps=%u", itf, alt, nEps);
2170
2171 while (foundEPs < nEps && (p_desc_end - p_desc > 0)) {
2172 LOGD(" scan: type=0x%02x len=%u offset=%d",
2173 p_desc[1], p_desc[0], (int)(p_desc - audio->p_desc));
2174 if (descType(p_desc) == kUsbDescTypeEndpoint) {
2175 UsbEndpointDescriptorView desc_ep = decodeEndpoint(p_desc);
2176
2177 LOGD(" activating ep=0x%02x type=%u...",
2178 desc_ep.bEndpointAddress, (unsigned)desc_ep.xferType);
2179 if (!activateEndpoint(rhport, desc_ep, desc_ep.direction())) {
2180 LOGD(" activateEndpoint FAILED");
2181 p_desc = descNext(p_desc);
2182 continue;
2183 }
2184 LOGD(" activated OK");
2185 // Skip clear_stall for isochronous OUT (iso_activate was also
2186 // skipped). For other endpoints, clear the stall as usual.
2187 if (!(desc_ep.direction() == UsbDir::Out &&
2189 backend().clearStall(rhport, desc_ep.bEndpointAddress);
2190
2191 uint8_t ep_addr = desc_ep.bEndpointAddress;
2192 if (isEpInEnabled() && desc_ep.direction() == UsbDir::In &&
2193 desc_ep.usage == 0x00)
2194 openEpIn(rhport, audio, itf, desc_ep, p_desc_for_params);
2195
2196 if (isEpOutEnabled()) {
2197 if (desc_ep.direction() == UsbDir::Out)
2198 openEpOut(rhport, audio, itf, desc_ep);
2199 if (isFeedbackEpEnabled() &&
2200 desc_ep.direction() == UsbDir::In && desc_ep.usage == 1)
2201 openEpFeedback(audio, desc_ep);
2202 }
2203 foundEPs += 1;
2204 }
2205 p_desc = descNext(p_desc);
2206 }
2207
2208 if (foundEPs != nEps) return true; // ZLP already sent
2209
2211 tud_audio_set_itf_cb_(this, rhport, UsbSetupPacket{});
2212
2213 setupFeedback(audio, func_id, alt);
2214 return true;
2215 }
2216 p_desc = descNext(p_desc);
2217 }
2218 return true;
2219 }
2220
2221 // ── Main SET_INTERFACE handler ────────────────────────────────────────
2222 bool audiod_set_interface(uint8_t rhport,
2223 UsbSetupPacket const& p_request) {
2224 uint8_t const itf = u16Low(p_request.wIndex);
2225 uint8_t const alt = u16Low(p_request.wValue);
2226 LOGD("SET_ITF itf=%u alt=%u [start]", itf, alt);
2227
2228 uint8_t func_id, idxItf;
2229 uint8_t const* p_desc;
2230 if (!audiod_get_AS_interface_index_global(itf, &func_id, &idxItf,
2231 &p_desc)) {
2232 LOGD(" AS interface %u not found", itf);
2233 backend().controlStatus(rhport, p_request);
2234 return true;
2235 }
2236 LOGD(" found func=%u idx=%u", func_id, idxItf);
2237
2238 audiod_function_t* audio = &audiod_fct_[func_id];
2239
2240 // 1. Close existing EPs
2241 LOGD(" close EPs");
2242 closeEpIn(rhport, audio, itf, p_request);
2243 closeEpOut(rhport, audio, itf, p_request);
2244
2245 // 2. Save alt setting and acknowledge
2246 audio->alt_setting[idxItf] = alt;
2247
2248 backend().controlStatus(rhport, p_request);
2249 openEndpointsForAltSetting(rhport, audio, func_id, itf, alt);
2250
2251 // 4. Update SOF and flow control
2252 if (isFeedbackEpEnabled()) {
2253 bool enable_sof = false;
2254 for (uint8_t i = 0; i < getAudioCount(); i++) {
2255 if (audiod_fct_[i].ep_fb != 0) {
2256 enable_sof = true;
2257 break;
2258 }
2259 }
2260 backend().enableSof(rhport, enable_sof);
2261 }
2264
2265 return true;
2266 }
2267
2268 static bool isPowerOfTwo(uint32_t value) {
2269 return value != 0 && (value & (value - 1)) == 0;
2270 }
2271
2272 static uint8_t log2Floor(uint32_t value) {
2273 uint8_t result = 0;
2274 while ((value >>= 1u) != 0u) result++;
2275 return result;
2276 }
2277
2278 bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq,
2279 uint32_t mclk_freq) {
2280 // Check if frame interval is within sane limits
2281 // The interval value n_frames was taken from the descriptors within
2282
2283 // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s
2284 // / f_m) for high speed this lower limit ensures the measures feedback
2285 // value has sufficient precision
2286 uint32_t const k = backend().isFullSpeed() ? 10 : 13;
2287 uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
2288
2289 if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) {
2290 LOGE(" UAC2 feedback interval too small");
2291 return false;
2292 }
2293
2294 // Check if parameters really allow for a power of two division
2295 if ((mclk_freq % sample_freq) == 0 &&
2296 isPowerOfTwo(mclk_freq / sample_freq)) {
2297 audio->feedback.compute_method =
2299 audio->feedback.compute.power_of_2 =
2300 (uint8_t)(16 - (audio->feedback.frame_shift - 1) -
2301 log2Floor(mclk_freq / sample_freq));
2302 } else if (audio->feedback.compute_method ==
2305 (float)sample_freq / (float)mclk_freq *
2306 (1UL << (16 - (audio->feedback.frame_shift - 1)));
2307 } else {
2308 audio->feedback.compute.fixed.sample_freq = sample_freq;
2309 audio->feedback.compute.fixed.mclk_freq = mclk_freq;
2310 }
2311
2312 return true;
2313 }
2314
2316 if (audio->format_type_tx != AUDIO_FORMAT_TYPE_I) return false;
2317 if (!audio->n_channels_tx) return false;
2318 if (!audio->n_bytes_per_sample_tx) return false;
2319 if (!audio->interval_tx) return false;
2320 if (!audio->sample_rate_tx) return false;
2321
2322 // Restart the fractional accumulator for this streaming session.
2323 audio->tx_sample_acc = 0;
2324
2325 bool full_speed = backend().isFullSpeed();
2326 const uint8_t interval =
2327 full_speed ? audio->interval_tx : 1 << (audio->interval_tx - 1);
2328
2329 const uint16_t sample_normimal = (uint16_t)(audio->sample_rate_tx *
2330 interval / (full_speed ? 1000 : 8000));
2331 const uint16_t sample_reminder = (uint16_t)(audio->sample_rate_tx *
2332 interval % (full_speed ? 1000 : 8000));
2333
2334 const uint16_t packet_sz_tx_min =
2335 (uint16_t)((sample_normimal - 1) * audio->n_channels_tx *
2336 audio->n_bytes_per_sample_tx);
2337 const uint16_t packet_sz_tx_norm =
2338 (uint16_t)(sample_normimal * audio->n_channels_tx *
2339 audio->n_bytes_per_sample_tx);
2340 const uint16_t packet_sz_tx_max =
2341 (uint16_t)((sample_normimal + 1) * audio->n_channels_tx *
2342 audio->n_bytes_per_sample_tx);
2343
2344 // Endpoint size must larger than packet size
2345 if (packet_sz_tx_max > audio->ep_in_sz) return false;
2346
2347 // Frmt20.pdf 2.3.1.1 USB Packets
2348 if (sample_reminder) {
2349 // All virtual frame packets must either contain INT(nav) audio slots
2350 // (small VFP) or INT(nav)+1 (large VFP) audio slots
2351 audio->packet_sz_tx[0] = packet_sz_tx_norm;
2352 audio->packet_sz_tx[1] = packet_sz_tx_norm;
2353 audio->packet_sz_tx[2] = packet_sz_tx_max;
2354 } else {
2355 // In the case where nav = INT(nav), ni may vary between INT(nav)-1 (small
2356 // VFP), INT(nav) (medium VFP) and INT(nav)+1 (large VFP).
2357 audio->packet_sz_tx[0] = packet_sz_tx_min;
2358 audio->packet_sz_tx[1] = packet_sz_tx_norm;
2359 audio->packet_sz_tx[2] = packet_sz_tx_max;
2360 }
2361
2362 return true;
2363 }
2364
2365 // Number of audio bytes to transmit in the current (micro)frame when IN
2366 // flow control is enabled. A fractional accumulator distributes the
2367 // sub-frame sample remainder over successive frames so the long-term average
2368 // matches the configured sample rate (e.g. alternating 176/180 bytes for
2369 // 44100 Hz stereo 16-bit, averaging 176.4 bytes = 44.1 samples per frame).
2371 if (audio->sample_rate_tx == 0 || audio->n_channels_tx == 0 ||
2372 audio->n_bytes_per_sample_tx == 0) {
2373 // Not enough info to size precisely: fall back to the max packet.
2374 return audio->ep_in_sz;
2375 }
2376 bool full_speed = backend().isFullSpeed();
2377 const uint32_t denom = full_speed ? 1000u : 8000u;
2378 const uint8_t iv = audio->interval_tx ? audio->interval_tx : 1;
2379 const uint32_t interval = full_speed ? iv : (1u << (iv - 1));
2380
2381 audio->tx_sample_acc += audio->sample_rate_tx * interval;
2382 const uint32_t samples = audio->tx_sample_acc / denom;
2383 audio->tx_sample_acc -= samples * denom;
2384
2385 uint32_t bytes =
2386 samples * audio->n_channels_tx * audio->n_bytes_per_sample_tx;
2387 if (bytes > audio->ep_in_sz) bytes = audio->ep_in_sz;
2388 return (uint16_t)bytes;
2389 }
2390
2391};
2392
2393} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define USB_DESCR_MAX_LEN
Definition USBAudioDeviceBase.h:47
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:175
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:120
AudioInfo info
Definition BaseStream.h:171
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:128
Shared functionality of all buffers.
Definition Buffers.h:23
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:34
virtual void reset()=0
clears the buffer
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:56
virtual size_t size()=0
virtual int availableForWrite()=0
provides the number of entries that are available to write
virtual int available()=0
provides the number of entries that are available to read
USB Audio Class 2.0 descriptor generator.
Definition USBAudio2DescriptorBuilder.h:29
const uint16_t buildFullDescriptor(uint8_t *desc)
Definition USBAudio2DescriptorBuilder.h:53
static constexpr uint8_t ENTITY_FU2
second Feature Unit (RXTX)
Definition USBAudio2DescriptorBuilder.h:39
static constexpr uint8_t ENTITY_FU1
first Feature Unit
Definition USBAudio2DescriptorBuilder.h:36
int audioFunctionsCount() const
Definition USBAudio2DescriptorBuilder.h:144
static constexpr uint8_t ENTITY_CLOCK
Definition USBAudio2DescriptorBuilder.h:34
uint16_t calcPacketSizeForRate(uint32_t rate) const
Definition USBAudio2DescriptorBuilder.h:162
uint16_t calcMaxPacketSize() const
Definition USBAudio2DescriptorBuilder.h:174
bool enableFeedbackEp() const
Definition USBAudio2DescriptorBuilder.h:153
Abstract seam for every raw USB-stack call the UAC2 driver (USBAudioDeviceBase) needs....
Definition USBAudioBackend.h:160
virtual bool isoActivateEndpoint(uint8_t rhport, const UsbEndpointDescriptorView &ep)=0
virtual bool controlTransfer(uint8_t rhport, const UsbSetupPacket &request, uint8_t *buffer, uint16_t length)=0
Complete a GET/SET control transfer's data stage with buffer.
bool isFullSpeed() const
Convenience: speed() == UsbSpeed::Full.
Definition USBAudioBackend.h:218
virtual bool claimEndpoint(uint8_t rhport, uint8_t ep_addr)=0
virtual bool closeEndpoint(uint8_t rhport, uint8_t ep_addr)=0
virtual bool openEndpoint(uint8_t rhport, const UsbEndpointDescriptorView &ep)=0
virtual bool controlStatus(uint8_t rhport, const UsbSetupPacket &request)=0
Send a zero-length-packet status acknowledgement.
virtual bool usesIsoAlloc() const =0
virtual bool transfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t length)=0
bool isHighSpeed() const
Convenience: speed() == UsbSpeed::High.
Definition USBAudioBackend.h:220
virtual bool clearStall(uint8_t rhport, uint8_t ep_addr)=0
Clear a halted/stalled condition on an endpoint.
virtual bool mounted() const =0
True once the device has completed USB enumeration.
virtual void enableSof(uint8_t rhport, bool enable)=0
virtual bool isoAllocEndpoint(uint8_t rhport, uint8_t ep_addr, uint16_t max_packet_size)=0
USB Audio Device class for audio streaming over USB.
Definition USBAudioDeviceBase.h:109
std::function< void(float, uint8_t)> volume_cb_
Definition USBAudioDeviceBase.h:852
std::function< void(USBAudioDeviceBase *, uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param)> tud_audio_feedback_params_cb_
Definition USBAudioDeviceBase.h:898
bool begin(const USBAudioConfig &cfg)
Apply a config and start the USB audio device.
Definition USBAudioDeviceBase.h:310
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request)> tud_audio_set_itf_cb_
Definition USBAudioDeviceBase.h:879
void setRxDoneCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, audiod_function_t *, uint16_t)> cb)
Register a callback for RX done events.
Definition USBAudioDeviceBase.h:566
bool setVolume(float vol, uint8_t channel)
Set the volume for a channel and notify the host.
Definition USBAudioDeviceBase.h:427
volatile uint32_t xfer_cb_rx_count_
Definition USBAudioDeviceBase.h:840
volatile uint32_t xfer_cb_tx_count_
Definition USBAudioDeviceBase.h:838
USBAudioDeviceBase(USBAudioConfig cfg)
Constructor which provides configuration at construction time.
Definition USBAudioDeviceBase.h:237
static constexpr int16_t kVolumeMinDb256
Convert AudioTools volume (0.0–1.0) to UAC2 int16 (1/256 dB). 0.0 maps to 0x8000 (silence),...
Definition USBAudioDeviceBase.h:1030
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, UsbSetupPacket const &request)
Definition USBAudioDeviceBase.h:1393
std::vector< float > volume_
Definition USBAudioDeviceBase.h:850
bool handleInterfaceRequest(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1678
bool isEpInEnabled() const
Returns true if the IN endpoint is enabled.
Definition USBAudioDeviceBase.h:391
void audiod_sof_isr(uint8_t rhport, uint32_t frame_count)
Definition USBAudioDeviceBase.h:1546
bool audiod_fb_send(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:1949
bool audiod_set_interface(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:2222
void setVolumeCallback(std::function< void(float, uint8_t)> cb)
Register a callback invoked when the host (or device) changes the volume.
Definition USBAudioDeviceBase.h:460
int core
Definition USBAudioDeviceBase.h:845
bool isMute(uint8_t channel=0) const
Returns the current mute state for the given channel.
Definition USBAudioDeviceBase.h:439
void setReqEntityCallback(std::function< bool(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for entity requests.
Definition USBAudioDeviceBase.h:576
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *pBuff)> tud_audio_set_req_itf_cb_
Definition USBAudioDeviceBase.h:886
bool isFeatureUnit(uint8_t id) const
Returns true if the given entity ID is a Feature Unit (FU1 or FU2).
Definition USBAudioDeviceBase.h:1051
void setAudioFeedbackFormatCorrectionCallback(std::function< bool(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for audio feedback format correction events.
Definition USBAudioDeviceBase.h:650
uint16_t audiod_open(uint8_t rhport, UsbInterfaceDescriptorView const &itf_desc, uint8_t const *raw_desc, uint16_t max_len)
Definition USBAudioDeviceBase.h:1251
static int16_t floatToUac2(float vol)
Convert linear volume (0.0–1.0) to UAC2 int16 (1/256 dB). Linear mapping: 0.0 → -100 dB (min),...
Definition USBAudioDeviceBase.h:1034
uint32_t getTxSampleRate() const
TX sample rate parsed from the descriptor (must be non-zero for flow control).
Definition USBAudioDeviceBase.h:815
std::function< void(USBAudioDeviceBase *, uint8_t func_id)> fb_done_cb_
Definition USBAudioDeviceBase.h:875
bool mounted()
Returns true if the device is mounted by the USB host.
Definition USBAudioDeviceBase.h:513
void openEpIn(uint8_t rhport, audiod_function_t *audio, uint8_t itf, const UsbEndpointDescriptorView &desc_ep, uint8_t const *p_desc_for_params)
Definition USBAudioDeviceBase.h:2050
void closeEpOut(uint8_t rhport, audiod_function_t *audio, uint8_t itf, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:2002
void setGetReqEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &)> cb)
Register a callback for GET requests on an endpoint.
Definition USBAudioDeviceBase.h:529
void setTxDoneCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, audiod_function_t *, uint16_t)> cb)
Register a callback for TX done events.
Definition USBAudioDeviceBase.h:556
float volume(uint8_t channel)
Returns the current volume for the given channel.
Definition USBAudioDeviceBase.h:419
bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int)
Definition USBAudioDeviceBase.h:1913
static USBAudioDeviceBase * s_active_
Definition USBAudioDeviceBase.h:919
virtual USBAudioBackend & backend()=0
Returns the backend used for all raw USB-stack calls (TinyUSB today, or a future native-HAL backend)....
bool isStreamingActiveTx() const
Returns true if the host has opened the IN (capture) stream.
Definition USBAudioDeviceBase.h:492
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &)> get_req_ep_cb_
Definition USBAudioDeviceBase.h:872
int16_t manual_feedback_percent_
Definition USBAudioDeviceBase.h:908
uint16_t getDescLen(uint8_t fn) const
Definition USBAudioDeviceBase.h:1087
virtual BaseBuffer< uint8_t > & bufferTx()=0
Returns the TX audio buffer. Must be overridden by subclasses.
USBAudioConfig config_
Definition USBAudioDeviceBase.h:856
void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t const *p_desc)
Definition USBAudioDeviceBase.h:1827
void setIntDoneCallback(std::function< void(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for interrupt done events.
Definition USBAudioDeviceBase.h:547
void setConfig(const USBAudioConfig &cfg)
Set the USB audio configuration (use begin(cfg) instead).
Definition USBAudioDeviceBase.h:922
volatile uint32_t tx_fifo_read_total_
Definition USBAudioDeviceBase.h:839
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request)> tud_audio_set_itf_close_EP_cb_
Definition USBAudioDeviceBase.h:894
uint32_t getRxXferCount() const
Number of times audiod_xfer_cb fired for the OUT endpoint.
Definition USBAudioDeviceBase.h:797
void setItfCloseEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &)> cb)
Register a callback for interface close endpoint events.
Definition USBAudioDeviceBase.h:629
bool is_started_
Definition USBAudioDeviceBase.h:835
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, audiod_function_t *, uint16_t bytes)> tx_done_cb_
Definition USBAudioDeviceBase.h:861
void audiod_init(void)
Definition USBAudioDeviceBase.h:1201
volatile uint32_t rx_total_bytes_
Definition USBAudioDeviceBase.h:841
bool activateEndpoint(uint8_t rhport, const UsbEndpointDescriptorView &desc_ep, UsbDir dir=UsbDir::In)
Definition USBAudioDeviceBase.h:2034
bool usb_task_active_
Definition USBAudioDeviceBase.h:836
bool tx_xfer_armed_
Definition USBAudioDeviceBase.h:837
bool rx_primed_
Definition USBAudioDeviceBase.h:846
void setGetReqItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &)> cb)
Register a callback for GET requests on the interface.
Definition USBAudioDeviceBase.h:519
bool handleClockSourceGet(uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *cb)
Definition USBAudioDeviceBase.h:1567
volatile uint16_t tx_frame_bytes_last_
Definition USBAudioDeviceBase.h:843
uint32_t getRxTotalBytes() const
Total bytes received from host via OUT endpoint.
Definition USBAudioDeviceBase.h:799
bool begin()
(Re-)start the USB audio device with the current config.
Definition USBAudioDeviceBase.h:327
void setFeedbackPercent(int percent)
Overrides the automatic (FIFO_COUNT) feedback computation with a fixed value driven by the caller,...
Definition USBAudioDeviceBase.h:670
bool isStreamingActiveRx() const
Returns true if the host has opened the OUT (playback) stream.
Definition USBAudioDeviceBase.h:500
bool setVolume(float volume) override
sets the volume for the master channel (channel 0)
Definition USBAudioDeviceBase.h:414
bool handleEndpointRequest(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1693
uint8_t getAudioCount() const
Returns the number of audio functions (always 1).
Definition USBAudioDeviceBase.h:510
int available() override
Bytes of received audio waiting in the RX buffer.
Definition USBAudioDeviceBase.h:736
bool audiod_deinit(void)
Definition USBAudioDeviceBase.h:1233
void setFeedbackAutomatic()
Reverts setFeedbackPercent() and restores the automatic (FIFO_COUNT) feedback computation.
Definition USBAudioDeviceBase.h:679
bool is_active_
Definition USBAudioDeviceBase.h:848
std::function< void(bool, bool)> streaming_state_cb_
Definition USBAudioDeviceBase.h:855
void sendInterruptNotification(uint8_t ctrlSel, uint8_t channel, uint8_t entityID)
Send a UAC2 status/change notification via the AC interrupt EP.
Definition USBAudioDeviceBase.h:1063
float volume() override
gets the volume for the master channel (channel 0)
Definition USBAudioDeviceBase.h:411
static bool isValidBitsPerSample(uint8_t bps)
Definition USBAudioDeviceBase.h:1091
bool audiod_calc_tx_packet_sz(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:2315
std::function< bool(USBAudioDeviceBase *, uint8_t func_id)> req_entity_cb_
Definition USBAudioDeviceBase.h:876
void setMuteCallback(std::function< void(bool, uint8_t)> cb)
Register a callback invoked when the host (or device) changes the mute state.
Definition USBAudioDeviceBase.h:467
virtual BaseBuffer< uint8_t > & bufferRx()=0
Returns the RX audio buffer. Must be overridden by subclasses.
void openEpOut(uint8_t rhport, audiod_function_t *audio, uint8_t itf, const UsbEndpointDescriptorView &desc_ep)
Definition USBAudioDeviceBase.h:2071
void setFbDoneCallback(std::function< void(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for feedback done events.
Definition USBAudioDeviceBase.h:539
volatile uint32_t rx_dropped_bytes_
Definition USBAudioDeviceBase.h:842
void setTudAudioSetItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &)> cb)
Register a callback for interface set requests.
Definition USBAudioDeviceBase.h:585
std::function< void(uint32_t)> sample_rate_cb_
Definition USBAudioDeviceBase.h:854
bool audiod_control_complete(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1404
USBAudioDeviceBase()
Default Constructor.
Definition USBAudioDeviceBase.h:234
int availableForWrite() override
Bytes of free space in the TX buffer.
Definition USBAudioDeviceBase.h:742
void setReqEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &, uint8_t *)> cb)
Register a callback for endpoint set requests.
Definition USBAudioDeviceBase.h:618
bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1743
void processVolume(uint8_t *data, size_t len)
Process audio data for volume control.
Definition USBAudioDeviceBase.h:952
audio_feedback_method_t
Methods for USB audio feedback endpoint operation.
Definition USBAudioDeviceBase.h:128
@ AUDIO_FEEDBACK_METHOD_FIFO_COUNT
Definition USBAudioDeviceBase.h:133
@ AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED
Definition USBAudioDeviceBase.h:130
@ AUDIO_FEEDBACK_METHOD_DISABLED
Definition USBAudioDeviceBase.h:129
@ AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2
Definition USBAudioDeviceBase.h:132
@ AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT
Definition USBAudioDeviceBase.h:131
virtual bool beginUSB()=0
Override in platform subclasses to register descriptors and start the USB host-controller stack (e....
uint32_t getRxDroppedBytes() const
Definition USBAudioDeviceBase.h:803
uint16_t getDescriptor(uint8_t *desc)
Returns the audio-function descriptor block for use in tud_descriptor_configuration_cb().
Definition USBAudioDeviceBase.h:776
static constexpr size_t getResetSize()
Definition USBAudioDeviceBase.h:1105
void setSampleRate(uint32_t rate)
Device-initiated sample rate change.
Definition USBAudioDeviceBase.h:1001
void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t, uint8_t frame_shift)
Definition USBAudioDeviceBase.h:1112
std::function< void(bool, uint8_t)> mute_cb_
Definition USBAudioDeviceBase.h:853
void setReqEntityCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &, uint8_t *)> cb)
Register a callback for entity set requests.
Definition USBAudioDeviceBase.h:596
static float uac2ToFloat(int16_t v)
Convert UAC2 int16 (1/256 dB) to linear volume (0.0–1.0). Linear mapping within the -100....
Definition USBAudioDeviceBase.h:1042
void audiod_reset(uint8_t rhport)
Definition USBAudioDeviceBase.h:1237
std::function< bool(USBAudioDeviceBase *, uint8_t func_id)> tud_audio_feedback_format_correction_cb_
Definition USBAudioDeviceBase.h:901
bool openEndpointsForAltSetting(uint8_t rhport, audiod_function_t *audio, uint8_t func_id, uint8_t itf, uint8_t alt)
Definition USBAudioDeviceBase.h:2153
bool isInterruptEpEnabled() const
Returns true if the interrupt endpoint is enabled.
Definition USBAudioDeviceBase.h:507
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *pBuff)> tud_audio_set_req_entity_cb_
Definition USBAudioDeviceBase.h:882
uint32_t getTxFifoReadTotal() const
Definition USBAudioDeviceBase.h:806
uint8_t numInterfaces() const
Total number of USB interfaces claimed by the audio function (1 AC + 1 or 2 AS), for use in the bNumI...
Definition USBAudioDeviceBase.h:786
std::vector< uint16_t > desc_len_
Definition USBAudioDeviceBase.h:911
virtual void resizeBuffers()=0
Resize the platform audio buffers. Both platforms use NBuffer-style block pools: block size = max USB...
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, audiod_function_t *, uint16_t xferred_bytes)> rx_done_cb_
Definition USBAudioDeviceBase.h:864
bool isEpInFlowControlEnabled() const
Returns true if IN endpoint flow control is enabled. When on, the per-frame isochronous packet size i...
Definition USBAudioDeviceBase.h:404
void end()
Stop audio streaming and clear buffers. Does not disconnect USB.
Definition USBAudioDeviceBase.h:751
USBAudioConfig active_config_
Definition USBAudioDeviceBase.h:857
bool isTxXferArmed() const
True if the initial isochronous IN transfer was armed successfully.
Definition USBAudioDeviceBase.h:792
bool audiod_control_request(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1708
void setSampleRateCallback(std::function< void(uint32_t)> cb)
Register a callback invoked when the host (or device) changes the sample rate.
Definition USBAudioDeviceBase.h:474
uint32_t getTxXferCount() const
Number of times audiod_xfer_cb fired for the IN endpoint.
Definition USBAudioDeviceBase.h:795
USBAudioConfig defaultConfig(RxTxMode mode=RXTX_MODE)
Returns a default configuration pre-filled for the requested direction (RX_MODE, TX_MODE,...
Definition USBAudioDeviceBase.h:246
bool audiod_get_interface(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1929
void setupFeedback(audiod_function_t *audio, uint8_t func_id, uint8_t alt)
Definition USBAudioDeviceBase.h:2095
void closeEpIn(uint8_t rhport, audiod_function_t *audio, uint8_t itf, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1985
virtual int getActualCore() const
Returns the actual core on which the call is running (for RP2040)
Definition USBAudioDeviceBase.h:832
static USBAudioDeviceBase & activeInstance()
Returns the most-recently-constructed instance (base or subclass).
Definition USBAudioDeviceBase.h:388
bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t *audio, uint8_t *idxItf, uint8_t const **pp_desc_int)
Definition USBAudioDeviceBase.h:1873
uint8_t getTxInterval() const
TX isochronous interval (bInterval) parsed from the descriptor.
Definition USBAudioDeviceBase.h:827
uint16_t audiod_tx_packet_size_fc(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:2370
void setAudioInfo(AudioInfo info) override
Change the sample rate and notify the host.
Definition USBAudioDeviceBase.h:268
bool handleEntityRequest(uint8_t rhport, UsbSetupPacket const &p_request, uint8_t entityID)
Definition USBAudioDeviceBase.h:1644
volatile uint32_t tx_xferred_last_
Definition USBAudioDeviceBase.h:844
std::vector< uint16_t > ctrl_buf_sz_
Definition USBAudioDeviceBase.h:913
uint16_t getTxFrameBytesLast() const
Definition USBAudioDeviceBase.h:810
std::function< void(USBAudioDeviceBase *, uint8_t rhport)> int_done_cb_
Definition USBAudioDeviceBase.h:859
uint16_t packetSize() const
Definition USBAudioDeviceBase.h:1101
bool isFeedbackEpEnabled() const
Returns true if the feedback endpoint is enabled. Only meaningful in pure RX (OUT-only) mode: with an...
Definition USBAudioDeviceBase.h:399
float getVolumeExt(uint8_t channel) const
Returns the effective volume for a per-channel index (1-based). Combines master volume (index 0) with...
Definition USBAudioDeviceBase.h:974
void setReqItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &, uint8_t *)> cb)
Register a callback for interface set requests.
Definition USBAudioDeviceBase.h:607
void openEpFeedback(audiod_function_t *audio, const UsbEndpointDescriptorView &desc_ep)
Definition USBAudioDeviceBase.h:2088
std::vector< bool > mute_
Definition USBAudioDeviceBase.h:851
audio_format_type_t
Supported USB audio format types.
Definition USBAudioDeviceBase.h:116
@ AUDIO_FORMAT_TYPE_III
Definition USBAudioDeviceBase.h:119
@ AUDIO_FORMAT_TYPE_II
Definition USBAudioDeviceBase.h:118
@ AUDIO_FORMAT_TYPE_I
Definition USBAudioDeviceBase.h:117
uint8_t int_notify_buf_[6]
Definition USBAudioDeviceBase.h:902
bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1803
bool isStreamingActive() const
Returns true if either IN or OUT streaming endpoint is open.
Definition USBAudioDeviceBase.h:487
void setAudioFeedbackParamsCallback(std::function< void(USBAudioDeviceBase *, uint8_t, uint8_t, audio_feedback_params_t *)> cb)
Register a callback for audio feedback parameter events.
Definition USBAudioDeviceBase.h:639
void setStreamingStateCallback(std::function< void(bool, bool)> cb)
Register a callback invoked when the streaming state changes. Fires when the host opens or closes a s...
Definition USBAudioDeviceBase.h:482
bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq)
Definition USBAudioDeviceBase.h:2278
bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1776
void notifyStreamingState()
Definition USBAudioDeviceBase.h:1095
static uint8_t log2Floor(uint32_t value)
Definition USBAudioDeviceBase.h:2272
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &)> get_req_itf_cb_
Definition USBAudioDeviceBase.h:868
std::vector< audiod_function_t > audiod_fct_
Definition USBAudioDeviceBase.h:915
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *pBuff)> tud_audio_set_req_ep_cb_
Definition USBAudioDeviceBase.h:890
uint16_t getCtrlBufSz(uint8_t fn) const
Definition USBAudioDeviceBase.h:1082
uint32_t getTxXferredLast() const
Definition USBAudioDeviceBase.h:813
uint8_t getTxBytesPerSample() const
TX bytes per sample parsed from the descriptor.
Definition USBAudioDeviceBase.h:823
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:1480
bool isEpOutEnabled() const
Returns true if the OUT endpoint is enabled.
Definition USBAudioDeviceBase.h:394
bool configChanged(const USBAudioConfig &n)
Definition USBAudioDeviceBase.h:1079
size_t readBytes(uint8_t *buffer, size_t bufsize)
Receive audio data from the host (host → device, speaker/playback). Until bufferRx() has filled past ...
Definition USBAudioDeviceBase.h:715
static bool isPowerOfTwo(uint32_t value)
Definition USBAudioDeviceBase.h:2268
virtual void serviceUSB()=0
Process pending USB events on platforms where the application drives the stack (RP2040,...
void processVolume(T *data, size_t sample_count)
Definition USBAudioDeviceBase.h:984
bool setMute(bool m, uint8_t channel=0)
Set the mute state for a channel and notify the host.
Definition USBAudioDeviceBase.h:447
uint8_t getTxChannels() const
TX channel count parsed from the descriptor.
Definition USBAudioDeviceBase.h:819
uint16_t audioPacketSize() const
One isochronous USB packet size in bytes (same formula as the descriptor builder).
Definition USBAudioDeviceBase.h:763
bool handleFeatureUnitGet(uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *cb)
Definition USBAudioDeviceBase.h:1614
size_t write(const uint8_t *data, size_t len)
Send audio data to the host (device → host, microphone/capture). Silently discards data when the host...
Definition USBAudioDeviceBase.h:684
USBAudio2DescriptorBuilder descr_builder
Definition USBAudioDeviceBase.h:858
Supports the setting and getting of the volume.
Definition AudioTypes.h:188
24bit integer which is used for I2S sound processing. The values are really using 3 bytes....
Definition int24_3bytes_t.h:21
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:26
@ RXTX_MODE
Definition AudioTypes.h:26
@ TX_MODE
Definition AudioTypes.h:26
@ RX_MODE
Definition AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
static constexpr uint8_t kAudioSubclassControl
Definition USBAudioDeviceBase.h:64
uint8_t descSubtype(uint8_t const *p)
Definition USBAudioBackend.h:107
static constexpr uint8_t kAudioCsCtrlSamFreq
Definition USBAudioDeviceBase.h:77
uint32_t descU32(uint8_t const *p)
Definition USBAudioBackend.h:113
static constexpr uint16_t kAudioDescIadLen
Definition USBAudioDeviceBase.h:85
UsbDir
USB transfer direction, mirrors TinyUSB's TUSB_DIR_OUT/TUSB_DIR_IN (0/1).
Definition USBAudioBackend.h:8
static constexpr uint8_t kUsbClassAudio
Definition USBAudioDeviceBase.h:63
static constexpr uint8_t kAudioIntProtocolCodeV2
Definition USBAudioDeviceBase.h:65
UsbEndpointDescriptorView decodeEndpoint(uint8_t const *p)
Definition USBAudioBackend.h:121
static constexpr uint8_t kAudioCsReqCur
Definition USBAudioDeviceBase.h:74
UsbInterfaceDescriptorView decodeInterface(uint8_t const *p)
Definition USBAudioBackend.h:136
static constexpr uint8_t kAudioCsAcOutputTerminal
Definition USBAudioDeviceBase.h:69
UsbXferResult
Definition USBAudioBackend.h:24
static constexpr uint8_t kControlStageData
Definition USBAudioDeviceBase.h:90
static constexpr uint8_t AUDIO_FU_CTRL_VOLUME
Definition USBAudioDeviceBase.h:81
static uint8_t u16Low(uint16_t v)
Definition USBAudioDeviceBase.h:92
static constexpr uint8_t kUsbDescTypeEndpoint
Definition USBAudioDeviceBase.h:59
uint16_t descU16(uint8_t const *p)
Definition USBAudioBackend.h:109
uint8_t descType(uint8_t const *p)
Definition USBAudioBackend.h:106
static uint8_t u16High(uint16_t v)
Definition USBAudioDeviceBase.h:93
uint8_t const * descNext(uint8_t const *p)
Definition USBAudioBackend.h:105
static constexpr uint8_t kAudioCsCtrlClkValid
Definition USBAudioDeviceBase.h:78
static constexpr uint8_t AUDIO_FU_CTRL_MUTE
Definition USBAudioDeviceBase.h:80
static constexpr uint16_t kAudioTermTypeUsbStreaming
Definition USBAudioDeviceBase.h:83
static constexpr uint8_t kAudioCsAcInputTerminal
Definition USBAudioDeviceBase.h:68
uint32_t highestSupportedSampleRate(const USBAudioConfig &cfg)
Definition USBAudioConfig.h:285
static constexpr uint8_t kUsbDescTypeCsInterface
Definition USBAudioDeviceBase.h:60
static constexpr uint8_t kUsbDescTypeInterface
Definition USBAudioDeviceBase.h:58
static constexpr uint8_t kAudioCsAsGeneral
Definition USBAudioDeviceBase.h:71
static constexpr uint8_t kAudioCsAsFormatType
Definition USBAudioDeviceBase.h:72
static constexpr uint8_t kControlStageSetup
Definition USBAudioDeviceBase.h:89
static constexpr uint8_t kAudioCsReqRange
Definition USBAudioDeviceBase.h:75
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:51
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:57
Configuration for USB Audio (inherits sample_rate / channels / bits_per_sample from AudioInfo).
Definition USBAudioConfig.h:117
bool enable_interrupt_ep
Definition USBAudioConfig.h:197
bool enable_multi_sample_rate
Definition USBAudioConfig.h:177
std::vector< uint32_t > supported_sample_rates
Definition USBAudioConfig.h:191
uint8_t rx_start_fill_percent
Definition USBAudioConfig.h:155
bool enable_ep_in
device → host (capture / microphone)
Definition USBAudioConfig.h:120
uint8_t itf_num_ac
Definition USBAudioConfig.h:140
bool enable_ep_in_flow_control
Definition USBAudioConfig.h:204
bool volume_active
Definition USBAudioConfig.h:230
bool enable_ep_out
host → device (playback / speaker)
Definition USBAudioConfig.h:121
Parameters for audio feedback endpoint configuration.
Definition USBAudioDeviceBase.h:221
uint8_t method
Definition USBAudioDeviceBase.h:222
struct audio_tools::USBAudioDeviceBase::audio_feedback_params_t::@6::@8 frequency
uint32_t mclk_freq
Definition USBAudioDeviceBase.h:227
uint32_t sample_freq
Definition USBAudioDeviceBase.h:223
Internal structure representing a USB audio function instance.
Definition USBAudioDeviceBase.h:142
uint8_t compute_method
Definition USBAudioDeviceBase.h:173
struct audio_tools::USBAudioDeviceBase::audiod_function_t::@2::@3::@4 fixed
uint8_t ep_fb
Definition USBAudioDeviceBase.h:164
bool mounted
Definition USBAudioDeviceBase.h:166
struct audio_tools::USBAudioDeviceBase::audiod_function_t::@2 feedback
uint8_t ep_out_as_intf_num
Definition USBAudioDeviceBase.h:163
uint16_t rate_const[2]
Definition USBAudioDeviceBase.h:186
uint8_t ep_in_as_intf_num
Definition USBAudioDeviceBase.h:159
uint32_t tx_sample_acc
Definition USBAudioDeviceBase.h:200
std::vector< uint8_t > ctrl_buf
Definition USBAudioDeviceBase.h:208
uint32_t fifo_lvl_avg
Definition USBAudioDeviceBase.h:184
uint8_t interval_tx
Definition USBAudioDeviceBase.h:193
float float_const
Definition USBAudioDeviceBase.h:177
uint16_t ep_in_sz
Definition USBAudioDeviceBase.h:157
uint16_t ep_out_sz
Definition USBAudioDeviceBase.h:161
uint8_t bclock_id_tx
Definition USBAudioDeviceBase.h:192
uint8_t ctrl_buf_sz
Definition USBAudioDeviceBase.h:207
std::vector< uint32_t > fb_buf
Definition USBAudioDeviceBase.h:212
uint16_t fifo_lvl_thr
Definition USBAudioDeviceBase.h:185
uint8_t n_bytes_per_sample_tx
Definition USBAudioDeviceBase.h:196
struct audio_tools::USBAudioDeviceBase::audiod_function_t::@2::@3::@5 fifo_count
uint8_t ep_int
Definition USBAudioDeviceBase.h:165
uint16_t desc_length
Definition USBAudioDeviceBase.h:167
uint32_t max_value
Definition USBAudioDeviceBase.h:171
std::vector< uint8_t > lin_buf_in
Definition USBAudioDeviceBase.h:211
uint32_t min_value
Definition USBAudioDeviceBase.h:170
uint32_t sample_rate_tx
Definition USBAudioDeviceBase.h:190
bool format_correction
Definition USBAudioDeviceBase.h:174
audio_format_type_t format_type_tx
Definition USBAudioDeviceBase.h:194
uint8_t frame_shift
Definition USBAudioDeviceBase.h:172
uint32_t nom_value
Definition USBAudioDeviceBase.h:183
uint32_t mclk_freq
Definition USBAudioDeviceBase.h:180
std::vector< uint8_t > lin_buf_out
Definition USBAudioDeviceBase.h:210
union audio_tools::USBAudioDeviceBase::audiod_function_t::@2::@3 compute
uint8_t power_of_2
Definition USBAudioDeviceBase.h:176
volatile uint8_t ep_out
Definition USBAudioDeviceBase.h:160
uint8_t rhport
Definition USBAudioDeviceBase.h:143
uint8_t n_channels_tx
Definition USBAudioDeviceBase.h:195
uint32_t sample_freq
Definition USBAudioDeviceBase.h:179
uint8_t const * p_desc
Definition USBAudioDeviceBase.h:144
uint16_t packet_sz_tx[3]
Definition USBAudioDeviceBase.h:191
uint32_t value
Definition USBAudioDeviceBase.h:169
UsbEndpointDescriptorView ep_out_view
Definition USBAudioDeviceBase.h:205
std::vector< uint8_t > alt_setting
Definition USBAudioDeviceBase.h:209
volatile uint8_t ep_in
Definition USBAudioDeviceBase.h:156
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
UsbDir direction() const
Definition USBAudioBackend.h:94
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 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
Recipient recipient() const
Definition USBAudioBackend.h:51
uint16_t wValue
Definition USBAudioBackend.h:37
UsbDir direction() const
Definition USBAudioBackend.h:50
uint16_t wIndex
Definition USBAudioBackend.h:38
Type type() const
Definition USBAudioBackend.h:49