From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- usblib/host/usbhhid.c | 746 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 746 insertions(+) create mode 100644 usblib/host/usbhhid.c (limited to 'usblib/host/usbhhid.c') diff --git a/usblib/host/usbhhid.c b/usblib/host/usbhhid.c new file mode 100644 index 0000000..bfbea85 --- /dev/null +++ b/usblib/host/usbhhid.c @@ -0,0 +1,746 @@ +//***************************************************************************** +// +// usbhhid.c - This file contains the host HID driver. +// +// Copyright (c) 2008-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/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbhid.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/host/usbhhid.h" + +static void * HIDDriverOpen(tUSBHostDevice *psDevice); +static void HIDDriverClose(void *pvInstance); + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// If the user has not explicitly stated the maximum number of HID devices to +// support, we assume that we need to support up to the maximum number of USB +// devices that the build is configured for. +// +//***************************************************************************** +#ifndef MAX_HID_DEVICES +#define MAX_HID_DEVICES MAX_USB_DEVICES +#endif + +//***************************************************************************** +// +// This is the structure that holds all of the data for a given instance of +// a HID device. +// +//***************************************************************************** +struct tHIDInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the callback. + // + tUSBCallback pfnCallback; + + // + // Callback data provided by caller. + // + void *pvCBData; + + // + // Used to remember what type of device was registered. + // + tHIDSubClassProtocol iDeviceType; + + // + // Interrupt IN pipe. + // + uint32_t ui32IntInPipe; +}; + +//***************************************************************************** +// +// The instance data storage for attached hid devices. +// +//***************************************************************************** +static tHIDInstance g_psHIDDevice[MAX_HID_DEVICES]; + +//***************************************************************************** +// +//! This constant global structure defines the HID Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHIDClassDriver = +{ + USB_CLASS_HID, + HIDDriverOpen, + HIDDriverClose, + 0 +}; + +//***************************************************************************** +// +//! This function is used to open an instance of a HID device. +//! +//! \param iDeviceType is the type of device that should be loaded for this +//! instance of the HID 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 HID device. The +//! \e iDeviceType parameter is one subclass/protocol values of the types +//! specified in enumerated types tHIDSubClassProtocol. 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 USBHHIDOpen() +//! should call USBHHIDClose() and pass in the value returned from the +//! USBHHIDOpen() 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. +// +//***************************************************************************** +tHIDInstance * +USBHHIDOpen(tHIDSubClassProtocol iDeviceType, tUSBCallback pfnCallback, + void *pvCBData) +{ + uint32_t ui32Loop; + + // + // Find a free device instance structure. + // + for(ui32Loop = 0; ui32Loop < MAX_HID_DEVICES; ui32Loop++) + { + if(g_psHIDDevice[ui32Loop].iDeviceType == eUSBHHIDClassNone) + { + // + // Save the instance data for this device. + // + g_psHIDDevice[ui32Loop].pfnCallback = pfnCallback; + g_psHIDDevice[ui32Loop].iDeviceType = iDeviceType; + g_psHIDDevice[ui32Loop].pvCBData = pvCBData; + + // + // Return the device instance pointer. + // + return(&g_psHIDDevice[ui32Loop]); + } + } + + // + // If we get here, there are no space device slots so return NULL to + // indicate a problem. + // + return(0); +} + +//***************************************************************************** +// +//! This function is used to release an instance of a HID device. +//! +//! \param psHIDInstance is the instance value for a HID device to release. +//! +//! This function releases an instance of a HID device that was created by a +//! call to USBHHIDOpen(). This call is required to allow other HID devices +//! to be enumerated after another HID device has been disconnected. The +//! \e psHIDInstance parameter should hold the value that was returned from +//! the previous call to USBHHIDOpen(). +//! +//! \return None. +// +//***************************************************************************** +void +USBHHIDClose(tHIDInstance *psHIDInstance) +{ + // + // Disable any more notifications from the HID layer. + // + psHIDInstance->pfnCallback = 0; + + // + // Mark this device slot as free. + // + psHIDInstance->iDeviceType = eUSBHHIDClassNone; +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint. +// +//***************************************************************************** +static void +HIDIntINCallback(uint32_t ui32Pipe, uint32_t ui32Event) +{ + int32_t i32Dev; + + switch (ui32Event) + { + // + // Handles a request to schedule a new request on the interrupt IN + // pipe. + // + case USB_EVENT_SCHEDULER: + { + USBHCDPipeSchedule(ui32Pipe, 0, 1); + break; + } + // + // Called when new data is available on the interrupt IN pipe. + // + case USB_EVENT_RX_AVAILABLE: + { + // + // Determine which device this notification is intended for. + // + for(i32Dev = 0; i32Dev < MAX_HID_DEVICES; i32Dev++) + { + // + // Does this device own the pipe we have been passed? + // + if(g_psHIDDevice[i32Dev].ui32IntInPipe == ui32Pipe) + { + // + // Yes - send the report data to the USB host HID device + // class driver. + // + g_psHIDDevice[i32Dev].pfnCallback( + g_psHIDDevice[i32Dev].pvCBData, + USB_EVENT_RX_AVAILABLE, ui32Pipe, 0); + } + } + + break; + } + } +} + +//***************************************************************************** +// +//! This function is used to open an instance of the HID driver. +//! +//! \param psDevice is a pointer to the device information structure. +//! +//! This function will attempt to open an instance of the HID 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 USBHIDClose() when the +//! driver is no longer needed. +//! +//! \return The function will return a pointer to a HID driver instance. +// +//***************************************************************************** +static void * +HIDDriverOpen(tUSBHostDevice *psDevice) +{ + int32_t i32Idx, i32Dev; + tEndpointDescriptor *psEndpointDescriptor; + tInterfaceDescriptor *psInterface; + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); + + // + // Search the currently open instances for one that supports the protocol + // of this device. + // + for(i32Dev = 0; i32Dev < MAX_HID_DEVICES; i32Dev++) + { + if(g_psHIDDevice[i32Dev].iDeviceType == + psInterface->bInterfaceProtocol) + { + // + // Save the device pointer. + // + g_psHIDDevice[i32Dev].psDevice = psDevice; + + for(i32Idx = 0; i32Idx < 3; i32Idx++) + { + // + // Get the first endpoint descriptor. + // + psEndpointDescriptor = USBDescGetInterfaceEndpoint(psInterface, + i32Idx, + 256); + + // + // If no more endpoints then break out. + // + if(psEndpointDescriptor == 0) + { + break; + } + + // + // Interrupt + // + if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // Interrupt IN. + // + if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + g_psHIDDevice[i32Dev].ui32IntInPipe = + USBHCDPipeAlloc(0, USBHCD_PIPE_INTR_IN, + psDevice, HIDIntINCallback); + USBHCDPipeConfig(g_psHIDDevice[i32Dev].ui32IntInPipe, + psEndpointDescriptor->wMaxPacketSize, + psEndpointDescriptor->bInterval, + (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_psHIDDevice[i32Dev].pfnCallback != 0) + { + g_psHIDDevice[i32Dev].pfnCallback( + g_psHIDDevice[i32Dev].pvCBData, + USB_EVENT_CONNECTED, + (uint32_t)&g_psHIDDevice[i32Dev], 0); + } + + // + // Save the device pointer. + // + g_psHIDDevice[i32Dev].psDevice = psDevice; + + return (&g_psHIDDevice[i32Dev]); + } + } + + // + // If we get here, no user has registered an interest in this particular + // HID device so we return an error. + // + return(0); +} + +//***************************************************************************** +// +//! This function is used to release an instance of the HID driver. +//! +//! \param pvInstance is an instance pointer that needs to be released. +//! +//! This function will free up any resources in use by the HID driver instance +//! that is passed in. The \e pvInstance pointer should be a valid value that +//! was returned from a call to USBHIDOpen(). +//! +//! \return None. +// +//***************************************************************************** +static void +HIDDriverClose(void *pvInstance) +{ + tHIDInstance *psInst; + + // + // Get our instance pointer. + // + psInst = (tHIDInstance *)pvInstance; + + // + // Reset the device pointer. + // + psInst->psDevice = 0; + + // + // Free the Interrupt IN pipe. + // + if(psInst->ui32IntInPipe != 0) + { + USBHCDPipeFree(psInst->ui32IntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(psInst->pfnCallback != 0) + { + psInst->pfnCallback(psInst->pvCBData, USB_EVENT_DISCONNECTED, + (uint32_t)pvInstance, 0); + } +} + +//***************************************************************************** +// +//! This function is used to set the idle timeout for a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui8Duration is the duration of the timeout in milliseconds. +//! \param ui8ReportID is the report identifier to set the timeout on. +//! +//! This function will send the Set Idle command to a HID device to set the +//! idle timeout for a given report. The length of the timeout is specified +//! by the \e ui8Duration parameter and the report the timeout for is in the +//! \e ui8ReportID value. +//! +//! \return Always returns 0. +// +//***************************************************************************** +uint32_t +USBHHIDSetIdle(tHIDInstance *psHIDInstance, uint8_t ui8Duration, + uint8_t ui8ReportID) +{ + tUSBRequest sSetupPacket; + + // + // This is a Class specific interface OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_IDLE; + sSetupPacket.wValue = (ui8Duration << 8) | ui8ReportID; + + // + // Set this on interface 1. + // + sSetupPacket.wIndex = 0; + + // + // This is always 0 for this request. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + return(USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, + 0, 0, MAX_PACKET_SIZE_EP0)); +} + +//***************************************************************************** +// +//! This function can be used to retrieve the report descriptor for a given +//! device instance. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param pui8Buffer is the memory buffer to use to store the report +//! descriptor. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! This function is used to return a report descriptor from a HID device +//! instance so that it can determine how to interpret reports that are +//! returned from the device indicated by the \e psHIDInstance parameter. +//! This call is blocking and will return the number of bytes read into the +//! \e pui8Buffer. +//! +//! \return Returns the number of bytes read into the \e pui8Buffer. +// +//***************************************************************************** +uint32_t +USBHHIDGetReportDescriptor(tHIDInstance *psHIDInstance, uint8_t *pui8Buffer, + uint32_t ui32Size) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Bytes; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = USB_HID_DTYPE_REPORT << 8; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // All devices must have at least an 8 byte max packet size so just ask + // for 8 bytes to start with. + // + sSetupPacket.wLength = ui32Size; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + ui32Bytes = USBHCDControlTransfer(0, &sSetupPacket, + psHIDInstance->psDevice, pui8Buffer, ui32Size, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function is used to set or clear the boot protocol state of a device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32BootProtocol is either zero or non-zero to indicate which +//! protocol to use for the device. +//! +//! A USB host device can use this function to set the protocol for a connected +//! HID device. This is commonly used to set keyboards and mice into their +//! simplified boot protocol modes to fix the report structure to a know +//! state. +//! +//! \return This function returns 0. +// +//***************************************************************************** +uint32_t +USBHHIDSetProtocol(tHIDInstance *psHIDInstance, uint32_t ui32BootProtocol) +{ + tUSBRequest sSetupPacket; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_PROTOCOL; + + if(ui32BootProtocol) + { + // + // Boot Protocol. + // + sSetupPacket.wValue = 0; + } + else + { + // + // Report Protocol. + // + sSetupPacket.wValue = 1; + } + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // Always 0. + // + sSetupPacket.wLength = 0; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, 0, 0, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to retrieve a report from a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32Interface is the interface to retrieve the report from. +//! \param pui8Data is the memory buffer to use to store the report. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! This function is used to retrieve a report from a USB pipe. It is usually +//! called when the USB HID layer has detected a new data available in a USB +//! pipe. The USB HID host device code will receive a +//! \b USB_EVENT_RX_AVAILABLE event when data is available, allowing the +//! callback function to retrieve the data. +//! +//! \return Returns the number of bytes read from report. +// +//***************************************************************************** +uint32_t +USBHHIDGetReport(tHIDInstance *psHIDInstance, uint32_t ui32Interface, + uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Read the Data out. + // + ui32Size = USBHCDPipeReadNonBlocking(psHIDInstance->ui32IntInPipe, + pui8Data, ui32Size); + + // + // Return the number of bytes read from the interrupt in pipe. + // + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to send a report to a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32Interface is the interface to send the report to. +//! \param pui8Data is the memory buffer to use to store the report. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! This function is used to send a report to a USB HID device. It can be +//! only be called from outside the callback context as this function will not +//! return from the call until the data has been sent successfully. +//! +//! \return Returns the number of bytes sent to the device. +// +//***************************************************************************** +uint32_t +USBHHIDSetReport(tHIDInstance *psHIDInstance, uint32_t ui32Interface, + uint8_t *pui8Data, uint32_t ui32Size) +{ + tUSBRequest sSetupPacket; + + // + // This is a class specific OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_REPORT; + sSetupPacket.wValue = USB_HID_REPORT_OUTPUT << 8; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = (uint16_t)ui32Interface; + + // + // Always 0. + // + sSetupPacket.wLength = ui32Size; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, + pui8Data, ui32Size, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psHIDInstance is the HID instance that was returned from the call +//! to USBHHIDOpen(). +//! +//! This function forwards a request from a HID device class to the host +//! controller to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHHIDLPMSleep(tHIDInstance *psHIDInstance) +{ + // + // Forward the request to the control endpoint of the device. + // + return(USBHCDLPMSleep(psHIDInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psHIDInstance is the HID instance that was returned from the call +//! to USBHHIDOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHHIDLPMStatus(tHIDInstance *psHIDInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psHIDInstance->psDevice)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** -- cgit v1.3.1