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.

TM4C1294NCPDT: USB 2.1 BOS descriptor

Part Number: TM4C1294NCPDT

Hello,

I have been trying to implement USB 2.1 composite device with one serial and one bulk interface with TM4C1924NCPDT. I tried searching the source code but couldn't find ways to have this work with the Tivaware USBlib.
Am I missing something? Can someone point me to the right direction regarding this?

Thanks!

-Gaurav

  • Gaurav Pandit said:

    Part Number: TM4C1294NCPDT

    Hello,

    I have been trying to implement USB 2.1 composite device with one serial and one bulk interface with TM4C1924NCPDT. I tried searching the source code but couldn't find ways to have this work with the Tivaware USBlib.
    Am I missing something? Can someone point me to the right direction regarding this?

    My reading of the USB library is that what they consider a "composite" device is really one which has more than one instance of the same Device Class. That is, consider a CDC device which has two serial ports in both directions.

    From the USB perspective, a composite device is really one which has multiple interfaces. So, for example, a standard USB Audio Device is actually a composite device, as it has one Audio Control interface and one or more Audio Streaming interfaces. The TI USB library, however, does not consider their Audio Class driver to be "composite."

    So as you read through the library, you'll see that it's trying to be very clever. If your device has two instances, it figures out which endpoints to use and keeps track of it all. But it's a whole lotta spaghetti and the redirections and pointers to structures which hold pointers to structures is maddening.

    This all means that you won't be able to easily bolt the two interfaces together using the TivaWare framework.

    The documentation for the USB library has a small section on how to implement device classes which are not supported by the library. Start there. Basically, you should create the descriptors for your composite device and then make them work with the tConfigHeader and tConfigSections types.

    Then create an instance of the tCustomHandlers structure. This structure holds all of your callbacks for USB events of interest. The library handles most of the events (like enumeration) but there are things you need to handle -- mostly your endpoint data transfers.

    Then your descriptors and the handler structure get wrapped up into a tDeviceInfo structure. And then that structure gets passed as a parameter in the call to USBDCDInit().

    To get it all to work, all that really matters is that your descriptors are correct and that you handle your endpoint data. The examples like the usb_dev_bulk put a lot of obstacles in your way. That example uses a tUsbBuffer, which is a software ring buffer that's in between the endpoint data handler and your application. You can dispense with that!

    All the action is done in the Endpoint Handler which you included in the tCustomHandlers structure. There is one USB interrupt which is handled first by the library. If the library cannot deal with the condition, it invokes the Handler you defined. And in your handler, you deal with the data or the condition which triggered the interrupt. 

    Anyway, you're not wrong if you think the library is confusing.

  • Hi Andy,

    Thanks for this really detailed post. You've described the whole situation very well! ...And I will join you in agreement that the library is very confusing. It has frustrated me as well as someone who inherited it.

    Hi Gaurav,

    Just to echo what Andy said, that ability isn't natively part of the USB library, so you would need to go through implementing it on your end. I agree with Andy's guidance regarding how to go about that task.

  • One more thing. Ralph knows that I've been working (as a background task behind the stuff that pays the bills) on implementing USB MIDI, which is possibly the simplest communication format one might imagine. USB MIDI devices are composite in the same way that Audio Class devices are composite. There is an Audio Control interface and a (single) MIDI Streaming interface. (In fact, one could add one or more Audio Streaming interfaces to the device.) I'm doing this because I need it for a design.

    Last night I finally got the USB OUT transfers to work!

    USB MIDI sends four-byte messages over bulk endpoints. The endpoint packet can contain one or more of those messages. For OUT transactions (host to device), the HandleEndpoints handler (invoked by the main USB0 ISR) checks the interrupt status (passed to the callback) to make sure that the interrupt was from my OUT endpoint, and then it checks the endpoint status to see if that is "receive packet ready."

    If so then I get the number of bytes actually received in the OUT transfer (USBEndpointDataAvail) and then actually get the data from the endpoint FIFO into a local 64-byte (max packet size) buffer (USBEndpointDataGet). Then I take that buffer, and use it to build four-byte USB MIDI packets. Each packet gets pushed onto a FIFO. Finally, I ACK the data (USBDevEndpointDataAck) which lets the host know that the packet was accepted. It was the ACK that I was stuck on, but the Bulk Device example had that ACK call in USBDBulkPacketRead, so once added, the transfers worked.

    The main loop monitors my message FIFO and if it has messages, it pops them one at a time and prints the contents of each to the terminal (UARTprintf). The next step is to do something more useful with the messages, like light LEDs in response to Control Change messages or perhaps route the message out a real UART-based MIDI port. (I already have the UART-based MIDI working well.)

    The next step is the USB IN transactions, in response to a button press. I will build up message packets and push them to a FIFO, but then the question becomes how do I start the transmission? What I've done in the past was to queue up data to send in a buffer and then invoke the ISR that is called when transmit is complete. That ISR looks to see if there are more bytes to send, and if so starts that process. But I don't know how I can invoke the ISR (or more likely the EndpointHandler callback) in this case.

  • Thanks Andy and Ralph for the pointers! I wasn't really expecting such a great response! Really appreciate it!
    The usb library is little confusing but, I think I will draw out a map or something for all the usb requests to understand the structure better and configure it correctly for my application. :)