Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added USB UAC quirk detection for MacOS compatibility #99

Merged
merged 3 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stm32/aioc-fw/Middlewares/Third-Party/tinyusb
Submodule tinyusb updated 996 files
38 changes: 0 additions & 38 deletions stm32/aioc-fw/Src/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions stm32/aioc-fw/Src/usb_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
146 changes: 97 additions & 49 deletions stm32/aioc-fw/Src/usb_descriptors.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
//--------------------------------------------------------------------+
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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
Expand All @@ -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;
}
}

//--------------------------------------------------------------------+
Expand Down Expand Up @@ -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;
}
}
8 changes: 6 additions & 2 deletions stm32/aioc-fw/Src/usb_descriptors.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef USB_DESCRIPTORS_H_
#define USB_DESCRIPTORS_H_

#include <stdbool.h>

/* Interfaces */
enum USB_DESCRIPTORS_ITF {
ITF_NUM_AUDIO_CONTROL = 0,
Expand Down Expand Up @@ -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 */ \
Expand Down Expand Up @@ -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 */ \
Expand Down Expand Up @@ -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_ */