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!
#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" #include <stdbool.h> #include <stdint.h> /************* Includes *************************/ /* driverlib Header files */ #include <inc/hw_ints.h> #include <inc/hw_types.h> /* usblib Header files */ #include <usblib/usb-ids.h> #include <usblib/usblib.h> #include <usblib/usbhid.h> #include <usblib/host/usbhost.h> #include <usblib/host/usbhhid.h> #include <usblib/host/usbhhidkeyboard.h> #include <USBCamHostApp.h> #include <usbhmisc.h> /*********************************************/ /* USB Reference Module Header file */ #define TASKSTACKSIZE 768 uint8_t startRecCmd[]={0x90, 0x10}; uint8_t stopRecCmd[]={0x90, 0x11}; /****************** Defines and vars ******************************/ /* Defines */ #define HCDMEMORYPOOLSIZE 512 /* Memory for the Host Class Driver */ typedef volatile enum { USBMISCH_NO_DEVICE = 0, USBMISCH_INIT, USBMISCH_CONNECTED, USBMISCH_UNKNOWN, USBMISCH_POWER_FAULT } USBKBH_USBState; /* Static variables and handles */ static volatile USBKBH_USBState state; static unsigned char memPoolHCD[HCDMEMORYPOOLSIZE]; static GateMutex_Handle gateUSBLibAccess; static GateMutex_Handle gateUSBWait; static Semaphore_Handle semUSBConnected; //extern tMISCInstance g_sUSBHMiscDevice; static tMISCInstance* pUSBHMiscInstance; /************************************************/ /****************** Prototypes *****************************/ // void cbMiscHandler(tMISCInstance* instance, uint32_t event); static Void USBKBH_hwiHandler(UArg arg0); static Void serviceUSBHost(UArg arg0, UArg arg1); static void usbHCDEvents(void *cbData); void USBMiscH_init(void); bool USBKBH_waitForConnect(unsigned int timeout); /**************************************************************/ Task_Struct task0Struct; Char task0Stack[TASKSTACKSIZE]; Semaphore_Handle semRSButtonHandle; Semaphore_Struct semRSButtonStruct; Void taskFxn(UArg arg0, UArg arg1) { while(true){ USBKBH_waitForConnect(BIOS_WAIT_FOREVER); } } /* Task that sends commands after RS button is pressed. Configured in .cfg file */ void rsComms(UArg arg0, UArg arg1){ while(1){ Semaphore_pend(semRSButtonHandle, BIOS_WAIT_FOREVER); GPIO_toggle(EK_TM4C129EXL_D1); USBHMISCSendData(pUSBHMiscInstance, startRecCmd, 2); // USBHMISCSendData(0, startRecCmd, 2); } } /* * RS button Callback */ void rsButtonCB(UArg arg0){ Semaphore_post(semRSButtonHandle); } /* * ======== main ======== */ int main(void) { /* Construct BIOS objects */ Task_Params taskParams; Semaphore_Params semParams; /* Call board init functions */ Board_initGeneral(); Board_initGPIO(); Board_initUSB(Board_USBHOST); /* Construct keyboard Task thread */ Task_Params_init(&taskParams); taskParams.stackSize = TASKSTACKSIZE; taskParams.stack = &task0Stack; taskParams.priority = 2; Task_construct(&task0Struct, (Task_FuncPtr)taskFxn, &taskParams, NULL); /* Turn on user LED */ GPIO_write(Board_LED0, Board_LED_ON); /*** GPIOs Interrupt enable and Callbacks ***/ GPIO_setCallback(EK_TM4C129EXL_USR_SW1, rsButtonCB); GPIO_enableInt(EK_TM4C129EXL_USR_SW1); /*** SEMAPHORE(s) ***/ Semaphore_Params_init(&semParams); Semaphore_construct(&semRSButtonStruct, 0, &semParams); semRSButtonHandle = Semaphore_handle(&semRSButtonStruct); System_printf("Starting the USB Keyboard Host example\nSystem provider is " "set to SysMin. Halt the target to view any SysMin contents" " in ROV.\n"); /* SysMin will only print to the console when you call flush or exit */ System_flush(); USBMiscH_init(); /* Start BIOS */ BIOS_start(); return (0); } /***************************** Fxn from USBCamHostApp.c ****************************************/ /* MACRO to create a generic USB event host driver */ DECLARE_EVENT_DRIVER(USBMISCH_eventDriver, 0, 0, usbHCDEvents); /* A list of available Host Class Drivers */ static tUSBHostClassDriver const * const usbHCDDriverList[] = { &g_sUSBMISCClassDriver, &USBMISCH_eventDriver }; /* Variable containing the number of HCDs in usbHCDDriverList */ const unsigned int numHostClassDrivers = sizeof(usbHCDDriverList) / sizeof(tUSBHostClassDriver *); void cbMiscHandler(tMISCInstance* instance, uint32_t event) { /* Determine what event has happened */ switch (event) { case USB_EVENT_CONNECTED: // state = USBMISCH_INIT; Semaphore_post(semUSBConnected); break; case USB_EVENT_DISCONNECTED: state = USBMISCH_NO_DEVICE; break; default: break; } } /* * ======== USBKBH_hwiHandler ======== * This function calls the USB library's device interrupt handler. */ static Void USBKBH_hwiHandler(UArg arg0) { USB0HostIntHandler(); } /* * ======== serviceUSBHost ======== * Task to periodically service the USB Stack * * USBHCDMain handles the USB Stack's statemachine. For example it handles the * enumeration process when a device connects. * Future USB library improvement goal is to remove this polling requirement.. */ static Void serviceUSBHost(UArg arg0, UArg arg1) { unsigned int key; while (true) { key = GateMutex_enter(gateUSBLibAccess); USBHCDMain(); GateMutex_leave(gateUSBLibAccess, key); /* Future enhancement to remove the Task_sleep */ Task_sleep(10); } } /* * ======== usbHCDEvents ======== * Generic USB Host Class Driver event callback. * * This callback is called to notify the application that a unknown device was * connected. (e.g. It wasn't a keyboard) */ static void usbHCDEvents(void *cbData) { tEventInfo *pEventInfo; /* Cast this pointer to its actual type. */ pEventInfo = (tEventInfo *)cbData; switch (pEventInfo->ui32Event) { case USB_EVENT_UNKNOWN_CONNECTED: /* An unknown device was detected. */ state = USBMISCH_UNKNOWN; System_printf("Unknown USB device connected\n"); System_flush(); break; case USB_EVENT_DISCONNECTED: /* Unknown device has been removed. */ state = USBMISCH_NO_DEVICE; break; case USB_EVENT_POWER_FAULT: /* No power means no device is present. */ state = USBMISCH_POWER_FAULT; System_printf("USB power fault detected\n"); System_flush(); break; default: break; } } /* * ======== USBMiscH_init ======== */ void USBMiscH_init(void) { Hwi_Handle hwi; Error_Block eb; Task_Handle task; Task_Params taskParams; Semaphore_Params semParams; Error_init(&eb); /* Initialize the USB stack for host mode. */ USBStackModeSet(0, eUSBModeHost, NULL); /* Register host class drivers */ USBHCDRegisterDrivers(0, usbHCDDriverList, numHostClassDrivers); pUSBHMiscInstance = USBHMISCOpen(cbMiscHandler); // pUSBMiscHInstance = USBHMISCOpen(cbMiscHandler); /* Install interrupt handler */ hwi = Hwi_create(INT_USB0, USBKBH_hwiHandler, NULL, &eb); if (hwi == NULL) { System_abort("Can't create USB Hwi"); } /* Initialize USB power configuration */ USBHCDPowerConfigInit(0, USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER); /* Enable the USB stack */ USBHCDInit(0, memPoolHCD, HCDMEMORYPOOLSIZE); /* RTOS primitives */ Semaphore_Params_init(&semParams); semParams.mode = Semaphore_Mode_BINARY; semUSBConnected = Semaphore_create(0, &semParams, &eb); if (semUSBConnected == NULL) { System_abort("Can't create USB semaphore"); } gateUSBLibAccess = GateMutex_create(NULL, &eb); if (gateUSBLibAccess == NULL) { System_abort("Can't create keyboard gate"); } gateUSBWait = GateMutex_create(NULL, &eb); if (gateUSBWait == NULL) { System_abort("Could not create USB Wait gate"); } /* * Note that serviceUSBHost() should not be run until the USB Stack has been * initialized!! */ Task_Params_init(&taskParams); taskParams.priority = Task_numPriorities - 1; taskParams.stackSize = 768; task = Task_create(serviceUSBHost, &taskParams, &eb); if (task == NULL) { System_abort("Can't create USB service task"); } } /* * ======== USBKBH_waitForConnect ======== */ bool USBKBH_waitForConnect(unsigned int timeout) { bool ret = true; unsigned int key; /* Need exclusive access to prevent a race condition */ key = GateMutex_enter(gateUSBWait); if (state == USBMISCH_NO_DEVICE) { if (!Semaphore_pend(semUSBConnected, timeout)) { ret = false; } } GateMutex_leave(gateUSBWait, key); return (ret); }
#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 //! @{ // //***************************************************************************** typedef struct tMISCInstance tMISCInstance; //***************************************************************************** //***************************************************************************** // // The prototype for the USB MSC host driver callback function. // //***************************************************************************** typedef void (*tUSBHMiscCallback)(tMISCInstance *psMiscInstance, uint32_t ui32Event); //***************************************************************************** // // Close the Doxygen group. //! @} // //***************************************************************************** //***************************************************************************** // // Prototypes. // //***************************************************************************** extern tMISCInstance * USBHMISCOpen(tUSBHMiscCallback pfnCallback); extern void USBHMISCClose(tMISCInstance *psMISCInstance); extern uint32_t USBHMISCSendData(tMISCInstance *psMISCInstance, uint8_t *pui8Data, uint32_t ui32Size); extern const tUSBHostClassDriver g_sUSBMISCClassDriver; //***************************************************************************** // // Mark the end of the C bindings section for C++ compilers. // //***************************************************************************** #ifdef __cplusplus } #endif #endif // __USBHMISC_H__
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 //! @{ // //***************************************************************************** //***************************************************************************** // // This is the structure that holds all of the data for a given instance of // a MISC device. // //***************************************************************************** struct tMISCInstance { // // Save the device instance. // tUSBHostDevice *psDevice; // // Used to save the callback. // tUSBHMiscCallback pfnCallback; // // Callback data provided by caller. // void *pvCBData; // // Bulk IN pipe. // uint32_t ui32BulkInPipe; // // Bulk OUT pipe. // uint32_t ui32BulkOutPipe; }; //***************************************************************************** // // The instance data storage for attached MISC devices. // //***************************************************************************** //static tMISCInstance g_sUSBHMiscDevice[MAX_MISC_DEVICES]; //***************************************************************************** // // The array of USB MSC host drivers. // //***************************************************************************** static tMISCInstance g_sUSBHMiscDevice = { 0 }; //***************************************************************************** // //! This constant global structure defines the MISC Class Driver that is //! provided with the USB library. // //***************************************************************************** const tUSBHostClassDriver g_sUSBMISCClassDriver = { USB_CLASS_MISC, MISCDriverOpen, MISCDriverClose, 0 }; //***************************************************************************** // //! This function is used to open an instance of a MISC device. //! //! \param iDeviceType is the type of device that should be loaded for this //! instance of the MISC device. //! \param pfnCallback is the function that will be called whenever changes //! are detected for this device. //! \param pvCBData is the data that will be returned in when the //! \e pfnCallback function is called. //! //! This function creates an instance of an specific type of MISC device. The //! \e iDeviceType parameter is one subclass/protocol values of the types //! specified in enumerated types tMISCSubClassProtocol. Only devices that //! enumerate with this type will be called back via the \e pfnCallback //! function. The \e pfnCallback parameter is the callback function for any //! events that occur for this device type. The \e pfnCallback function must //! point to a valid function of type \e tUSBCallback for this call to complete //! successfully. To release this device instance the caller of USBHMISCOpen() //! should call USBHMISCClose() and pass in the value returned from the //! USBHMISCOpen() call. //! //! \return This function returns and instance value that should be used with //! any other APIs that require an instance value. If a value of 0 is returned //! then the device instance could not be created. // //***************************************************************************** tMISCInstance * USBHMISCOpen(tUSBHMiscCallback pfnCallback) { g_sUSBHMiscDevice.pfnCallback = pfnCallback; return(&g_sUSBHMiscDevice); } //***************************************************************************** // //! This function is used to release an instance of a MISC device. //! //! \param psMISCInstance is the instance value for a MISC device to release. //! //! This function releases an instance of a MISC device that was created by a //! call to USBHMISCOpen(). This call is required to allow other MISC devices //! to be enumerated after another MISC device has been disconnected. The //! \e psMISCInstance parameter should hold the value that was returned from //! the previous call to USBHMISCOpen(). //! //! \return None. // //***************************************************************************** void USBHMISCClose(tMISCInstance *psMISCInstance) { // // Disable any more notifications from the MISC layer. // psMISCInstance->pfnCallback = 0; } //***************************************************************************** // //! This function is used to open an instance of the MISC driver. //! //! \param psDevice is a pointer to the device information structure. //! //! This function will attempt to open an instance of the MISC driver based on //! the information contained in the psDevice structure. This call can fail if //! there are not sufficient resources to open the device. The function will //! return a value that should be passed back into USBMISCClose() when the //! driver is no longer needed. //! //! \return The function will return a pointer to a MISC driver instance. // //***************************************************************************** static void * MISCDriverOpen(tUSBHostDevice *psDevice) { int32_t i32Idx; tEndpointDescriptor *psEndpointDescriptor; tInterfaceDescriptor *psInterface; // // Don't allow the device to be opened without closing first. // // if(g_sUSBHMiscDevice.psDevice) // { // return(0); // } // // Save the device pointer. // g_sUSBHMiscDevice.psDevice = psDevice; // // Get the interface descriptor. // psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); for(i32Idx = 0; i32Idx < 3; i32Idx++) { // // Get the first endpoint descriptor. // psEndpointDescriptor = USBDescGetInterfaceEndpoint(psInterface, i32Idx, psDevice->ui32ConfigDescriptorSize); // // If no more endpoints then break out. // if(psEndpointDescriptor == 0) { break; } // // See if this is a bulk endpoint. // if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == USB_EP_ATTR_BULK) { // // See if this is bulk IN or bulk OUT. // if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) { // // Allocate the USB Pipe for this Bulk IN endpoint. // g_sUSBHMiscDevice.ui32BulkInPipe = USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_IN_DMA, psDevice, psEndpointDescriptor->wMaxPacketSize, 0); // // Configure the USB pipe as a Bulk IN endpoint. // USBHCDPipeConfig(g_sUSBHMiscDevice.ui32BulkInPipe, psEndpointDescriptor->wMaxPacketSize, 0, (psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_NUM_M)); } else { // // Allocate the USB Pipe for this Bulk OUT endpoint. // g_sUSBHMiscDevice.ui32BulkOutPipe = USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_OUT_DMA, psDevice, psEndpointDescriptor->wMaxPacketSize, 0); // // Configure the USB pipe as a Bulk OUT endpoint. // USBHCDPipeConfig(g_sUSBHMiscDevice.ui32BulkOutPipe, psEndpointDescriptor->wMaxPacketSize, 0, (psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_NUM_M)); } } } // // If there is a callback function call it to inform the application that // the device has been enumerated. // if(g_sUSBHMiscDevice.pfnCallback != 0) { g_sUSBHMiscDevice.pfnCallback(&g_sUSBHMiscDevice, USB_EVENT_CONNECTED); } return (&g_sUSBHMiscDevice); } //***************************************************************************** // //! This function is used to release an instance of the MISC driver. //! //! \param pvInstance is an instance pointer that needs to be released. //! //! This function will free up any resources in use by the MISC driver instance //! that is passed in. The \e pvInstance pointer should be a valid value that //! was returned from a call to USBMISCOpen(). //! //! \return None. // //***************************************************************************** static void MISCDriverClose(void *pvInstance) { tMISCInstance *psInst; // // Get our instance pointer. // psInst = (tMISCInstance *)pvInstance; // // Reset the device pointer. // psInst->psDevice = 0; if(psInst->ui32BulkInPipe != 0) { USBHCDPipeFree(psInst->ui32BulkInPipe); } if(psInst->ui32BulkOutPipe != 0) { USBHCDPipeFree(psInst->ui32BulkOutPipe); } // // If the callback exists, call it with a DISCONNECTED event. // if(psInst->pfnCallback != 0) { psInst->pfnCallback(pvInstance, USB_EVENT_DISCONNECTED); } } //***************************************************************************** // //! @} // //***************************************************************************** uint32_t USBHMISCSendData(tMISCInstance *psMISCInstance, uint8_t *pui8Data, uint32_t ui32Size) { USBHCDPipeWrite(psMISCInstance->ui32BulkOutPipe, pui8Data, ui32Size); return(ui32Size); }
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.
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.
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:
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.