EK-TM4C129EXL: USB host with new class uses address 0 for bulk transfer after full enumeration

Part Number: EK-TM4C129EXL
Other Parts Discussed in Thread: EK-TM4C1294XL

Tool/software:

Hello,

I'm working on a project that needs to fully enumerate and send small commands via bulk transfer to a USB device of class Miscellaneous. To do that, I modified the USB Keyboard Host example so it could recognize and do the full enumeration of the desired miscellaneous class. When I connect the device to the EK-TM4C129EXL, the device is fully enumerated. Address 1 is assigned to it and it gets all the Device, Configuration, Interface and Endpoint descriptors (see screenshots below). The problem that I have, is that after the enumeration is done and I try to send a small command (using the function USBHCDPipeWrite) the host used address 0 and endpoint 0. So, there is obviously something I'm missing or doing wrong. Find the main file below as well as the screenshot of my data capture. Thanks for the help beforehand!

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdbool.h>
/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/gates/GateMutex.h>
#include <ti/sysbios/hal/Hwi.h>
/* TI-RTOS Header files */
#include <ti/drivers/GPIO.h>
#include <USBCamHostApp.h>
/* Example/Board Header files */
#include "Board.h"
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef __USBHMISC_H__
#define __USBHMISC_H__
#ifdef __cplusplus
extern "C"
{
#endif
//******************************************************************************
// Creating event base for Miscellaneous class. Values for other classes are in usblib.h
#define USBD_MISC_EVENT_BASE (USB_CLASS_EVENT_BASE + 0x6000) //In usblib.h the device classes are separated by 0x1000
#define USBH_MISC_EVENT_BASE (USBD_AUDIO_EVENT_BASE + 0x800)
//*****************************************************************************
//
//! \addtogroup usblib_host_class
//! @{
//
//*****************************************************************************
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
include <stdbool.h>
#include <stdint.h>
#include "inc/hw_types.h"
#include "driverlib/usb.h"
#include "usblib/usblib.h"
#include "usblib/usblibpriv.h"
#include "usblib/host/usbhost.h"
#include "usblib/host/usbhostpriv.h"
#include "usbhmisc.h"
#include <ti/drivers/GPIO.h>
#include "Board.h"
static void * MISCDriverOpen(tUSBHostDevice *psDevice);
static void MISCDriverClose(void *pvInstance);
//*****************************************************************************
//
//! \addtogroup usblib_host_class
//! @{
//
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

  • Hi,

      Sorry, I wish to provide you some guidance but I don't know what is wrong either. Can you examine the USB Keyboard Host example and find out for which address the device is enumerated with and if the host can talk to that address accordingly?

  • Hi Charles,

    In both, the original example and in my modified code, the communication starts with address 0. Then, before the host starts asking for the configuration descriptor, the address is set to 1. From there all the communication is carried using that address. The problem in my code is that when I declare an instance of the device in the host application code and later on use it to send the commands, the commands are sent with address 0, which it shouldn't happen since the address was already set. Looking at file usbhostenum.c, it seems like USBHCDPipeWrite() uses the pipe number to figure what endpoint to use to send the command, but there is no definition of address inside it. 

  • Hi Alsiro,

    In both, the original example and in my modified code, the communication starts with address 0.

    I wonder for the original HID keyboard example, only endpoint 0 is used for data transfer and hence the pipe is tied to endpoint 0 at address 0. 

  • Hi Charles,

    Endpoint 0 is only used for control transfers. To send other type of data other endpoints are used. The address is setup to 1.

  • Hi Alsiro,

      I must say I'm not an expert on the USB host side operations. Reading the 'HID Device' model, only the Endpoint0 is used for both control and output transactions while the Interrupt IN transactions such as HID input reports can take place on another endpoint. I tend to think that if you base off of the Host Keyboard example, the command you are sending out from the host is done using the Endpoint0. 

  • Charles,

    Thanks for your swift response. 

    Regardless of the kind of communication that is used (Bulk, interrupt, Isochronous) the messages that are not control transfer, are sent via an endpoint other than 0. Then main issue I'm chasing, is that after the device has been assigned address 1 and the whole enumeration ends, when I try to send something via  USBHCDPipeWrite(), which is the function needed to send data other that control transfer, as is stated in the  TivaWare USB Library Host section, the data is being sent to address 0 instead. You can see this in the previously shared data captures.

  • Hi Alsiro,

      Yes, I see the host set the address to 1 in your capture but I don't know what is wrong. I also see that you have another endpoint for OUT transactions at EP2. For some reason, I don't know if this is inheritance to the HID library (for which you base off of) that it will only send an OUT transaction through EP0. 

    Another thing I come across is that you are using the TI-RTOS. Although I believe TI-RTOS uses the same USB library as the bare-metal examples, I don't know for sure if there is any difference between them.  I will suggest two things:

    1. Try the bare metal HID Keyboard example at C:\ti\TivaWare_C_Series-2.2.0.295\examples\boards\ek-tm4c1294xl\usb_host_keyboard. I tend to think you will see the same thing but if you don't then there must be some difference on the library.

      2. Here is an unpublished USB CDC bare-metal example for host mode without comprehensive validation.  In this example the CDC class uses BULK transfers. There’s a read me file with detailed instructions, it’s a little elaborate to set as you need to add a file and re-build usblib as well as be able to connect two LaunchPads together through the Target USB sides in order to run the example. If you can get this example to work then I think you should able to see the OUT transactions on EP1 using address 1. You can then modify to support your Miscellaneous device. 

    6712.USB_Host_CDC.zip

  • Thanks for sending the files Charles. 
    I tried your recommended option 2, followed all the steps in the readme file and I was able to compile the base example. Following that, I modified g_sUSBCDCClassDriver with USB_CLASS_MISC, which is the class I'm looking to serve. Unfortunately, the enumeration stops right after getting the configuration descriptor. It does not do the Interface nor Endpoints. With my modified version of the HID Keyboard (the one that I started the questions of this post), I'm able to do all the enumeration steps. Thoughts?

  • Hi Alsiro,

      Can you get the example as-is to work without enumeration issue? If you run the example as-is, can you see transactions carries out on a different address using a different endpoint?

  • Hi Charles,

    With the example as is, the enumeration gets to the same point (only gets to the Configuration get descriptor). The piece of enumeration that works with the CDC example, it does the same as the HID Keyboard example that I have been working with. Starts with zero address, it gets the device descriptor. Sets the address to 1. Gets the configurations descriptor and stops the enumeration. With the HID Keyboard modified code, it finishes the enumeration.

  • You are saying the example I attached 6712.USB_Host_CDC.zip does not work as it does not even enumerate. I need to try it out myself. In the instruction, it says you need to rebuild the usblib. I suppose you did, right?

    As I mentioned this is an unpublished/unverified example I thought will be something to help you get started. 

  • Yes, I went through the instructions in the readme, step by step. 

  • Hi,

      I went through the instructions myself and I'm able to run the example. Below are the instructions for the last few steps. 

    5. Have both LaunchPad's powered and connect to a UART terminal for each Stellaris Virtual Serial Port
    6. Connect Target USB ports of the two EK-TM4C1294XL LaunchPads together, if done right the Host should print 'CDC Device Connected'
    7. On the Device side Terminal windows, make sure the CR-LF feature is turned on for sending bytes
    8. Send a 'Z' which should trigger 64 messages of 'xx USB Host CDC' onto the Device side.
    9. Done!

      All I can say is that I can get this CDC host example to display on the terminal windows the messages as expected. 

    With the example as is, the enumeration gets to the same point (only gets to the Configuration get descriptor).

    I'm not clear with your comments here about enumeration. Are you talking about the enumeration for the USB Debug Port on the LaunchPad or the USB Device/Host port? There are two USB ports on a LaunchPad. The USB Debug Port is connected to the PC. Although I don't have a USB analyzer to view the enumeration process, I can certainly confirm that the enumeration is complete for the USB Debug Port. See below for the two EK-TM4C1294XL boards I connect to the PC. Between the USB host port on one LaunchPad and the USB device port on the other LaunchPad, I won't be able to see them on Windows Device Managers since they are not connected to the PC. Again, I don't have a USB analyzer to view the enumeration process between the host and the device ports.. I just wanted to understand that when you said the enumeration fails, which USB port are you referring to? Since the example as-is works for me, I believe the enumeration is passing between the host and the device. Can you rerun the example and before you do that, you need to rebuild the USB library as documented and rebuild the host and device examples linking to the updated library

  • Charles,

    Let me give you more details. The example as is, connecting host and device to the computer works as you have seen.

    After I got the usb_host_serial example to compile, which is the one I need, I modified the code the same way I did to the HID Keyboard example. The main modification is the following:

    • I changed the ulInterfaceClass member of the tUSBClassDriver structure to the one I need (According to section 3.4.6 TivaWare USB Library User's Guide)

    After doing this, I connected to desired class device and the enumeration stops after getting the configuration descriptor. 

    This is pretty much what I did to the host HID keyboard (although was a bit extensive by changing names, etc) and with this one, the enumeration completes.

    If the enumeration does not continue, nothing else can be done since the device needs the rest of the information regarding interfaces and endpoints.

    Let me know if that makes sense.

  • I changed the ulInterfaceClass member of the tUSBClassDriver structure to the one I need (According to section 3.4.6 TivaWare USB Library User's Guide)

    Can you tell me what lines of code in which file(s) that you modified? As I mentioned, I'm not an expert in USB and I have not really delved into USB host library before.  

  • The tUSBHostClassDriver is declared in host class driver .c file. In the case of the CDC device that is in usbhcdc.c and in the case of HID usbhhid.c. In the usbhcdc.c is line 137.

    A prototype of this struct is present in the host class driver .h file (line 176).

    All supported drivers need to be delcared in the usbhost.h file. It's near the end of the file.

  • Hi,

    The tUSBHostClassDriver is declared in host class driver .c file. In the case of the CDC device that is in usbhcdc.c and in the case of HID usbhhid.c. In the usbhcdc.c is line 137.

    Why don't you just show me your modified file(s) and new file(s) you have added so I can understand what you did. In line 137, it is the USB_CLASS_CDC. Did you just change to USB_CLASS_MISC and that is the only thing you changed?

    const tUSBHostClassDriver g_sUSBCDCClassDriver =
    {
    USB_CLASS_CDC,
    CDCDriverOpen,
    CDCDriverClose,
    0
    };

    I have some comments after reading the USB library user's guide for the host support. During enumeration, the host controller will search a corresponding host class driver for your declared MISC class. Did you provide the driver for this miscellaneous class? The USB library does not come with support for all possible classes. The only supported classes are HID, MSC and Audio as for as I know. The CDC class is the one I sent you which is a unpublished driver that does not come in the official TivaWare USB library. If you did not provide your own Misc class driver, then I'm not surprised things don't work. See below. 

    During the enumeration process the host controller driver searches a list of host class drivers provided by the
    application in the USBHCDRegisterDrivers() call. The details of this structure are covered in the
    host class drivers section of this document. If the host controller driver finds a host class driver that
    matches the class of the enumerated device, it calls the open function for that host class driver. If no
    host class driver is found the host controller driver ignores the device and there is no notification to
    the application. The host controller driver or the host class driver can provide callbacks up through
    the USB library to inform the application of enumeration events. The host class drivers are responsible
    for configuring the USB pipes based on the type of device that is discovered.

    You mentioned that even for HID Keyboard example, the data transfer is carried out using EP0. Refer to the below description that HID class driver uses control transactions to send data to the USB device over EP0 even after enumeration.

    All USB control transactions are handled through the USBHCDControlTransfer() function. This
    function is primarily used inside the host controller driver itself during enumeration, however some
    devices may require using control transactions through endpoint 0. The HID class drivers are a
    good example of a USB class driver that uses control transactions to send data to a USB device.

    The below notes say that the library currently only supports a limited number of classes. In the usblib.h file, there are many other classes as place holders. Just to name a few,  there are USB_CLASS_IMAGE, USB_CLASS_SMART_CARD, USB_CLASS_VIDEO, USB_CLASS_WIRELESS, USB_CLASS_MISC among others. This is by no means that the USB library is supporting all of them at the moment. For your USB_CLASS_MISC class, you need to provide the driver yourself. 

    3.4 Host Class Driver
    The host class drivers provide access to devices that use a common USB class interface. The
    USB library currently supports the following two USB class drivers: Mass Storage Class(MSC)
    and Human Interface Device(HID). In order to use these class drivers, the application must provide
    a list of the host class drivers that it uses by calling the USBHCDRegisterDrivers() function.
    The g_USBHIDClassDriver structure defines the interface for the Host HID class driver and the
    g_USBHostMSCClassDriver structure defines the interface for the Host MSC class driver.

    You must create your own MISC class driver and add to the library.

    //*****************************************************************************
    //
    // The host class drivers supported by the USB library.
    //
    //*****************************************************************************
    extern const tUSBHostClassDriver g_sUSBHostMSCClassDriver;
    extern const tUSBHostClassDriver g_sUSBHIDClassDriver;
    extern const tUSBHostClassDriver g_sUSBHostAudioClassDriver;

    When you run your own MISC application based off of the HID Keyboard example, I will suppose you get some type of UNKNOWN_CONNECTED event since the host controller cannot find a matching driver for the MISC class. See below. 

    The USB host library includes a method to receive non-device class specific events in the application
    by using a USB event driver. This driver can be included in applications by declaring an
    instance of the USB event driver using the DECLARE_EVENT_DRIVER() macro and then adding
    the variable that is declared to the list of drivers supported by the application. This event driver
    allows applications to notify users that an unsupported device has been inserted or to provide notification
    that a power fault has occurred and power may have been shut off, depending on the
    settings provided to the USBHCDPowerConfigInit() function. Depending on configuration the following
    events can occur:
    USB_EVENT_CONNECTED indicates that a support device has been connected.
    USB_EVENT_UNKNOWN_CONNECTED indicates that an unsupported device has been
    connected.
    USB_EVENT_DISCONNECTED indicates that an unsupported device has been disconnected.
    USB_EVENT_POWER_FAULT indicates that a power fault has occurred.
    USB_EVENT_POWER_ENABLE indicates that power should be enabled by the application
    since it has requested to manually control the power.
    USB_EVENT_POWER_DISABLE indicates that power should be enabled by the application
    since it has requested to manually control the power.
    USB_EVENT_SOF indicates that a SOF event has occurred(default disabled).

    The USB pipes are usually only used within the
    USB library or by host class drivers and are not usually directly accessed by applications. The USB
    pipes are allocated and freed by calling the USBHCDPipeAlloc() and USBHCDPipeFree() functions
    and are initially configured by calling the USBHCDPipeConfig(). The USBHCDPipeAlloc() and USBHCDPipeConfig()
    functions are used during USB device enumeration to allocate USB pipes to
    specific endpoints of the USB device.

    //
    // Unsupported device detected.
    //
    case USB_EVENT_UNKNOWN_CONNECTED:
    {
    UARTprintf("Unsupported Device Class (0x%02x) Connected.\n",
    pEventInfo->ui32Instance);

    //
    // An unknown device was detected.
    //
    g_eUSBState = STATE_UNKNOWN_DEVICE;

    break;
    }

    Again, you must provide your own MISC driver that is visible tot he host controller driver. That is my understanding. 

    3.4.6 Implementing Custom Host Class Drivers
    This next section covers how to implement a custom host class driver and how the host controller
    driver finds the driver. All host class drivers must provide their own driver interface that is visible
    to the host controller driver. As with the host class drivers that are included with the USB library,
    this means exposing a driver interface of the type tUSBClassDriver. In the example below the
    USBGenericOpen() function is called when the host controller driver enumerates a device that
    matches the “USB_CLASS_SOMECLASS” interface class.

  • Charles,

    Thanks for sharing your notes of the USB user's guide. That's the main document I've used since I started this project. I've read it many times to see if I've been missing something.

    I started this thread by sharing the main files of my modified project: The file with all the functions including main(), usbhmisc.h and usbhmisc.c. Take a closer look to those files and you will find the answer to the questions you pose above. 

  • Hi Alsiro,

      Can you clarify a few things?

      - Have you run the bare-metal HID Keyboard example? The reason I askes is because your original thread was for the TI-RTOS based HID Keyboard.

      - Are you seeing the same issue if you modify the bare-metal HID Keyboard example? Please note the TivaWare version in TI-RTOS is much older than the latest TivaWare SDK which is version 2.2.0.295 although I doubt the version would make a difference as far as USB is concerned. 

      - When you modified for the TI-RTOS example, did you rebuild the usblib.lib?

      I will suggest you use the bare-metal example as a starting point as it will be easier to debug as to take the TI-RTOS and its older version of the TivaWare out of the equation. 

      - Can you clarify about your USB device? Is that a custom device that only a MISC class can work? 

  • Hi Charles,

    I did rebuild the usblib using the TI-RTOS example.

    The same thing happens by modifying the bare metal HID Keyboard example.

    According to the USB Library User's guide: " The ulInterfaceClass member of the tUSBClassDriver structure is the class read from the device’s interface descriptor during enumeration". 

    For example: My device has several interface. One of the interfaces class is image. Then, in my class code, the first member of the struct tUSBHostClassDriver needs to be USB_CLASS_IMAGE.