blob: ed13ce993e8400b12cd736ff320c61ef97096417 [file] [log] [blame]
Austin Schuh41baf202022-01-01 14:33:40 -08001/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2020 Reinhard Panhuber
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 */
25
26/* plot_audio_samples.py requires following modules:
27 * $ sudo apt install libportaudio
28 * $ pip3 install sounddevice matplotlib
29 *
30 * Then run
31 * $ python3 plot_audio_samples.py
32 */
33
34#include <stdlib.h>
35#include <stdio.h>
36#include <string.h>
37
38#include "bsp/board.h"
39#include "tusb.h"
40
41//--------------------------------------------------------------------+
42// MACRO CONSTANT TYPEDEF PROTYPES
43//--------------------------------------------------------------------+
44
45#ifndef AUDIO_SAMPLE_RATE
46#define AUDIO_SAMPLE_RATE 48000
47#endif
48
49/* Blink pattern
50 * - 250 ms : device not mounted
51 * - 1000 ms : device mounted
52 * - 2500 ms : device is suspended
53 */
54enum {
55 BLINK_NOT_MOUNTED = 250,
56 BLINK_MOUNTED = 1000,
57 BLINK_SUSPENDED = 2500,
58};
59
60static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
61
62// Audio controls
63// Current states
64bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
65uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
66uint32_t sampFreq;
67uint8_t clkValid;
68
69// Range states
70audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
71audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
72
73// Audio test data
74uint16_t test_buffer_audio[CFG_TUD_AUDIO_EP_SZ_IN/2];
75uint16_t startVal = 0;
76
77void led_blinking_task(void);
78void audio_task(void);
79
80/*------------- MAIN -------------*/
81int main(void)
82{
83 board_init();
84
85 tusb_init();
86
87 // Init values
88 sampFreq = AUDIO_SAMPLE_RATE;
89 clkValid = 1;
90
91 sampleFreqRng.wNumSubRanges = 1;
92 sampleFreqRng.subrange[0].bMin = AUDIO_SAMPLE_RATE;
93 sampleFreqRng.subrange[0].bMax = AUDIO_SAMPLE_RATE;
94 sampleFreqRng.subrange[0].bRes = 0;
95
96 while (1)
97 {
98 tud_task(); // tinyusb device task
99 led_blinking_task();
100 audio_task();
101 }
102
103
104 return 0;
105}
106
107//--------------------------------------------------------------------+
108// Device callbacks
109//--------------------------------------------------------------------+
110
111// Invoked when device is mounted
112void tud_mount_cb(void)
113{
114 blink_interval_ms = BLINK_MOUNTED;
115}
116
117// Invoked when device is unmounted
118void tud_umount_cb(void)
119{
120 blink_interval_ms = BLINK_NOT_MOUNTED;
121}
122
123// Invoked when usb bus is suspended
124// remote_wakeup_en : if host allow us to perform remote wakeup
125// Within 7ms, device must draw an average of current less than 2.5 mA from bus
126void tud_suspend_cb(bool remote_wakeup_en)
127{
128 (void) remote_wakeup_en;
129 blink_interval_ms = BLINK_SUSPENDED;
130}
131
132// Invoked when usb bus is resumed
133void tud_resume_cb(void)
134{
135 blink_interval_ms = BLINK_MOUNTED;
136}
137
138//--------------------------------------------------------------------+
139// AUDIO Task
140//--------------------------------------------------------------------+
141
142void audio_task(void)
143{
144 // Yet to be filled - e.g. put meas data into TX FIFOs etc.
145 // asm("nop");
146}
147
148//--------------------------------------------------------------------+
149// Application Callback API Implementations
150//--------------------------------------------------------------------+
151
152// Invoked when audio class specific set request received for an EP
153bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
154{
155 (void) rhport;
156 (void) pBuff;
157
158 // We do not support any set range requests here, only current value requests
159 TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
160
161 // Page 91 in UAC2 specification
162 uint8_t channelNum = TU_U16_LOW(p_request->wValue);
163 uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
164 uint8_t ep = TU_U16_LOW(p_request->wIndex);
165
166 (void) channelNum; (void) ctrlSel; (void) ep;
167
168 return false; // Yet not implemented
169}
170
171// Invoked when audio class specific set request received for an interface
172bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
173{
174 (void) rhport;
175 (void) pBuff;
176
177 // We do not support any set range requests here, only current value requests
178 TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
179
180 // Page 91 in UAC2 specification
181 uint8_t channelNum = TU_U16_LOW(p_request->wValue);
182 uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
183 uint8_t itf = TU_U16_LOW(p_request->wIndex);
184
185 (void) channelNum; (void) ctrlSel; (void) itf;
186
187 return false; // Yet not implemented
188}
189
190// Invoked when audio class specific set request received for an entity
191bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
192{
193 (void) rhport;
194
195 // Page 91 in UAC2 specification
196 uint8_t channelNum = TU_U16_LOW(p_request->wValue);
197 uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
198 uint8_t itf = TU_U16_LOW(p_request->wIndex);
199 uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
200
201 (void) itf;
202
203 // We do not support any set range requests here, only current value requests
204 TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
205
206 // If request is for our feature unit
207 if ( entityID == 2 )
208 {
209 switch ( ctrlSel )
210 {
211 case AUDIO_FU_CTRL_MUTE:
212 // Request uses format layout 1
213 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
214
215 mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
216
217 TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
218 return true;
219
220 case AUDIO_FU_CTRL_VOLUME:
221 // Request uses format layout 2
222 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
223
224 volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur;
225
226 TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
227 return true;
228
229 // Unknown/Unsupported control
230 default:
231 TU_BREAKPOINT();
232 return false;
233 }
234 }
235 return false; // Yet not implemented
236}
237
238// Invoked when audio class specific get request received for an EP
239bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
240{
241 (void) rhport;
242
243 // Page 91 in UAC2 specification
244 uint8_t channelNum = TU_U16_LOW(p_request->wValue);
245 uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
246 uint8_t ep = TU_U16_LOW(p_request->wIndex);
247
248 (void) channelNum; (void) ctrlSel; (void) ep;
249
250 // return tud_control_xfer(rhport, p_request, &tmp, 1);
251
252 return false; // Yet not implemented
253}
254
255// Invoked when audio class specific get request received for an interface
256bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
257{
258 (void) rhport;
259
260 // Page 91 in UAC2 specification
261 uint8_t channelNum = TU_U16_LOW(p_request->wValue);
262 uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
263 uint8_t itf = TU_U16_LOW(p_request->wIndex);
264
265 (void) channelNum; (void) ctrlSel; (void) itf;
266
267 return false; // Yet not implemented
268}
269
270// Invoked when audio class specific get request received for an entity
271bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
272{
273 (void) rhport;
274
275 // Page 91 in UAC2 specification
276 uint8_t channelNum = TU_U16_LOW(p_request->wValue);
277 uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
278 // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since we have only one audio function implemented, we do not need the itf value
279 uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
280
281 // Input terminal (Microphone input)
282 if (entityID == 1)
283 {
284 switch ( ctrlSel )
285 {
286 case AUDIO_TE_CTRL_CONNECTOR:
287 {
288 // The terminal connector control only has a get request with only the CUR attribute.
289 audio_desc_channel_cluster_t ret;
290
291 // Those are dummy values for now
292 ret.bNrChannels = 1;
293 ret.bmChannelConfig = 0;
294 ret.iChannelNames = 0;
295
296 TU_LOG2(" Get terminal connector\r\n");
297
298 return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
299 }
300 break;
301
302 // Unknown/Unsupported control selector
303 default:
304 TU_BREAKPOINT();
305 return false;
306 }
307 }
308
309 // Feature unit
310 if (entityID == 2)
311 {
312 switch ( ctrlSel )
313 {
314 case AUDIO_FU_CTRL_MUTE:
315 // Audio control mute cur parameter block consists of only one byte - we thus can send it right away
316 // There does not exist a range parameter block for mute
317 TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
318 return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
319
320 case AUDIO_FU_CTRL_VOLUME:
321 switch ( p_request->bRequest )
322 {
323 case AUDIO_CS_REQ_CUR:
324 TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
325 return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
326
327 case AUDIO_CS_REQ_RANGE:
328 TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
329
330 // Copy values - only for testing - better is version below
331 audio_control_range_2_n_t(1)
332 ret;
333
334 ret.wNumSubRanges = 1;
335 ret.subrange[0].bMin = -90; // -90 dB
336 ret.subrange[0].bMax = 90; // +90 dB
337 ret.subrange[0].bRes = 1; // 1 dB steps
338
339 return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
340
341 // Unknown/Unsupported control
342 default:
343 TU_BREAKPOINT();
344 return false;
345 }
346 break;
347
348 // Unknown/Unsupported control
349 default:
350 TU_BREAKPOINT();
351 return false;
352 }
353 }
354
355 // Clock Source unit
356 if ( entityID == 4 )
357 {
358 switch ( ctrlSel )
359 {
360 case AUDIO_CS_CTRL_SAM_FREQ:
361 // channelNum is always zero in this case
362 switch ( p_request->bRequest )
363 {
364 case AUDIO_CS_REQ_CUR:
365 TU_LOG2(" Get Sample Freq.\r\n");
366 return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
367
368 case AUDIO_CS_REQ_RANGE:
369 TU_LOG2(" Get Sample Freq. range\r\n");
370 return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
371
372 // Unknown/Unsupported control
373 default:
374 TU_BREAKPOINT();
375 return false;
376 }
377 break;
378
379 case AUDIO_CS_CTRL_CLK_VALID:
380 // Only cur attribute exists for this request
381 TU_LOG2(" Get Sample Freq. valid\r\n");
382 return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
383
384 // Unknown/Unsupported control
385 default:
386 TU_BREAKPOINT();
387 return false;
388 }
389 }
390
391 TU_LOG2(" Unsupported entity: %d\r\n", entityID);
392 return false; // Yet not implemented
393}
394
395bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
396{
397 (void) rhport;
398 (void) itf;
399 (void) ep_in;
400 (void) cur_alt_setting;
401
402 tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN);
403
404 return true;
405}
406
407bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
408{
409 (void) rhport;
410 (void) n_bytes_copied;
411 (void) itf;
412 (void) ep_in;
413 (void) cur_alt_setting;
414
415 for (size_t cnt = 0; cnt < CFG_TUD_AUDIO_EP_SZ_IN/2; cnt++)
416 {
417 test_buffer_audio[cnt] = startVal++;
418 }
419
420 return true;
421}
422
423bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
424{
425 (void) rhport;
426 (void) p_request;
427 startVal = 0;
428
429 return true;
430}
431
432//--------------------------------------------------------------------+
433// BLINKING TASK
434//--------------------------------------------------------------------+
435void led_blinking_task(void)
436{
437 static uint32_t start_ms = 0;
438 static bool led_state = false;
439
440 // Blink every interval ms
441 if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
442 start_ms += blink_interval_ms;
443
444 board_led_write(led_state);
445 led_state = 1 - led_state; // toggle
446}