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.

TM4C1233E6PM: TIVAWare USB Library HID Application

Part Number: TM4C1233E6PM

 

Customer trying to implement a USB HID and transfers data on USB HID Frames. The packet length is variable and  implementation can send or receive data at any time (within USB HID restrictions of course). The packets also include a Sequence Number to allow the host and or device to notice if packets are being dropped. Using TM4C1233E6PM, they are using the TivaWare USB Library to handle the USB layer. Over long runs of data being transferred in both directions, they noticed we would receive duplicate packets very sporadically. After troubleshooting with a debugger, they noticed the following:

 

Using a USB analyzer, we noticed that we would have random ‘Classification Error’ failures. The USB analyzer defines a ‘Classification Error’ as ‘An error occurred during class-level parsing.’ This message will always have 0 byte payload


  • This ‘classification error’ packet will always occur 1-2 Frames after a Frame where there was an input report and output report.

     

  • While troubleshooting, they noticed that sometimes immediately after the ‘classification error’ packet the ‘USBDHIDReportWrite’ function that returned ‘0’ (which per the function header, means a failure) when it was called.

  • When the ‘USBDHIDReportWrite’ function that returned ‘0’ (which per the function header, means a failure) they would see duplicate packets being sent to the host

  • The code is set up so if ‘USBDHIDReportWrite’ returned ‘0’, they would not move the buffer forward to the next USB HID packet, as they interpreted a failure as meaning they would need to reload it into the USB HID driver again.

  • Because of this observation, they decided to modify the code to advance the buffer regardless of the value that was returned from the ‘USBDHIDReportWrite’ function

  • Running a quick test with the debugger, they were able to trap on the on a case where ‘USBDHIDReportWrite’ returned ‘0’ and noticed that no packets were duplicated (the ‘classification error’ failure was still present)

  • Based on these results, they decided to run a test overnight to see the performance

    • In this software configuration, they verified that the approximately 5 million messages that were sent and/or received contained the correct sequence number, resulting in none being analyzed as dropped and/or duplicated

 

They believe they have found a workaround for the duplicate message, but based on the function header, they are not cofident that they are using the library in the correct way. They believe the ‘classification error’ packet is acceptable as it will be dropped, but  would not consider it ‘expected’.

The larger concern for them is if they are doing something incorrect causing the ‘USBDHIDReportWrite’ function to report a failure, yet still send out the message.


 


  • Hello Lawrence,

    To really be able to dig in and find out if the header description is misleading or if they are misusing the API, I'd need to be able to observe the issue in a manner where I can do a full debug on it. This means I would need source code and ideally it being on a known good TI hardware setup such as a LaunchPad.

    I would say though that it wouldn't be the first time a TivaWare header isn't 100% accurate. It sounds like the case they hit isn't a common case, so it may have not been tested or observed to see exact function behavior. Furthermore, if the communication channel didn't break as a result, it could be possible it just was never noticed during testing.

    Looking into the details of the API, there are two spots it can return a false at. I would like to see if they can trace which of the two is hit when that issue occurs. The one I would be most interested to know if they hit is this one:

        //
        // Can we send the data provided?
        //
        if(psInst->iHIDTxState != eHIDStateIdle)
        {
            //
            // We are in the middle of sending another report.  Return 0 to
            // indicate that we can't send this report until the previous one
            // finishes.
            //
            psInst->bSendInProgress = false;
            return(0);
        }

    They should be able to set some sort of flag or counter in there. If they do a counter they may even be able to count how many times the counter increments vs how many duplicate packets they got.

    Looking into our TivaWare examples it looks like the examples were setup to match the header and a return of 0 was treated as false, but again, not sure if the devices and the library were ever tested in a situation that both an in and out report were in the same frame, or if that occurred, would such an event have been noticed.

  • Ralph,

    Customer placed counters within the USB Library and it never hit the if statement that you referenced. Whenever the issue occurs, they have noticed the following:

    - Within the USBDHIDReportWrite function, the ScheduleReportTransmission function always returns -1
    - Within the ScheduleReportTransmission function, the MAP_USBEndpointDataPut function always returns -1

    Not sure if that helps. Any suggestion?
  • Hello Lawrence,

    Okay, that is helpful in that we can at least trace the failure to the source now.

    In DriverLib the USBEndpointDataPut function has only the following block which would return a -1:

        //
        // Don't allow transmit of data if the TxPktRdy bit is already set.
        //
        if(HWREGB(ui32Base + USB_O_CSRL0 + ui32Endpoint) & ui8TxPktRdy)
        {
            return(-1);
        }

    So this would indicate that the USB data was never actually transmitted in that function call per what the DriverLib function indicates... which I don't feel aligns with their observations. That said, I am not sure yet what the ROM API does so it may be possible there are differences between the ROM API and the latest DriverLib API.

    I am going to see if I can find out the details of the ROM API call, but maybe they can try and map to the DriverLib USBEndpointDataPut call which is in usb.c instead and see if the results are identical or if they change at all.

    Additionally, looking at the ScheduleReportTransmission function in further detail, the following comment is said: "The function ensures that reports are sent as a sequence of full packets followed by either a single int16_t packet or a packet with no data to indicate the end of the transaction." - made me wonder a bit if this might be the 0 byte payload packet they are seeing...?

    While the function says that it ensures that, I don't see any sort of multiple send sequence in ScheduleReportTransmission or USBDHIDReportWrite to indicate that a 'sequence of full packets followed by...' could be executed without help from higher layers. All this doesn't yet align with the above statements about the ROM API call returning a -1, but just another train of thought that came to mind on my end...

  • Hi Lawrence,

    I confirmed the API's function the same. I'd like to understand if when the MAP_USBEndpointDataPut function returns -1 if they see a USB packet being transmitted anyways. Is this something they can check?
  • Update from the customer:

    They was able to verify that the code that was in the previous email is where the transaction fails. By using the logic analyzer and breaking on the ‘return (-1)’ line of code, it shows that the last packet to be sent out was the 0 byte packet. After the code was paused, they placed a breakpoint on the call the USBDHIDReportWrite, which should pause the code before another packet was due to be sent out. When this breakpoint was hit, the logic analyzer showed that one packet had been sent to the host. I confirmed that this was the packet that was due to be sent out the last time where the transaction failed.

     

    As a sidenote that came to mind while tracing this, they are actually implementing a USB Composite Device (HID on Endpoint 1, DFU on Endpoint 2)

     

    At this point they removed all breakpoints except for the ‘return(-1)’ line within USBEndpointDataPut. Once this breakpoint was hit, they added another breakpoint at the beginning of the USBEndpointDataPut function, hoping to catch where the next message transmission was coming from. They were able to trap on this and here is the where this unexpected packet is coming from:

     

    • USB0DeviceIntHandler Interrupt is triggered

    • USB0DeviceIntHandler Interrupt calls USBDeviceIntHandlerInternal

    • USBDeviceIntHandlerInternal calls the Endpoint Handler (g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler(pvInstance, ui32Status))

      • I believe ui32Status = 0x20002

    • The Composite Endpoint Handler (HandleEndpoints within usbdcomp.c) calls the HID Endpoint Handler (HandleEndpoints within usbdhid.c)

    • HandleEndpoints calls ProcessDataToHost

      • Enters based upon ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint)) being TRUE

      • Ui32Status = 0x20002 and PsInst->ui8INPoint = 0x10, therefore (0x20002 & (1 << ((0x10) >> 4) = TRUE

    • ProcessDataToHost calls ScheduleReportTransmission

      • (psInst->ui16InReportSize (23) == psInst->ui16InReportIndex (0))

      • 23 is the length of the message to be sent out that failed

    • ScheduleReportTransmission calls USBEndpointDataPut with the pui8Data and ui32NumBytes of the message that was incorrect.

     

    Based on the comments in the library, maybe the issue is in the ProcessDataToHost function being able to call ScheduleReportTransmission? That is the first place that we see remnants from the failed request.

  • Hello Lawrence,

    "ScheduleReportTransmission calls USBEndpointDataPut with the pui8Data and ui32NumBytes of the message that was incorrect."
    "Based on the comments in the library, maybe the issue is in the ProcessDataToHost function being able to call ScheduleReportTransmission?"

    • I'm not sure I follow here. ui32NumBytes is calculated by this:
    ui32NumBytes = (uint32_t)(psHIDInst->ui16InReportSize -
                                  psHIDInst->ui16InReportIndex);
    • If that variable is incorrect, then the pointer for the HID Device Data is the source of the fault. Note that the ScheduleReportTranmission API is only called if this if statement is false:
    if(psInst->ui16InReportSize == psInst->ui16InReportIndex)
    • These are the same variables used to calculate ui32NumBytes, so if they are wrong, than the issue isn't in ProcessDataToHost but rather with the packet being fed to it from further up the line. I don't see a reason based on this example why the ProcessDataToHost API should not be allowed to call the ScheduleReportTransmission API.

    This all said, I had no role in any of the development of the USB Library, I can just interpret the API's based on what they do and what comments are left behind. So it isn't clear to me where the instance data issue is originating from.

    If they can shed more light into their reasoning that the ProcessDataToHost function is at fault for it's API call, I'm all ears and will look further into it, but based on what they provided I am not seeing that to be case currently.

    I think going back to the original topic at hand which was an inquiry of whether they are misusing the API, right now I have yet to see anything which makes me think that specific API is misused. Rather, I think it might be prudent to trace where exactly the 23 byte long packet is being fed into the instance data and ascertain if is being loaded correctly or not and/or if it's being signaled to go to the correct Endpoint Handler. It seems somewhere along the line, the USB stack is not anticipating that packet coming in. Also I will note that I'm not very familiar with composite devices so I am not sure if there are any added complications which could amount from that.

  • Richard,

    Additional information below.  Note customer provided below before receiving your latest feedback:

    My expectation for the code is that for each USBDHIDReportWrite call that is successful, we would receive one call to USB TX Callback with a status of ‘USB_EVENT_TX_COMPLETE’. Our design uses these function calls to lock and unlock our mutex that will restrict multiple calls to USBDHIDReportWrite when it is unexpected. I used a breakpoint to see if there is a case in which the TX Callback was received without the mutex being locked. This breakpoint was hit and the USB Analyzer shows this was received immediately after the 0-byte ‘classification error’ message. Looking at the ProcessDataToHost function, the ‘psInst->ui16InReportSize’ function reports 38 which lines up with the length of the message before the 0-byte message. I have not been able to further track down where the 0-byte message comes from. Eventhough having this message be sent should not cause any failures on the host side, it would still be nice to understand the reasoning behind why we are seeing and potentially resolve it.

     

    For the duplicate message issue, one solution I am testing is to set ‘psInst->ui16InReportSize’ to 0 within ‘USBDHIDReportWrite’ if we receive a -1 from ‘ScheduleReportTransmission’. My thought is that since we have already verified that we are in the ‘eHIDStateIdle’ state before entering ‘ScheduleReportTransmission’, if there was a failure scheduling the packet, we should reset the Report size to make it look like no packet was loaded into the FIFO. I am running a long term test to see if this fixes our issue, however while using breakpoints and a logic analyzer to gather a couple data points, it looks promising.