at91work/at91lib/usb/device/composite/HIDDFunctionDriver.c

467 lines
17 KiB
C

/* ----------------------------------------------------------------------------
* 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