This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320C5517 USB: EP0 cannot send Configuration Descriptors longer than 64 bytes

Other Parts Discussed in Thread: SPRC133

Hi All,

I am trying to run a UVC class on my DSPShield board. The silicon revision is AZCH20.

The problem Is that with no means I can get the request for Configuration descriptor to be sent correctly. According to the TRM, there should be an EP0 interrupt when transferring data to the Host and the FIFO is emptied, which can be used to load the next part of data in the FIFO.

I am using Ubuntu's Linux kernel module "usbmon" to sniff the packets exchanded with the host..

I took the CDC Example (from the CSL) as a base. do receive an interrupt after I have sent the first 64 bytes of the Configuration descriptor, but the UNDERRUN flag is set, which means that the host has sent an IN token , but there is no data in the FIFO. Which is an error condition, according to the TRM. Yet, if I insist and do send the next chunk of data, the 1-st chunk's URB packet is marked with error in the usbmon's log.

Did anybody succeed to run a USB class with a configuration descriptor longer than 64 bytes?

Regards

Georgi

  • When transferring data of length more than 64 bytes over EP0, you need to indicate the USB controller that data copied to FIFO is partial packet till we copy the last block of data. This will ensure that data completion handshake is not done at HW level till all the data is copied. Otherwise you will receive underrun error.

    In your code after copying first 64 bytes, you need to set TXPKTRDY but should not set DATAEND bit. When sending the last block of data, set both TXPKTRDY and DATAEND.

    You can refer CSL_USB_IntcExample example in CSL which allows to receive data more that 64 bytes on EP0

    - Pratap.

  • Hi, Pratap

    Thanks for the reply

    I do exactly what you suggest in the code. I set DATAEND on the last chunk only. 

    I am based on the cdc example from the CSL. You can take the example as is, and just for the sake of the experiment, set  bTotalLength field in the configuration descriptor to a number > 64, and you will see my problem. 

    Regards

    Georgi

  • Hi,

    It seems the next set of 64bytes need to be already present in the FIFO by the time the IN token arrives. Underrun is occurring because data is not being supplied fast enough, i.e. not as fast as expected by the host.
    Could it be possible to update the FIFO again with the next chunk of data before the IN token arrives?
     
    If that does not work, could you use double-buffering? A few excerpts from the TRM on the same:
     
    "Double packet buffering: When enabled, up to two packets can be stored in the FIFO awaiting
    transmission to the host. Double packet buffering is enabled by setting the DPB bit of T/RXFIFOSZ
    register (bit 4).
    If double packet buffering is enabled, then after the first packet has been
    loaded and the TXPKTRDY bit set, the TXPKTRDY bit will immediately be cleared by the USB controller
    and an interrupt generated so that a second packet can be loaded into the FIFO. The software should
    operate in the same way, loading a packet when it receives an interrupt, regardless of whether double
    packet buffering is enabled or not.
    NOTE: It may be necessary to set FLUSHFIFO bit twice in succession if double buffering is enabled.
    (bit 3 of PERI_TXCSR and bit 4 of PERI_RXCSR)."

    A similar situation is described in the Isochronous usb transmission section of the TRM. Could you please check if the mentioned solutions are able to alleviate the problem?
    Please find below a snapshot of the section. Have highlighted a few keywords/phrases:

    Best Regards.

  • Hi Ravi,

    I would be happy to use the double buffering feature, but I do not see how it can be enabled for EP0.

    According to the TRM, FIFO size for EP0 is 64 bytes and fixed address 0. How can I split it? I am not sure if TXFIFOSZ register exists for EP0 at all.

    The other problem is that even if the double buffering existe, how could I know when to fill the 3-rd chunk of data (suppose that the first 2 I can fill directly, regardless of the transfer status). I tried to poll the TXPKTRDY flag (TRM says that it is cleared after the FIFO is empied), but if I reload the FIFO immediately after this flag falls to 0, the host misses part of the data (so probably this is too early). According to the TRM, I should get an interrupt for EP0, just after the FIFO is empied. But it does not come.

    Regards

    Georgi

  • Double buffering is not supported for EP0.

    You should make sure your FIFO is loaded properly when EP0 interrupt is generated to avoid errors.

    - When you receive get config request, load first 64 block of configuration descriptor and set  TXPKTRDY

    - When you receive Tx completion interrupt for previous block, load next block of config descriptor and set TXPKTRDY

    -  - -

    - When you receive Tx completion interrupt for previous block, load last block of config descriptor and set TXPKTRDY and DATAEND

    - Pratap.

  • Thanks for the reply, Pratap.

    There is only 1 interrupt for EP0. How can I distinguish when it is for Tx completion? A state machine approach is suggested in the TRM.

    Unfortunately, the only interrupt I receive after the first 64 bytes are sent, comes with the UNDERRUN flag set.

    Do I miss some setting? Some flag? Or the TRM is incomplete?


    Regards

    Georgi

  • Hi Georgi,

    When you are building a class driver, it is recommended to maintain state machine for EP0 transfers to track what is the next state of EP0. 

    You can refer other examples of CSL USB for getting some clarity

    In csl_usb_intc_example.c, when EP0 transfer is more than 64, 'sentLongEp0Pkt' is set and is used to service next EP0 interrupt. This is very basic approach to confirm you are able to send more than 64 bytes over EP0 but works only for two packets.

    In csl_usb_msc_dma_example.c, MUSB_Handle_EP0_Intr() maintains EP0 state machine which is ideal approach to build a class driver.

    For resolving underrun error you can refer to csl_usb_intc_example.c and confirm you are not missing any sequence. Then you can design a state machine for EP0 transfers.

    - Pratap.

  • Yes Georgi. EP0 doesn't have double-pkt buffering. Missed that.

    "There is only 1 interrupt for EP0. How can I distinguish when it is for Tx completion? A state machine approach is suggested in the TRM.Unfortunately, the only interrupt I receive after the first 64 bytes are sent, comes with the UNDERRUN flag set."

    When you receive the interrupt, you may check the PERI_CSR0 register for TXPKTRDY bit. If it's shown to have been cleared, then it indicates it's a TX completion.

    You would also note that this register doesn't have any UNDERRUN bit. This bit is present only in PERI_TXCSR registers which cater to EP1-4. You mention that you're concerned only with EP0. So this bit being set in loc. 0x8412 means it's SENTSTALL, not UNDERRUN (refer PERI_CSR0, not PERI_TXCSR).

    PFB a snapshot. The highlighted section might be the cause of this bit being set. Could there be a control from the host side to avoid this additional IN request being sent, as this might be constituting a violation of the protocol set by the command byte sent in the control transaction.

    Best Regards.

  • Hi Ravi,

    Thank you for the fast reply. Indeed, there is no UNDERRUN flag for EP0. Is is SENTSTALL what is set. Now I handle it correctly and clear the SENTSTALL, SENDSTALL bits and flush the FIFO. Then the Host resends the command few more times with the same end result.

    The question now is how to avoid this STALL condition from the Host. There is no STALL condition if the configuration descriptor's length is set to <= 64 bytes.  I will have to investigate. Your answer really helped me to go a step further.

    Regards

    Georgi

  • Hi Georgi,

    USB controller sends stall to host when error happens in data transfer or unexpected request comes from host. In your case it should be because of receiving IN tokens when FIFO is empty which is underrun condition.

    If you are using USB_coreEventProcessEp0() function, you need to modify it to handle transferring multiple packets. This function calls USB_sendEpZLP() for every request which will end the transfer. 

    - Pratap.

  • Following from the highlighted section in the snapshot above, could you also confirm:

    i) if the 8-byte command is being recognised by your software running on the C5517 end?

    ii) if the C5517-host protocol is clearly and correctly defined vis-a-vis the data payload size? That is, could it be possible that the C5517-end software you're running is somehow not defining the payload size correctly in the device request during the SETUP phase and thereby not expecting the IN token from the host.

    You mentioned you're using the cdc example as a reference. Please consider the following definitions:

    Uint16    cfgDesc[40] = {
           // Configure descriptor
           0x0209, // USB configure descriptor type | size of configure descriptor
           0x0034, // Total length of data for this configuration
           ......
           ......
           ......
           ....

    // Configuration Descriptor for USB FullSpeed
    Uint16    cfgDescFS[40] = {
     // Configure descriptor
     0x0709, // USB alternate configure descriptor type | size of configure descriptor
     0x0034, // Total length of data for this configuration
           ......
           ......
           ......
           ....

    You're sending a payload of 64bytes as you'd mentioned, but the "total length of data payload" in the config-descriptor is defined to be 0x34, i.e.just 52 bytes.

    Although payload size you're sending is <txmaxp for EP0, it is greater than what you'd defined in the protocol you'd established. Hence, the C5517 USB controller is sending a STALL as it was not expecting an IN token from the host requesting for more data even after having sent the 52bytes (0x34) as per the agreed-upon configuration.

    And that's why the controller sets the SENTSTALL bit and generates an interrupt for EP0. To correct the situation, you may change this field in the cfgDesc{} or cfgDescFS{} structure whichever is relevant for your application.

    Hope this solves the issue.

    Best Regards.

  • Correction: The 0x34 was for the particular config descriptor defined there.

    Nevertheless, if you could check what you're sending in this field as data-payload size and see if by any chance it is less than what you're actually sending.

    Host-C5517 USB EP protocol shouldn't be violated for the STALL to be not to be sent.

    Best Regards.

  • My configuration descriptor is 205 bytes long.

    This is a part of the usbmon's log:

    ffff8800c5f82780 764099747 S Ci:1:051:0 s 80 06 0100 0000 0012 18 <
    ffff8800c5f82780 764099974 C Ci:1:051:0 0 18 = 12010002 ef020140
    51041190 00010102 0001
    ffff8800c5f82780 764100158 S Ci:1:051:0 s 80 06 0200 0000 0009 9 <
    ffff8800c5f82780 764100343 C Ci:1:051:0 0 9 = 0902cd00 02010080 fa
    ffff8800c5f82780 764100365 S Ci:1:051:0 s 80 06 0200 0000 00cd 205 <
    ffff8800c5f82780 764100720 C Ci:1:051:0 -32 64 = 0902cd00 02010080
    fa080b00 020e0300 00090400 00010e01 00000d24 01000150
    ffff8800c5f82780 764100741 S Ci:1:051:0 s 80 06 0200 0000 00cd 205 <
    ffff8800c5f82780 764101218 C Ci:1:051:0 -32 64 = 0902cd00 02010080
    fa080b00 020e0300 00090400 00010e01 00000d24 01000150
    ffff8800c5f82780 764101239 S Ci:1:051:0 s 80 06 0200 0000 00cd 205 <
    ffff8800c5f82780 764101843 C Ci:1:051:0 -32 64 = 0902cd00 02010080
    fa080b00 020e0300 00090400 00010e01 00000d24 01000150
    ffff8800c5f82780 764101895 S Co:1:001:0 s 23 01 0001 0008 0000 0
    ffff8800c5f82780 764101948 C Co:1:001:0 0 0

    "18 <" means that host expects 18 bytes

    "18 = " means Host has received 18 bytes

    "-32" is error code.

    I did place a condition in USB_coreEventProcessEp0:

        if(pContext->ep0State == CSL_USB_EP0_IDLE){
                            USB_sendEpZLP(CSL_USB_EP0);
         }

    I can see that I do not enter this function anymore but this did not help.

    Maybe I will have to go step by step in the debugger to see what happens after TXPKTRDY flag for the first 64 bytes is set.

  • The log does show the host receiving only 64 out of 205bytes.

    Could you please share how you are defining your total payload size? If you could share the section of code defining the payload size (note: not the txmaxp reg setting).

    Best Regards.

  • Hi Ravi,

    I guess you are talking about the wTotalLegth field in the configuration descriptor. Right?

    Because there is no general setting for the length of a packet to send. The transfer ends when the FIFO is emptied.

    /* Standard High Speed Configuration Descriptor */
    Uint16 USBHSConfigDscr[] =
                {

                    /* Configuration Descriptor Type */
                    0x09 |                            /* Descriptor Size */
                    2 << 8,        /* Configuration Descriptor Type */
                    0xCD | 0x00 << 8 ,                      /* Length of this descriptor and all sub descriptors */
                    0x02 |                            /* Number of interfaces */
                    0x01 << 8,                           /* Configuration number */
                    0x00 |                           /* COnfiguration string index */
                    0x80 << 8,                           /* Config characteristics - Bus powered */
                    0xFA |                            /* Max power consumption of device (in 2mA unit) : 500mA */

                    /* Interface Association Descriptor */
                    0x08 << 8,                           /* Descriptor Size */
                    11 |       /* Interface Association Descr Type: 11 */
                    0x00 << 8,                           /* I/f number of first VideoControl i/f */
                    0x02 |                           /* Number of Video i/f */
                    0x0E << 8,                           /* CC_VIDEO : Video i/f class code */
                    0x03 |                            /* SC_VIDEO_INTERFACE_COLLECTION : Subclass code */
                    0x00 << 8,                           /* Protocol : Not used */
                    0x00 |                           /* String desc index for interface */

                    /* Standard Video Control Interface Descriptor */
                    0x09 << 8,                           /* Descriptor size */
                    LIBUSB_DT_INTERFACE |        /* Interface Descriptor type */
                    0x00 << 8,                           /* Interface number */
                    0x00 |                           /* Alternate setting number */
                    0x01 << 8,                           /* Number of end points */
                    0x0E |                           /* CC_VIDEO : Interface class */
                    0x01 << 8,                           /* CC_VIDEOCONTROL : Interface sub class */
                    0x00 |                           /* Interface protocol code */
                    0x00 << 8,                           /* Interface descriptor string index */

    ...... etc

    Regards

    Georgi

  • Hi Georgi,

    Please check the USB CSL code with few modifications here 0243.csl_usb.zip.

    I have added EP0 state check in USB_coreEventProcessEp0() for config description request and corrected the function USB_clearEpRxPktRdy().

    Ep0 state check in USB_coreEventProcessEp0 allows to transfer configuration descriptor of length more than 64. You may add state check for other requests as per your need. Hope this helps to resolve your issue.

    - Pratap.

  • Yes Georgi. Exactly.

    CSLv3.03 usb examples do deal with Config descriptors >64bytes in length.

    Could you please download the same from: http://www.ti.com/tool/sprc133

    The usb poll, interrupt, dma examples (inside c55xx_csl\ccs_v5.0_examples\usb folder) have the config descriptor size as 74bytes to be specific.
    You would also require to download and install the usb host driver the link for which is provided in the C5515 bootloader spec at: http://www.ti.com/lit/an/sprabd7b/sprabd7b.pdf
    Therein the following link would be available in the first page first para: http://www-s.ti.com/sc/techlit/sprabd7.zip.

    I have run the same and they do work fine.

    The definition of the config descriptor is as follows:
    Uint16    cfgDesc[40] = {0x0209, 0x004A, 0x0101, 0xC001, 0x0928, // configure descriptor
                                            ...........

                                            ...........

                                            ...........

                                           };

    0x4A (74bytes) being treated as the size of the descriptor further down in the code where the USB_postTransaction() function invokes the same.
     
               CSL_Status CSL_usbPollTest(void)
              {
                          .............
                          ............
                          ..................
                          case CSL_USB_GET_DESCRIPTOR :
                                                 switch(usbSetup.wValue >> 8)
                                                 {.........
                                                  ..........
                                                   case CSL_USB_CONFIGURATION_DESCRIPTOR_TYPE:
                                                           if(usbSetup.wLength == 0x0009)
                                                           {

                                                                     cfgDescPtr = cfgDesc;

                                                                     status = USB_postTransaction(hEp[1],

                                                                                      9, cfgDescPtr,

                                                                                      CSL_USB_IN_TRANSFER);

                                                           }

                                                           else

                                                           {

                                                                     if((cfgDesc[1] & 0xFF) > 0x40)

                                                                     {

                                                                      sentLongEp0Pkt = 1;

                                                                     }
                                                                     cfgDescPtr = cfgDesc;
                                                                     status = USB_postTransaction(hEp[1],

                                                                          cfgDesc[1], cfgDescPtr,

                                                                          CSL_USB_IN_TRANSFER);

                                                            }
                                                            break;
     
    hEP[1] is defined up in the code before this call as:

     hEp[1] = USB_requestEndpt(hUsbDev, CSL_USB_IN_EP0, &status);
     if(status != CSL_SOK)
     {
      printf("\nUSB request ep failed\n");
      return(result);
     }

     epCfg.xferType = CSL_USB_CTRL;
        epCfg.eventMask = CSL_USB_EVENT_EOT;
        epCfg.maxPktSize = CSL_USB_EP0_PACKET_SIZE;
        epCfg.fxn = NULL;

     /* Initialize the Control Endpoint IN 0 */
        status = USB_configEndpt(hEp[1], &epCfg);
     if(status != CSL_SOK)
     {
      printf("\nUSB_configEndpt failed\n");
      return(result);
     }

     

    Hence, I do believe the USB_postTransaction() function does handle the config descriptor length >64bytes.

  • Hi Pratap,

    Thank you very much for the modified CSL. It works now. My device is correctly recognized and configured under Ubuntu as Video class device.

    Regards

    Georgi

  • Hi Georgi,

    Good to know that your code is working now. You can build your EP0 state machine around  USB_coreEventProcessEp0() function to handle other requests or error cases like stall if needed.

    USB data transfers on EP0 are very much simple when compared with other end points. Never get things complicated by trying double buffering or other stuff especially when your device enumeration is not successful. USB CSL examples provide enough details on usage of different end points for different use cases. You can get good idea on building class driver using USB CSL by referring to these example codes.

    Happy programming!! 

    - Pratap.

  • Hi Pratap,
    As I can see in correction of USB_clearEpRxPktRdy function, you removed the setting of CSR0_INDX_DATAEND bit. For what purpose??