USB Host Audio Stream Receive Data Length Misreporting

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

USB Host Audio Stream Receive Data Length Misreporting

Jump to solution
2,209 Views
amwilhite
Contributor III

I'm confused about the operation of the USB Host Audio APIs and was hoping someone may be able to clarify a few questions.

My RT1050 is setup as a USB Audio Host. I'm able to receive audio data from a connected device by repeatedly calling USB_HostAudioStreamRecv. However, the dataLength parameter returned in the _HostAudioInCallback function is different from what I was expecting.

Each time the _HostAudioInCallback is called, the dataLength parameter is set to 128 bytes, despite the audioBuffer passed into the USB_HostAudioStreamRecv function being 2048 bytes.

However, looking at the memory location of the audioBuffer, it looks like 2048 bytes of data are written between each call to _HostAudioInCallback.

Is anyone able to explain this behavior?

#define AUDIO_BUFFER_SIZE (2048u)

static void _HostAudioInCallback(
 void *param,
 uint8_t *data,
 uint32_t dataLength,
 usb_status_t status)
{
    USBHostAudioInstance_t *audioInstance = (USBHostAudioInstance_t *)param;

 if (audioInstance->deviceState == kStatus_DEV_Attached)
 {
  if (status == kStatus_USB_Success)
  {
   DEBUG_USB("%s: Successfully received %u bytes\r\n", __func__);
                }
        }
}


status = USB_HostAudioStreamRecv(audioInstance->classHandle,
            audioInstance->audioBuffer,
            AUDIO_BUFFER_SIZE,
            _HostAudioInCallback,
            audioInstance);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
Labels (1)
Tags (2)
1 Solution
1,773 Views
amwilhite
Contributor III

Jorge,

I was able to identify the bug in the USB stack that causes the misreported data length.

The problem exists in the USB_HostEhciTransactionDone function in the usb/host/usb_host_echi.c file.

Lines 3700-3704

Replace

/* remove itd from frame list and release itd */
dataLength = USB_HostEhciItdArrayRelease(ehciInstance,
                                        (usb_host_ehci_itd_t *)transfer->union1.unitHead,
                                        (usb_host_ehci_itd_t *)transfer->union2.unitTail);
transfer->transferSofar = dataLength;‍‍‍‍‍

With

/* remove itd from frame list and release itd */
transfer->transferSofar = USB_HostEhciItdArrayRelease(ehciInstance,
                                                           (usb_host_ehci_itd_t *)transfer->union1.unitHead,
                                                           (usb_host_ehci_itd_t *)transfer->union2.unitTail);

transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ?
                                                      0 :
                                                      (transfer->transferLength -transfer->transferSofar);‍‍‍‍‍‍‍‍

Lines 3737-41

Replace

/* remove sitd from frame list and release itd */
dataLength = USB_HostEhciSitdArrayRelease(ehciInstance,
                                          (usb_host_ehci_sitd_t *)transfer->union1.unitHead,
                                          (usb_host_ehci_sitd_t *)transfer->union2.unitTail);
transfer->transferSofar = dataLength;‍‍‍‍‍

With

/* remove itd from frame list and release itd */
transfer->transferSofar = USB_HostEhciSitdArrayRelease(ehciInstance,
                                                       (usb_host_ehci_sitd_t *)transfer->union1.unitHead,
                                                       (usb_host_ehci_sitd_t *)transfer->union2.unitTail);
transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ?
                                                      0 :
                                                      (transfer->transferLength - transfer->transferSofar);

View solution in original post

10 Replies
1,773 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Austin Wilhite

There isn't a demo for USB Host Audio in the SDK, so I guess that you create this demo by yourself, am I correct? could you clarify what demo did you base?

Could you clarify what is the sampling rate of your device? At the point when you called the USB_HostAudioStreamRecv API, you should have set the audio interface previously. In this "set of interface", you should have the descriptor of your device, which contains the sampling rate along with the format, etc. So, the packet size depends on the capabilities of your device and format used.

If you have a stereo audio speaker with a 96 kHz sample rate and 24-bit samples. Your speed will be 96000 x 2 channels x 4 bytes (24bit) = 768,000 bytes per second. The isochronous endpoint (used in audio class) run at a rate of one transfer per 125 us or 8000 transfers per second. This means that you have 768000 bytes per second / 8000 transfer per second = 96 bytes per transfer.

Hope this information helps you

Best regards

Jorge Alcala

1,773 Views
amwilhite
Contributor III

Jorge,

I created the USB Audio setup based off of the USB HID Mouse example. I included the state machine below that sets the control and streaming interfaces.

The device I'm streaming from has 6, 16-bit, 16kHz PCM audio channels.

I was under the impression that there should be one data packet each USB Full Speed frame of 1 ms, or 1000 transfers per second. However, you mentioned that there is instead one isochronous transfer per 125 us, or 8000 transfers per second. Can you explain why that is true?

Mirroring your calculation above: 16,000 kHz * 6 channels * 2 bytes (16-bit) = 192,000 bytes per second.

192,000 bytes / 8000 transfers per second = 24 bytes per transfer.

I'm still unable to explain why the received dataLength is 128 bytes and not 24 bytes as the above calculation shows.

My biggest problem is that 128 is not divisible by 6, so I don't know how to interpret the data into its separate channels.

/*! @brief host app run status */
typedef enum USBHostAudioRunState
{
    USBHostAudioRunState_Idle = 0,                      /*!< idle */
    USBHostAudioRunState_SetControlInterface,           /*!< execute set interface code */
    USBHostAudioRunState_WaitSetControlInterface,       /*!< wait set interface done */
    USBHostAudioRunState_SetStreamingInterface,         /*!< execute set interface code */
    USBHostAudioRunState_WaitSetStreamingInterface,     /*!< wait set interface done */
    USBHostAudioRunState_ReceiveStream,                 /*!< set interface is done, start streaming */
    USBHostAudioRunState_WaitReceiveStream              /*!< wait for streaming callback */
} USBHostAudioRunState_t;

/*! @brief USB host audio instance structure */
typedef struct USBHostAudioInstance
{
    usb_host_configuration_handle configHandle; /*!< the audio configuration handle */
    usb_device_handle deviceHandle;             /*!< the audio device handle */
    usb_host_class_handle classHandle;          /*!< the audio class handle */
    usb_host_interface_handle streamingInterfaceHandle;  /*!< the audio streaming interface handle */
    usb_host_interface_handle controlInterfaceHandle;    /*!< the audio control interface handle */
    uint8_t deviceState;                        /*!< device attach/detach status */
    uint8_t prevState;                          /*!< device attach/detach previous status */
    uint8_t runState;                           /*!< audio application run status */
    uint8_t runWaitState;   /*!< audio application wait status, go to next run status when the wait status success */
    uint16_t maxPacketSize; /*!< Interrupt in max packet size */
    uint8_t *audioBuffer;   /*!< use to receive data */
} USBHostAudioInstance_t;


static void _HostAudioStateManager(void *param)
{
    usb_status_t status;
    USBHostAudioInstance_t *audioInstance = (USBHostAudioInstance_t *)param;

    /* Process device state changes (process once for each state) */

    if (audioInstance->deviceState != audioInstance->prevState)
    {
        audioInstance->prevState = audioInstance->deviceState;
        switch (audioInstance->deviceState)
        {
            case kStatus_DEV_Idle:
                break;

            /* Device is attached and enumeration is done */
            case kStatus_DEV_Attached:

                audioInstance->runState = USBHostAudioRunState_SetControlInterface;
                status = USB_HostAudioInit(audioInstance->deviceHandle, &audioInstance->classHandle);
                if (status != kStatus_USB_Success)
    {
     DEBUG_USB("USB Host Audio class initialize failure\r\n");
     ASSERT_FAILURE();
    }
                break;

            /* Device is detached */
            case kStatus_DEV_Detached:
                audioInstance->deviceState = kStatus_DEV_Idle;
                audioInstance->runState = USBHostAudioRunState_Idle;

                /* Audio class de-initialization */
                USB_HostAudioDeinit(audioInstance->deviceHandle,
                                  audioInstance->classHandle);

                audioInstance->classHandle = NULL;
                DEBUG_USB("USB Audio device detached\r\n");
                break;

            default:
                break;
        }
    }

    /* Process the run state */

    switch (audioInstance->runState)
    {
        case USBHostAudioRunState_Idle:
            break;

        /* Set the audio control interface */
        case USBHostAudioRunState_SetControlInterface:
            audioInstance->runWaitState = USBHostAudioRunState_WaitSetControlInterface;
            audioInstance->runState = USBHostAudioRunState_Idle;

   status = USB_HostAudioControlSetInterface(audioInstance->classHandle,
               audioInstance->controlInterfaceHandle,
               0,
               _HostAudioControlCallback,
               audioInstance);
   ASSERT(status == kStatus_USB_Success);

   DEBUG_USB("Set Control Interface Complete\r\n");
            break;

        /* Set the audio streaming interface */
        case USBHostAudioRunState_SetStreamingInterface:
   audioInstance->runWaitState = USBHostAudioRunState_WaitSetStreamingInterface;
   audioInstance->runState = USBHostAudioRunState_Idle;

   status = USB_HostAudioStreamSetInterface(audioInstance->classHandle,
               audioInstance->streamingInterfaceHandle,
               1,
               _HostAudioControlCallback,
               audioInstance);
   ASSERT(status == kStatus_USB_Success);

   DEBUG_USB("Set Streaming Interface Complete\r\n");
            break;

        case USBHostAudioRunState_ReceiveStream:
            audioInstance->runWaitState = USBHostAudioRunState_WaitReceiveStream;
            audioInstance->runState = USBHostAudioRunState_Idle;
         status = USB_HostAudioStreamRecv(audioInstance->classHandle,
                  audioInstance->audioBuffer,
            AUDIO_BUFFER_SIZE,
            _HostAudioInCallback,
            audioInstance);

         ASSERT(status == kStatus_USB_Success);
            break;
        default:
            break;
    }
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
1,773 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Austin Wilhite

Sorry, the 8000 bytes was making reference for a High-Speed device, for Full Speed you are correct, you will have 1 ms interval which represents 1000 transfer per second.

So according to your device, you will have 16,000 kHz * 6 channels * 2 bytes (16-bit) = 192,000 bytes per second and

192,000 bytes / 1000 transfers per second = 192 bytes per transfer.

This information is requested by the host and the device should have it in the descriptor.

You should have a _USB_HostAudioOpenInterface function, in this part, you have a line as:

 pipe_init.maxPacketSize = 
(uint16_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(ep_desc->wMaxPacketSize)
 & USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_SIZE_MASK);‍‍

 

Which is then copy to the audioPtr->inPacketSize field, here you will have the maximum packet size that you can use in the USB_HostAudioStreamRecv function. You can actually get this size with the function

AUDIO_BUFFER_SIZE = USB_HostAudioPacketSize(Audio_Handle_name, USB_ENDPOINT_ISOCHRONOUS, USB_IN);

status = USB_HostAudioStreamRecv(audioInstance->classHandle,
                        audioInstance->audioBuffer,
                        AUDIO_BUFFER_SIZE,
                        _HostAudioInCallback,
                        audioInstance)

Hope this could help

Best regards

0 Kudos
1,773 Views
amwilhite
Contributor III

Jorge,

 

Can you explain why the receive buffer size can only be set to a maximum value of the max packet size? Why is it unable to receive an entire transaction or even transfer worth of data at a time, rather than just one packet?

 

Based on Table 5-4 in the USB 2.0 spec, a data payload of 32 bytes should be capable of 36 transfers per frame.

0 Kudos
1,773 Views
amwilhite
Contributor III

Jorge,

I did as you suggested and found the maxPacketSize to be 204 bytes, and then set the AUDIO_BUFFER_SIZE variable to 204.

Now, each call to _HostAudioInCallback returns a dataLength of 12 bytes.

Although the 12 bytes is at least divisible by the 6 channels, it's still not the 192 bytes I was expecting.

I've attached an image of the audioPtr's values as seen with the debugger in the _USB_HostAudioOpenInterface function.

I've confirmed the number of channels, bit resolution, and sampling frequency with the values in the structure.

Do you have any insight into why the dataLength is now 12 bytes rather than 192?

audioptr.png

0 Kudos
1,773 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Austin Wilhite

At what speed are you requesting data? if you decrease the time between each USB_HostAudioStreamRecv? Do you still get 12 bytes?

Please remember that the 204 size that you get is the max capacity that your device can give, but it doesn't mean that it will give you the 204. In an isochronous communication, the bandwidth has to be guaranteed, but if the device is not ready to provide the information, it will be less than the max packate size (204 in your case).

Hope this helps

Best regards

0 Kudos
1,773 Views
amwilhite
Contributor III

Jorge,

Can I get an update on this?

It's been almost three weeks since my initial post pertaining to using the USB Audio Streaming APIs, and I still don't have a working system.

Will someone at NXP be working on reproducing this issue?

0 Kudos
1,773 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Austin

Could you share the code that you are using? Also, could you describe how do you set the received task? this could be a problem related to the stack as you mentioned that you received all the data, but I need to reproduce the issue in my side.

Regards

Jorge Alcala

0 Kudos
1,774 Views
amwilhite
Contributor III

Jorge,

I was able to identify the bug in the USB stack that causes the misreported data length.

The problem exists in the USB_HostEhciTransactionDone function in the usb/host/usb_host_echi.c file.

Lines 3700-3704

Replace

/* remove itd from frame list and release itd */
dataLength = USB_HostEhciItdArrayRelease(ehciInstance,
                                        (usb_host_ehci_itd_t *)transfer->union1.unitHead,
                                        (usb_host_ehci_itd_t *)transfer->union2.unitTail);
transfer->transferSofar = dataLength;‍‍‍‍‍

With

/* remove itd from frame list and release itd */
transfer->transferSofar = USB_HostEhciItdArrayRelease(ehciInstance,
                                                           (usb_host_ehci_itd_t *)transfer->union1.unitHead,
                                                           (usb_host_ehci_itd_t *)transfer->union2.unitTail);

transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ?
                                                      0 :
                                                      (transfer->transferLength -transfer->transferSofar);‍‍‍‍‍‍‍‍

Lines 3737-41

Replace

/* remove sitd from frame list and release itd */
dataLength = USB_HostEhciSitdArrayRelease(ehciInstance,
                                          (usb_host_ehci_sitd_t *)transfer->union1.unitHead,
                                          (usb_host_ehci_sitd_t *)transfer->union2.unitTail);
transfer->transferSofar = dataLength;‍‍‍‍‍

With

/* remove itd from frame list and release itd */
transfer->transferSofar = USB_HostEhciSitdArrayRelease(ehciInstance,
                                                       (usb_host_ehci_sitd_t *)transfer->union1.unitHead,
                                                       (usb_host_ehci_sitd_t *)transfer->union2.unitTail);
transfer->transferSofar = (transfer->transferLength < transfer->transferSofar) ?
                                                      0 :
                                                      (transfer->transferLength - transfer->transferSofar);
1,773 Views
amwilhite
Contributor III

Jorge,

USB_HostAudioStreamRecv is called continuously in a task's loop as soon as the callback from the previous request is received. Therefore, data is being requested as fast as possible.

I was able to take a trace of the USB bus with another device connected to my RT1050 host.

This device has the following characteristics:

1 mono audio channel

16-bit resolution

44100 Hz sampling rate

Expected bandwidth: 44100 Hz * 2 bytes (16-bit) * 1 channel = 88,200 bytes per second.

Expected packet size = 88,200 bytes per second / 1000 frames per second = 88.2 bytes

Due to the non-integer packet size calculated, I expect 88-byte packets and a 2 byte-larger packet every tenth packet.

Looking at the trace, the USB device is indeed sending 88-byte and 90-byte packets.

In the _HostAudioInCallback, dataLength is still being set to 12. However, looking at the received data buffer in memory, I see that the RT1050 actually did receive all 88 bytes of the packet that I saw in the trace of the bus.

Here's the trace that shows the 88-byte packet and an expanded view that shows the packet's data.

88 Bytes.PNG

88 Bytes Data.PNG

Here's the memory viewer window showing the receipt of all 88 bytes of data that was seen on the bus.

Memery Viewer 88 bytess.PNG

I there a configuration setting that can lead to this improper reporting of dataLength, or is there a flaw in the USB stack that is causing this issue?

0 Kudos