/* ---------------------------------------------------------------------------- * ATMEL Microcontroller Software Support * ---------------------------------------------------------------------------- * Copyright (c) 2008, Atmel Corporation * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the disclaimer below. * * Atmel's name may not be used to endorse or promote products derived from * this software without specific prior written permission. * * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ---------------------------------------------------------------------------- */ #if defined(usb_CDCHID) || defined(usb_HIDAUDIO) || defined(usb_HIDMSD) //----------------------------------------------------------------------------- // Headers //----------------------------------------------------------------------------- // GENERAL #include #include // USB #include #include #include // HID #include #include #include #include #include #include #include #include #include #include #include #include "HIDDFunctionDriver.h" #include "HIDDFunctionDriverDescriptors.h" //----------------------------------------------------------------------------- // Internal types //----------------------------------------------------------------------------- /// Driver structure for an HID device implementing keyboard functionalities. typedef struct { /// Pointer to USB device driver instance USBDDriver * pUsbdDriver; /// Idle rate (in milliseconds) of the input report unsigned char inputReportIdleRate; /// Input report instance. HIDDKeyboardInputReport inputReport; /// Output report instance. HIDDKeyboardOutputReport outputReport; } HIDDKeyboardDriver; //----------------------------------------------------------------------------- // Internal variables //----------------------------------------------------------------------------- /// Static instance of the HID keyboard device driver. static HIDDKeyboardDriver hiddKeyboardDriver; //----------------------------------------------------------------------------- // Internal functions //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// Returns the descriptor requested by the host. /// \param type Descriptor type. /// \param length Maximum number of bytes to send. /// \return 1 if the request has been handled by this function, otherwise 0. //----------------------------------------------------------------------------- static unsigned char HIDD_GetDescriptor(unsigned char type, unsigned char length) { const USBConfigurationDescriptor *pConfiguration; HIDDescriptor *hidDescriptor; switch (type) { case HIDGenericDescriptor_REPORT: TRACE_INFO_WP("Report "); // Adjust length and send report descriptor if (length > HIDD_Descriptors_REPORTSIZE) { length = HIDD_Descriptors_REPORTSIZE; } USBD_Write(0, &hiddReportDescriptor, length, 0, 0); break; case HIDGenericDescriptor_HID: TRACE_INFO_WP("HID "); // Configuration descriptor is different depending on speed if (USBD_IsHighSpeed()) { pConfiguration = hiddKeyboardDriver .pUsbdDriver->pDescriptors->pHsConfiguration; } else { pConfiguration = hiddKeyboardDriver .pUsbdDriver->pDescriptors->pFsConfiguration; } // Parse the device configuration to get the HID descriptor USBConfigurationDescriptor_Parse(pConfiguration, 0, 0, (USBGenericDescriptor **) &hidDescriptor); // Adjust length and send HID descriptor if (length > sizeof(HIDDescriptor)) { length = sizeof(HIDDescriptor); } USBD_Write(0, hidDescriptor, length, 0, 0); break; default: return 0; } return 1; } //----------------------------------------------------------------------------- /// Sends the current Idle rate of the input report to the host. //----------------------------------------------------------------------------- static void HIDD_GetIdle() { TRACE_INFO_WP("gIdle "); USBD_Write(0, &(hiddKeyboardDriver.inputReportIdleRate), 1, 0, 0); } //----------------------------------------------------------------------------- /// Retrieves the new idle rate of the input report from the USB host. /// \param idleRate New input report idle rate. //----------------------------------------------------------------------------- static void HIDD_SetIdle(unsigned char idleRate) { TRACE_INFO_WP("sIdle(%d) ", idleRate); hiddKeyboardDriver.inputReportIdleRate = idleRate; USBD_Write(0, 0, 0, 0, 0); } //----------------------------------------------------------------------------- /// Sends the requested report to the host. /// \param type Report type. /// \param length Maximum number of bytes to send. //----------------------------------------------------------------------------- static void HIDD_GetReport(unsigned char type, unsigned short length) { TRACE_INFO_WP("gReport "); // Check report type switch (type) { case HIDReportRequest_INPUT: TRACE_INFO_WP("In "); // Adjust size and send report if (length > sizeof(HIDDKeyboardInputReport)) { length = sizeof(HIDDKeyboardInputReport); } USBD_Write(0, // Endpoint #0 &(hiddKeyboardDriver.inputReport), length, 0, // No callback 0); break; case HIDReportRequest_OUTPUT: TRACE_INFO_WP("Out "); // Adjust size and send report if (length > sizeof(HIDDKeyboardOutputReport)) { length = sizeof(HIDDKeyboardOutputReport); } USBD_Write(0, // Endpoint #0 &(hiddKeyboardDriver.outputReport), length, 0, // No callback 0); break; default: USBD_Stall(0); } } //----------------------------------------------------------------------------- /// Callback invoked when an output report has been received from the host. /// Forward the new status of the LEDs to the user program via the //----------------------------------------------------------------------------- static void HIDD_ReportReceived() { TRACE_INFO_WP("oReport "); // Trigger callback HIDDKeyboardCallbacks_LedsChanged( HIDDKeyboardOutputReport_GetNumLockStatus( &(hiddKeyboardDriver.outputReport)), HIDDKeyboardOutputReport_GetCapsLockStatus( &(hiddKeyboardDriver.outputReport)), HIDDKeyboardOutputReport_GetScrollLockStatus( &(hiddKeyboardDriver.outputReport))); // Restart transfer USBD_Read(HIDD_Descriptors_INTERRUPTOUT, &(hiddKeyboardDriver.outputReport), sizeof(HIDDKeyboardOutputReport), (TransferCallback) HIDD_ReportReceived, 0); // No argument for callback function } //----------------------------------------------------------------------------- /// Retrieves the new value of a report from the host and saves it. /// \param type Report type. /// \param length Report length. //----------------------------------------------------------------------------- static void HIDD_SetReport(unsigned char type, unsigned short length) { TRACE_INFO_WP("sReport "); // Check report type switch (type) { case HIDReportRequest_INPUT: // SET_REPORT requests on input reports are ignored USBD_Stall(0); break; case HIDReportRequest_OUTPUT: // Check report length if (length != sizeof(HIDDKeyboardOutputReport)) { USBD_Stall(0); } else { USBD_Read(0, // Endpoint #0 &(hiddKeyboardDriver.outputReport), length, (TransferCallback) HIDD_ReportReceived, 0); // No argument to the callback function } break; default: USBD_Stall(0); } } //----------------------------------------------------------------------------- // Exported functions //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// Initializes an USB HID keyboard function driver. /// \param pUsbdDriver Pointer to the USB driver instance. //----------------------------------------------------------------------------- void HIDDFunctionDriver_Initialize(USBDDriver * pUsbdDriver) { hiddKeyboardDriver.inputReportIdleRate = 0; HIDDKeyboardInputReport_Initialize(&(hiddKeyboardDriver.inputReport)); HIDDKeyboardOutputReport_Initialize(&(hiddKeyboardDriver.outputReport)); hiddKeyboardDriver.pUsbdDriver = pUsbdDriver; } //----------------------------------------------------------------------------- /// Handles HID-specific SETUP request sent by the host. /// \param request Pointer to a USBGenericRequest instance. /// \return 0 if the request is Unsupported, 1 if the request handled. //----------------------------------------------------------------------------- unsigned char HIDDFunctionDriver_RequestHandler( const USBGenericRequest *request) { TRACE_INFO_WP("NewReq "); // Check if this is a standard request if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { // This is a standard request switch (USBGenericRequest_GetRequest(request)) { case USBGenericRequest_GETDESCRIPTOR: // Check if this is a HID descriptor, otherwise forward it to // the standard driver if (!HIDD_GetDescriptor( USBGetDescriptorRequest_GetDescriptorType(request), USBGenericRequest_GetLength(request))) { USBDDriver_RequestHandler(hiddKeyboardDriver.pUsbdDriver, request); } break; default: return 0; } } // Check if this is a class request else if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { // This is a class-specific request switch (USBGenericRequest_GetRequest(request)) { case HIDGenericRequest_GETIDLE: HIDD_GetIdle(); break; case HIDGenericRequest_SETIDLE: HIDD_SetIdle(HIDIdleRequest_GetIdleRate(request)); break; case HIDGenericRequest_GETREPORT: HIDD_GetReport( HIDReportRequest_GetReportType(request), USBGenericRequest_GetLength(request)); break; case HIDGenericRequest_SETREPORT: HIDD_SetReport( HIDReportRequest_GetReportType(request), USBGenericRequest_GetLength(request)); break; default: return 0; } } return 1; } //----------------------------------------------------------------------------- /// Invoked whenever the configuration of the device is changed by the host. /// \param cfgnum Newly configuration number. //----------------------------------------------------------------------------- void HIDDFunctionCallbacks_ConfigurationChanged(unsigned char cfgnum) { if (cfgnum > 0) { // Start receiving output reports USBD_Read(HIDD_Descriptors_INTERRUPTOUT, &(hiddKeyboardDriver.outputReport), sizeof(HIDDKeyboardOutputReport), (TransferCallback) HIDD_ReportReceived, 0); // No argument for callback function } } //----------------------------------------------------------------------------- /// Reports a change in which keys are currently pressed or release to the /// host. /// \param pressedKeys Pointer to an array of key codes indicating keys that /// have been pressed since the last call to /// . /// \param pressedKeysSize Number of key codes in the pressedKeys array. /// \param releasedKeys Pointer to an array of key codes indicates keys that /// have been released since the last call to /// . /// \param releasedKeysSize Number of key codes in the releasedKeys array. /// \return if the report has been sent to the host; /// otherwise an error code. //----------------------------------------------------------------------------- unsigned char HIDDKeyboardDriver_ChangeKeys(unsigned char *pressedKeys, unsigned char pressedKeysSize, unsigned char *releasedKeys, unsigned char releasedKeysSize) { // Press keys while (pressedKeysSize > 0) { // Check if this is a standard or modifier key if (HIDKeypad_IsModifierKey(*pressedKeys)) { // Set the corresponding bit in the input report HIDDKeyboardInputReport_PressModifierKey( &(hiddKeyboardDriver.inputReport), *pressedKeys); } else { HIDDKeyboardInputReport_PressStandardKey( &(hiddKeyboardDriver.inputReport), *pressedKeys); } pressedKeysSize--; pressedKeys++; } // Release keys while (releasedKeysSize > 0) { // Check if this is a standard or modifier key if (HIDKeypad_IsModifierKey(*releasedKeys)) { // Set the corresponding bit in the input report HIDDKeyboardInputReport_ReleaseModifierKey( &(hiddKeyboardDriver.inputReport), *releasedKeys); } else { HIDDKeyboardInputReport_ReleaseStandardKey( &(hiddKeyboardDriver.inputReport), *releasedKeys); } releasedKeysSize--; releasedKeys++; } // Send input report through the interrupt IN endpoint return USBD_Write(HIDD_Descriptors_INTERRUPTIN, &(hiddKeyboardDriver.inputReport), sizeof(HIDDKeyboardInputReport), 0, 0); } //----------------------------------------------------------------------------- /// Starts a remote wake-up sequence if the host has explicitely enabled it /// by sending the appropriate SET_FEATURE request. //----------------------------------------------------------------------------- void HIDDKeyboardDriver_RemoteWakeUp(void) { // Remote wake-up has been enabled if (USBDDriver_IsRemoteWakeUpEnabled(hiddKeyboardDriver.pUsbdDriver)) { USBD_RemoteWakeUp(); } } #endif