summaryrefslogtreecommitdiff
path: root/at91lib/usb/device/composite/HIDDFunctionDriver.c
diff options
context:
space:
mode:
Diffstat (limited to 'at91lib/usb/device/composite/HIDDFunctionDriver.c')
-rw-r--r--at91lib/usb/device/composite/HIDDFunctionDriver.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/at91lib/usb/device/composite/HIDDFunctionDriver.c b/at91lib/usb/device/composite/HIDDFunctionDriver.c
new file mode 100644
index 0000000..1ba89a8
--- /dev/null
+++ b/at91lib/usb/device/composite/HIDDFunctionDriver.c
@@ -0,0 +1,466 @@
+/* ----------------------------------------------------------------------------
+ * 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 <utility/trace.h>
+#include <utility/assert.h>
+// USB
+#include <usb/device/core/USBD.h>
+#include <usb/common/core/USBGetDescriptorRequest.h>
+#include <usb/device/core/USBDDriver.h>
+// HID
+#include <usb/device/hid-keyboard/HIDDKeyboardDriver.h>
+#include <usb/device/hid-keyboard/HIDDKeyboardDriverDescriptors.h>
+#include <usb/device/hid-keyboard/HIDDKeyboardCallbacks.h>
+#include <usb/device/hid-keyboard/HIDDKeyboardInputReport.h>
+#include <usb/device/hid-keyboard/HIDDKeyboardOutputReport.h>
+#include <usb/common/hid/HIDGenericDescriptor.h>
+#include <usb/common/hid/HIDDescriptor.h>
+#include <usb/common/hid/HIDGenericRequest.h>
+#include <usb/common/hid/HIDReportRequest.h>
+#include <usb/common/hid/HIDIdleRequest.h>
+#include <usb/common/hid/HIDKeypad.h>
+
+#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
+/// <HIDDKeyboardDriver_ChangeKeys>.
+/// \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
+/// <HIDDKeyboardDriver_ChangeKeys>.
+/// \param releasedKeysSize Number of key codes in the releasedKeys array.
+/// \return <USBD_STATUS_SUCCESS> 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
+
personal git repositories of Harald Welte. Your mileage may vary