//***************************************************************************** // // usbdhid.c - USB HID device class driver. // // Copyright (c) 2008-2010 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 AM1808 StarterWare USB Library and reused from revision 6288 // of the Stellaris USB Library. // //***************************************************************************** #include "hw_usb.h" #include "hw_types.h" #include "debug.h" #include "usb.h" #include "interrupt.h" #include "usblib.h" #include "usbhid.h" #include "usbdevice.h" #include "usbdhid.h" #include "usblibpriv.h" //***************************************************************************** // //! \addtogroup hid_device_class_api //! @{ // //***************************************************************************** //***************************************************************************** //This macro is used to diable the bit band operartion. Need to undefine this macro to use the // bit band operation. //*************************************************************************** #define DISABLE_BIT_BAND //***************************************************************************** // // 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 usDeferredOpFlags 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->usDeferredOpFlags to indicate that the operation is pending. // //***************************************************************************** #define HID_DO_PACKET_RX 5 #define HID_DO_SEND_IDLE_REPORT 6 //***************************************************************************** // // Macros to convert between USB controller base address and an index. These // are currently trivial but are included to allow for the possibility of // supporting more than one controller in the future. // //***************************************************************************** #if (USB_NUM_INSTANCE == 2) #define USB_BASE_TO_INDEX(BaseAddr, index) do{ \ if(USB0_BASE==BaseAddr) \ index = 0; \ else if(USB1_BASE==BaseAddr) \ index = 1; \ else \ index = -1; \ }while(0) #define USB_INDEX_TO_BASE(Index, BaseAddr) ( \ if(0==Index) \ BaseAddr = USB0_BASE; \ else if(1==Index) \ BaseAddr = USB1_BASE; \ else \ BaseAddr = -1; \ ) #else #define USB_BASE_TO_INDEX(BaseAddr, index) do{ \ if(USB0_BASE==BaseAddr) \ index = 0; \ else \ index = -1; \ }while(0) #define USB_INDEX_TO_BASE(Index, BaseAddr) ( \ if(0==Index) \ BaseAddr = USB0_BASE; \ else \ BaseAddr = -1; \ ) #endif //***************************************************************************** // // 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 //***************************************************************************** // // Maximum packet size for the interrupt endpoints used for report transmission // and reception and the associated FIFO sizes to set aside for each endpoint. // //***************************************************************************** #define INT_IN_EP_FIFO_SIZE USB_FIFO_SZ_64 #define INT_OUT_EP_FIFO_SIZE USB_FIFO_SZ_64 #define INT_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(INT_IN_EP_FIFO_SIZE) #define INT_OUT_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(INT_IN_EP_FIFO_SIZE) //***************************************************************************** // // USB instance Object // //***************************************************************************** extern tUSBInstanceObject g_USBInstance[]; //***************************************************************************** // // Device Descriptor. This is stored in RAM to allow several fields to be // changed at runtime based on the client's requirements. // //***************************************************************************** unsigned char g_pHIDDeviceDescriptor[] = { 18, // Size of this structure. USB_DTYPE_DEVICE, // Type of this structure. USBShort(0x200), // 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 64, // 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. }; //***************************************************************************** // // HID device configuration descriptor. // // It is vital that the configuration descriptor bConfigurationValue field // (byte 6) is 1 for the first configuration and increments by 1 for each // additional configuration defined here. This relationship is assumed in the // device stack for simplicity even though the USB 2.0 specification imposes // no such restriction on the bConfigurationValue values. // // Note that this structure is deliberately located in RAM since we need to // be able to patch some values in it based on client requirements. // //***************************************************************************** unsigned char g_pHIDDescriptor[] = { // // Configuration descriptor header. // 9, // Size of the configuration descriptor. USB_DTYPE_CONFIGURATION, // Type of this descriptor. USBShort(34), // The total size of this full structure. 1, // The number of interfaces in this // configuration. 1, // The unique value for this configuration. 5, // The string identifier that describes this // configuration. USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake up. 250, // The maximum power in 2mA increments. }; //***************************************************************************** // // The remainder of the configuration descriptor is stored in flash since we // don't need to modify anything in it at runtime. // //***************************************************************************** unsigned char g_pHIDInterface[] = { // // HID Device Class Interface Descriptor. // 9, // Size of the interface descriptor. USB_DTYPE_INTERFACE, // Type of this descriptor. 0, // The index for this interface. 0, // The alternate setting for this interface. 2, // The number of endpoints used by this // interface. USB_CLASS_HID, // The interface class 0, // The interface sub-class. 0, // The interface protocol for the sub-class // specified above. 4, // The string index for this interface. }; const unsigned char g_pHIDInEndpoint[] = { // // Interrupt IN endpoint descriptor // 7, // The size of the endpoint descriptor. USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. USB_EP_DESC_IN | USB_EP_TO_INDEX(INT_IN_ENDPOINT), USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. USBShort(INT_IN_EP_MAX_SIZE), // The maximum packet size. 16, // The polling interval for this endpoint. }; const unsigned char g_pHIDOutEndpoint[] = { // // Interrupt OUT endpoint descriptor // 7, // The size of the endpoint descriptor. USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. USB_EP_DESC_OUT | USB_EP_TO_INDEX(INT_OUT_ENDPOINT), USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. USBShort(INT_OUT_EP_MAX_SIZE), // The maximum packet size. 16, // The polling interval for this endpoint. }; //***************************************************************************** // // The HID configuration descriptor is defined as four or five sections // depending upon the client's configuration choice. These sections are: // // 1. The 9 byte configuration descriptor (RAM). // 2. The interface descriptor (RAM). // 3. The HID report and physical descriptors (provided by the client) // (FLASH). // 4. The mandatory interrupt IN endpoint descriptor (FLASH). // 5. The optional interrupt OUT endpoint descriptor (FLASH). // //***************************************************************************** const tConfigSection g_sHIDConfigSection = { sizeof(g_pHIDDescriptor), g_pHIDDescriptor }; const tConfigSection g_sHIDInterfaceSection = { sizeof(g_pHIDInterface), g_pHIDInterface }; const tConfigSection g_sHIDInEndpointSection = { sizeof(g_pHIDInEndpoint), g_pHIDInEndpoint }; const tConfigSection g_sHIDOutEndpointSection = { sizeof(g_pHIDOutEndpoint), g_pHIDOutEndpoint }; //***************************************************************************** // // Place holder for the user's HID descriptor block. // //***************************************************************************** tConfigSection g_sHIDDescriptorSection = { 0, (void *)0 }; //***************************************************************************** // // This array lists all the sections that must be concatenated to make a // single, complete HID configuration descriptor. // //***************************************************************************** const tConfigSection *g_psHIDSections[] = { &g_sHIDConfigSection, &g_sHIDInterfaceSection, &g_sHIDDescriptorSection, &g_sHIDInEndpointSection, &g_sHIDOutEndpointSection }; #define NUM_HID_SECTIONS (sizeof(g_psHIDSections) / \ sizeof(tConfigSection *)) //***************************************************************************** // // The header for the single configuration we support. This is the root of // the data structure that defines all the bits and pieces that are pulled // together to generate the configuration descriptor. Note that this must be // in RAM since we need to include or exclude the final section based on // client supplied initialization parameters. // //***************************************************************************** tConfigHeader g_sHIDConfigHeader = { NUM_HID_SECTIONS, g_psHIDSections }; //***************************************************************************** // // Configuration Descriptor. // //***************************************************************************** const tConfigHeader * const g_pHIDConfigDescriptors[] = { &g_sHIDConfigHeader }; //***************************************************************************** // // Forward references for device handler callbacks // //***************************************************************************** static void HandleGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest, unsigned int ulIndex); static void HandleRequest(void *pvInstance, tUSBRequest *pUSBRequest, unsigned int ulIndex); static void HandleConfigChange(void *pvInstance, unsigned int ulInfo, unsigned int ulIndex); static void HandleEP0DataReceived(void *pvInstance, unsigned int ulInfo, unsigned int ulIndex); static void HandleEP0DataSent(void *pvInstance, unsigned int ulInfo, unsigned int ulIndex); static void HandleReset(void *pvInstance); static void HandleSuspend(void *pvInstance); static void HandleResume(void *pvInstance); static void HandleDisconnect(void *pvInstance); static void HandleEndpoints(void *pvInstance, unsigned int ulStatus, unsigned int ulIndex); static void HandleDevice(void *pvInstance, unsigned int ulRequest, void *pvRequestData); //***************************************************************************** // // The device information structure for the USB HID devices. // //***************************************************************************** tDeviceInfo g_sHIDDeviceInfo = { // // Device event handler callbacks. // { HandleGetDescriptor, // GetDescriptor HandleRequest, // RequestHandler 0, // InterfaceChange HandleConfigChange, // ConfigChange HandleEP0DataReceived, // DataReceived HandleEP0DataSent, // DataSentCallback HandleReset, // ResetHandler HandleSuspend, // SuspendHandler HandleResume, // ResumeHandler HandleDisconnect, // DisconnectHandler HandleEndpoints, // EndpointHandler HandleDevice // Device handler. }, g_pHIDDeviceDescriptor, g_pHIDConfigDescriptors, 0, // Will be completed during USBDHIDInit(). 0, // Will be completed during USBDHIDInit(). &g_sUSBDefaultFIFOConfig }; //***************************************************************************** // // Set or clear deferred operation flags in an "atomic" manner. // // \param pusDeferredOp points to the flags variable which is to be modified. // \param usBit 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 unsigned short *pusDeferredOp, unsigned short usBit, tBoolean bSet) { #ifdef DISABLE_BIT_BAND if(bSet) { HWREG(pusDeferredOp) |= (1<ucNumInputReports > 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(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) { if(psDevice->psReportIdle[ulLoop].ucReportID == ucReportID) { break; } } } else { ulLoop = 0; } // // If we drop out of the loop with an index less than ucNumInputReports, // we found the relevant report so clear its timer. // if(ulLoop < psDevice->ucNumInputReports) { psDevice->psReportIdle[ulLoop].ulTimeSinceReportmS = 0; } } //***************************************************************************** // // This function is called to clear the idle period timers for each input // report supported by the device. // // \param psDevice points to the HID device structure whose timers are to be // cleared. // \param ulTimemS is the elapsed time in milliseconds since the last call // to this function. // // \return None. // //***************************************************************************** static void ClearIdleTimers(const tUSBDHIDDevice *psDevice) { unsigned int ulLoop; // // Clear the "time till next report" counters for each input report. // for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) { psDevice->psReportIdle[ulLoop].usTimeTillNextmS = psDevice->psReportIdle[ulLoop].ucDuration4mS * 4; } } //***************************************************************************** // // This function is called periodically to allow us to process the report idle // timers. // // \param psDevice points to the HID device structure whose timers are to be // updated. // \param ulElapsedmS indicates the number of milliseconds that have elapsed // since the last call to this function. // // \return None. // //***************************************************************************** static void ProcessIdleTimers(const tUSBDHIDDevice *psDevice, unsigned int ulElapsedmS) { unsigned int ulLoop; unsigned int ulSizeReport; void *pvReport; tHIDInstance *psInst; tBoolean bDeferred; // // Get our instance data pointer // psInst = ((tUSBDHIDDevice *)psDevice)->psPrivateHIDData; // // We have not had to defer any report transmissions yet. // bDeferred = false; // // Look at each of the input report idle timers in turn. // for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) { // // Update the time since the last report was sent. // psDevice->psReportIdle[ulLoop].ulTimeSinceReportmS += ulElapsedmS; // // Is this timer running? // if(psDevice->psReportIdle[ulLoop].ucDuration4mS) { // // Yes - is it about to expire? // if(psDevice->psReportIdle[ulLoop].usTimeTillNextmS <= ulElapsedmS) { // // The timer is about to expire. Can we send a report right // now? // if((psInst->eHIDTxState == HID_STATE_IDLE) && (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. // ulSizeReport = psDevice->pfnRxCallback( psDevice->pvRxCBData, USBD_HID_EVENT_IDLE_TIMEOUT, psDevice->psReportIdle[ulLoop].ucReportID, &pvReport); // // Schedule the report for transmission. // USBDHIDReportWrite((void *)psDevice, pvReport, ulSizeReport, true); // // Reload the timer for the next period. // psDevice->psReportIdle[ulLoop].usTimeTillNextmS = psDevice->psReportIdle[ulLoop].ucDuration4mS * 4; } else { // // We can't send the report straight away so flag it for // transmission as soon as the previous transmission ends. // psDevice->psReportIdle[ulLoop].usTimeTillNextmS = 0; bDeferred = true; } } else { // // The timer is not about to expire. Update the time till the // next report transmission. // psDevice->psReportIdle[ulLoop].usTimeTillNextmS -= ulElapsedmS; } } } // // If we had to defer transmission of any report, remember this so that we // will process it as soon as possible. // SetDeferredOpFlag(&psInst->usDeferredOpFlags, HID_DO_SEND_IDLE_REPORT, bDeferred); } static void SetIdleTimeout(const tUSBDHIDDevice *psDevice, unsigned char ucReportID, unsigned char ucTimeout4mS) { unsigned int ulLoop; tBoolean 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(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) { psIdle = &psDevice->psReportIdle[ulLoop]; // // 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(!ucReportID || (ucReportID == psIdle->ucReportID)) { // // Save the new duration for the idle timer. // psIdle->ucDuration4mS = ucTimeout4mS; // // Are we enabling the idle timer? If so, fix up the time until it // needs to fire. // if(ucTimeout4mS) { // // Determine what the timeout is for this report given the time // since the last report of this type was sent. // if(psIdle->ulTimeSinceReportmS >= ((unsigned int)ucTimeout4mS * 4)) { psIdle->usTimeTillNextmS = 0; bReportNeeded = true; } else { psIdle->usTimeTillNextmS = (((unsigned short)ucTimeout4mS * 4) - psIdle->ulTimeSinceReportmS); } } } } // // 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(psDevice, 0); } } //***************************************************************************** // // Find the idle timeout for a given HID input report. // // \param psDevice points to the HID device whose report idle timeout is to be // found. // \param ucReportID 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 unsigned int GetIdleTimeout(const tUSBDHIDDevice *psDevice, unsigned char ucReportID) { unsigned int ulLoop; tHIDReportIdle *psIdle; // // Search through all the input reports looking for ones that fit the // requirements. // for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) { psIdle = &psDevice->psReportIdle[ulLoop]; // // 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(!ucReportID || (ucReportID == psIdle->ucReportID)) { // // 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((unsigned int)psIdle->ucDuration4mS); } } // // If we drop out, the report couldn't 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 psDevice points to the HID device which is to be searched for the // required class descriptor. // \param ucType is the type of class descriptor being requested. This will // be either USB_HID_DTYPE_REPORT or USB_HID_DTYPE_PHYSICAL. // \param ulIndex 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 unsigned int FindHIDDescriptor(const tUSBDHIDDevice *psDevice, unsigned char ucType, unsigned int ulIndex, unsigned int *pulLen) { tBoolean bFoundType; unsigned int ulLoop; unsigned int ulCount; unsigned int ulLastFound; const tHIDClassDescriptorInfo *psDesc; // // Remember that we have not found any descriptor with a matching type yet. // bFoundType = false; ulCount = 0; ulLastFound = 0; // // Walk through all the class descriptors looking for the one which // matches the requested index and type. // for(ulLoop = 0; ulLoop < psDevice->psHIDDescriptor->bNumDescriptors; ulLoop++) { psDesc = &(psDevice->psHIDDescriptor->sClassDescriptor[ulLoop]); if(psDesc->bDescriptorType == ucType) { // // We found a descriptor of the correct type. Is this the // correct index? // bFoundType = true; // // Is this the descriptor we are looking for? // if(ulCount == ulIndex) { // // Yes - we found it so return the index and size to the // caller. // *pulLen = (unsigned int)psDesc->wDescriptorLength; return(ulLoop); } 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. // ulCount++; ulLastFound = ulLoop; } } } // // If we drop out, we didn't find the requested descriptor. Now handle // the special case of a physical descriptor - if we found any physical // descriptors, return the last one. // if((ucType == USB_HID_DTYPE_PHYSICAL) && bFoundType) { // // Get the length of the last descriptor we found. // psDesc = &(psDevice->psHIDDescriptor->sClassDescriptor[ulLastFound]); *pulLen = (unsigned int)psDesc->wDescriptorLength; // // Return the index to the caller. // return(ulLastFound); } else { // // We couldn't 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 psInst points to the 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 short packet or a // packet with no data to indicate the end of the transaction. // //***************************************************************************** static int ScheduleReportTransmission(tHIDInstance *psInst) { unsigned int ulNumBytes; unsigned char *pucData; int iRetcode; // // Set the number of bytes to send this iteration. // ulNumBytes = (unsigned int)(psInst->usInReportSize - psInst->usInReportIndex); // // Limit individual transfers to the maximum packet size for the endpoint. // if(ulNumBytes > INT_IN_EP_MAX_SIZE) { ulNumBytes = INT_IN_EP_MAX_SIZE; } // // Where are we sending this data from? // pucData = psInst->pucInReportData + psInst->usInReportIndex; // // Put the data in the correct FIFO. // iRetcode = USBEndpointDataPut(psInst->ulUSBBase, psInst->ucINEndpoint, pucData, ulNumBytes); if(iRetcode != -1) { // // Update the count and index ready for the next time round. // psInst->usInReportIndex += ulNumBytes; // // Send out the current data. // iRetcode = USBEndpointDataSend(psInst->ulUSBBase, psInst->ucINEndpoint, USB_TRANS_IN); } // // Tell the caller how we got on. // return(iRetcode); } //***************************************************************************** // // Receives notifications related to data received from the host. // // \param psDevice is the device instance whose endpoint is to be processed. // \param ulStatus 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 tBoolean ProcessDataFromHost(const tUSBDHIDDevice *psDevice, unsigned int ulStatus, unsigned int ulIndex) { unsigned int ulEPStatus; unsigned int ulSize; tHIDInstance *psInst; // // Get a pointer to our instance data. // psInst = psDevice->psPrivateHIDData; // // Get the endpoint status to see why we were called. // ulEPStatus = USBEndpointStatus(g_USBInstance[ulIndex].uiBaseAddr, psInst->ucOUTEndpoint); // // Clear the status bits. // USBDevEndpointStatusClear(g_USBInstance[ulIndex].uiBaseAddr, psInst->ucOUTEndpoint, ulEPStatus); // // Has a packet been received? // if(ulEPStatus & 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 doesn't read // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, // the event will be signaled later during tick processing. // SetDeferredOpFlag(&psInst->usDeferredOpFlags, HID_DO_PACKET_RX, true); // // How big is the packet we've just been sent? // ulSize = USBEndpointDataAvail(psInst->ulUSBBase, psInst->ucOUTEndpoint); // // 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. // psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_RX_AVAILABLE, ulSize, (void *)0); } else { // // No packet was received. Some error must have been reported. Check // and pass this on to the client if necessary. // if(ulEPStatus & USB_RX_ERROR_FLAGS) { // // This is an error we report to the client so... // psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_ERROR, (ulEPStatus & USB_RX_ERROR_FLAGS), (void *)0); } return (false); } return (true); } //***************************************************************************** // // Receives notifications related to data sent to the host. // // \param psDevice is the device instance whose endpoint is to be processed. // \param ulStatus 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 tBoolean ProcessDataToHost(const tUSBDHIDDevice *psDevice, unsigned int ulStatus, unsigned int ulIndex) { tHIDInstance *psInst; unsigned int ulEPStatus; // // Get a pointer to our instance data. // psInst = psDevice->psPrivateHIDData; // // Get the endpoint status to see why we were called. // ulEPStatus = USBEndpointStatus(psInst->ulUSBBase, psInst->ucINEndpoint); // // Clear the status bits. // USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucINEndpoint, ulEPStatus); // // 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 usInReportIndex has reached the usInReportSize value. // if(psInst->usInReportSize == psInst->usInReportIndex) { // // We finished sending the last report so are idle once again. // psInst->eHIDTxState = HID_STATE_IDLE; // // Notify the client that the report transmission completed. // psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, psInst->usInReportSize, (void *)0); // // Do we have any reports to send as a result of idle timer timeouts? // if(psInst->usDeferredOpFlags & (1 << HID_DO_SEND_IDLE_REPORT)) { // // Yes - send reports for any timers that expired recently. // ProcessIdleTimers(psDevice, 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 ulStatus. // //***************************************************************************** static void HandleEndpoints(void *pvInstance, unsigned int ulStatus, unsigned int ulIndex) { const tUSBDHIDDevice *psHIDInst; tHIDInstance *psInst; ASSERT(pvInstance != 0); // // Determine if the serial device is in single or composite mode because // the meaning of ulIndex is different in both cases. // psHIDInst = (const tUSBDHIDDevice *)pvInstance; psInst = psHIDInst->psPrivateHIDData; // // Handler for the interrupt OUT data endpoint. // if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) { // // Data is being sent to us from the host. // ProcessDataFromHost(pvInstance, ulStatus, ulIndex); } // // Handler for the interrupt IN data endpoint. // if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) { ProcessDataToHost(pvInstance, ulStatus, ulIndex); } } //***************************************************************************** // // Called by the USB stack whenever a configuration change occurs. // //***************************************************************************** static void HandleConfigChange(void *pvInstance, unsigned int ulInfo, unsigned int ulIndex) { tHIDInstance *psInst; const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Create the instance pointer. // psDevice = pvInstance; // // Get a pointer to our instance data. // psInst = psDevice->psPrivateHIDData; // // Set all our endpoints to idle state. // psInst->eHIDRxState = HID_STATE_IDLE; psInst->eHIDTxState = HID_STATE_IDLE; // // 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. // psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_CONNECTED, 0, (void *)0); } // // Clear the idle timers for each input report. // ClearIdleTimers(psDevice); // // Remember that we are connected. // psInst->bConnected = true; } //***************************************************************************** // // Device instance specific handler. // //***************************************************************************** static void HandleDevice(void *pvInstance, unsigned int ulRequest, void *pvRequestData) { tHIDInstance *psInst; unsigned char *pucData; // // Create the serial instance data. // psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; // // Create the char array used by the events supported by the USB CDC // serial class. // pucData = (unsigned char *)pvRequestData; switch(ulRequest) { // // This was an interface change event. // case USB_EVENT_COMP_IFACE_CHANGE: { psInst->ucInterface = pucData[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(pucData[0] & USB_EP_DESC_IN) { psInst->ucINEndpoint = INDEX_TO_USB_EP((pucData[1] & 0x7f)); } else { // // Extract the new endpoint number. // psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); } break; } default: { break; } } } //***************************************************************************** // // This function is called by the USB device stack whenever the device is // disconnected from the host. // //***************************************************************************** static void HandleDisconnect(void *pvInstance) { const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Create the instance pointer. // psDevice = (const tUSBDHIDDevice *)pvInstance; // // If we are not currently connected so let the client know we are open // for business. // if(psDevice->psPrivateHIDData->bConnected) { // // Pass the disconnected event to the client. // psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_DISCONNECTED, 0, (void *)0); } // // Remember that we are no longer connected. // psDevice->psPrivateHIDData->bConnected = false; } //***************************************************************************** // // This function is called by the USB device stack whenever a request for a // non-standard descriptor is received. // // \param pvInstance is the instance data for this request. // \param pUSBRequest 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 *pvInstance, tUSBRequest *pUSBRequest, unsigned int ulIndex) { unsigned int ulSize; unsigned int ulDesc; const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Which device are we dealing with? // psDevice = pvInstance; // // Which type of class descriptor are we being asked for? // switch(pUSBRequest->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. // ulSize = 0; ulDesc = FindHIDDescriptor(psDevice, pUSBRequest->wValue >> 8, pUSBRequest->wValue & 0xFF, &ulSize); // // Did we find the descriptor? // if(ulDesc == HID_NOT_FOUND) { // // No - stall the endpoint and return. // USBDCDStallEP0(ulIndex); return; } // // If there is more data to send than the host requested then just // send the requested amount of data. // if(ulSize > pUSBRequest->wLength) { ulSize = pUSBRequest->wLength; } // // Send the data via endpoint 0. // USBDCDSendDataEP0(0, (unsigned char *)psDevice->ppClassDescriptors[ulDesc], ulSize); 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? // ulSize = (unsigned int)psDevice->psHIDDescriptor->bLength; // // If there is more data to send than the host requested then just // send the requested amount of data. // if(ulSize > pUSBRequest->wLength) { ulSize = pUSBRequest->wLength; } // // Send the data via endpoint 0. // USBDCDSendDataEP0(0, (unsigned char *)psDevice->psHIDDescriptor, ulSize); break; } // // This was an unknown request so stall. // default: { USBDCDStallEP0(ulIndex); break; } } } //***************************************************************************** // // This function is called by the USB device stack whenever a non-standard // request is received. // // \param pvInstance is the instance data for this HID device. // \param pUSBRequest 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 *pvInstance, tUSBRequest *pUSBRequest, unsigned int ulIndex) { tHIDInstance *psInst; unsigned char ucProtocol; const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Which device are we dealing with? // psDevice = pvInstance; // // Get a pointer to our instance data. // psInst = psDevice->psPrivateHIDData; // // Make sure the request was for this interface. // if(pUSBRequest->wIndex != psInst->ucInterface) { return; } // // Determine the type of request. // switch(pUSBRequest->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->usOutReportSize = pUSBRequest->wLength; psInst->pucOutReportData = (unsigned char *)psDevice->pfnRxCallback( psDevice->pvRxCBData, USBD_HID_EVENT_GET_REPORT_BUFFER, pUSBRequest->wValue, (void *)(unsigned int)(pUSBRequest->wLength)); // // Did the client provide us a buffer? // if(!psInst->pucOutReportData) { // // The application couldn't provide us a buffer so stall the // request. // USBDCDStallEP0(ulIndex); } 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->eHIDRxState = HID_STATE_WAIT_DATA; // // Now read the payload of the request. We handle the actual // operation in the data callback once this data is received. // USBDCDRequestDataEP0(0, psInst->pucOutReportData, (unsigned int)pUSBRequest->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. // USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); } break; } // // A Get Report request is used by the host to poll a device for its // current state. // case USBREQ_GET_REPORT: { unsigned int ulSize; unsigned char *pucReport; // // Get the latest report from the application. // ulSize = psDevice->pfnRxCallback(psDevice->pvRxCBData, USBD_HID_EVENT_GET_REPORT, pUSBRequest->wValue, &pucReport); // // Need to ACK the data on end point 0 in this case. // USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); // // ..then send back the requested report. // psInst->bGetRequestPending = true; USBDCDSendDataEP0(0, pucReport, ulSize); 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(psDevice, pUSBRequest->wValue & 0xFF, (pUSBRequest->wValue >> 8) & 0xFF); // // Need to ACK the data on end point 0 in this case. // USBDevEndpointDataAck(psInst->ulUSBBase, 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: { unsigned int ulTimeout; // // Determine the timeout for the requested report. // ulTimeout = GetIdleTimeout(psDevice, pUSBRequest->wValue); if(ulTimeout != HID_NOT_FOUND) { // // Need to ACK the data on end point 0 in this case. // USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); // // Send our response to the host. // USBDCDSendDataEP0(0, (unsigned char *)&ulTimeout, 1); } else { // // The report ID was not found so stall the endpoint. // USBDCDStallEP0(ulIndex); } 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(psDevice->ucSubclass == USB_HID_SCLASS_BOOT) { // // We need to ACK the data on end point 0 in this case. // USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); // // We are a boot subclass device so pass this on to the // application. // psDevice->pfnRxCallback(psDevice->pvRxCBData, USBD_HID_EVENT_SET_PROTOCOL, pUSBRequest->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(ulIndex); } 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(psDevice->ucSubclass == USB_HID_SCLASS_BOOT) { // // We need to ACK the data on end point 0 in this case. // USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); // // We are a boot subclass device so pass this on to the // application callback to get the answer. // ucProtocol = (unsigned char)psDevice->pfnRxCallback( psDevice->pvRxCBData, USBD_HID_EVENT_GET_PROTOCOL, 0, (void *)0); // // Send our response to the host. // USBDCDSendDataEP0(0, (unsigned char *)&ucProtocol, 1); } else { // // This is not a boot subclass device so stall the endpoint to // show that we don't support this request. // USBDCDStallEP0(ulIndex); } break; } // // This request was not recognized so stall. // default: { USBDCDStallEP0(ulIndex); break; } } } //***************************************************************************** // // This function is called by the USB device stack whenever the data requested // on endpoint zero is received. // //***************************************************************************** static void HandleEP0DataReceived(void *pvInstance, unsigned int ulDataSize, unsigned int ulIndex) { tHIDInstance *psInst; const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Which device are we dealing with? // psDevice = pvInstance; // // If we were not passed any data, just return. // if(ulDataSize == 0) { return; } // // Get our instance data pointer. // psInst = psDevice->psPrivateHIDData; // // Make sure we are actually expecting something. // if(psInst->eHIDRxState != HID_STATE_WAIT_DATA) { return; } // // Change the endpoint state back to idle now that we have been passed // the data we were waiting for. // psInst->eHIDRxState = HID_STATE_IDLE; // // 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. // psDevice->pfnRxCallback(psDevice->pvRxCBData, USBD_HID_EVENT_SET_REPORT, psInst->usOutReportSize, psInst->pucOutReportData); } //***************************************************************************** // // 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 *pvInstance, unsigned int ulInfo, unsigned int ulIndex) { tHIDInstance *psInst; const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Which device are we dealing with? // psDevice = pvInstance; // // Get our instance data pointer. // psInst = psDevice->psPrivateHIDData; // // 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; psDevice->pfnRxCallback(psDevice->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 *pvInstance) { ASSERT(pvInstance != 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(pvInstance); } //***************************************************************************** // // This function is called by the USB device stack whenever the bus is put into // suspend state. // //***************************************************************************** static void HandleSuspend(void *pvInstance) { const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Create the instance pointer. // psDevice = (const tUSBDHIDDevice *)pvInstance; // // Pass the event on to the client. // psDevice->pfnRxCallback(psDevice->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 *pvInstance) { const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Create the instance pointer. // psDevice = (const tUSBDHIDDevice *)pvInstance; // // Pass the event on to the client. // psDevice->pfnRxCallback(psDevice->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 pvInstance is the instance data for this request. // \param ulTimemS is the elapsed time in milliseconds since the last call // to this function. // // \return None. // //***************************************************************************** static void HIDTickHandler(void *pvInstance, unsigned int ulTimemS, unsigned int ulIndex) { tHIDInstance *psInst; unsigned int ulSize; const tUSBDHIDDevice *psDevice; ASSERT(pvInstance != 0); // // Create the instance pointer. // psDevice = (const tUSBDHIDDevice *)pvInstance; // // Get our instance data pointer. // psInst = psDevice->psPrivateHIDData; // // If we are connected, process our idle timers. // if(psInst->bConnected) { ProcessIdleTimers(psDevice, ulTimemS); } // // Do we have a deferred receive waiting // if(psInst->usDeferredOpFlags & (1 << HID_DO_PACKET_RX)) { // // Yes - how big is the waiting packet? // ulSize = USBEndpointDataAvail(g_USBInstance[ulIndex].uiBaseAddr, psInst->ucOUTEndpoint); // // Tell the client that there is a packet waiting for it. // psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_RX_AVAILABLE, ulSize, (void *)0); } return; } //***************************************************************************** // //! Initializes HID device operation for a given USB controller. //! //! \param ulIndex is the index of the USB controller which is to be //! initialized for HID device operation. //! \param psDevice 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 psDevice 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 64 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 //! 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 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, 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 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 psDevice pointer on success. // //***************************************************************************** void * USBDHIDInit(unsigned int ulIndex, const tUSBDHIDDevice *psDevice) { // // Check parameter validity. // ASSERT(ulIndex == 0); ASSERT(psDevice); ASSERT(psDevice->ppStringDescriptors); ASSERT(psDevice->psPrivateHIDData); ASSERT(psDevice->pfnRxCallback); ASSERT(psDevice->pfnTxCallback); ASSERT(psDevice->ppClassDescriptors); ASSERT(psDevice->psHIDDescriptor); ASSERT((psDevice->ucNumInputReports == 0) || psDevice->psReportIdle); USBDHIDCompositeInit(ulIndex, psDevice); // // All is well so now pass the descriptors to the lower layer and put // the HID device on the bus. // USBDCDInit(ulIndex, psDevice->psPrivateHIDData->psDevInfo); // // Return the pointer to the instance indicating that everything went well. // return((void *)psDevice); } //***************************************************************************** // //! Initializes HID device operation for a given USB controller. //! //! This function is used for initializing an instance related information of the //! HID device. //! //! \param ulIndex is the index of the USB controller which is to be //! initialized for HID device operation. //! \param psDevice points to a structure containing parameters customizing //! the operation of the HID device. //! //! \return Returns NULL on failure or the \e psDevice pointer on success. // //***************************************************************************** void * USBDHIDCompositeInit(unsigned int ulIndex, const tUSBDHIDDevice *psDevice) { tHIDInstance *psInst; tDeviceDescriptor *psDevDesc; tInterfaceDescriptor *psDevIf; // // Check parameter validity. // ASSERT(ulIndex == 0); ASSERT(psDevice); ASSERT(psDevice->ppStringDescriptors); ASSERT(psDevice->psPrivateHIDData); ASSERT(psDevice->pfnRxCallback); ASSERT(psDevice->pfnTxCallback); ASSERT(psDevice->ppClassDescriptors); ASSERT(psDevice->psHIDDescriptor); ASSERT((psDevice->ucNumInputReports == 0) || psDevice->psReportIdle); if(ulIndex == 0) { g_USBInstance[ulIndex].uiUSBInstance = ulIndex; g_USBInstance[ulIndex].uiBaseAddr = USB0_BASE; g_USBInstance[ulIndex].uiSubBaseAddr = USB_0_OTGBASE; g_USBInstance[ulIndex].uiInterruptNum = SYS_INT_USB0; g_USBInstance[ulIndex].uiSubInterruptNum = SYS_INT_USBSSINT; g_USBInstance[ulIndex].uiPHYConfigRegAddr = CFGCHIP2_USBPHYCTRL; } #if (USB_NUM_INSTANCE == 2) else if(ulIndex == 1) { g_USBInstance[ulIndex].uiUSBInstance = ulIndex; g_USBInstance[ulIndex].uiBaseAddr = USB1_BASE; g_USBInstance[ulIndex].uiSubBaseAddr = USB_1_OTGBASE; g_USBInstance[ulIndex].uiInterruptNum = SYS_INT_USB1; g_USBInstance[ulIndex].uiSubInterruptNum = SYS_INT_USBSSINT; g_USBInstance[ulIndex].uiPHYConfigRegAddr = CFGCHIP2_USB1PHYCTRL; } #endif // // Initialize the workspace in the passed instance structure. // psInst = psDevice->psPrivateHIDData; psInst->psConfDescriptor = (tConfigDescriptor *)g_pHIDDescriptor; psInst->psDevInfo = &g_sHIDDeviceInfo; psInst->ulUSBBase = g_USBInstance[ulIndex].uiBaseAddr; psInst->eHIDRxState = HID_STATE_UNCONFIGURED; psInst->eHIDTxState = HID_STATE_UNCONFIGURED; psInst->usDeferredOpFlags = 0; psInst->bConnected = false; psInst->bGetRequestPending = false; psInst->bSendInProgress = false; psInst->usInReportIndex = 0; psInst->usInReportSize = 0; psInst->pucInReportData = (unsigned char *)0; psInst->usOutReportSize = 0; psInst->pucOutReportData = (unsigned char *)0; // // Set the default endpoint and interface assignments. // psInst->ucINEndpoint = INT_IN_ENDPOINT; psInst->ucOUTEndpoint = INT_OUT_ENDPOINT; psInst->ucInterface = 0; // // Fix up the device descriptor with the client-supplied values. // psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; psDevDesc->idVendor = psDevice->usVID; psDevDesc->idProduct = psDevice->usPID; // // Fix up the configuration descriptor with client-supplied values. // psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; psInst->psConfDescriptor->bMaxPower = (unsigned char)(psDevice->usMaxPowermA / 2); // // Slot the client's HID descriptor into our standard configuration // descriptor. // g_sHIDDescriptorSection.ucSize = psDevice->psHIDDescriptor->bLength; g_sHIDDescriptorSection.pucData = (unsigned char *)psDevice->psHIDDescriptor; // // Fix up the interface and endpoint descriptors depending upon client // choices. // psDevIf = (tInterfaceDescriptor *)g_pHIDInterface; psDevIf->bNumEndpoints = psDevice->bUseOutEndpoint ? 2 : 1; psDevIf->bInterfaceSubClass = psDevice->ucSubclass; psDevIf->bInterfaceProtocol = psDevice->ucProtocol; // // If necessary, remove the interrupt OUT endpoint from the configuration // descriptor. // if(psDevice->bUseOutEndpoint == false) { g_sHIDConfigHeader.ucNumSections = (NUM_HID_SECTIONS - 1); } else { g_sHIDConfigHeader.ucNumSections = NUM_HID_SECTIONS; } // // Plug in the client's string table to the device information // structure. // psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; psInst->psDevInfo->ulNumStringDescriptors = psDevice->ulNumStringDescriptors; psInst->psDevInfo->pvInstance = (void *)psDevice; // // Initialize the input report idle timers if any input reports exist. // ClearIdleTimers(psDevice); // // Register our tick handler (this must be done after USBDCDInit). // InternalUSBRegisterTickHandler(USB_TICK_HANDLER_DEVICE, HIDTickHandler, (void *)psDevice); // // Return the pointer to the instance indicating that everything went well. // return((void *)psDevice); } //***************************************************************************** // //! Shuts down the HID device. //! //! \param pvInstance 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 pvInstance instance should not me used in any //! other calls. //! //! \return None. // //***************************************************************************** void USBDHIDTerm(void *pvInstance) { tHIDInstance *psInst; int index; ASSERT(pvInstance); // // Get a pointer to our instance data. // psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; // // Terminate the requested instance. // USB_BASE_TO_INDEX(psInst->ulUSBBase, index); USBDCDTerm(index); psInst->ulUSBBase = 0; psInst->psDevInfo = (tDeviceInfo *)0; psInst->psConfDescriptor = (tConfigDescriptor *)0; } //***************************************************************************** // //! Sets the client-specific pointer parameter for the receive channel //! callback. //! //! \param pvInstance 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 pvInstance 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 *pvInstance, void *pvCBData) { void *pvOldValue; ASSERT(pvInstance); // // Set the callback data for the receive channel after remembering the // previous value. // pvOldValue = ((tUSBDHIDDevice *)pvInstance)->pvRxCBData; ((tUSBDHIDDevice *)pvInstance)->pvRxCBData = pvCBData; // // Return the previous callback data value. // return (pvOldValue); } //***************************************************************************** // //! Sets the client-specific data pointer for the transmit callback. //! //! \param pvInstance 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 pvInstance 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 *pvInstance, void *pvCBData) { void *pvOldValue; ASSERT(pvInstance); // // Set the callback data for the transmit channel after remembering the // previous value. // pvOldValue = ((tUSBDHIDDevice *)pvInstance)->pvTxCBData; ((tUSBDHIDDevice *)pvInstance)->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 pvInstance is the pointer to the device instance structure as //! returned by USBDHIDInit(). //! \param pcData points to the first byte of data which is to be transmitted. //! \param ulLength 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 pucData 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. // //***************************************************************************** unsigned int USBDHIDReportWrite(void *pvInstance, unsigned char *pcData, unsigned int ulLength, tBoolean bLast) { tHIDInstance *psInst; int iRetcode; ASSERT(pvInstance); // // Get our instance data pointer // psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; // // 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->eHIDTxState != HID_STATE_IDLE) { // // 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(ulLength) { ClearReportTimer(pvInstance, *pcData); } // // Keep track of the whereabouts of the report so that we can send it in // multiple packets if necessary. // psInst->pucInReportData = pcData; psInst->usInReportIndex = 0; psInst->usInReportSize = ulLength; // // Schedule transmission of the first packet of the report. // psInst->eHIDTxState = HID_STATE_WAIT_DATA; iRetcode = 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(iRetcode != -1) { // // No - tell the caller we sent all the bytes provided. // return (ulLength); } else { // // Yes - tell the caller we couldn't send the data. // return (0); } } //***************************************************************************** // //! Reads a packet of data received from the USB host via the interrupt OUT //! endpoint (if in use). //! //! \param pvInstance is the pointer to the device instance structure as //! returned by USBDHIDInit(). //! \param pcData points to a buffer into which the received data will be //! written. //! \param ulLength is the size of the buffer pointed to by pcData. //! \param bLast indicates whether the client will make a further call to //! read additional data from the packet. //! //! This function reads up to ulLength 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. // //***************************************************************************** unsigned int USBDHIDPacketRead(void *pvInstance, unsigned char *pcData, unsigned int ulLength, tBoolean bLast) { unsigned int ulEPStatus, ulPkt; unsigned int ulCount; tHIDInstance *psInst; int iRetcode; ASSERT(pvInstance); // // Get our instance data pointer // psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; // // Does the relevant endpoint FIFO have a packet waiting for us? // ulEPStatus = USBEndpointStatus(psInst->ulUSBBase, psInst->ucOUTEndpoint); if(ulEPStatus & USB_DEV_RX_PKT_RDY) { // // How many bytes are available for us to receive? // ulPkt = USBEndpointDataAvail(psInst->ulUSBBase, psInst->ucOUTEndpoint); // // Get as much data as we can. // ulCount = ulLength; iRetcode = USBEndpointDataGet(psInst->ulUSBBase, psInst->ucOUTEndpoint, pcData, &ulCount); // // Did we read the last of the packet data? // if(ulCount == ulPkt) { // // Clear the endpoint status so that we know no packet is // waiting. // USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucOUTEndpoint, ulEPStatus); // // Acknowledge the data, thus freeing the host to send the // next packet. // USBDevEndpointDataAck(psInst->ulUSBBase, psInst->ucOUTEndpoint, true); // // Clear the flag we set to indicate that a packet read is // pending. // SetDeferredOpFlag(&psInst->usDeferredOpFlags, HID_DO_PACKET_RX, false); } // // If all went well, tell the caller how many bytes they got. // if(iRetcode != -1) { return (ulCount); } } // // 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 pvInstance 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 (64) 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 64 //! if no transmission is currently in progress. // //***************************************************************************** unsigned int USBDHIDTxPacketAvailable(void *pvInstance) { tHIDInstance *psInst; ASSERT(pvInstance); // // Get our instance data pointer. // psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; // // Do we have a packet transmission currently ongoing? // if(psInst->eHIDTxState != HID_STATE_IDLE) { // // 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 (INT_IN_EP_MAX_SIZE); } } //***************************************************************************** // //! Determines whether a packet is available and, if so, the size of the //! buffer required to read it. //! //! \param pvInstance 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. // //***************************************************************************** unsigned int USBDHIDRxPacketAvailable(void *pvInstance) { unsigned int ulEPStatus; unsigned int ulSize; tHIDInstance *psInst; ASSERT(pvInstance); // // Get our instance data pointer // psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; // // Does the relevant endpoint FIFO have a packet waiting for us? // ulEPStatus = USBEndpointStatus(psInst->ulUSBBase, psInst->ucOUTEndpoint); if(ulEPStatus & USB_DEV_RX_PKT_RDY) { // // Yes - a packet is waiting. How big is it? // ulSize = USBEndpointDataAvail(psInst->ulUSBBase, psInst->ucOUTEndpoint); return (ulSize); } else { // // There is no packet waiting to be received. // return (0); } } //***************************************************************************** // //! Reports the device power status (bus- or self-powered) to the USB library. //! //! \param pvInstance is the pointer to the HID device instance structure. //! \param ucPower 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 *pvInstance, unsigned char ucPower) { ASSERT(pvInstance); // // Pass the request through to the lower layer. // USBDCDPowerStatusSet(0, ucPower); } //***************************************************************************** // //! Requests a remote wake up to resume communication when in suspended state. //! //! \param pvInstance 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. // //***************************************************************************** tBoolean USBDHIDRemoteWakeupRequest(void *pvInstance) { ASSERT(pvInstance); // // Pass the request through to the lower layer. // return(USBDCDRemoteWakeupRequest(0)); } //***************************************************************************** // // Close the Doxygen group. //! @} // //*****************************************************************************