I think there is an issue in the TivaWare revision 2.1.2.111 USB library (usblib). When operating in host mode, the enumeration process only seems to enumerate the first interface, numbered 0 (zero), of a connected USB device.
Here is the code from the beginning of the function USBHCDOpenDriver() from host/usbhostenum.c. Notice how this code gets one interface from the device and then loops through the Host Class drivers to find a match:
static int32_t USBHCDOpenDriver(uint32_t ui32Index, uint32_t ui32DeviceNum) { int32_t i32Driver; uint32_t ui32Class; tInterfaceDescriptor *psInterface; tEventInfo sEvent; ASSERT(ui32Index == 0); // // Get the interface descriptor. // psInterface = USBDescGetInterface(g_sUSBHCD.psUSBDevice[ui32DeviceNum].psConfigDescriptor, g_sUSBHCD.psUSBDevice[ui32DeviceNum].ui32Interface, USB_DESC_ANY); // // Read the interface class. // ui32Class = psInterface->bInterfaceClass; // // Search through the Host Class driver list for the devices class. // for(i32Driver = 0; i32Driver < g_sUSBHCD.ui32NumClassDrivers; i32Driver++) { // // If a driver was found call the open for this driver and save which driver is in use.
To get the library to enumerate all the interfaces of a device, I modified the above code as follows, so that it would loop through all of the interfaces of the device. Note that the number of interfaces of a device is stored in g_sUSBHCD.psUSBDevice[ui32DeviceNum].psConfigDescriptor->bNumInterfaces:
static int32_t USBHCDOpenDriver(uint32_t ui32Index, uint32_t ui32DeviceNum) { int32_t i32Driver; uint32_t ui32Interface; uint32_t ui32Class; tInterfaceDescriptor *psInterface; tEventInfo sEvent; ASSERT(ui32Index == 0); // ADDED: loop through all interfaces for (ui32Interface = 0; ui32Interface < g_sUSBHCD.psUSBDevice[ui32DeviceNum].psConfigDescriptor->bNumInterfaces; ui32Interface++) { // ADDED: below line to set g_sUSBHCD.psUSBDevice[ui32DeviceNum].ui32Interface to the current interface number being looped through g_sUSBHCD.psUSBDevice[ui32DeviceNum].ui32Interface = ui32Interface; // // Get the interface descriptor. // psInterface = USBDescGetInterface(g_sUSBHCD.psUSBDevice[ui32DeviceNum].psConfigDescriptor, g_sUSBHCD.psUSBDevice[ui32DeviceNum].ui32Interface, USB_DESC_ANY); // // Read the interface class. // ui32Class = psInterface->bInterfaceClass; // // Search through the Host Class driver list for the devices class. // for(i32Driver = 0; i32Driver < g_sUSBHCD.ui32NumClassDrivers; i32Driver++) { // // If a driver was found call the open for this driver and save which // driver is in use. // if(g_sUSBHCD.ppsClassDrivers[i32Driver]->ui32InterfaceClass == ui32Class) { // // Call the open function for the class driver. // g_ppvDriverInstance[ui32DeviceNum] = g_sUSBHCD.ppsClassDrivers[i32Driver]->pfnOpen( &g_sUSBHCD.psUSBDevice[ui32DeviceNum]); // // If the driver was successfully loaded then break out of the // loop. // if(g_ppvDriverInstance[ui32DeviceNum] != 0) { break; } } } // // If no drivers were found then return -1 to indicate an invalid // driver instance. // if(i32Driver == g_sUSBHCD.ui32NumClassDrivers) { // // Send an unknown connection event. // SendUnknownConnect(ui32Index, (ui32Index << 16) | ui32DeviceNum); // // Indicate that no driver was found. // i32Driver = -1; } else { // // If the connect event is enabled then send the event. // sEvent.ui32Event = USB_EVENT_CONNECTED; sEvent.ui32Instance = (ui32Index << 16) | ui32DeviceNum; InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_CONNECT); } } return(i32Driver); }
There are several relatively small issues with the above change:
1. The function returns the index of the Host Class driver suitable for the interface, or -1 if such a driver was not found. With the above modification, it is now possible for the function to have first found a suitable Host Class driver for the first interface it iterated through, while not finding a suitable Host Class driver for the second interface it iterates through. In this case, the function will return -1 even though it successfully located a driver for the first interface, and called the open function for that Host Class driver. The reverse is also possible. I have not yet fully investigated what the repercussions are.
2. The global g_ppvDriverInstance[ui32DeviceNum] has room to store only one Host Class driver (in essence, it stores the result returned by the Host Class driver open function). This is OK if both interfaces are served by the same Host Class driver (for example both are HID interfaces). However, in case the device exposes two interfaces that require two different Host Class drivers, a change is needed so that both Host Class drivers (again, strictly speaking the result of calling the open function of the Host Class driver) are saved.
In addition to the above change, for HID devices with multiple interfaces (such as the ubiquitous sets of a wireless keyboard and mouse), I had to change the below code in the function HIDDriverOpen() in host/usbhhid.c (note how a fixed zero (0) is passed as the second parameter to the call to USBDescGetInterface()):
static void *HIDDriverOpen(tUSBHostDevice *psDevice) { int32_t i32Idx, i32Dev; tEndpointDescriptor *psEndpointDescriptor; tInterfaceDescriptor *psInterface; // // Get the interface descriptor. // psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0);
I modified this code as follows (notice how the second parameter to the function now receives the value of psDevice->ui32Interface):
static void *HIDDriverOpen(tUSBHostDevice *psDevice) { int32_t i32Idx, i32Dev; tEndpointDescriptor *psEndpointDescriptor; tInterfaceDescriptor *psInterface; // // Get the interface descriptor. // // support multiple interfaces psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, psDevice->ui32Interface, 0);
My question is whether this is a known issue, and if the above seems like a reasonable way to solve this issue.
Thank you,
Ariel