hathach / tinyusb

An open source cross-platform USB stack for embedded system
https://www.tinyusb.org
MIT License
4.98k stars 1.05k forks source link

uac2_headset fails on STM32F401 #1935

Closed elagil closed 1 year ago

elagil commented 1 year ago

Prerequisites

I want to use an STM32F401 as a UAC2 sound card. For that, I start with the uac2_headset example.

Operating System

Linux

Board

Custom, based on STM32F401RBT. I tested the ST USB stack (Audio device) on it, and it works as expected.

Firmware

My firmware is custom, and based on STM32CubeMX. It builds as a simple Makefile project.

The following steps were taken to include the tinyusb library and build it:

Sources

tinyusb/src/tusb.c
tinyusb/src/common/tusb_fifo.c
tinyusb/src/device/usbd.c
tinyusb/src/device/usbd_control.c
tinyusb/src/class/audio/audio_device.c
tinyusb/src/class/cdc/cdc_device.c
tinyusb/src/class/dfu/dfu_device.c
tinyusb/src/class/dfu/dfu_rt_device.c
tinyusb/src/class/hid/hid_device.c
tinyusb/src/class/midi/midi_device.c
tinyusb/src/class/msc/msc_device.c
tinyusb/src/class/net/ecm_rndis_device.c
tinyusb/src/class/net/ncm_device.c
tinyusb/src/class/usbtmc/usbtmc_device.c
tinyusb/src/class/video/video_device.c
tinyusb/src/class/vendor/vendor_device.c
tinyusb/src/portable/synopsys/dwc2/dcd_dwc2.c

Includes

tinyusb/src

osThreadId_t usbTaskHandle; const osThreadAttr_t usbTask_attributes = { .name = "usbTask", .stack_size = USBD_STACK_SIZE, .priority = (osPriority_t) osPriorityHigh, };

int main(void) { // Other initialization usbTaskHandle = osThreadNew(usbTask, NULL, &usbTask_attributes); // ... }

void usbTask(void *argument) { tusb_init();

while (true) { tud_task(); } }


- Make the `OTG_FS_IRQHandler` dispatch the tinyusb interrupt handler (see below). For it to exist, I enabled the global interrupt in CubeMX

![image](https://user-images.githubusercontent.com/9093113/222964557-995b09bb-154b-4fd2-838f-497a01f0eae9.png)

```C
/**
  * @brief This function handles USB On The Go FS global interrupt.
  */
void OTG_FS_IRQHandler(void)
{
  /* USER CODE BEGIN OTG_FS_IRQn 0 */

  // Pass USB interrupts to the tinyUsb stack and exit.
    tud_int_handler(BOARD_TUD_RHPORT);
    return;

  /* USER CODE END OTG_FS_IRQn 0 */
  HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
  /* USER CODE BEGIN OTG_FS_IRQn 1 */

  /* USER CODE END OTG_FS_IRQn 1 */
}

The full code is here: https://github.com/elagil/usb-uac2/. My implementation is located in Core/Src and Core/Inc.

What happened ?

On a linux machine, the USB device connects, but fails to report its capabilities (sample rate, volume range). See the dmesg output below:

[13935.135744] usb 1-1.3: USB disconnect, device number 19
[13937.518800] usb 1-1.3: new full-speed USB device number 20 using xhci_hcd
[13937.842867] usb 1-1.3: New USB device found, idVendor=cafe, idProduct=4010, bcdDevice= 1.00
[13937.842885] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[13937.842898] usb 1-1.3: Product: TinyUSB headset
[13937.842909] usb 1-1.3: Manufacturer: TinyUSB
[13937.842919] usb 1-1.3: SerialNumber: 000001
[13937.953078] usb 1-1.3: parse_audio_format_rates_v2v3(): unable to retrieve number of sample rates (clock 4)
[13937.973773] usb 1-1.3: parse_audio_format_rates_v2v3(): unable to retrieve number of sample rates (clock 4)
[13938.013336] usb 1-1.3: parse_audio_format_rates_v2v3(): unable to retrieve number of sample rates (clock 4)
[13938.033988] usb 1-1.3: parse_audio_format_rates_v2v3(): unable to retrieve number of sample rates (clock 4)
[13938.065350] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.065380] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.077751] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x200, wIndex = 0x200, type = 4
[13938.077767] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.196407] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.196424] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.208748] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.208761] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.221121] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.221132] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.233494] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.233505] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.245871] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.245882] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.258256] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.258268] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.270642] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.270654] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.282998] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.283010] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.295391] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x201, wIndex = 0x200, type = 4
[13938.295405] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.405788] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x200, wIndex = 0x200, type = 4
[13938.405808] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.418184] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x200, wIndex = 0x200, type = 4
[13938.418203] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.430558] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x200, wIndex = 0x200, type = 4
[13938.430576] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.443307] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x200, wIndex = 0x200, type = 4
[13938.443324] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.455722] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x200, wIndex = 0x200, type = 4
[13938.455740] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.468252] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x200, wIndex = 0x200, type = 4
[13938.468270] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)
[13938.480686] usb 1-1.3: cannot get ctl value: req = 0x83, wValue = 0x200, wIndex = 0x200, type = 4
[13938.480703] usb 1-1.3: 2:0: cannot get min/max values for control 2 (id 2)

Find the log below.

How to reproduce ?

  1. Connect device
  2. Wait for enumeration

Debug Log as txt file

I don't see anything obvious in the log output, logged at level 3. Most notably, the requested capabilities (sample rates, volume ranges) are reported fine here. They just don't make it to the PC.

log.txt

Screenshots

No response

I have checked existing issues, dicussion and documentation

HiFiPhile commented 1 year ago

Interesting, these Stall EP0 shouldn't happen. Have you tried stock example without modification ? Have you tried other classes ?

HiFiPhile commented 1 year ago

Seems like caused by newly added tu_memcpy_s https://github.com/hathach/tinyusb/blob/34798ff85ec819bafc09da57f7853a1d116a7fff/src/class/audio/audio_device.c#L2293

elagil commented 1 year ago

Just tested the HID composite example, works fine.

[17504.035119] usb 1-1.3: new full-speed USB device number 27 using xhci_hcd
[17504.324266] usb 1-1.3: New USB device found, idVendor=cafe, idProduct=4004, bcdDevice= 1.00
[17504.324282] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[17504.324294] usb 1-1.3: Product: TinyUSB Device
[17504.324305] usb 1-1.3: Manufacturer: TinyUSB
[17504.324316] usb 1-1.3: SerialNumber: 123456
[17504.429244] input: TinyUSB TinyUSB Device Keyboard as /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0/0003:CAFE:4004.0001/input/input8
[17504.488030] input: TinyUSB TinyUSB Device Mouse as /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0/0003:CAFE:4004.0001/input/input9
[17504.488482] input: TinyUSB TinyUSB Device Consumer Control as /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0/0003:CAFE:4004.0001/input/input10
[17504.488772] input: TinyUSB TinyUSB Device as /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0/0003:CAFE:4004.0001/input/input11
[17504.489058] hid-generic 0003:CAFE:4004.0001: input,hidraw0: USB HID v1.11 Keyboard [TinyUSB TinyUSB Device] on usb-0000:01:00.0-1.3/input0

and the log output: hid_log.txt

elagil commented 1 year ago

I tried your suggested fix in https://github.com/hathach/tinyusb/pull/1852#issuecomment-1455102835, but it runs into an assertion in dcd_dwc2.c:658:

  if(dir == TUSB_DIR_OUT)
  {
    // Calculate required size of RX FIFO
    uint16_t const sz = calc_grxfsiz(4*fifo_size, ep_count);

    // If size_rx needs to be extended check if possible and if so enlarge it
    if (dwc2->grxfsiz < sz)
    {
>>  TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size/4);

      // Enlarge RX FIFO
      dwc2->grxfsiz = sz;
    }

Here, for the rhs value, _dwc2_controller[rhport].ep_fifo_size = 1280, so the rhs value is 320. For the lhs value sz = 411, _allocated_fifo_words_tx = 16, so 427. Thus, it fails.

HiFiPhile commented 1 year ago

I tried your suggested fix in https://github.com/hathach/tinyusb/pull/1852#issuecomment-1455102835, but it runs into an assertion in dcd_dwc2.c:658

I think F411's USB FIFO is not enough for high-res format like 24bit/96kHz, could you try to reduce sample rate/bit res like how #define __RX__ did in tusb_config.h and main.c ?

elagil commented 1 year ago

So, I just added #define __RX__ which changes the point at which it locks up. It now runs into the default case of handle_rxflvl_irq with pktsts == 11 and hangs at TU_BREAKPOINT() in dcd_dwc2.c:1085.

By the way, it's an F401.

elagil commented 1 year ago

However, I don't see as many stalls. There are three: changes_log.txt

The log suddenly stops when I hit TU_BREAKPOINT().

In Windows, the device is even detected correctly, but I can't play sound. It will jump into the TU_BREAKPOINT() immediately.

Edit: Sometimes, I can play a bit of sound. Then it fails after a short while. I guess it is somehow FIFO related.

elagil commented 1 year ago

Another piece of information: The STM32F401 has the same total FIFO size as all the other F4 series parts, namely 1280 bytes for the full speed port. It shouldn't be too small then. Could it be that the device is too slow in emptying the FIFO buffer? I only run at 48 MHz SYSCLK.

elagil commented 1 year ago

I tested the 4-channel microphone example, and it somewhat works (only with your fix, replacing sizeof(...)).

Two channels show a sawtooth, two are empty. Also, I had to drastically increase the level of the sawtooth, otherwise it appeared to be zero.

I increased the dataVal increment from 1 to 200.

  // Generate dummy data
  for (uint16_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++)
  {
    uint16_t * p_buff = i2s_dummy_buffer[cnt];              // 2 bytes per sample
    dataVal = 1;
    for (uint16_t cnt2 = 0; cnt2 < AUDIO_SAMPLE_RATE/1000; cnt2++)
    {
      for (uint8_t cnt3 = 0; cnt3 < CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX; cnt3++)
      {
        *p_buff++ = dataVal;
      }
      dataVal+=200;
    }
  }

image

HiFiPhile commented 1 year ago

@hathach I think there is an issue in dwc2 Rx FIFO allocation. In uac2_headset example the max Rx packet size is 24(96+1) = 776 which can be hold in 1280 bytes FIFO.

https://github.com/hathach/tinyusb/blob/34798ff85ec819bafc09da57f7853a1d116a7fff/src/portable/synopsys/dwc2/dcd_dwc2.c#L108

However calc_grxfsiz(776, 4) will return 411 which is larger than FIFO size.

At least 2*(max_ep_size/4) seems wrong.

elagil commented 1 year ago

which is larger than FIFO size.

since this is in units of 32 bit words, right?

It says in the F401 reference manual:

Program the OTG_FS_GRXFSIZ register, to be able to receive control OUT data and setup data. If thresholding is not enabled, at a minimum, this must be equal to 1 max packet size of control endpoint 0 + 2 words (for the status of the control OUT data packet) + 10 words (for setup packets).

I don't see the calculation matching this, but it was probably derived from a different reference manual.

There is more in the source code, which cites the reference manual (I don't know which): https://github.com/hathach/tinyusb/blob/34798ff85ec819bafc09da57f7853a1d116a7fff/src/portable/synopsys/dwc2/dcd_dwc2.c#L191

Edit: I just checked. In the headphone example, calc_grxfsiz is called with

elagil commented 1 year ago

Latest testing with completely unchanged firmware was seemingly successful. I changed the debugger!

I can even start up the device in 96 kHz mode image

But, at 96 kHz, it fails during use, at dcd_dwc2.c:658, which seems to be expected.


  if(dir == TUSB_DIR_OUT)
  {
    // Calculate required size of RX FIFO
    uint16_t const sz = calc_grxfsiz(4*fifo_size, ep_count);

    // If size_rx needs to be extended check if possible and if so enlarge it
    if (dwc2->grxfsiz < sz)
    {
  >>> TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size/4);

      // Enlarge RX FIFO
      dwc2->grxfsiz = sz;
    }

    dwc2->epout[epnum].doepctl |= (1 << DOEPCTL_USBAEP_Pos) |
                                  (desc_edpt->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) |
                                  (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) |
                                  (xfer->max_size << DOEPCTL_MPSIZ_Pos);

    dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
  }

At 48 kHz, it plays fine, but I did not do long-term testing. I have to get the data out via I2S first, for actually listening.

elagil commented 1 year ago

Does not seem to be quite reliable yet, it does run into the same TU_BREAKPOINT() sometimes.

elagil commented 1 year ago

Seems stable during operation now, but I am hitting a new issue. See https://github.com/hathach/tinyusb/issues/1949