arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioDevice.h
1#pragma once
2#include <cstddef>
3#include <cstdint>
4#include <cstring>
5#include <functional>
6#include <mutex>
7#include <vector>
8
9#ifdef ESP32
10#ifndef ARDUINO_USB_MODE
11#error This Microcontroller has no Native USB interface
12#else
13#if ARDUINO_USB_MODE == 1
14#error This sketch should be used when USB is in OTG mode
15#endif
16#endif
17#else
18#ifndef USE_TINYUSB
19#error This Microcontroller has no Native USB interface
20#endif
21#endif
22
23#include "USBAudio2DescriptorBuilder.h"
24#include "USBAudioConfig.h"
25
26extern "C" {
27#include "device/usbd.h"
28#include "device/usbd_pvt.h"
29#include "tusb.h"
30}
31
32namespace audio_tools {
33
49 enum audio_format_type_t {
50 AUDIO_FORMAT_TYPE_I,
51 AUDIO_FORMAT_TYPE_II,
52 AUDIO_FORMAT_TYPE_III,
53 };
54
61 enum audio_feedback_method_t {
62 AUDIO_FEEDBACK_METHOD_DISABLED,
63 AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED,
64 AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT,
65 AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, // For driver internal use only
66 AUDIO_FEEDBACK_METHOD_FIFO_COUNT
67 };
68
75 struct audiod_function_t {
76 uint8_t rhport;
77 uint8_t const* p_desc; // Pointer to Standard AC Interface Descriptor
78 uint8_t ep_in; // TX audio data EP.
79 uint16_t ep_in_sz; // Current size of TX EP
80 uint8_t
81 ep_in_as_intf_num; // Standard AS Interface Descriptor number for IN
82 uint8_t ep_out; // RX audio data EP.
83 uint16_t ep_out_sz; // Current size of RX EP
84 uint8_t
85 ep_out_as_intf_num; // Standard AS Interface Descriptor number for OUT
86 uint8_t ep_fb; // Feedback EP.
87 uint8_t ep_int; // Audio control interrupt EP.
88 bool mounted; // Device opened
89 uint16_t desc_length; // Length of audio function descriptor
90 struct {
91 uint32_t value;
92 uint32_t min_value;
93 uint32_t max_value;
94 uint8_t frame_shift;
95 uint8_t compute_method;
96 bool format_correction;
97 union {
98 uint8_t power_of_2;
99 float float_const;
100 struct {
101 uint32_t sample_freq;
102 uint32_t mclk_freq;
103 } fixed;
104 struct {
105 uint32_t nom_value;
106 uint32_t fifo_lvl_avg;
107 uint16_t fifo_lvl_thr;
108 uint16_t rate_const[2];
109 } fifo_count;
110 } compute;
111 } feedback;
112 uint32_t sample_rate_tx;
113 uint16_t packet_sz_tx[3];
114 uint8_t bclock_id_tx;
115 uint8_t interval_tx;
116 audio_format_type_t format_type_tx;
117 uint8_t n_channels_tx;
118 uint8_t n_bytes_per_sample_tx;
119 // From this point, data is not cleared by bus reset
120 uint8_t ctrl_buf_sz;
121 tu_fifo_t ep_out_ff;
122 tu_fifo_t ep_in_ff;
123 std::vector<uint8_t> ctrl_buf;
124 std::vector<uint8_t> alt_setting;
125 std::vector<uint8_t> lin_buf_out;
126 std::vector<uint8_t> lin_buf_in;
127 std::vector<uint32_t> fb_buf;
128 std::vector<uint8_t> ep_in_sw_buf; // For feedback EP
129 std::vector<uint8_t> ep_out_sw_buf; // For feedback EP
130 };
131
138 struct audio_feedback_params_t {
139 uint8_t method;
140 uint32_t sample_freq; // sample frequency in Hz
141
142 union {
143 struct {
144 uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to
145 // which sample clock is based on
146 } frequency;
147 };
148 };
149
150 public:
151
152 USBAudioConfig defaultConfig(RxTxMode mode = RXTX_MODE) {
153 USBAudioConfig cfg;
154 switch(mode){
155 case RX_MODE:
156 cfg.enable_ep_out = true;
157 cfg.enable_ep_in = false;
158 break;
159 case TX_MODE:
160 cfg.enable_ep_out = false;
161 cfg.enable_ep_in = true;
162 break;
163 case RXTX_MODE:
164 cfg.enable_ep_out = true;
165 cfg.enable_ep_in = true;
166 break;
167 default:
168 break;
169 }
170 return cfg;
171 }
172
173 bool begin(USBAudioConfig& cfg) {
174 config_ = cfg;
175 return begin();
176 }
177
178 bool begin() {
179 return true;
180 }
185 void setConfig(USBAudioConfig& cfg) { config_ = cfg; }
186
188 bool getEnableEpIn() const { return config_.enable_ep_in; }
190 bool getEnableEpOut() const { return config_.enable_ep_out; }
192 bool getEnableFeedbackEp() const { return config_.enable_feedback_ep; }
195 return config_.enable_ep_in_flow_control;
196 }
198 bool getEnableInterruptEp() const { return config_.enable_interrupt_ep; }
200 bool getEnableFifoMutex() const { return config_.enable_fifo_mutex; }
201
203 uint8_t getAudioCount() const { return config_.audio_count; }
204
213 const uint8_t* getAudioDescriptors(uint8_t itf, uint8_t alt,
214 uint16_t* out_length) {
215 // // Example: simple UAC1 streaming interface with one IN and one OUT
216 // endpoint static const uint8_t desc[] = {
217 // // Interface Association Descriptor (IAD)
218 // 0x08, 0x0B, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00,
219 // // Standard AC Interface Descriptor
220 // 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
221 // // Class-specific AC Interface Descriptor
222 // 0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01,
223 // // Standard AS Interface Descriptor (alt 0 - zero bandwidth)
224 // 0x09, 0x04, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
225 // // Standard AS Interface Descriptor (alt 1 - operational, IN)
226 // 0x09, 0x04, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00,
227 // // Class-specific AS Interface Descriptor (IN)
228 // 0x07, 0x24, 0x01, 0x01, 0x01, 0x01, 0x00,
229 // // Type I Format Type Descriptor (IN)
230 // 0x0B, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01, 0x44, 0xAC, 0x00,
231 // // Standard AS Isochronous Audio Data Endpoint Descriptor (IN)
232 // 0x07, 0x05, 0x81, 0x01, 0x40, 0x00, 0x01,
233 // // Class-specific AS Isochronous Audio Data Endpoint Descriptor (IN)
234 // 0x07, 0x25, 0x01, 0x00, 0x00, 0x00, 0x00,
235 // // Standard AS Interface Descriptor (alt 2 - operational, OUT)
236 // 0x09, 0x04, 0x02, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00,
237 // // Class-specific AS Interface Descriptor (OUT)
238 // 0x07, 0x24, 0x01, 0x01, 0x01, 0x01, 0x00,
239 // // Type I Format Type Descriptor (OUT)
240 // 0x0B, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01, 0x44, 0xAC, 0x00,
241 // // Standard AS Isochronous Audio Data Endpoint Descriptor (OUT)
242 // 0x07, 0x05, 0x01, 0x01, 0x40, 0x00, 0x01,
243 // // Class-specific AS Isochronous Audio Data Endpoint Descriptor (OUT)
244 // 0x07, 0x25, 0x01, 0x00, 0x00, 0x00, 0x00};
245 // if (out_length) *out_length = sizeof(desc);
246 // return desc;
247 return descr_builder.buildDescriptor(itf, alt, out_length);
248 }
249
251 bool mounted() const { return tud_mounted(); }
252
260 bool handleControlRequest(const tusb_control_request_t* request, void* buffer,
261 uint16_t length) {
262 // Example: handle standard GET_DESCRIPTOR request for audio interface
263 if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
264 request->bRequest == TUSB_REQ_GET_DESCRIPTOR) {
265 uint16_t desc_len;
266
267 uint8_t const itf = tu_u16_low(request->wIndex);
268 uint8_t const alt = tu_u16_low(request->wValue);
269
270 const uint8_t* desc = getAudioDescriptors(itf, alt, &desc_len);
271 if (desc && buffer && length >= desc_len) {
272 std::memcpy(buffer, desc, desc_len);
273 return true;
274 }
275 }
276 // TODO: handle class-specific requests (mute, volume, etc.)
277 return false;
278 }
279
286 void process() {
287 // Example: check if host is ready for IN, send audio packet
288 if (tud_ready()) {
289 uint8_t audio_data[48] = {0};
290 uint16_t bytes_written = 48;
291 if (tx_callback_)
292 bytes_written = tx_callback_(audio_data, sizeof(audio_data));
293 write(audio_data, bytes_written);
294 }
295 // Example: check for OUT data
296 if (tud_ready()) {
297 uint8_t buf[48];
298 uint16_t n = read(buf, sizeof(buf));
299 if (n > 0 && rx_callback_) rx_callback_(buf, n);
300 }
301 }
302
307 void setRxCallback(std::function<void(const uint8_t*, uint16_t)> cb) {
308 rx_callback_ = cb;
309 }
310
316 void setTxCallback(std::function<uint16_t(const uint8_t*, uint16_t)> cb) {
317 tx_callback_ = cb;
318 }
319
324 void setGetReqItfCallback(std::function<bool(USBAudioDevice*, uint8_t,
325 tusb_control_request_t const*)>
326 cb) {
327 get_req_itf_cb_ = cb;
328 }
329
335 std::function<bool(USBAudioDevice*, uint8_t,
336 tusb_control_request_t const*)>
337 cb) {
338 get_req_entity_cb_ = cb;
339 }
340
345 void setGetReqEpCallback(std::function<bool(USBAudioDevice*, uint8_t,
346 tusb_control_request_t const*)>
347 cb) {
348 get_req_ep_cb_ = cb;
349 }
350
355 void setFbDoneCallback(std::function<void(USBAudioDevice*, uint8_t)> cb) {
356 fb_done_cb_ = cb;
357 }
358
363 void setIntDoneCallback(std::function<void(USBAudioDevice*, uint8_t)> cb) {
364 int_done_cb_ = cb;
365 }
366
372 std::function<bool(USBAudioDevice*, uint8_t, audiod_function_t*)> cb) {
373 tx_done_cb_ = cb;
374 }
375
380 void setRxDoneCallback(std::function<bool(USBAudioDevice*, uint8_t,
381 audiod_function_t*, uint16_t)>
382 cb) {
383 rx_done_cb_ = cb;
384 }
385
390 void setReqEntityCallback(std::function<bool(USBAudioDevice*, uint8_t)> cb) {
391 req_entity_cb_ = cb;
392 }
393
399 std::function<bool(USBAudioDevice*, uint8_t,
400 tusb_control_request_t const*)>
401 cb) {
402 tud_audio_set_itf_cb_ = cb;
403 }
404
410 std::function<bool(USBAudioDevice*, uint8_t,
411 tusb_control_request_t const*, uint8_t*)>
412 cb) {
413 tud_audio_set_req_entity_cb_ = cb;
414 }
415
421 std::function<bool(USBAudioDevice*, uint8_t,
422 tusb_control_request_t const*, uint8_t*)>
423 cb) {
424 tud_audio_set_req_itf_cb_ = cb;
425 }
426
432 std::function<bool(USBAudioDevice*, uint8_t,
433 tusb_control_request_t const*, uint8_t*)>
434 cb) {
435 tud_audio_set_req_ep_cb_ = cb;
436 }
437
442 void setItfCloseEpCallback(std::function<bool(USBAudioDevice*, uint8_t,
443 tusb_control_request_t const*)>
444 cb) {
445 tud_audio_set_itf_close_EP_cb_ = cb;
446 }
447
453 std::function<bool(USBAudioDevice*, uint8_t, audiod_function_t*)> cb) {
454 audiod_tx_done_cb_ = cb;
455 }
456
462 std::function<void(USBAudioDevice*, uint8_t, uint8_t,
463 audio_feedback_params_t*)>
464 cb) {
465 tud_audio_feedback_params_cb_ = cb;
466 }
467
473 std::function<bool(USBAudioDevice*, uint8_t)> cb) {
474 tud_audio_feedback_format_correction_cb_ = cb;
475 }
476
482 static USBAudioDevice instance_;
483 return instance_;
484 }
485
491 usbd_class_driver_t const* usbd_app_driver_get(uint8_t* count) {
492 static usbd_class_driver_t driver;
493 driver.name = "Audio";
494 driver.init = [](void) { USBAudioDevice::instance().audiod_init(); };
495 driver.deinit = [](void) {
496 return USBAudioDevice::instance().audiod_deinit();
497 };
498 driver.reset = [](uint8_t rhport) {
499 USBAudioDevice::instance().audiod_reset(rhport);
500 };
501 driver.open = [](uint8_t rhport, tusb_desc_interface_t const* itf_desc,
502 uint16_t max_len) {
503 return USBAudioDevice::instance().audiod_open(rhport, itf_desc, max_len);
504 };
505 driver.control_xfer_cb = [](uint8_t rhport, uint8_t stage,
506 tusb_control_request_t const* request) {
507 return USBAudioDevice::instance().audiod_control_xfer_cb(rhport, stage,
508 request);
509 };
510 driver.xfer_cb = [](uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
511 uint32_t xferred_bytes) {
512 return USBAudioDevice::instance().audiod_xfer_cb(rhport, ep_addr, result,
513 xferred_bytes);
514 };
515 driver.sof = [](uint8_t rhport, uint32_t frame_count) {
516 USBAudioDevice::instance().audiod_sof_isr(rhport, frame_count);
517 };
518 *count = 1;
519 return &driver;
520 }
521
522 protected:
523 // Only one set of member variables and methods retained
524 USBAudioConfig config_;
525 USBAudio2DescriptorBuilder descr_builder{config_};
526 std::function<void(const uint8_t*, uint16_t)> rx_callback_;
527 std::function<uint16_t(const uint8_t*, uint16_t)> tx_callback_;
528 std::function<void(USBAudioDevice*, uint8_t rhport)> int_done_cb_;
529 std::function<bool(USBAudioDevice*, uint8_t rhport, audiod_function_t*)>
530 tx_done_cb_;
531 std::function<bool(USBAudioDevice*, uint8_t rhport, audiod_function_t*,
532 uint16_t xferred_bytes)>
533 rx_done_cb_;
534 // Callback for interface GET requests
535 std::function<bool(USBAudioDevice*, uint8_t rhport,
536 tusb_control_request_t const*)>
537 get_req_itf_cb_;
538 // Callback for entity GET requests
539 std::function<bool(USBAudioDevice*, uint8_t rhport,
540 tusb_control_request_t const*)>
541 get_req_entity_cb_;
542 // Callback for endpoint GET requests
543 std::function<bool(USBAudioDevice*, uint8_t rhport,
544 tusb_control_request_t const*)>
545 get_req_ep_cb_;
546
547 // Callback for feedback done event
548 std::function<void(USBAudioDevice*, uint8_t func_id)> fb_done_cb_;
549 std::function<bool(USBAudioDevice*, uint8_t func_id)> req_entity_cb_;
550 std::function<bool(USBAudioDevice*, uint8_t rhport,
551 tusb_control_request_t const* p_request)>
552 tud_audio_set_itf_cb_;
553 std::function<bool(USBAudioDevice*, uint8_t rhport,
554 tusb_control_request_t const* p_request, uint8_t* pBuff)>
555 tud_audio_set_req_entity_cb_;
556
557 std::function<bool(USBAudioDevice*, uint8_t rhport,
558 tusb_control_request_t const* p_request, uint8_t* pBuff)>
559 tud_audio_set_req_itf_cb_;
560
561 std::function<bool(USBAudioDevice*, uint8_t rhport,
562 tusb_control_request_t const* p_request, uint8_t* pBuff)>
563 tud_audio_set_req_ep_cb_;
564
565 std::function<bool(USBAudioDevice*, uint8_t rhport,
566 tusb_control_request_t const* p_request)>
567 tud_audio_set_itf_close_EP_cb_;
568
569 std::function<bool(USBAudioDevice*, uint8_t rhport, audiod_function_t* audio)>
570 audiod_tx_done_cb_;
571
572 std::function<void(USBAudioDevice*, uint8_t func_id, uint8_t alt_itf,
573 audio_feedback_params_t* feedback_param)>
574 tud_audio_feedback_params_cb_;
575
576 std::function<bool(USBAudioDevice*, uint8_t func_id)>
577 tud_audio_feedback_format_correction_cb_;
578
579 // TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT,
580 // CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT)
581 std::vector<uint16_t> ep_out_sw_buf_sz_;
582 // (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write
583 // FIFO every 1ms, so it should be 8 times larger for HS device
584 std::vector<uint16_t> ep_in_sw_buf_sz_;
585 // calculate!
586 std::vector<uint16_t> desc_len_;
587 // 64
588 std::vector<uint16_t> ctrl_buf_sz_;
589
590 std::vector<audiod_function_t> audiod_fct_;
591 std::vector<osal_mutex_def_t> ep_in_ff_mutex_wr_;
592 std::vector<osal_mutex_def_t> ep_out_ff_mutex_rd_;
593
594 USBAudioDevice() = default;
595
596 // Returns the control buffer size for a given function number
597 uint16_t getCtrlBufSz(uint8_t fn) const {
598 return (fn < ctrl_buf_sz_.size()) ? ctrl_buf_sz_[fn] : 64;
599 }
600
601 // Returns the OUT software buffer size for a given function number
602 uint16_t getEpOutSwBufSz(uint8_t fn) const {
603 return (fn < ep_out_sw_buf_sz_.size()) ? ep_out_sw_buf_sz_[fn] : 0;
604 }
605
606 // Returns the IN software buffer size for a given function number
607 uint16_t getEpInSwBufSz(uint8_t fn) const {
608 return (fn < ep_in_sw_buf_sz_.size()) ? ep_in_sw_buf_sz_[fn] : 0;
609 }
610
611 // Returns the descriptor length for a given function number
612 uint16_t getDescLen(uint8_t fn) const {
613 return (fn < desc_len_.size()) ? desc_len_[fn] : 0;
614 }
615
616 // Returns whether linear buffer RX is enabled
617 bool getUseLinearBufferRx() const { return config_.use_linear_buffer_rx; }
618
619 bool getUseLinearBufferTx() const { return config_.use_linear_buffer_tx; }
620
621 // Returns the reset size for audiod_function_t up to and including
622 // ctrl_buf_sz
623 static constexpr size_t getResetSize() {
624 return offsetof(audiod_function_t, ctrl_buf_sz) +
625 sizeof(((audiod_function_t*)0)->ctrl_buf_sz);
626 }
627
628 // Dummy for tud_audio_feedback_interval_isr (should be implemented elsewhere
629 // if not available)
630 void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_count,
631 uint8_t frame_shift) {
632 // Implement or forward to actual USB stack as needed
633 }
634
635 // Write audio data to IN endpoint
636 uint16_t write(const void* data, uint16_t len) {
637 uint16_t written = tud_audio_n_write(config_.ep_in, data, len);
638 return written;
639 return 0;
640 }
641
642 // Read audio data from OUT endpoint
643 uint16_t read(void* buffer, uint16_t bufsize) {
644 return tud_audio_n_read(config_.ep_out, buffer, bufsize);
645 }
646
647 // USBD Driver API
648 void audiod_init(void) {
649 audiod_fct_.resize(getAudioCount());
650 alloc_mutex();
651
652 // Initialize control buffers
653 for (uint8_t i = 0; i < getAudioCount(); i++) {
654 audiod_function_t* audio = &audiod_fct_[i];
655 // Initialize control buffers
656 int size = getCtrlBufSz(i);
657 audio->ctrl_buf.resize(size);
658 audio->ctrl_buf_sz = size;
659 // Initialize active alternate interface buffers
660 audio->alt_setting.resize(config_.audio_functions_count());
661 // Initialize IN EP FIFO if required
662 if (getEnableEpIn()) {
663 audio->ep_in_sw_buf.resize(getEpInSwBufSz(i));
664
665 tu_fifo_config(&audio->ep_in_ff,
666 audio->ep_in_sw_buf.data(), // Use data pointer for FIFO
667 getEpInSwBufSz(i), 1, true);
668 if (getEnableFifoMutex()) {
669 tu_fifo_config_mutex(&audio->ep_in_ff,
670 osal_mutex_create(&ep_in_ff_mutex_wr_[i]), NULL);
671 }
672 }
673 // Initialize linear buffers
674 if (getUseLinearBufferTx()) {
675 audio->lin_buf_in.resize(config_.lin_buf_in_size_per_func);
676 }
677 // Initialize OUT EP FIFO if required
678 if (getEnableEpOut()) {
679 audio->ep_out_sw_buf.resize(getEpOutSwBufSz(i));
680 tu_fifo_config(&audio->ep_out_ff, audio->ep_out_sw_buf.data(),
681 getEpOutSwBufSz(i), 1, true);
682 if (getEnableFifoMutex()) {
683 tu_fifo_config_mutex(&audio->ep_out_ff, NULL,
684 osal_mutex_create(&ep_out_ff_mutex_rd_[i]));
685 }
686 }
687 // Initialize linear buffers
688 if (getUseLinearBufferRx()) {
689 audio->lin_buf_out.resize(config_.lin_buf_in_size_per_func);
690 }
691 if (getEnableFeedbackEp()) {
692 audio->fb_buf.resize(getEpOutSwBufSz(i));
693 }
694 }
695 }
696
697 void alloc_mutex() {
698 if (getEnableFifoMutex()) {
699 if (getEnableEpIn()) {
700 ep_in_ff_mutex_wr_.resize(getAudioCount());
701 }
702 if (getEnableEpOut()) {
703 ep_out_ff_mutex_rd_.resize(getAudioCount());
704 }
705 }
706 }
707
708 bool audiod_deinit(void) {
709 return false; // TODO not implemented yet
710 }
711
712 void audiod_reset(uint8_t rhport) {
713 (void)rhport;
714 for (uint8_t i = 0; i < getAudioCount(); i++) {
715 audiod_function_t* audio = &audiod_fct_[i];
716 memset(audio, 0, getResetSize());
717 if (getEnableEpIn()) {
718 tu_fifo_clear(&audio->ep_in_ff);
719 }
720 if (getEnableEpOut()) {
721 tu_fifo_clear(&audio->ep_out_ff);
722 }
723 }
724 }
725
726 uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc,
727 uint16_t max_len) {
728 (void)max_len;
729 TU_VERIFY(TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
730 AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
731 TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
732 TU_ASSERT(itf_desc->bNumEndpoints <= 1);
733 if (itf_desc->bNumEndpoints == 1) {
734 TU_ASSERT(getEnableInterruptEp());
735 }
736 TU_VERIFY(itf_desc->bAlternateSetting == 0);
737 uint8_t i;
738 for (i = 0; i < getAudioCount(); i++) {
739 if (!audiod_fct_[i].p_desc) {
740 audiod_fct_[i].p_desc = (uint8_t const*)itf_desc;
741 audiod_fct_[i].rhport = rhport;
742 audiod_fct_[i].desc_length = getDescLen(i);
744 uint8_t ep_in = 0, ep_out = 0, ep_fb = 0;
745 uint16_t ep_in_size = 0, ep_out_size = 0;
746 uint8_t const* p_desc = audiod_fct_[i].p_desc;
747 uint8_t const* p_desc_end =
748 p_desc + audiod_fct_[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
749 while (p_desc_end - p_desc > 0) {
750 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
751 tusb_desc_endpoint_t const* desc_ep =
752 (tusb_desc_endpoint_t const*)p_desc;
753 if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
754 if (getEnableFeedbackEp() && desc_ep->bmAttributes.usage == 1) {
755 ep_fb = desc_ep->bEndpointAddress;
756 }
757 if (desc_ep->bmAttributes.usage == 0) {
758 if (getEnableEpIn() &&
759 tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
760 ep_in = desc_ep->bEndpointAddress;
761 ep_in_size =
762 TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
763 } else if (getEnableEpOut() &&
764 tu_edpt_dir(desc_ep->bEndpointAddress) ==
765 TUSB_DIR_OUT) {
766 ep_out = desc_ep->bEndpointAddress;
767 ep_out_size =
768 TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
769 }
770 }
771 }
772 }
773 p_desc = tu_desc_next(p_desc);
774 }
775 if (getEnableEpIn() && ep_in) {
776 usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size);
777 }
778 if (getEnableEpOut() && ep_out) {
779 usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size);
780 }
781 if (getEnableFeedbackEp() && ep_fb) {
782 usbd_edpt_iso_alloc(rhport, ep_fb, 4);
783 }
784 }
786 uint8_t const* p_desc = audiod_fct_[i].p_desc;
787 uint8_t const* p_desc_end =
788 p_desc + audiod_fct_[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
789 while (p_desc_end - p_desc > 0) {
790 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
791 tusb_desc_endpoint_t const* desc_ep =
792 (tusb_desc_endpoint_t const*)p_desc;
793 if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS &&
794 desc_ep->bmAttributes.usage == 0 &&
795 tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
796 audiod_fct_[i].interval_tx = desc_ep->bInterval;
797 }
798 } else if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
799 tu_desc_subtype(p_desc) ==
800 AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL) {
801 if (tu_unaligned_read16(p_desc + 4) ==
802 AUDIO_TERM_TYPE_USB_STREAMING) {
803 audiod_fct_[i].bclock_id_tx = p_desc[8];
804 }
805 }
806 p_desc = tu_desc_next(p_desc);
807 }
808 }
809 if (getEnableInterruptEp()) {
810 uint8_t const* p_desc = audiod_fct_[i].p_desc;
811 uint8_t const* p_desc_end =
812 p_desc + audiod_fct_[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
813 while (p_desc_end - p_desc > 0) {
814 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
815 tusb_desc_endpoint_t const* desc_ep =
816 (tusb_desc_endpoint_t const*)p_desc;
817 uint8_t const ep_addr = desc_ep->bEndpointAddress;
818 if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN &&
819 desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
820 audiod_fct_[i].ep_int = ep_addr;
821 TU_ASSERT(usbd_edpt_open(audiod_fct_[i].rhport, desc_ep));
822 }
823 }
824 p_desc = tu_desc_next(p_desc);
825 }
826 }
827 audiod_fct_[i].mounted = true;
828 break;
829 }
830 }
831 TU_ASSERT(i < getAudioCount());
832 uint16_t drv_len = audiod_fct_[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
833 return drv_len;
834 }
835
836 bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage,
837 tusb_control_request_t const* request) {
838 if (stage == CONTROL_STAGE_SETUP) {
839 return audiod_control_request(rhport, request);
840 } else if (stage == CONTROL_STAGE_DATA) {
841 return audiod_control_complete(rhport, request);
842 }
843 return true;
844 }
845 // Invoked when class request DATA stage is finished.
846 // return false to stall control EP (e.g Host send non-sense DATA)
847 bool audiod_control_complete(uint8_t rhport,
848 tusb_control_request_t const* p_request) {
849 // Handle audio class specific set requests
850 if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
851 p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) {
852 uint8_t func_id;
853
854 switch (p_request->bmRequestType_bit.recipient) {
855 case TUSB_REQ_RCPT_INTERFACE: {
856 uint8_t itf = TU_U16_LOW(p_request->wIndex);
857 uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
858
859 if (entityID != 0) {
860 // Check if entity is present and get corresponding driver index
861 TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
862
864 uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
865 if (audiod_fct_[func_id].bclock_id_tx == entityID &&
866 ctrlSel == AUDIO_CS_CTRL_SAM_FREQ &&
867 p_request->bRequest == AUDIO_CS_REQ_CUR) {
868 audiod_fct_[func_id].sample_rate_tx =
869 tu_unaligned_read32(audiod_fct_[func_id].ctrl_buf.data());
870 }
871 }
872
873 // Invoke callback
874 if (tud_audio_set_req_entity_cb_) {
875 return tud_audio_set_req_entity_cb_(
876 this, rhport, p_request,
877 audiod_fct_[func_id].ctrl_buf.data());
878 }
879 } else {
880 // Find index of audio driver structure and verify interface really
881 // exists
882 TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
883
884 // Invoke callback
885 if (tud_audio_set_req_itf_cb_) {
886 return tud_audio_set_req_itf_cb_(
887 this, rhport, p_request,
888 audiod_fct_[func_id].ctrl_buf.data());
889 }
890 }
891 } break;
892
893 case TUSB_REQ_RCPT_ENDPOINT: {
894 uint8_t ep = TU_U16_LOW(p_request->wIndex);
895
896 // Check if entity is present and get corresponding driver index
897 TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
898
899 // Invoke callback
900 if (tud_audio_set_req_ep_cb_) {
901 return tud_audio_set_req_ep_cb_(
902 this, rhport, p_request, audiod_fct_[func_id].ctrl_buf.data());
903 }
904 } break;
905 // Unknown/Unsupported recipient
906 default:
907 TU_BREAKPOINT();
908 return false;
909 }
910 }
911 return true;
912 }
913
914 bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
915 uint32_t xferred_bytes) {
916 (void)result;
917 (void)xferred_bytes;
918 for (uint8_t func_id = 0; func_id < getAudioCount(); func_id++) {
919 audiod_function_t* audio = &audiod_fct_[func_id];
920 if (getEnableInterruptEp() && audio->ep_int == ep_addr) {
921 if (int_done_cb_) int_done_cb_(this, rhport);
922 return true;
923 }
924 if (getEnableEpIn() && audio->ep_in == ep_addr &&
925 audio->alt_setting.size() != 0) {
926 if (tx_done_cb_ && tx_done_cb_(this, rhport, audio)) return true;
927 return true;
928 }
929 if (getEnableEpOut() && audio->ep_out == ep_addr) {
930 if (rx_done_cb_ &&
931 rx_done_cb_(this, rhport, audio, (uint16_t)xferred_bytes))
932 return true;
933 return true;
934 }
935 if (getEnableFeedbackEp() && audio->ep_fb == ep_addr) {
936 if (fb_done_cb_) {
937 fb_done_cb_(this, func_id);
938 }
939 if (usbd_edpt_claim(rhport, audio->ep_fb)) {
940 return audiod_fb_send(audio);
941 }
942 }
943 }
944 return false;
945 }
946
947 TU_ATTR_FAST_FUNC void audiod_sof_isr(uint8_t rhport, uint32_t frame_count) {
948 (void)rhport;
949 (void)frame_count;
951 for (uint8_t i = 0; i < getAudioCount(); i++) {
952 audiod_function_t* audio = &audiod_fct_[i];
953 if (audio->ep_fb != 0) {
954 uint8_t const hs_adjust =
955 (TUSB_SPEED_HIGH == tud_speed_get()) ? 3 : 0;
956 uint32_t const interval =
957 1UL << (audio->feedback.frame_shift - hs_adjust);
958 if (0 == (frame_count & (interval - 1))) {
959 tud_audio_feedback_interval_isr(i, frame_count,
960 audio->feedback.frame_shift);
961 }
962 }
963 }
964 }
965 }
966
967 bool audiod_control_request(uint8_t rhport,
968 tusb_control_request_t const* p_request) {
969 (void)rhport;
970
971 // Handle standard requests - standard set requests usually have no data
972 // stage so we also handle set requests here
973 if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
974 switch (p_request->bRequest) {
975 case TUSB_REQ_GET_INTERFACE:
976 return audiod_get_interface(rhport, p_request);
977
978 case TUSB_REQ_SET_INTERFACE:
979 return audiod_set_interface(rhport, p_request);
980
981 case TUSB_REQ_CLEAR_FEATURE:
982 return true;
983
984 // Unknown/Unsupported request
985 default:
986 TU_BREAKPOINT();
987 return false;
988 }
989 }
990
991 // Handle class requests
992 if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
993 uint8_t itf = TU_U16_LOW(p_request->wIndex);
994 uint8_t func_id;
995
996 // Conduct checks which depend on the recipient
997 switch (p_request->bmRequestType_bit.recipient) {
998 case TUSB_REQ_RCPT_INTERFACE: {
999 uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
1000
1001 // Verify if entity is present
1002 if (entityID != 0) {
1003 // Find index of audio driver structure and verify entity really
1004 // exists
1005 TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
1006
1007 // In case we got a get request invoke callback - callback needs to
1008 // answer as defined in UAC2 specification page 89 - 5. Requests
1009 if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN &&
1010 req_entity_cb_) {
1011 return req_entity_cb_(this, func_id);
1012 }
1013 } else {
1014 // Find index of audio driver structure and verify interface really
1015 // exists
1016 TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
1017
1018 // In case we got a get request invoke callback - callback needs to
1019 // answer as defined in UAC2 specification page 89 - 5. Requests
1020 if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
1021 // Use callback if set, otherwise return false
1022 if (get_req_itf_cb_) {
1023 return get_req_itf_cb_(this, rhport, p_request);
1024 }
1025 return false;
1026 }
1027 }
1028 } break;
1029
1030 case TUSB_REQ_RCPT_ENDPOINT: {
1031 uint8_t ep = TU_U16_LOW(p_request->wIndex);
1032
1033 // Find index of audio driver structure and verify EP really exists
1034 TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
1035
1036 // In case we got a get request invoke callback - callback needs to
1037 // answer as defined in UAC2 specification page 89 - 5. Requests
1038 if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
1039 // Use callback if set, otherwise return false
1040 if (get_req_ep_cb_) {
1041 return get_req_ep_cb_(this, rhport, p_request);
1042 }
1043 return false;
1044 }
1045 } break;
1046
1047 // Unknown/Unsupported recipient
1048 default:
1049 TU_LOG2(" Unsupported recipient: %d\r\n",
1050 p_request->bmRequestType_bit.recipient);
1051 TU_BREAKPOINT();
1052 return false;
1053 }
1054
1055 // If we end here, the received request is a set request - we schedule a
1056 // receive for the data stage and return true here. We handle the rest
1057 // later in audiod_control_complete() once the data stage was finished
1058 TU_VERIFY(tud_control_xfer(rhport, p_request,
1059 audiod_fct_[func_id].ctrl_buf.data(),
1060 audiod_fct_[func_id].ctrl_buf_sz));
1061 return true;
1062 }
1063
1064 // There went something wrong - unsupported control request type
1065 TU_BREAKPOINT();
1066 return false;
1067 }
1068 // Verify an entity with the given ID exists and returns also the
1069 // corresponding driver index
1070 bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID,
1071 uint8_t* func_id) {
1072 uint8_t i;
1073 for (i = 0; i < getAudioCount(); i++) {
1074 // Look for the correct driver by checking if the unique standard AC
1075 // interface number fits
1076 if (audiod_fct_[i].p_desc &&
1077 ((tusb_desc_interface_t const*)audiod_fct_[i].p_desc)
1078 ->bInterfaceNumber == itf) {
1079 // Get pointers after class specific AC descriptors and end of AC
1080 // descriptors - entities are defined in between
1081 uint8_t const* p_desc =
1082 tu_desc_next(audiod_fct_[i].p_desc); // Points to CS AC descriptor
1083 uint8_t const* p_desc_end =
1084 ((audio_desc_cs_ac_interface_t const*)p_desc)->wTotalLength +
1085 p_desc;
1086 p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor
1087
1088 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1089 // strict-overflow warning
1090 while (p_desc_end - p_desc > 0) {
1091 if (p_desc[3] == entityID) // Entity IDs are always at offset 3
1092 {
1093 *func_id = i;
1094 return true;
1095 }
1096 p_desc = tu_desc_next(p_desc);
1097 }
1098 }
1099 }
1100 return false;
1101 }
1102
1103 bool audiod_verify_ep_exists(uint8_t ep, uint8_t* func_id) {
1104 uint8_t i;
1105 for (i = 0; i < getAudioCount(); i++) {
1106 if (audiod_fct_[i].p_desc) {
1107 // Get pointer at end
1108 uint8_t const* p_desc_end =
1109 audiod_fct_[i].p_desc + audiod_fct_[i].desc_length;
1110
1111 // Advance past AC descriptors - EP we look for are streaming EPs
1112 uint8_t const* p_desc = tu_desc_next(audiod_fct_[i].p_desc);
1113 p_desc += ((audio_desc_cs_ac_interface_t const*)p_desc)->wTotalLength;
1114
1115 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1116 // strict-overflow warning
1117 while (p_desc_end - p_desc > 0) {
1118 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT &&
1119 ((tusb_desc_endpoint_t const*)p_desc)->bEndpointAddress == ep) {
1120 *func_id = i;
1121 return true;
1122 }
1123 p_desc = tu_desc_next(p_desc);
1124 }
1125 }
1126 }
1127 return false;
1128 }
1129
1130 bool audiod_verify_itf_exists(uint8_t itf, uint8_t* func_id) {
1131 uint8_t i;
1132 for (i = 0; i < getAudioCount(); i++) {
1133 if (audiod_fct_[i].p_desc) {
1134 // Get pointer at beginning and end
1135 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1136 uint8_t const* p_desc_end = audiod_fct_[i].p_desc +
1137 audiod_fct_[i].desc_length -
1138 TUD_AUDIO_DESC_IAD_LEN;
1139 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1140 // strict-overflow warning
1141 while (p_desc_end - p_desc > 0) {
1142 if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
1143 ((tusb_desc_interface_t const*)audiod_fct_[i].p_desc)
1144 ->bInterfaceNumber == itf) {
1145 *func_id = i;
1146 return true;
1147 }
1148 p_desc = tu_desc_next(p_desc);
1149 }
1150 }
1151 }
1152 return false;
1153 }
1154
1155 void audiod_parse_flow_control_params(audiod_function_t* audio,
1156 uint8_t const* p_desc) {
1157 p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor
1158 // of current alternate interface descriptor
1159
1160 // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format
1161 // type and format and also to get number of physical channels
1162 if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
1163 tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL) {
1164 audio->n_channels_tx =
1165 ((audio_desc_cs_as_interface_t const*)p_desc)->bNrChannels;
1166 audio->format_type_tx =
1167 (audio_format_type_t)(((audio_desc_cs_as_interface_t const*)p_desc)
1168 ->bFormatType);
1169 // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
1170 p_desc = tu_desc_next(p_desc);
1171 if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
1172 tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE &&
1173 ((audio_desc_type_I_format_t const*)p_desc)->bFormatType ==
1174 AUDIO_FORMAT_TYPE_I) {
1175 audio->n_bytes_per_sample_tx =
1176 ((audio_desc_type_I_format_t const*)p_desc)->bSubslotSize;
1177 }
1178 }
1179 }
1180
1181 // This helper function finds for a given audio function and AS interface
1182 // number the index of the attached driver structure, the index of the
1183 // interface in the audio function
1184 // (e.g. the std. AS interface with interface number 15 is the first AS
1185 // interface for the given audio function and thus gets index zero), and
1186 // finally a pointer to the std. AS interface, where the pointer always points
1187 // to the first alternate setting i.e. alternate interface zero.
1188 bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t* audio,
1189 uint8_t* idxItf,
1190 uint8_t const** pp_desc_int) {
1191 if (audio->p_desc) {
1192 // Get pointer at end
1193 uint8_t const* p_desc_end =
1194 audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
1195
1196 // Advance past AC descriptors
1197 uint8_t const* p_desc = tu_desc_next(audio->p_desc);
1198 p_desc += ((audio_desc_cs_ac_interface_t const*)p_desc)->wTotalLength;
1199
1200 uint8_t tmp = 0;
1201 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1202 // strict-overflow warning
1203 while (p_desc_end - p_desc > 0) {
1204 // We assume the number of alternate settings is increasing thus we
1205 // return the index of alternate setting zero!
1206 if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
1207 ((tusb_desc_interface_t const*)p_desc)->bAlternateSetting == 0) {
1208 if (((tusb_desc_interface_t const*)p_desc)->bInterfaceNumber == itf) {
1209 *idxItf = tmp;
1210 *pp_desc_int = p_desc;
1211 return true;
1212 }
1213 // Increase index, bytes read, and pointer
1214 tmp++;
1215 }
1216 p_desc = tu_desc_next(p_desc);
1217 }
1218 }
1219 return false;
1220 }
1221
1222 // This helper function finds for a given AS interface number the index of the
1223 // attached driver structure, the index of the interface in the audio function
1224 // (e.g. the std. AS interface with interface number 15 is the first AS
1225 // interface for the given audio function and thus gets index zero), and
1226 // finally a pointer to the std. AS interface, where the pointer always points
1227 // to the first alternate setting i.e. alternate interface zero.
1228 bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t* func_id,
1229 uint8_t* idxItf,
1230 uint8_t const** pp_desc_int) {
1231 // Loop over audio driver interfaces
1232 uint8_t i;
1233 for (i = 0; i < getAudioCount(); i++) {
1234 if (audiod_get_AS_interface_index(itf, &audiod_fct_[i], idxItf,
1235 pp_desc_int)) {
1236 *func_id = i;
1237 return true;
1238 }
1239 }
1240
1241 return false;
1242 }
1243
1244 bool audiod_get_interface(uint8_t rhport,
1245 tusb_control_request_t const* p_request) {
1246 uint8_t const itf = tu_u16_low(p_request->wIndex);
1247
1248 // Find index of audio streaming interface
1249 uint8_t func_id, idxItf;
1250 uint8_t const* dummy;
1251
1252 TU_VERIFY(
1253 audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy));
1254 TU_VERIFY(tud_control_xfer(rhport, p_request,
1255 &audiod_fct_[func_id].alt_setting[idxItf], 1));
1256
1257 TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf,
1258 audiod_fct_[func_id].alt_setting[idxItf]);
1259
1260 return true;
1261 }
1262
1263 bool audiod_fb_send(audiod_function_t* audio) {
1264 bool apply_correction = (TUSB_SPEED_FULL == tud_speed_get()) &&
1265 audio->feedback.format_correction;
1266 // Format the feedback value
1267 if (apply_correction) {
1268 uint8_t* fb = (uint8_t*)audio->fb_buf.data();
1269
1270 // For FS format is 10.14
1271 *(fb++) = (audio->feedback.value >> 2) & 0xFF;
1272 *(fb++) = (audio->feedback.value >> 10) & 0xFF;
1273 *(fb++) = (audio->feedback.value >> 18) & 0xFF;
1274 *fb = 0;
1275 } else {
1276 audio->fb_buf[0] = audio->feedback.value;
1277 }
1278
1279 // About feedback format on FS
1280 //
1281 // 3 variables: Format | packetSize | sendSize | Working OS:
1282 // 16.16 4 4 Linux, Windows
1283 // 16.16 4 3 Linux
1284 // 16.16 3 4 Linux
1285 // 16.16 3 3 Linux
1286 // 10.14 4 4 Linux
1287 // 10.14 4 3 Linux
1288 // 10.14 3 4 Linux, OSX
1289 // 10.14 3 3 Linux, OSX
1290 //
1291 // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty
1292 // ugly
1293 return usbd_edpt_xfer(audio->rhport, audio->ep_fb,
1294 (uint8_t*)audio->fb_buf.data(),
1295 apply_correction ? 3 : 4);
1296 }
1297
1298 bool audiod_set_interface(uint8_t rhport,
1299 tusb_control_request_t const* p_request) {
1300 (void)rhport;
1301
1302 // Here we need to do the following:
1303
1304 // 1. Find the audio driver assigned to the given interface to be set
1305 // Since one audio driver interface has to be able to cover an unknown
1306 // number of interfaces (AC, AS + its alternate settings), the best memory
1307 // efficient way to solve this is to always search through the descriptors.
1308 // The audio driver is mapped to an audio function by a reference pointer to
1309 // the corresponding AC interface of this audio function which serves as a
1310 // starting point for searching
1311
1312 // 2. Close EPs which are currently open
1313 // To do so it is not necessary to know the current active alternate
1314 // interface since we already save the current EP addresses - we simply
1315 // close them
1316
1317 // 3. Open new EP
1318
1319 uint8_t const itf = tu_u16_low(p_request->wIndex);
1320 uint8_t const alt = tu_u16_low(p_request->wValue);
1321
1322 TU_LOG2(" Set itf: %u - alt: %u\r\n", itf, alt);
1323
1324 // Find index of audio streaming interface and index of interface
1325 uint8_t func_id, idxItf;
1326 uint8_t const* p_desc;
1327 TU_VERIFY(
1328 audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &p_desc));
1329
1330 audiod_function_t* audio = &audiod_fct_[func_id];
1331
1332 // Look if there is an EP to be closed - for this driver, there are only 3
1333 // possible EPs which may be closed (only AS related EPs can be closed, AC
1334 // EP (if present) is always open)
1335 if (getEnableEpIn()) {
1336 if (audio->ep_in_as_intf_num == itf) {
1337 audio->ep_in_as_intf_num = 0;
1338#ifndef TUP_DCD_EDPT_ISO_ALLOC
1339 usbd_edpt_close(rhport, audio->ep_in);
1340#endif
1341
1342 // Clear FIFOs, since data is no longer valid
1343 tu_fifo_clear(&audio->ep_in_ff);
1344
1345 // Invoke callback - can be used to stop data sampling
1346 if (tud_audio_set_itf_close_EP_cb_) {
1347 TU_VERIFY(tud_audio_set_itf_close_EP_cb_(this, rhport, p_request));
1348 }
1349
1350 audio->ep_in = 0; // Necessary?
1351
1353 audio->packet_sz_tx[0] = 0;
1354 audio->packet_sz_tx[1] = 0;
1355 audio->packet_sz_tx[2] = 0;
1356 }
1357 }
1358 } // getEnableEpIn()
1359
1360 if (getEnableEpOut()) {
1361 if (audio->ep_out_as_intf_num == itf) {
1362 audio->ep_out_as_intf_num = 0;
1363#ifndef TUP_DCD_EDPT_ISO_ALLOC
1364 usbd_edpt_close(rhport, audio->ep_out);
1365#endif
1366
1367 // Clear FIFOs, since data is no longer valid
1368 tu_fifo_clear(&audio->ep_out_ff);
1369
1370 // Invoke callback - can be used to stop data sampling
1371 if (tud_audio_set_itf_close_EP_cb_) {
1372 TU_VERIFY(tud_audio_set_itf_close_EP_cb_(this, rhport, p_request));
1373 }
1374
1375 audio->ep_out = 0; // Necessary?
1376
1377 // Close corresponding feedback EP
1378 if (getEnableFeedbackEp()) {
1379 // #ifndef TUP_DCD_EDPT_ISO_ALLOC
1380 // usbd_edpt_close(rhport, audio->ep_fb);
1381 // #endif
1382 audio->ep_fb = 0;
1383 tu_memclr(&audio->feedback, sizeof(audio->feedback));
1384 }
1385 }
1386 } // getEnableEpOut()
1387
1388 // Save current alternative interface setting
1389 audio->alt_setting[idxItf] = alt;
1390
1391 // Open new EP if necessary - EPs are only to be closed or opened for AS
1392 // interfaces - Look for AS interface with correct alternate interface Get
1393 // pointer at end
1394 uint8_t const* p_desc_end =
1395 audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
1396
1397 // p_desc starts at required interface with alternate setting zero
1398 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1399 // strict-overflow warning
1400 while (p_desc_end - p_desc > 0) {
1401 // Find correct interface
1402 if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
1403 ((tusb_desc_interface_t const*)p_desc)->bInterfaceNumber == itf &&
1404 ((tusb_desc_interface_t const*)p_desc)->bAlternateSetting == alt) {
1405 uint8_t const* p_desc_parse_for_params = nullptr;
1407 p_desc_parse_for_params = p_desc;
1408 }
1409 // From this point forward follow the EP descriptors associated to the
1410 // current alternate setting interface - Open EPs if necessary
1411 uint8_t foundEPs = 0,
1412 nEps = ((tusb_desc_interface_t const*)p_desc)->bNumEndpoints;
1413 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1414 // strict-overflow warning
1415 while (foundEPs < nEps && (p_desc_end - p_desc > 0)) {
1416 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
1417 tusb_desc_endpoint_t const* desc_ep =
1418 (tusb_desc_endpoint_t const*)p_desc;
1419#ifdef TUP_DCD_EDPT_ISO_ALLOC
1420 TU_ASSERT(usbd_edpt_iso_activate(rhport, desc_ep));
1421#else
1422 TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
1423#endif
1424 uint8_t const ep_addr = desc_ep->bEndpointAddress;
1425
1426 // TODO: We need to set EP non busy since this is not taken care of
1427 // right now in ep_close() - THIS IS A WORKAROUND!
1428 usbd_edpt_clear_stall(rhport, ep_addr);
1429
1430 if (getEnableEpIn()) {
1431 if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN &&
1432 desc_ep->bmAttributes.usage ==
1433 0x00) // Check if usage is data EP
1434 {
1435 // Save address
1436 audio->ep_in = ep_addr;
1437 audio->ep_in_as_intf_num = itf;
1438 audio->ep_in_sz = tu_edpt_packet_size(desc_ep);
1439
1440 // If flow control is enabled, parse for the corresponding
1441 // parameters - doing this here means only AS interfaces with
1442 // EPs get scanned for parameters
1444 audiod_parse_flow_control_params(audio,
1445 p_desc_parse_for_params);
1446 }
1447 // Schedule first transmit if alternate interface is not zero
1448 // i.e. streaming is disabled - in case no sample data is
1449 // available a ZLP is loaded It is necessary to trigger this
1450 // here since the refill is done with an RX FIFO empty interrupt
1451 // which can only trigger if something was in there
1452 if (audiod_tx_done_cb_) {
1453 TU_VERIFY(
1454 audiod_tx_done_cb_(this, rhport, &audiod_fct_[func_id]));
1455 }
1456 }
1457 } // getEnableEpIn()
1458
1459 if (getEnableEpOut()) {
1460 if (tu_edpt_dir(ep_addr) ==
1461 TUSB_DIR_OUT) // Checking usage not necessary
1462 {
1463 // Save address
1464 audio->ep_out = ep_addr;
1465 audio->ep_out_as_intf_num = itf;
1466 audio->ep_out_sz = tu_edpt_packet_size(desc_ep);
1467
1468 // Prepare for incoming data
1469 if (getUseLinearBufferRx()) {
1470 TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out,
1471 audio->lin_buf_out.data(),
1472 audio->ep_out_sz),
1473 false);
1474 } else {
1475 TU_VERIFY(
1476 usbd_edpt_xfer_fifo(rhport, audio->ep_out,
1477 &audio->ep_out_ff, audio->ep_out_sz),
1478 false);
1479 }
1480 }
1481
1482 if (getEnableFeedbackEp()) {
1483 if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN &&
1484 desc_ep->bmAttributes.usage ==
1485 1) // Check if usage is explicit data feedback
1486 {
1487 audio->ep_fb = ep_addr;
1488 audio->feedback.frame_shift = desc_ep->bInterval - 1;
1489 }
1490 }
1491 } // getEnableEpOut()
1492
1493 foundEPs += 1;
1494 }
1495 p_desc = tu_desc_next(p_desc);
1496 }
1497
1498 TU_VERIFY(foundEPs == nEps);
1499
1500 // Invoke one callback for a final set interface
1501 if (tud_audio_set_itf_cb_) {
1502 TU_VERIFY(tud_audio_set_itf_cb_(this, rhport, p_request));
1503 }
1504
1505 if (getEnableFeedbackEp()) {
1506 // Prepare feedback computation if endpoint is available
1507 if (audio->ep_fb != 0) {
1508 audio_feedback_params_t fb_param;
1509
1510 if (tud_audio_feedback_params_cb_) {
1511 tud_audio_feedback_params_cb_(this, func_id, alt, &fb_param);
1512 }
1513 audio->feedback.compute_method = fb_param.method;
1514
1515 if (TUSB_SPEED_FULL == tud_speed_get())
1516 if (tud_audio_feedback_format_correction_cb_) {
1517 audio->feedback.format_correction =
1518 tud_audio_feedback_format_correction_cb_(this, func_id);
1519 }
1520
1521 // Minimal/Maximum value in 16.16 format for full speed (1ms per
1522 // frame) or high speed (125 us per frame)
1523 uint32_t const frame_div =
1524 (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
1525 audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div)
1526 << 16;
1527 audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1)
1528 << 16;
1529
1530 switch (fb_param.method) {
1531 case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
1532 case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
1533 case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
1534 audiod_set_fb_params_freq(audio, fb_param.sample_freq,
1535 fb_param.frequency.mclk_freq);
1536 break;
1537
1538 case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
1539 // Initialize the threshold level to half filled
1540 uint16_t fifo_lvl_thr = tu_fifo_depth(&audio->ep_out_ff) / 2;
1541 audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
1542 audio->feedback.compute.fifo_count.fifo_lvl_avg =
1543 ((uint32_t)fifo_lvl_thr) << 16;
1544 // Avoid 64bit division
1545 uint32_t nominal =
1546 ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
1547 audio->feedback.compute.fifo_count.nom_value = nominal;
1548 audio->feedback.compute.fifo_count.rate_const[0] =
1549 (uint16_t)((audio->feedback.max_value - nominal) /
1550 fifo_lvl_thr);
1551 audio->feedback.compute.fifo_count.rate_const[1] =
1552 (uint16_t)((nominal - audio->feedback.min_value) /
1553 fifo_lvl_thr);
1554 // On HS feedback is more sensitive since packet size can vary
1555 // every MSOF, could cause instability
1556 if (tud_speed_get() == TUSB_SPEED_HIGH) {
1557 audio->feedback.compute.fifo_count.rate_const[0] /= 8;
1558 audio->feedback.compute.fifo_count.rate_const[1] /= 8;
1559 }
1560 } break;
1561
1562 // nothing to do
1563 default:
1564 break;
1565 }
1566 }
1567 } // getEnableFeedbackEp()
1568
1569 // We are done - abort loop
1570 break;
1571 }
1572
1573 // Moving forward
1574 p_desc = tu_desc_next(p_desc);
1575 }
1576
1577 if (getEnableFeedbackEp()) {
1578 // Disable SOF interrupt if no driver has any enabled feedback EP
1579 bool enable_sof = false;
1580 for (uint8_t i = 0; i < getAudioCount(); i++) {
1581 if (audiod_fct_[i].ep_fb != 0 &&
1582 (audiod_fct_[i].feedback.compute_method ==
1583 AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED ||
1584 audiod_fct_[i].feedback.compute_method ==
1585 AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT ||
1586 audiod_fct_[i].feedback.compute_method ==
1587 AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2)) {
1588 enable_sof = true;
1589 break;
1590 }
1591 }
1592 usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, enable_sof);
1593 }
1594
1596 audiod_calc_tx_packet_sz(audio);
1597 }
1598
1599 tud_control_status(rhport, p_request);
1600
1601 return true;
1602 }
1603
1604 bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq,
1605 uint32_t mclk_freq) {
1606 // Check if frame interval is within sane limits
1607 // The interval value n_frames was taken from the descriptors within
1608
1609 // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s
1610 // / f_m) for high speed this lower limit ensures the measures feedback
1611 // value has sufficient precision
1612 uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
1613 uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
1614
1615 if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) {
1616 TU_LOG1(" UAC2 feedback interval too small\r\n");
1617 TU_BREAKPOINT();
1618 return false;
1619 }
1620
1621 // Check if parameters really allow for a power of two division
1622 if ((mclk_freq % sample_freq) == 0 &&
1623 tu_is_power_of_two(mclk_freq / sample_freq)) {
1624 audio->feedback.compute_method =
1625 AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
1626 audio->feedback.compute.power_of_2 =
1627 (uint8_t)(16 - (audio->feedback.frame_shift - 1) -
1628 tu_log2(mclk_freq / sample_freq));
1629 } else if (audio->feedback.compute_method ==
1630 AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
1631 audio->feedback.compute.float_const =
1632 (float)sample_freq / (float)mclk_freq *
1633 (1UL << (16 - (audio->feedback.frame_shift - 1)));
1634 } else {
1635 audio->feedback.compute.fixed.sample_freq = sample_freq;
1636 audio->feedback.compute.fixed.mclk_freq = mclk_freq;
1637 }
1638
1639 return true;
1640 }
1641
1642 bool audiod_calc_tx_packet_sz(audiod_function_t* audio) {
1643 TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
1644 TU_VERIFY(audio->n_channels_tx);
1645 TU_VERIFY(audio->n_bytes_per_sample_tx);
1646 TU_VERIFY(audio->interval_tx);
1647 TU_VERIFY(audio->sample_rate_tx);
1648
1649 const uint8_t interval = (tud_speed_get() == TUSB_SPEED_FULL)
1650 ? audio->interval_tx
1651 : 1 << (audio->interval_tx - 1);
1652
1653 const uint16_t sample_normimal =
1654 (uint16_t)(audio->sample_rate_tx * interval /
1655 ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
1656 const uint16_t sample_reminder =
1657 (uint16_t)(audio->sample_rate_tx * interval %
1658 ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
1659
1660 const uint16_t packet_sz_tx_min =
1661 (uint16_t)((sample_normimal - 1) * audio->n_channels_tx *
1662 audio->n_bytes_per_sample_tx);
1663 const uint16_t packet_sz_tx_norm =
1664 (uint16_t)(sample_normimal * audio->n_channels_tx *
1665 audio->n_bytes_per_sample_tx);
1666 const uint16_t packet_sz_tx_max =
1667 (uint16_t)((sample_normimal + 1) * audio->n_channels_tx *
1668 audio->n_bytes_per_sample_tx);
1669
1670 // Endpoint size must larger than packet size
1671 TU_ASSERT(packet_sz_tx_max <= audio->ep_in_sz);
1672
1673 // Frmt20.pdf 2.3.1.1 USB Packets
1674 if (sample_reminder) {
1675 // All virtual frame packets must either contain INT(nav) audio slots
1676 // (small VFP) or INT(nav)+1 (large VFP) audio slots
1677 audio->packet_sz_tx[0] = packet_sz_tx_norm;
1678 audio->packet_sz_tx[1] = packet_sz_tx_norm;
1679 audio->packet_sz_tx[2] = packet_sz_tx_max;
1680 } else {
1681 // In the case where nav = INT(nav), ni may vary between INT(nav)-1 (small
1682 // VFP), INT(nav) (medium VFP) and INT(nav)+1 (large VFP).
1683 audio->packet_sz_tx[0] = packet_sz_tx_min;
1684 audio->packet_sz_tx[1] = packet_sz_tx_norm;
1685 audio->packet_sz_tx[2] = packet_sz_tx_max;
1686 }
1687
1688 return true;
1689 }
1690
1691 uint16_t tud_audio_n_write(uint8_t func_id, const void* data, uint16_t len) {
1692 TU_VERIFY(func_id < getAudioCount() && audiod_fct_[func_id].p_desc != NULL);
1693 return tu_fifo_write_n(&audiod_fct_[func_id].ep_in_ff, data, len);
1694 }
1695
1696 uint16_t tud_audio_n_available(uint8_t func_id) {
1697 TU_VERIFY(func_id < getAudioCount() && audiod_fct_[func_id].p_desc != NULL);
1698 return tu_fifo_count(&audiod_fct_[func_id].ep_out_ff);
1699 }
1700
1701 uint16_t tud_audio_n_read(uint8_t func_id, void* buffer, uint16_t bufsize) {
1702 TU_VERIFY(func_id < getAudioCount() && audiod_fct_[func_id].p_desc != NULL);
1703 return tu_fifo_read_n(&audiod_fct_[func_id].ep_out_ff, buffer, bufsize);
1704 }
1705};
1706
1707} // namespace audio_tools
1708
1709// Custom driver registration
1710extern usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* count) {
1712}
Definition USBAudio2DescriptorBuilder.h:12
USB Audio Device class for audio streaming over USB.
Definition USBAudioDevice.h:43
void setRxDoneCallback(std::function< bool(USBAudioDevice *, uint8_t, audiod_function_t *, uint16_t)> cb)
Register a callback for RX done events.
Definition USBAudioDevice.h:380
bool handleControlRequest(const tusb_control_request_t *request, void *buffer, uint16_t length)
Handle a USB control request for the audio device.
Definition USBAudioDevice.h:260
void setConfig(USBAudioConfig &cfg)
Set the USB audio configuration.
Definition USBAudioDevice.h:185
bool getEnableInterruptEp() const
Returns true if the interrupt endpoint is enabled.
Definition USBAudioDevice.h:198
void setReqEntityCallback(std::function< bool(USBAudioDevice *, uint8_t, tusb_control_request_t const *, uint8_t *)> cb)
Register a callback for entity set requests.
Definition USBAudioDevice.h:409
bool getEnableEpInFlowControl() const
Returns true if IN endpoint flow control is enabled.
Definition USBAudioDevice.h:194
void setIntDoneCallback(std::function< void(USBAudioDevice *, uint8_t)> cb)
Register a callback for interrupt done events.
Definition USBAudioDevice.h:363
void process()
Main processing loop for USB audio streaming.
Definition USBAudioDevice.h:286
bool getEnableFeedbackEp() const
Returns true if the feedback endpoint is enabled.
Definition USBAudioDevice.h:192
void setAudiodTxDoneCallback(std::function< bool(USBAudioDevice *, uint8_t, audiod_function_t *)> cb)
Register a callback for audio TX done events.
Definition USBAudioDevice.h:452
const uint8_t * getAudioDescriptors(uint8_t itf, uint8_t alt, uint16_t *out_length)
Get the USB audio descriptors for the specified interface and alternate setting.
Definition USBAudioDevice.h:213
bool getEnableEpOut() const
Returns true if the OUT endpoint is enabled.
Definition USBAudioDevice.h:190
static USBAudioDevice & instance()
Get the singleton instance of USBAudioDevice.
Definition USBAudioDevice.h:481
void setReqEpCallback(std::function< bool(USBAudioDevice *, uint8_t, tusb_control_request_t const *, uint8_t *)> cb)
Register a callback for endpoint set requests.
Definition USBAudioDevice.h:431
void setReqEntityCallback(std::function< bool(USBAudioDevice *, uint8_t)> cb)
Register a callback for entity requests.
Definition USBAudioDevice.h:390
uint8_t getAudioCount() const
Returns the number of audio functions configured.
Definition USBAudioDevice.h:203
bool getEnableEpIn() const
Returns true if the IN endpoint is enabled.
Definition USBAudioDevice.h:188
bool getEnableFifoMutex() const
Returns true if FIFO mutex is enabled.
Definition USBAudioDevice.h:200
void setGetReqEntityCallback(std::function< bool(USBAudioDevice *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for GET requests on an entity.
Definition USBAudioDevice.h:334
void setGetReqEpCallback(std::function< bool(USBAudioDevice *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for GET requests on an endpoint.
Definition USBAudioDevice.h:345
bool mounted() const
Returns true if the device is mounted by the USB host.
Definition USBAudioDevice.h:251
void setAudioFeedbackFormatCorrectionCallback(std::function< bool(USBAudioDevice *, uint8_t)> cb)
Register a callback for audio feedback format correction events.
Definition USBAudioDevice.h:472
void setTxDoneCallback(std::function< bool(USBAudioDevice *, uint8_t, audiod_function_t *)> cb)
Register a callback for TX done events.
Definition USBAudioDevice.h:371
void setTxCallback(std::function< uint16_t(const uint8_t *, uint16_t)> cb)
Register a callback for transmitting audio data (IN endpoint).
Definition USBAudioDevice.h:316
void setAudioFeedbackParamsCallback(std::function< void(USBAudioDevice *, uint8_t, uint8_t, audio_feedback_params_t *)> cb)
Register a callback for audio feedback parameter events.
Definition USBAudioDevice.h:461
void setFbDoneCallback(std::function< void(USBAudioDevice *, uint8_t)> cb)
Register a callback for feedback done events.
Definition USBAudioDevice.h:355
void setRxCallback(std::function< void(const uint8_t *, uint16_t)> cb)
Register a callback for received audio data (OUT endpoint).
Definition USBAudioDevice.h:307
void setGetReqItfCallback(std::function< bool(USBAudioDevice *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for GET requests on the interface.
Definition USBAudioDevice.h:324
usbd_class_driver_t const * usbd_app_driver_get(uint8_t *count)
Get the USB device class driver for TinyUSB integration.
Definition USBAudioDevice.h:491
void setTudAudioSetItfCallback(std::function< bool(USBAudioDevice *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for interface set requests.
Definition USBAudioDevice.h:398
void setReqItfCallback(std::function< bool(USBAudioDevice *, uint8_t, tusb_control_request_t const *, uint8_t *)> cb)
Register a callback for interface set requests.
Definition USBAudioDevice.h:420
void setItfCloseEpCallback(std::function< bool(USBAudioDevice *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for interface close endpoint events.
Definition USBAudioDevice.h:442
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:30
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Configuration structure for USB Audio, inheriting from AudioInfo.
Definition USBAudioConfig.h:30