simtrace2/sam3s_example/atmel_softpack_libraries/usb/device/hid-transfer/HIDDFunction.c

696 lines
24 KiB
C

/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2010, 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.
* ----------------------------------------------------------------------------
*/
/** \file
* Implementation of the HIDDFunction class methods.
*/
/** \addtogroup usbd_hid
* @{
*/
/*------------------------------------------------------------------------------
* Headers
*------------------------------------------------------------------------------*/
#include <HIDDFunction.h>
#include <USBDescriptors.h>
#include <HIDDescriptors.h>
#include <USBLib_Trace.h>
/*------------------------------------------------------------------------------
* Definitions
*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
* Macros
*------------------------------------------------------------------------------*/
/**
* Get byte pointer
*/
#define _PU8(v) ((uint8_t*)&(v))
/**
* Get word from un-aligned value
*/
#define _Word(a) (_PU8(a)[0] + (_PU8(a)[1] << 8))
/*------------------------------------------------------------------------------
* Types
*------------------------------------------------------------------------------*/
/** Parse data extention for descriptor parsing */
typedef struct _HIDDParseData {
HIDDFunction * pHidd;
USBInterfaceDescriptor * pIfDesc;
} HIDDParseData;
/** Parse data extension for HID descriptor */
/*------------------------------------------------------------------------------
* Internal variables
*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
* Internal functions
*------------------------------------------------------------------------------*/
/**
* Returns the descriptor requested by the host.
* \param pHidd Pointer to HIDDFunction instance
* \param bType Descriptor type.
* \param wLength Maximum number of bytes to send.
* \return USBRC_SUCCESS if the request has been handled by this function,
* otherwise USBRC_PARAM_ERR.
*/
static uint32_t HIDDFunction_GetDescriptor(HIDDFunction *pHidd,
uint8_t bType,
uint32_t wLength)
{
HIDDescriptor1 *pHidDescriptor = (HIDDescriptor1 *)pHidd->pHidDescriptor;
uint16_t wDescriptorLength;
TRACE_INFO_WP("gDesc{%x) ", bType);
switch (bType) {
case HIDGenericDescriptor_REPORT:
/* Adjust length and send report descriptor */
/*
wDescriptorLength = pHidDescriptor->bDescriptorLength0[0]
+ pHidDescriptor->bDescriptorLength0[1];
*/
wDescriptorLength = _Word(pHidDescriptor->wDescriptorLength0);
if (wLength > wDescriptorLength)
wLength = wDescriptorLength;
TRACE_INFO_WP("Report(%d) ", wLength);
USBD_Write(0, pHidd->pReportDescriptor, wLength, 0, 0);
break;
case HIDGenericDescriptor_HID:
/* Adjust length and send HID descriptor */
if (wLength > sizeof(HIDDescriptor1))
wLength = sizeof(HIDDescriptor1);
TRACE_INFO_WP("HID(%d) ", wLength);
USBD_Write(0, pHidDescriptor, wLength, 0, 0);
break;
default:
return USBRC_PARAM_ERR;
}
return USBRC_SUCCESS;
}
/**
* Return expected report header pointer.
* \param pHidd Pointer to HIDDFunction instance
* \param bType Report type.
* \param bID Report ID.
*/
static HIDDReport* HIDDFunction_FindReport(const HIDDFunction *pHidd,
uint8_t bType,
uint8_t bID)
{
HIDDReport** pReportList;
int32_t listSize, i;
switch(bType) {
case HIDReportRequest_INPUT:
pReportList = pHidd->pInputList;
listSize = pHidd->bInputListSize;
break;
case HIDReportRequest_OUTPUT:
pReportList = pHidd->pOutputList;
listSize = pHidd->bOutputListSize;
break;
/* No other reports supported */
default:
TRACE_INFO("Report %x.%x not support\n\r", bType, bID);
return 0;
}
/* No list */
if (pReportList == 0)
return 0;
/* Find report in the list */
for (i = 0; i < listSize; i ++) {
if (bID == pReportList[i]->bID)
return pReportList[i];
}
/* Not found */
return 0;
}
/**
* Sends the current Idle rate of the input report to the host.
* \param pHidd Pointer to HIDDFunction instance
* \param bID Report ID
*/
static void HIDDFunction_GetIdle(HIDDFunction *pHidd,
uint8_t bID)
{
HIDDReport *pReport = HIDDFunction_FindReport(pHidd,
HIDReportRequest_INPUT,
bID);
TRACE_INFO_WP("gIdle(%x) ", bID);
if (pReport == 0) {
USBD_Stall(0);
return;
}
USBD_Write(0, &pReport->bIdleRate, 1, 0, 0);
}
/**
* Retrieves the new idle rate of the input report from the USB host.
* \param pHidd Pointer to HIDDFunction instance
* \param bType Report type
* \param bID Report ID
* \param bIdleRate Report idle rate.
*/
static void HIDDFunction_SetIdle(HIDDFunction *pHidd,
uint8_t bID,
uint8_t bIdleRate)
{
HIDDReport *pReport = HIDDFunction_FindReport(pHidd,
HIDReportRequest_INPUT,
bID);
TRACE_INFO_WP("sIdle(%x<%x) ", bID, bIdleRate);
if (pReport == 0) {
USBD_Stall(0);
return;
}
USBD_Write(0, 0, 0, 0, 0);
}
/**
* Callback function when GetReport request data sent to host
* \param pReport Pointer to report information.
* \param status Result status
* \param transferred Number of bytes transferred
* \param remaining Number of bytes that are not transferred yet
*/
static void _GetReportCallback(HIDDReport *pReport,
uint8_t status,
uint32_t transferred,
uint32_t remaining)
{
pReport->wTransferred = transferred;
if (pReport->fCallback)
pReport->fCallback(HIDD_EC_GETREPORT, pReport->pArg);
USBD_Read(0, 0, 0, 0, 0);
}
/**
* Sends the requested report to the host.
* \param pHidd Pointer to HIDDFunction instance
* \param bType Report type.
* \param bID Report ID.
* \param wLength Maximum number of bytes to send.
*/
static void HIDDFunction_GetReport(HIDDFunction *pHidd,
uint8_t bType,
uint8_t bID,
uint8_t wLength)
{
HIDDReport *pReport = HIDDFunction_FindReport(pHidd,
bType,
bID);
TRACE_INFO_WP("gReport(%x.%x) ", bType, bID);
if (pReport == 0) {
USBD_Stall(0);
return;
}
if (wLength >= pReport->wMaxSize) {
wLength = pReport->wMaxSize;
}
USBD_Write(0, pReport->bData, wLength,
(TransferCallback)_GetReportCallback, pReport);
}
/**
* Callback function when GetReport request data sent to host
* \param pReport Pointer to report information.
* \param status Result status
* \param transferred Number of bytes transferred
* \param remaining Number of bytes that are not transferred yet
*/
static void _SetReportCallback(HIDDReport *pReport,
uint8_t status,
uint32_t transferred,
uint32_t remaining)
{
pReport->wTransferred = transferred;
if (pReport->fCallback) {
pReport->fCallback(HIDD_EC_SETREPORT, pReport->pArg);
}
}
/**
* Reads the requested report from the host.
* \param pHidd Pointer to HIDDFunction instance
* \param bType Report type.
* \param bID Report ID.
* \param wLength Maximum number of bytes to read.
*/
static void HIDDFunction_SetReport(HIDDFunction *pHidd,
uint8_t bType,
uint8_t bID,
uint8_t wLength)
{
HIDDReport *pReport = HIDDFunction_FindReport(pHidd,
bType,
bID);
TRACE_INFO_WP("sReport(%x.%x) ", bType, bID);
if (pReport == 0) {
USBD_Stall(0);
return;
}
if (wLength >= pReport->wMaxSize) {
wLength = pReport->wMaxSize;
}
USBD_Read(0, pReport->bData, wLength,
(TransferCallback)_SetReportCallback, pReport);
}
/**
* Parse descriptors: Interface, Interrupt IN/OUT.
* \param desc Pointer to descriptor list.
* \param arg Argument, pointer to HIDDParseData instance.
*/
static uint32_t HIDDFunction_Parse(USBGenericDescriptor * pDesc,
HIDDParseData * pArg)
{
/* Find HID Interface */
if (pArg->pIfDesc == 0) {
if (pDesc->bDescriptorType == USBGenericDescriptor_INTERFACE) {
USBInterfaceDescriptor *pIf = (USBInterfaceDescriptor*)pDesc;
/* Right interface for HID:
HID Class + at least 1 endpoint */
if (pIf->bInterfaceClass == HIDInterfaceDescriptor_CLASS
&& pIf->bNumEndpoints >= 1) {
/* Obtain new interface setting */
if (pArg->pHidd->bInterface == 0xFF) {
pArg->pHidd->bInterface = pIf->bInterfaceNumber;
pArg->pIfDesc = pIf;
}
/* Find specific interface setting */
else if (pArg->pHidd->bInterface == pIf->bInterfaceNumber) {
pArg->pIfDesc = pIf;
}
}
}
}
/* Interface end */
else {
/* Start another interface ? */
if (pDesc->bDescriptorType == USBGenericDescriptor_INTERFACE) {
/* Terminate the parse */
return USBRC_PARTIAL_DONE;
}
/* Parse HID descriptor */
else if (pDesc->bDescriptorType == HIDGenericDescriptor_HID) {
pArg->pHidd->pHidDescriptor = (HIDDescriptor*)pDesc;
}
/* Parse endpoints */
else if (pDesc->bDescriptorType == USBGenericDescriptor_ENDPOINT) {
USBEndpointDescriptor *pEp = (USBEndpointDescriptor*)pDesc;
if (pEp->bEndpointAddress & 0x80)
pArg->pHidd->bPipeIN = pEp->bEndpointAddress & 0x7F;
else
pArg->pHidd->bPipeOUT = pEp->bEndpointAddress;
}
/* Check if all data is OK */
if (pArg->pHidd->bInterface != 0xFF
&& pArg->pHidd->bPipeIN != 0xFF
&& pArg->pHidd->bPipeOUT != 0xFF)
return USBRC_FINISHED;
}
return 0;
}
/**
* Callback function when interrupt OUT data received from host
* \param pHidd Pointer to HIDDFunction instance
* \param status Result status
* \param transferred Number of bytes transferred
* \param remaining Number of bytes that are not transferred yet
*/
static void HIDDFunction_ReportReceived(HIDDFunction *pHidd,
uint8_t status,
uint32_t transferred,
uint32_t remaining)
{
HIDDReport *pOut = pHidd->pOutputList[pHidd->bCurrOutput];
if (status != USBRC_SUCCESS) {
TRACE_ERROR("HIDDFun::ReadReport: %x\n\r", status);
return;
}
/* Transfered information */
pOut->wTransferred = transferred;
/* Data Change callback */
if (pOut->fCallback)
pOut->fCallback(HIDD_EC_REPORTCHANGED, pOut->pArg);
/* Proceed to next output report */
pHidd->bCurrOutput ++;
if (pHidd->bCurrOutput >= pHidd->bOutputListSize)
pHidd->bCurrOutput = 0;
/* Start reading a report */
USBD_Read(pHidd->bPipeOUT,
pHidd->pOutputList[pHidd->bCurrOutput]->bData,
pHidd->pOutputList[pHidd->bCurrOutput]->wMaxSize,
(TransferCallback)HIDDFunction_ReportReceived,
(void*)pHidd);
}
/**
* Callback function when interrupt IN data sent to host
* \param pHidd Pointer to HIDDFunction instance
* \param status Result status
* \param transferred Number of bytes transferred
* \param remaining Number of bytes that are not transferred yet
*/
static void HIDDFunction_ReportSent(HIDDFunction *pHidd,
uint8_t status,
uint32_t transferred,
uint32_t remaining)
{
HIDDReport *pIn = pHidd->pInputList[pHidd->bCurrInput];
if (status != USBRC_SUCCESS) {
TRACE_ERROR("HIDDFun::WriteReport: %x\n\r", status);
return;
}
/* Transfered information */
pIn->wTransferred = transferred;
/* Report Sent Callback */
if (pIn->fCallback)
pIn->fCallback(HIDD_EC_REPORTSENT, pIn->pArg);
/* Proceed to next output report */
pHidd->bCurrInput ++;
if (pHidd->bCurrInput >= pHidd->bInputListSize)
pHidd->bCurrInput = 0;
/* Start writing a report */
USBD_Write(pHidd->bPipeIN,
pHidd->pInputList[pHidd->bCurrInput]->bData,
pHidd->pInputList[pHidd->bCurrInput]->wMaxSize,
(TransferCallback)HIDDFunction_ReportReceived,
(void*)pHidd);
}
/*------------------------------------------------------------------------------
* Exported functions
*------------------------------------------------------------------------------*/
/**
* Initialize the USB Device HID function, for general HID device support.
* \param pHidd Pointer to HIDDFunction instance.
* \param pUsbd Pointer to USBDDriver instance.
* \param bInterfaceNb Interface number,
* can be 0xFF to obtain from descriptors.
* \param pReportDescriptor Pointer to report descriptor.
* \param pInputList Pointer to an HID input report list
* \param bInputListSize HID input report list size
* \param pOutputList Pointer to an HID output report list
* \param bOutputListSize HID output report list size
*/
void HIDDFunction_Initialize(HIDDFunction * pHidd,
USBDDriver * pUsbd, uint8_t bInterfaceNb,
const uint8_t * pReportDescriptor,
HIDDReport* pInputList[], uint8_t bInputListSize,
HIDDReport* pOutputList[], uint8_t bOutputListSize)
{
TRACE_INFO("HIDDFunction_Initialize\n\r");
pHidd->pUsbd = pUsbd;
pHidd->pReportDescriptor = (uint8_t *)pReportDescriptor;
pHidd->pHidDescriptor = 0;
pHidd->bInterface = bInterfaceNb;
pHidd->bPipeIN = 0xFF;
pHidd->bPipeOUT = 0xFF;
pHidd->bProtocol = HIDProtocol_REPORT; /* Non-boot protocol */
pHidd->pInputList = pInputList;
pHidd->pOutputList = pOutputList;
pHidd->bInputListSize = bInputListSize;
pHidd->bOutputListSize = bOutputListSize;
pHidd->bCurrInput = 0;
pHidd->bCurrOutput = 0;
}
/**
* Parse the USB HID Function Interface.
* Only first interface and its endpoints parsed.
* \param pHidd Pointer to HIDDFunction instance.
* \param pDescriptors Pointer to descriptor list.
* \param dwLength Descriptor list block length in bytes.
* \return Pointer to next descriptor. 0 means no other descriptor.
*/
USBGenericDescriptor *HIDDFunction_ParseInterface(HIDDFunction * pHidd,
USBGenericDescriptor * pDescriptors,
uint32_t dwLength)
{
HIDDParseData data;
pHidd->bPipeIN = 0xFF;
pHidd->bPipeOUT = 0xFF;
data.pHidd = pHidd;
data.pIfDesc = 0;
return USBGenericDescriptor_Parse(pDescriptors,
dwLength,
(USBDescriptorParseFunction)HIDDFunction_Parse,
(void*)&data);
}
/**
* Start polling interrupt OUT pipe
* (output report, host to device) if there is.
* \param pHidd Pointer to HIDDFunction instance.
*/
uint32_t HIDDFunction_StartPollingOutputs(HIDDFunction * pHidd)
{
/* No report, do nothing */
if (pHidd->bOutputListSize == 0
|| pHidd->pOutputList == 0)
return USBRC_PARAM_ERR;
/* Start reading a report */
return USBD_Read(pHidd->bPipeOUT,
pHidd->pOutputList[pHidd->bCurrOutput]->bData,
pHidd->pOutputList[pHidd->bCurrOutput]->wMaxSize,
(TransferCallback)HIDDFunction_ReportReceived,
(void*)pHidd);
}
/**
* Start sending reports via interrupt IN pipe
* (input report, device to host) if there is.
* \param pHidd Pointer to HIDDFunction instance.
*/
uint32_t HIDDFunction_StartSendingInputs(HIDDFunction * pHidd)
{
/* No report, do nothing */
if (pHidd->bInputListSize == 0
|| pHidd->pInputList == 0)
return USBRC_PARAM_ERR;
/* Start sending a report */
return USBD_Write(pHidd->bPipeIN,
pHidd->pInputList[pHidd->bCurrInput]->bData,
pHidd->pInputList[pHidd->bCurrInput]->wMaxSize,
(TransferCallback)HIDDFunction_ReportSent,
(void*)pHidd);
}
/**
* Handles HID-specific SETUP request sent by the host.
* \param pHidd Pointer to HIDDFunction instance.
* \param request Pointer to a USBGenericRequest instance
*/
uint32_t HIDDFunction_RequestHandler(HIDDFunction *pHidd,
const USBGenericRequest *request)
{
uint32_t reqCode = (request->bmRequestType << 8)
| (request->bRequest);
switch (reqCode) {
/* Get_Descriptor */
case USBGenericRequest_GETDESCRIPTOR|(0x81<<8):
return HIDDFunction_GetDescriptor(
pHidd,
USBGetDescriptorRequest_GetDescriptorType(request),
USBGenericRequest_GetLength(request));
/* Clear_Feature (EP) */
case USBGenericRequest_CLEARFEATURE|(0x02<<8):
if (USBFeatureRequest_GetFeatureSelector(request)
== USBFeatureRequest_ENDPOINTHALT) {
uint8_t ep = USBGenericRequest_GetEndpointNumber(request);
if (USBD_IsHalted(ep)) {
/* Unhalt EP */
USBD_Unhalt(ep);
/* Restart Polling OUT */
if (ep == pHidd->bPipeOUT) {
HIDDFunction_StartPollingOutputs(pHidd);
}
/* and send a zero-length packet */
USBD_Write(0, 0, 0, 0, 0);
}
break; /* Handled success */
}
return USBRC_PARAM_ERR;
/* Set_Descriptor */
case USBGenericRequest_SETDESCRIPTOR|(0x01<<8):
/* Optional, not implemented */
USBD_Stall(0);
break;
/* Get_Idle */
case (0xa1<<8)|HIDGenericRequest_GETIDLE:
HIDDFunction_GetIdle(pHidd,
HIDReportRequest_GetReportId(request));
break;
/* Set_Idle */
case (0x21<<8)|HIDGenericRequest_SETIDLE:
HIDDFunction_SetIdle(pHidd,
HIDReportRequest_GetReportId(request),
HIDIdleRequest_GetIdleRate(request));
break;
/* Get_Report */
case (0xa1<<8)|HIDGenericRequest_GETREPORT:
HIDDFunction_GetReport(pHidd,
HIDReportRequest_GetReportType(request),
HIDReportRequest_GetReportId(request),
USBGenericRequest_GetLength(request));
break;
/* Set_Report */
case (0x21<<8)|HIDGenericRequest_SETREPORT:
HIDDFunction_SetReport(pHidd,
HIDReportRequest_GetReportType(request),
HIDReportRequest_GetReportId(request),
USBGenericRequest_GetLength(request));
break;
/* Get_Protocol */
case (0xa1<<8)|HIDGenericRequest_SETPROTOCOL:
pHidd->bProtocol = request->wValue;
USBD_Write(0, 0, 0, 0, 0);
break;
/* Set_Protocol */
case (0x21<<8)|HIDGenericRequest_GETPROTOCOL:
USBD_Write(0, &pHidd->bProtocol, 1, 0, 0);
break;
default:
return USBRC_PARAM_ERR;
}
return USBRC_SUCCESS;
}
/**
* Read raw data through USB interrupt OUT EP.
* \param pHidd Pointer to HIDDFunction instance.
* \param pData Pointer to the data buffer.
* \param dwLength The data length.
* \param fCallback Callback function invoked when transferring done.
* \param pArg Pointer to additional arguments.
*/
uint32_t HIDDFunction_Read(const HIDDFunction *pHidd,
void* pData,
uint32_t dwLength,
TransferCallback fCallback,
void* pArg)
{
return USBD_Read(pHidd->bPipeIN,
pData, dwLength,
fCallback, pArg);
}
/**
* Write raw data through USB interrupt IN EP.
* \param pHidd Pointer to HIDDFunction instance.
* \param pData Pointer to the data sent.
* \param dwLength The data length.
* \param fCallback Callback function invoked when transferring done.
* \param pArg Pointer to additional arguments.
*/
uint32_t HIDDFunction_Write(const HIDDFunction *pHidd,
void* pData,
uint32_t dwLength,
TransferCallback fCallback,
void* pArg)
{
return USBD_Write(pHidd->bPipeIN,
pData, dwLength,
fCallback, pArg);
}
/**
* Initialize a report.
* \param pReport Pointer to HIDDReport instance.
* \param wSize Size of the report data.
* \param bID Report ID.
* \param fCallback Callback function for report events.
* \param pArg Pointer to event handler arguments.
*/
void HIDDFunction_InitializeReport(HIDDReport* pReport,
uint16_t wSize,
uint8_t bID,
HIDDReportEventCallback fCallback,
void* pArg)
{
pReport->wMaxSize = wSize;
pReport->wTransferred = 0;
pReport->bIdleRate = 0;
pReport->bDelay = 0;
pReport->bID = bID;
pReport->fCallback = fCallback;
pReport->pArg = pArg;
}
/**@}*/