//***************************************************************************** // // usbdhidgame.c - USB HID Gamepad device class driver // // Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. // Software License Agreement // // Texas Instruments (TI) is supplying this software for use solely and // exclusively on TI's microcontroller products. The software is owned by // TI and/or its suppliers, and is protected under applicable copyright // laws. You may not combine this software with "viral" open-source // software in order to form a larger program. // // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL // DAMAGES, FOR ANY REASON WHATSOEVER. // // This is part of revision 2.1.0.12573 of the Tiva USB Library. // //***************************************************************************** #include #include #include "inc/hw_types.h" #include "driverlib/debug.h" #include "driverlib/usb.h" #include "usblib/usblib.h" #include "usblib/usblibpriv.h" #include "usblib/device/usbdevice.h" #include "usblib/usbhid.h" #include "usblib/device/usbdhid.h" #include "usblib/device/usbdhidgamepad.h" //***************************************************************************** // //! \addtogroup hid_gamepad_device_class_api //! @{ // //***************************************************************************** //***************************************************************************** // // HID device configuration descriptor. // // It is vital that the configuration descriptor bConfigurationValue field // (byte 6) is 1 for the first configuration and increments by 1 for each // additional configuration defined here. This relationship is assumed in the // device stack for simplicity even though the USB 2.0 specification imposes // no such restriction on the bConfigurationValue values. // //***************************************************************************** static uint8_t g_pui8GameDescriptor[] = { // // Configuration descriptor header. // 9, // Size of the configuration descriptor. USB_DTYPE_CONFIGURATION, // Type of this descriptor. USBShort(24), // The total size of this full structure. 1, // The number of interfaces in this // configuration. 1, // The unique value for this configuration. 5, // The string identifier that describes this // configuration. USB_CONF_ATTR_SELF_PWR, // Self Powered. 0, // The maximum power in 2mA increments. }; //***************************************************************************** // // This is the HID interface descriptor for the gamepad device. // //***************************************************************************** static uint8_t g_pui8HIDInterface[HIDINTERFACE_SIZE] = { // // HID Device Class Interface Descriptor. // 9, // Size of the interface descriptor. USB_DTYPE_INTERFACE, // Type of this descriptor. 0, // The index for this interface. 0, // The alternate setting for this interface. 1, // The number of endpoints used by this // interface. USB_CLASS_HID, // The interface class 0, // The interface sub-class. 0, // The interface protocol for the sub-class // specified above. 4, // The string index for this interface. }; //***************************************************************************** // // This is the HID IN endpoint descriptor for the gamepad device. // //***************************************************************************** static const uint8_t g_pui8HIDInEndpoint[HIDINENDPOINT_SIZE] = { // // Interrupt IN endpoint descriptor // 7, // The size of the endpoint descriptor. USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. USB_EP_DESC_IN | USBEPToIndex(USB_EP_1), USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), // The maximum packet size. 1, // The polling interval for this endpoint. }; //***************************************************************************** // // The following is the HID report structure definition that is passed back // to the host. // //***************************************************************************** static const uint8_t g_pui8GameReportDescriptor[] = { UsagePage(USB_HID_GENERIC_DESKTOP), Usage(USB_HID_JOYSTICK), Collection(USB_HID_APPLICATION), // // The axis for the controller. // UsagePage(USB_HID_GENERIC_DESKTOP), Usage (USB_HID_POINTER), Collection (USB_HID_PHYSICAL), // // The X, Y and Z values which are specified as 8-bit absolute // position values. // Usage (USB_HID_X), Usage (USB_HID_Y), Usage (USB_HID_Z), // // 3 8-bit absolute values. // ReportSize(8), ReportCount(3), Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | USB_HID_INPUT_ABS), // // The 8 buttons. // UsagePage(USB_HID_BUTTONS), UsageMinimum(1), UsageMaximum(8), LogicalMinimum(0), LogicalMaximum(1), PhysicalMinimum(0), PhysicalMaximum(1), // // 8 - 1 bit values for the buttons. // ReportSize(1), ReportCount(8), Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | USB_HID_INPUT_ABS), EndCollection, EndCollection }; //***************************************************************************** // // The HID descriptor for the gamepad device. // //***************************************************************************** static tHIDDescriptor g_sGameHIDDescriptor = { 9, // bLength USB_HID_DTYPE_HID, // bDescriptorType 0x111, // bcdHID (version 1.11 compliant) 0, // bCountryCode (not localized) 1, // bNumDescriptors { { USB_HID_DTYPE_REPORT, // Report descriptor sizeof(g_pui8GameReportDescriptor) // Size of report descriptor } } }; //***************************************************************************** // // The HID configuration descriptor is defined as four sections. // These sections are: // // 1. The 9 byte configuration descriptor. // 2. The interface descriptor. // 3. The HID report and physical descriptors, provided by the application // or the default can be used. // 4. The mandatory interrupt IN endpoint descriptor. // //***************************************************************************** static const tConfigSection g_sHIDConfigSection = { sizeof(g_pui8GameDescriptor), g_pui8GameDescriptor }; static const tConfigSection g_sHIDInterfaceSection = { sizeof(g_pui8HIDInterface), g_pui8HIDInterface }; static const tConfigSection g_sHIDInEndpointSection = { sizeof(g_pui8HIDInEndpoint), g_pui8HIDInEndpoint }; //***************************************************************************** // // Place holder for the user's HID descriptor block. // //***************************************************************************** static tConfigSection g_sHIDDescriptorSection = { sizeof(g_sGameHIDDescriptor), (const uint8_t *)&g_sGameHIDDescriptor }; //***************************************************************************** // // This array lists all the sections that must be concatenated to make a // single, complete HID configuration descriptor. // //***************************************************************************** static const tConfigSection *g_psHIDSections[] = { &g_sHIDConfigSection, &g_sHIDInterfaceSection, &g_sHIDDescriptorSection, &g_sHIDInEndpointSection, }; #define NUM_HID_SECTIONS ((sizeof(g_psHIDSections) / \ sizeof(tConfigSection *))) //***************************************************************************** // // The header for the single configuration supported. This is the root of // the data structure that defines all the bits and pieces that are pulled // together to generate the configuration descriptor. Note that this must be // in RAM since we need to include or exclude the final section based on // client supplied initialization parameters. // //***************************************************************************** static tConfigHeader g_sHIDConfigHeader = { NUM_HID_SECTIONS, g_psHIDSections }; //***************************************************************************** // // Configuration Descriptor. // //***************************************************************************** static const tConfigHeader * const g_ppsHIDConfigDescriptors[] = { &g_sHIDConfigHeader }; //***************************************************************************** // // The HID class descriptor table. For the gamepad class there is only a // single report descriptor. // //***************************************************************************** static const uint8_t *g_ppui8GameClassDescriptors[] = { g_pui8GameReportDescriptor }; //***************************************************************************** // // HID gamepad transmit channel event handler function. // // \param pvGameDevice is the event callback pointer provided during // USBDHIDInit(). This is a pointer to the HID gamepad device structure // of the type tUSBDHIDGamepadDevice. // \param ui32Event identifies the event we are being called back for. // \param ui32MsgData is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the lower level HID device class driver to inform // the application of particular asynchronous events related to report events // related to using the interrupt IN endpoint. // // \return Returns a value which is event-specific. // //***************************************************************************** static uint32_t HIDGamepadTxHandler(void *pvGameDevice, uint32_t ui32Event, uint32_t ui32MsgData, void *pvMsgData) { tUSBDGamepadInstance *psInst; tUSBDHIDGamepadDevice *psGamepad; // // Make sure we did not get a NULL pointer. // ASSERT(pvGameDevice); // // Get a pointer to our instance data // psGamepad = (tUSBDHIDGamepadDevice *)pvGameDevice; psInst = &psGamepad->sPrivateData; // // Which event were we sent? // switch (ui32Event) { // // A report transmitted via the interrupt IN endpoint was acknowledged // by the host. // case USB_EVENT_TX_COMPLETE: { // // The last transmission is complete so return to the idle state. // psInst->iState = eHIDGamepadStateIdle; // // Pass the event on to the application. // psGamepad->pfnCallback(psGamepad->pvCBData, USB_EVENT_TX_COMPLETE, ui32MsgData, (void *)0); break; } // // Ignore all other events related to transmission of reports via // the interrupt IN endpoint. // default: { break; } } return(0); } //***************************************************************************** // // Main HID device class event receive handler function. // // \param pvGameDevice is the event callback pointer provided during // USBDHIDInit(). This is a pointer to the HID gamepad device structure // of the type tUSBDHIDGamepadDevice. // \param ui32Event identifies the event we are being called back for. // \param ui32MsgData is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the lower level HID device class driver to inform // the application of particular asynchronous events related to operation of // the gamepad HID device. // // \note This function also receive all generic events as well such as // \b USB_EVENT_CONNECTED and USB_EVENT_DISCONNECTED. // // \return Returns a value which is event-specific. // //***************************************************************************** static uint32_t HIDGamepadRxHandler(void *pvGamepad, uint32_t ui32Event, uint32_t ui32MsgData, void *pvMsgData) { tUSBDGamepadInstance *psInst; tUSBDHIDGamepadDevice *psGamepad; uint32_t ui32Ret; // // Make sure we did not get a NULL pointer. // ASSERT(pvGamepad); // // Return zero by default. // ui32Ret = 0; // // Get a pointer to our instance data // psGamepad = (tUSBDHIDGamepadDevice *)pvGamepad; psInst = &psGamepad->sPrivateData; // // Which event were we sent? // switch(ui32Event) { // // The host has connected to us and configured the device. // case USB_EVENT_CONNECTED: { // // Now in the idle state. // psInst->iState = eHIDGamepadStateIdle; // // Pass the information on to the application. // psGamepad->pfnCallback(psGamepad->pvCBData, USB_EVENT_CONNECTED, 0, (void *)0); break; } // // The host has disconnected from us. // case USB_EVENT_DISCONNECTED: { psInst->iState = eHIDGamepadStateNotConnected; // // Pass the information on to the application. // ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, USB_EVENT_DISCONNECTED, 0, (void *)0); break; } // // This handles the Set Idle command. // case USBD_HID_EVENT_IDLE_TIMEOUT: { // // Give the pointer to the idle report structure. // *(void **)pvMsgData = (void *)&psInst->sReportIdle; ui32Ret = sizeof(psInst->sReportIdle); break; } // // The host is polling for a particular report and the HID driver // is asking for the latest version to transmit. // case USBD_HID_EVENT_GET_REPORT: { // // If this is an IN request then pass the request on to the // application. All other requests are ignored. // if(ui32MsgData == USB_HID_REPORT_IN) { ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, USBD_HID_EVENT_GET_REPORT, 0, pvMsgData); } break; } // // The device class driver has completed sending a report to the // host in response to a Get_Report request. // case USBD_HID_EVENT_REPORT_SENT: { // // We have nothing to do here. // break; } // // Pass these events to the client unchanged. // case USB_EVENT_ERROR: case USB_EVENT_SUSPEND: case USB_EVENT_RESUME: case USB_EVENT_LPM_RESUME: case USB_EVENT_LPM_SLEEP: case USB_EVENT_LPM_ERROR: { ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, ui32Event, ui32MsgData, pvMsgData); break; } // // This event is sent in response to a host Set_Report request which // is not supported for gamepads. // case USBD_HID_EVENT_GET_REPORT_BUFFER: // // We ignore all other events. // default: { break; } } return(ui32Ret); } //***************************************************************************** // //! Initializes HID gamepad device operation for a given USB controller. //! //! \param ui32Index is the index of the USB controller that is to be //! initialized for HID gamepad device operation. //! \param psGamepad points to a structure containing parameters //! customizing the operation of the HID gamepad device. //! //! An application that enables a USB HID gamepad interface to a USB host //! must call this function to initialize the USB controller and attach the //! gamepad device to the USB bus. This function performs all required USB //! initialization, and the device is ready for operation on the function //! return. //! //! On successful completion, this function returns the modified \e psGamepad //! pointer passed to it or returns a NULL pointer if there was a problem. //! This pointer must be passed on all future calls to the HID gamepad device //! driver. //! //! When a host connects and configures the device, the application callback //! receives \b USB_EVENT_CONNECTED, after which calls can be made to //! USBDHIDGamepadSendReport() to report changes to the gamepad interface to //! the USB host when it requests them. //! //! \note The application must not make any calls to the lower level USB device //! interfaces if interacting with USB via the USB HID gamepad device class //! API. //! //! \return Returns NULL on failure or the \e psGamepad pointer on success. // //***************************************************************************** tUSBDHIDGamepadDevice * USBDHIDGamepadInit(uint32_t ui32Index, tUSBDHIDGamepadDevice *psGamepad) { void *pvRetcode; tUSBDHIDDevice *psHIDDevice; tConfigDescriptor *pConfigDesc; // // Check basic parameter validity. // ASSERT(psGamepad); ASSERT(psGamepad->ppui8StringDescriptors); ASSERT(psGamepad->pfnCallback); // // Get a pointer to the HID device data. // psHIDDevice = &psGamepad->sPrivateData.sHIDDevice; // // Call the common initialization routine. // pvRetcode = USBDHIDGamepadCompositeInit(ui32Index, psGamepad, 0); pConfigDesc = (tConfigDescriptor *)g_pui8GameDescriptor; pConfigDesc->bmAttributes = psGamepad->ui8PwrAttributes; pConfigDesc->bMaxPower = (uint8_t)(psGamepad->ui16MaxPowermA / 2); // // If we initialized the HID layer successfully, pass our device pointer // back as the return code, otherwise return NULL to indicate an error. // if(pvRetcode) { // // Initialize the lower layer HID driver and pass it the various // structures and descriptors necessary to declare that we are a // gamepad. // pvRetcode = USBDHIDInit(ui32Index, psHIDDevice); return(psGamepad); } else { return((tUSBDHIDGamepadDevice *)0); } } //***************************************************************************** // //! Initializes HID gamepad device operation for a given USB controller. //! //! \param ui32Index is the index of the USB controller that is to be //! initialized for HID gamepad device operation. //! \param psGamepad points to a structure containing parameters //! customizing the operation of the HID gamepad device. //! \param psCompEntry is the composite device entry to initialize when //! creating a composite device. //! //! This call is very similar to USBDHIDGamepadInit() except that it is used //! for initializing an instance of the HID gamepad device for use in a //! composite device. If this HID gamepad is part of a composite device, then //! the \e psCompEntry should point to the composite device entry to //! initialize. This entry is part of the array that is passed to the //! USBDCompositeInit() function to start up and complete configuration of a //! composite USB device. //! //! \return Returns NULL on failure or the \e psGamepad value that should be //! used with the remaining USB HID gamepad APIs. // //***************************************************************************** tUSBDHIDGamepadDevice * USBDHIDGamepadCompositeInit(uint32_t ui32Index, tUSBDHIDGamepadDevice *psGamepad, tCompositeEntry *psCompEntry) { tUSBDGamepadInstance *psInst; tUSBDHIDDevice *psHIDDevice; // // Check parameter validity. // ASSERT(psGamepad); ASSERT(psGamepad->ppui8StringDescriptors); ASSERT(psGamepad->pfnCallback); // // Get a pointer to our instance data // psInst = &psGamepad->sPrivateData; // // Initialize the various fields in our instance structure. // psInst->iState = eHIDGamepadStateNotConnected; // // Get a pointer to the HID device data. // psHIDDevice = &psInst->sHIDDevice; // // Initialize the HID device class instance structure based on input from // the caller. // psHIDDevice->ui16PID = psGamepad->ui16PID; psHIDDevice->ui16VID = psGamepad->ui16VID; psHIDDevice->ui16MaxPowermA = psGamepad->ui16MaxPowermA; psHIDDevice->ui8PwrAttributes = psGamepad->ui8PwrAttributes; psHIDDevice->ui8Subclass = 0; psHIDDevice->ui8Protocol = 0; psHIDDevice->ui8NumInputReports = 1; psHIDDevice->psReportIdle = &psInst->sReportIdle; psInst->sReportIdle.ui8Duration4mS = 125; psInst->sReportIdle.ui8ReportID = 0; psInst->sReportIdle.ui32TimeSinceReportmS = 0; psInst->sReportIdle.ui16TimeTillNextmS = 0; psHIDDevice->pfnTxCallback = HIDGamepadTxHandler; psHIDDevice->pvRxCBData = (void *)psGamepad; psHIDDevice->pfnRxCallback = HIDGamepadRxHandler; psHIDDevice->pvTxCBData = (void *)psGamepad; psHIDDevice->bUseOutEndpoint = false, psHIDDevice->psHIDDescriptor = &g_sGameHIDDescriptor; psHIDDevice->ppui8ClassDescriptors = g_ppui8GameClassDescriptors; psHIDDevice->ppui8StringDescriptors = psGamepad->ppui8StringDescriptors; psHIDDevice->ui32NumStringDescriptors = psGamepad->ui32NumStringDescriptors; psHIDDevice->ppsConfigDescriptor = g_ppsHIDConfigDescriptors; // // If there was an override for the report descriptor then use it. // if(psGamepad->pui8ReportDescriptor) { // // Save the report descriptor in the list of report descriptors. // g_ppui8GameClassDescriptors[0] = psGamepad->pui8ReportDescriptor; // // Override the report descriptor size. // g_sGameHIDDescriptor.sClassDescriptor[0].wDescriptorLength = psGamepad->ui32ReportSize; } // // Initialize the lower layer HID driver and pass it the various structures // and descriptors necessary to declare that we are a gamepad. // return(USBDHIDCompositeInit(ui32Index, psHIDDevice, psCompEntry)); } //***************************************************************************** // //! Schedules a report to be sent once the host requests more data. //! //! \param psHIDGamepad is the structure pointer that is returned from the //! USBDHIDGamepadCompositeInit() or USBDHIDGamepadInit() functions. //! \param pvReport is the data to send to the host. //! \param ui32Size is the number of bytes in the \e pvReport buffer. //! //! This call is made by an application to schedule data to be sent to the //! host when the host requests an update from the device. The application //! must then wait for a \b USB_EVENT_TX_COMPLETE event in the function //! provided in the \e pfnCallback pointer in the tUSBDHIDGamepadDevice //! structure before being able to send more data with this function. The //! pointer passed in the \e pvReport can be updated once this call returns as //! the data has been copied from the buffer. The function returns //! \b USBDGAMEPAD_SUCCESS if the transmission was successfully scheduled or //! \b USBDGAMEPAD_TX_ERROR if the report could not be sent at this time. //! If the call is made before the device is connected or ready to communicate //! with the host, then the function can return \b USBDGAMEPAD_NOT_CONFIGURED. //! //! \return The function returns one of the \b USBDGAMEPAD_* values. // //***************************************************************************** uint32_t USBDHIDGamepadSendReport(tUSBDHIDGamepadDevice *psHIDGamepad, void *pvReport, uint32_t ui32Size) { uint32_t ui32Retcode, ui32Count; tUSBDGamepadInstance *psInst; tUSBDHIDDevice *psHIDDevice; // // Get a pointer to the HID device data. // psHIDDevice = &psHIDGamepad->sPrivateData.sHIDDevice; // // Get a pointer to our instance data // psInst = &psHIDGamepad->sPrivateData; // // If we are not configured, return an error here before trying to send // anything. // if(psInst->iState == eHIDGamepadStateNotConnected) { return(USBDGAMEPAD_NOT_CONFIGURED); } // // Only send a report if the transmitter is currently free. // if(USBDHIDTxPacketAvailable((void *)psHIDDevice)) { // // Send the report to the host. // psInst->iState = eHIDGamepadStateSending; ui32Count = USBDHIDReportWrite((void *)psHIDDevice, pvReport, ui32Size, true); // // Did we schedule a packet for transmission correctly? // if(ui32Count == 0) { // // No - report the error to the caller. // ui32Retcode = USBDGAMEPAD_TX_ERROR; } else { ui32Retcode = USBDGAMEPAD_SUCCESS; } } else { ui32Retcode = USBDGAMEPAD_TX_ERROR; } // // Return the relevant error code to the caller. // return(ui32Retcode); } //***************************************************************************** // //! Shuts down the HID gamepad device. //! //! \param psGamepad is the pointer to the device instance structure //! as returned by USBDHIDGamepadInit() or USBDHIDGamepadCompositeInit(). //! //! This function terminates HID gamepad operation for the instance supplied //! and removes the device from the USB bus. Following this call, the //! \e psGamepad instance may not me used in any other call to the HID //! gamepad device other than to reinitialize by calling USBDHIDGamepadInit() //! or USBDHIDGamepadCompositeInit(). //! //! \return None. // //***************************************************************************** void USBDHIDGamepadTerm(tUSBDHIDGamepadDevice *psGamepad) { tUSBDHIDDevice *psHIDDevice; ASSERT(psGamepad); // // Get a pointer to the HID device data. // psHIDDevice = &psGamepad->sPrivateData.sHIDDevice; // // Mark the device as no longer connected. // psGamepad->sPrivateData.iState = eHIDGamepadStateNotConnected; // // Terminate the low level HID driver. // USBDHIDTerm(psHIDDevice); } //***************************************************************************** // // Close the Doxygen group. //! @} // //*****************************************************************************