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: Implementing other USB device classes

Part Number: TM4C1294NCPDT

This question is likely rhetorical, but here goes.

Has anyone had any success in implementing a USB device using a class other than the handful which are supported by the Tiva USB library? 

The library is crazy. Multiple levels of redirection, structures within structures, and while there is some documentation on how to do this, it's not very helpful.

Specifically, I want to implement the USB MIDI Device class. This is a subset of the Audio Class (which has "support" in the Tiva USB library but no examples are provided). Since it has two interfaces, it should be configured as a composite device, with the Audio Control interface (over the control endpoint) and the MIDI Streaming interface (which uses two bulk endpoints, one in each direction).

I started with the provided bulk example, because that appears to be close to what I need. But jeez, can it be any more complicated? Just the way the descriptors are set up, with the tConfigSection, and then an array of them, and then the tConfigHeaders and then an array of them -- why? Why not just let the descriptors be two structures, one for the device descriptor and one for the entire configuration descriptor? 

There is a structure defined called tUSBBulkDevice which gets passed around a lot, and this has a private member structure tBulkInstance and just unwrapping this all is difficult because nowhere is it documented why this is all set up as it is. (The great failing of doxygen!) 

(I notice that this is all pretty much the same for the MSP432 devices.)

I implemented this device class on the Silicon Labs EFM8UB2 and also on their EFM32GG11, using their provided USB device library, and the whole process was straightforward and took less than a week.

  • Hello Andy,

    As someone who inherited USB support for the TM4C devices and had absolutely no hand in how the library was written... your summary about the library is wholly accurate. I wish I had the time to rip it apart and build it again from the ground up. For the parts that are supported, there's no doubt it works and it works very well. But it is unnecessarily painful to debug or add features to it.

    I am not sure if any community members would be able to offer their guidance on how to do the MIDI device class, we shall see. I don't recall MIDI really coming up before.

    For what it's worth, I added a Host CDC class not long back that is still to be released, and I could share those files with you if you want to see how I went about the process for that case. Though that was much simpler than what you are trying to do, I think it could be a good reference point for you?

    Ideally you'll be able to reuse a lot of the driver files itself and just have to make the device level files to handle the audio and composite pieces... that said, the actual composite driver is probably the most painful area of the library. Particularly how descriptor creation is handled. I anticipate you'll have some added challenges there, though if you get past the composite piece successfully you are probably well on your way.

  • HI Andy,

      Unfortunately we don't have an USB device example using audio or MIDI class. However, there are both an usb_host_audio example and an usb_host_audio_in example in the TivaWare library. You can find them in <TivaWare_Installation>/examples/boards/dk-tm4c129x/.  Although they are not for the device, I hope it will be easier for you to use these host examples as a starting point than the usb_dev_bulk. 

  • Hi Ralph,

      We responses cross. Thanks for your input. 

  • Hi, Ralph,

    Thanks for the note of support and agreement! I can understand your position of having to support something you didn't write (been there).

    As for the host CDC example -- as you know, host and device have different functions. For the device side it really comes down to presenting descriptors (in response to GET_DESCRIPTOR requests) and then when the device is finally active, watching OUT endpoint buffers for new data and presenting IN endpoint buffers with data to send to the host. Conceptually it's simple!

    From what I see in the bulk device example, I should be able to use most of it, as it really does just present the two endpoint buffers. But it's getting the descriptors in the format expected by the library that is vexing. What I've done in the past when bringing up a USB device on a new processor is to get the descriptors working and have the device enumerate as expected. Once the host sees it as a device of class XYZ, then I get the data handling working. Right now I don't even get an inkling that it's attempting to connect to the USB, much less even start enumerating.

    USB MIDI is actually a friendly device class to implement, as it requires no custom drivers for the host and there are plenty of host tools one can use to send and receive data. (I like PocketMIDI on the Mac, and there's the pyMIDI library for doing MIDI in Python). Once you understand how the class defines a "Jack" and the connection between "Jacks" it all just works.

    If you choose to actually re-do the USB device library from scratch but decide to support only the MSP432E4 devices, I am totally fine with that, as they seem to be a superset of the Tiva parts. I chose the Tiva parts for this because they offered what I needed -- High-Speed USB (with the add-on ULPI chip) and 100 Mb Ethernet, primarily, and to be fair, the TI web site device-filter/search feature showed the Tiva parts ahead of the MSP432E4 when I started looking!

    Or maybe I'll just jump in and write my own USB device stack for the parts. It'll be fun, right?

    Thanks.

  • Hello Andy,

    From a descriptor standpoint, you'll want to study the USBDBulkCompositeInit API a lot. That's going to handle passing the descriptor information.

    For a quick test if the descriptors are similar in structure, you could try modifying the Bulk Device descriptors in usbdbulk.c. You'd need to re-build usblib or just link it manually so the changes apply, and then see if maybe you can prove out enumeration that way to start.

    Hopefully you have a USB analyzer to see how the data sent is changing as you make modifications.

  • Ralph Jacobi said:

    Hello Andy,

    From a descriptor standpoint, you'll want to study the USBDBulkCompositeInit API a lot. That's going to handle passing the descriptor information.

    I see that! My initial cut at this was to do basically what was in that function as it seemed to be the one which was doing the real work. What gets confusing is that from a USB spec perspective, this class is a composite device. It has two different interfaces. But it's not two instances of the same class, in the way that having two serial ports in a CDC is composite. As I understand it, the library uses "composite" to mean the latter, and that's how it keeps track of the transactions intended for the two different instances.

    For a quick test if the descriptors are similar in structure, you could try modifying the Bulk Device descriptors in usbdbulk.c. You'd need to re-build usblib or just link it manually so the changes apply, and then see if maybe you can prove out enumeration that way to start.

    That's a good idea. I already copied the usblib sources to my project (and set the -include option to point to it, and also I deleted the libusb.a reference in the linker stuff). I did that so I could step through the library in the debugger to see what was calling what.

    Hopefully you have a USB analyzer to see how the data sent is changing as you make modifications.

    That I do! I've had the Ellisys USB Tracker for many years -- I bought it to do a TUSB3200A design. (There, I'm aging myself.) Though I should probably spring for one of the Total Phase Beagle 480s for high speed stuff, as the ultimate goal of this is to build a board with the ULPI chip and do High Speed USB. (Nothing I'm doing calls for USB 3 SuperSpeed, so using USB 2.0 stuff suffices.)

    Anyway, I'll shoehorn my descriptors into a copy of the bulk example and report back.

  • Ralph Jacobi said:
    For a quick test if the descriptors are similar in structure, you could try modifying the Bulk Device descriptors in usbdbulk.c. You'd need to re-build usblib or just link it manually so the changes apply, and then see if maybe you can prove out enumeration that way to start.

    OK, I just did that (had to fight a bit with the include paths as they're inscrutable) and success -- the thing enumerates as a USB-MIDI class device.

    It doesn't show up in the Mac's Audio MIDI Setup, but that's likely because the data transfers aren't working.

  • Hi Andy,

    Great to see the proof of concept worked well. You should be able to clone how the bulk descriptors are handled for the most part then to make the custom MIDI descriptors. Might be a bit tedious with all the structures, but hopefully that gives you a solid starting point (and maybe you can simplify some of it in the process..)

    By the way for when you get moving toward high speed, if you haven't seen it yet we have a TI design for that: http://www.ti.com/tool/TIDM-TM4C129USBHS

  • After a few days away from this, I am back at it.

    All of the documentation for all of the class examples, for both Tiva and for the MSP432 devices, has some verbiage about setting up various things, and then there is always a call:

    "From your main initialization function call the [CLASS] device driver initialization function to configuration the USB controller and place the device on the bus."

    pvDevice = USBDCDInit(0, &g_sMouseDeviceInfo);

    (A mouse device, for example)

    There's a problem here.

    The function USBDCDInit() is declared as:

    void
    USBDCDInit(uint32_t ui32Index, tDeviceInfo *psDevice, void *pvDCDCBData);

    -- there is a third argument!  This argument is a pointer to a structure which includes other structures and it's an undocumented mess.

    I mean, I understand the reason behind the goofball structure. The sources are all compiled down to a library so the developer needs to only add the library to the project, and fill in the blanks with VID, PID and some strings.

    It's simply unusable, and it makes me wonder if anyone bothers. The worst part is that none of this stuff is new -- the Tiva USB library document is dated March 2013 -- Revised July 2016. So it's dead. The MSP432 library is exactly the same, so it too, is dead.

    I'm looking at alternative solutions. I'd like to actually build something.

  • Hello Andy,

    For CDC you would use USBDCDCInit to initialize, not USBDCDInit. 

    USBDCDCInit will later call USBDCDInit within it, and it is declared as:

    void *
    USBDCDCInit(uint32_t ui32Index, tUSBDCDCDevice *psCDCDevice)

    That said, I can understand your frustration. We obviously acknowledge it could be done better, but when it's working for 1000's of customers and re-doing it from the ground up would be a huge effort, the ROI so far as not been high enough compared to other activities. I'll add your issues & complaints to the list of USB issues, but unfortunately this has come up rarely as most use usblib as is.

    As far the documentation date, that would be for 2.1.3. There should be a document in your TivaWare for Feb 2017 which had been the last TivaWare release. There is a new release coming in March, although for usblib the changes are limited to a couple bug fixes and adding USB CDC Host.