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/device/usbdhid.c | 2502 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2502 insertions(+) create mode 100644 usblib/device/usbdhid.c (limited to 'usblib/device/usbdhid.c') diff --git a/usblib/device/usbdhid.c b/usblib/device/usbdhid.c new file mode 100644 index 0000000..c708f62 --- /dev/null +++ b/usblib/device/usbdhid.c @@ -0,0 +1,2502 @@ +//***************************************************************************** +// +// usbdhid.c - USB HID device class 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_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdhid.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup hid_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The subset of endpoint status flags that we consider to be reception +// errors. These are passed to the client via USB_EVENT_ERROR if seen. +// +//***************************************************************************** +#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \ + USBERR_DEV_RX_OVERRUN | \ + USBERR_DEV_RX_FIFO_FULL) + +//***************************************************************************** +// +// Marker used to indicate that a given HID descriptor cannot be found in the +// client-supplied list. +// +//***************************************************************************** +#define HID_NOT_FOUND 0xFFFFFFFF + +//***************************************************************************** +// +// Flags that may appear in ui16DeferredOpFlags to indicate some operation that +// has been requested but could not be processed at the time it was received. +// Each deferred operation is defined as the bit number that should be set in +// tHIDInstance->ui16DeferredOpFlags to indicate that the operation is pending. +// +//***************************************************************************** +#define HID_DO_PACKET_RX 5 +#define HID_DO_SEND_IDLE_REPORT 6 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define INT_IN_ENDPOINT USB_EP_3 +#define INT_OUT_ENDPOINT USB_EP_3 + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +uint8_t g_pui8HIDDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + USB_CLASS_DEVICE, // USB Device Class + 0, // USB Device Sub-class + USB_HID_PROTOCOL_NONE, // USB Device protocol + USBDHID_MAX_PACKET, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (VID). + USBShort(0), // Product ID (PID). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvHIDInstance, tUSBRequest *psUSBRequest); +static void HandleRequest(void *pvHIDInstance, tUSBRequest *psUSBRequest); +static void HandleConfigChange(void *pvHIDInstance, uint32_t ui32Info); +static void HandleEP0DataReceived(void *pvHIDInstance, uint32_t ui32Info); +static void HandleEP0DataSent(void *pvHIDInstance, uint32_t ui32Info); +static void HandleReset(void *pvHIDInstance); +static void HandleSuspend(void *pvHIDInstance); +static void HandleResume(void *pvHIDInstance); +static void HandleDisconnect(void *pvHIDInstance); +static void HandleEndpoints(void *pvHIDInstance, uint32_t ui32Status); +static void HandleDevice(void *pvHIDInstance, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB HID devices. +// +//***************************************************************************** +const tCustomHandlers g_sHIDHandlers = +{ + // + // GetDescriptor + // + HandleGetDescriptor, + + // + // RequestHandler + // + HandleRequest, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + HandleConfigChange, + + // + // DataReceived + // + HandleEP0DataReceived, + + // + // DataSentCallback + // + HandleEP0DataSent, + + // + // ResetHandler + // + HandleReset, + + // + // SuspendHandler + // + HandleSuspend, + + // + // ResumeHandler + // + HandleResume, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler. + // + HandleDevice +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pui16DeferredOp points to the flags variable which is to be modified. +// \param ui16Bit indicates which bit number is to be set or cleared. +// \param bSet indicates the state that the flag must be set to. If \b true, +// the flag is set, if \b false, the flag is cleared. +// +// This function safely sets or clears a bit in a flag variable. The operation +// makes use of bitbanding to ensure that the operation is atomic (no read- +// modify-write is required). +// +// \return None. +// +//***************************************************************************** +static void +SetDeferredOpFlag(volatile uint16_t *pui16DeferredOp, uint16_t ui16Bit, + bool bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pui16DeferredOp, ui16Bit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// This function is called to clear the counter used to keep track of the time +// elapsed since a given report was last sent. +// +// \param psHIDDevice points to the HID device structure whose report timer is +// to be cleared. +// \param ui8ReportID is the first byte of the report to be sent. If this +// device offers more than one input report, this value is used to find the +// relevant report timer structure in the psHIDDevice structure. +// +// \return None. +// +//***************************************************************************** +static void +ClearReportTimer(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID) +{ + uint32_t ui32Loop; + + if(psHIDDevice->ui8NumInputReports > 1) + { + // + // We have more than 1 input report so the report must begin with a + // byte containing the report ID. Scan the table we were provided + // when the device was initialized to find the entry for this report. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; + ui32Loop++) + { + if(psHIDDevice->psReportIdle[ui32Loop].ui8ReportID == ui8ReportID) + { + break; + } + } + } + else + { + ui32Loop = 0; + } + + // + // If we drop out of the loop with an index less than ui8NumInputReports, + // we found the relevant report so clear its timer. + // + if(ui32Loop < psHIDDevice->ui8NumInputReports) + { + psHIDDevice->psReportIdle[ui32Loop].ui32TimeSinceReportmS = 0; + } +} + +//***************************************************************************** +// +// This function is called to clear the idle period timers for each input +// report supported by the device. +// +// \param psHIDDevice points to the HID device structure whose timers are to be +// cleared. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +ClearIdleTimers(const tUSBDHIDDevice *psHIDDevice) +{ + uint32_t ui32Loop; + + // + // Clear the "time till next report" counters for each input report. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = + psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS * 4; + } +} + +//***************************************************************************** +// +// This function is called periodically to allow us to process the report idle +// timers. +// +// \param psHIDDevice points to the HID device structure whose timers are to be +// updated. +// \param ui32ElapsedmS indicates the number of milliseconds that have elapsed +// since the last call to this function. +// +// \return None. +// +//***************************************************************************** +static void +ProcessIdleTimers(const tUSBDHIDDevice *psHIDDevice, uint32_t ui32ElapsedmS) +{ + uint32_t ui32Loop, ui32SizeReport; + void *pvReport; + tHIDInstance *psInst; + bool bDeferred; + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)psHIDDevice)->sPrivateData; + + // + // We have not had to defer any report transmissions yet. + // + bDeferred = false; + + // + // Look at each of the input report idle timers in turn. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + // + // Update the time since the last report was sent. + // + psHIDDevice->psReportIdle[ui32Loop].ui32TimeSinceReportmS += + ui32ElapsedmS; + + // + // Is this timer running? + // + if(psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS) + { + // + // Yes - is it about to expire? + // + if(psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS <= + ui32ElapsedmS) + { + // + // The timer is about to expire. Can we send a report right + // now? + // + if((psInst->iHIDTxState == eHIDStateIdle) && + (psInst->bSendInProgress == false)) + { + // + // We can send a report so send a message to the + // application to retrieve its latest report for + // transmission to the host. + // + ui32SizeReport = psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, + USBD_HID_EVENT_IDLE_TIMEOUT, + psHIDDevice->psReportIdle[ui32Loop].ui8ReportID, + &pvReport); + + // + // Schedule the report for transmission. + // + USBDHIDReportWrite((void *)psHIDDevice, pvReport, + ui32SizeReport, true); + + // + // Reload the timer for the next period. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = + psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS * 4; + } + else + { + // + // We can't send the report straight away so flag it for + // transmission as soon as the previous transmission ends. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = 0; + bDeferred = true; + } + } + else + { + // + // The timer is not about to expire. Update the time till the + // next report transmission. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS -= + ui32ElapsedmS; + } + } + } + + // + // If we had to defer transmission of any report, remember this so that we + // will process it as soon as possible. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, HID_DO_SEND_IDLE_REPORT, + bDeferred); +} + +static void +SetIdleTimeout(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID, + uint8_t ui8Timeout4mS) +{ + uint32_t ui32Loop; + bool bReportNeeded; + tHIDReportIdle *psIdle; + + // + // Remember that we have not found any report that needs to be sent + // immediately. + // + bReportNeeded = false; + + // + // Search through all the input reports looking for ones that fit the + // requirements. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psIdle = &psHIDDevice->psReportIdle[ui32Loop]; + + // + // If the report ID passed matches the report ID in the idle timer + // control structure or we were passed a report ID of zero, which + // indicates that all timers are to be set... + // + if(!ui8ReportID || (ui8ReportID == psIdle->ui8ReportID)) + { + // + // Save the new duration for the idle timer. + // + psIdle->ui8Duration4mS = ui8Timeout4mS; + + // + // Are we enabling the idle timer? If so, fix up the time until it + // needs to fire. + // + if(ui8Timeout4mS) + { + // + // Determine what the timeout is for this report given the time + // since the last report of this type was sent. + // + if(psIdle->ui32TimeSinceReportmS >= + ((uint32_t)ui8Timeout4mS * 4)) + { + psIdle->ui16TimeTillNextmS = 0; + bReportNeeded = true; + } + else + { + psIdle->ui16TimeTillNextmS = + (((uint16_t)ui8Timeout4mS * 4) - + psIdle->ui32TimeSinceReportmS); + } + } + } + } + + // + // If we get to here and bReportNeeded is true, this means we need to + // send back at least one of the input reports as soon as possible. Try + // to do this immediately. + // + if(bReportNeeded) + { + ProcessIdleTimers(psHIDDevice, 0); + } +} + +//***************************************************************************** +// +// Find the idle timeout for a given HID input report. +// +// \param psHIDDevice points to the HID device whose report idle timeout is to +// be found. +// \param ui8ReportID identifies the report whose timeout is requested. If 0, +// the timeout for the first report is returns, regardless of its ID (or +// whether it has one). +// +// This function returns the current idle timeout for a given HID input report. +// The value returned is expressed in terms of 4mS intervals. Convert to +// milliseconds by multiplying by 4. If the return value is 0, this indicates +// that an infinite timeout is currently set and the device will not send the +// report unless a state change occurs. +// +// \return Returns the current idle timeout for the given report. +// +//***************************************************************************** +static uint32_t +GetIdleTimeout(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID) +{ + uint32_t ui32Loop; + tHIDReportIdle *psIdle; + + // + // Search through all the input reports looking for ones that fit the + // requirements. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psIdle = &psHIDDevice->psReportIdle[ui32Loop]; + + // + // If the report ID passed matches the report ID in the idle timer + // control structure or we were passed a report ID of zero, which + // indicates that all timers are to be set... + // + if(!ui8ReportID || (ui8ReportID == psIdle->ui8ReportID)) + { + // + // We found a report matching the required ID or we were not passed + // an ID and we are looking at the first report information. + // + return((uint32_t)psIdle->ui8Duration4mS); + } + } + + // + // If we drop out, the report could not be found so we need to indicate + // an error. + // + return(HID_NOT_FOUND); +} + +//***************************************************************************** +// +// Find the n-th HID class descriptor of a given type in the client-provided +// descriptor table. +// +// \param psHIDDevice points to the HID device which is to be searched for the +// required class descriptor. +// \param ui8Type is the type of class descriptor being requested. This will +// be either USB_HID_DTYPE_REPORT or USB_HID_DTYPE_PHYSICAL. +// \param ui32Index is the zero-based index of the descriptor that is being +// requested. +// +// This function parses the supplied HID descriptor to find the index into the +// sClassDescriptor array that corresponds to the requested descriptor. If +// a descriptor with the requested index does not exist, HID_NOT_FOUND will be +// returned unless the request is for a physical descriptor and at least one +// such descriptor exists. In this case, the index returned will be for the +// last physical descriptor (as required by the HID spec 7.1.1). +// +// \return Returns the index of the descriptor within the sClassDescriptor +// of the tHIDDevice structure if found or HID_NOT_FOUND otherwise. +// +//***************************************************************************** +static uint32_t +FindHIDDescriptor(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8Type, + uint32_t ui32Index, uint32_t *pui32Len) +{ + bool bFoundType; + uint32_t ui32Loop, ui32Count, ui32LastFound; + const tHIDClassDescriptorInfo *psDesc; + + // + // Remember that we have not found any descriptor with a matching type yet. + // + bFoundType = false; + ui32Count = 0; + ui32LastFound = 0; + + // + // Walk through all the class descriptors looking for the one which + // matches the requested index and type. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->psHIDDescriptor->bNumDescriptors; + ui32Loop++) + { + psDesc = &(psHIDDevice->psHIDDescriptor->sClassDescriptor[ui32Loop]); + if(psDesc->bDescriptorType == ui8Type) + { + // + // We found a descriptor of the correct type. Is this the + // correct index? + // + bFoundType = true; + + // + // Is this the descriptor we are looking for? + // + if(ui32Count == ui32Index) + { + // + // Yes - we found it so return the index and size to the + // caller. + // + *pui32Len = (uint32_t)psDesc->wDescriptorLength; + return(ui32Loop); + } + else + { + // + // Update our count and keep looking. Remember where we were + // when we found this descriptor in case we need to return the + // last physical descriptor. + // + ui32Count++; + ui32LastFound = ui32Loop; + } + } + } + + // + // If we drop out, we did not find the requested descriptor. Now handle + // the special case of a physical descriptor - if we found any physical + // descriptors, return the last one. + // + if((ui8Type == USB_HID_DTYPE_PHYSICAL) && bFoundType) + { + // + // Get the length of the last descriptor we found. + // + psDesc = + &(psHIDDevice->psHIDDescriptor->sClassDescriptor[ui32LastFound]); + *pui32Len = (uint32_t)psDesc->wDescriptorLength; + + // + // Return the index to the caller. + // + return(ui32LastFound); + } + else + { + // + // We could not find the descriptor so return an appropriate error. + // + return(HID_NOT_FOUND); + } +} + +//***************************************************************************** +// +// Schedule transmission of the next packet forming part of an input report. +// +// \param psHIDInst points to the HID device instance whose input report is to +// be sent. +// +// This function is called to transmit the next packet of an input report +// passed to the driver via a call to USBDHIDReportWrite. If any data remains +// to be sent, a USB packet is written to the FIFO and scheduled for +// transmission to the host. The function ensures that reports are sent as +// a sequence of full packets followed by either a single int16_t packet or a +// packet with no data to indicate the end of the transaction. +// +//***************************************************************************** +static int32_t +ScheduleReportTransmission(tHIDInstance *psHIDInst) +{ + uint32_t ui32NumBytes; + uint8_t *pui8Data; + int32_t i32Retcode; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = (uint32_t)(psHIDInst->ui16InReportSize - + psHIDInst->ui16InReportIndex); + + // + // Limit individual transfers to the maximum packet size for the endpoint. + // + if(ui32NumBytes > USBDHID_MAX_PACKET) + { + ui32NumBytes = USBDHID_MAX_PACKET; + } + + // + // Where are we sending this data from? + // + pui8Data = psHIDInst->pui8InReportData + psHIDInst->ui16InReportIndex; + + // + // Put the data in the correct FIFO. + // + i32Retcode = MAP_USBEndpointDataPut(psHIDInst->ui32USBBase, + psHIDInst->ui8INEndpoint, + pui8Data, ui32NumBytes); + + if(i32Retcode != -1) + { + // + // Update the count and index ready for the next time round. + // + psHIDInst->ui16InReportIndex += ui32NumBytes; + + // + // Send out the current data. + // + i32Retcode = MAP_USBEndpointDataSend(psHIDInst->ui32USBBase, + psHIDInst->ui8INEndpoint, + USB_TRANS_IN); + } + + // + // Tell the caller how we got on. + // + return(i32Retcode); +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psHIDDevice is the device instance whose endpoint is to be processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts signaling +// the arrival of data on the interrupt OUT endpoint (in other words, whenever +// the host has sent us a packet of data). We inform the client that a packet +// is available and, on return, check to see if the packet has been read. If +// not, we schedule another notification to the client for a later time. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static bool +ProcessDataFromHost(tUSBDHIDDevice *psHIDDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus, ui32Size; + tHIDInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Has a packet been received? + // + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Set the flag we use to indicate that a packet read is pending. This + // will be cleared if the packet is read. If the client does not read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, HID_DO_PACKET_RX, + true); + + // + // How big is the packet we have just been sent? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // The receive channel is not blocked so let the caller know + // that a packet is waiting. The parameters are set to indicate + // that the packet has not been read from the hardware FIFO yet. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ui32EPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_ERROR, + (ui32EPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psHIDDevice is the device instance whose endpoint is to be processed. +// \param ui32Status is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the interrupt IN endpoint (in other words, whenever data has been +// transmitted to the USB host). We examine the cause of the interrupt and, +// if due to completion of a transmission, notify the client. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static bool +ProcessDataToHost(tUSBDHIDDevice *psHIDDevice, uint32_t ui32Status) +{ + tHIDInstance *psInst; + uint32_t ui32EPStatus; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8INEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, psInst->ui8INEndpoint, + ui32EPStatus); + + // + // Our last packet was transmitted successfully. Is there any more data to + // send or have we finished sending the whole report? We know we finished + // if the ui16InReportIndex has reached the ui16InReportSize value. + // + if(psInst->ui16InReportSize == psInst->ui16InReportIndex) + { + // + // We finished sending the last report so are idle once again. + // + psInst->iHIDTxState = eHIDStateIdle; + + // + // Notify the client that the report transmission completed. + // + psHIDDevice->pfnTxCallback(psHIDDevice->pvTxCBData, + USB_EVENT_TX_COMPLETE, + psInst->ui16InReportSize, (void *)0); + + // + // Do we have any reports to send as a result of idle timer timeouts? + // + if(psInst->ui16DeferredOpFlags & (1 << HID_DO_SEND_IDLE_REPORT)) + { + // + // Yes - send reports for any timers that expired recently. + // + ProcessIdleTimers(psHIDDevice, 0); + } + } + else + { + // + // There must be more data or a zero length packet waiting to be sent + // so go ahead and do this. + // + ScheduleReportTransmission(psInst); + } + + return(true); +} + +//***************************************************************************** +// +// Called by the USB stack for any activity involving one of our endpoints +// other than EP0. This function is a fan out that merely directs the call to +// the correct handler depending upon the endpoint and transaction direction +// signaled in ui32Status. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvHIDInstance, uint32_t ui32Status) +{ + tUSBDHIDDevice *psHIDInst; + tHIDInstance *psInst; + + ASSERT(pvHIDInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ui32Index is different in both cases. + // + psHIDInst = (tUSBDHIDDevice *)pvHIDInstance; + psInst = &psHIDInst->sPrivateData; + + // + // Handler for the interrupt OUT data endpoint. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(pvHIDInstance, ui32Status); + } + + // + // Handler for the interrupt IN data endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint))) + { + ProcessDataToHost(pvHIDInstance, ui32Status); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvHIDInstance, uint32_t ui32Info) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = pvHIDInstance; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Set all our endpoints to idle state. + // + psInst->iHIDRxState = eHIDStateIdle; + psInst->iHIDTxState = eHIDStateIdle; + + // + // If we are not currently connected let the client know we are open for + // business. + // + if(!psInst->bConnected) + { + // + // Pass the connected event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + } + + // + // Clear the idle timers for each input report. + // + ClearIdleTimers(psHIDDevice); + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvHIDInstance, uint32_t ui32Request, void *pvRequestData) +{ + tHIDInstance *psInst; + uint8_t *pui8Data; + tUSBDHIDDevice *psHIDDevice; + + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // Create the serial instance data. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Create the int8_t array used by the events supported by the USB CDC + // serial class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ui8Interface = pui8Data[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f); + } + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM resume event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_RESUME, 0, (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM sleep event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_SLEEP, 0, (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM error event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_ERROR, 0, (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvHIDInstance) +{ + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(psHIDDevice->sPrivateData.bConnected) + { + // + // Pass the disconnected event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psHIDDevice->sPrivateData.bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a request for a +// non-standard descriptor is received. +// +// \param pvHIDInstance is the instance data for this request. +// \param psUSBRequest points to the request received. +// +// This call parses the provided request structure and determines which +// descriptor is being requested. Assuming the descriptor can be found, it is +// scheduled for transmission via endpoint zero. If the descriptor cannot be +// found, the endpoint is stalled to indicate an error to the host. +// +//***************************************************************************** +static void +HandleGetDescriptor(void *pvHIDInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Size, ui32Desc; + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Which type of class descriptor are we being asked for? + // + switch(psUSBRequest->wValue >> 8) + { + // + // This is a request for a HID report or physical descriptor. + // + case USB_HID_DTYPE_REPORT: + case USB_HID_DTYPE_PHYSICAL: + { + // + // Find the index to the descriptor that is being queried. + // + ui32Size = 0; + ui32Desc = FindHIDDescriptor(psHIDDevice, + psUSBRequest->wValue >> 8, + psUSBRequest->wValue & 0xFF, + &ui32Size); + + // + // Did we find the descriptor? + // + if(ui32Desc == HID_NOT_FOUND) + { + // + // No - stall the endpoint and return. + // + USBDCDStallEP0(0); + return; + } + + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if(ui32Size > psUSBRequest->wLength) + { + ui32Size = psUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, + (uint8_t *)psHIDDevice->ppui8ClassDescriptors[ui32Desc], + ui32Size); + + break; + } + + // + // This is a request for the HID descriptor (as found in the + // configuration descriptor following the relevant interface). + // + case USB_HID_DTYPE_HID: + { + // + // How big is the HID descriptor? + // + ui32Size = (uint32_t)psHIDDevice->psHIDDescriptor->bLength; + + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if(ui32Size > psUSBRequest->wLength) + { + ui32Size = psUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, (uint8_t *)psHIDDevice->psHIDDescriptor, + ui32Size); + break; + } + + // + // This was an unknown request so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvHIDInstance is the instance data for this HID device. +// \param psUSBRequest points to the request received. +// +// This call parses the provided request structure. Assuming the request is +// understood, it is handled and any required response generated. If the +// request cannot be handled by this device class, endpoint zero is stalled to +// indicate an error to the host. +// +//***************************************************************************** +static void +HandleRequest(void *pvHIDInstance, tUSBRequest *psUSBRequest) +{ + tHIDInstance *psInst; + uint8_t ui8Protocol; + uint32_t ui32Size, ui32Timeout; + uint8_t *pui8Report; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Make sure the request was for this interface. + // + if(psUSBRequest->wIndex != psInst->ui8Interface) + { + return; + } + + // + // Determine the type of request. + // + switch(psUSBRequest->bRequest) + { + // + // A Set Report request is received from the host when it sends an + // Output report via endpoint 0. + // + case USBREQ_SET_REPORT: + { + // + // Ask the application for a buffer large enough to hold the + // report we are to be sent. + // + psInst->ui16OutReportSize = psUSBRequest->wLength; + psInst->pui8OutReportData = + (uint8_t *)psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT_BUFFER, + psUSBRequest->wValue, + (void *)(uint32_t)(psUSBRequest->wLength)); + + // + // Did the client provide us a buffer? + // + if(!psInst->pui8OutReportData) + { + // + // The application could not provide us a buffer so stall the + // request. + // + USBDCDStallEP0(0); + } + else + { + // + // The client provided us a buffer to read the report into + // so request the data from the host. + // + + // + // Set the state to indicate we are waiting for data. + // + psInst->iHIDRxState = eHIDStateWaitData; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, psInst->pui8OutReportData, + (uint32_t)psUSBRequest->wLength); + + // + // Need to ACK the data on end point 0 in this case. Do this + // after requesting the data to prevent race conditions that + // occur if you acknowledge before setting up to receive the + // request data. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, false); + } + + break; + } + + // + // A Get Report request is used by the host to poll a device for its + // current state. + // + case USBREQ_GET_REPORT: + { + // + // Get the latest report from the application. + // + ui32Size = psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT, + psUSBRequest->wValue, &pui8Report); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // ..then send back the requested report. + // + psInst->bGetRequestPending = true; + USBDCDSendDataEP0(0, pui8Report, ui32Size); + + break; + } + + // + // A set IDLE request has been made. This indicates to us how often a + // given report should be sent back to the host in the absence of any + // change in state of the device. + // + case USBREQ_SET_IDLE: + { + // + // Set the idle timeout for the requested report(s). + // + SetIdleTimeout(psHIDDevice, psUSBRequest->wValue & 0xFF, + (psUSBRequest->wValue >> 8) & 0xFF); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + break; + } + + // + // A get IDLE request has been made. This request queries the current + // idle timeout for a given report. + // + case USBREQ_GET_IDLE: + { + // + // Determine the timeout for the requested report. + // + ui32Timeout = GetIdleTimeout(psHIDDevice, psUSBRequest->wValue); + + if(ui32Timeout != HID_NOT_FOUND) + { + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui32Timeout, 1); + } + else + { + // + // The report ID was not found so stall the endpoint. + // + USBDCDStallEP0(0); + } + break; + } + + // + // Set either boot or report protocol for reports sent from the device. + // This is only supported by devices in the boot subclass. + // + case USBREQ_SET_PROTOCOL: + { + if(psHIDDevice->ui8Subclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_SET_PROTOCOL, + psUSBRequest->wValue, + (void *)0); + } + else + { + // + // This is not a boot subclass device so stall the endpoint to + // show that we don't support this request. + // + USBDCDStallEP0(0); + } + break; + } + + // + // Inform the host of the protocol, boot or report, that is currently + // in use. This is only supported by devices in the boot subclass. + // + case USBREQ_GET_PROTOCOL: + { + if(psHIDDevice->ui8Subclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application callback to get the answer. + // + ui8Protocol = (uint8_t)psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, USBD_HID_EVENT_GET_PROTOCOL, 0, + (void *)0); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui8Protocol, 1); + } + else + { + // + // This is not a boot subclass device so stall the endpoint to + // show that we don't support this request. + // + USBDCDStallEP0(0); + } + break; + } + + // + // This request was not recognized so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the data requested +// on endpoint zero is received. +// +//***************************************************************************** +static void +HandleEP0DataReceived(void *pvHIDInstance, uint32_t ui32DataSize) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // If we were not passed any data, just return. + // + if(ui32DataSize == 0) + { + return; + } + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Make sure we are actually expecting something. + // + if(psInst->iHIDRxState != eHIDStateWaitData) + { + return; + } + + // + // Change the endpoint state back to idle now that we have been passed + // the data we were waiting for. + // + psInst->iHIDRxState = eHIDStateIdle; + + // + // The only things we ever request via endpoint zero are reports sent to + // us via a Set_Report request. Pass the newly received report on to + // the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_SET_REPORT, + psInst->ui16OutReportSize, + psInst->pui8OutReportData); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the data sent on +// endpoint zero is received and acknowledged by the host. +// +//***************************************************************************** +static void +HandleEP0DataSent(void *pvHIDInstance, uint32_t ui32Info) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // If we just sent a report in response to a Get_Report request, send an + // event to the application telling it that the transmission completed. + // + if(psInst->bGetRequestPending) + { + // + // Clear the flag now that we are sending the application callback. + // + psInst->bGetRequestPending = false; + + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_REPORT_SENT, 0, (void *)0); + } + + return; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// reset. If we are currently connected, send a disconnect event at this +// point. +// +//***************************************************************************** +static void +HandleReset(void *pvHIDInstance) +{ + ASSERT(pvHIDInstance != 0); + + // + // Merely call the disconnect handler. This causes a disconnect message to + // be sent to the client if we think we are currently connected. + // + HandleDisconnect(pvHIDInstance); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvHIDInstance) +{ + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (const tUSBDHIDDevice *)pvHIDInstance; + + // + // Pass the event on to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, USB_EVENT_SUSPEND, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is taken +// out of suspend state. +// +//***************************************************************************** +static void +HandleResume(void *pvHIDInstance) +{ + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (const tUSBDHIDDevice *)pvHIDInstance; + + // + // Pass the event on to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, USB_EVENT_RESUME, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called periodically and provides us with a time reference +// and method of implementing delayed or time-dependent operations. +// +// \param pvHIDInstance is the instance data for this request. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +HIDTickHandler(void *pvHIDInstance, uint32_t ui32TimemS) +{ + tHIDInstance *psInst; + uint32_t ui32Size; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // If we are connected, process our idle timers. + // + if(psInst->bConnected) + { + ProcessIdleTimers(psHIDDevice, ui32TimemS); + } + + // + // Do we have a deferred receive waiting + // + if(psInst->ui16DeferredOpFlags & (1 << HID_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ui32Size = MAP_USBEndpointDataAvail(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psHIDDevice points to a structure containing parameters customizing +//! the operation of the HID device. +//! +//! An application wishing to offer a USB HID interface to a host system +//! must call this function to initialize the USB controller and attach the +//! device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function will return the \e psHIDDevice +//! pointer passed to it. This must be passed on all future calls from the +//! application to the HID device class driver. +//! +//! The USB HID device class API offers the application a report-based transmit +//! interface for Input reports. Output reports may be received via the +//! control endpoint or via a dedicated Interrupt OUT endpoint. If using the +//! dedicated endpoint, report data is delivered to the application packet-by- +//! packet. If the application uses reports longer than \b USBDHID_MAX_PACKET +//! bytes and would rather receive full reports, it may use a USB buffer above +//! the receive channel to allow full reports to be read. +//! +//! Transmit Operation: +//! +//! Calls to USBDHIDReportWrite() pass complete reports to the driver for +//! transmission. These will be transmitted to the host using as many USB +//! packets as are necessary to complete the transmission. +//! +//! Once a full Input report has been acknowledged by the USB host, a +//! \b USB_EVENT_TX_COMPLETE event is sent to the application transmit callback +//! to inform it that another report may be transmitted. +//! +//! Receive Operation (when using a dedicated interrupt OUT endpoint): +//! +//! An incoming USB data packet will result in a call to the application +//! callback with event \b USB_EVENT_RX_AVAILABLE. The application must then +//! call USBDHIDPacketRead(), passing a buffer capable of holding the received +//! packet. The size of the packet may be determined by calling function +//! USBDHIDRxPacketAvailable() prior to reading the packet. +//! +//! Receive Operation (when not using a dedicated OUT endpoint): +//! +//! If no dedicated OUT endpoint is used, Output and Feature reports are sent +//! from the host using the control endpoint, endpoint zero. When such a +//! report is received, \b USBD_HID_EVENT_GET_REPORT_BUFFER is sent to the +//! application which must respond with a buffer large enough to hold the +//! report. The device class driver will then copy the received report into +//! the supplied buffer before sending \b USBD_HID_EVENT_SET_REPORT to indicate +//! that the report is now available. +//! +//! \note The application must not make any calls to the low level USB device +//! interface if interacting with USB via the USB HID device class API. Doing +//! so will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or the \e psHIDDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDInit(uint32_t ui32Index, tUSBDHIDDevice *psHIDDevice) +{ + tDeviceDescriptor *pi16DevDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psHIDDevice); + ASSERT(psHIDDevice->ppui8StringDescriptors); + ASSERT(psHIDDevice->pfnRxCallback); + ASSERT(psHIDDevice->pfnTxCallback); + ASSERT(psHIDDevice->ppui8ClassDescriptors); + ASSERT(psHIDDevice->psHIDDescriptor); + ASSERT((psHIDDevice->ui8NumInputReports == 0) || psHIDDevice->psReportIdle); + + USBDHIDCompositeInit(ui32Index, psHIDDevice, 0); + + // + // Fix up the device descriptor with the client-supplied values. + // + pi16DevDesc = (tDeviceDescriptor *)psHIDDevice->sPrivateData.sDevInfo.pui8DeviceDescriptor; + pi16DevDesc->idVendor = psHIDDevice->ui16VID; + pi16DevDesc->idProduct = psHIDDevice->ui16PID; + + // + // All is well so now pass the descriptors to the lower layer and put + // the HID device on the bus. + // + USBDCDInit(ui32Index, &psHIDDevice->sPrivateData.sDevInfo, + (void *)psHIDDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psHIDDevice); +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psHIDDevice points to a structure containing parameters customizing +//! the operation of the HID device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! USB HID device classes call this function to initialize the lower level +//! HID interface in the USB controller. If this HID device device is part of +//! a composite device, then the \e psCompEntry should point to the composite +//! device entry to initialize. This is part of the array that is passed to +//! the USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID APIs. +// +//***************************************************************************** +void * +USBDHIDCompositeInit(uint32_t ui32Index, tUSBDHIDDevice *psHIDDevice, + tCompositeEntry *psCompEntry) +{ + tHIDInstance *psInst; + tEndpointDescriptor *psEndpoint; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psHIDDevice); + ASSERT(psHIDDevice->ppsConfigDescriptor); + ASSERT(psHIDDevice->ppui8StringDescriptors); + ASSERT(psHIDDevice->pfnRxCallback); + ASSERT(psHIDDevice->pfnTxCallback); + ASSERT(psHIDDevice->ppui8ClassDescriptors); + ASSERT(psHIDDevice->psHIDDescriptor); + ASSERT((psHIDDevice->ui8NumInputReports == 0) || psHIDDevice->psReportIdle); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sHIDHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8HIDDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = psHIDDevice->ppsConfigDescriptor; + psInst->sDevInfo.ppui8StringDescriptors = + psHIDDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psHIDDevice->ui32NumStringDescriptors; + + // + // Default the endpoints zero before looking for them in the configuration + // descriptor. + // + psInst->ui8Interface = 0; + psInst->ui8INEndpoint = 0; + psInst->ui8OUTEndpoint = 0; + + // + // Get the first endpoint descriptor on interface 0. + // + psEndpoint = + USBDCDConfigGetInterfaceEndpoint(psHIDDevice->ppsConfigDescriptor[0], + psInst->ui8Interface, 0, 0); + + if(psEndpoint) + { + if(psEndpoint->bEndpointAddress & 0x80) + { + psInst->ui8INEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + else + { + psInst->ui8OUTEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + } + + // + // Get the second endpoint descriptor on interface 0. + // + psEndpoint = + USBDCDConfigGetInterfaceEndpoint(psHIDDevice->ppsConfigDescriptor[0], + psInst->ui8Interface, 0, 1); + if(psEndpoint) + { + if(psEndpoint->bEndpointAddress & 0x80) + { + psInst->ui8INEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + else + { + psInst->ui8OUTEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + } + + // + // Must have at least an IN endpoint. + // + if(psInst->ui8INEndpoint == 0) + { + return((void *)0); + } + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psHIDDevice; + } + + psInst->ui32USBBase = USB0_BASE; + psInst->iHIDRxState = eHIDStateUnconfigured; + psInst->iHIDTxState = eHIDStateUnconfigured; + psInst->ui16DeferredOpFlags = 0; + psInst->bConnected = false; + psInst->bGetRequestPending = false; + psInst->bSendInProgress = false; + psInst->ui16InReportIndex = 0; + psInst->ui16InReportSize = 0; + psInst->pui8InReportData = (uint8_t *)0; + psInst->ui16OutReportSize = 0; + psInst->pui8OutReportData = (uint8_t *)0; + + // + // Initialize the device info structure for the HID device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Initialize the input report idle timers if any input reports exist. + // + ClearIdleTimers(psHIDDevice); + + // + // Initialize the USB tick module, this will prevent it from being + // initialized later in the call to USBDCDInit(); + // + InternalUSBTickInit(); + + // + // Register our tick handler (this must be done after USBDCDInit). + // + InternalUSBRegisterTickHandler(HIDTickHandler, (void *)psHIDDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psHIDDevice); +} + +//***************************************************************************** +// +//! Shuts down the HID device. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function terminates HID operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the HID device is part of a composite device and instead the +//! USBDCompositeTerm() function should be called for the full composite +//! device. +//! +//! Following this call, the \e pvHIDInstance instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDTerm(void *pvHIDInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get a pointer to our instance data. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USBBaseToIndex(psInst->ui32USBBase)); + + psInst->ui32USBBase = 0; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the receive channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnRxCallback function +//! passed on USBDHIDInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the pvHIDInstance structure passed to USBDHIDInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDHIDSetRxCBData(void *pvHIDInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvHIDInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvHIDInstance)->pvRxCBData; + ((tUSBDHIDDevice *)pvHIDInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data pointer for the transmit callback. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the transmit channel callback function. +//! +//! The client uses this function to change the callback data pointer passed in +//! the first parameter on all callbacks to the \e pfnTxCallback function +//! passed on USBDHIDInit(). +//! +//! If a client wants to make runtime changes in the callback data, it must +//! ensure that the pvHIDInstance structure passed to USBDHIDInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback data pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDHIDSetTxCBData(void *pvHIDInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvHIDInstance); + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvHIDInstance)->pvTxCBData; + ((tUSBDHIDDevice *)pvHIDInstance)->pvTxCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a HID device report to the USB host via the HID interrupt IN +//! endpoint. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pi8Data points to the first byte of data which is to be transmitted. +//! \param ui32Length is the number of bytes of data to transmit. +//! \param bLast is ignored in this implementation. This parameter is required +//! to ensure compatibility with other device class drivers and USB buffers. +//! +//! This function schedules the supplied data for transmission to the USB +//! host in a single USB transaction using as many packets as it takes to send +//! all the data in the report. If no transmission is currently ongoing, +//! the first packet of data is immediately copied to the relevant USB endpoint +//! FIFO for transmission. Whenever all the report data has been acknowledged +//! by the host, a \b USB_EVENT_TX_COMPLETE event will be sent to the +//! application transmit callback indicating that another report can now be +//! transmitted. +//! +//! The caller must ensure that the data pointed to by \e pui8Data remains +//! accessible and unaltered until the \b USB_EVENT_TX_COMPLETE is received. +//! +//! \return Returns the number of bytes actually scheduled for transmission. +//! At this level, this will either be the number of bytes passed or 0 to +//! indicate a failure. +// +//***************************************************************************** +uint32_t +USBDHIDReportWrite(void *pvHIDInstance, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + tHIDInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Set a flag indicating that we are currently in the process of sending + // a packet. + // + psInst->bSendInProgress = true; + + // + // Can we send the data provided? + // + if(psInst->iHIDTxState != eHIDStateIdle) + { + // + // We are in the middle of sending another report. Return 0 to + // indicate that we can't send this report until the previous one + // finishes. + // + psInst->bSendInProgress = false; + return(0); + } + + // + // Clear the elapsed time since this report was last sent. + // + if(ui32Length) + { + ClearReportTimer(pvHIDInstance, *pi8Data); + } + + // + // Keep track of the whereabouts of the report so that we can send it in + // multiple packets if necessary. + // + psInst->pui8InReportData = pi8Data; + psInst->ui16InReportIndex = 0; + psInst->ui16InReportSize = ui32Length; + + // + // Schedule transmission of the first packet of the report. + // + psInst->iHIDTxState = eHIDStateWaitData; + i32Retcode = ScheduleReportTransmission(psInst); + + // + // Clear the flag we use to indicate that we are in the midst of sending + // a packet. + // + psInst->bSendInProgress = false; + + // + // Did an error occur while trying to send the data? + // + if(i32Retcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ui32Length); + } + else + { + // + // Yes - tell the caller we could not send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the interrupt OUT +//! endpoint (if in use). +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pi8Data points to a buffer into which the received data will be +//! written. +//! \param ui32Length is the size of the buffer pointed to by pi8Data. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to \e ui32Length bytes of data received from the USB +//! host into the supplied application buffer. If the driver detects that the +//! entire packet has been read, it is acknowledged to the host. +//! +//! The \e bLast parameter is ignored in this implementation since the end of +//! a packet can be determined without relying upon the client to provide +//! this information. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +uint32_t +USBDHIDPacketRead(void *pvHIDInstance, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + uint32_t ui32EPStatus, ui32Count, ui32Pkt; + tHIDInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ui32Pkt = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Get as much data as we can. + // + ui32Count = ui32Length; + i32Retcode = MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + pi8Data, &ui32Count); + + // + // Did we read the last of the packet data? + // + if(ui32Count == ui32Pkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + HID_DO_PACKET_RX, false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(i32Retcode != -1) + { + return(ui32Count); + } + } + + // + // No packet was available or an error occurred while reading so tell + // the caller no bytes were returned. + // + return(0); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the transmit buffer. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function indicates to the caller whether or not it is safe to send a +//! new report using a call to USBDHIDReportWrite(). The value returned will +//! be the maximum USB packet size (\b USBDHID_MAX_PACKET) if no transmission +//! is currently outstanding or 0 if a transmission is in progress. Since the +//! function USBDHIDReportWrite() can accept full reports longer than a single +//! USB packet, the caller should be aware that the returned value from this +//! class driver, unlike others, does not indicate the maximum size of report +//! that can be written but is merely an indication that another report can be +//! written. +//! +//! \return Returns 0 if an outgoing report is still being transmitted or +//! \b USBDHID_MAX_PACKET if no transmission is currently in progress. +// +//***************************************************************************** +uint32_t +USBDHIDTxPacketAvailable(void *pvHIDInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->iHIDTxState != eHIDStateIdle) + { + // + // We are not ready to receive a new packet so return 0. + // + return(0); + } + else + { + // + // We can receive a packet so return the max packet size for the + // relevant endpoint. + // + return(USBDHID_MAX_PACKET); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function may be used to determine if a received packet remains to be +//! read and allows the application to determine the buffer size needed to +//! read the data. +//! +//! \return Returns 0 if no received packet remains unprocessed or the +//! size of the packet if a packet is waiting to be read. +// +//***************************************************************************** +uint32_t +USBDHIDRxPacketAvailable(void *pvHIDInstance) +{ + uint32_t ui32EPStatus, ui32Size; + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + return(ui32Size); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvHIDInstance is the pointer to the HID device instance structure. +//! \param ui8Power indicates the current power status, either +//! \b USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDPowerStatusSet(void *pvHIDInstance, uint8_t ui8Power) +{ + ASSERT(pvHIDInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvHIDInstance is the pointer to the HID device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +bool +USBDHIDRemoteWakeupRequest(void *pvHIDInstance) +{ + ASSERT(pvHIDInstance); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1