A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

usbaudio: convert to asynchronous operation

Add feedback not based on samples used, but on buffers filled - idea
being we can do "PID" (someone who has actually implemented Real PID
could probably rewrite the calculation) based on how many buffers
we have filled versus the ideal buffer filled level (16).

Feedback is based on a historical average of the last two feedback
intervals.

This feedback math is done as fixed-point math to keep floats out of core. Note that a couple division operations needed to be strategically staged to avoid overflow or truncation.

Floats are still used for debug screen printout.

Also fixed a typo in the definition of usb_audio_control_request()

Host:
Linux: works
MacOS: works
Windows: Feedback does not work! It appears that Windows may not
support asynchronous devices at all. Playback may "work",
but results will vary as the number of buffers filled will
drift over time.

Change-Id: I027feb16705e6e46c1144b1d08920b53de42cb26

+439 -65
+6 -1
apps/debug_menu.c
··· 2542 simplelist_addline("out ep: 0x%X in ep: 0x%X", usb_audio_get_out_ep(), usb_audio_get_in_ep()); 2543 simplelist_addline("Volume: %d", usb_audio_get_cur_volume()); 2544 simplelist_addline("Playback Frequency: %lu", usb_audio_get_playback_sampling_frequency()); 2545 - simplelist_addline("Buffers filled: %d", usb_audio_get_prebuffering()); 2546 simplelist_addline("%s", usb_audio_get_underflow()?"UNDERFLOW!":" "); 2547 simplelist_addline("%s", usb_audio_get_overflow()?"OVERFLOW!":" "); 2548 simplelist_addline("%s", usb_audio_get_alloc_failed()?"ALLOC FAILED!":" ");
··· 2542 simplelist_addline("out ep: 0x%X in ep: 0x%X", usb_audio_get_out_ep(), usb_audio_get_in_ep()); 2543 simplelist_addline("Volume: %d", usb_audio_get_cur_volume()); 2544 simplelist_addline("Playback Frequency: %lu", usb_audio_get_playback_sampling_frequency()); 2545 + simplelist_addline("Frames dropped: %d", usb_audio_get_frames_dropped()); 2546 + simplelist_addline("Buffers filled: %f", (double)usb_audio_get_prebuffering_avg()/(1<<16)); // convert from 16.16 fixed to float 2547 + simplelist_addline("Min: %d / Max: %d", usb_audio_get_prebuffering_maxmin(false), usb_audio_get_prebuffering_maxmin(true)); 2548 + simplelist_addline("Samples used per Frame: %f", (double)usb_audio_get_samplesperframe()/(1<<16)); // convert from 16.16 fixed to float 2549 + simplelist_addline("Samples received per frame: %f", (double)usb_audio_get_samples_rx_perframe()/(1<<16)); // convert from 16.16 fixed to float 2550 + simplelist_addline("Samples diff: %f", (double)(usb_audio_get_samplesperframe()-usb_audio_get_samples_rx_perframe())/(1<<16)); // convert from 16.16 fixed to float 2551 simplelist_addline("%s", usb_audio_get_underflow()?"UNDERFLOW!":" "); 2552 simplelist_addline("%s", usb_audio_get_overflow()?"OVERFLOW!":" "); 2553 simplelist_addline("%s", usb_audio_get_alloc_failed()?"ALLOC FAILED!":" ");
+4
firmware/export/usb_ch9.h
··· 345 #define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2) 346 #define USB_ENDPOINT_SYNC_SYNC (3 << 2) 347 348 #define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ 349 #define USB_ENDPOINT_XFER_CONTROL 0 350 #define USB_ENDPOINT_XFER_ISOC 1
··· 345 #define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2) 346 #define USB_ENDPOINT_SYNC_SYNC (3 << 2) 347 348 + #define USB_ENDPOINT_USAGE_DATA (0 << 4) /* in bmAttributes */ 349 + #define USB_ENDPOINT_USAGE_FEEDBACK (1 << 4) 350 + #define USB_ENDPOINT_USAGE_IMPLICITFEEDBACK (2 << 4) 351 + 352 #define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ 353 #define USB_ENDPOINT_XFER_CONTROL 0 354 #define USB_ENDPOINT_XFER_ISOC 1
+329 -57
firmware/usbstack/usb_audio.c
··· 16 * KIND, either express or implied. 17 * 18 ****************************************************************************/ 19 #include "string.h" 20 #include "system.h" 21 #include "usb_core.h" ··· 37 #define LOGF_ENABLE 38 #include "logf.h" 39 40 /* Audio Control Interface */ 41 static struct usb_interface_descriptor 42 ac_interface = ··· 58 .bLength = USB_AC_SIZEOF_HEADER(1), /* one interface */ 59 .bDescriptorType = USB_DT_CS_INTERFACE, 60 .bDescriptorSubType = USB_AC_HEADER, 61 - .bcdADC = 0x0100, 62 .wTotalLength = 0, /* fill later */ 63 .bInCollection = 1, /* one interface */ 64 .baInterfaceNr = {0}, /* fill later */ ··· 140 .bDescriptorType = USB_DT_INTERFACE, 141 .bInterfaceNumber = 0, 142 .bAlternateSetting = 1, 143 - .bNumEndpoints = 1, 144 .bInterfaceClass = USB_CLASS_AUDIO, 145 .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 146 .bInterfaceProtocol = 0, ··· 176 } 177 }; 178 179 - /* 180 - * TODO: 181 - * It appears that "Adaptive" sync mode means it it the device's duty 182 - * to adapt its consumption rate of data to whatever the host sends, which 183 - * has the possibility to cause trouble in underflows/overflows, etc. 184 - * In practice, this is probably not a large concern, as we have a fairly large 185 - * amount of buffering in the PCM system. 186 - * An improvement may be to use "Asynchronous", but this is more complicated 187 - * due to the need to inform the host about how fast the device will consume 188 - * the data. 189 - * So implementation of "Asynchronous" mode will be left for a later improvement. 190 - */ 191 - static struct usb_iso_audio_endpoint_descriptor 192 - out_iso_ep = 193 { 194 - .bLength = sizeof(struct usb_iso_audio_endpoint_descriptor), 195 .bDescriptorType = USB_DT_ENDPOINT, 196 .bEndpointAddress = USB_DIR_OUT, /* filled later */ 197 - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE, 198 .wMaxPacketSize = 0, /* filled later */ 199 - .bInterval = 0, /* filled later */ 200 .bRefresh = 0, 201 - .bSynchAddress = 0 /* filled later */ 202 }; 203 204 - static struct usb_as_iso_endpoint 205 - as_out_iso_ep = 206 { 207 - .bLength = sizeof(struct usb_as_iso_endpoint), 208 .bDescriptorType = USB_DT_CS_ENDPOINT, 209 .bDescriptorSubType = USB_AS_EP_GENERAL, 210 .bmAttributes = USB_AS_EP_CS_SAMPLING_FREQ_CTL, ··· 216 { 217 (struct usb_descriptor_header *) &ac_header, 218 (struct usb_descriptor_header *) &ac_playback_input, 219 (struct usb_descriptor_header *) &ac_playback_output, 220 - (struct usb_descriptor_header *) &ac_playback_feature 221 }; 222 223 #define AC_CS_DESCRIPTORS_LIST_SIZE (sizeof(ac_cs_descriptors_list)/sizeof(ac_cs_descriptors_list[0])) 224 225 static const struct usb_descriptor_header* const usb_descriptors_list[] = 226 { 227 /* Audio Control */ ··· 237 (struct usb_descriptor_header *) &as_interface_alt_playback, 238 (struct usb_descriptor_header *) &as_playback_cs_interface, 239 (struct usb_descriptor_header *) &as_playback_format_type_i, 240 - (struct usb_descriptor_header *) &out_iso_ep, 241 - (struct usb_descriptor_header *) &as_out_iso_ep, 242 }; 243 244 #define USB_DESCRIPTORS_LIST_SIZE (sizeof(usb_descriptors_list)/sizeof(usb_descriptors_list[0])) ··· 249 static int as_playback_freq_idx; /* audio playback streaming frequency index (in hw_freq_sampr) */ 250 251 static int out_iso_ep_adr; /* output isochronous endpoint */ 252 - static int in_iso_ep_adr; /* input isochronous endpoint */ 253 254 /* small buffer used for control transfers */ 255 static unsigned char usb_buffer[128] USB_DEVBSS_ATTR; ··· 314 /* usb overflow ? */ 315 bool usb_rx_overflow; 316 317 /* Schematic view of the RX situation: 318 * (in case NR_BUFFERS = 4) 319 * ··· 352 return arr[0] | (arr[1] << 8) | (arr[2] << 16); 353 } 354 355 static void set_playback_sampling_frequency(unsigned long f) 356 { 357 // only values 44.1k and higher (array is in descending order) ··· 373 374 unsigned long usb_audio_get_playback_sampling_frequency(void) 375 { 376 - logf("usbaudio: get playback sampl freq %lu Hz", 377 - hw_freq_sampr[as_playback_freq_idx]); 378 return hw_freq_sampr[as_playback_freq_idx]; 379 } 380 ··· 382 { 383 unsigned int i; 384 /* initialized tSamFreq array */ 385 - logf("usbaudio: supported frequencies"); 386 // only values 44.1k and higher (array is in descending order) 387 for(i = 0; i <= HW_FREQ_44; i++) 388 { ··· 393 394 int usb_audio_request_buf(void) 395 { 396 - 397 // stop playback first thing 398 audio_stop(); 399 ··· 439 return -1; 440 } 441 442 - in_iso_ep_adr = usb_core_request_endpoint(USB_ENDPOINT_XFER_ISOC, USB_DIR_IN, drv); 443 - if(in_iso_ep_adr < 0) 444 { 445 usb_core_release_endpoint(out_iso_ep_adr); 446 - logf("usbaudio: cannot get an out iso endpoint"); 447 return -1; 448 } 449 450 - logf("usbaudio: iso out ep is 0x%x, in ep is 0x%x", out_iso_ep_adr, in_iso_ep_adr); 451 452 - out_iso_ep.bEndpointAddress = out_iso_ep_adr; 453 - out_iso_ep.bSynchAddress = 0; 454 455 return 0; 456 } ··· 462 463 unsigned int usb_audio_get_in_ep(void) 464 { 465 - return in_iso_ep_adr; 466 } 467 468 int usb_audio_set_first_interface(int interface) ··· 478 unsigned int i; 479 unsigned char *orig_dest = dest; 480 481 - // logf("get config descriptors"); 482 483 /** Configuration */ 484 ··· 498 as_interface_alt_playback.bInterfaceNumber = usb_interface + 1; 499 500 /* endpoints */ 501 - out_iso_ep.wMaxPacketSize = 1023; /* one micro-frame per transaction */ 502 /** Endpoint Interval calculation: 503 * typically sampling frequency is 44100 Hz and top is 192000 Hz, which 504 * account for typical 44100*2(stereo)*2(16-bit) ~= 180 kB/s 505 - * and top 770 kB/s. Since there are 1000 frames per seconds and maximum 506 * packet size is set to 1023, one transaction per frame is good enough 507 - * for over 1 MB/s. At high-speed, add 3 to this value because there are 508 - * 8 = 2^3 micro-frames per frame. 509 * Recall that actual is 2^(bInterval - 1) */ 510 - out_iso_ep.bInterval = usb_drv_port_speed() ? 4 : 1; 511 512 /** Packing */ 513 for(i = 0; i < USB_DESCRIPTORS_LIST_SIZE; i++) ··· 535 *start = rx_buffer + (rx_play_idx * REAL_BUF_SIZE); 536 *size = rx_buf_size[rx_play_idx]; 537 rx_play_idx = (rx_play_idx + 1) % NR_BUFFERS; 538 /* if usb RX buffers had overflowed, we can start to receive again 539 * guard against IRQ to avoid race with completion usb completion (although 540 * this function is probably running in IRQ context anyway) */ ··· 556 rx_play_idx = 0; 557 rx_usb_idx = 0; 558 559 // TODO: implement recording from the USB stream 560 #if (INPUT_SRC_CAPS != 0) 561 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); ··· 570 571 static void usb_audio_stop_playback(void) 572 { 573 - // logf("usbaudio: stop playback"); 574 if(usb_audio_playing) 575 { 576 mixer_channel_stop(PCM_MIXER_CHAN_USBAUDIO); 577 usb_audio_playing = false; 578 } 579 } 580 581 int usb_audio_set_interface(int intf, int alt) ··· 642 { 643 return usb_as_playback_intf_alt; 644 } 645 646 - static bool usb_audio_as_playback_endpoint_request(struct usb_ctrlrequest* req, void *reqdata) 647 { 648 /* only support sampling frequency */ 649 if(req->wValue != (USB_AS_EP_CS_SAMPLING_FREQ_CTL << 8)) ··· 707 int ep = req->wIndex & 0xff; 708 709 if(ep == out_iso_ep_adr) 710 - return usb_audio_as_playback_endpoint_request(req, reqdata); 711 else 712 { 713 logf("usbaudio: unhandled ep req (ep=%d)", ep); ··· 727 { 728 logf("usbaudio: mute !"); 729 tmp_saved_vol = sound_current(SOUND_VOLUME); 730 - // sound_set_volume(sound_min(SOUND_VOLUME)); 731 // setvol does range checking for us! 732 global_status.volume = sound_min(SOUND_VOLUME); 733 setvol(); ··· 736 else if(value == 0) 737 { 738 logf("usbaudio: not muted !"); 739 - // sound_set_volume(tmp_saved_vol); 740 // setvol does range checking for us! 741 global_status.volume = tmp_saved_vol; 742 setvol(); ··· 808 809 logf("usbaudio: set volume=%d dB", usb_audio_volume_to_db(value, sound_numdecimals(SOUND_VOLUME))); 810 811 - // sound_set_volume(usb_audio_volume_to_db(value, sound_numdecimals(SOUND_VOLUME))); 812 // setvol does range checking for us! 813 // we cannot guarantee the host will send us a volume within our range 814 global_status.volume = usb_audio_volume_to_db(value, sound_numdecimals(SOUND_VOLUME)); ··· 829 return false; 830 } 831 832 - logf("usbaudio: get %s volume=%d dB", usb_audio_ac_ctl_req_str(cmd), usb_audio_volume_to_db(*value, sound_numdecimals(SOUND_VOLUME))); 833 return true; 834 } 835 ··· 856 return false; 857 } 858 /* selectors */ 859 - /* all send/received values are integers so already read data if necessary and store in it in an integer */ 860 if(req->bRequest & USB_AC_GET_REQ) 861 { 862 /* get */ ··· 987 } 988 } 989 990 - bool usb_audio_control_request(struct usb_ctrlrequest* req, void *reqdata) 991 { 992 (void) reqdata; 993 994 switch(req->bRequestType & USB_RECIP_MASK) 995 { ··· 1034 /* determine if enough prebuffering has been done to restart audio */ 1035 bool prebuffering_done(void) 1036 { 1037 - /* restart audio if at least two buffers are filled */ 1038 int diff = (rx_usb_idx - rx_play_idx + NR_BUFFERS) % NR_BUFFERS; 1039 return diff >= MINIMUM_BUFFERS_QUEUED; 1040 } ··· 1044 return (rx_usb_idx - rx_play_idx + NR_BUFFERS) % NR_BUFFERS; 1045 } 1046 1047 bool usb_audio_get_underflow(void) 1048 { 1049 return playback_audio_underflow; ··· 1052 bool usb_audio_get_overflow(void) 1053 { 1054 return usb_rx_overflow; 1055 } 1056 1057 void usb_audio_transfer_complete(int ep, int dir, int status, int length) ··· 1067 bool usb_audio_fast_transfer_complete(int ep, int dir, int status, int length) 1068 { 1069 (void) dir; 1070 1071 if(ep == out_iso_ep_adr && usb_as_playback_intf_alt == 1) 1072 { 1073 - // logf("usbaudio: frame: %d", usb_drv_get_frame_number()); 1074 if(status != 0) 1075 return true; /* FIXME how to handle error here ? */ 1076 /* store length, queue buffer */ 1077 rx_buf_size[rx_usb_idx] = length; 1078 rx_usb_idx = (rx_usb_idx + 1) % NR_BUFFERS; 1079 /* guard against IRQ to avoid race with completion audio completion */ 1080 int oldlevel = disable_irq_save(); 1081 /* setup a new transaction except if we ran out of buffers */ ··· 1098 mixer_channel_play_data(PCM_MIXER_CHAN_USBAUDIO, playback_audio_get_more, NULL, 0); 1099 } 1100 restore_irq(oldlevel); 1101 - return true; 1102 } 1103 else 1104 - return false; 1105 }
··· 16 * KIND, either express or implied. 17 * 18 ****************************************************************************/ 19 + 20 + /* NOTE 21 + * 22 + * This is USBAudio 1.0. USBAudio 2.0 is notably _not backwards compatible!_ 23 + * USBAudio 1.0 over _USB_ 2.0 is perfectly valid! 24 + * 25 + * Relevant specifications are USB 2.0 and USB Audio Class 1.0. 26 + */ 27 + 28 #include "string.h" 29 #include "system.h" 30 #include "usb_core.h" ··· 46 #define LOGF_ENABLE 47 #include "logf.h" 48 49 + // is there a "best practices" for converting between floats and fixed point? 50 + // NOTE: SIGNED 51 + #define TO_16DOT16_FIXEDPT(val) ((int32_t)(val) * (1<<16)) 52 + #define TO_DOUBLE(val) ((double)(val) / (1<<16)) 53 + 54 /* Audio Control Interface */ 55 static struct usb_interface_descriptor 56 ac_interface = ··· 72 .bLength = USB_AC_SIZEOF_HEADER(1), /* one interface */ 73 .bDescriptorType = USB_DT_CS_INTERFACE, 74 .bDescriptorSubType = USB_AC_HEADER, 75 + .bcdADC = 0x0100, /* Identifies this as usb audio class 1.0 */ 76 .wTotalLength = 0, /* fill later */ 77 .bInCollection = 1, /* one interface */ 78 .baInterfaceNr = {0}, /* fill later */ ··· 154 .bDescriptorType = USB_DT_INTERFACE, 155 .bInterfaceNumber = 0, 156 .bAlternateSetting = 1, 157 + .bNumEndpoints = 2, // iso audio, iso feedback 158 .bInterfaceClass = USB_CLASS_AUDIO, 159 .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 160 .bInterfaceProtocol = 0, ··· 190 } 191 }; 192 193 + static struct usb_as_iso_audio_endpoint 194 + as_iso_audio_out_ep = 195 { 196 + .bLength = sizeof(struct usb_as_iso_audio_endpoint), 197 .bDescriptorType = USB_DT_ENDPOINT, 198 .bEndpointAddress = USB_DIR_OUT, /* filled later */ 199 + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_USAGE_DATA, 200 .wMaxPacketSize = 0, /* filled later */ 201 + .bInterval = 0, /* filled later - 1 for full speed, 4 for high-speed */ 202 .bRefresh = 0, 203 + .bSynchAddress = 0 /* filled later to the address of as_iso_synch_in_ep */ 204 + }; 205 + 206 + /* 207 + * Updaing the desired sample frequency: 208 + * 209 + * The iso OUT ep is inextricably linked to the feedback iso IN ep 210 + * when using Asynchronous mode. It periodically describes to the host 211 + * how fast to send the data. 212 + * 213 + * Some notes from the usbaudio 1.0 documentation: 214 + * - bSyncAddress of the iso OUT ep must be set to the address of the iso IN feedback ep 215 + * - bSyncAddress of the iso IN feedback ep must be zero 216 + * - F_f (desired sampling frequency) describes directly the number of samples the endpoint 217 + * wants to receive per frame to match the actual sampling frequency F_s 218 + * - There is a value, (2^(10-P)), which is how often (in 1mS frames) the F_f value will be sent 219 + * - P appears to be somewhat arbitrary, though the spec wants it to relate the real sample rate 220 + * F_s to the master clock rate F_m by the relationship (F_m = F_s * (2^(P-1))) 221 + * - The above description of P is somewhat moot because of how much buffering we have. I suspect it 222 + * was written for devices with essentially zero buffering. 223 + * - bRefresh of the feedback endpoint descriptor should be set to (10-P). This can range from 1 to 9. 224 + * A value of 1 would mean refreshing every 2^1 mS = 2 mS, a value of 9 would mean refreshing every 225 + * 2^9 mS = 512 mS. 226 + * - The F_f value should be encoded in "10.10" format, but justified to the leftmost 24 bits, 227 + * so it ends up looking like "10.14" format. This format is 3 bytes long. On USB 2.0, it seems that 228 + * the USB spec overrides the UAC 1.0 spec here, so high-speed bus operation needs "16.16" format, 229 + * in a 4 byte packet. 230 + */ 231 + #define FEEDBACK_UPDATE_RATE_P 5 232 + #define FEEDBACK_UPDATE_RATE_REFRESH (10-FEEDBACK_UPDATE_RATE_P) 233 + #define FEEDBACK_UPDATE_RATE_FRAMES (0x1<<FEEDBACK_UPDATE_RATE_REFRESH) 234 + static struct usb_as_iso_synch_endpoint 235 + as_iso_synch_in_ep = 236 + { 237 + .bLength = sizeof(struct usb_as_iso_synch_endpoint), 238 + .bDescriptorType = USB_DT_ENDPOINT, 239 + .bEndpointAddress = USB_DIR_IN, /* filled later */ 240 + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_NONE | USB_ENDPOINT_USAGE_FEEDBACK, 241 + .wMaxPacketSize = 4, 242 + .bInterval = 0, /* filled later - 1 or 4 depending on bus speed */ 243 + .bRefresh = FEEDBACK_UPDATE_RATE_REFRESH, /* This describes how often this ep will update F_f (see above) */ 244 + .bSynchAddress = 0 /* MUST be zero! */ 245 }; 246 247 + static struct usb_as_iso_ctrldata_endpoint 248 + as_iso_ctrldata_samfreq = 249 { 250 + .bLength = sizeof(struct usb_as_iso_ctrldata_endpoint), 251 .bDescriptorType = USB_DT_CS_ENDPOINT, 252 .bDescriptorSubType = USB_AS_EP_GENERAL, 253 .bmAttributes = USB_AS_EP_CS_SAMPLING_FREQ_CTL, ··· 259 { 260 (struct usb_descriptor_header *) &ac_header, 261 (struct usb_descriptor_header *) &ac_playback_input, 262 + (struct usb_descriptor_header *) &ac_playback_feature, 263 (struct usb_descriptor_header *) &ac_playback_output, 264 }; 265 266 #define AC_CS_DESCRIPTORS_LIST_SIZE (sizeof(ac_cs_descriptors_list)/sizeof(ac_cs_descriptors_list[0])) 267 268 + // TODO: AudioControl Interrupt endpoint to inform the host that changes were made on-device! 269 + // The most immediately useful of this capability is volume and mute changes. 270 static const struct usb_descriptor_header* const usb_descriptors_list[] = 271 { 272 /* Audio Control */ ··· 282 (struct usb_descriptor_header *) &as_interface_alt_playback, 283 (struct usb_descriptor_header *) &as_playback_cs_interface, 284 (struct usb_descriptor_header *) &as_playback_format_type_i, 285 + /* NOTE: the order of these three is important for maximum compatibility. 286 + * Synch ep should follow iso out ep, with ctrldata descriptor coming first. */ 287 + (struct usb_descriptor_header *) &as_iso_ctrldata_samfreq, 288 + (struct usb_descriptor_header *) &as_iso_audio_out_ep, 289 + (struct usb_descriptor_header *) &as_iso_synch_in_ep, 290 }; 291 292 #define USB_DESCRIPTORS_LIST_SIZE (sizeof(usb_descriptors_list)/sizeof(usb_descriptors_list[0])) ··· 297 static int as_playback_freq_idx; /* audio playback streaming frequency index (in hw_freq_sampr) */ 298 299 static int out_iso_ep_adr; /* output isochronous endpoint */ 300 + static int in_iso_feedback_ep_adr; /* input feedback isochronous endpoint */ 301 302 /* small buffer used for control transfers */ 303 static unsigned char usb_buffer[128] USB_DEVBSS_ATTR; ··· 362 /* usb overflow ? */ 363 bool usb_rx_overflow; 364 365 + /* feedback variables */ 366 + #define USB_FRAME_MAX 0x7FF 367 + #define NR_SAMPLES_HISTORY 32 368 + int32_t samples_fb; 369 + int32_t buffers_filled_old; 370 + long buffers_filled_accumulator; 371 + long buffers_filled_accumulator_old; 372 + int buffers_filled_avgcount; 373 + int buffers_filled_avgcount_old; 374 + static uint8_t sendFf[4] USB_DEVBSS_ATTR; 375 + static bool sent_fb_this_frame = false; 376 + int fb_startframe = 0; 377 + bool send_fb = false; 378 + 379 + /* debug screen sample count display variables */ 380 + static unsigned long samples_received; 381 + static unsigned long samples_received_last; 382 + int32_t samples_received_report; 383 + int buffers_filled_min; 384 + int buffers_filled_min_last; 385 + int buffers_filled_max; 386 + int buffers_filled_max_last; 387 + 388 + /* frame drop recording variables */ 389 + static int last_frame = 0; 390 + static int frames_dropped = 0; 391 + 392 /* Schematic view of the RX situation: 393 * (in case NR_BUFFERS = 4) 394 * ··· 427 return arr[0] | (arr[1] << 8) | (arr[2] << 16); 428 } 429 430 + // size is samples per frame! 431 + static void encodeFBfixedpt(uint8_t arr[4], int32_t value, bool portspeed) 432 + { 433 + uint32_t fixedpt; 434 + // high-speed 435 + if (portspeed) 436 + { 437 + // Q16.16 438 + fixedpt = value; 439 + 440 + arr[0] = (fixedpt & 0xFF); 441 + arr[1] = (fixedpt>>8) & 0xFF; 442 + arr[2] = (fixedpt>>16) & 0xFF; 443 + arr[3] = (fixedpt>>24) & 0xFF; 444 + } 445 + else // full-speed 446 + { 447 + // Q16.16 --> Q10.10 --> Q10.14 448 + fixedpt = value / (1<<2); // convert from Q16.16 to Q10.14 449 + 450 + // then aligned so it's more like Q10.14 451 + // NOTE: this line left for posterity 452 + // fixedpt = fixedpt << (4); 453 + 454 + arr[0] = (fixedpt & 0xFF); 455 + arr[1] = (fixedpt>>8) & 0xFF; 456 + arr[2] = (fixedpt>>16) & 0xFF; 457 + } 458 + 459 + } 460 + 461 static void set_playback_sampling_frequency(unsigned long f) 462 { 463 // only values 44.1k and higher (array is in descending order) ··· 479 480 unsigned long usb_audio_get_playback_sampling_frequency(void) 481 { 482 + // logf("usbaudio: get playback sampl freq %lu Hz", hw_freq_sampr[as_playback_freq_idx]); 483 return hw_freq_sampr[as_playback_freq_idx]; 484 } 485 ··· 487 { 488 unsigned int i; 489 /* initialized tSamFreq array */ 490 + logf("usbaudio: (init) supported frequencies"); 491 // only values 44.1k and higher (array is in descending order) 492 for(i = 0; i <= HW_FREQ_44; i++) 493 { ··· 498 499 int usb_audio_request_buf(void) 500 { 501 // stop playback first thing 502 audio_stop(); 503 ··· 543 return -1; 544 } 545 546 + in_iso_feedback_ep_adr = usb_core_request_endpoint(USB_ENDPOINT_XFER_ISOC, USB_DIR_IN, drv); 547 + if(in_iso_feedback_ep_adr < 0) 548 { 549 usb_core_release_endpoint(out_iso_ep_adr); 550 + logf("usbaudio: cannot get an in iso endpoint"); 551 return -1; 552 } 553 554 + logf("usbaudio: iso out ep is 0x%x, in ep is 0x%x", out_iso_ep_adr, in_iso_feedback_ep_adr); 555 556 + as_iso_audio_out_ep.bEndpointAddress = out_iso_ep_adr; 557 + as_iso_audio_out_ep.bSynchAddress = in_iso_feedback_ep_adr; 558 + 559 + as_iso_synch_in_ep.bEndpointAddress = in_iso_feedback_ep_adr; 560 + as_iso_synch_in_ep.bSynchAddress = 0; 561 562 return 0; 563 } ··· 569 570 unsigned int usb_audio_get_in_ep(void) 571 { 572 + return in_iso_feedback_ep_adr; 573 } 574 575 int usb_audio_set_first_interface(int interface) ··· 585 unsigned int i; 586 unsigned char *orig_dest = dest; 587 588 + logf("get config descriptors"); 589 590 /** Configuration */ 591 ··· 605 as_interface_alt_playback.bInterfaceNumber = usb_interface + 1; 606 607 /* endpoints */ 608 + as_iso_audio_out_ep.wMaxPacketSize = 1023; 609 + 610 /** Endpoint Interval calculation: 611 * typically sampling frequency is 44100 Hz and top is 192000 Hz, which 612 * account for typical 44100*2(stereo)*2(16-bit) ~= 180 kB/s 613 + * and top 770 kB/s. Since there are ~1000 frames per seconds and maximum 614 * packet size is set to 1023, one transaction per frame is good enough 615 + * for over 1 MB/s. 616 * Recall that actual is 2^(bInterval - 1) */ 617 + 618 + /* In simpler language, This is intended to emulate full-speed's 619 + * one-packet-per-frame rate on high-speed, where we have multiple microframes per millisecond. 620 + */ 621 + as_iso_audio_out_ep.bInterval = usb_drv_port_speed() ? 4 : 1; 622 + as_iso_synch_in_ep.bInterval = usb_drv_port_speed() ? 4 : 1; // per spec 623 + 624 + logf("usbaudio: port_speed=%s", usb_drv_port_speed()?"hs":"fs"); 625 626 /** Packing */ 627 for(i = 0; i < USB_DESCRIPTORS_LIST_SIZE; i++) ··· 649 *start = rx_buffer + (rx_play_idx * REAL_BUF_SIZE); 650 *size = rx_buf_size[rx_play_idx]; 651 rx_play_idx = (rx_play_idx + 1) % NR_BUFFERS; 652 + 653 /* if usb RX buffers had overflowed, we can start to receive again 654 * guard against IRQ to avoid race with completion usb completion (although 655 * this function is probably running in IRQ context anyway) */ ··· 671 rx_play_idx = 0; 672 rx_usb_idx = 0; 673 674 + // feedback initialization 675 + fb_startframe = usb_drv_get_frame_number(); 676 + samples_fb = 0; 677 + samples_received_report = 0; 678 + 679 + // debug screen info - frame drop counter 680 + frames_dropped = 0; 681 + last_frame = -1; 682 + buffers_filled_min = -1; 683 + buffers_filled_min_last = -1; 684 + buffers_filled_max = -1; 685 + buffers_filled_max_last = -1; 686 + 687 + // debug screen info - sample counters 688 + samples_received = 0; 689 + samples_received_last = 0; 690 + 691 // TODO: implement recording from the USB stream 692 #if (INPUT_SRC_CAPS != 0) 693 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); ··· 702 703 static void usb_audio_stop_playback(void) 704 { 705 + logf("usbaudio: stop playback"); 706 if(usb_audio_playing) 707 { 708 mixer_channel_stop(PCM_MIXER_CHAN_USBAUDIO); 709 usb_audio_playing = false; 710 } 711 + send_fb = false; 712 } 713 714 int usb_audio_set_interface(int intf, int alt) ··· 775 { 776 return usb_as_playback_intf_alt; 777 } 778 + 779 + int32_t usb_audio_get_samplesperframe(void) 780 + { 781 + return samples_fb; 782 + } 783 784 + int32_t usb_audio_get_samples_rx_perframe(void) 785 + { 786 + return samples_received_report; 787 + } 788 + 789 + static bool usb_audio_as_ctrldata_endpoint_request(struct usb_ctrlrequest* req, void *reqdata) 790 { 791 /* only support sampling frequency */ 792 if(req->wValue != (USB_AS_EP_CS_SAMPLING_FREQ_CTL << 8)) ··· 850 int ep = req->wIndex & 0xff; 851 852 if(ep == out_iso_ep_adr) 853 + return usb_audio_as_ctrldata_endpoint_request(req, reqdata); 854 else 855 { 856 logf("usbaudio: unhandled ep req (ep=%d)", ep); ··· 870 { 871 logf("usbaudio: mute !"); 872 tmp_saved_vol = sound_current(SOUND_VOLUME); 873 + 874 // setvol does range checking for us! 875 global_status.volume = sound_min(SOUND_VOLUME); 876 setvol(); ··· 879 else if(value == 0) 880 { 881 logf("usbaudio: not muted !"); 882 + 883 // setvol does range checking for us! 884 global_status.volume = tmp_saved_vol; 885 setvol(); ··· 951 952 logf("usbaudio: set volume=%d dB", usb_audio_volume_to_db(value, sound_numdecimals(SOUND_VOLUME))); 953 954 // setvol does range checking for us! 955 // we cannot guarantee the host will send us a volume within our range 956 global_status.volume = usb_audio_volume_to_db(value, sound_numdecimals(SOUND_VOLUME)); ··· 971 return false; 972 } 973 974 + // logf("usbaudio: get %s volume=%d dB", usb_audio_ac_ctl_req_str(cmd), usb_audio_volume_to_db(*value, sound_numdecimals(SOUND_VOLUME))); 975 return true; 976 } 977 ··· 998 return false; 999 } 1000 /* selectors */ 1001 + /* all send/received values are integers already - read data if necessary and store in it in an integer */ 1002 if(req->bRequest & USB_AC_GET_REQ) 1003 { 1004 /* get */ ··· 1129 } 1130 } 1131 1132 + bool usb_audio_control_request(struct usb_ctrlrequest* req, void *reqdata, unsigned char* dest) 1133 { 1134 (void) reqdata; 1135 + (void) dest; 1136 1137 switch(req->bRequestType & USB_RECIP_MASK) 1138 { ··· 1177 /* determine if enough prebuffering has been done to restart audio */ 1178 bool prebuffering_done(void) 1179 { 1180 + /* restart audio if at least MINIMUM_BUFFERS_QUEUED buffers are filled */ 1181 int diff = (rx_usb_idx - rx_play_idx + NR_BUFFERS) % NR_BUFFERS; 1182 return diff >= MINIMUM_BUFFERS_QUEUED; 1183 } ··· 1187 return (rx_usb_idx - rx_play_idx + NR_BUFFERS) % NR_BUFFERS; 1188 } 1189 1190 + int32_t usb_audio_get_prebuffering_avg(void) 1191 + { 1192 + if (buffers_filled_avgcount == 0) 1193 + { 1194 + return TO_16DOT16_FIXEDPT(usb_audio_get_prebuffering()); 1195 + } else { 1196 + return (TO_16DOT16_FIXEDPT(buffers_filled_accumulator)/buffers_filled_avgcount) + TO_16DOT16_FIXEDPT(MINIMUM_BUFFERS_QUEUED); 1197 + } 1198 + } 1199 + 1200 + int usb_audio_get_prebuffering_maxmin(bool max) 1201 + { 1202 + if (max) 1203 + { 1204 + return buffers_filled_max == -1 ? buffers_filled_max_last : buffers_filled_max; 1205 + } 1206 + else 1207 + { 1208 + return buffers_filled_min == -1 ? buffers_filled_min_last : buffers_filled_min; 1209 + } 1210 + } 1211 + 1212 bool usb_audio_get_underflow(void) 1213 { 1214 return playback_audio_underflow; ··· 1217 bool usb_audio_get_overflow(void) 1218 { 1219 return usb_rx_overflow; 1220 + } 1221 + 1222 + int usb_audio_get_frames_dropped(void) 1223 + { 1224 + return frames_dropped; 1225 } 1226 1227 void usb_audio_transfer_complete(int ep, int dir, int status, int length) ··· 1237 bool usb_audio_fast_transfer_complete(int ep, int dir, int status, int length) 1238 { 1239 (void) dir; 1240 + bool retval = false; 1241 1242 if(ep == out_iso_ep_adr && usb_as_playback_intf_alt == 1) 1243 { 1244 + // check for dropped frames 1245 + if (last_frame != usb_drv_get_frame_number()) 1246 + { 1247 + if ((((last_frame + 1) % (USB_FRAME_MAX + 1)) != usb_drv_get_frame_number()) && (last_frame != -1)) 1248 + { 1249 + frames_dropped++; 1250 + } 1251 + last_frame = usb_drv_get_frame_number(); 1252 + } 1253 + 1254 + // If audio and feedback EPs happen to have the same base number (with opposite directions, of course), 1255 + // we will get replies to the feedback here, don't want that to be interpreted as data. 1256 + if (length <= 4) 1257 + { 1258 + return true; 1259 + } 1260 + 1261 + logf("usbaudio: frame: %d bytes: %d", usb_drv_get_frame_number(), length); 1262 if(status != 0) 1263 return true; /* FIXME how to handle error here ? */ 1264 /* store length, queue buffer */ 1265 rx_buf_size[rx_usb_idx] = length; 1266 rx_usb_idx = (rx_usb_idx + 1) % NR_BUFFERS; 1267 + 1268 + // debug screen counter 1269 + samples_received = samples_received + length; 1270 + 1271 /* guard against IRQ to avoid race with completion audio completion */ 1272 int oldlevel = disable_irq_save(); 1273 /* setup a new transaction except if we ran out of buffers */ ··· 1290 mixer_channel_play_data(PCM_MIXER_CHAN_USBAUDIO, playback_audio_get_more, NULL, 0); 1291 } 1292 restore_irq(oldlevel); 1293 + retval = true; 1294 } 1295 else 1296 + { 1297 + retval = false; 1298 + } 1299 + 1300 + // send feedback value every N frames! 1301 + // NOTE: important that we need to queue this up _the frame before_ it's needed - on MacOS especially! 1302 + if ((usb_drv_get_frame_number()+1) % FEEDBACK_UPDATE_RATE_FRAMES == 0 && send_fb) 1303 + { 1304 + if (!sent_fb_this_frame) 1305 + { 1306 + /* NOTE: the division of frequency must be staged to avoid overflow of 16-bit signed int 1307 + * as well as truncating the result to ones place! 1308 + * Must avoid values > 32,768 (2^15) 1309 + * Largest value: 192,000 --> /10: 19,200 --> /100: 192 1310 + * Smallest value: 44,100 --> /10: 4,410 --> /100: 44.1 1311 + */ 1312 + int32_t samples_base = TO_16DOT16_FIXEDPT(hw_freq_sampr[as_playback_freq_idx]/10)/100; 1313 + int32_t buffers_filled = 0; 1314 + 1315 + if (buffers_filled_avgcount != 0) 1316 + { 1317 + buffers_filled = TO_16DOT16_FIXEDPT((int32_t)buffers_filled_accumulator) / buffers_filled_avgcount; 1318 + } 1319 + buffers_filled_accumulator = buffers_filled_accumulator - buffers_filled_accumulator_old; 1320 + buffers_filled_avgcount = buffers_filled_avgcount - buffers_filled_avgcount_old; 1321 + buffers_filled_accumulator_old = buffers_filled_accumulator; 1322 + buffers_filled_avgcount_old = buffers_filled_avgcount; 1323 + 1324 + // someone who has implemented actual PID before might be able to do this correctly, 1325 + // but this seems to work good enough? 1326 + // Coefficients were 1, 0.25, 0.025 in float math --> 1, /4, /40 in fixed-point math 1327 + samples_fb = samples_base - (buffers_filled/4) + ((buffers_filled_old - buffers_filled)/40); 1328 + buffers_filled_old = buffers_filled; 1329 + 1330 + // must limit to +/- 1 sample from nominal 1331 + samples_fb = samples_fb > (samples_base + TO_16DOT16_FIXEDPT(1)) ? samples_base + TO_16DOT16_FIXEDPT(1) : samples_fb; 1332 + samples_fb = samples_fb < (samples_base - TO_16DOT16_FIXEDPT(1)) ? samples_base - TO_16DOT16_FIXEDPT(1) : samples_fb; 1333 + 1334 + encodeFBfixedpt(sendFf, samples_fb, usb_drv_port_speed()); 1335 + logf("usbaudio: frame %d fbval 0x%02X%02X%02X%02X", usb_drv_get_frame_number(), sendFf[3], sendFf[2], sendFf[1], sendFf[0]); 1336 + usb_drv_send_nonblocking(in_iso_feedback_ep_adr, sendFf, usb_drv_port_speed()?4:3); 1337 + 1338 + // debug screen counters 1339 + // 1340 + // samples_received NOTE: need some "division staging" to not overflow signed 16-bit value 1341 + // samples / (feedback frames * 2) --> samples/2 1342 + // samples_report / (2ch * 2bytes per sample) --> samples/4 1343 + // total: samples/8 1344 + samples_received_report = TO_16DOT16_FIXEDPT(samples_received/8) / FEEDBACK_UPDATE_RATE_FRAMES; 1345 + samples_received = samples_received - samples_received_last; 1346 + samples_received_last = samples_received; 1347 + buffers_filled_max_last = buffers_filled_max; 1348 + buffers_filled_max = -1; 1349 + buffers_filled_min_last = buffers_filled_min; 1350 + buffers_filled_min = -1; 1351 + } 1352 + sent_fb_this_frame = true; 1353 + } 1354 + else 1355 + { 1356 + sent_fb_this_frame = false; 1357 + if (!send_fb) 1358 + { 1359 + // arbitrary wait during startup 1360 + if (usb_drv_get_frame_number() == (fb_startframe + (FEEDBACK_UPDATE_RATE_FRAMES*2))%(USB_FRAME_MAX+1)) 1361 + { 1362 + send_fb = true; 1363 + } 1364 + } 1365 + buffers_filled_accumulator = buffers_filled_accumulator + (usb_audio_get_prebuffering() - MINIMUM_BUFFERS_QUEUED); 1366 + buffers_filled_avgcount++; 1367 + if (usb_audio_get_prebuffering() < buffers_filled_min || buffers_filled_min == -1) 1368 + { 1369 + buffers_filled_min = usb_audio_get_prebuffering(); 1370 + } else if (usb_audio_get_prebuffering() > buffers_filled_max) 1371 + { 1372 + buffers_filled_max = usb_audio_get_prebuffering(); 1373 + } 1374 + } 1375 + 1376 + return retval; 1377 }
+56
firmware/usbstack/usb_audio.h
··· 21 22 #include "usb_ch9.h" 23 24 /* 25 * usb_audio_request_endpoints(): 26 * ··· 179 int usb_audio_get_alt_intf(void); 180 181 /* 182 * usb_audio_get_out_ep(): 183 * 184 * Return the out (to device) endpoint ··· 200 int usb_audio_get_prebuffering(void); 201 202 /* 203 * usb_audio_get_underflow(): 204 * 205 * Return whether playback is in "underflow" state ··· 212 * Return whether usb is in "overflow" state 213 */ 214 bool usb_audio_get_overflow(void); 215 216 /* 217 * usb_audio_get_cur_volume():
··· 21 22 #include "usb_ch9.h" 23 24 + /* NOTE 25 + * 26 + * This is USBAudio 1.0. USBAudio 2.0 is notably _not backwards compatible!_ 27 + * USBAudio 1.0 over _USB_ 2.0 is perfectly valid! 28 + * 29 + * Relevant specifications are USB 2.0 and USB Audio Class 1.0. 30 + */ 31 + 32 /* 33 * usb_audio_request_endpoints(): 34 * ··· 187 int usb_audio_get_alt_intf(void); 188 189 /* 190 + * usb_audio_get_samplesperframe(): 191 + * 192 + * Return the samples per frame over the last two feedback cycles 193 + * This is the samples sent to the mixer. 194 + * 195 + * This is returned in floating point 16.16 type. To convert to float, 196 + * do ((double)result / (1<<16)) 197 + */ 198 + int32_t usb_audio_get_samplesperframe(void); 199 + 200 + /* 201 + * usb_audio_get_samplesperframe(): 202 + * 203 + * Return the samples per frame over the last two feedback cycles 204 + * This is the samples received from USB. 205 + * 206 + * This is returned in floating point 16.16 type. To convert to float, 207 + * do ((double)result / (1<<16)) 208 + */ 209 + int32_t usb_audio_get_samples_rx_perframe(void); 210 + 211 + /* 212 * usb_audio_get_out_ep(): 213 * 214 * Return the out (to device) endpoint ··· 230 int usb_audio_get_prebuffering(void); 231 232 /* 233 + * usb_audio_get_prebuffering_avg(): 234 + * 235 + * Return the average number of buffers filled ahead of playback 236 + * over the last two feedback cycles 237 + * 238 + * This is returned in floating point 16.16 type. To convert to float, 239 + * do ((double)result / (1<<16)) 240 + */ 241 + int32_t usb_audio_get_prebuffering_avg(void); 242 + 243 + /* 244 + * usb_audio_get_prebuffering_maxmin(): 245 + * 246 + * Return the max or min number of buffers filled ahead of playback 247 + * over the last feedback cycle 248 + */ 249 + int usb_audio_get_prebuffering_maxmin(bool max); 250 + 251 + /* 252 * usb_audio_get_underflow(): 253 * 254 * Return whether playback is in "underflow" state ··· 261 * Return whether usb is in "overflow" state 262 */ 263 bool usb_audio_get_overflow(void); 264 + 265 + /* 266 + * usb_audio_get_frames_dropped(): 267 + * 268 + * Return the number of frames which have been dropped during playback 269 + */ 270 + int usb_audio_get_frames_dropped(void); 271 272 /* 273 * usb_audio_get_cur_volume():
+44 -7
firmware/usbstack/usb_audio_def.h
··· 18 ****************************************************************************/ 19 /* Parts of this file are based on Frank Gevaerts work and on audio.h from linux */ 20 21 #ifndef USB_AUDIO_DEF_H 22 #define USB_AUDIO_DEF_H 23 ··· 188 #define USB_AS_EP_CS_SAMPLING_FREQ_CTL 0x01 189 #define USB_AS_EP_CS_PITCH_CTL 0x02 190 191 - struct usb_iso_audio_endpoint_descriptor { 192 uint8_t bLength; 193 uint8_t bDescriptorType; 194 195 uint8_t bEndpointAddress; 196 uint8_t bmAttributes; 197 uint16_t wMaxPacketSize; 198 - uint8_t bInterval; 199 - uint8_t bRefresh; 200 - uint8_t bSynchAddress; 201 } __attribute__ ((packed)); 202 203 - struct usb_as_iso_endpoint { 204 uint8_t bLength; 205 uint8_t bDescriptorType; /* USB_DT_CS_ENDPOINT */ 206 uint8_t bDescriptorSubType; /* USB_AS_EP_GENERAL */ 207 uint8_t bmAttributes; 208 - uint8_t bLockDelayUnits; 209 - uint16_t wLockDelay; 210 } __attribute__ ((packed)); 211 212 #define USB_AS_FORMAT_TYPE_I_UNDEFINED 0x0
··· 18 ****************************************************************************/ 19 /* Parts of this file are based on Frank Gevaerts work and on audio.h from linux */ 20 21 + /* NOTE 22 + * 23 + * This is USBAudio 1.0. USBAudio 2.0 is notably _not backwards compatible!_ 24 + * USBAudio 1.0 over _USB_ 2.0 is perfectly valid! 25 + * 26 + * Relevant specifications are USB 2.0 and USB Audio Class 1.0. 27 + */ 28 + 29 #ifndef USB_AUDIO_DEF_H 30 #define USB_AUDIO_DEF_H 31 ··· 196 #define USB_AS_EP_CS_SAMPLING_FREQ_CTL 0x01 197 #define USB_AS_EP_CS_PITCH_CTL 0x02 198 199 + // Standard* AudioStreaming Isochronous Audio Data Endpoint Descriptor 200 + // 201 + // *Note, per usbaudio 1.0 this is explititly identical 202 + // to a standard endpoint descriptor but with extra information 203 + struct usb_as_iso_audio_endpoint { 204 uint8_t bLength; 205 uint8_t bDescriptorType; 206 207 uint8_t bEndpointAddress; 208 uint8_t bmAttributes; 209 uint16_t wMaxPacketSize; 210 + uint8_t bInterval; /* MUST be set to 1 (full speed), +3 for high-speed? */ 211 + uint8_t bRefresh; /* USB Audio Class 1.0 specific: set to 0 */ 212 + uint8_t bSynchAddress; /* USB Audio Class 1.0 specific: used to identify synch ep */ 213 } __attribute__ ((packed)); 214 215 + // Class-Specific AudioStreaming Isochronous Audio* (Control) Data Endpoint Descriptor 216 + // 217 + // *The name is a bit misleading, this is for _control_ of audio, 218 + // such as sampling frequency or pitch. 219 + struct usb_as_iso_ctrldata_endpoint { 220 uint8_t bLength; 221 uint8_t bDescriptorType; /* USB_DT_CS_ENDPOINT */ 222 uint8_t bDescriptorSubType; /* USB_AS_EP_GENERAL */ 223 uint8_t bmAttributes; 224 + uint8_t bLockDelayUnits; /* USB Audio Class 1.0 specific */ 225 + uint16_t wLockDelay; /* USB Audio Class 1.0 specific */ 226 + } __attribute__ ((packed)); 227 + 228 + // Standard* AudioStreaming Isochronous Synch (Feedback) Endpoint Descriptor 229 + // 230 + // *Note, per usbaudio 1.0 this is explititly identical 231 + // to a standard endpoint descriptor but with extra information 232 + struct usb_as_iso_synch_endpoint { 233 + uint8_t bLength; 234 + uint8_t bDescriptorType; /* USB_DT_CS_ENDPOINT */ 235 + 236 + uint8_t bEndpointAddress; /* D7: direction (0 = source, 1 = sink) 237 + * D6..4: Zeros 238 + * D3..0: EP Number */ 239 + uint8_t bmAttributes; /* USBAudio 1.0: 240 + * D3..2: Synch type (00 = None) 241 + * D1..0: Transfer type (01 = Isochronous) 242 + */ 243 + uint16_t wMaxPacketSize; 244 + uint8_t bInterval; /* MUST be set to 1 (full speed), +3 for high-speed? */ 245 + uint8_t bRefresh; /* USB Audio Class 1.0 specific: Range of 1 to 9 (full-speed). high-speed, probably range of 1 to 16? */ 246 + uint8_t bSynchAddress; /* USB Audio Class 1.0 specific: MUST be set to 0 */ 247 } __attribute__ ((packed)); 248 249 #define USB_AS_FORMAT_TYPE_I_UNDEFINED 0x0