mirror of
https://gitea.osmocom.org/sim-card/simtrace2.git
synced 2026-03-16 21:28:33 +03:00
In the standard atmel lib only one configuration was possible. On a GETDESCRIPTOR request the board would always return the full buffer with both configurations. The USB driver requests each configuration one after another, using the configuration index number. The atmel lib did not support more than one USB configuration.
482 lines
16 KiB
C
482 lines
16 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.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/** \file
|
|
* \addtogroup usbd_hid_tran
|
|
*@{
|
|
*/
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Headers
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "HIDDTransferDriver.h"
|
|
#include <USBLib_Trace.h>
|
|
|
|
#include <USBRequests.h>
|
|
#include <HIDDescriptors.h>
|
|
#include <HIDDFunction.h>
|
|
|
|
#include <USBD_HAL.h>
|
|
|
|
#include <string.h>
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Internal types
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Report struct for HID transfer.
|
|
*/
|
|
typedef struct _HIDDTransferReport {
|
|
/** Callback when report done */
|
|
HIDDReportEventCallback fCallback;
|
|
/** Callback arguments */
|
|
void* pArg;
|
|
|
|
/** Report size (ID + DATA) */
|
|
uint16_t wMaxSize;
|
|
/** Transfered size */
|
|
uint16_t wTransferred;
|
|
/** Report idle rate */
|
|
uint8_t bIdleRate;
|
|
/** Delay count for Idle */
|
|
uint8_t bDelay;
|
|
/** Report ID */
|
|
uint8_t bID;
|
|
/** Report data address */
|
|
uint8_t bData[HIDDTransferDriver_REPORTSIZE];
|
|
} HIDDTransferReport;
|
|
|
|
/**
|
|
* Driver structure for an HID device implementing simple transfer
|
|
* functionalities.
|
|
*/
|
|
typedef struct _HIDDTransferDriver {
|
|
|
|
/** Standard HID function interface. */
|
|
HIDDFunction hidFunction;
|
|
|
|
/** HID Input report list */
|
|
HIDDReport *inputReports[1];
|
|
/** HID Output report list */
|
|
HIDDReport *outputReports[1];
|
|
|
|
/* OUT Report - block input for SET_REPORT */
|
|
/**< Output report block size */
|
|
uint16_t iReportLen;
|
|
/**< Output report data buffer */
|
|
uint8_t iReportBuf[HIDDTransferDriver_REPORTSIZE];
|
|
|
|
} HIDDTransferDriver;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Internal variables
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
/** Input report buffers */
|
|
static HIDDTransferReport inputReport;
|
|
|
|
/** Output report buffers */
|
|
static HIDDTransferReport outputReport;
|
|
|
|
/** Static instance of the HID Transfer device driver. */
|
|
static HIDDTransferDriver hiddTransferDriver;
|
|
|
|
/** Report descriptor used by the driver. */
|
|
static const uint8_t hiddTransferReportDescriptor[] = {
|
|
|
|
/* Global Usage Page */
|
|
HIDReport_GLOBAL_USAGEPAGE + 2, 0xFF, 0xFF, /* Vendor-defined */
|
|
|
|
/* Collection: Application */
|
|
HIDReport_LOCAL_USAGE + 1, 0xFF, /* Vendor-defined */
|
|
HIDReport_COLLECTION + 1, HIDReport_COLLECTION_APPLICATION,
|
|
|
|
/* Input report: Vendor-defined */
|
|
HIDReport_LOCAL_USAGE + 1, 0xFF, /* Vendor-defined usage */
|
|
HIDReport_GLOBAL_REPORTCOUNT + 1, HIDDTransferDriver_REPORTSIZE,
|
|
HIDReport_GLOBAL_REPORTSIZE + 1, 8,
|
|
HIDReport_GLOBAL_LOGICALMINIMUM + 1, (uint8_t) -128,
|
|
HIDReport_GLOBAL_LOGICALMAXIMUM + 1, (uint8_t) 127,
|
|
HIDReport_INPUT + 1, 0, /* No Modifiers */
|
|
|
|
/* Output report: vendor-defined */
|
|
HIDReport_LOCAL_USAGE + 1, 0xFF, /* Vendor-defined usage */
|
|
HIDReport_GLOBAL_REPORTCOUNT + 1, HIDDTransferDriver_REPORTSIZE,
|
|
HIDReport_GLOBAL_REPORTSIZE + 1, 8,
|
|
HIDReport_GLOBAL_LOGICALMINIMUM + 1, (uint8_t) -128,
|
|
HIDReport_GLOBAL_LOGICALMAXIMUM + 1, (uint8_t) 127,
|
|
HIDReport_OUTPUT + 1, 0, /* No Modifiers */
|
|
HIDReport_ENDCOLLECTION
|
|
};
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* 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 uint8_t HIDDTransferDriver_GetDescriptor(uint8_t type,
|
|
uint8_t length)
|
|
{
|
|
HIDDTransferDriver *pDrv = &hiddTransferDriver;
|
|
HIDDFunction *pHidd = &pDrv->hidFunction;
|
|
|
|
const USBConfigurationDescriptor *pConfiguration;
|
|
HIDDescriptor *hidDescriptors[2];
|
|
|
|
switch (type) {
|
|
|
|
case HIDGenericDescriptor_REPORT:
|
|
TRACE_INFO("Report ");
|
|
|
|
/* Adjust length and send report descriptor */
|
|
if (length > HIDDTransferDriver_REPORTDESCRIPTORSIZE) {
|
|
|
|
length = HIDDTransferDriver_REPORTDESCRIPTORSIZE;
|
|
}
|
|
USBD_Write(0, &hiddTransferReportDescriptor, length, 0, 0);
|
|
break;
|
|
|
|
case HIDGenericDescriptor_HID:
|
|
TRACE_INFO("HID ");
|
|
|
|
/* Configuration descriptor is different depending on configuration */
|
|
if (USBD_IsHighSpeed()) {
|
|
|
|
pConfiguration =
|
|
pHidd->pUsbd->pDescriptors->pHsConfiguration;
|
|
}
|
|
else {
|
|
|
|
pConfiguration =
|
|
pHidd->pUsbd->pDescriptors->pFsConfiguration[0];
|
|
}
|
|
|
|
/* Parse the device configuration to get the HID descriptor */
|
|
USBConfigurationDescriptor_Parse(pConfiguration, 0, 0,
|
|
(USBGenericDescriptor **) hidDescriptors);
|
|
|
|
/* Adjust length and send HID descriptor */
|
|
if (length > sizeof(HIDDescriptor)) {
|
|
|
|
length = sizeof(HIDDescriptor);
|
|
}
|
|
USBD_Write(0, hidDescriptors[0], length, 0, 0);
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Callback function when SetReport request data received from host
|
|
* \param pArg Pointer to additional argument struct
|
|
* \param status Result status
|
|
* \param transferred Number of bytes transferred
|
|
* \param remaining Number of bytes that are not transferred yet
|
|
*/
|
|
static void HIDDTransferDriver_ReportReceived(void *pArg,
|
|
uint8_t status,
|
|
uint32_t transferred,
|
|
uint32_t remaining)
|
|
{
|
|
HIDDTransferDriver *pDrv = &hiddTransferDriver;
|
|
pDrv->iReportLen = transferred;
|
|
USBD_Write(0, 0, 0, 0, 0);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Exported functions
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Initializes the HID Transfer %device driver.
|
|
* \param pDescriptors Pointer to USBDDriverDescriptors instance.
|
|
*/
|
|
void HIDDTransferDriver_Initialize(const USBDDriverDescriptors * pDescriptors)
|
|
{
|
|
HIDDTransferDriver * pDrv = &hiddTransferDriver;
|
|
USBDDriver *pUsbd = USBD_GetDriver();
|
|
|
|
/* One input report */
|
|
pDrv->inputReports[0] = (HIDDReport*)&inputReport;
|
|
HIDDFunction_InitializeReport((HIDDReport *)pDrv->inputReports[0],
|
|
HIDDTransferDriver_REPORTSIZE,
|
|
0,
|
|
0, 0);
|
|
/* One output report */
|
|
pDrv->outputReports[0] = (HIDDReport*)&outputReport;
|
|
HIDDFunction_InitializeReport((HIDDReport *)pDrv->outputReports[0],
|
|
HIDDTransferDriver_REPORTSIZE,
|
|
0,
|
|
0, 0);
|
|
|
|
/* Initialize USBD Driver instance */
|
|
USBDDriver_Initialize(pUsbd,
|
|
pDescriptors,
|
|
0); /* Multiple interface settings not supported */
|
|
/* Function instance initialize */
|
|
HIDDFunction_Initialize(&pDrv->hidFunction,
|
|
pUsbd, 0,
|
|
hiddTransferReportDescriptor,
|
|
(HIDDReport **)(&pDrv->inputReports), 1,
|
|
(HIDDReport **)(&pDrv->outputReports), 1);
|
|
/* Initialize USBD */
|
|
USBD_Init();
|
|
}
|
|
|
|
/**
|
|
* Handles configureation changed event.
|
|
* \param cfgnum New configuration number
|
|
*/
|
|
void HIDDTransferDriver_ConfigurationChangedHandler(uint8_t cfgnum)
|
|
{
|
|
const USBDDriverDescriptors * pDescriptors = USBD_GetDriver()->pDescriptors;
|
|
HIDDTransferDriver * pDrv = &hiddTransferDriver;
|
|
HIDDFunction * pHidd = &pDrv->hidFunction;
|
|
|
|
USBConfigurationDescriptor *pDesc;
|
|
|
|
if (cfgnum > 0) {
|
|
|
|
/* Parse endpoints for reports */
|
|
if (USBD_HAL_IsHighSpeed() && pDescriptors->pHsConfiguration)
|
|
pDesc = (USBConfigurationDescriptor*)pDescriptors->pHsConfiguration;
|
|
else
|
|
pDesc = (USBConfigurationDescriptor*)pDescriptors->pFsConfiguration[0];
|
|
HIDDFunction_ParseInterface(pHidd,
|
|
(USBGenericDescriptor*)pDesc,
|
|
pDesc->wTotalLength);
|
|
|
|
/* Start polling for Output Reports */
|
|
HIDDFunction_StartPollingOutputs(pHidd);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles HID-specific SETUP request sent by the host.
|
|
* \param request Pointer to a USBGenericRequest instance
|
|
*/
|
|
void HIDDTransferDriver_RequestHandler(const USBGenericRequest *request)
|
|
{
|
|
HIDDTransferDriver *pDrv = &hiddTransferDriver;
|
|
HIDDFunction *pHidd = &pDrv->hidFunction;
|
|
|
|
TRACE_INFO("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 (!HIDDTransferDriver_GetDescriptor(
|
|
USBGetDescriptorRequest_GetDescriptorType(request),
|
|
USBGenericRequest_GetLength(request))) {
|
|
|
|
USBDDriver_RequestHandler(pHidd->pUsbd,
|
|
request);
|
|
}
|
|
return; /* Handled, no need to do others */
|
|
|
|
case USBGenericRequest_CLEARFEATURE:
|
|
|
|
/* Check which is the requested feature */
|
|
switch (USBFeatureRequest_GetFeatureSelector(request)) {
|
|
case USBFeatureRequest_ENDPOINTHALT:
|
|
{ uint8_t ep =
|
|
USBGenericRequest_GetEndpointNumber(request);
|
|
if (USBD_IsHalted(ep)) {
|
|
/* Unhalt endpoint restart OUT EP
|
|
*/
|
|
USBD_Unhalt(ep);
|
|
if (ep == pHidd->bPipeOUT) {
|
|
HIDDFunction_StartPollingOutputs(pHidd);
|
|
}
|
|
}
|
|
/* and send a zero-length packet */
|
|
USBD_Write(0, 0, 0, 0, 0);
|
|
return; /* Handled, no need to do others */
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
/* We use different buffer for SetReport */
|
|
else if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) {
|
|
|
|
switch (USBGenericRequest_GetRequest(request)) {
|
|
|
|
case HIDGenericRequest_SETREPORT:
|
|
{
|
|
uint16_t length = USBGenericRequest_GetLength(request);
|
|
uint8_t type = HIDReportRequest_GetReportType(request);
|
|
if (type == HIDReportRequest_OUTPUT) {
|
|
if (length > HIDDTransferDriver_REPORTSIZE)
|
|
length = HIDDTransferDriver_REPORTSIZE;
|
|
USBD_Read(0,
|
|
pDrv->iReportBuf,
|
|
length,
|
|
HIDDTransferDriver_ReportReceived,
|
|
0); /* No argument to the callback function */
|
|
}
|
|
else {
|
|
|
|
USBD_Stall(0);
|
|
}
|
|
}
|
|
return; /* Handled, no need do others */
|
|
}
|
|
}
|
|
|
|
|
|
/* Process HID requests */
|
|
if (USBRC_SUCCESS == HIDDFunction_RequestHandler(pHidd,
|
|
request)) {
|
|
return;
|
|
}
|
|
else
|
|
USBDDriver_RequestHandler(pHidd->pUsbd, request);
|
|
}
|
|
|
|
/**
|
|
* Try to read request buffer of SetReport.
|
|
* Set pData to 0 to get current data length only.
|
|
* \param pData Pointer to data buffer
|
|
* \param dwLength Data buffer length
|
|
* \return Number of bytes read
|
|
*/
|
|
uint16_t HIDDTransferDriver_ReadReport(void *pData,
|
|
uint32_t dwLength)
|
|
{
|
|
HIDDTransferDriver *pDrv = &hiddTransferDriver;
|
|
|
|
if (pData == 0) {
|
|
|
|
return pDrv->iReportLen;
|
|
}
|
|
|
|
if (dwLength > HIDDTransferDriver_REPORTSIZE) {
|
|
|
|
dwLength = HIDDTransferDriver_REPORTSIZE;
|
|
}
|
|
if (dwLength > pDrv->iReportLen) {
|
|
|
|
dwLength = pDrv->iReportLen;
|
|
}
|
|
pDrv->iReportLen = 0;
|
|
memcpy(pData, pDrv->iReportBuf, dwLength);
|
|
|
|
return dwLength;
|
|
}
|
|
|
|
/**
|
|
* Try to read request buffer of interrupt OUT EP.
|
|
* Set pData to 0 to get current data length only.
|
|
* \param pData Pointer to data buffer
|
|
* \param dLength Data buffer length
|
|
* \return Number of bytes read
|
|
*/
|
|
uint16_t HIDDTransferDriver_Read(void *pData,
|
|
uint32_t dLength)
|
|
{
|
|
HIDDTransferDriver *pDrv = &hiddTransferDriver;
|
|
if (pData == 0) {
|
|
|
|
return pDrv->outputReports[0]->wTransferred;
|
|
}
|
|
|
|
if (dLength > HIDDTransferDriver_REPORTSIZE) {
|
|
|
|
dLength = HIDDTransferDriver_REPORTSIZE;
|
|
}
|
|
if (dLength > pDrv->outputReports[0]->wTransferred) {
|
|
|
|
dLength = pDrv->outputReports[0]->wTransferred;
|
|
}
|
|
pDrv->outputReports[0]->wTransferred = 0;
|
|
memcpy(pData, pDrv->outputReports[0]->bData, dLength);
|
|
|
|
return dLength;
|
|
}
|
|
|
|
/**
|
|
* Write data through USB interrupt IN EP.
|
|
* \param pData Pointer to the data sent.
|
|
* \param dLength The data length.
|
|
* \param fCallback Callback function invoked when transferring done.
|
|
* \param pArg Pointer to additional arguments.
|
|
*/
|
|
uint8_t HIDDTransferDriver_Write(const void *pData,
|
|
uint32_t dLength,
|
|
TransferCallback fCallback,
|
|
void *pArg)
|
|
{
|
|
HIDDTransferDriver *pDrv = &hiddTransferDriver;
|
|
if (dLength != HIDDTransferDriver_REPORTSIZE) {
|
|
|
|
dLength = HIDDTransferDriver_REPORTSIZE;
|
|
}
|
|
return USBD_Write(pDrv->hidFunction.bPipeIN,
|
|
pData, dLength,
|
|
fCallback, pArg);
|
|
}
|
|
|
|
/**
|
|
* Starts a remote wake-up sequence if the host has explicitely enabled it
|
|
* by sending the appropriate SET_FEATURE request.
|
|
*/
|
|
void HIDDTransferDriver_RemoteWakeUp(void)
|
|
{
|
|
HIDDTransferDriver *pDrv = &hiddTransferDriver;
|
|
|
|
/* Remote wake-up has been enabled */
|
|
if (USBDDriver_IsRemoteWakeUpEnabled(pDrv->hidFunction.pUsbd)) {
|
|
|
|
USBD_RemoteWakeUp();
|
|
}
|
|
}
|
|
|
|
/**@}*/
|