From d480241be6a1180467493b43dd4dd4a44e76cd2c Mon Sep 17 00:00:00 2001 From: Simon Kueppers Date: Thu, 19 Sep 2024 11:18:21 +0200 Subject: [PATCH 1/3] Bumped tinyusb to 0.17.0 release --- stm32/aioc-fw/Middlewares/Third-Party/tinyusb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32/aioc-fw/Middlewares/Third-Party/tinyusb b/stm32/aioc-fw/Middlewares/Third-Party/tinyusb index 1eb6ce7..5217cee 160000 --- a/stm32/aioc-fw/Middlewares/Third-Party/tinyusb +++ b/stm32/aioc-fw/Middlewares/Third-Party/tinyusb @@ -1 +1 @@ -Subproject commit 1eb6ce784ca9b8acbbe43dba9f1d9c26c2e80eb0 +Subproject commit 5217cee5de4cd555018da90f9f1bcc87fb1c1d3a From 52b4f5f96a31bd4245093451958c24452d9c1722 Mon Sep 17 00:00:00 2001 From: Simon Kueppers Date: Thu, 19 Sep 2024 11:18:45 +0200 Subject: [PATCH 2/3] Removed obsolete manual stm32 endpoint callback --- stm32/aioc-fw/Src/usb.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/stm32/aioc-fw/Src/usb.c b/stm32/aioc-fw/Src/usb.c index 59f0800..03d1368 100644 --- a/stm32/aioc-fw/Src/usb.c +++ b/stm32/aioc-fw/Src/usb.c @@ -6,44 +6,6 @@ #include "usb_audio.h" #include "usb_hid.h" -// We have ISOCHRONOUS endpoints defined that share the same endpoint number, but have opposing directions. -// However with STM32 hardware, ISOCHRONOUS endpoints use both RX and TX structures of the same endpoint register in hardware -// We circumvent a clash by defining our own custom endpoint map for the tiny usb stack. -// This callback is probably not needed with new versions of tinyusb -uint8_t tu_stm32_edpt_number_cb(uint8_t addr) -{ - switch (addr) { - case 0x00: - case 0x80: - /* Endpoint Zero */ - return 0x00; - - case EPNUM_AUDIO_IN: - return 0x01; - - case EPNUM_AUDIO_OUT: - return 0x02; - - case EPNUM_AUDIO_FB: - return 0x03; - - case EPNUM_HID_IN: - case EPNUM_HID_OUT: - return 0x04; - - case EPNUM_CDC_0_OUT: - case EPNUM_CDC_0_IN: - return 0x05; - - case EPNUM_CDC_0_NOTIF: - return 0x06; - - default: - TU_BREAKPOINT(); - return 0x00; - } -} - // FIXME: Do all three need to be handled, or just the LP one? // USB high-priority interrupt (Channel 74): Triggered only by a correct // transfer event for isochronous and double-buffer bulk transfer to reach From f6195274e386fa3ab0965aae1406a10f7dab7501 Mon Sep 17 00:00:00 2001 From: Simon Kueppers Date: Thu, 19 Sep 2024 11:19:40 +0200 Subject: [PATCH 3/3] Implemented a rudimentary Host OS detection for better MacOS support for UAC2.0 --- stm32/aioc-fw/Src/usb_audio.c | 11 +++ stm32/aioc-fw/Src/usb_descriptors.c | 146 ++++++++++++++++++---------- stm32/aioc-fw/Src/usb_descriptors.h | 8 +- 3 files changed, 114 insertions(+), 51 deletions(-) diff --git a/stm32/aioc-fw/Src/usb_audio.c b/stm32/aioc-fw/Src/usb_audio.c index 1741920..8095bfc 100644 --- a/stm32/aioc-fw/Src/usb_audio.c +++ b/stm32/aioc-fw/Src/usb_audio.c @@ -651,6 +651,17 @@ void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedba feedback_param->method = AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED; } +bool tud_audio_feedback_format_correction_cb(uint8_t func_id) +{ + /* Use the quirk detection to detect whether we need format correction (10.14) according to the USB specification (MacOS) + * or whether we use no correction (16.16) as a quirk (Windows). Linux works either way. */ + if (tud_speed_get() == TUSB_SPEED_FULL) { + return USB_DescUAC2Quirk() ? false : true; + } else { + return false; + } +} + TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift) { static uint32_t prev_cycles = 0; diff --git a/stm32/aioc-fw/Src/usb_descriptors.c b/stm32/aioc-fw/Src/usb_descriptors.c index 019d9fd..6f8685b 100644 --- a/stm32/aioc-fw/Src/usb_descriptors.c +++ b/stm32/aioc-fw/Src/usb_descriptors.c @@ -27,6 +27,23 @@ #include "settings.h" #include "stm32f3xx_hal.h" +/* For quirk detection, borrowed from tinyusb uac2_speaker_fb example */ +static tusb_desc_type_t desc_req_buf[2]; +static int desc_req_idx = 0; + +void quirk_host_os_hint_desc_cb(tusb_desc_type_t desc) { + if (desc == TUSB_DESC_DEVICE) { + desc_req_idx = 0; + } else if (desc_req_idx < 2 && (desc == TUSB_DESC_CONFIGURATION || desc == TUSB_DESC_BOS || desc == TUSB_DESC_STRING)) { + // Skip redundant request + if (desc_req_idx == 0 || (desc_req_idx > 0 && desc_req_buf[desc_req_idx - 1] != desc)) { + desc_req_buf[desc_req_idx++] = desc; + } + } +} + + + //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ @@ -54,6 +71,8 @@ uint8_t const* tud_descriptor_device_cb(void) { desc_device.idVendor = (settingsRegMap[SETTINGS_REG_USBID] & SETTINGS_REG_USBID_VID_MASK) >> SETTINGS_REG_USBID_VID_OFFS; desc_device.idProduct = (settingsRegMap[SETTINGS_REG_USBID] & SETTINGS_REG_USBID_PID_MASK) >> SETTINGS_REG_USBID_PID_OFFS; + quirk_host_os_hint_desc_cb(TUSB_DESC_DEVICE); + return (uint8_t const*) &desc_device; } @@ -125,57 +144,65 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) AIOC_DFU_RT_DESC_LEN \ ) -uint8_t const desc_fs_configuration[] = { - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR( - /* config_num */ 1, - /* _itfcount */ ITF_NUM_TOTAL, - /* _stridx */ 0x00, - /* _total_len */ CONFIG_TOTAL_LEN, - /* _attribute */ 0x00, - /* _power_ma */ 100 - ), - - AIOC_AUDIO_DESCRIPTOR( - /* _itfnum */ ITF_NUM_AUDIO_CONTROL, - /* _stridx */ STR_IDX_AUDIOITF, - /* _nBytesPerSample */ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE, - /* _nBitsUsedPerSample */ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE*8, - /* _epin */ EPNUM_AUDIO_IN, - /* _epinsize */ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX, - /* _epout */ EPNUM_AUDIO_OUT, - /* _epoutsize */ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, - /* _epfb */ EPNUM_AUDIO_FB - ), - AIOC_HID_DESCRIPTOR( - /* _itfnum */ ITF_NUM_HID, - /* _stridx */ STR_IDX_HIDITF, - /* _boot_protocol */HID_ITF_PROTOCOL_NONE, - /*_report_desc_len*/sizeof(desc_hid_report), - /* _epin */ EPNUM_HID_IN, - /* _epsize */ CFG_TUD_HID_EP_BUFSIZE, - /* _ep_interval */ 0x20 - ), - - AIOC_CDC_DESCRIPTOR( - /* _itfnum */ ITF_NUM_CDC_0, - /* _stridx */ STR_IDX_CDCITF, - /* _ep_notif */ EPNUM_CDC_0_NOTIF, - /* _ep_notif_size */ 8, - /* _epout */ EPNUM_CDC_0_OUT, - /* _epin */ EPNUM_CDC_0_IN, - /* _epsize */ CFG_TUD_CDC_EP_BUFSIZE - ), - - AIOC_DFU_RT_DESCRIPTOR( - /* _itfnum */ ITF_NUM_DFU_RT, - /* _stridx */ STR_IDX_DFU_RT, +/* Make this as template, due to quirks necessary in feedback endpoint size */ +#define CONFIG_DESC(_quirk) \ + TUD_CONFIG_DESCRIPTOR( \ + /* config_num */ 1, \ + /* _itfcount */ ITF_NUM_TOTAL, \ + /* _stridx */ 0x00, \ + /* _total_len */ CONFIG_TOTAL_LEN, \ + /* _attribute */ 0x00, \ + /* _power_ma */ 100 \ + ), \ + AIOC_AUDIO_DESCRIPTOR( \ + /* _itfnum */ ITF_NUM_AUDIO_CONTROL, \ + /* _stridx */ STR_IDX_AUDIOITF, \ + /* _nBytesPerSample */ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE, \ + /* _nBitsUsedPerSample */ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE*8, \ + /* _epin */ EPNUM_AUDIO_IN, \ + /* _epinsize */ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX, \ + /* _epout */ EPNUM_AUDIO_OUT, \ + /* _epoutsize */ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, \ + /* _epfb */ EPNUM_AUDIO_FB, \ + /* epfbsize */ ((_quirk) ? 4 : 3) \ + ), \ + AIOC_HID_DESCRIPTOR( \ + /* _itfnum */ ITF_NUM_HID, \ + /* _stridx */ STR_IDX_HIDITF, \ + /* _boot_protocol */HID_ITF_PROTOCOL_NONE, \ + /*_report_desc_len*/sizeof(desc_hid_report), \ + /* _epin */ EPNUM_HID_IN, \ + /* _epsize */ CFG_TUD_HID_EP_BUFSIZE, \ + /* _ep_interval */ 0x20 \ + ), \ + AIOC_CDC_DESCRIPTOR( \ + /* _itfnum */ ITF_NUM_CDC_0, \ + /* _stridx */ STR_IDX_CDCITF, \ + /* _ep_notif */ EPNUM_CDC_0_NOTIF, \ + /* _ep_notif_size */ 8, \ + /* _epout */ EPNUM_CDC_0_OUT, \ + /* _epin */ EPNUM_CDC_0_IN, \ + /* _epsize */ CFG_TUD_CDC_EP_BUFSIZE \ + ), \ + AIOC_DFU_RT_DESCRIPTOR( \ + /* _itfnum */ ITF_NUM_DFU_RT, \ + /* _stridx */ STR_IDX_DFU_RT, \ /* _attr */ DFU_ATTR_WILL_DETACH | \ DFU_ATTR_CAN_UPLOAD | \ - DFU_ATTR_CAN_DOWNLOAD, - /* _timeout */ 255, /* not used if WILL_DETACH */ - /* _xfer_size */ 2048 /* max size for stm32 dfu bootloader */ + DFU_ATTR_CAN_DOWNLOAD, \ + /* _timeout */ 255, /* not used if WILL_DETACH */ \ + /* _xfer_size */ 2048 /* max size for stm32 dfu bootloader */ \ ) + +uint8_t const desc_fs_configuration_quirk[] = { + /* quirk is required for Windows, Linux doesn't care. + * (see https://github.com/hathach/tinyusb/pull/2328/commits/6a67bac47c0f83eebd63ca99654ed26e51b21145) */ + CONFIG_DESC(/* quirk */1) +}; + +uint8_t const desc_fs_configuration[] = { + /* no quirk is required for MacOS, Linux doesn't care */ + CONFIG_DESC(/* quirk */0) }; // Invoked when received GET CONFIGURATION DESCRIPTOR @@ -185,7 +212,15 @@ uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations TU_ASSERT(!TUD_OPT_HIGH_SPEED); - return desc_fs_configuration; + + quirk_host_os_hint_desc_cb(TUSB_DESC_CONFIGURATION); + + /* Try to guess the host OS so we can apply the quirk where needed */ + if (USB_DescUAC2Quirk()) { + return desc_fs_configuration_quirk; + } else { + return desc_fs_configuration; + } } //--------------------------------------------------------------------+ @@ -312,5 +347,18 @@ const uint16_t * tud_descriptor_string_cb(uint8_t index, uint16_t langid) { buffer[0] = len + 2; buffer[1] = TUSB_DESC_STRING; + quirk_host_os_hint_desc_cb(TUSB_DESC_STRING); + return (void *) buffer; } + +bool USB_DescUAC2Quirk(void) +{ + if ( (desc_req_buf[0] == TUSB_DESC_STRING) && (desc_req_buf[1] == TUSB_DESC_BOS || desc_req_buf[1] == TUSB_DESC_CONFIGURATION) ) { + /* This pattern of descriptor requests is only found on MacOS devices. MacOS needs the non-quirked descriptor */ + return false; + } else { + /* Other patterns hint to Windows, which requires the quirk. Or Linux, which doesn't care and works either way */ + return true; + } +} diff --git a/stm32/aioc-fw/Src/usb_descriptors.h b/stm32/aioc-fw/Src/usb_descriptors.h index 394541f..049279f 100644 --- a/stm32/aioc-fw/Src/usb_descriptors.h +++ b/stm32/aioc-fw/Src/usb_descriptors.h @@ -1,6 +1,8 @@ #ifndef USB_DESCRIPTORS_H_ #define USB_DESCRIPTORS_H_ +#include + /* Interfaces */ enum USB_DESCRIPTORS_ITF { ITF_NUM_AUDIO_CONTROL = 0, @@ -104,7 +106,7 @@ enum USB_STRING_IDX { TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN + \ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) -#define AIOC_AUDIO_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epinsize, _epout, _epoutsize, _epfb) \ +#define AIOC_AUDIO_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epinsize, _epout, _epoutsize, _epfb, _epfbsize) \ /* Standard Interface Association Descriptor (IAD) */ \ TUD_AUDIO_DESC_IAD(_itfnum, AUDIO_NUM_INTERFACES, /*_stridx*/ 0x00), \ /* Audio Control Interface */ \ @@ -138,7 +140,7 @@ enum USB_STRING_IDX { /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ TUD_AUDIO_DESC_CS_AS_ISO_EP(AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, AUDIO_CTRL_NONE, AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, 0x0000), \ /* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */ \ - TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_epfb, 1), \ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_epfb, _epfbsize, 1), \ /* Microphone Interface */ \ /* Standard AS Interface Descriptor(4.9.1) */ \ /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ @@ -174,4 +176,6 @@ enum USB_STRING_IDX { #define AIOC_DFU_RT_DESCRIPTOR TUD_DFU_RT_DESCRIPTOR +bool USB_DescUAC2Quirk(void); + #endif /* USB_DESCRIPTORS_H_ */