Files
base/middleware/usb/uC-USBH/Class/HID/usbh_hid.c
2025-11-07 09:57:14 +08:00

2198 lines
88 KiB
C

/*
*********************************************************************************************************
* uC/USB-Host
* The Embedded USB Host Stack
*
* Copyright 2004-2021 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* NOTICE
*
* Semidrive modified this file to adapt it for ssdk platform.
* The modifications are only intended for use with Semidrive chips.
* Copyright of all the modifications belongs to Semidrive Semiconductor.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* HUMAN INTERFACE DEVICE CLASS
*
* Filename : usbh_hid.c
* Version : V3.42.01
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#define USBH_HID_MODULE
#define MICRIUM_SOURCE
#include "usbh_hid.h"
#include "usbh_hidparser.h"
#include "../../Source/usbh_core.h"
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
#define USBH_HID_MAX_DUR 1020u
#define USBH_HID_DUR_RESOLUTION 4u
#define USBH_HID_LEN_HID_DESC 9u
/*
*********************************************************************************************************
* SUBCLASS CODES
*
* Note(s) : (1) See 'Device Class Definition for Human Interface Devices (HID), 6/27/01, Version 1.11',
* section 4.2 for more details about subclass codes.
*********************************************************************************************************
*/
#define USBH_HID_SUBCLASS_CODE_NONE 0x00u
#define USBH_HID_SUBCLASS_CODE_BOOT_IF 0x01u
/*
*********************************************************************************************************
* CLASS-SPECIFIC DESCRIPTORS
*
* Note(s) : (1) See 'Device Class Definition for Human Interface Devices (HID), 6/27/01, Version 1.11',
* section 7.1 for more details about class-specific descriptors.
*
* (2) For a 'get descriptor' setup request, the low byte of the 'wValue' field may contain
* one of these values.
*********************************************************************************************************
*/
#define USBH_HID_DESC_TYPE_HID 0x21u
#define USBH_HID_DESC_TYPE_REPORT 0x22u
#define USBH_HID_DESC_TYPE_PHYSICAL 0x23u
/*
*********************************************************************************************************
* CLASS-SPECIFIC REQUESTS
*
* Note(s) : (1) See 'Device Class Definition for Human Interface Devices (HID), 6/27/01, Version 1.11',
* section 7.2 for more details about class-s[ecific requests.
*
* (2) The 'bRequest' field of a class-specific setup request may contain one of these values.
*********************************************************************************************************
*/
#define USBH_HID_REQ_GET_REPORT 0x01u
#define USBH_HID_REQ_GET_IDLE 0x02u
#define USBH_HID_REQ_GET_PROTOCOL 0x03u
#define USBH_HID_REQ_SET_REPORT 0x09u
#define USBH_HID_REQ_SET_IDLE 0x0Au
#define USBH_HID_REQ_SET_PROTOCOL 0x0Bu
/*
*********************************************************************************************************
* GET REPORT REQUEST TYPES
*
* Note(s) : (1) See 'Device Class Definition for Human Interface Devices (HID), 6/27/01, Version 1.11',
* section 7.2.1 for more details about get report request types.
*
* (2) The upper byte of the 'wValue' field of a get report setup request may contain one of
* these values.
*********************************************************************************************************
*/
#define USBH_HID_REPORT_TYPE_IN 0x01u
#define USBH_HID_REPORT_TYPE_OUT 0x02u
#define USBH_HID_REPORT_TYPE_FEATURE 0x03u
/*
*********************************************************************************************************
* LOCAL CONSTANTS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL TABLES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
static USBH_HID_DEV USBH_HID_DevArr[USBH_HID_CFG_MAX_DEV];
static MEM_POOL USBH_HID_DevPool;
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void USBH_HID_GlobalInit (USBH_ERR *p_err);
static void *USBH_HID_ProbeIF (USBH_DEV *p_dev,
USBH_IF *p_if,
USBH_ERR *p_err);
static void USBH_HID_Suspend (void *p_class_dev);
static void USBH_HID_Resume (void *p_class_dev);
static void USBH_HID_Disconn (void *p_class_dev);
static void USBH_HID_DevClr (USBH_HID_DEV *p_hid_dev);
static USBH_ERR USBH_HID_DevLock (USBH_HID_DEV *p_hid_dev);
static void USBH_HID_DevUnlock (USBH_HID_DEV *p_hid_dev);
static CPU_INT32U USBH_HID_TxData (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
USBH_ERR *p_err);
static CPU_INT32U USBH_HID_RxData (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
USBH_ERR *p_err);
static USBH_ERR USBH_HID_MemReadHIDDesc (USBH_HID_DEV *p_hid_dev);
static void USBH_HID_IntrRxCB (USBH_EP *p_ep,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT32U xfer_len,
void *p_arg,
USBH_ERR err);
static void USBH_HID_DispatchReport (USBH_HID_DEV *p_hid_dev,
CPU_INT08U *p_buf,
CPU_INT32U buf_len,
CPU_INT32U xfer_len,
USBH_ERR err);
static USBH_ERR USBH_HID_ProcessReportDesc(USBH_HID_DEV *p_hid_dev);
static USBH_ERR USBH_HID_RxReportAsync (USBH_HID_DEV *p_hid_dev);
/*
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* HID CLASS DRIVER
*********************************************************************************************************
*/
USBH_CLASS_DRV USBH_HID_ClassDrv = {
(CPU_INT08U *)"HID",
USBH_HID_GlobalInit,
0,
USBH_HID_ProbeIF,
USBH_HID_Suspend,
USBH_HID_Resume,
USBH_HID_Disconn
};
/*
*********************************************************************************************************
*********************************************************************************************************
* GLOBAL FUNCTION
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBH_HID_Init()
*
* Description : Initialize the HID device, read & parse report descriptor and create report ID list.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : USBH_ERR_NONE If device successfully initialized.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'.
*
* ----- RETURNED BY USBH_HID_ProcessReportDesc() : -----
* USBH_ERR_DESC_ALLOC, Failed to allocate memory for descriptor.
* USBH_ERR_HID_RD_PARSER_FAIL, Cannot parse report descriptor.
* USBH_ERR_UNKNOWN, Unknown error occurred.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE, Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* USBH_ERR_DESC_EXTRA_NOT_FOUND, HID descriptor not found.
* USBH_ERR_DESC_INVALID, HID descriptor contains invalid value(s).
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* ----- RETURNED BY USBH_HID_CreateReportID -----
* USBH_ERR_ALLOC, if report ID cannot be allocated.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_Init (USBH_HID_DEV *p_hid_dev)
{
USBH_ERR err;
if (p_hid_dev == (USBH_HID_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_HID_DevLock(p_hid_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
err = USBH_HID_ProcessReportDesc(p_hid_dev); /* Read and parse report desc. */
if (err == USBH_ERR_NONE) {
err = USBH_HID_CreateReportID(p_hid_dev); /* Create report ID list. */
if (err == USBH_ERR_NONE) {
p_hid_dev->MaxReportPtr = USBH_HID_MaxReport(p_hid_dev, USBH_HID_MAIN_ITEM_TAG_IN);
p_hid_dev->IsInit = DEF_TRUE;
}
}
USBH_HID_DevUnlock(p_hid_dev);
return (err);
}
/*
*********************************************************************************************************
* USBH_HID_RefAdd()
*
* Description : Increment the application reference count to HID device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : USBH_ERR_NONE If access to device is successful.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* ----- RETURNED BY USBH_OS_MutexUnlock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_FAIL, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_RefAdd (USBH_HID_DEV *p_hid_dev)
{
USBH_ERR err;
if (p_hid_dev == (USBH_HID_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_OS_MutexLock(p_hid_dev->HMutex);
if (err != USBH_ERR_NONE) {
return (err);
}
p_hid_dev->AppRefCnt++;
err = USBH_OS_MutexUnlock(p_hid_dev->HMutex);
if (err != USBH_ERR_NONE) {
return (err);
}
return (USBH_ERR_NONE);
}
/*
*********************************************************************************************************
* USBH_HID_RefRel()
*
* Description : Decrement reference count of a HID device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : USBH_ERR_NONE If reference release is successful.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_RefRel (USBH_HID_DEV *p_hid_dev)
{
USBH_ERR err;
LIB_ERR err_lib;
if (p_hid_dev == (USBH_HID_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_OS_MutexLock(p_hid_dev->HMutex);
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_hid_dev->AppRefCnt > 0) {
p_hid_dev->AppRefCnt--;
if ((p_hid_dev->AppRefCnt == 0 ) &&
(p_hid_dev->State == USBH_CLASS_DEV_STATE_DISCONN)) {
(void)USBH_OS_MutexUnlock(p_hid_dev->HMutex);
Mem_PoolBlkFree( &USBH_HID_DevPool, /* If no more ref on dev, release it. */
(void *)p_hid_dev,
&err_lib);
return (USBH_ERR_NONE);
}
}
(void)USBH_OS_MutexUnlock(p_hid_dev->HMutex);
return (USBH_ERR_NONE);
}
/*
*********************************************************************************************************
* USBH_HID_GetReportIDArray()
*
* Description : Returns report id structure array and number of report id structures.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* p_report_id Pointer to variable that will receive the report ID structure
* array base.
*
* p_nbr_report_id Pointer to variable that will receive the number of report ID
* structures.
*
* Return(s) : USBH_ERR_NONE If report ID array successfully retrieved.
* USBH_ERR_DEV_NOT_READY If device is not ready.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_GetReportIDArray (USBH_HID_DEV *p_hid_dev,
USBH_HID_REPORT_ID **p_report_id,
CPU_INT08U *p_nbr_report_id)
{
USBH_ERR err;
if (p_hid_dev == (USBH_HID_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_HID_DevLock(p_hid_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_DEV_NOT_READY);
}
*p_report_id = p_hid_dev->ReportID; /* Rtn report ID struct array base addr. */
*p_nbr_report_id = p_hid_dev->NbrReportID; /* Rtn nbr of report ID struct. */
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_NONE);
}
/*
*********************************************************************************************************
* USBH_HID_GetAppCollArray()
*
* Description : Returns application collection structure array and number of application collection
* structures.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* p_app_coll Pointer to the application collection structure array base.
*
* p_nbr_app_coll Pointer to number of application collection structures.
*
* Return(s) : USBH_ERR_NONE If application collection array successfully retrieved.
* USBH_ERR_DEV_NOT_READY If device is not ready.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'/'p_app_coll'/
* 'p_nbr_app_coll'.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_GetAppCollArray (USBH_HID_DEV *p_hid_dev,
USBH_HID_APP_COLL **p_app_coll,
CPU_INT08U *p_nbr_app_coll)
{
USBH_ERR err;
if ((p_hid_dev == (USBH_HID_DEV *)0) ||
(p_app_coll == (USBH_HID_APP_COLL **)0) ||
(p_nbr_app_coll == (CPU_INT08U *)0)) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_HID_DevLock(p_hid_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_DEV_NOT_READY);
}
*p_app_coll = p_hid_dev->AppColl; /* Return App collection structure array base address */
*p_nbr_app_coll = p_hid_dev->NbrAppColl; /* Return the number of App collection structure */
USBH_HID_DevUnlock(p_hid_dev);
return (err);
}
/*
*********************************************************************************************************
* USBH_HID_IsBootDev()
*
* Description : Test whether HID interface belongs to boot subclass.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* p_err Pointer to variable that will receive the return error code from this function.
*
* USBH_ERR_NONE, If information retrieved successfully.
* USBH_ERR_DEV_NOT_READY If device is not ready.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Return(s) : DEF_TRUE Device belongs to boot subclass.
* DEF_FALSE Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_BOOLEAN USBH_HID_IsBootDev (USBH_HID_DEV *p_hid_dev,
USBH_ERR *p_err)
{
CPU_BOOLEAN is_boot;
if (p_hid_dev == (USBH_HID_DEV *)0) {
*p_err = USBH_ERR_INVALID_ARG;
return (DEF_FALSE);
}
*p_err = USBH_HID_DevLock(p_hid_dev);
if (*p_err != USBH_ERR_NONE) {
return (DEF_FALSE);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_DEV_NOT_READY);
}
is_boot = (p_hid_dev->SubClass == USBH_HID_SUBCLASS_CODE_BOOT_IF) ? DEF_TRUE : DEF_FALSE;
USBH_HID_DevUnlock(p_hid_dev);
return (is_boot);
}
/*
*********************************************************************************************************
* USBH_HID_RxReport()
*
* Description : Receive input report from device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* report_id Report id.
*
* p_buf Pointer to buffer that will receive the report.
*
* buf_len Buffer length, in octets.
*
* timeout_ms Timeout, in milliseconds.
*
* p_err Variable that will receive the return error code from this function.
* USBH_ERR_NONE, Report successfully received.
* USBH_ERR_DEV_NOT_READY, Device not ready.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_hid_dev'.
*
* ----- RETURNED BY USBH_HID_RxData() : -----
* USBH_ERR_NULL_PTR Invalid null pointer passed to 'p_buf'.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not interrupt or direction is not IN.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_UNKNOWN, Unknown error occurred.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Return(s) : Number of octets received.
*
* Note(s) : 1) p_buf contains only the report data without the report id.
*********************************************************************************************************
*/
CPU_INT08U USBH_HID_RxReport (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
void *p_buf,
CPU_INT08U buf_len,
CPU_INT16U timeout_ms,
USBH_ERR *p_err)
{
CPU_INT08U xfer_len;
if (p_hid_dev == (USBH_HID_DEV *)0) {
*p_err = USBH_ERR_INVALID_ARG;
return (0u);
}
*p_err = USBH_HID_DevLock(p_hid_dev);
if (*p_err != USBH_ERR_NONE) {
return (0u);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
*p_err = USBH_ERR_DEV_NOT_READY;
return (0u);
}
xfer_len = (CPU_INT08U)USBH_HID_RxData(p_hid_dev, /* Receive input report. */
report_id,
p_buf,
buf_len,
timeout_ms,
p_err);
USBH_HID_DevUnlock(p_hid_dev);
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_HID_TxReport()
*
* Description : Send the report to the device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* report_id Report id.
*
* p_buf Pointer to the buffer that contains the report.
*
* buf_len Buffer length, in octets.
*
* timeout_ms Timeout, in milliseconds.
*
* p_err Variable that will receive the return error code from this function.
*
* USBH_ERR_NONE Report successfully transmitted.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_hid_dev'.
* USBH_ERR_DEV_NOT_READY Device is not ready.
*
* ----- RETURNED BY USBH_HID_TxData() : -----
* USBH_ERR_NONE Report successfully transmitted.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'buf_len'.
* USBH_ERR_NULL_PTR Invalid null pointer passed to 'p_buf'.
* USBH_ERR_CLASS_DRV_NOT_FOUND Interface is not of HID type.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not interrupt or direction is not OUT.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_UNKNOWN Unknown error occurred.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Return(s) : Number of octets sent.
*
* Note(s) : 1) Do not add the report id to p_buf, it will be added automatically.
*********************************************************************************************************
*/
CPU_INT08U USBH_HID_TxReport (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
void *p_buf,
CPU_INT08U buf_len,
CPU_INT16U timeout_ms,
USBH_ERR *p_err)
{
CPU_INT08U xfer_len;
if (p_hid_dev == (USBH_HID_DEV *)0) {
*p_err = USBH_ERR_INVALID_ARG;
return (0u);
}
*p_err = USBH_HID_DevLock(p_hid_dev);
if (*p_err != USBH_ERR_NONE) {
return (0u);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
*p_err = USBH_ERR_DEV_NOT_READY;
return (0u);
}
xfer_len = (CPU_INT08U)USBH_HID_TxData(p_hid_dev, /* Send the output report. */
report_id,
p_buf,
buf_len,
timeout_ms,
p_err);
USBH_HID_DevUnlock(p_hid_dev);
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_HID_RegRxCB()
*
* Description : Register a callback function to receive reports from device asynchronously.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* report_id Report id.
*
* async_fnct Callback function.
*
* p_async_arg Pointer to context that will be passed to callback function.
*
* Return(s) : USBH_ERR_NONE, If callback successfully registered.
* USBH_ERR_INVALID_ARG, If invalid arguemnt passed to 'async_fnct'/'p_hid_dev'.
* USBH_ERR_DEV_NOT_READY, If device is not ready.
* USBH_ERR_HID_NOT_IN_REPORT, If incorrect report descriptor provided by device.
* USBH_ERR_ALLOC, If rx callback cannot be allocated.
* USBH_ERR_HID_REPORT_ID, If callback with same report ID is already registered.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_RegRxCB (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
USBH_HID_RXCB_FNCT async_fnct,
void *p_async_arg)
{
CPU_INT08U ix;
CPU_INT32U report_len_bytes;
USBH_HID_RXCB *p_rx_cb;
USBH_ERR err;
if ((p_hid_dev == (USBH_HID_DEV *)0) ||
(async_fnct == (USBH_HID_RXCB_FNCT)0)) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_HID_DevLock(p_hid_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_DEV_NOT_READY);
}
if (p_hid_dev->MaxReportPtr == (USBH_HID_REPORT_ID *)0) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_HID_NOT_IN_REPORT);
}
/* Check max buf size to recv data. */
report_len_bytes = (p_hid_dev->MaxReportPtr->Size / 8u);
if ((p_hid_dev->MaxReportPtr->Size % 8u) != 0u) {
report_len_bytes++;
}
if (report_len_bytes > USBH_HID_CFG_MAX_RX_BUF_SIZE) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_ALLOC);
}
for (ix = 0u; ix < USBH_HID_CFG_MAX_NBR_RXCB; ix++) { /* Check already reg callback with same report id. */
if ((p_hid_dev->RxCB[ix].InUse == DEF_TRUE) &&
(p_hid_dev->RxCB[ix].ReportID == report_id)) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_HID_REPORT_ID);
}
}
p_rx_cb = (USBH_HID_RXCB *)0; /* Search for empty callback structure. */
for (ix = 0u; ix < USBH_HID_CFG_MAX_NBR_RXCB; ix++) {
if (p_hid_dev->RxCB[ix].InUse == DEF_FALSE) {
p_rx_cb = &p_hid_dev->RxCB[ix];
break;
}
}
if (p_rx_cb == (USBH_HID_RXCB *)0) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_ALLOC);
}
/* Fill callback structure. */
p_rx_cb->ReportID = report_id;
p_rx_cb->AsyncFnct = async_fnct;
p_rx_cb->AsyncArgPtr = p_async_arg;
p_rx_cb->InUse = DEF_TRUE;
if (p_hid_dev->RxInProg == DEF_FALSE) { /* Receive Async if not started */
err = USBH_HID_RxReportAsync(p_hid_dev);
} else {
err = USBH_ERR_NONE;
}
USBH_HID_DevUnlock(p_hid_dev);
return (err);
}
/*
*********************************************************************************************************
* USBH_HID_UnregRxCB()
*
* Description : Unregisters the callback function for the given report ID.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* report_id Report id
*
* Return(s) : USBH_ERR_NONE If success
* USBH_ERR_DEV_NOT_READY If the device is not ready
* USBH_ERR_HID_REPORTID_NOT_REGISTERED If report ID is not registered
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_UnregRxCB (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id)
{
CPU_INT08U ix;
USBH_ERR err;
if (p_hid_dev == (USBH_HID_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_HID_DevLock(p_hid_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_DEV_NOT_READY);
}
for (ix = 0u; ix < USBH_HID_CFG_MAX_NBR_RXCB; ix++) { /* Search and close callback structure */
if ((p_hid_dev->RxCB[ix].InUse == DEF_TRUE ) &&
(p_hid_dev->RxCB[ix].ReportID == report_id)) {
p_hid_dev->RxCB[ix].InUse = DEF_FALSE;
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_NONE);
}
}
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_HID_REPORT_ID);
}
/*
*********************************************************************************************************
* USBH_HID_ProtocolSet()
*
* Description : Set protocol (boot/report descriptor) of HID device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* protocol Protocol to set.
*
* Return(s) : USBH_ERR_NONE, If protocol is successfully set.
* USBH_ERR_DEV_NOT_READY, If device is not ready.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'.
*
* ----- RETURNED BY USBH_CtrlTx() : -----
* USBH_ERR_UNKNOWN Unknown error occurred.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Note(s) : For more information on the 'Set_Protocol' request, see 'Device Class Definition for
* Human Interface Devices (HID), 6/27/01, Version 1.11, section 7.2.6'.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_ProtocolSet (USBH_HID_DEV *p_hid_dev,
CPU_INT16U protocol)
{
USBH_ERR err;
if (p_hid_dev == (USBH_HID_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_HID_DevLock(p_hid_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_DEV_NOT_READY);
}
(void)USBH_CtrlTx( p_hid_dev->DevPtr, /* Send Set protocol request. */
USBH_HID_REQ_SET_PROTOCOL,
(USBH_REQ_DIR_HOST_TO_DEV | USBH_REQ_TYPE_CLASS | USBH_REQ_RECIPIENT_IF),
protocol,
p_hid_dev->IfNbr,
(void *)0,
0u,
USBH_CFG_STD_REQ_TIMEOUT,
&err);
if (err == USBH_ERR_NONE) {
p_hid_dev->Boot = DEF_TRUE;
}
USBH_HID_DevUnlock(p_hid_dev);
return (err);
}
/*
*********************************************************************************************************
* USBH_HID_ProtocolGet()
*
* Description : Get protocol (boot/report) of the HID device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* p_protocol Variable that will receive protocol of the device.
*
* Return(s) : USBH_ERR_NONE, If protocol was successfully retrieved.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'.
* USBH_ERR_DEV_NOT_READY, If device is not ready.
*
* ----- RETURNED BY USBH_CtrlRx() : -----
* USBH_ERR_UNKNOWN, Unknown error occurred.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE, Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Note(s) : For more information on the 'Get_Protocol' request, see 'Device Class Definition for
* Human Interface Devices (HID), 6/27/01, Version 1.11, section 7.2.5'.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_ProtocolGet (USBH_HID_DEV *p_hid_dev,
CPU_INT16U *p_protocol)
{
USBH_ERR err;
if (p_hid_dev == (USBH_HID_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_HID_DevLock(p_hid_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
return (USBH_ERR_DEV_NOT_READY);
}
(void)USBH_CtrlRx( p_hid_dev->DevPtr, /* Send Get protocol request. */
USBH_HID_REQ_GET_PROTOCOL,
(USBH_REQ_DIR_DEV_TO_HOST | USBH_REQ_TYPE_CLASS | USBH_REQ_RECIPIENT_IF),
0u,
p_hid_dev->IfNbr,
(void *)p_protocol,
1u,
USBH_CFG_STD_REQ_TIMEOUT,
&err);
USBH_HID_DevUnlock(p_hid_dev);
return (err);
}
/*
*********************************************************************************************************
* USBH_HID_IdleSet()
*
* Description : Set idle state duration for a given report ID.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* report_id Report id.
*
* dur Duration of the idle state in milliseconds.
*
* Return(s) : USBH_ERR_NONE, If duration is successfully set.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_hid_dev'/'dur'.
* USBH_ERR_DEV_NOT_READY. If device is not ready.
*
* ----- RETURNED BY USBH_CtrlTx() : -----
* USBH_ERR_UNKNOWN Unknown error occurred.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Note(s) : For more information on the 'Set_Idle' request, see 'Device Class Definition for
* Human Interface Devices (HID), 6/27/01, Version 1.11, section 7.2.4'.
*********************************************************************************************************
*/
USBH_ERR USBH_HID_IdleSet (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
CPU_INT32U dur)
{
USBH_ERR err;
if (p_hid_dev == (USBH_HID_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
if (dur > USBH_HID_MAX_DUR) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_HID_DevLock(p_hid_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
dur = dur / USBH_HID_DUR_RESOLUTION; /* Convert into resolution units. */
/* Send class-specific request. */
(void)USBH_CtrlTx( p_hid_dev->DevPtr,
USBH_HID_REQ_SET_IDLE,
(USBH_REQ_DIR_HOST_TO_DEV | USBH_REQ_TYPE_CLASS | USBH_REQ_RECIPIENT_IF),
(((dur << 8u) & 0xFF00u) | report_id),
p_hid_dev->IfNbr,
(void *)0,
0u,
USBH_CFG_STD_REQ_TIMEOUT,
&err);
if (err == USBH_ERR_EP_STALL) {
(void)USBH_EP_Reset( p_hid_dev->DevPtr,
(USBH_EP *)0);
}
USBH_HID_DevUnlock(p_hid_dev);
return (err);
}
/*
*********************************************************************************************************
* USBH_HID_IdleGet()
*
* Description : Get idle duration for given report ID.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* report_id Report id.
*
* p_err Variable that will receive the return error code from this function.
*
* USBH_ERR_NONE Idle duration successfully retrieved.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_hid_dev'.
* USBH_ERR_DEV_NOT_READY Device is not ready.
*
* ----- RETURNED BY USBH_CtrlRx() : -----
* USBH_ERR_UNKNOWN, Unknown error occurred.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE, Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_HID_DevLock -----
* USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Return(s) : Idle duration in milliseconds.
*
* Note(s) : For more information on the 'Get_Idle' request, see 'Device Class Definition for
* Human Interface Devices (HID), 6/27/01, Version 1.11, section 7.2.3'.
*********************************************************************************************************
*/
CPU_INT32U USBH_HID_IdleGet (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
USBH_ERR *p_err)
{
ALLOC_CACHE_ALIGN_BUFFER(CPU_INT08U, dur, 1);
if (p_hid_dev == (USBH_HID_DEV *)0) {
*p_err = USBH_ERR_INVALID_ARG;
return (0u);
}
*p_err = USBH_HID_DevLock(p_hid_dev);
if (*p_err != USBH_ERR_NONE) {
return (0u);
}
if (p_hid_dev->IsInit == DEF_FALSE) {
USBH_HID_DevUnlock(p_hid_dev);
*p_err = USBH_ERR_DEV_NOT_READY;
return (0u);
}
(void)USBH_CtrlRx( p_hid_dev->DevPtr, /* Send the class-specific request. */
USBH_HID_REQ_GET_IDLE,
(USBH_REQ_DIR_DEV_TO_HOST | USBH_REQ_TYPE_CLASS | USBH_REQ_RECIPIENT_IF),
report_id,
p_hid_dev->IfNbr,
(void *) dur,
1u,
USBH_CFG_STD_REQ_TIMEOUT,
p_err);
USBH_HID_DevUnlock(p_hid_dev);
/* Convert to ms. */
return (dur[0] * USBH_HID_DUR_RESOLUTION);
}
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTION
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBH_HID_GlobalInit()
*
* Description : This function is used to initialize the global variables
*
* Argument(s) : p_err Variable that will receive the return error code from this function.
* USBH_ERR_NONE, Idle duration successfully retrieved.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_hid_dev'.
* USBH_ERR_DEV_NOT_READY, Device is not ready.
*
* ----- RETURNED BY USBH_HID_ParserGlobalInit -----
* USBH_ERR_ALLOC, if global item or collection list allocation failed.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_HID_GlobalInit (USBH_ERR *p_err)
{
CPU_INT08U ix;
LIB_ERR err_lib;
CPU_SIZE_T octets_reqd;
/* --------------- INIT HID DEV STRUCT ---------------- */
for (ix = 0u; ix < USBH_HID_CFG_MAX_DEV; ix++) {
(void)USBH_OS_MutexCreate(&USBH_HID_DevArr[ix].HMutex); /* Mutex for protection from multiple app access. */
}
Mem_PoolCreate ( &USBH_HID_DevPool, /* POOL for managing HID dev struct. */
(void *)USBH_HID_DevArr,
sizeof(USBH_HID_DEV) * USBH_HID_CFG_MAX_DEV,
USBH_HID_CFG_MAX_DEV,
sizeof(USBH_HID_DEV),
sizeof(CPU_ALIGN),
&octets_reqd,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBH_ERR_ALLOC;
return;
}
*p_err = USBH_HID_ParserGlobalInit(); /* Init HID parser struct. */
}
/*
*********************************************************************************************************
* USBH_HID_ProbeIF()
*
* Description : Determine if the interface is HID class interface.
*
* Argument(s) : p_dev Pointer to USB device.
*
* p_if Pointer to interface.
*
* p_err Variable that will receive the return error code from this function.
* USBH_ERR_NONE Interface is of HID type.
* USBH_ERR_DEV_ALLOC Cannot allocate HID device.
* USBH_ERR_CLASS_DRV_NOT_FOUND Interface is not of HID type.
*
* ----- RETURNED BY USBH_IF_DescGet() : -----
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'alt_ix'.
*
* ----- RETURNED BY USBH_IntrInOpen() : -----
* USBH_ERR_EP_ALLOC, If USBH_CFG_MAX_NBR_EPS reached.
* USBH_ERR_EP_NOT_FOUND, If endpoint with given type and direction not found.
* USBH_ERR_OS_SIGNAL_CREATE, if mutex or semaphore creation failed.
* Host controller drivers error, Otherwise.
*
* ----- RETURNED BY USBH_IntrOutOpen() : -----
* USBH_ERR_EP_ALLOC, If USBH_CFG_MAX_NBR_EPS reached.
* USBH_ERR_EP_NOT_FOUND, If endpoint with given type and direction not found.
* USBH_ERR_OS_SIGNAL_CREATE, if mutex or semaphore creation failed.
* Host controller drivers error, Otherwise.
*
* Return(s) : p_hid_dev, If device has a HID class interface.
* 0, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void *USBH_HID_ProbeIF (USBH_DEV *p_dev,
USBH_IF *p_if,
USBH_ERR *p_err)
{
USBH_IF_DESC p_if_desc;
USBH_HID_DEV *p_hid_dev;
LIB_ERR err_lib;
*p_err = USBH_IF_DescGet(p_if, 0u, &p_if_desc); /* Get IF desc from IF. */
if (*p_err != USBH_ERR_NONE) {
return ((void *)0);
}
/* Check for HID class code. */
if (p_if_desc.bInterfaceClass == USBH_CLASS_CODE_HID){
/* Alloc a dev from HID dev pool. */
p_hid_dev = (USBH_HID_DEV *)Mem_PoolBlkGet( &USBH_HID_DevPool,
(CPU_SIZE_T)sizeof(USBH_HID_DEV),
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBH_ERR_DEV_ALLOC;
return ((void *)0);
}
USBH_HID_DevClr(p_hid_dev);
p_hid_dev->State = USBH_CLASS_DEV_STATE_CONN;
p_hid_dev->RxInProg = DEF_FALSE;
p_hid_dev->IsInit = DEF_FALSE;
p_hid_dev->DevPtr = p_dev;
p_hid_dev->IfPtr = p_if;
p_hid_dev->IfNbr = p_if_desc.bInterfaceNumber;
p_hid_dev->SubClass = p_if_desc.bInterfaceSubClass;
p_hid_dev->Protocol = p_if_desc.bInterfaceProtocol;
*p_err = USBH_IntrInOpen(p_hid_dev->DevPtr, /* Open intr IN EP. */
p_hid_dev->IfPtr,
&p_hid_dev->IntrInEP);
if (*p_err != USBH_ERR_NONE) {
Mem_PoolBlkFree( &USBH_HID_DevPool,
(void *)p_hid_dev,
&err_lib);
return ((void *)0);
}
*p_err = USBH_IntrOutOpen(p_hid_dev->DevPtr, /* Try to open optional intr OUT EP. */
p_hid_dev->IfPtr,
&p_hid_dev->IntrOutEP);
if (*p_err != USBH_ERR_NONE) {
p_hid_dev->IsOutEP_Present = DEF_FALSE;
*p_err = USBH_ERR_NONE;
} else {
p_hid_dev->IsOutEP_Present = DEF_TRUE;
}
} else {
p_hid_dev = (USBH_HID_DEV *)0;
*p_err = USBH_ERR_CLASS_DRV_NOT_FOUND;
}
return ((void *)p_hid_dev);
}
/*
*********************************************************************************************************
* USBH_HID_Disconn()
*
* Description : Handle disconnection of HID device.
*
* Argument(s) : p_class_dev Pointer to HID device.
*
* Return(s) : None.
*
* Note(s) : None
*********************************************************************************************************
*/
static void USBH_HID_Disconn (void *p_class_dev)
{
LIB_ERR err_lib;
USBH_HID_DEV *p_hid_dev;
p_hid_dev = (USBH_HID_DEV *)p_class_dev;
(void)USBH_OS_MutexLock(p_hid_dev->HMutex);
p_hid_dev->State = USBH_CLASS_DEV_STATE_DISCONN;
/* -------------------- CLOSE EPS --------------------- */
USBH_EP_Close(&p_hid_dev->IntrInEP);
if (p_hid_dev->IsOutEP_Present == DEF_TRUE) {
USBH_EP_Close(&p_hid_dev->IntrOutEP);
}
if (p_hid_dev->AppRefCnt == 0u) { /* Release HID dev if app reference count is zero. */
(void)USBH_OS_MutexUnlock(p_hid_dev->HMutex);
Mem_PoolBlkFree( &USBH_HID_DevPool, /* App refcnt is 0 and Dev is removed, release HID dev. */
(void *)p_hid_dev,
&err_lib);
return;
}
(void)USBH_OS_MutexUnlock(p_hid_dev->HMutex);
}
/*
*********************************************************************************************************
* USBH_HID_Suspend()
*
* Description : Suspend HID device, waits for completion of any pending I/O.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_HID_Suspend (void *p_class_dev)
{
USBH_HID_DEV *p_hid_dev;
p_hid_dev = (USBH_HID_DEV *)p_class_dev;
(void)USBH_OS_MutexLock(p_hid_dev->HMutex);
p_hid_dev->State = USBH_CLASS_DEV_STATE_SUSPEND;
(void)USBH_OS_MutexUnlock(p_hid_dev->HMutex);
}
/*
*********************************************************************************************************
* USBH_HID_Resume()
*
* Description : Resume HID device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_HID_Resume (void *p_class_dev)
{
USBH_HID_DEV *p_hid_dev;
p_hid_dev = (USBH_HID_DEV *)p_class_dev;
(void)USBH_OS_MutexLock(p_hid_dev->HMutex);
p_hid_dev->State = USBH_CLASS_DEV_STATE_CONN;
(void)USBH_OS_MutexUnlock(p_hid_dev->HMutex);
}
/*
*********************************************************************************************************
* USBH_HID_DevClr()
*
* Description : Clear HID device structure.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_HID_DevClr (USBH_HID_DEV *p_hid_dev)
{
USBH_HMUTEX h_mutex;
h_mutex = p_hid_dev->HMutex; /* Save mutex var. */
Mem_Clr((void *)p_hid_dev, sizeof(USBH_HID_DEV));
p_hid_dev->State = USBH_CLASS_DEV_STATE_NONE;
p_hid_dev->HMutex = h_mutex; /* Restore mutex var. */
}
/*
*********************************************************************************************************
* USBH_HID_DevLock()
*
* Description : Lock HID device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : USBH_ERR_NONE, If HID device successfully locked.
* USBH_ERR_DEV_NOT_READY, If HID device not ready.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_HID_DevLock (USBH_HID_DEV *p_hid_dev)
{
CPU_INT08U state;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
state = p_hid_dev->State;
CPU_CRITICAL_EXIT();
if (state != USBH_CLASS_DEV_STATE_CONN) {
return (USBH_ERR_DEV_NOT_READY);
}
(void)USBH_OS_MutexLock(p_hid_dev->HMutex);
if (p_hid_dev->State != USBH_CLASS_DEV_STATE_CONN) {
(void)USBH_OS_MutexUnlock(p_hid_dev->HMutex);
return (USBH_ERR_DEV_NOT_READY);
}
return (USBH_ERR_NONE);
}
/*
*********************************************************************************************************
* USBH_HID_DevUnlock()
*
* Description : Unlock HID device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_HID_DevUnlock (USBH_HID_DEV *p_hid_dev)
{
(void)USBH_OS_MutexUnlock(p_hid_dev->HMutex);
}
/*
*********************************************************************************************************
* USBH_HID_TxData()
*
* Description : Send data (report) to HID device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* report_id Report Id.
*
* p_buf Pointer to buffer that contains the report.
*
* buf_len Buffer length, in octets.
*
* timeout_ms Timeout, in milliseconds.
*
* p_err Variable that will receive the return error code from this function.
*
* USBH_ERR_NONE Report successfully transmitted.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'buf_len'.
* USBH_ERR_NULL_PTR Invalid null pointer passed to 'p_buf'.
* USBH_ERR_CLASS_DRV_NOT_FOUND Interface is not of HID type.
*
* ----- RETURNED BY USBH_IntrTx() : -----
* USBH_ERR_NONE Interrupt transfer successfully transmitted.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not interrupt or direction is not OUT.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_NONE, URB is successfully submitted to host controller.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_CtrlTx() : -----
* USBH_ERR_UNKNOWN Unknown error occurred.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* Return(s) : Number of octets sent.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_INT32U USBH_HID_TxData (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
USBH_ERR *p_err)
{
CPU_INT32U xfer_len;
if ((p_buf == (void *)0 ) ||
(buf_len == 0u)) {
*p_err = USBH_ERR_NULL_PTR;
return (0u);
}
/* Use optional intr OUT EP if available. */
if (p_hid_dev->IsOutEP_Present == DEF_TRUE) {
if (report_id != 0u) { /* Copy report id and buf if report is non zero. */
if (buf_len > USBH_HID_CFG_MAX_TX_BUF_SIZE) {
*p_err = USBH_ERR_INVALID_ARG;
return (0u);
}
p_hid_dev->TxBuf[0] = report_id; /* First byte should be report id. */
Mem_Copy((void *)&p_hid_dev->TxBuf[1],
p_buf,
buf_len);
xfer_len = USBH_IntrTx(&p_hid_dev->IntrOutEP,
p_hid_dev->TxBuf,
buf_len + 1u,
timeout_ms,
p_err);
if ((*p_err == USBH_ERR_NONE) &&
( xfer_len != 0u )) {
xfer_len--;
}
} else {
xfer_len = USBH_IntrTx(&p_hid_dev->IntrOutEP,
p_buf,
buf_len,
timeout_ms,
p_err);
}
} else {
/* Send report via ctrl EP if intr OUT not available. */
xfer_len = USBH_CtrlTx(p_hid_dev->DevPtr,
USBH_HID_REQ_SET_REPORT,
(USBH_REQ_DIR_HOST_TO_DEV | USBH_REQ_TYPE_CLASS | USBH_REQ_RECIPIENT_IF),
(((USBH_HID_REPORT_TYPE_OUT << 8u) & 0xFF00u) | report_id),
p_hid_dev->IfNbr,
p_buf,
buf_len,
timeout_ms,
p_err);
}
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_HID_RxData()
*
* Description : Receive data (report) from device.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* report_id Report id.
*
* p_buf Pointer to buffer that will receive the report.
*
* buf_len Buffer length, in octets.
*
* timeout_ms Timeout, in milliseconds.
*
* p_err Variable that will receive the return error code from this function.
*
* USBH_ERR_NONE Report successfully received.
* USBH_ERR_NULL_PTR Invalid null pointer passed to 'p_buf'.
*
* ----- RETURNED BY USBH_IntrRx() : -----
* USBH_ERR_NONE Interrupt transfer successfully received.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not interrupt or direction is not IN.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_NONE, URB is successfully submitted to host controller.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_CtrlRx() : -----
* USBH_ERR_UNKNOWN, Unknown error occurred.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE, Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* Return(s) : Number of octets received.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_INT32U USBH_HID_RxData (USBH_HID_DEV *p_hid_dev,
CPU_INT08U report_id,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
USBH_ERR *p_err)
{
CPU_INT32U xfer_len;
if ((p_buf == (void *)0 ) ||
(buf_len == 0u)) {
*p_err = USBH_ERR_NULL_PTR;
return (0u);
}
if ((report_id != 0u) || /* If report ID 0 specified or intr IN EP busy, use ... */
(p_hid_dev->RxInProg == DEF_TRUE)) { /* ... ctrl EP. */
xfer_len = USBH_CtrlRx(p_hid_dev->DevPtr,
USBH_HID_REQ_GET_REPORT,
(USBH_REQ_DIR_DEV_TO_HOST | USBH_REQ_TYPE_CLASS | USBH_REQ_RECIPIENT_IF),
(((USBH_HID_REPORT_TYPE_IN << 8u) & 0xFF00u) | report_id),
p_hid_dev->IfNbr,
p_buf,
buf_len,
timeout_ms,
p_err);
} else { /* Rcv report on intr IN EP. */
xfer_len = USBH_IntrRx(&p_hid_dev->IntrInEP,
p_buf,
buf_len,
timeout_ms,
p_err);
}
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_HID_MemReadHIDDesc()
*
* Description : Retrieve and parse HID descriptor.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : USBH_ERR_NONE, HID descriptor successfully retrieved and parsed.
* USBH_ERR_DESC_EXTRA_NOT_FOUND, HID descriptor not found.
* USBH_ERR_DESC_INVALID, HID descriptor contains invalid value(s).
*
* Note(s) : (1) The HID descriptor must have at least report descriptor type/length pair.
*********************************************************************************************************
*/
static USBH_ERR USBH_HID_MemReadHIDDesc (USBH_HID_DEV *p_hid_dev)
{
USBH_HID_DESC *p_hid_desc;
CPU_INT08U *p_mem;
CPU_INT08U len;
CPU_INT08U b_type;
CPU_INT16U tot_len;
CPU_INT16U w_len;
/* Get HID desc from extra desc. */
p_mem = (CPU_INT08U *)USBH_IF_ExtraDescGet(p_hid_dev->IfPtr,
0u,
&tot_len);
if (p_mem == (CPU_INT08U *)0) {
return (USBH_ERR_DESC_EXTRA_NOT_FOUND);
}
p_hid_desc = &p_hid_dev->Desc;
p_hid_desc->bLength = MEM_VAL_GET_INT08U_LITTLE((void *)p_mem);/* Get desc len. */
len = p_hid_desc->bLength;
if (len < USBH_HID_LEN_HID_DESC) { /* Chk len. */
return (USBH_ERR_DESC_INVALID);
}
p_mem += sizeof(CPU_INT08U);
len -= sizeof(CPU_INT08U);
/* Get desc type. */
p_hid_desc->bDescriptorType = MEM_VAL_GET_INT08U_LITTLE((void *)p_mem);
if (p_hid_desc->bDescriptorType != USBH_HID_DESC_TYPE_HID) {/* Type should be USBH_HID_DESC_TYPE_HID. */
return (USBH_ERR_DESC_INVALID);
}
p_mem += sizeof(CPU_INT08U);
len -= sizeof(CPU_INT08U);
/* -------------------- GET bcdHID -------------------- */
p_hid_desc->bcdHID = MEM_VAL_GET_INT16U_LITTLE((void *)p_mem);
p_mem += sizeof(CPU_INT16U);
len -= sizeof(CPU_INT16U);
/* ----------------- GET COUNTRY CODE ----------------- */
p_hid_desc->bCountryCode = MEM_VAL_GET_INT08U_LITTLE((void *)p_mem);
p_mem += sizeof(CPU_INT08U);
len -= sizeof(CPU_INT08U);
/* -------------- GET NBR OF CLASS DESC --------------- */
p_hid_desc->bNbrDescriptors = MEM_VAL_GET_INT08U_LITTLE((void *)p_mem);
p_mem += sizeof(CPU_INT08U);
len -= sizeof(CPU_INT08U);
if (p_hid_desc->bNbrDescriptors < 1u) { /* Nbr of desc should be at least one. */
return (USBH_ERR_DESC_INVALID);
}
/* ----------------- GET REPORT DESC ------------------ */
while (len > 0) {
b_type = MEM_VAL_GET_INT08U_LITTLE((void *)p_mem);
p_mem += sizeof(CPU_INT08U);
len -= sizeof(CPU_INT08U);
w_len = MEM_VAL_GET_INT16U_LITTLE((void *)p_mem);
p_mem += sizeof(CPU_INT16U);
len -= sizeof(CPU_INT16U);
/* If this is a report desc ... */
if ((b_type == USBH_HID_DESC_TYPE_REPORT) &&
(w_len > 0u )) {
p_hid_desc->bDescriptorType = b_type;
p_hid_desc->wClassDescriptorLength = w_len;
return (USBH_ERR_NONE); /* ... assign len & rtn. */
}
}
return (USBH_ERR_DESC_EXTRA_NOT_FOUND);
}
/*
*********************************************************************************************************
* USBH_HID_IntrRxCB()
*
* Description : Interrupt callback funtion :
* (1) Check transfer status.
* (2) Dispatch report.
*
* Argument(s) : p_ep Pointer to endpoint.
*
* p_buf Pointer to receive buffer.
*
* buf_len Length of received buffer in octets.
*
* xfer_len Number of octets received.
*
* p_arg Context variable.
*
* err Receive status.
*
* Return(s) : None.
*
* Note(s) : (1) The bInterval period associated to periodic transfer can be managed by hardware
* (i.e. host controller) or by sofware (i.e. host stack).
*
* (a) Hardware: host controller will emit the periodic IN request every bInterval
* written into a register. Essentially, a list-based host controller will use this
* method (e.g. OHCI host controller).
*
* (b) Software: host controller doesn't provide a hardware management of the bInterval
* period. In this case, the stack will wait bInterval frame before re-submiting an
* IN request.
*
* (2) If endpoint has no interrupt data to transmit when accessed by the host, it
* responds with NAK. Next polling from the Host will take place at the next period
* (i.e. bInterval of the endpoint).
*********************************************************************************************************
*/
static void USBH_HID_IntrRxCB (USBH_EP *p_ep,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT32U xfer_len,
void *p_arg,
USBH_ERR err)
{
USBH_ERR temp_err;
USBH_HID_DEV *p_hid_dev;
USBH_ERR lock_err;
CPU_INT08U state;
CPU_INT08U ix;
CPU_SR_ALLOC();
(void)p_ep;
p_hid_dev = (USBH_HID_DEV *)p_arg; /* Get HID dev. */
if (p_hid_dev == (USBH_HID_DEV *)0) {
return;
}
CPU_CRITICAL_ENTER();
state = p_hid_dev->State;
CPU_CRITICAL_EXIT();
if (state != USBH_CLASS_DEV_STATE_CONN) {
for (ix = 0u; ix < USBH_HID_CFG_MAX_NBR_RXCB; ix++) { /* Notify all callback about state. */
if (p_hid_dev->RxCB[ix].InUse == DEF_TRUE) {
p_hid_dev->RxCB[ix].AsyncFnct((void *)p_hid_dev->RxCB[ix].AsyncArgPtr,
(void *)0,
0u,
USBH_ERR_DEV_NOT_READY);
}
}
return;
}
switch (err) {
case USBH_ERR_EP_NACK: /* NAK is not an error. Device has just no valid data */
err = USBH_ERR_NONE;
temp_err = USBH_ERR_EP_NACK;
case USBH_ERR_NONE:
p_hid_dev->ErrCnt = 0u;
break;
case USBH_ERR_EP_INVALID_STATE:
case USBH_ERR_HC_IO:
case USBH_ERR_EP_STALL:
USBH_EP_Reset(p_ep->DevPtr, p_ep);
p_hid_dev->ErrCnt++;
break;
case USBH_ERR_URB_ABORT:
default:
p_hid_dev->ErrCnt = USBH_HID_CFG_MAX_ERR_CNT;
break;
}
/* Dispatch data and err to app. */
USBH_HID_DispatchReport(p_hid_dev,
p_buf,
buf_len,
xfer_len,
err);
lock_err = USBH_HID_DevLock(p_hid_dev);
if (lock_err != USBH_ERR_NONE) {
return;
}
if (p_hid_dev->ErrCnt < USBH_HID_CFG_MAX_ERR_CNT) { /* If err cnt > MAX, do not resubmit. */
if(((p_hid_dev->DevPtr->DevSpd == USBH_DEV_SPD_LOW ) ||
(p_hid_dev->DevPtr->DevSpd == USBH_DEV_SPD_FULL))
&& (temp_err == USBH_ERR_EP_NACK)) { /* See Note #1. */
USBH_OS_DlyMS(p_hid_dev->IntrInEP.Desc.bInterval); /* Wait bInterval period associated to IN endpoint */
/* before resubmitting xfer. See Note #2. */
}
USBH_HID_RxReportAsync(p_hid_dev); /* Resubmit xfer if dev is in operational state. */
}
USBH_HID_DevUnlock(p_hid_dev);
}
/*
*********************************************************************************************************
* USBH_HID_DispatchReport()
*
* Description : Dispatch reports received asynchronously from device:
* (1) Identify report ID.
* (2) Find callback for report ID.
* (3) Invoke callback.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* p_buf Pointer to buffer containing.
*
* buf_len Length of report buffer in octets.
*
* xfer_len Number of octets received.
*
* p_arg Context variable.
*
* err Receive status.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_HID_DispatchReport (USBH_HID_DEV *p_hid_dev,
CPU_INT08U *p_buf,
CPU_INT32U buf_len,
CPU_INT32U xfer_len,
USBH_ERR err)
{
CPU_INT08U report_id;
CPU_INT08U ix;
void *p_arg;
USBH_ERR lock_err;
USBH_HID_RXCB_FNCT fnct;
(void)buf_len;
lock_err = USBH_HID_DevLock(p_hid_dev);
if (lock_err != USBH_ERR_NONE) {
return;
}
if (err != USBH_ERR_NONE) {
for (ix = 0u; ix < USBH_HID_CFG_MAX_NBR_RXCB; ix++) { /* Notify all reg's callback of err. */
if (p_hid_dev->RxCB[ix].InUse == DEF_TRUE) {
p_arg = p_hid_dev->RxCB[ix].AsyncArgPtr;
fnct = p_hid_dev->RxCB[ix].AsyncFnct;
USBH_HID_DevUnlock(p_hid_dev); /* Unlock dev before invoking callback. */
fnct( p_arg,
(void *)0,
0u,
err);
lock_err = USBH_HID_DevLock(p_hid_dev);
if (lock_err != USBH_ERR_NONE) {
return;
}
}
}
USBH_HID_DevUnlock(p_hid_dev);
return;
}
if (xfer_len == 0u) { /* Nothing to dispatch. */
USBH_HID_DevUnlock(p_hid_dev);
return;
}
if (p_hid_dev->Boot == DEF_TRUE) { /* If boot dev, report ID is 0. */
report_id = 0u;
} else {
if (p_hid_dev->ReportID[0].ReportID == 0u) { /* If first report ID is 0, there is no report ID. */
report_id = 0u;
} else { /* Otherwise, report ID is first byte of buf. */
report_id = p_buf[0];
}
}
/* Find callback with same report ID. */
for (ix = 0u; ix < USBH_HID_CFG_MAX_NBR_RXCB; ix++) {
if ((p_hid_dev->RxCB[ix].InUse == DEF_TRUE ) &&
(p_hid_dev->RxCB[ix].ReportID == report_id)) {
p_arg = p_hid_dev->RxCB[ix].AsyncArgPtr;
fnct = p_hid_dev->RxCB[ix].AsyncFnct;
USBH_HID_DevUnlock(p_hid_dev); /* Unlock dev before invoking callback. */
if (report_id != 0u) {
fnct( p_arg,
(void *)&p_buf[1],
xfer_len - 1u,
err);
} else {
fnct( p_arg,
(void *)&p_buf[0],
xfer_len,
err);
}
return;
}
}
USBH_HID_DevUnlock(p_hid_dev);
}
/*
*********************************************************************************************************
* USBH_HID_ProcessReportDesc()
*
* Description : Read and parse report descriptor.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : USBH_ERR_NONE, Report descriptor successfully processed.
* USBH_ERR_DESC_ALLOC, Failed to allocate memory for descriptor.
* USBH_ERR_HID_RD_PARSER_FAIL, Cannot parse report descriptor.
*
* ----- RETURNED BY USBH_CtrlRx() : -----
* USBH_ERR_UNKNOWN, Unknown error occurred.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE, Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_HID_MemReadHIDDesc() : -----
* USBH_ERR_DESC_EXTRA_NOT_FOUND, HID descriptor not found.
* USBH_ERR_DESC_INVALID, HID descriptor contains invalid value(s).
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_HID_ProcessReportDesc (USBH_HID_DEV *p_hid_dev)
{
CPU_INT32U len;
USBH_ERR err;
err = USBH_HID_MemReadHIDDesc(p_hid_dev); /* Read HID desc. */
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_hid_dev->Desc.wClassDescriptorLength > USBH_HID_CFG_MAX_REPORT_DESC_LEN) {
return (USBH_ERR_DESC_ALLOC);
}
len = USBH_CtrlRx( p_hid_dev->DevPtr, /* Read report desc. */
USBH_REQ_GET_DESC,
(USBH_REQ_DIR_DEV_TO_HOST | USBH_REQ_TYPE_STD | USBH_REQ_RECIPIENT_IF),
((USBH_HID_DESC_TYPE_REPORT << 8u) & 0xFF00u),
p_hid_dev->IfNbr,
(void *)&p_hid_dev->ReportDesc,
p_hid_dev->Desc.wClassDescriptorLength,
USBH_CFG_STD_REQ_TIMEOUT,
&err);
if ((err == USBH_ERR_NONE ) &&
(len == p_hid_dev->Desc.wClassDescriptorLength)) {
err = USBH_HID_ItemParser(p_hid_dev, /* Parse report desc. */
p_hid_dev->ReportDesc,
len);
if (err != USBH_ERR_NONE) {
err = USBH_ERR_HID_RD_PARSER_FAIL;
}
}
return (err);
}
/*
*********************************************************************************************************
* USBH_HID_RxReportAsync()
*
* Description : Read report on interrupt in endpoint.
*
* Argument(s) : p_hid_dev Pointer to HID device.
*
* Return(s) : USBH_ERR_NONE, if read request successfully transmitted.
*
* ----- RETURNED BY USBH_IntrRxAsync() : -----
* USBH_ERR_NONE If request is successfully submitted.
* USBH_ERR_INVALID_ARG If invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE If endpoint type is not interrupt or direction is not IN.
* USBH_ERR_EP_INVALID_STATE If endpoint is not opened.
* USBH_ERR_ALLOC If URB cannot be allocated.
* USBH_ERR_UNKNOWN If unknown error occured.
* Host controller drivers error code, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_HID_RxReportAsync (USBH_HID_DEV *p_hid_dev)
{
USBH_ERR err;
CPU_INT08U len;
len = p_hid_dev->MaxReportPtr->Size / 8u;
if (p_hid_dev->MaxReportPtr->ReportID != 0u) {
len++;
}
/* Start receiving in data. */
err = USBH_IntrRxAsync( &p_hid_dev->IntrInEP,
(void *)p_hid_dev->RxBuf,
len,
USBH_HID_IntrRxCB,
(void *)p_hid_dev);
if (err != USBH_ERR_NONE) {
p_hid_dev->RxInProg = DEF_FALSE;
USBH_PRINT_ERR(err);
} else {
p_hid_dev->RxInProg = DEF_TRUE;
}
return (err);
}
/*
*********************************************************************************************************
* END
*********************************************************************************************************
*/