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!
#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); }