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.

Issue with TivaWare 2.1.2.111 USB Host enumeration process

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

  • Hello Ariel,

    Thanks for putting in the effort for this detail post.

    I have forwarded this post to my colleague who has more knowledge about this topic. He should get in touch with you shortly.

    Thanks,
    Sai
  • Hello Ariel,

    Great post and extremely informative-well written. Now to answer your specific question, Embedded Host Stack in TivaWare, was never designed for composite devices. It supports multiple devices (via Hub). Now the changes that you have made has to be looked at in conjunction with the Application Code.

    TivaWare allows for customers to modify usblib as per implementation requirement on their end application.

    If your testing of the code indicates that it is working as expected under all test conditions, then it should be OK. Again this highly application specific change and for us to commit that it is OK, would be misleading your testing of the end application.

    Right now we are prioritizing USB drivers for DMA operation for the bulk class. Hopefully we should be coming around to composite device support in Embedded Host after that.

    Regards
    Amit
  • Thank you Sai for your reply and for forwarding the issue.

    Thank you Amit for your gracious and fast response.

    I did not know that the TivaWare Host Stack was not designed to support devices with multiple interfaces (composite devices). I don't think I saw this limitation documented in any of the extensive documentation that I read. Thank you for clarifying this.

    My application also includes code to work with hubs; however, it does not seem to work yet. I will perhaps document my findings regarding hub support in the TivaWare Host Stack in a separate thread, as not to confuse the issue.

    I understand that the changes I make are entirely my responsibility and need to be tested in conjunction with my application.

    Please let me rephrase my question: if you, as TI, were to add support for devices with multiple interfaces in the TivaWare Host Stack, would you do it in a way that is similar to the changes I made? Or would you create a separate API?

    I am asking because it is important to my application that I be able to easily apply new revisions of the TivaWare USB Library as they are released. In the ideal world, I should be able to take a new revision of TivaWare and use it in my application without the need for modifications either in my application or in the TivaWare code. In this case, I did not see a way to add support for devices with multiple interfaces without making modifications to the TivaWare code. I therefore made as minimal a change as possible to the TivaWare code and still achieve my goal.

    Thanks again,

    Ariel
  • Hello Ariel,

    It may be similar or it may not be. The reason why I state the latter is because, because in a composite device support there could be different combinations of not only device interfaces but the requirement of CPU or DMA for buffer management, that needs to be evaluated.

    Regards
    Amit