8688 lines
351 KiB
C
8688 lines
351 KiB
C
/*
|
|
*********************************************************************************************************
|
|
* uC/USB-Device
|
|
* The Embedded USB Device 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.
|
|
*
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*
|
|
* USB DEVICE CORE OPERATIONS
|
|
*
|
|
* Filename : usbd_core.c
|
|
* Version : V4.06.01
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* INCLUDE FILES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define MICRIUM_SOURCE
|
|
#include "usbd_core.h"
|
|
#include "usbd_internal.h"
|
|
#include <lib_str.h>
|
|
#include <lib_mem.h>
|
|
#include <lib_math.h>
|
|
#include <cpu_core.h>
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* MODULE
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* OBJECTS TOTAL NUMBER DEFINES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define USBD_DEV_NBR_TOT (DEF_INT_08U_MAX_VAL - 1u)
|
|
#define USBD_CFG_NBR_TOT (DEF_INT_08U_MAX_VAL - 1u)
|
|
#define USBD_IF_NBR_TOT (DEF_INT_08U_MAX_VAL - 1u)
|
|
#define USBD_IF_ALT_NBR_TOT (DEF_INT_08U_MAX_VAL - 1u)
|
|
#define USBD_IF_GRP_NBR_TOT (DEF_INT_08U_MAX_VAL - 1u)
|
|
#define USBD_EP_NBR_TOT (DEF_INT_08U_MAX_VAL - 1u)
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL DEFINES
|
|
*
|
|
* Note(s) : (1) The descriptor buffer is used to send the device, configuration and string descriptors.
|
|
*
|
|
* (a) The size of the descriptor buffer is set to 64 which is the maximum packet size
|
|
* allowed by the USB specification for FS and HS devices.
|
|
*
|
|
* (2) USB spec 2.0 section 9.6.3, table 9-10 specify the bitmap for the configuration
|
|
* attributes.
|
|
*
|
|
* D7 Reserved (set to one)
|
|
* D6 Self-powered
|
|
* D5 Remote Wakeup
|
|
* D4..0 Reserved (reset to zero)
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define USBD_CFG_DESC_BUF_LEN 64u /* See Note #1a. */
|
|
#define USBD_EP_CTRL_ALLOC (DEF_BIT_00 | DEF_BIT_01)
|
|
|
|
#define USBD_CFG_DESC_SELF_POWERED DEF_BIT_06 /* See Note #2. */
|
|
#define USBD_CFG_DESC_REMOTE_WAKEUP DEF_BIT_05
|
|
#define USBD_CFG_DESC_RSVD_SET DEF_BIT_07
|
|
|
|
/* -------------- MICROSOFT DESC DEFINES -------------- */
|
|
#define USBD_MS_OS_DESC_COMPAT_ID_HDR_VER_1_0 0x0010u
|
|
#define USBD_MS_OS_DESC_EXT_PROPERTIES_HDR_VER_1_0 0x000Au
|
|
#define USBD_MS_OS_DESC_VER_1_0 0x0100u
|
|
|
|
#define USBD_STR_MS_OS_LEN 18u /* Length of MS OS string. */
|
|
#define USBD_STR_MS_OS_IX 0xEEu /* Index of MS OS string. */
|
|
|
|
#define USBD_MS_OS_DESC_COMPAT_ID_HDR_LEN 16u
|
|
#define USBD_MS_OS_DESC_COMPAT_ID_SECTION_LEN 24u
|
|
|
|
#define USBD_MS_OS_DESC_EXT_PROPERTIES_HDR_LEN 10u
|
|
#define USBD_MS_OS_DESC_EXT_PROPERTIES_SECTION_HDR_LEN 8u
|
|
|
|
#define USBD_MS_OS_FEATURE_COMPAT_ID 0x0004u
|
|
#define USBD_MS_OS_FEATURE_EXT_PROPERTIES 0x0005u
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL CONSTANTS
|
|
*
|
|
* Note(s) : (1) For more information, see "Extended Compat ID OS Feature Descriptor Specification",
|
|
* Appendix A, available at http://msdn.microsoft.com/en-us/windows/hardware/gg463179.aspx.
|
|
*********************************************************************************************************
|
|
*/
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
static const CPU_CHAR USBD_StrMS_Signature[] = "MSFT100"; /* Signature used in MS OS string desc. */
|
|
|
|
static const CPU_CHAR USBD_MS_CompatID[][8u] = {
|
|
{0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u},
|
|
{'R', 'N', 'D', 'I', 'S', 0u, 0u, 0u},
|
|
{'P', 'T', 'P', 0u, 0u, 0u, 0u, 0u},
|
|
{'M', 'T', 'P', 0u, 0u, 0u, 0u, 0u},
|
|
{'X', 'U', 'S', 'B', '2', '0', 0u, 0u},
|
|
{'B', 'L', 'U', 'T', 'U', 'T', 'H', 0u},
|
|
{'W', 'I', 'N', 'U', 'S', 'B', 0u, 0u},
|
|
{'W', 'I', 'N', 'N', 'C', 'M', 0u, 0u},
|
|
};
|
|
|
|
static const CPU_CHAR USBD_MS_SubCompatID[][8u] = {
|
|
{0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u},
|
|
{'1', '1', 0u, 0u, 0u, 0u, 0u, 0u},
|
|
{'1', '2', 0u, 0u, 0u, 0u, 0u, 0u},
|
|
{'E', 'D', 'R', 0u, 0u, 0u, 0u, 0u},
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL DATA TYPES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* CORE EVENTS DATA TYPE
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
typedef enum usbd_event_code {
|
|
USBD_EVENT_BUS_RESET = 0u,
|
|
USBD_EVENT_BUS_SUSPEND,
|
|
USBD_EVENT_BUS_RESUME,
|
|
USBD_EVENT_BUS_CONN,
|
|
USBD_EVENT_BUS_DISCONN,
|
|
USBD_EVENT_BUS_HS,
|
|
USBD_EVENT_EP,
|
|
USBD_EVENT_SETUP
|
|
} USBD_EVENT_CODE;
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* ENDPOINT INFORMATION DATA TYPE
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
typedef struct usbd_ep_info {
|
|
CPU_INT08U Addr; /* Endpoint address. */
|
|
CPU_INT08U Attrib; /* Endpoint attributes. */
|
|
CPU_INT08U Interval; /* Endpoint interval. */
|
|
CPU_INT16U MaxPktSize;
|
|
CPU_INT08U SyncAddr; /* Audio Class Only: associated sync endpoint. */
|
|
CPU_INT08U SyncRefresh; /* Audio Class Only: sync feedback rate. */
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
struct usbd_ep_info *NextPtr; /* Pointer to next interface group structure. */
|
|
#endif
|
|
} USBD_EP_INFO;
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* DEVICE INTERFACE GROUP DATA TYPE
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
typedef struct usbd_if_grp {
|
|
CPU_INT08U ClassCode; /* IF class code. */
|
|
CPU_INT08U ClassSubCode; /* IF sub class code. */
|
|
CPU_INT08U ClassProtocolCode; /* IF protocol code. */
|
|
CPU_INT08U IF_Start; /* IF index of the first IFs associated with a group. */
|
|
CPU_INT08U IF_Cnt; /* Number of contiguous IFs associated with a group. */
|
|
const CPU_CHAR *NamePtr; /* IF group name. */
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
struct usbd_if_grp *NextPtr; /* Pointer to next interface group structure. */
|
|
#endif
|
|
} USBD_IF_GRP;
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* INTERFACE ALTERNATE SETTING DATA TYPE
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
typedef struct usbd_if_alt {
|
|
void *ClassArgPtr; /* Dev class drv arg ptr specific to alternate setting. */
|
|
CPU_INT32U EP_AllocMap; /* EP allocation bitmap. */
|
|
CPU_INT08U EP_NbrTotal; /* Number of EP. */
|
|
const CPU_CHAR *NamePtr;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
USBD_EP_INFO *EP_TblPtrs[USBD_EP_MAX_NBR];
|
|
CPU_INT32U EP_TblMap;
|
|
#else
|
|
USBD_EP_INFO *EP_HeadPtr;
|
|
USBD_EP_INFO *EP_TailPtr;
|
|
#endif
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
struct usbd_if_alt *NextPtr; /* Pointer to next alternate setting structure. */
|
|
#endif
|
|
} USBD_IF_ALT;
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* INTERFACE DATA TYPE
|
|
*
|
|
* Note(s): (1) The interface structure contains information about the USB interfaces. It contains a
|
|
* list of all alternate settings (including the default interface).
|
|
*
|
|
* IFs | --------------------- Alt IF Settings ------------------- |
|
|
* Dflt Alt_0 Alt_1 ... Alt_n
|
|
* ----- +--------+ +---------+ +---------+ +---------+ +---------+
|
|
* | ----- | IF_0 |---->| IF_0_0 |--->| IF_0_0 |-->| IF_0_1 |-- ... --> | IF_0_1 |
|
|
* | | +--------+ +---------+ +---------+ +---------+ +---------+
|
|
* | | |
|
|
* | GRP0 V
|
|
* | | +--------+ +---------+ +---------+ +---------+ +---------+
|
|
* | | | IF_1 |---->| IF_1_0 |--->| IF_1_0 |-->| IF_1_1 |-- ... --> | IF_1_1 |
|
|
* | ----- +--------+ +---------+ +---------+ +---------+ +---------+
|
|
* | |
|
|
* CFGx | V
|
|
* | +--------+ +---------+ +---------+ +---------+ +---------+
|
|
* | | IF_2 |---->| IF_1_0 |--->| IF_1_0 |-->| IF_1_1 |-- ... --> | IF_1_1 |
|
|
* | +--------+ +---------+ +---------+ +---------+ +---------+
|
|
* | .
|
|
* | .
|
|
* | .
|
|
* | |
|
|
* | V
|
|
* | +--------+ +---------+ +---------+ +---------+ +---------+
|
|
* | | IF_n |---->| IF_n_0 |--->| IF_n_0 |-->| IF_n_1 |-- ... --> | IF_n_1 |
|
|
* | +--------+ +---------+ +---------+ +---------+ +---------+
|
|
* ------
|
|
*
|
|
* (2) Interfaces can be combined together creating a logical group. This logical group
|
|
* represents a function. The device uses the Interface Association Descriptor (IAD)
|
|
* to notify the host that multiple interfaces belong to one single function. The
|
|
* 'GrpNbr' stores the logical group number that the interface belongs to. By default,
|
|
* it is defined to 'USBD_IF_GRP_NBR_NONE'.
|
|
*
|
|
* (3) The 'EP_AllocMap' is a bitmap of the allocated physical endpoints.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
typedef struct usbd_if {
|
|
/* ------------ INTERFACE CLASS INFORMATION ----------- */
|
|
CPU_INT08U ClassCode; /* Device interface class code. */
|
|
CPU_INT08U ClassSubCode; /* Device interface sub class code. */
|
|
CPU_INT08U ClassProtocolCode; /* Device interface protocol code. */
|
|
USBD_CLASS_DRV *ClassDrvPtr; /* Device class driver pointer. */
|
|
void *ClassArgPtr; /* Dev class drv arg ptr specific to interface. */
|
|
/* ----------- INTERFACE ALTERNATE SETTINGS ----------- */
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
/* IF alternate settings array. */
|
|
USBD_IF_ALT *AltTblPtrs[USBD_CFG_MAX_NBR_IF_ALT];
|
|
#else
|
|
USBD_IF_ALT *AltHeadPtr; /* IF alternate settings linked-list. */
|
|
USBD_IF_ALT *AltTailPtr;
|
|
#endif
|
|
USBD_IF_ALT *AltCurPtr; /* Pointer to current alternate setting. */
|
|
CPU_INT08U AltCur; /* Alternate setting selected by host. */
|
|
CPU_INT08U AltNbrTotal; /* Number of alternate settings supported by this IF. */
|
|
CPU_INT08U GrpNbr; /* Interface group number. */
|
|
CPU_INT32U EP_AllocMap; /* EP allocation bitmap. */
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
struct usbd_if *NextPtr; /* Pointer to next interface structure. */
|
|
#endif
|
|
} USBD_IF;
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* DEVICE CONFIGURATION DATA TYPE
|
|
*
|
|
* Note(s): (1) The configuration structure contains information about USB configurations. It contains a
|
|
* list of interfaces.
|
|
*
|
|
* CFG | ----------------- INTERFACES ---------------- |
|
|
* ----- +-------+ +------+ +------+ +------+ +------+
|
|
* | | CFG_0 |--->| IF_0 |--->| IF_1 |--->| IF_2 |-- ... -->| IF_n |
|
|
* | +-------+ +------+ +------+ +------+ +------+
|
|
* | |
|
|
* | V
|
|
* | +-------+ +------+ +------+ +------+ +------+
|
|
* | | CFG_1 |--->| IF_0 |--->| IF_1 |--->| IF_2 |-- ... -->| IF_n |
|
|
* | +-------+ +------+ +------+ +------+ +------+
|
|
* | |
|
|
* | V
|
|
* | +-------+ +------+ +------+ +------+ +------+
|
|
* DEVICEx | | CFG_2 |--->| IF_0 |--->| IF_1 |--->| IF_2 |-- ... -->| IF_n |
|
|
* | +-------+ +------+ +------+ +------+ +------+
|
|
* | |
|
|
* | V
|
|
* | .
|
|
* | .
|
|
* | .
|
|
* | |
|
|
* | V
|
|
* | +-------+ +------+ +------+ +------+ +------+
|
|
* | | CFG_n |--->| IF_0 |--->| IF_1 |--->| IF_2 |-- ... -->| IF_n |
|
|
* ------ +-------+ +------+ +------+ +------+ +------+
|
|
*
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
typedef struct usbd_cfg { /* -------------- CONFIGURATION STRUCTURE ------------- */
|
|
CPU_INT08U Attrib; /* Configuration attributes. */
|
|
CPU_INT16U MaxPwr; /* Maximum bus power drawn. */
|
|
CPU_INT16U DescLen; /* Configuration descriptor length. */
|
|
const CPU_CHAR *NamePtr; /* Configuration name. */
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Interface & group list: */
|
|
USBD_IF *IF_TblPtrs[USBD_CFG_MAX_NBR_IF]; /* Interfaces list (array). */
|
|
/* Interfaces group list (array). */
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
USBD_IF_GRP *IF_GrpTblPtrs[USBD_CFG_MAX_NBR_IF_GRP];
|
|
#endif
|
|
#else
|
|
USBD_IF *IF_HeadPtr; /* Interfaces list (linked list). */
|
|
USBD_IF *IF_TailPtr;
|
|
USBD_IF_GRP *IF_GrpHeadPtr; /* Interfaces group list (linked list). */
|
|
USBD_IF_GRP *IF_GrpTailPtr;
|
|
#endif
|
|
|
|
CPU_INT08U IF_NbrTotal; /* Number of interfaces in this configuration. */
|
|
CPU_INT08U IF_GrpNbrTotal; /* Number of interfaces group. */
|
|
CPU_INT32U EP_AllocMap; /* EP allocation bitmap. */
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
CPU_INT08U CfgOtherSpd; /* Other-speed configuration. */
|
|
#endif
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
struct usbd_cfg *NextPtr;
|
|
#endif
|
|
} USBD_CFG;
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USB DEVICE DATA TYPE
|
|
*
|
|
* Note(s): (1) A USB device could contain multiple configurations. A configuration is a set of
|
|
* interfaces.
|
|
*
|
|
* USB Spec 2.0 section 9.2.6.6 states "device capable of operation at high-speed
|
|
* can operate in either full- or high-speed. The device always knows its operational
|
|
* speed due to having to manage its transceivers correctly as part of reset processing."
|
|
*
|
|
* "A device also operates at a single speed after completing the reset sequence. In
|
|
* particular, there is no speed switch during normal operation. However, a high-
|
|
* speed capable device may have configurations that are speed dependent. That is,
|
|
* it may have some configurations that are only possible when operating at high-
|
|
* speed or some that are only possible when operating at full-speed. High-speed
|
|
* capable devices must support reporting their speed dependent configurations."
|
|
*
|
|
* The device structure contains two list of configurations for HS and FS.
|
|
*
|
|
* +--------+ +-------+ +-------+ +-------+ +-------+
|
|
* | HS_CFG |--->| CFG_0 |--->| CFG_1 |--->| CFG_2 |-- ... -->| CFG_n |
|
|
* +--------+ +-------+ +-------+ +-------+ +-------+
|
|
*
|
|
* +--------+ +-------+ +-------+ +-------+ +-------+
|
|
* | FS_CFG |--->| CFG_0 |--->| CFG_1 |--->| CFG_2 |-- ... -->| CFG_n |
|
|
* +--------+ +-------+ +-------+ +-------+ +-------+
|
|
*
|
|
* (2) If the USB stack is optimized for speed, objects (Cfgs, IFs, EPs, etc) are implemented
|
|
* using a hash linking. Pointers are stored in an array allowing easy access by index.
|
|
*
|
|
* (3) If the USB stack is optimized for size, objects (Cfgs, IFs, EPs, etc) are implemented
|
|
* using a link list. Objects are linked dynamically reducing the overall memory footprint.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
typedef struct usbd_dev { /* ----------------- DEVICE STRUCTURE ----------------- */
|
|
CPU_INT08U Addr; /* Device address assigned by host. */
|
|
USBD_DEV_STATE State; /* Device state. */
|
|
USBD_DEV_STATE StatePrev; /* Device previous state. */
|
|
CPU_BOOLEAN ConnStatus; /* Device connection status. */
|
|
USBD_DEV_SPD Spd; /* Device operating speed. */
|
|
CPU_INT08U Nbr; /* Device instance number */
|
|
USBD_DEV_CFG *DevCfgPtr; /* Device configuration pointer. */
|
|
USBD_DRV Drv; /* Device driver information. */
|
|
/* --------------- DEVICE CONFIGURATIONS -------------- */
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Configuration list (see Note #1). */
|
|
/* FS configuration array (see Note #2). */
|
|
USBD_CFG *CfgFS_SpdTblPtrs[USBD_CFG_MAX_NBR_CFG];
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
/* HS configuration array (see Note #2). */
|
|
USBD_CFG *CfgHS_SpdTblPtrs[USBD_CFG_MAX_NBR_CFG];
|
|
#endif
|
|
#else
|
|
USBD_CFG *CfgFS_HeadPtr; /* FS configuration linked-list (see Note #3). */
|
|
USBD_CFG *CfgFS_TailPtr;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
USBD_CFG *CfgHS_HeadPtr; /* HS configuration linked-list (see Note #3). */
|
|
USBD_CFG *CfgHS_TailPtr;
|
|
#endif
|
|
#endif
|
|
USBD_CFG *CfgCurPtr; /* Current device configuration pointer. */
|
|
CPU_INT08U CfgCurNbr; /* Current device configuration number. */
|
|
|
|
CPU_INT08U CfgFS_TotalNbr; /* Number of FS configurations supported by the device. */
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
CPU_INT08U CfgHS_TotalNbr; /* Number of HS configurations supported by the device. */
|
|
#endif
|
|
/* ---- CONFIGURATION AND STRING DESCRIPTOR BUFFER ---- */
|
|
CPU_INT08U *ActualBufPtr; /* Pointer to the buffer where data will be written. */
|
|
CPU_INT08U *DescBufPtr; /* Configuration & string descriptor buffer. */
|
|
CPU_INT08U DescBufIx; /* Configuration & string descriptor buffer index. */
|
|
CPU_INT16U DescBufReqLen; /* Configuration & string descriptor requested length. */
|
|
CPU_INT16U DescBufMaxLen; /* Configuration & string descriptor maximum length. */
|
|
USBD_ERR *DescBufErrPtr; /* Configuration & string descriptor error pointer. */
|
|
/* --------------- ENDPOINT INFORMATION -------------- */
|
|
CPU_INT16U EP_CtrlMaxPktSize; /* Ctrl EP maximum packet size. */
|
|
CPU_INT08U EP_IF_Tbl[USBD_EP_MAX_NBR]; /* EP to IF number reference table. */
|
|
CPU_INT08U EP_MaxPhyNbr; /* EP Maximum physical number. */
|
|
/* ------------------ STRING STORAGE ----------------- */
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
const CPU_CHAR *StrDesc_Tbl[USBD_CFG_MAX_NBR_STR]; /* String pointers table. */
|
|
CPU_INT08U StrMaxIx; /* Current String index. */
|
|
#endif
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
CPU_INT08U StrMS_VendorCode; /* Microsoft Vendor code used in Microsoft OS str. */
|
|
#endif
|
|
|
|
USBD_BUS_FNCTS *BusFnctsPtr; /* Pointer to bus events callback functions. */
|
|
USBD_SETUP_REQ SetupReq; /* Setup request. */
|
|
USBD_SETUP_REQ SetupReqNext; /* Next setup request. */
|
|
|
|
CPU_BOOLEAN SelfPwr; /* Device self powered? */
|
|
|
|
CPU_BOOLEAN RemoteWakeup; /* Remote Wakeup feature. */
|
|
|
|
CPU_INT08U *CtrlStatusBufPtr; /* Buf used for ctrl status xfers. */
|
|
} USBD_DEV;
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USB CORE EVENTS
|
|
*
|
|
* Note(s) : (1) USB device driver queues bus and transaction events to the core task queue using
|
|
* the 'USBD_CORE_EVENT' structure.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
typedef struct usbd_core_event {
|
|
USBD_EVENT_CODE Type; /* Core event type. */
|
|
USBD_DRV *DrvPtr; /* Pointer to driver structure. */
|
|
CPU_INT08U EP_Addr; /* Endpoint address. */
|
|
USBD_ERR Err; /* Error Code returned by Driver, if any. */
|
|
} USBD_CORE_EVENT;
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USB DEBUG DATA TYPE
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
typedef struct usbd_dbg_event {
|
|
const CPU_CHAR *MsgPtr;
|
|
CPU_INT08U EP_Addr;
|
|
CPU_INT08U IF_Nbr;
|
|
CPU_BOOLEAN ArgEn;
|
|
CPU_INT32U Arg;
|
|
USBD_ERR Err;
|
|
CPU_INT32U Cnt;
|
|
CPU_TS Ts;
|
|
struct usbd_dbg_event *NextPtr;
|
|
} USBD_DBG_EVENT;
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL TABLES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL MACROS'S
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL GLOBAL VARIABLES
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USB OBJECTS POOL
|
|
*
|
|
* Note(s) : (1) USB objects (device, configuration, interfaces, alternative interface, endpoint, etc)
|
|
* are allocated from their pools.
|
|
*
|
|
* (a) USB objects CANNOT be returned to the pool.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
static USBD_DEV USBD_DevTbl[USBD_CFG_MAX_NBR_DEV]; /* Device object pool. */
|
|
static CPU_INT08U USBD_DevNbrNext;
|
|
|
|
static USBD_CFG USBD_CfgTbl[USBD_CFG_MAX_NBR_CFG]; /* Configuration object pool. */
|
|
static CPU_INT08U USBD_CfgNbrNext;
|
|
|
|
static USBD_IF USBD_IF_Tbl[USBD_CFG_MAX_NBR_IF]; /* Interface object pool. */
|
|
static CPU_INT08U USBD_IF_NbrNext;
|
|
|
|
/* Alternative interface object pool. */
|
|
static USBD_IF_ALT USBD_IF_AltTbl[USBD_CFG_MAX_NBR_IF_ALT];
|
|
static CPU_INT08U USBD_IF_AltNbrNext;
|
|
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
/* Interface group object pool. */
|
|
static USBD_IF_GRP USBD_IF_GrpTbl[USBD_CFG_MAX_NBR_IF_GRP];
|
|
static CPU_INT08U USBD_IF_GrpNbrNext;
|
|
#endif
|
|
/* Endpoints object pool. */
|
|
static USBD_EP_INFO USBD_EP_InfoTbl[USBD_CFG_MAX_NBR_EP_DESC];
|
|
static CPU_INT08U USBD_EP_InfoNbrNext;
|
|
|
|
#if (USBD_CFG_DBG_STATS_EN == DEF_ENABLED)
|
|
USBD_DBG_STATS_DEV USBD_DbgStatsDevTbl[USBD_CFG_MAX_NBR_DEV];
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* CORE EVENTS POOL
|
|
*
|
|
* Note(s) : (1) USB device driver signals the core task using a core event queue.
|
|
* The core event queue contains core event objects. These objects are
|
|
* allocated from the core event pool.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_INT32U USBD_CoreEventPoolIx;
|
|
static USBD_CORE_EVENT USBD_CoreEventPoolData[USBD_CORE_EVENT_NBR_TOTAL];
|
|
static USBD_CORE_EVENT *USBD_CoreEventPoolPtrs[USBD_CORE_EVENT_NBR_TOTAL];
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* DEBUG EVENTS POOL
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
/* Debug event pool. */
|
|
static USBD_DBG_EVENT USBD_DbgEventTbl[USBD_CFG_DBG_TRACE_NBR_EVENTS];
|
|
|
|
static USBD_DBG_EVENT *USBD_DbgEventFreePtr; /* Free debug events list pointer. */
|
|
|
|
static USBD_DBG_EVENT *USBD_DbgEventHeadPtr; /* Debug events list head pointer. */
|
|
static USBD_DBG_EVENT *USBD_DbgEventTailPtr; /* Debug events list tail pointer. */
|
|
static CPU_INT32U USBD_DbgEventCtr; /* Global debug event counter. */
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* TRACING MACROS
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
#define USBD_DBG_CORE_BUS(msg) USBD_DBG_GENERIC((msg), \
|
|
USBD_EP_ADDR_NONE, \
|
|
USBD_IF_NBR_NONE)
|
|
|
|
#define USBD_DBG_CORE_STD(msg) USBD_DBG_GENERIC((msg), \
|
|
0u, \
|
|
USBD_IF_NBR_NONE)
|
|
|
|
#define USBD_ERR_LINE_0(x) #x
|
|
#define USBD_ERR_LINE(x) USBD_ERR_LINE_0(x)
|
|
#define USBD_DBG_CORE_STD_ERR(msg, err) USBD_DBG_GENERIC_ERR( msg " line: "USBD_ERR_LINE(__LINE__)" error: ", \
|
|
0u, \
|
|
USBD_IF_NBR_NONE, \
|
|
(err))
|
|
|
|
#define USBD_DBG_CORE_STD_ARG(msg, arg) USBD_DBG_GENERIC_ARG((msg), \
|
|
0u, \
|
|
USBD_IF_NBR_NONE, \
|
|
(arg))
|
|
#else
|
|
#define USBD_DBG_CORE_BUS(msg)
|
|
#define USBD_DBG_CORE_STD(msg)
|
|
#define USBD_DBG_CORE_STD_ERR(msg, err)
|
|
#define USBD_DBG_CORE_STD_ARG(msg, arg)
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL FUNCTION PROTOTYPES
|
|
*********************************************************************************************************
|
|
*/
|
|
/* ------------- STANDARD REQUEST HANDLERS ------------ */
|
|
static void USBD_StdReqHandler( USBD_DEV *p_dev);
|
|
|
|
static CPU_BOOLEAN USBD_StdReqDev ( USBD_DEV *p_dev,
|
|
CPU_INT08U request);
|
|
|
|
static CPU_BOOLEAN USBD_StdReqIF ( USBD_DEV *p_dev,
|
|
CPU_INT08U request);
|
|
|
|
static CPU_BOOLEAN USBD_StdReqEP (const USBD_DEV *p_dev,
|
|
CPU_INT08U request);
|
|
|
|
static CPU_BOOLEAN USBD_StdReqClass (const USBD_DEV *p_dev);
|
|
|
|
static CPU_BOOLEAN USBD_StdReqVendor (const USBD_DEV *p_dev);
|
|
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
static CPU_BOOLEAN USBD_StdReqDevMS (const USBD_DEV *p_dev);
|
|
|
|
static CPU_BOOLEAN USBD_StdReqIF_MS (const USBD_DEV *p_dev);
|
|
#endif
|
|
|
|
static CPU_BOOLEAN USBD_StdReqDescGet( USBD_DEV *p_dev);
|
|
|
|
#if 0
|
|
static CPU_BOOLEAN USBD_StdReqDescSet( USBD_DEV *p_dev);
|
|
#endif
|
|
|
|
static void USBD_CfgClose ( USBD_DEV *p_dev);
|
|
|
|
static void USBD_CfgOpen ( USBD_DEV *p_dev,
|
|
CPU_INT08U cfg_nbr,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_DevDescSend ( USBD_DEV *p_dev,
|
|
CPU_BOOLEAN other,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_CfgDescSend ( USBD_DEV *p_dev,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_BOOLEAN other,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_StrDescSend ( USBD_DEV *p_dev,
|
|
CPU_INT08U str_ix,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err);
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
static void USBD_StrDescAdd ( USBD_DEV *p_dev,
|
|
const CPU_CHAR *p_str,
|
|
USBD_ERR *p_err);
|
|
#endif
|
|
|
|
static CPU_INT08U USBD_StrDescIxGet (const USBD_DEV *p_dev,
|
|
const CPU_CHAR *p_str);
|
|
|
|
static const CPU_CHAR *USBD_StrDescGet (const USBD_DEV *p_dev,
|
|
CPU_INT08U str_nbr);
|
|
|
|
static void USBD_DescWrStart ( USBD_DEV *p_dev,
|
|
CPU_INT16U req_len);
|
|
|
|
static void USBD_DescWrStop ( USBD_DEV *p_dev,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_DescWrReq08 ( USBD_DEV *p_dev,
|
|
CPU_INT08U val);
|
|
|
|
static void USBD_DescWrReq16 ( USBD_DEV *p_dev,
|
|
CPU_INT16U val);
|
|
|
|
static void USBD_DescWrReq ( USBD_DEV *p_dev,
|
|
const CPU_INT08U *p_buf,
|
|
CPU_INT16U len);
|
|
|
|
/* --------------- USB OBJECT FUNCTIONS --------------- */
|
|
static USBD_DEV *USBD_DevRefGet ( CPU_INT08U dev_nbr);
|
|
|
|
static USBD_CFG *USBD_CfgRefGet (const USBD_DEV *p_dev,
|
|
CPU_INT08U cfg_nbr);
|
|
|
|
static USBD_IF *USBD_IF_RefGet (const USBD_CFG *p_cfg,
|
|
CPU_INT08U if_nbr);
|
|
|
|
static USBD_IF_ALT *USBD_IF_AltRefGet (const USBD_IF *p_if,
|
|
CPU_INT08U if_alt_nbr);
|
|
|
|
static void USBD_IF_AltOpen ( USBD_DEV *p_dev,
|
|
CPU_INT08U if_nbr,
|
|
const USBD_IF_ALT *p_if_alt,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_IF_AltClose ( USBD_DEV *p_dev,
|
|
const USBD_IF_ALT *p_if_alt);
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
static USBD_IF_GRP *USBD_IF_GrpRefGet (const USBD_CFG *p_cfg,
|
|
CPU_INT08U if_grp_nbr);
|
|
#endif
|
|
|
|
static void USBD_EventSet ( USBD_DRV *p_drv,
|
|
USBD_EVENT_CODE event);
|
|
|
|
static void USBD_EventProcess ( USBD_DEV *p_dev,
|
|
USBD_EVENT_CODE event);
|
|
|
|
|
|
static CPU_INT08U USBD_EP_Add ( CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U if_nbr,
|
|
CPU_INT08U if_alt_nbr,
|
|
CPU_INT08U attrib,
|
|
CPU_BOOLEAN dir_in,
|
|
CPU_INT16U max_pkt_len,
|
|
CPU_INT08U interval,
|
|
USBD_ERR *p_err);
|
|
|
|
static CPU_BOOLEAN USBD_EP_Alloc ( USBD_DEV *p_dev,
|
|
USBD_DEV_SPD spd,
|
|
CPU_INT08U type,
|
|
CPU_BOOLEAN dir_in,
|
|
CPU_INT16U max_pkt_len,
|
|
CPU_INT08U if_alt_nbr,
|
|
USBD_EP_INFO *p_ep,
|
|
CPU_INT32U *p_alloc_bit_map);
|
|
|
|
static void USBD_CoreEventFree( USBD_CORE_EVENT *p_core_event);
|
|
|
|
static USBD_CORE_EVENT *USBD_CoreEventGet (void);
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
static USBD_DBG_EVENT *USBD_DbgEventGet (void);
|
|
|
|
static void USBD_DbgEventFree ( USBD_DBG_EVENT *p_event);
|
|
|
|
static void USBD_DbgEventPut ( USBD_DBG_EVENT *p_event);
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* LOCAL CONFIGURATION ERRORS
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* GLOBAL FUNCTIONS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Init()
|
|
*
|
|
* Description : (1) Initialize USB device stack:
|
|
*
|
|
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device stack successfully initialized.
|
|
* USBD_ERR_ALLOC Allocation failed.
|
|
*
|
|
* ---- RETURNED BY USBD_OS_Init() : ----
|
|
* USBD_ERR_OS_INIT_FAIL OS layer NOT successfully initialized.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USBD_Init() MUST be called ... :
|
|
*
|
|
* (a) ONLY ONCE from a product's application; ...
|
|
* (b) (1) AFTER product's OS has been initialized.
|
|
* (2) BEFORE product's application calls any USB device stack function(s).
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_Init (USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
USBD_IF_GRP *p_if_grp;
|
|
#endif
|
|
USBD_EP_INFO *p_ep;
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
USBD_DBG_EVENT *p_event;
|
|
#endif
|
|
CPU_INT16U tbl_ix;
|
|
LIB_ERR err_lib;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(;);
|
|
}
|
|
#endif
|
|
|
|
USBD_OS_Init(p_err); /* Initialize OS Interface. */
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return;
|
|
}
|
|
/* ------------ DEVICE TABLE INITIALIZATION ----------- */
|
|
for (tbl_ix = 0u; tbl_ix < USBD_CFG_MAX_NBR_DEV; tbl_ix++) {
|
|
p_dev = &USBD_DevTbl[tbl_ix];
|
|
p_dev->Addr = 0u; /* Dflt dev addr. */
|
|
p_dev->State = USBD_DEV_STATE_NONE;
|
|
p_dev->StatePrev = USBD_DEV_STATE_NONE;
|
|
p_dev->ConnStatus = DEF_FALSE;
|
|
p_dev->Spd = USBD_DEV_SPD_INVALID;
|
|
p_dev->DevCfgPtr = (USBD_DEV_CFG *)0;
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Init HS & FS cfg list: */
|
|
/* array implementation. */
|
|
Mem_Clr((void *)&p_dev->CfgFS_SpdTblPtrs[0u],
|
|
(CPU_SIZE_T )USBD_CFG_MAX_NBR_CFG * (sizeof(USBD_DEV_CFG *)));
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
Mem_Clr((void *)&p_dev->CfgHS_SpdTblPtrs[0u],
|
|
(CPU_SIZE_T )USBD_CFG_MAX_NBR_CFG * (sizeof(USBD_DEV_CFG *)));
|
|
#endif
|
|
|
|
#else
|
|
p_dev->CfgFS_HeadPtr = (USBD_CFG *)0; /* linked-list implementation. */
|
|
p_dev->CfgFS_TailPtr = (USBD_CFG *)0;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
p_dev->CfgHS_HeadPtr = (USBD_CFG *)0;
|
|
p_dev->CfgHS_TailPtr = (USBD_CFG *)0;
|
|
#endif
|
|
#endif
|
|
|
|
p_dev->CfgCurPtr = (USBD_CFG *)0;
|
|
p_dev->CfgCurNbr = USBD_CFG_NBR_NONE;
|
|
p_dev->CfgFS_TotalNbr = 0u;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
p_dev->CfgHS_TotalNbr = 0u;
|
|
#endif
|
|
|
|
Mem_Clr((void *)&p_dev->EP_IF_Tbl[0u],
|
|
(CPU_SIZE_T)(USBD_EP_MAX_NBR * (sizeof(CPU_INT08U))));
|
|
|
|
/* Alloc desc buf from heap. */
|
|
p_dev->DescBufPtr = (CPU_INT08U *)Mem_HeapAlloc( USBD_CFG_DESC_BUF_LEN,
|
|
USBD_CFG_BUF_ALIGN_OCTETS,
|
|
(CPU_SIZE_T *)DEF_NULL,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
*p_err = USBD_ERR_ALLOC;
|
|
return;
|
|
}
|
|
|
|
Mem_Clr((void *)&p_dev->DescBufPtr[0u],
|
|
USBD_CFG_DESC_BUF_LEN);
|
|
/* Alloc ctrl status buf from heap. */
|
|
p_dev->CtrlStatusBufPtr = (CPU_INT08U *)Mem_HeapAlloc( 2u,
|
|
USBD_CFG_BUF_ALIGN_OCTETS,
|
|
(CPU_SIZE_T *)DEF_NULL,
|
|
&err_lib);
|
|
if (err_lib != LIB_MEM_ERR_NONE) {
|
|
*p_err = USBD_ERR_ALLOC;
|
|
return;
|
|
}
|
|
|
|
p_dev->ActualBufPtr = p_dev->DescBufPtr;
|
|
p_dev->DescBufIx = 0u;
|
|
p_dev->DescBufReqLen = 0u;
|
|
p_dev->DescBufMaxLen = USBD_CFG_DESC_BUF_LEN;
|
|
p_dev->DescBufErrPtr = (USBD_ERR *)0u;
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
Mem_Clr((void *)&p_dev->StrDesc_Tbl[0u],
|
|
(CPU_SIZE_T)USBD_CFG_MAX_NBR_STR);
|
|
|
|
p_dev->StrMaxIx = 0u;
|
|
#endif
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
p_dev->StrMS_VendorCode = 0u;
|
|
#endif
|
|
p_dev->BusFnctsPtr = (USBD_BUS_FNCTS *)0;
|
|
|
|
Mem_Clr((void *)&p_dev->SetupReq,
|
|
(CPU_SIZE_T)sizeof(USBD_SETUP_REQ));
|
|
|
|
Mem_Clr((void *)&p_dev->SetupReqNext,
|
|
(CPU_SIZE_T)sizeof(USBD_SETUP_REQ));
|
|
|
|
p_dev->EP_CtrlMaxPktSize = 0u;
|
|
p_dev->EP_MaxPhyNbr = 0u;
|
|
|
|
Mem_Set((void *)&p_dev->EP_IF_Tbl[0u],
|
|
USBD_IF_NBR_NONE,
|
|
(CPU_SIZE_T ) USBD_EP_MAX_NBR);
|
|
|
|
p_dev->SelfPwr = DEF_NO;
|
|
p_dev->RemoteWakeup = DEF_DISABLED;
|
|
p_dev->Drv.DevNbr = USBD_DEV_NBR_NONE;
|
|
p_dev->Drv.API_Ptr = (USBD_DRV_API *)0;
|
|
p_dev->Drv.CfgPtr = (USBD_DRV_CFG *)0;
|
|
p_dev->Drv.DataPtr = (void *)0;
|
|
p_dev->Drv.BSP_API_Ptr = (USBD_DRV_BSP_API *)0;
|
|
|
|
USBD_DBG_STATS_DEV_RESET(tbl_ix);
|
|
USBD_DBG_STATS_DEV_SET_DEV_NBR(tbl_ix);
|
|
}
|
|
/* -------- CONFIGURATION TABLE INITIALIZATION -------- */
|
|
for (tbl_ix = 0u; tbl_ix < USBD_CFG_MAX_NBR_CFG; tbl_ix++) {
|
|
p_cfg = &USBD_CfgTbl[tbl_ix];
|
|
p_cfg->Attrib = DEF_BIT_NONE;
|
|
p_cfg->MaxPwr = 0u;
|
|
p_cfg->NamePtr = (CPU_CHAR *)0;
|
|
p_cfg->DescLen = 0u;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Init IF list: */
|
|
/* array implementation. */
|
|
Mem_Clr((void *)&p_cfg->IF_TblPtrs[0u],
|
|
(CPU_SIZE_T)USBD_CFG_MAX_NBR_IF * (sizeof(USBD_IF *)));
|
|
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
Mem_Clr((void *)&p_cfg->IF_GrpTblPtrs[0u],
|
|
(CPU_SIZE_T)USBD_CFG_MAX_NBR_IF_GRP * (sizeof(USBD_IF_GRP *)));
|
|
#endif
|
|
|
|
#else
|
|
p_cfg->IF_HeadPtr = (USBD_IF *)0; /* linked-list implementation. */
|
|
p_cfg->IF_TailPtr = (USBD_IF *)0;
|
|
p_cfg->IF_GrpHeadPtr = (USBD_IF_GRP *)0;
|
|
p_cfg->IF_GrpTailPtr = (USBD_IF_GRP *)0;
|
|
p_cfg->NextPtr = (USBD_CFG *)0;
|
|
#endif
|
|
p_cfg->IF_NbrTotal = 0u;
|
|
p_cfg->IF_GrpNbrTotal = 0u;
|
|
p_cfg->EP_AllocMap = DEF_BIT_NONE;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
p_cfg->CfgOtherSpd = USBD_CFG_NBR_NONE;
|
|
#endif
|
|
}
|
|
/* ---------- INTERFACE TABLE INITIALIZATION ---------- */
|
|
for (tbl_ix = 0u; tbl_ix < USBD_CFG_MAX_NBR_IF; tbl_ix++) {
|
|
p_if = &USBD_IF_Tbl[tbl_ix];
|
|
p_if->ClassCode = USBD_CLASS_CODE_USE_IF_DESC;
|
|
p_if->ClassSubCode = USBD_SUBCLASS_CODE_USE_IF_DESC;
|
|
p_if->ClassProtocolCode = USBD_PROTOCOL_CODE_USE_IF_DESC;
|
|
p_if->ClassDrvPtr = (USBD_CLASS_DRV *)0;
|
|
p_if->ClassArgPtr = (void *)0;
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
Mem_Clr((void *)&p_if->AltTblPtrs[0u],
|
|
(CPU_SIZE_T)USBD_CFG_MAX_NBR_IF_ALT * (sizeof(USBD_IF_ALT *)));
|
|
#else
|
|
p_if->AltHeadPtr = (USBD_IF_ALT *)0;
|
|
p_if->AltTailPtr = (USBD_IF_ALT *)0;
|
|
#endif
|
|
p_if->AltCurPtr = (USBD_IF_ALT *)0;
|
|
p_if->AltCur = USBD_IF_ALT_NBR_NONE;
|
|
p_if->AltNbrTotal = 0u;
|
|
p_if->GrpNbr = USBD_IF_GRP_NBR_NONE;
|
|
p_if->EP_AllocMap = DEF_BIT_NONE;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
p_if->NextPtr = (USBD_IF *)0;
|
|
#endif
|
|
}
|
|
/* ------ ALTERNATE SETTINGS TABLE INITIALIZATION ----- */
|
|
for (tbl_ix = 0u; tbl_ix < USBD_CFG_MAX_NBR_IF_ALT; tbl_ix++) {
|
|
p_if_alt = &USBD_IF_AltTbl[tbl_ix];
|
|
p_if_alt->ClassArgPtr = (void *)0;
|
|
p_if_alt->EP_AllocMap = DEF_BIT_NONE;
|
|
p_if_alt->EP_NbrTotal = 0u;
|
|
p_if_alt->NamePtr = (const CPU_CHAR *)0;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
p_if_alt->NextPtr = (USBD_IF_ALT *)0;
|
|
#endif
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
Mem_Clr((void *)&p_if_alt->EP_TblPtrs[0u],
|
|
(CPU_SIZE_T)USBD_EP_MAX_NBR * (sizeof(USBD_EP_INFO *)));
|
|
p_if_alt->EP_TblMap = 0u;
|
|
#else
|
|
p_if_alt->EP_HeadPtr = (USBD_EP_INFO *)0;
|
|
p_if_alt->EP_TailPtr = (USBD_EP_INFO *)0;
|
|
#endif
|
|
}
|
|
/* ------- INTERFACE GROUP TABLE INITIALIZATION ------- */
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
for (tbl_ix = 0u; tbl_ix < USBD_CFG_MAX_NBR_IF_GRP; tbl_ix++) {
|
|
p_if_grp = &USBD_IF_GrpTbl[tbl_ix];
|
|
p_if_grp->ClassCode = USBD_CLASS_CODE_USE_IF_DESC;
|
|
p_if_grp->ClassSubCode = USBD_SUBCLASS_CODE_USE_IF_DESC;
|
|
p_if_grp->ClassProtocolCode = USBD_PROTOCOL_CODE_USE_IF_DESC;
|
|
p_if_grp->IF_Start = USBD_IF_NBR_NONE;
|
|
p_if_grp->IF_Cnt = 0u;
|
|
p_if_grp->NamePtr = (const CPU_CHAR *)0;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
p_if_grp->NextPtr = (USBD_IF_GRP *)0;
|
|
#endif
|
|
}
|
|
#endif
|
|
/* ----- ENDPOINT INFORMATION TABLE INITIALIZATION ---- */
|
|
for (tbl_ix = 0u; tbl_ix < USBD_CFG_MAX_NBR_EP_DESC; tbl_ix++) {
|
|
p_ep = &USBD_EP_InfoTbl[tbl_ix];
|
|
p_ep->Addr = USBD_EP_NBR_NONE;
|
|
p_ep->Attrib = DEF_BIT_NONE;
|
|
p_ep->Interval = 0u;
|
|
p_ep->SyncAddr = 0u; /* Dflt sync addr is zero. */
|
|
p_ep->SyncRefresh = 0u; /* Dflt feedback rate exponent is zero. */
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
p_ep->NextPtr = (USBD_EP_INFO *)0;
|
|
#endif
|
|
}
|
|
|
|
/* Init pool of core events. */
|
|
for (tbl_ix = 0u; tbl_ix < USBD_CORE_EVENT_NBR_TOTAL; tbl_ix++) {
|
|
USBD_CoreEventPoolPtrs[tbl_ix] = &USBD_CoreEventPoolData[tbl_ix];
|
|
}
|
|
|
|
/* Init pool of debug events. */
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
for (tbl_ix = 0u; tbl_ix < (USBD_CFG_DBG_TRACE_NBR_EVENTS - 1u); tbl_ix++) {
|
|
p_event = &USBD_DbgEventTbl[tbl_ix];
|
|
p_event->NextPtr = &USBD_DbgEventTbl[tbl_ix + 1u];
|
|
}
|
|
|
|
p_event = &USBD_DbgEventTbl[tbl_ix];
|
|
p_event->NextPtr = (USBD_DBG_EVENT *)0;
|
|
USBD_DbgEventCtr = 0u;
|
|
USBD_DbgEventFreePtr = &USBD_DbgEventTbl[0u];
|
|
#endif
|
|
|
|
USBD_DevNbrNext = 0u;
|
|
USBD_CfgNbrNext = 0u;
|
|
USBD_IF_NbrNext = 0u;
|
|
USBD_IF_AltNbrNext = 0u;
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
USBD_IF_GrpNbrNext = 0u;
|
|
#endif
|
|
USBD_EP_InfoNbrNext = 0u;
|
|
USBD_CoreEventPoolIx = USBD_CORE_EVENT_NBR_TOTAL;
|
|
|
|
USBD_EP_Init();
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_VersionGet()
|
|
*
|
|
* Description : Get USB Device stack version.
|
|
*
|
|
* Argument(s) : none.
|
|
*
|
|
* Return(s) : USB Device stack version (see Note #1b).
|
|
*
|
|
* Note(s) : (1) (a) The USB Device software version is denoted as follows :
|
|
*
|
|
* Vx.yy.zz
|
|
*
|
|
* where
|
|
* V denotes 'Version' label
|
|
* x denotes major software version revision number
|
|
* yy denotes minor software version revision number
|
|
* zz denotes sub-minor software version revision number
|
|
*
|
|
* (b) The software version label #define is formatted as follows :
|
|
*
|
|
* ver = x.yyzz * 100 * 100
|
|
*
|
|
* where
|
|
* ver denotes software version number scaled as an integer value
|
|
* x.yyzz denotes software version number, where the unscaled integer
|
|
* portion denotes the major version number & the unscaled
|
|
* fractional portion denotes the (concatenated) minor
|
|
* version numbers
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT16U USBD_VersionGet (void)
|
|
{
|
|
CPU_INT16U ver;
|
|
|
|
|
|
ver = USBD_VERSION;
|
|
|
|
return (ver);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevAdd()
|
|
*
|
|
* Description : Add a device to the stack:
|
|
*
|
|
* (a) Create default control endpoints.
|
|
*
|
|
* Argument(s) : p_dev_cfg Pointer to specific USB device configuration.
|
|
*
|
|
* p_bus_fnct Pointer to specific USB device bus events callback functions.
|
|
*
|
|
* p_drv_api Pointer to specific USB device driver API.
|
|
*
|
|
* p_drv_cfg Pointer to specific USB device driver configuration.
|
|
*
|
|
* p_bsp_api Pointer to specific USB device board-specific API.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device successfully added.
|
|
* USBD_ERR_NULL_PTR Argument 'p_dev_cfg'/'p_drv_api'/'p_drv_cfg'/
|
|
* 'p_bsp_api' passed a NULL pointer.
|
|
* USBD_ERR_DEV_ALLOC NO available USB devices (see 'usbd_cfg.h'
|
|
* USBD_CFG_MAX_NBR_DEV).
|
|
* USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available.
|
|
*
|
|
* Return(s) : Device number, if NO error(s).
|
|
*
|
|
* USBD_DEV_NBR_NONE, otherwise.
|
|
*
|
|
* Note(s) : (1) Some driver functions are required for the driver to work correctly with the core.
|
|
* The pointers to these functions are checked in this function to make sure they are
|
|
* valid and can be used throughout the core.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_DevAdd (USBD_DEV_CFG *p_dev_cfg,
|
|
USBD_BUS_FNCTS *p_bus_fnct,
|
|
USBD_DRV_API *p_drv_api,
|
|
USBD_DRV_CFG *p_drv_cfg,
|
|
USBD_DRV_BSP_API *p_bsp_api,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
CPU_INT08U dev_nbr;
|
|
CPU_INT08U ep_phy_nbr;
|
|
CPU_INT32U ep_alloc_map;
|
|
USBD_EP_INFO ep_info;
|
|
CPU_BOOLEAN alloc;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
/* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
|
|
if ((p_dev_cfg == (USBD_DEV_CFG *)0) || /* Validate mandatory ptrs. */
|
|
(p_drv_api == (USBD_DRV_API *)0) ||
|
|
(p_drv_cfg == (USBD_DRV_CFG *)0) ||
|
|
(p_bsp_api == (USBD_DRV_BSP_API *)0)) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (USBD_DEV_NBR_NONE);
|
|
}
|
|
|
|
if ((p_drv_api->Init == (void *)0) || /* Validate mandatory fnct ptrs. See Note #1. */
|
|
(p_drv_api->Start == (void *)0) ||
|
|
(p_drv_api->Stop == (void *)0) ||
|
|
(p_drv_api->EP_Open == (void *)0) ||
|
|
(p_drv_api->EP_Close == (void *)0) ||
|
|
(p_drv_api->EP_RxStart == (void *)0) ||
|
|
(p_drv_api->EP_Rx == (void *)0) ||
|
|
(p_drv_api->EP_RxZLP == (void *)0) ||
|
|
(p_drv_api->EP_Tx == (void *)0) ||
|
|
(p_drv_api->EP_TxStart == (void *)0) ||
|
|
(p_drv_api->EP_TxZLP == (void *)0) ||
|
|
(p_drv_api->EP_Abort == (void *)0) ||
|
|
(p_drv_api->EP_Stall == (void *)0) ||
|
|
(p_drv_api->ISR_Handler == (void *)0)) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (USBD_DEV_NBR_NONE);
|
|
}
|
|
#endif
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
dev_nbr = USBD_DevNbrNext;
|
|
if (dev_nbr >= USBD_CFG_MAX_NBR_DEV) { /* Chk if dev nbr is valid. */
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_DEV_ALLOC;
|
|
return (USBD_DEV_NBR_NONE);
|
|
}
|
|
USBD_DevNbrNext++;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
/* ------------ INITIALIZE DEVICE STRUCTURE ----------- */
|
|
p_dev = &USBD_DevTbl[dev_nbr];
|
|
p_dev->Nbr = dev_nbr;
|
|
p_dev->Spd = USBD_DEV_SPD_FULL; /* Set dflt speed (FS). */
|
|
p_dev->BusFnctsPtr = p_bus_fnct;
|
|
p_dev->DevCfgPtr = p_dev_cfg;
|
|
p_dev->Drv.DevNbr = dev_nbr;
|
|
p_dev->Drv.API_Ptr = p_drv_api;
|
|
p_dev->Drv.CfgPtr = p_drv_cfg;
|
|
p_dev->Drv.BSP_API_Ptr = p_bsp_api;
|
|
|
|
ep_alloc_map = DEF_BIT_NONE;
|
|
|
|
|
|
alloc = USBD_EP_Alloc(p_dev, /* Alloc physical EP for ctrl OUT. */
|
|
p_drv_cfg->Spd,
|
|
USBD_EP_TYPE_CTRL,
|
|
DEF_NO,
|
|
0u,
|
|
0u,
|
|
&ep_info,
|
|
&ep_alloc_map);
|
|
if (alloc != DEF_OK) {
|
|
*p_err = USBD_ERR_EP_NONE_AVAIL;
|
|
return (USBD_DEV_NBR_NONE);
|
|
}
|
|
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_info.Addr);
|
|
ep_phy_nbr++;
|
|
|
|
if (p_dev->EP_MaxPhyNbr < ep_phy_nbr) {
|
|
p_dev->EP_MaxPhyNbr = ep_phy_nbr;
|
|
}
|
|
|
|
alloc = USBD_EP_Alloc(p_dev, /* Alloc physical EP for ctrl IN. */
|
|
p_drv_cfg->Spd,
|
|
USBD_EP_TYPE_CTRL,
|
|
DEF_YES,
|
|
0u,
|
|
0u,
|
|
&ep_info,
|
|
&ep_alloc_map);
|
|
if (alloc != DEF_OK) {
|
|
*p_err = USBD_ERR_EP_NONE_AVAIL;
|
|
return (USBD_DEV_NBR_NONE);
|
|
}
|
|
|
|
p_dev->EP_CtrlMaxPktSize = ep_info.MaxPktSize;
|
|
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_info.Addr);
|
|
ep_phy_nbr++;
|
|
|
|
if (p_dev->EP_MaxPhyNbr < ep_phy_nbr) {
|
|
p_dev->EP_MaxPhyNbr = ep_phy_nbr;
|
|
}
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
/* Add device configuration strings: */
|
|
USBD_StrDescAdd(p_dev, /* Manufacturer string. */
|
|
p_dev_cfg->ManufacturerStrPtr,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return (USBD_DEV_NBR_NONE);
|
|
}
|
|
USBD_StrDescAdd(p_dev, /* Product string. */
|
|
p_dev_cfg->ProductStrPtr,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return (USBD_DEV_NBR_NONE);
|
|
}
|
|
USBD_StrDescAdd(p_dev, /* Serial number string. */
|
|
p_dev_cfg->SerialNbrStrPtr,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return (USBD_DEV_NBR_NONE);
|
|
}
|
|
#endif
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
return (dev_nbr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevStart()
|
|
*
|
|
* Description : Start device stack.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device successfully started.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state (see Note #1).
|
|
*
|
|
* ---- RETURNED BY 'p_drv_api->Init()' : ----
|
|
* See specific device driver(s) 'Init()' for
|
|
* additional return error codes.
|
|
*
|
|
* --- RETURNED BY 'p_drv_api->Start()' : ----
|
|
* See specific device driver(s) 'Start()' for
|
|
* additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Device stack can be only started if the device is in one of the following states:
|
|
*
|
|
* USBD_DEV_STATE_NONE Device controller has not been initialized.
|
|
* USBD_DEV_STATE_INIT Device controller already initialized.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_DevStart (CPU_INT08U dev_nbr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_DRV *p_drv;
|
|
USBD_DRV_API *p_drv_api;
|
|
CPU_BOOLEAN init;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(;);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_NONE) && /* Chk curr dev state. */
|
|
(p_dev->State != USBD_DEV_STATE_INIT)) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return;
|
|
}
|
|
|
|
p_drv = &p_dev->Drv;
|
|
p_drv_api = p_drv->API_Ptr;
|
|
|
|
init = DEF_NO;
|
|
|
|
if (p_dev->State == USBD_DEV_STATE_NONE) { /* If dev not initialized ... */
|
|
p_drv_api->Init(p_drv, p_err); /* ... call dev drv 'Init()' function. */
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return;
|
|
}
|
|
|
|
init = DEF_YES;
|
|
}
|
|
|
|
p_drv_api->Start(p_drv, p_err);
|
|
|
|
if (init == DEF_YES) {
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->State = USBD_DEV_STATE_INIT;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevStop()
|
|
*
|
|
* Description : Stop device stack.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device successfully stopped.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_DevStop (CPU_INT08U dev_nbr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_DRV *p_drv;
|
|
USBD_DRV_API *p_drv_api;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(;);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER(); /* Signal to stop processing events while the driver */
|
|
p_dev->State = USBD_DEV_STATE_STOPPING; /* shuts down. */
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
if (p_dev->State == USBD_DEV_STATE_NONE) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return;
|
|
}
|
|
|
|
USBD_CfgClose(p_dev); /* Close curr cfg. */
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->State = USBD_DEV_STATE_INIT; /* Re-init dev stack to 'INIT' state. */
|
|
p_dev->StatePrev = USBD_DEV_STATE_INIT;
|
|
p_dev->ConnStatus = DEF_FALSE;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
p_drv = &p_dev->Drv;
|
|
p_drv_api = p_drv->API_Ptr;
|
|
|
|
p_drv_api->Stop(p_drv);
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CfgAdd()
|
|
*
|
|
* Description : Add a configuration to the device.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* attrib Configuration attributes.
|
|
*
|
|
* USBD_DEV_ATTRIB_SELF_POWERED Power does not come from VBUS.
|
|
* USBD_DEV_ATTRIB_REMOTE_WAKEUP Remote wakeup feature enabled.
|
|
*
|
|
* max_pwr Bus power required for this device (see Note #1).
|
|
*
|
|
* spd Configuration speed.
|
|
*
|
|
* USBD_DEV_SPD_FULL Configuration is added in the full-speed configuration set.
|
|
* USBD_DEV_SPD_HIGH Configuration is added in the high-speed configuration set.
|
|
*
|
|
* p_name Pointer to string describing the configuration (see Note #2).
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Configuration successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state (see Note #3).
|
|
* USBD_ERR_DEV_INVALID_SPD Speed mismatch in device controller (see Note #4).
|
|
* USBD_ERR_CFG_INVALID_MAX_PWR Invalid maximum power (see Note #1).
|
|
* USBD_ERR_CFG_ALLOC Configuration cannot be allocated.
|
|
*
|
|
* Return(s) : Configuration number, if NO error(s).
|
|
*
|
|
* USBD_CFG_NBR_NONE, otherwise.
|
|
*
|
|
* Note(s) : (1) USB spec 2.0, section 7.2.1.3/4 defines power constrains for bus-powered devices:
|
|
*
|
|
* "A low-power function is one that draws up to one unit load from the USB cable when
|
|
* operational"
|
|
*
|
|
* "A function is defined as being high-power if, when fully powered, it draws over
|
|
* one but no more than five unit loads from the USB cable."
|
|
*
|
|
* A unit load is defined as 100mA, thus 'max_pwr' argument should be between 0 mA
|
|
* and 500mA
|
|
*
|
|
* (2) String support is optional, in this case 'p_name' can be a NULL string pointer.
|
|
*
|
|
* (3) Configuration can ONLY be added when the device is in the following states:
|
|
*
|
|
* USBD_DEV_STATE_NONE Device controller has not been initialized.
|
|
* USBD_DEV_STATE_INIT Device controller already initialized.
|
|
*
|
|
* (4) A high-speed configuration can only be added if the device controller is high-speed.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_CfgAdd ( CPU_INT08U dev_nbr,
|
|
CPU_INT08U attrib,
|
|
CPU_INT16U max_pwr,
|
|
USBD_DEV_SPD spd,
|
|
const CPU_CHAR *p_name,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
CPU_INT08U cfg_tbl_ix;
|
|
CPU_INT08U cfg_nbr;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_DISABLED)
|
|
(void)spd;
|
|
#endif
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
|
|
if (max_pwr > USBD_MAX_BUS_PWR_LIMIT_mA) { /* Chk max pwr (see Note #1). */
|
|
*p_err = USBD_ERR_CFG_INVALID_MAX_PWR;
|
|
return (USBD_CFG_NBR_NONE);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (USBD_CFG_NBR_NONE);
|
|
}
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_NONE) && /* Chk curr dev state. */
|
|
(p_dev->State != USBD_DEV_STATE_INIT)) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return (USBD_CFG_NBR_NONE);
|
|
}
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if ((p_dev->Drv.CfgPtr->Spd != USBD_DEV_SPD_HIGH) && /* Chk if dev supports high spd. */
|
|
(spd == USBD_DEV_SPD_HIGH)) {
|
|
*p_err = USBD_ERR_DEV_INVALID_SPD;
|
|
return (USBD_CFG_NBR_NONE);
|
|
}
|
|
#endif
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
cfg_tbl_ix = USBD_CfgNbrNext;
|
|
if (cfg_tbl_ix >= USBD_CFG_MAX_NBR_CFG) { /* Chk if cfg is avail. */
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_CFG_ALLOC;
|
|
return (USBD_CFG_NBR_NONE);
|
|
}
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
/* Add cfg to dev ... */
|
|
if (spd == USBD_DEV_SPD_HIGH) { /* ... HS cfg. */
|
|
cfg_nbr = p_dev->CfgHS_TotalNbr;
|
|
if ((cfg_nbr > USBD_CFG_NBR_TOT) || /* Chk cfg limit. */
|
|
(cfg_nbr >= USBD_CFG_MAX_NBR_CFG)) {
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_CFG_ALLOC;
|
|
return (USBD_CFG_NBR_NONE);
|
|
}
|
|
p_dev->CfgHS_TotalNbr++;
|
|
} else {
|
|
#endif
|
|
/* ... FS cfg. */
|
|
cfg_nbr = p_dev->CfgFS_TotalNbr;
|
|
if ((cfg_nbr > USBD_CFG_NBR_TOT) || /* Chk cfg limit. */
|
|
(cfg_nbr >= USBD_CFG_MAX_NBR_CFG)) {
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_CFG_ALLOC;
|
|
return (USBD_CFG_NBR_NONE);
|
|
}
|
|
p_dev->CfgFS_TotalNbr++;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
USBD_CfgNbrNext++;
|
|
|
|
/* ------ CONFIGURATION STRUCTURE INITIALIZATION ------ */
|
|
p_cfg = &USBD_CfgTbl[cfg_tbl_ix];
|
|
/* Link cfg into dev struct. */
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (spd == USBD_DEV_SPD_HIGH) {
|
|
p_dev->CfgHS_SpdTblPtrs[cfg_nbr] = p_cfg;
|
|
DEF_BIT_SET(cfg_nbr, USBD_CFG_NBR_SPD_BIT); /* Set spd bit in cfg nbr. */
|
|
} else {
|
|
#endif
|
|
p_dev->CfgFS_SpdTblPtrs[cfg_nbr] = p_cfg;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
|
|
|
|
#else
|
|
p_cfg->NextPtr = (USBD_CFG *)0;
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (spd == USBD_DEV_SPD_HIGH) {
|
|
if (p_dev->CfgHS_HeadPtr == (USBD_CFG *)0) { /* Link cfg in HS list. */
|
|
p_dev->CfgHS_HeadPtr = p_cfg;
|
|
p_dev->CfgHS_TailPtr = p_cfg;
|
|
} else {
|
|
p_dev->CfgHS_TailPtr->NextPtr = p_cfg;
|
|
p_dev->CfgHS_TailPtr = p_cfg;
|
|
}
|
|
DEF_BIT_SET(cfg_nbr, USBD_CFG_NBR_SPD_BIT); /* Set spd bit in cfg nbr. */
|
|
} else {
|
|
#endif
|
|
if (p_dev->CfgFS_HeadPtr == (USBD_CFG *)0) { /* Link cfg in FS list. */
|
|
p_dev->CfgFS_HeadPtr = p_cfg;
|
|
p_dev->CfgFS_TailPtr = p_cfg;
|
|
} else {
|
|
p_dev->CfgFS_TailPtr->NextPtr = p_cfg;
|
|
p_dev->CfgFS_TailPtr = p_cfg;
|
|
}
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
CPU_CRITICAL_EXIT();
|
|
#endif
|
|
|
|
p_cfg->Attrib = attrib;
|
|
p_cfg->NamePtr = p_name;
|
|
p_cfg->EP_AllocMap = USBD_EP_CTRL_ALLOC; /* Init EP alloc bitmap. */
|
|
p_cfg->MaxPwr = max_pwr;
|
|
p_cfg->DescLen = 0u; /* Init cfg desc len. */
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
USBD_StrDescAdd(p_dev, p_name, p_err); /* Add cfg string to dev. */
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return (USBD_CFG_NBR_NONE);
|
|
}
|
|
#else
|
|
(void)p_name;
|
|
#endif
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
return (cfg_nbr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CfgOtherSpeed()
|
|
*
|
|
* Description : Associate a configuration with its other-speed counterpart.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* cfg_other Other-speed configuration number.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Configuration successfully associated.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state (see Note #2).
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Configurations from high- and full-speed can be associated with each other to provide
|
|
* comparable functionality regardless of speed.
|
|
*
|
|
* (2) Configuration can ONLY be associated when the device is in the following states:
|
|
*
|
|
* USBD_DEV_STATE_NONE Device controller has not been initialized.
|
|
* USBD_DEV_STATE_INIT Device controller already initialized.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
void USBD_CfgOtherSpeed (CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U cfg_other,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
USBD_CFG *p_cfg_other;
|
|
|
|
|
|
/* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(;);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_NONE) && /* Chk curr dev state. */
|
|
(p_dev->State != USBD_DEV_STATE_INIT)) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return;
|
|
}
|
|
|
|
if (((cfg_nbr & USBD_CFG_NBR_SPD_BIT) ^ /* Chk if both cfg are from same spd. */
|
|
(cfg_other & USBD_CFG_NBR_SPD_BIT)) != USBD_CFG_NBR_SPD_BIT) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr);
|
|
p_cfg_other = USBD_CfgRefGet(p_dev, cfg_other);
|
|
if ((p_cfg == (USBD_CFG *)0) ||
|
|
(p_cfg_other == (USBD_CFG *)0)) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if ((p_cfg->CfgOtherSpd != USBD_CFG_NBR_NONE) || /* Chk if cfg already associated. */
|
|
(p_cfg_other->CfgOtherSpd != USBD_CFG_NBR_NONE)) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
p_cfg->CfgOtherSpd = cfg_other;
|
|
p_cfg_other->CfgOtherSpd = cfg_nbr;
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevStateGet()
|
|
*
|
|
* Description : Get current device state.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device state successfully retrieved.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
*
|
|
* Return(s) : Current device state, if NO error(s).
|
|
*
|
|
* USBD_DEV_STATE_NONE, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
USBD_DEV_STATE USBD_DevStateGet (CPU_INT08U dev_nbr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (USBD_DEV_STATE_NONE);
|
|
}
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
return (p_dev->State);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevSpdGet()
|
|
*
|
|
* Description : Get device speed.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Frame number successfully retrieved.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state.
|
|
*
|
|
* Return(s) : The current device speed, if successful.
|
|
* USBD_DEV_SPD_INVALID, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
USBD_DEV_SPD USBD_DevSpdGet (CPU_INT08U dev_nbr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (USBD_DEV_SPD_INVALID);
|
|
}
|
|
|
|
if (p_dev->State == USBD_DEV_STATE_NONE) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return (USBD_DEV_SPD_INVALID);
|
|
}
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
|
|
return (p_dev->Spd);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevSelfPwrSet()
|
|
*
|
|
* Description : Set device current power source.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* self_pwr The power source of the device :
|
|
*
|
|
* DEF_TRUE device is self-powered.
|
|
* DEF_FALSE device is bus-powered.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device power source correctly set.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_DevSelfPwrSet (CPU_INT08U dev_nbr,
|
|
CPU_BOOLEAN self_pwr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(;);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->SelfPwr = self_pwr;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevSetMS_VendorCode()
|
|
*
|
|
* Description : Set device Microsoft vendor code.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* vendor_code Microsoft vendor code.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Microsoft vendor code correctly set.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) The vendor code used MUST be different from any vendor bRequest value.
|
|
*********************************************************************************************************
|
|
*/
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
void USBD_DevSetMS_VendorCode (CPU_INT08U dev_nbr,
|
|
CPU_INT08U vendor_code,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(;);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->StrMS_VendorCode = vendor_code;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevGetCfg()
|
|
*
|
|
* Description : Get device configuration.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device state successfully retrieved.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
*
|
|
* Return(s) : Pointer to device configuration, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
USBD_DEV_CFG *USBD_DevCfgGet (CPU_INT08U dev_nbr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return ((USBD_DEV_CFG *)0);
|
|
}
|
|
*p_err = USBD_ERR_NONE;
|
|
return (p_dev->DevCfgPtr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevFrameNbrGet()
|
|
*
|
|
* Description : Get the last frame number from the driver.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Frame number successfully retrieved.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state.
|
|
* USBD_ERR_DEV_UNAVAIL_FEAT Driver does not support this feature.
|
|
*
|
|
* Return(s) : The current frame number.
|
|
*
|
|
* Note(s) : (1) The frame number will always be in the range 0-2047 (11 bits).
|
|
*
|
|
* (2) Frame number returned to the caller contains the frame and microframe numbers. It is
|
|
* encoded following this 16-bit format:
|
|
*
|
|
* | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
|
* | 0 0 | microframe | frame |
|
|
*
|
|
* Caller must use the macros USBD_FRAME_NBR_GET() or USBD_MICROFRAME_NBR_GET() to get
|
|
* the frame or microframe number only.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT16U USBD_DevFrameNbrGet (CPU_INT08U dev_nbr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_DRV *p_drv;
|
|
USBD_DRV_API *p_drv_api;
|
|
CPU_INT16U frame_nbr;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0u);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (0u);
|
|
}
|
|
|
|
if (p_dev->State == USBD_DEV_STATE_NONE) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return (0u);
|
|
}
|
|
|
|
p_drv = &p_dev->Drv;
|
|
p_drv_api = p_drv->API_Ptr;
|
|
if (p_drv_api->FrameNbrGet == (void*)0) {
|
|
*p_err = USBD_ERR_DEV_UNAVAIL_FEAT;
|
|
return (0u);
|
|
}
|
|
|
|
frame_nbr = p_drv_api->FrameNbrGet(p_drv);
|
|
*p_err = USBD_ERR_NONE;
|
|
|
|
return (frame_nbr); /* See Note #2. */
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IF_Add()
|
|
*
|
|
* Description : Add an interface to a specific configuration.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration index to add the interface.
|
|
*
|
|
* p_class_drv Pointer to interface driver.
|
|
*
|
|
* p_if_class_arg Pointer to interface driver argument.
|
|
*
|
|
* p_if_alt_class_arg Pointer to alternate interface argument.
|
|
*
|
|
* class_code Class code assigned by the USB-IF.
|
|
*
|
|
* class_sub_code Subclass code assigned by the USB-IF.
|
|
*
|
|
* class_protocol_code Protocol code assigned by the USB-IF.
|
|
*
|
|
* p_name Pointer to string describing the Interface.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Interface successfully added.
|
|
* USBD_ERR_NULL_PTR Argument 'p_class_drv'/'p_class_drv->Conn'/
|
|
* 'p_class_drv->Disconn'/'p_class_drv->IF_Desc'/
|
|
* 'p_class_drv->IF_DescSizeGet'/'p_class_drv->EP_Desc'/
|
|
* 'p_class_drv->EP_DescSizeGet' passed a NULL
|
|
* pointer.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_ALLOC Interfaces NOT available.
|
|
* USBD_ERR_IF_ALT_ALLOC Interface alternate settings NOT available.
|
|
*
|
|
* Return(s) : Interface number, if NO error(s).
|
|
*
|
|
* USBD_IF_NBR_NONE, otherwise.
|
|
*
|
|
* Note(s) : (1) USB Spec 2.0 section 9.6.5 Interface states: "An interface may include alternate
|
|
* settings that allow the endpoints and/or their characteristics to be varied after
|
|
* the device has been configured. The default setting for an interface is always
|
|
* alternate setting zero."
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_IF_Add ( CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
USBD_CLASS_DRV *p_class_drv,
|
|
void *p_if_class_arg,
|
|
void *p_if_alt_class_arg,
|
|
CPU_INT08U class_code,
|
|
CPU_INT08U class_sub_code,
|
|
CPU_INT08U class_protocol_code,
|
|
const CPU_CHAR *p_name,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_INT08U if_tbl_ix;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U if_alt_nbr;
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
|
|
if (p_class_drv == (USBD_CLASS_DRV *)0) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
|
|
if (((p_class_drv->IF_Desc == (void *)0) && /* Chk if IF_Desc() & IF_DescSizeGet() are either ... */
|
|
(p_class_drv->IF_DescSizeGet != (void *)0)) || /* ... present or not. */
|
|
((p_class_drv->IF_Desc != (void *)0) &&
|
|
(p_class_drv->IF_DescSizeGet == (void *)0))) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
|
|
if (((p_class_drv->EP_Desc == (void *)0) && /* Chk if EP_Desc() & EP_DescSizeGet() are either ... */
|
|
(p_class_drv->EP_DescSizeGet != (void *)0)) || /* ... present or not. */
|
|
((p_class_drv->EP_Desc != (void *)0) &&
|
|
(p_class_drv->EP_DescSizeGet == (void *)0))) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_NONE) && /* Chk curr dev state. */
|
|
(p_dev->State != USBD_DEV_STATE_INIT)) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr); /* Get cfg struct. */
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if_tbl_ix = USBD_IF_NbrNext;
|
|
if (if_tbl_ix >= USBD_CFG_MAX_NBR_IF) { /* Chk if IF struct is avail. */
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_IF_ALLOC;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
|
|
if_alt_nbr = USBD_IF_AltNbrNext;
|
|
if (if_alt_nbr >= USBD_CFG_MAX_NBR_IF_ALT) { /* Chk if IF alt struct is avail. */
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_IF_ALT_ALLOC;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
|
|
if_nbr = p_cfg->IF_NbrTotal; /* Get next IF nbr in cfg. */
|
|
if ((if_nbr > USBD_IF_NBR_TOT) || /* Chk IF limit. */
|
|
(if_nbr >= USBD_CFG_MAX_NBR_IF_ALT)) {
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_IF_ALLOC;
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
|
|
USBD_IF_NbrNext++;
|
|
USBD_IF_AltNbrNext++;
|
|
p_cfg->IF_NbrTotal++;
|
|
|
|
p_if = &USBD_IF_Tbl[if_tbl_ix];
|
|
p_if_alt = &USBD_IF_AltTbl[if_alt_nbr]; /* Get IF alt struct (see Note #1). */
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Link IF and alt setting. */
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
p_cfg->IF_TblPtrs[if_nbr] = p_if;
|
|
p_if->AltTblPtrs[0u] = p_if_alt;
|
|
#else
|
|
p_if->NextPtr = (USBD_IF *)0;
|
|
p_if_alt->NextPtr = (USBD_IF_ALT *)0;
|
|
p_if->AltHeadPtr = p_if_alt;
|
|
p_if->AltTailPtr = p_if_alt;
|
|
|
|
if (p_cfg->IF_HeadPtr == (USBD_IF *)0) {
|
|
p_cfg->IF_HeadPtr = p_if;
|
|
p_cfg->IF_TailPtr = p_if;
|
|
} else {
|
|
p_cfg->IF_TailPtr->NextPtr = p_if;
|
|
p_cfg->IF_TailPtr = p_if;
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
#endif
|
|
|
|
p_if->ClassCode = class_code;
|
|
p_if->ClassSubCode = class_sub_code;
|
|
p_if->ClassProtocolCode = class_protocol_code;
|
|
p_if->ClassDrvPtr = p_class_drv;
|
|
p_if->ClassArgPtr = p_if_class_arg;
|
|
p_if->EP_AllocMap = USBD_EP_CTRL_ALLOC;
|
|
p_if->AltCurPtr = p_if_alt; /* Set curr alt setting. */
|
|
p_if->AltCur = 0u;
|
|
p_if->AltNbrTotal = 1u;
|
|
p_if_alt->NamePtr = p_name;
|
|
p_if_alt->EP_AllocMap = p_if->EP_AllocMap;
|
|
p_if_alt->ClassArgPtr = p_if_alt_class_arg;
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
USBD_StrDescAdd(p_dev, p_name, p_err); /* Add IF string to dev. */
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return (USBD_IF_NBR_NONE);
|
|
}
|
|
#else
|
|
(void)p_name;
|
|
#endif
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
return (if_nbr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IF_AltAdd()
|
|
*
|
|
* Description : Add an alternate setting to a specific interface.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* p_class_arg Pointer to alternate interface argument.
|
|
*
|
|
* p_name Pointer to alternate setting name.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Interface alternate setting successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number.
|
|
* USBD_ERR_IF_ALT_ALLOC Interface alternate settings NOT available.
|
|
*
|
|
* Return(s) : Interface alternate setting number, if NO error(s).
|
|
*
|
|
* USBD_IF_ALT_NBR_NONE, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_IF_AltAdd ( CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U if_nbr,
|
|
void *p_class_arg,
|
|
const CPU_CHAR *p_name,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
CPU_INT08U if_alt_tbl_ix;
|
|
CPU_INT08U if_alt_nbr;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
#endif
|
|
/* --------------- GET OBJECT REFERENCES -------------- */
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (USBD_IF_ALT_NBR_NONE);
|
|
}
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr); /* Get cfg struct. */
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return (USBD_IF_ALT_NBR_NONE);
|
|
}
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr); /* Get IF struct. */
|
|
if (p_if == (USBD_IF *)0) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return (USBD_IF_ALT_NBR_NONE);
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if_alt_tbl_ix = USBD_IF_AltNbrNext;
|
|
if (if_alt_tbl_ix >= USBD_CFG_MAX_NBR_IF_ALT) { /* Chk if next alt setting is avail. */
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_IF_ALT_ALLOC;
|
|
return (USBD_IF_ALT_NBR_NONE);
|
|
}
|
|
|
|
if_alt_nbr = p_if->AltNbrTotal;
|
|
if ((if_alt_nbr > USBD_IF_ALT_NBR_TOT) || /* Chk if alt setting is avail. */
|
|
(if_alt_nbr >= USBD_CFG_MAX_NBR_IF_ALT)) {
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_IF_ALT_ALLOC;
|
|
return (USBD_IF_ALT_NBR_NONE);
|
|
}
|
|
|
|
USBD_IF_AltNbrNext++;
|
|
p_if->AltNbrTotal++;
|
|
|
|
p_if_alt = &USBD_IF_AltTbl[if_alt_tbl_ix];
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Add alt setting to IF. */
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
p_if->AltTblPtrs[if_alt_nbr] = p_if_alt;
|
|
#else
|
|
p_if_alt->NextPtr = (USBD_IF_ALT *)0;
|
|
|
|
p_if->AltTailPtr->NextPtr = p_if_alt;
|
|
p_if->AltTailPtr = p_if_alt;
|
|
CPU_CRITICAL_EXIT();
|
|
#endif
|
|
|
|
|
|
p_if_alt->ClassArgPtr = p_class_arg;
|
|
p_if_alt->EP_AllocMap = USBD_EP_CTRL_ALLOC;
|
|
p_if_alt->NamePtr = p_name;
|
|
|
|
DEF_BIT_CLR(p_if_alt->EP_AllocMap, p_if->EP_AllocMap);
|
|
DEF_BIT_SET(p_if_alt->EP_AllocMap, USBD_EP_CTRL_ALLOC);
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
USBD_StrDescAdd(p_dev, p_name, p_err); /* Add alt setting string to dev. */
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return (USBD_IF_ALT_NBR_NONE);
|
|
}
|
|
#else
|
|
(void)p_name;
|
|
#endif
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
return (if_alt_nbr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IF_Grp()
|
|
*
|
|
* Description : Create a interface group.
|
|
*
|
|
* Argument(s) : dev_nbr Device number
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* class_code Class code assigned by the USB-IF.
|
|
*
|
|
* class_sub_code Subclass code assigned by the USB-IF.
|
|
*
|
|
* class_protocol_code Protocol code assigned by the USB-IF.
|
|
*
|
|
* if_start Interface number of the first interface that is associated with this group.
|
|
*
|
|
* if_cnt Number of consecutive interfaces that are associated with this group.
|
|
*
|
|
* p_name Pointer to string describing interface group.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Interface group successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number.
|
|
* USBD_ERR_IF_GRP_NBR_IN_USE Interface is part of another group.
|
|
* USBD_ERR_IF_GRP_ALLOC Interface groups NOT available.
|
|
*
|
|
* Return(s) : Interface group number, if NO error(s).
|
|
*
|
|
* USBD_IF_GRP_NBR_NONE, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
CPU_INT08U USBD_IF_Grp ( CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U class_code,
|
|
CPU_INT08U class_sub_code,
|
|
CPU_INT08U class_protocol_code,
|
|
CPU_INT08U if_start,
|
|
CPU_INT08U if_cnt,
|
|
const CPU_CHAR *p_name,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_GRP *p_if_grp;
|
|
CPU_INT08U if_grp_tbl_ix;
|
|
CPU_INT08U if_grp_nbr;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U if_end;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
#endif
|
|
|
|
if (((CPU_INT16U)(if_start) + (CPU_INT16U)(if_cnt)) > (CPU_INT16U)USBD_IF_NBR_TOT) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
|
|
/* --------------- GET OBJECT REFERENCES -------------- */
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr); /* Get cfg struct. */
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
/* Verify that IFs do NOT belong to another group. */
|
|
for (if_nbr = 0u; if_nbr < if_cnt; if_nbr++) {
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr + if_start);
|
|
if (p_if == (USBD_IF *)0) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
|
|
if (p_if->GrpNbr != USBD_IF_GRP_NBR_NONE) {
|
|
*p_err = USBD_ERR_IF_GRP_NBR_IN_USE;
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if_grp_tbl_ix = USBD_IF_GrpNbrNext;
|
|
if (if_grp_tbl_ix >= USBD_CFG_MAX_NBR_IF_GRP) { /* Chk if IF grp is avail. */
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_IF_GRP_ALLOC;
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
USBD_IF_GrpNbrNext++;
|
|
|
|
p_if_grp = &USBD_IF_GrpTbl[if_grp_tbl_ix];
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
if_grp_nbr = p_cfg->IF_GrpNbrTotal;
|
|
p_cfg->IF_GrpNbrTotal++;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
p_cfg->IF_GrpTblPtrs[if_grp_nbr] = p_if_grp;
|
|
#else
|
|
p_if_grp->NextPtr = (USBD_IF_GRP *)0;
|
|
|
|
if_grp_nbr = p_cfg->IF_GrpNbrTotal;
|
|
p_cfg->IF_GrpNbrTotal++;
|
|
|
|
if (if_grp_nbr == 0u) {
|
|
p_cfg->IF_GrpHeadPtr = p_if_grp;
|
|
p_cfg->IF_GrpTailPtr = p_if_grp;
|
|
} else {
|
|
p_cfg->IF_GrpTailPtr->NextPtr = p_if_grp;
|
|
p_cfg->IF_GrpTailPtr = p_if_grp;
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
#endif
|
|
|
|
p_if_grp->ClassCode = class_code;
|
|
p_if_grp->ClassSubCode = class_sub_code;
|
|
p_if_grp->ClassProtocolCode = class_protocol_code;
|
|
p_if_grp->IF_Start = if_start;
|
|
p_if_grp->IF_Cnt = if_cnt;
|
|
p_if_grp->NamePtr = p_name;
|
|
|
|
if_end = if_cnt + if_start;
|
|
for (if_nbr = if_start; if_nbr < if_end; if_nbr++) {
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
if (p_if == (USBD_IF *)0) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
|
|
if (p_if->GrpNbr != USBD_IF_GRP_NBR_NONE) {
|
|
*p_err = USBD_ERR_IF_GRP_NBR_IN_USE;
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_if->GrpNbr = if_grp_nbr;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
USBD_StrDescAdd(p_dev, p_name, p_err); /* Add IF grp string to dev. */
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return (USBD_IF_GRP_NBR_NONE);
|
|
}
|
|
#else
|
|
(void)p_name;
|
|
#endif
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
return (if_grp_nbr);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescDevGet()
|
|
*
|
|
* Description : Get the device descriptor.
|
|
*
|
|
* Argument(s) : p_drv Pointer to device driver structure.
|
|
*
|
|
* p_buf Pointer to the destination buffer.
|
|
*
|
|
* max_len Maximum number of bytes to write in destination buffer.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device descriptor successfully obtained.
|
|
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'p_drv'/'p_buf'/
|
|
* 'max_len'.
|
|
* USBD_ERR_NULL_PTR
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number obtained from p_drv.
|
|
* USBD_ERR_DEV_INVALID_STATE State of the device is not USBD_DEV_STATE_NONE.
|
|
* USBD_ERR_ALLOC From 'DescBufErrPtr', in USBD_DescWrReq(), if
|
|
* function was called from a driver's context.
|
|
*
|
|
* - RETURNED BY USBD_DevDescSend() -
|
|
* See USBD_DevDescSend() for additional return error codes.
|
|
*
|
|
* Return(s) : Number of bytes actually in the descriptor, if NO error(s).
|
|
*
|
|
* 0, otherwise.
|
|
*
|
|
* Note(s) : (1) This function should be used by drivers supporting standard requests auto-reply,
|
|
* during the initialization process.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_DescDevGet (USBD_DRV *p_drv,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT08U max_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
CPU_INT08U desc_len;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0u);
|
|
}
|
|
|
|
if (p_drv == (USBD_DRV *)0) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (0u);
|
|
}
|
|
|
|
if (p_buf == (CPU_INT08U *)0) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (0u);
|
|
}
|
|
|
|
if (max_len == 0u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (0u);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(p_drv->DevNbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (0u);
|
|
}
|
|
|
|
if (p_dev->State != USBD_DEV_STATE_NONE) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return (0u);
|
|
}
|
|
|
|
p_dev->ActualBufPtr = p_buf;
|
|
p_dev->DescBufMaxLen = max_len;
|
|
p_dev->DescBufErrPtr = p_err;
|
|
|
|
USBD_DevDescSend(p_dev,
|
|
DEF_NO,
|
|
max_len,
|
|
p_err);
|
|
|
|
desc_len = p_dev->DescBufIx;
|
|
p_dev->DescBufErrPtr = (USBD_ERR *)0;
|
|
|
|
return (desc_len);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescCfgGet()
|
|
*
|
|
* Description : Get a configuration descriptor.
|
|
*
|
|
* Argument(s) : p_drv Pointer to device driver structure.
|
|
*
|
|
* p_buf Pointer to the destination buffer.
|
|
*
|
|
* max_len Maximum number of bytes to write in destination buffer.
|
|
*
|
|
* cfg_ix Index of the desired configuration descriptor.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Configuration descriptor successfully obtained.
|
|
* USBD_ERR_NULL_PTR Null pointer passed to 'p_drv'/'p_buf'.
|
|
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'max_len'.
|
|
* USBD_ERR_DEV_INVALID_STATE State of the device is not USBD_DEV_STATE_NONE.
|
|
* USBD_ERR_ALLOC From 'DescBufErrPtr', in USBD_DescWrReq(), if
|
|
* function was called from a driver's context.
|
|
*
|
|
* - RETURNED BY USBD_CfgDescSend() -
|
|
* See USBD_CfgDescSend() for additional return error codes.
|
|
*
|
|
*
|
|
* Return(s) : Number of bytes actually in the descriptor, if NO error(s).
|
|
*
|
|
* 0, otherwise.
|
|
*
|
|
* Note(s) : (1) This function should be used by drivers supporting standard requests auto-reply,
|
|
* during the initialization process.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT16U USBD_DescCfgGet (USBD_DRV *p_drv,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT16U max_len,
|
|
CPU_INT08U cfg_ix,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
CPU_INT08U desc_len;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0u);
|
|
}
|
|
|
|
if (p_drv == (USBD_DRV *)0) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (0u);
|
|
}
|
|
|
|
if (p_buf == (CPU_INT08U *)0) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (0u);
|
|
}
|
|
|
|
if (max_len == 0u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (0u);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(p_drv->DevNbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return (0u);
|
|
}
|
|
|
|
if (p_dev->State != USBD_DEV_STATE_NONE) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return (0u);
|
|
}
|
|
|
|
p_dev->ActualBufPtr = p_buf;
|
|
p_dev->DescBufMaxLen = max_len;
|
|
p_dev->DescBufErrPtr = p_err;
|
|
|
|
USBD_CfgDescSend(p_dev,
|
|
cfg_ix,
|
|
DEF_NO,
|
|
max_len,
|
|
p_err);
|
|
|
|
desc_len = p_dev->DescBufIx;
|
|
p_dev->DescBufErrPtr = (USBD_ERR *)0;
|
|
|
|
return (desc_len);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescStrGet()
|
|
*
|
|
* Description : Get a string descriptor.
|
|
*
|
|
* Argument(s) : p_drv Pointer to device driver structure.
|
|
*
|
|
* p_buf Pointer to the destination buffer.
|
|
*
|
|
* max_len Maximum number of bytes to write in destination buffer.
|
|
*
|
|
* str_ix Index of the desired string descriptor.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE String descriptor successfully obtained.
|
|
* USBD_ERR_NULL_PTR Null pointer passed to 'p_drv'/'p_buf'.
|
|
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'max_len'.
|
|
* USBD_ERR_DEV_INVALID_STATE State of the device is not USBD_DEV_STATE_NONE.
|
|
* USBD_ERR_ALLOC From 'DescBufErrPtr', in USBD_DescWrReq(), if
|
|
* function was called from a driver's context.
|
|
*
|
|
* - RETURNED BY USBD_StrDescSend() -
|
|
* See USBD_StrDescSend() for additional return error codes.
|
|
*
|
|
* Return(s) : Number of bytes actually in the descriptor, if NO error(s).
|
|
*
|
|
* 0, otherwise.
|
|
*
|
|
* Note(s) : (1) This function should be used by drivers supporting standard requests auto-reply,
|
|
* during the initialization process.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_DescStrGet (USBD_DRV *p_drv,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT08U max_len,
|
|
CPU_INT08U str_ix,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
CPU_INT08U desc_len;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0u);
|
|
}
|
|
|
|
if (p_drv == (USBD_DRV *)0) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (0u);
|
|
}
|
|
|
|
if (p_buf == (CPU_INT08U *)0) {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return (0u);
|
|
}
|
|
|
|
if (max_len == 0u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (0u);
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(p_drv->DevNbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return (0u);
|
|
}
|
|
|
|
if (p_dev->State != USBD_DEV_STATE_NONE) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return (0u);
|
|
}
|
|
|
|
p_dev->ActualBufPtr = p_buf;
|
|
p_dev->DescBufMaxLen = max_len;
|
|
p_dev->DescBufErrPtr = p_err;
|
|
|
|
USBD_StrDescSend(p_dev,
|
|
str_ix,
|
|
max_len,
|
|
p_err);
|
|
|
|
desc_len = p_dev->DescBufIx;
|
|
p_dev->DescBufErrPtr = (USBD_ERR *)0;
|
|
|
|
return (desc_len);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StrAdd()
|
|
*
|
|
* Description : Add string to USB device.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_str Pointer to string to add (see Note #1).
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE String successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
*
|
|
* -RETURNED BY USBD_StrDescAdd()-
|
|
* See USBD_StrDescAdd() for additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB spec 2.0 chapter 9.5 states "Where appropriate, descriptors contain references
|
|
* to string descriptors that provide displayable information describing a descriptor
|
|
* in human-readable form. The inclusion of string descriptors is optional. However,
|
|
* the reference fields within descriptors are mandatory. If a device does not support
|
|
* string descriptors, string reference fields must be reset to zero to indicate no
|
|
* string descriptor is available".
|
|
*
|
|
* Since string descriptors are optional, 'p_str' could be a NULL pointer.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_StrAdd ( CPU_INT08U dev_nbr,
|
|
const CPU_CHAR *p_str,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_NONE) && /* Chk curr dev state. */
|
|
(p_dev->State != USBD_DEV_STATE_INIT)) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return;
|
|
}
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
USBD_StrDescAdd(p_dev, p_str, p_err);
|
|
#else
|
|
(void)p_str;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StrIxGet()
|
|
*
|
|
* Description : Get string index corresponding to a given string.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_str Pointer to string.
|
|
*
|
|
* Return(s) : String index.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_StrIxGet ( CPU_INT08U dev_nbr,
|
|
const CPU_CHAR *p_str)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
CPU_INT08U str_ix;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return (0u);
|
|
}
|
|
|
|
str_ix = USBD_StrDescIxGet(p_dev, p_str);
|
|
|
|
return (str_ix);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWr08()
|
|
*
|
|
* Description : Write 8-bit value to the descriptor buffer.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* val 8-bit value to write in descriptor buffer.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB classes MAY used this function to append class-specific descriptors to the
|
|
* configuration descriptor.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_DescWr08 (CPU_INT08U dev_nbr,
|
|
CPU_INT08U val)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return;
|
|
}
|
|
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
USBD_DescWrReq(p_dev, &val, 1u);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWr16()
|
|
*
|
|
* Description : Write a 16-bit value to the descriptor buffer.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* val 16-bit value to write in descriptor buffer.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB classes MAY used this function to append class-specific descriptors to the
|
|
* configuration descriptor.
|
|
*
|
|
* (2) USB descriptors are in little-endian format.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_DescWr16 (CPU_INT08U dev_nbr,
|
|
CPU_INT16U val)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return;
|
|
}
|
|
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
CPU_INT08U buf[2u];
|
|
|
|
|
|
buf[0u] = (CPU_INT08U)( val & DEF_INT_08_MASK);
|
|
buf[1u] = (CPU_INT08U)((val >> 8u) & DEF_INT_08_MASK);
|
|
|
|
USBD_DescWrReq(p_dev, &buf[0u], 2u);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWr24()
|
|
*
|
|
* Description : Write a 24-bit value to the descriptor buffer.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* val 32-bit value containing 24 useful bits to write in descriptor buffer.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB classes MAY used this function to append class-specific descriptors to the
|
|
* configuration descriptor.
|
|
*
|
|
* (2) USB descriptors are in little-endian format.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_DescWr24 (CPU_INT08U dev_nbr,
|
|
CPU_INT32U val)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return;
|
|
}
|
|
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
CPU_INT08U buf[3u];
|
|
|
|
|
|
buf[0u] = (CPU_INT08U)( val & DEF_INT_08_MASK);
|
|
buf[1u] = (CPU_INT08U)((val >> 8u) & DEF_INT_08_MASK);
|
|
buf[2u] = (CPU_INT08U)((val >> 16u) & DEF_INT_08_MASK);
|
|
|
|
USBD_DescWrReq(p_dev, &buf[0u], 3u);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWr32()
|
|
*
|
|
* Description : Write a 32-bit value to the descriptor buffer.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* val 32-bit value to write in descriptor buffer.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB classes MAY used this function to append class-specific descriptors to the
|
|
* configuration descriptor.
|
|
*
|
|
* (2) USB descriptors are in little-endian format.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_DescWr32 (CPU_INT08U dev_nbr,
|
|
CPU_INT32U val)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return;
|
|
}
|
|
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
CPU_INT08U buf[4u];
|
|
|
|
|
|
buf[0u] = (CPU_INT08U)( val & DEF_INT_08_MASK);
|
|
buf[1u] = (CPU_INT08U)((val >> 8u ) & DEF_INT_08_MASK);
|
|
buf[2u] = (CPU_INT08U)((val >> 16u) & DEF_INT_08_MASK);
|
|
buf[3u] = (CPU_INT08U)((val >> 24u) & DEF_INT_08_MASK);
|
|
|
|
USBD_DescWrReq(p_dev, &buf[0u], 4u);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWr()
|
|
*
|
|
* Description : Write buffer into the descriptor buffer.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* p_buf Pointer to buffer to write into descriptor buffer.
|
|
*
|
|
* len Length of the buffer.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB classes MAY used this function to append class-specific descriptors to the
|
|
* configuration descriptor.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_DescWr ( CPU_INT08U dev_nbr,
|
|
const CPU_INT08U *p_buf,
|
|
CPU_INT16U len)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return;
|
|
}
|
|
|
|
if ((p_buf == (CPU_INT08U *)0) ||
|
|
(len == 0u)) {
|
|
return;
|
|
}
|
|
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
USBD_DescWrReq(p_dev, p_buf, len);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_BulkAdd()
|
|
*
|
|
* Description : Add a bulk endpoint to alternate setting interface.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* if_alt_nbr Interface alternate setting number.
|
|
*
|
|
* dir_in Endpoint direction.
|
|
* DEF_YES IN direction.
|
|
* DEF_NO OUT direction.
|
|
*
|
|
* max_pkt_len Endpoint maximum packet length (see Note #1)
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Bulk endpoint successfully added.
|
|
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'max_pkt_len'.
|
|
*
|
|
* ------- RETURNED BY USBD_EP_Add() : -------
|
|
* USBD_ERR_NONE Endpoint successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number.
|
|
* USBD_ERR_IF_ALT_INVALID_NBR Invalid alternate interface number.
|
|
* USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available.
|
|
* USBD_ERR_EP_ALLOC Endpoints NOT available.
|
|
*
|
|
* Return(s) : Endpoint address, if NO error(s).
|
|
*
|
|
* USBD_EP_ADDR_NONE, otherwise.
|
|
*
|
|
* Note(s) : (1) If the 'max_pkt_len' argument is '0' the stack will allocate the first available
|
|
* BULK endpoint regardless its maximum packet size.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_BulkAdd (CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U if_nbr,
|
|
CPU_INT08U if_alt_nbr,
|
|
CPU_BOOLEAN dir_in,
|
|
CPU_INT16U max_pkt_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_INT08U ep_addr;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED) /* USBD_CFG_NBR_SPD_BIT will always be clear in FS. */
|
|
if (((max_pkt_len != 0u) && /* Chk EP size. */
|
|
(max_pkt_len != 512u)) &&
|
|
(DEF_BIT_IS_SET(cfg_nbr, USBD_CFG_NBR_SPD_BIT))) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_ADDR_NONE);
|
|
}
|
|
#endif
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (((max_pkt_len != 0u) &&
|
|
(max_pkt_len != 8u) &&
|
|
(max_pkt_len != 16u) &&
|
|
(max_pkt_len != 32u) &&
|
|
(max_pkt_len != 64u)) &&
|
|
(DEF_BIT_IS_CLR(cfg_nbr, USBD_CFG_NBR_SPD_BIT) == DEF_YES)) {
|
|
#else
|
|
if ( (max_pkt_len != 0u) &&
|
|
(max_pkt_len != 8u) &&
|
|
(max_pkt_len != 16u) &&
|
|
(max_pkt_len != 32u) &&
|
|
(max_pkt_len != 64u)) {
|
|
#endif
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_ADDR_NONE);
|
|
}
|
|
#endif
|
|
|
|
ep_addr = USBD_EP_Add(dev_nbr,
|
|
cfg_nbr,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
USBD_EP_TYPE_BULK,
|
|
dir_in,
|
|
max_pkt_len,
|
|
0u,
|
|
p_err);
|
|
return (ep_addr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IntrAdd()
|
|
*
|
|
* Description : Add an interrupt endpoint to alternate setting interface.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* if_alt_nbr Interface alternate setting number.
|
|
*
|
|
* dir_in Endpoint Direction.
|
|
* DEF_YES IN direction.
|
|
* DEF_NO OUT direction.
|
|
*
|
|
* max_pkt_len Endpoint maximum packet length. (see note #1)
|
|
*
|
|
* interval Endpoint interval in frames or microframes.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Interrupt endpoint successfully added.
|
|
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'interval'/
|
|
* 'max_pkt_len'.
|
|
*
|
|
* ------- RETURNED BY USBD_EP_Add() : -------
|
|
* USBD_ERR_NONE Endpoint successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number.
|
|
* USBD_ERR_IF_ALT_INVALID_NBR Invalid alternate interface number.
|
|
* USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available.
|
|
* USBD_ERR_EP_ALLOC Endpoints NOT available.
|
|
*
|
|
* Return(s) : Endpoint address, if NO error(s).
|
|
*
|
|
* USBD_EP_ADDR_NONE, otherwise.
|
|
*
|
|
* Note(s) : (1) If the 'max_pkt_len' argument is '0' the stack will allocate the first available
|
|
* INTERRUPT endpoint regardless its maximum packet size.
|
|
*
|
|
* (2) For high-speed interrupt endpoints, bInterval value must be in the range
|
|
* from 1 to 16. The bInterval value is used as the exponent for a 2^(bInterval-1)
|
|
* value. Maximum polling interval value is 2^(16-1) = 32768 32768 microframes
|
|
* (i.e. 4096 frames) in high-speed.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_IntrAdd (CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U if_nbr,
|
|
CPU_INT08U if_alt_nbr,
|
|
CPU_BOOLEAN dir_in,
|
|
CPU_INT16U max_pkt_len,
|
|
CPU_INT16U interval,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_INT08U ep_addr;
|
|
CPU_INT08U interval_code;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
|
|
CPU_SW_EXCEPTION(0);
|
|
}
|
|
|
|
if (interval == 0u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
#endif
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED) /* USBD_CFG_NBR_SPD_BIT will always be clear in FS. */
|
|
/* Full spd validation. */
|
|
if (DEF_BIT_IS_CLR(cfg_nbr, USBD_CFG_NBR_SPD_BIT) == DEF_YES) {
|
|
#endif
|
|
if (max_pkt_len > 64u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
if (interval < 255u) {
|
|
interval_code = (CPU_INT08U)interval;
|
|
} else {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
} else { /* High spd validation. */
|
|
if (((if_alt_nbr == 0u) &&
|
|
(max_pkt_len > 64u)) ||
|
|
(max_pkt_len > 1024u)) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
if (interval > USBD_EP_MAX_INTERVAL_VAL) { /* See Note #2. */
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
if (MATH_IS_PWR2(interval) == DEF_NO) { /* Interval must be a power of 2. */
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
/* Compute bInterval exponent in 2^(bInterval-1). */
|
|
interval_code = (CPU_INT08U)(32u - CPU_CntLeadZeros32(interval));
|
|
|
|
if (interval_code > 16u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ep_addr = USBD_EP_Add(dev_nbr,
|
|
cfg_nbr,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
USBD_EP_TYPE_INTR,
|
|
dir_in,
|
|
max_pkt_len,
|
|
interval_code,
|
|
p_err);
|
|
return (ep_addr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IsocAdd()
|
|
*
|
|
* Description : Add an isochronous endpoint to alternate setting interface.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* if_alt_nbr Interface alternate setting number.
|
|
*
|
|
* dir_in Endpoint Direction :
|
|
* DEF_YES, IN direction.
|
|
* DEF_NO, OUT direction.
|
|
*
|
|
* attrib Isochronous endpoint synchronization and usage type attributes.
|
|
*
|
|
* max_pkt_len Endpoint maximum packet length (see Note #1).
|
|
*
|
|
* transaction_frame Endpoint transactions per (micro)frame (see Note #2).
|
|
*
|
|
* interval Endpoint interval in frames or microframes.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Interrupt endpoint successfully added.
|
|
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'attrib'/
|
|
* 'max_pkt_len'/'transaction_frame'/
|
|
* 'interval'.
|
|
*
|
|
* ------- RETURNED BY USBD_EP_Add() : -------
|
|
* USBD_ERR_NONE Endpoint successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number.
|
|
* USBD_ERR_IF_ALT_INVALID_NBR Invalid alternate interface number.
|
|
* USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available.
|
|
* USBD_ERR_EP_ALLOC Endpoints NOT available.
|
|
*
|
|
* Return(s) : Endpoint address, if NO error(s).
|
|
*
|
|
* USBD_EP_ADDR_NONE, otherwise.
|
|
*
|
|
* Note(s) : (1) If the 'max_pkt_len' argument is '0', the stack allocates the first available
|
|
* ISOCHRONOUS endpoint regardless of its maximum packet size.
|
|
*
|
|
* (2) For full-speed endpoints, 'transaction_frame' must be set to 1 since there is no
|
|
* support for high-bandwidth endpoints.
|
|
*
|
|
* (3) For full-/high-speed isochronous endpoints, bInterval value must be in the range
|
|
* from 1 to 16. The bInterval value is used as the exponent for a 2^(bInterval-1)
|
|
* value. Maximum polling interval value is 2^(16-1) = 32768 frames in full-speed and
|
|
* 32768 microframes (i.e. 4096 frames) in high-speed.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_EP_ISOC_EN == DEF_ENABLED)
|
|
CPU_INT08U USBD_IsocAdd (CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U if_nbr,
|
|
CPU_INT08U if_alt_nbr,
|
|
CPU_BOOLEAN dir_in,
|
|
CPU_INT08U attrib,
|
|
CPU_INT16U max_pkt_len,
|
|
CPU_INT08U transaction_frame,
|
|
CPU_INT16U interval,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_INT08U ep_addr;
|
|
CPU_INT16U pkt_len;
|
|
CPU_INT08U interval_code;
|
|
|
|
/* ---------------- VALIDATE ARGUMENTS ---------------- */
|
|
if ((if_alt_nbr == 0u) && /* Chk if dflt IF setting with isoc EP max_pkt_len > 0. */
|
|
(max_pkt_len > 0u)) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
/* Chk if sync & usage bits are used. */
|
|
if ((attrib & ~(USBD_EP_TYPE_SYNC_MASK | USBD_EP_TYPE_USAGE_MASK)) != 0u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED) /* USBD_CFG_NBR_SPD_BIT will always be clear in FS. */
|
|
/* Full spd validation. */
|
|
if (DEF_BIT_IS_CLR(cfg_nbr, USBD_CFG_NBR_SPD_BIT) == DEF_YES) {
|
|
#endif
|
|
if (max_pkt_len > 1023u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
if (transaction_frame != 1u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
} else { /* High spd validation. */
|
|
switch (transaction_frame) {
|
|
case 1u:
|
|
if (max_pkt_len > 1024u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
break;
|
|
|
|
|
|
case 2u:
|
|
if ((max_pkt_len < 513u) ||
|
|
(max_pkt_len > 1024u)) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
break;
|
|
|
|
case 3u:
|
|
if ((max_pkt_len < 683u) ||
|
|
(max_pkt_len > 1024u)) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Explicit feedback EP must be set to no sync. */
|
|
if (((attrib & USBD_EP_TYPE_USAGE_MASK) == USBD_EP_TYPE_USAGE_FEEDBACK) &&
|
|
((attrib & USBD_EP_TYPE_SYNC_MASK) != USBD_EP_TYPE_SYNC_NONE)) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
if (interval > USBD_EP_MAX_INTERVAL_VAL) { /* See Note #3. */
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
if (MATH_IS_PWR2(interval) == DEF_NO) { /* Interval must be a power of 2. */
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
/* Compute bInterval exponent in 2^(bInterval-1). */
|
|
interval_code = (CPU_INT08U)(32u - CPU_CntLeadZeros32(interval));
|
|
|
|
if (interval_code > 16u) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
pkt_len = (transaction_frame - 1u) << 11u |
|
|
max_pkt_len;
|
|
|
|
ep_addr = USBD_EP_Add(dev_nbr,
|
|
cfg_nbr,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
USBD_EP_TYPE_ISOC | attrib,
|
|
dir_in,
|
|
pkt_len,
|
|
interval_code,
|
|
p_err);
|
|
|
|
return (ep_addr);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IsocSyncRefreshSet()
|
|
*
|
|
* Description : Set synchronization feedback rate on synchronization isochronous endpoint.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* if_alt_nbr Interface alternate setting number.
|
|
*
|
|
* synch_ep_addr Synchronization endpoint address.
|
|
*
|
|
* sync_refresh Exponent of synchronization feedback rate (see Note #3).
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Interrupt endpoint successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state (see Note #1).
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number (see Note #2).
|
|
* USBD_ERR_IF_ALT_INVALID_NBR Invalid interface alternate setting number.
|
|
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
|
|
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
|
|
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'sync_refresh'.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Synchronization endpoints can ONLY be associated when the device is in the following
|
|
* states:
|
|
*
|
|
* USBD_DEV_STATE_NONE Device controller has not been initialized.
|
|
* USBD_DEV_STATE_INIT Device controller already initialized.
|
|
*
|
|
* (2) Interface class code must be USBD_CLASS_CODE_AUDIO and protocol 'zero', for audio
|
|
* class 1.0.
|
|
*
|
|
* (3) If explicit synchronization mechanism is needed to maintain synchronization during
|
|
* transfers, the information carried over the synchronization path must be available
|
|
* every 2 ^ (10 - P) frames, with P ranging from 1 to 9 (512 ms down to 2 ms).
|
|
*
|
|
* (4) Table 4-22 "Standard AS Isochronous Synch Endpoint Descriptor" of Audio 1.0
|
|
* specification indicates for bmAttributes field no usage type for bits 5..4. But
|
|
* USB 2.0 specification, Table 9-13 "Standard Endpoint Descriptor" indicates several
|
|
* types of usage. When an explicit feedback is defined for a asynchronous isochronous
|
|
* endpoint, the associated synch feedback should use the Usage type 'Feedback endpoint'.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_EP_ISOC_EN == DEF_ENABLED)
|
|
void USBD_IsocSyncRefreshSet (CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U if_nbr,
|
|
CPU_INT08U if_alt_nbr,
|
|
CPU_INT08U synch_ep_addr,
|
|
CPU_INT08U sync_refresh,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
USBD_EP_INFO *p_ep;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
CPU_INT32U ep_alloc_map;
|
|
#endif
|
|
CPU_INT08U ep_nbr;
|
|
CPU_BOOLEAN found;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
if ((sync_refresh < 1u) || /* See Note #3. */
|
|
(sync_refresh > 9u)) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return;
|
|
}
|
|
|
|
/* --------------- GET OBJECT REFERENCES -------------- */
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_NONE) && /* Chk curr dev state. */
|
|
(p_dev->State != USBD_DEV_STATE_INIT)) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return;
|
|
}
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr); /* Get cfg struct. */
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr); /* Get IF struct. */
|
|
if (p_if == (USBD_IF *)0) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if (p_if->ClassCode != USBD_CLASS_CODE_AUDIO) { /* Chk if audio class. */
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if (p_if->ClassProtocolCode != 0u) { /* Chk if audio class, version 1.0. */
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
p_if_alt = USBD_IF_AltRefGet(p_if, if_alt_nbr); /* Get IF alt setting struct. */
|
|
if (p_if_alt == (USBD_IF_ALT *)0) {
|
|
*p_err = USBD_ERR_IF_ALT_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
found = DEF_NO;
|
|
p_ep = (USBD_EP_INFO *)0;
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
ep_alloc_map = p_if_alt->EP_TblMap;
|
|
while ((ep_alloc_map != DEF_BIT_NONE) &&
|
|
(found != DEF_YES)) {
|
|
ep_nbr = (CPU_INT08U)CPU_CntTrailZeros32(ep_alloc_map);
|
|
p_ep = p_if_alt->EP_TblPtrs[ep_nbr];
|
|
|
|
if (p_ep->Addr == synch_ep_addr) {
|
|
found = DEF_YES;
|
|
}
|
|
|
|
DEF_BIT_CLR(ep_alloc_map, DEF_BIT32(ep_nbr));
|
|
}
|
|
#else
|
|
p_ep = p_if_alt->EP_HeadPtr;
|
|
|
|
for (ep_nbr = 0u; ep_nbr < p_if_alt->EP_NbrTotal; ep_nbr++) {
|
|
if (p_ep->Addr == synch_ep_addr) {
|
|
found = DEF_YES;
|
|
break;
|
|
}
|
|
|
|
p_ep = p_ep->NextPtr;
|
|
}
|
|
#endif
|
|
|
|
if (found != DEF_YES) {
|
|
*p_err = USBD_ERR_EP_INVALID_ADDR;
|
|
return;
|
|
}
|
|
/* Chk EP type attrib. */
|
|
if ((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_ISOC) {
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
|
|
switch (p_ep->Attrib & USBD_EP_TYPE_SYNC_MASK) { /* Chk EP sync type attrib. */
|
|
case USBD_EP_TYPE_SYNC_NONE:
|
|
break;
|
|
|
|
case USBD_EP_TYPE_SYNC_ASYNC:
|
|
case USBD_EP_TYPE_SYNC_ADAPTIVE:
|
|
case USBD_EP_TYPE_SYNC_SYNC:
|
|
default:
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
|
|
switch (p_ep->Attrib & USBD_EP_TYPE_USAGE_MASK) { /* Chk EP usage type attrib. */
|
|
case USBD_EP_TYPE_USAGE_FEEDBACK: /* See Note #4. */
|
|
break;
|
|
|
|
case USBD_EP_TYPE_USAGE_DATA:
|
|
case USBD_EP_TYPE_USAGE_IMPLICIT_FEEDBACK:
|
|
default:
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
|
|
if (p_ep->SyncAddr != 0u) { /* Chk associated sync EP addr. */
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_ep->SyncRefresh = sync_refresh;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IsocSyncAddrSet()
|
|
*
|
|
* Description : Associate synchronization endpoint to isochronous endpoint.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* if_alt_nbr Interface alternate setting number.
|
|
*
|
|
* data_ep_addr Data endpoint address.
|
|
*
|
|
* sync_addr Associated synchronization endpoint.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Interrupt endpoint successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Invalid device state (see Note #1).
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number (see Note #2).
|
|
* USBD_ERR_IF_ALT_INVALID_NBR Invalid interface alternate setting number.
|
|
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
|
|
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
|
|
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'sync_addr'.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Synchronization endpoints can ONLY be associated when the device is in the following
|
|
* states:
|
|
*
|
|
* USBD_DEV_STATE_NONE Device controller has not been initialized.
|
|
* USBD_DEV_STATE_INIT Device controller already initialized.
|
|
*
|
|
* (2) Interface class code must be USBD_CLASS_CODE_AUDIO and protocol 'zero', for audio
|
|
* class 1.0.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_EP_ISOC_EN == DEF_ENABLED)
|
|
void USBD_IsocSyncAddrSet (CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U if_nbr,
|
|
CPU_INT08U if_alt_nbr,
|
|
CPU_INT08U data_ep_addr,
|
|
CPU_INT08U sync_addr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
USBD_EP_INFO *p_ep;
|
|
USBD_EP_INFO *p_ep_isoc;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
CPU_INT32U ep_alloc_map;
|
|
#endif
|
|
CPU_INT08U ep_nbr;
|
|
CPU_BOOLEAN found_ep;
|
|
CPU_BOOLEAN found_sync;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
/* --------------- GET OBJECT REFERENCES -------------- */
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_NONE) && /* Chk curr dev state. */
|
|
(p_dev->State != USBD_DEV_STATE_INIT)) {
|
|
*p_err = USBD_ERR_DEV_INVALID_STATE;
|
|
return;
|
|
}
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr); /* Get cfg struct. */
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr); /* Get IF struct. */
|
|
if (p_if == (USBD_IF *)0) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if (p_if->ClassCode != USBD_CLASS_CODE_AUDIO) { /* Chk if audio class. */
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
if (p_if->ClassProtocolCode != 0u) { /* Chk if audio class, version 1.0. */
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
p_if_alt = USBD_IF_AltRefGet(p_if, if_alt_nbr); /* Get IF alt setting struct. */
|
|
if (p_if_alt == (USBD_IF_ALT *)0) {
|
|
*p_err = USBD_ERR_IF_ALT_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
found_ep = DEF_NO;
|
|
found_sync = DEF_NO;
|
|
p_ep_isoc = (USBD_EP_INFO *)0;
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
ep_alloc_map = p_if_alt->EP_TblMap;
|
|
while ((ep_alloc_map != DEF_BIT_NONE) &&
|
|
((found_ep != DEF_YES) ||
|
|
(found_sync != DEF_YES))) {
|
|
ep_nbr = (CPU_INT08U)CPU_CntTrailZeros32(ep_alloc_map);
|
|
p_ep = p_if_alt->EP_TblPtrs[ep_nbr];
|
|
|
|
if (p_ep->Addr == data_ep_addr) {
|
|
found_ep = DEF_YES;
|
|
p_ep_isoc = p_ep;
|
|
}
|
|
|
|
if (p_ep->Addr == sync_addr) {
|
|
found_sync = DEF_YES;
|
|
}
|
|
|
|
DEF_BIT_CLR(ep_alloc_map, DEF_BIT32(ep_nbr));
|
|
}
|
|
#else
|
|
p_ep = p_if_alt->EP_HeadPtr;
|
|
|
|
for (ep_nbr = 0u; ep_nbr < p_if_alt->EP_NbrTotal; ep_nbr++) {
|
|
if (p_ep->Addr == data_ep_addr) {
|
|
found_ep = DEF_YES;
|
|
p_ep_isoc = p_ep;
|
|
}
|
|
|
|
if (p_ep->Addr == sync_addr) {
|
|
found_sync = DEF_YES;
|
|
}
|
|
|
|
if ((found_ep == DEF_YES) &&
|
|
(found_sync == DEF_YES)) {
|
|
break;
|
|
}
|
|
|
|
p_ep = p_ep->NextPtr;
|
|
}
|
|
#endif
|
|
|
|
if (found_ep != DEF_YES) {
|
|
*p_err = USBD_ERR_EP_INVALID_ADDR;
|
|
return;
|
|
}
|
|
|
|
if (found_sync != DEF_YES) {
|
|
*p_err = USBD_ERR_INVALID_ARG;
|
|
return;
|
|
}
|
|
/* Chk EP type attrib. */
|
|
if ((p_ep_isoc->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_ISOC) {
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
|
|
switch (p_ep_isoc->Attrib & USBD_EP_TYPE_SYNC_MASK) { /* Chk EP sync type attrib. */
|
|
case USBD_EP_TYPE_SYNC_ASYNC:
|
|
if (USBD_EP_IS_IN(p_ep_isoc->Addr) == DEF_YES) {
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case USBD_EP_TYPE_SYNC_ADAPTIVE:
|
|
if (USBD_EP_IS_IN(p_ep_isoc->Addr) == DEF_NO) {
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case USBD_EP_TYPE_SYNC_NONE:
|
|
case USBD_EP_TYPE_SYNC_SYNC:
|
|
default:
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
|
|
switch (p_ep_isoc->Attrib & USBD_EP_TYPE_USAGE_MASK) { /* Chk EP usage type attrib. */
|
|
case USBD_EP_TYPE_USAGE_DATA:
|
|
break;
|
|
|
|
case USBD_EP_TYPE_USAGE_FEEDBACK:
|
|
case USBD_EP_TYPE_USAGE_IMPLICIT_FEEDBACK:
|
|
default:
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
|
|
if (p_ep_isoc->SyncRefresh != 0u) { /* Refresh interval must be set to zero. */
|
|
*p_err = USBD_ERR_EP_INVALID_TYPE;
|
|
return;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_ep_isoc->SyncAddr = sync_addr;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_EP_Add()
|
|
*
|
|
* Description : Add an endpoint to alternate setting interface.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* if_alt_nbr Interface alternate setting number.
|
|
*
|
|
* type Endpoint's attributes.
|
|
*
|
|
* dir_in Endpoint Direction.
|
|
*
|
|
* max_pkt_len Endpoint maximum packet size.
|
|
* ----------- Argument validated by the caller.
|
|
*
|
|
* interval Interval for polling data transfers.
|
|
* -------- Argument validated by the caller.
|
|
*
|
|
* class_desc Callback to append a class-specific descriptor in the configuration
|
|
* descriptor.
|
|
*
|
|
* class_desc_size Size of the class class-specific descriptor.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Endpoint successfully added.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number.
|
|
* USBD_ERR_IF_ALT_INVALID_NBR Invalid alternate interface number.
|
|
* USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available.
|
|
* USBD_ERR_EP_ALLOC Endpoints NOT available.
|
|
*
|
|
* Return(s) : Endpoint number, if NO error(s).
|
|
*
|
|
* USBD_EP_NBR_NONE, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_INT08U USBD_EP_Add (CPU_INT08U dev_nbr,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_INT08U if_nbr,
|
|
CPU_INT08U if_alt_nbr,
|
|
CPU_INT08U attrib,
|
|
CPU_BOOLEAN dir_in,
|
|
CPU_INT16U max_pkt_len,
|
|
CPU_INT08U interval,
|
|
USBD_ERR *p_err)
|
|
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
USBD_EP_INFO *p_ep;
|
|
CPU_INT08U ep_type;
|
|
CPU_INT32U ep_alloc_map;
|
|
CPU_INT32U ep_alloc_map_clr;
|
|
CPU_INT08U ep_nbr;
|
|
CPU_INT08U ep_phy_nbr;
|
|
USBD_DEV_SPD dev_spd;
|
|
CPU_BOOLEAN alloc;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
/* --------------- GET OBJECT REFERENCES -------------- */
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
*p_err = USBD_ERR_DEV_INVALID_NBR;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr); /* Get cfg struct. */
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr); /* Get IF struct. */
|
|
if (p_if == (USBD_IF *)0) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
p_if_alt = USBD_IF_AltRefGet(p_if, if_alt_nbr); /* Get IF alt setting struct. */
|
|
if (p_if_alt == (USBD_IF_ALT *)0) {
|
|
*p_err = USBD_ERR_IF_ALT_INVALID_NBR;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
if (p_if_alt->EP_NbrTotal >= USBD_CFG_MAX_NBR_EP_OPEN) {
|
|
*p_err = USBD_ERR_EP_NONE_AVAIL;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
ep_nbr = USBD_EP_InfoNbrNext;
|
|
if (ep_nbr >= USBD_CFG_MAX_NBR_EP_DESC) { /* Chk if EP is avail. */
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_EP_ALLOC;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
USBD_EP_InfoNbrNext++;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
ep_type = attrib & USBD_EP_TYPE_MASK;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (DEF_BIT_IS_SET(cfg_nbr, USBD_CFG_NBR_SPD_BIT) == DEF_YES) {
|
|
dev_spd = USBD_DEV_SPD_HIGH;
|
|
} else {
|
|
#endif
|
|
dev_spd = USBD_DEV_SPD_FULL;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
|
|
p_ep = &USBD_EP_InfoTbl[ep_nbr];
|
|
p_ep->Interval = interval;
|
|
p_ep->Attrib = attrib;
|
|
p_ep->SyncAddr = 0u; /* Dflt sync addr is zero. */
|
|
p_ep->SyncRefresh = 0u; /* Dflt feedback rate exponent is zero. */
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
ep_alloc_map = p_cfg->EP_AllocMap; /* Get cfg EP alloc bit map. */
|
|
DEF_BIT_CLR(ep_alloc_map, p_if->EP_AllocMap); /* Clr EP already alloc'd in the IF. */
|
|
DEF_BIT_SET(ep_alloc_map, p_if_alt->EP_AllocMap);
|
|
|
|
ep_alloc_map_clr = ep_alloc_map;
|
|
|
|
alloc = USBD_EP_Alloc(p_dev, /* Alloc physical EP. */
|
|
dev_spd,
|
|
ep_type,
|
|
dir_in,
|
|
max_pkt_len & 0x7FF, /* Mask out transactions per microframe. */
|
|
if_alt_nbr,
|
|
p_ep,
|
|
&ep_alloc_map);
|
|
if (alloc != DEF_OK) {
|
|
USBD_EP_InfoNbrNext--;
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_EP_NONE_AVAIL;
|
|
return (USBD_EP_NBR_NONE);
|
|
}
|
|
|
|
p_ep->MaxPktSize |= max_pkt_len & 0x1800; /* Set transactions per microframe. */
|
|
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(p_ep->Addr);
|
|
ep_phy_nbr++;
|
|
|
|
if (p_dev->EP_MaxPhyNbr < ep_phy_nbr) {
|
|
p_dev->EP_MaxPhyNbr = ep_phy_nbr;
|
|
}
|
|
|
|
p_if_alt->EP_AllocMap |= ep_alloc_map & ~ep_alloc_map_clr;
|
|
p_if->EP_AllocMap |= p_if_alt->EP_AllocMap;
|
|
p_cfg->EP_AllocMap |= p_if->EP_AllocMap;
|
|
|
|
p_if_alt->EP_NbrTotal++;
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
ep_nbr = USBD_EP_ADDR_TO_PHY(p_ep->Addr);
|
|
p_if_alt->EP_TblPtrs[ep_nbr] = p_ep;
|
|
DEF_BIT_SET(p_if_alt->EP_TblMap, DEF_BIT32(ep_nbr));
|
|
#else
|
|
p_ep->NextPtr = (USBD_EP_INFO *)0;
|
|
if (p_if_alt->EP_HeadPtr == (USBD_EP_INFO *)0) {
|
|
p_if_alt->EP_HeadPtr = p_ep;
|
|
p_if_alt->EP_TailPtr = p_ep;
|
|
} else {
|
|
p_if_alt->EP_TailPtr->NextPtr = p_ep;
|
|
p_if_alt->EP_TailPtr = p_ep;
|
|
}
|
|
#endif
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
|
|
return (p_ep->Addr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_EP_Alloc()
|
|
*
|
|
* Description : Allocate a physical endpoint from the device controller.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevAdd()' & 'USBD_EP_Add()'
|
|
*
|
|
* spd Endpoint speed.
|
|
*
|
|
* USBD_DEV_SPD_FULL Endpoint is full-speed.
|
|
* USBD_DEV_SPD_HIGH Endpoint is high-speed.
|
|
*
|
|
* type Endpoint type.
|
|
* USBD_EP_TYPE_CTRL Control endpoint.
|
|
* USBD_EP_TYPE_ISOC Isochronous endpoint.
|
|
* USBD_EP_TYPE_BULK Bulk endpoint.
|
|
* USBD_EP_TYPE_INTR Interrupt endpoint.
|
|
*
|
|
* dir_in Endpoint direction.
|
|
* DEF_YES IN endpoint.
|
|
* DEF_NO OUT endpoint.
|
|
*
|
|
* max_pkt_len Endpoint maximum packet size length.
|
|
*
|
|
* if_alt_nbr Alternate interface number containing the endpoint.
|
|
*
|
|
* p_ep Pointer to variable that will receive the endpoint parameters.
|
|
* ---- Argument validated in 'USBD_DevAdd()' & 'USBD_EP_Add()'
|
|
*
|
|
* p_alloc_bit_map Pointer to allocation table bit-map.
|
|
* --------------- Argument validated in 'USBD_DevAdd()' & 'USBD_EP_Add()'
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) 'Universal Serial Bus Specification, Revision 2.0, April 27, 2000' Section 5.5.3
|
|
*
|
|
* "An endpoint for control transfers specifies the maximum data payload size that
|
|
* the endpoint can accept from or transmit to the bus. The allowable maximum control
|
|
* transfer data payload sizes for full-speed devices is 8, 16, 32, or 64 bytes; for
|
|
* high-speed devices, it is 64 bytes and for low-speed devices, it is 8 bytes."
|
|
*
|
|
* "All Host Controllers are required to have support for 8-, 16-, 32-, and 64-byte
|
|
* maximum data payload sizes for full-speed control endpoints, only 8-byte maximum
|
|
* data payload sizes for low-speed control endpoints, and only 64-byte maximum data
|
|
* payload size for high-speed control endpoints"
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN USBD_EP_Alloc (USBD_DEV *p_dev,
|
|
USBD_DEV_SPD spd,
|
|
CPU_INT08U type,
|
|
CPU_BOOLEAN dir_in,
|
|
CPU_INT16U max_pkt_len,
|
|
CPU_INT08U if_alt_nbr,
|
|
USBD_EP_INFO *p_ep,
|
|
CPU_INT32U *p_alloc_bit_map)
|
|
{
|
|
USBD_DRV_EP_INFO *p_ep_tbl;
|
|
USBD_DRV *p_drv;
|
|
CPU_INT08U ep_tbl_ix;
|
|
CPU_INT08U ep_attrib;
|
|
CPU_INT08U ep_attrib_srch;
|
|
CPU_INT08U ep_max_pkt_bits;
|
|
CPU_INT16U ep_max_pkt;
|
|
CPU_BOOLEAN ep_found;
|
|
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_DISABLED)
|
|
(void)spd;
|
|
(void)if_alt_nbr;
|
|
#endif
|
|
|
|
if (dir_in == DEF_YES) {
|
|
ep_attrib_srch = USBD_EP_INFO_DIR_IN;
|
|
} else {
|
|
ep_attrib_srch = USBD_EP_INFO_DIR_OUT;
|
|
}
|
|
|
|
switch (type) {
|
|
case USBD_EP_TYPE_CTRL:
|
|
DEF_BIT_SET(ep_attrib_srch, USBD_EP_INFO_TYPE_CTRL);
|
|
break;
|
|
|
|
case USBD_EP_TYPE_ISOC:
|
|
DEF_BIT_SET(ep_attrib_srch, USBD_EP_INFO_TYPE_ISOC);
|
|
break;
|
|
|
|
case USBD_EP_TYPE_BULK:
|
|
DEF_BIT_SET(ep_attrib_srch, USBD_EP_INFO_TYPE_BULK);
|
|
break;
|
|
|
|
case USBD_EP_TYPE_INTR:
|
|
DEF_BIT_SET(ep_attrib_srch, USBD_EP_INFO_TYPE_INTR);
|
|
break;
|
|
|
|
default:
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
p_drv = &p_dev->Drv;
|
|
p_ep_tbl = p_drv->CfgPtr->EP_InfoTbl; /* Get ctrl EP info tbl. */
|
|
ep_attrib = p_ep_tbl->Attrib; /* Get attrib for first entry. */
|
|
ep_tbl_ix = 0u;
|
|
ep_found = DEF_NO;
|
|
|
|
while ((ep_attrib != DEF_BIT_NONE) && /* Search until last entry or EP found. */
|
|
(ep_found == DEF_NO)) {
|
|
/* Chk if EP not alloc'd and EP attrib match req'd ... */
|
|
/* ... attrib. */
|
|
if ((DEF_BIT_IS_CLR(*p_alloc_bit_map, DEF_BIT32(ep_tbl_ix)) == DEF_YES) &&
|
|
(DEF_BIT_IS_SET(ep_attrib, ep_attrib_srch) == DEF_YES)) {
|
|
|
|
ep_max_pkt = p_ep_tbl[ep_tbl_ix].MaxPktSize;
|
|
|
|
switch (type) {
|
|
case USBD_EP_TYPE_CTRL: /* Chk ctrl transfer pkt size constrains. */
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, 64u);
|
|
/* Get next power of 2. */
|
|
ep_max_pkt_bits = (CPU_INT08U)(31u - CPU_CntLeadZeros32(ep_max_pkt));
|
|
ep_max_pkt = DEF_BIT16(ep_max_pkt_bits);
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if ((spd == USBD_DEV_SPD_HIGH) &&
|
|
(ep_max_pkt != 64u)) {
|
|
break;
|
|
}
|
|
|
|
if ((spd == USBD_DEV_SPD_HIGH) &&
|
|
(ep_max_pkt < 8u)) {
|
|
break;
|
|
}
|
|
#endif
|
|
ep_found = DEF_YES;
|
|
break;
|
|
|
|
case USBD_EP_TYPE_BULK:
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
/* Max pkt size is 512 for bulk EP in HS. */
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, 512u);
|
|
if ((spd == USBD_DEV_SPD_HIGH) &&
|
|
(ep_max_pkt == 512u)) {
|
|
ep_found = DEF_YES;
|
|
break;
|
|
}
|
|
#endif
|
|
/* Max pkt size is 64 for bulk EP in FS. */
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, 64u);
|
|
ep_max_pkt_bits = (CPU_INT08U)(31u - CPU_CntLeadZeros32(ep_max_pkt));
|
|
ep_max_pkt = DEF_BIT16(ep_max_pkt_bits);
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if ((spd == USBD_DEV_SPD_HIGH) &&
|
|
(ep_max_pkt >= 8u)) {
|
|
break;
|
|
}
|
|
#endif
|
|
ep_found = DEF_YES;
|
|
break;
|
|
|
|
case USBD_EP_TYPE_ISOC:
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (spd == USBD_DEV_SPD_HIGH) {
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, 1024u);
|
|
} else {
|
|
#endif
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, 1023u);
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
|
|
if (max_pkt_len > 0u) {
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, max_pkt_len);
|
|
}
|
|
|
|
ep_found = DEF_YES;
|
|
break;
|
|
|
|
case USBD_EP_TYPE_INTR:
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if ((spd == USBD_DEV_SPD_HIGH) &&
|
|
(if_alt_nbr != 0u)) { /* Dflt IF intr EP max pkt size limited to 64. */
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, 1024u);
|
|
} else {
|
|
#endif
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, 64u);
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
if (max_pkt_len > 0u) {
|
|
ep_max_pkt = DEF_MIN(ep_max_pkt, max_pkt_len);
|
|
}
|
|
|
|
ep_found = DEF_YES;
|
|
break;
|
|
|
|
default:
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
if ((ep_found == DEF_YES) &&
|
|
((max_pkt_len == ep_max_pkt) ||
|
|
(max_pkt_len == 0u))) {
|
|
p_ep->MaxPktSize = ep_max_pkt;
|
|
DEF_BIT_SET(*p_alloc_bit_map, DEF_BIT32(ep_tbl_ix));
|
|
p_ep->Addr = p_ep_tbl[ep_tbl_ix].Nbr;
|
|
if (dir_in == DEF_TRUE) {
|
|
p_ep->Addr |= USBD_EP_DIR_IN; /* Add dir bit (IN EP). */
|
|
}
|
|
} else {
|
|
ep_found = DEF_NO;
|
|
ep_tbl_ix++;
|
|
ep_attrib = p_ep_tbl[ep_tbl_ix].Attrib;
|
|
}
|
|
} else {
|
|
ep_tbl_ix++;
|
|
ep_attrib = p_ep_tbl[ep_tbl_ix].Attrib;
|
|
}
|
|
}
|
|
|
|
if (ep_found == DEF_NO) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
return (DEF_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_EP_MaxPhyNbrGet()
|
|
*
|
|
* Description : Get the maximum physical endpoint number.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* Return(s) : Maximum physical endpoint number, if NO error(s).
|
|
*
|
|
* USBD_EP_PHY_NONE, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
CPU_INT08U USBD_EP_MaxPhyNbrGet (CPU_INT08U dev_nbr)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
CPU_INT08U ep_phy_nbr;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return (USBD_EP_PHY_NONE);
|
|
}
|
|
|
|
if (p_dev->EP_MaxPhyNbr == 0u) {
|
|
ep_phy_nbr = USBD_EP_PHY_NONE;
|
|
} else {
|
|
ep_phy_nbr = p_dev->EP_MaxPhyNbr - 1u;
|
|
}
|
|
|
|
return (ep_phy_nbr);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_EventConn()
|
|
* USBD_EventDisconn()
|
|
* USBD_EventReset()
|
|
* USBD_EventHS()
|
|
* USBD_EventSuspend()
|
|
* USBD_EventResume()
|
|
*
|
|
* Description : Notify USB bus events to the device stack.
|
|
*
|
|
* Argument(s) : p_drv Pointer to device driver.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB device driver should call the following functions for each bus related events:
|
|
*
|
|
* (a) Bus connection USBD_EventConn().
|
|
* (b) Bus disconnection USBD_EventDisconn().
|
|
* (c) Bus reset USBD_EventReset().
|
|
* (d) Bus HS detection USBD_EventHS().
|
|
* (e) Bus suspend USBD_EventSuspend().
|
|
* (f) Bus resume USBD_EventResume().
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_EventConn (USBD_DRV *p_drv)
|
|
{
|
|
USBD_EventSet(p_drv, USBD_EVENT_BUS_CONN);
|
|
}
|
|
|
|
|
|
void USBD_EventDisconn (USBD_DRV *p_drv)
|
|
{
|
|
USBD_EventSet(p_drv, USBD_EVENT_BUS_DISCONN);
|
|
}
|
|
|
|
|
|
void USBD_EventHS (USBD_DRV *p_drv)
|
|
{
|
|
USBD_EventSet(p_drv, USBD_EVENT_BUS_HS);
|
|
}
|
|
|
|
|
|
void USBD_EventReset (USBD_DRV *p_drv)
|
|
{
|
|
USBD_EventSet(p_drv, USBD_EVENT_BUS_RESET);
|
|
}
|
|
|
|
void USBD_EventSuspend (USBD_DRV *p_drv)
|
|
{
|
|
USBD_EventSet(p_drv, USBD_EVENT_BUS_SUSPEND);
|
|
}
|
|
|
|
void USBD_EventResume (USBD_DRV *p_drv)
|
|
{
|
|
USBD_EventSet(p_drv, USBD_EVENT_BUS_RESUME);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_EventSetup()
|
|
*
|
|
* Description : Send a USB setup event to the core task.
|
|
*
|
|
* Argument(s) : p_drv Pointer to device driver.
|
|
*
|
|
* p_buf Pointer to the setup packet.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_EventSetup (USBD_DRV *p_drv,
|
|
void *p_buf)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_CORE_EVENT *p_core_event;
|
|
CPU_INT08U *p_buf_08;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (p_buf == (void *)0) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
p_dev = USBD_DevRefGet(p_drv->DevNbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return;
|
|
}
|
|
|
|
p_core_event = USBD_CoreEventGet(); /* Get core event struct. */
|
|
if (p_core_event == (USBD_CORE_EVENT *)0) {
|
|
return;
|
|
}
|
|
|
|
USBD_DBG_CORE_STD("Setup Pkt");
|
|
|
|
p_buf_08 = (CPU_INT08U *)p_buf;
|
|
p_dev->SetupReqNext.bmRequestType = p_buf_08[0u];
|
|
p_dev->SetupReqNext.bRequest = p_buf_08[1u];
|
|
p_dev->SetupReqNext.wValue = MEM_VAL_GET_INT16U_LITTLE(p_buf_08 + 2u);
|
|
p_dev->SetupReqNext.wIndex = MEM_VAL_GET_INT16U_LITTLE(p_buf_08 + 4u);
|
|
p_dev->SetupReqNext.wLength = MEM_VAL_GET_INT16U_LITTLE(p_buf_08 + 6u);
|
|
|
|
p_core_event->Type = USBD_EVENT_SETUP;
|
|
p_core_event->DrvPtr = p_drv;
|
|
p_core_event->Err = USBD_ERR_NONE;
|
|
|
|
USBD_OS_CoreEventPut(p_core_event);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* INTERNAL FUNCTIONS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_EventEP()
|
|
*
|
|
* Description : Send a USB endpoint event to the core task.
|
|
*
|
|
* Argument(s) : p_drv Pointer to device driver.
|
|
*
|
|
* ep_addr Endpoint address.
|
|
*
|
|
* err Error code returned by the USB device driver.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_EventEP (USBD_DRV *p_drv,
|
|
CPU_INT08U ep_addr,
|
|
USBD_ERR err)
|
|
{
|
|
USBD_CORE_EVENT *p_core_event;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (p_drv == (USBD_DRV *)0) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
p_core_event = USBD_CoreEventGet(); /* Get core event struct. */
|
|
if (p_core_event == (USBD_CORE_EVENT *)0) {
|
|
return;
|
|
}
|
|
|
|
p_core_event->Type = USBD_EVENT_EP;
|
|
p_core_event->DrvPtr = p_drv;
|
|
p_core_event->EP_Addr = ep_addr;
|
|
p_core_event->Err = err;
|
|
|
|
USBD_OS_CoreEventPut(p_core_event); /* Queue core event. */
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DrvRefGet()
|
|
*
|
|
* Description : Get a reference to the device driver structure.
|
|
*
|
|
* Argument(s) : dev_nbr Device number
|
|
*
|
|
* Return(s) : Pointer to device driver structure, if NO error(s).
|
|
*
|
|
* Pointer to NULL , otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
USBD_DRV *USBD_DrvRefGet (CPU_INT08U dev_nbr)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
USBD_DRV *p_drv;
|
|
|
|
|
|
p_dev = USBD_DevRefGet(dev_nbr); /* Get dev struct. */
|
|
if (p_dev == (USBD_DEV *)0) {
|
|
return ((USBD_DRV *)0);
|
|
}
|
|
|
|
p_drv = &p_dev->Drv;
|
|
|
|
return (p_drv);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL FUNCTIONS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqHandler()
|
|
*
|
|
* Description : Standard request process.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_StdReqHandler (USBD_DEV *p_dev)
|
|
{
|
|
CPU_INT08U recipient;
|
|
CPU_INT08U type;
|
|
CPU_INT08U request;
|
|
CPU_BOOLEAN valid;
|
|
CPU_BOOLEAN dev_to_host;
|
|
USBD_ERR err;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER(); /* Copy setup request. */
|
|
p_dev->SetupReq.bmRequestType = p_dev->SetupReqNext.bmRequestType;
|
|
p_dev->SetupReq.bRequest = p_dev->SetupReqNext.bRequest;
|
|
p_dev->SetupReq.wValue = p_dev->SetupReqNext.wValue;
|
|
p_dev->SetupReq.wIndex = p_dev->SetupReqNext.wIndex;
|
|
p_dev->SetupReq.wLength = p_dev->SetupReqNext.wLength;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
|
|
recipient = p_dev->SetupReq.bmRequestType & USBD_REQ_RECIPIENT_MASK;
|
|
type = p_dev->SetupReq.bmRequestType & USBD_REQ_TYPE_MASK;
|
|
request = p_dev->SetupReq.bRequest;
|
|
dev_to_host = DEF_BIT_IS_SET(p_dev->SetupReq.bmRequestType, USBD_REQ_DIR_BIT);
|
|
valid = DEF_FAIL;
|
|
|
|
switch (type) {
|
|
case USBD_REQ_TYPE_STANDARD:
|
|
switch (recipient) { /* Select req recipient: */
|
|
case USBD_REQ_RECIPIENT_DEVICE: /* Device. */
|
|
valid = USBD_StdReqDev(p_dev, request);
|
|
break;
|
|
|
|
case USBD_REQ_RECIPIENT_INTERFACE: /* Interface. */
|
|
valid = USBD_StdReqIF(p_dev, request);
|
|
break;
|
|
|
|
case USBD_REQ_RECIPIENT_ENDPOINT: /* Endpoint. */
|
|
valid = USBD_StdReqEP(p_dev, request);
|
|
break;
|
|
|
|
case USBD_REQ_RECIPIENT_OTHER: /* Not supported. */
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_REQ_TYPE_CLASS: /* Class-specific req. */
|
|
switch (recipient) {
|
|
case USBD_REQ_RECIPIENT_INTERFACE:
|
|
case USBD_REQ_RECIPIENT_ENDPOINT:
|
|
valid = USBD_StdReqClass(p_dev); /* Class-specific req. */
|
|
break;
|
|
|
|
|
|
case USBD_REQ_RECIPIENT_DEVICE:
|
|
case USBD_REQ_RECIPIENT_OTHER:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_REQ_TYPE_VENDOR:
|
|
switch (recipient) {
|
|
case USBD_REQ_RECIPIENT_INTERFACE:
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
if (request == p_dev->StrMS_VendorCode) {
|
|
err = USBD_ERR_NONE;
|
|
p_dev->DescBufErrPtr = &err;
|
|
valid = USBD_StdReqIF_MS(p_dev); /* Microsoft OS descriptor req. */
|
|
p_dev->DescBufErrPtr = (USBD_ERR *)0;
|
|
} else {
|
|
valid = USBD_StdReqVendor(p_dev); /* Vendor-specific req. */
|
|
}
|
|
break;
|
|
#endif
|
|
case USBD_REQ_RECIPIENT_ENDPOINT:
|
|
valid = USBD_StdReqVendor(p_dev); /* Vendor-specific req. */
|
|
break;
|
|
|
|
case USBD_REQ_RECIPIENT_DEVICE:
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
if (request == p_dev->StrMS_VendorCode) {
|
|
err = USBD_ERR_NONE;
|
|
p_dev->DescBufErrPtr = &err;
|
|
valid = USBD_StdReqDevMS(p_dev); /* Microsoft OS descriptor req. */
|
|
p_dev->DescBufErrPtr = (USBD_ERR *)0;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case USBD_REQ_RECIPIENT_OTHER:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_REQ_TYPE_RESERVED:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (valid == DEF_FAIL) {
|
|
USBD_DBG_CORE_STD("Request Error");
|
|
USBD_CtrlStall(p_dev->Nbr, &err);
|
|
|
|
} else {
|
|
|
|
if (dev_to_host == DEF_YES) {
|
|
USBD_DBG_CORE_STD("Rx Status");
|
|
USBD_CtrlRxStatus(p_dev->Nbr, USBD_CFG_CTRL_REQ_TIMEOUT_mS, &err);
|
|
} else {
|
|
USBD_DBG_CORE_STD("Tx Status");
|
|
USBD_CtrlTxStatus(p_dev->Nbr, USBD_CFG_CTRL_REQ_TIMEOUT_mS, &err);
|
|
|
|
if ((type == USBD_REQ_TYPE_STANDARD) &&
|
|
(recipient == USBD_REQ_RECIPIENT_DEVICE) &&
|
|
(request == USBD_REQ_SET_ADDRESS) &&
|
|
(p_dev->Drv.API_Ptr->AddrEn != (void*)0)) {
|
|
p_dev->Drv.API_Ptr->AddrEn(&p_dev->Drv, p_dev->Addr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqDev()
|
|
*
|
|
* Description : Process device standard request.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* request USB device request.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : (1) USB Spec 2.0, section 9.4.6 specifies the format of the SET_ADDRESS request. The
|
|
* SET_ADDRESS sets the device address for all future device access.
|
|
*
|
|
* (a) The 'wValue' filed specify the device address to use for all subsequent accesses.
|
|
*
|
|
* (b) If the specified device address is greater than 127 or if 'wIndex' or 'wLength'
|
|
* are non-zero, the behavior of the device is not specified.
|
|
*
|
|
* (c) If the device is in the default state and the address specified is non-zero,
|
|
* the device shall enter the device address, otherwise the device remains in the
|
|
* default state' (this is not an error condition).
|
|
*
|
|
* (d) If the device is in the address state and the address specified is zero, then
|
|
* the device shall enter the default state otherwise, the device remains in
|
|
* the address state but uses the newly-specified address.
|
|
*
|
|
* (e) Device behavior when the SET_ADDRESS request is received while the device is not
|
|
* in the default or address state is not specified.
|
|
*
|
|
* (f) USB Spec 2.0, section 9.2.6.3 specifies the maximum timeout for the SET_ADDRESS
|
|
* request:
|
|
*
|
|
* "After the reset/resume recovery interval, if a device receives a SetAddress()
|
|
* request, the device must be able to complete processing of the request and be
|
|
* able to successfully complete the Status stage of the request within 50 ms. In
|
|
* the case of the SetAddress() request, the Status stage successfully completes
|
|
* when the device sends the zero-length Status packet or when the device sees
|
|
* the ACK in response to the Status stage data packet."
|
|
*
|
|
* (2) USB Spec 2.0, section 9.4.7 specifies the format of the SET_CONFIGURATION request.
|
|
*
|
|
* (a) The lower byte of 'wValue' field specifies the desired configuration.
|
|
*
|
|
* (b) If 'wIndex', 'wLength', or the upper byte of wValue is non-zero, then the behavior
|
|
* of this request is not specified.
|
|
*
|
|
* (c) The configuration value must be zero or match a configuration value from a
|
|
* configuration value from a configuration descriptor. If the configuration value
|
|
* is zero, the device is place in its address state.
|
|
*
|
|
* (d) Device behavior when this request is received while the device is in the Default
|
|
* state is not specified.
|
|
*
|
|
* (e) If device is in address state and the specified configuration value is zero,
|
|
* then the device remains in the Address state. If the specified configuration value
|
|
* matches the configuration value from a configuration descriptor, then that
|
|
* configuration is selected and the device enters the Configured state. Otherwise,
|
|
* the device responds with a Request Error.
|
|
*
|
|
* (f) If the specified configuration value is zero, then the device enters the Address
|
|
* state. If the specified configuration value matches the configuration value from a
|
|
* configuration descriptor, then that configuration is selected and the device
|
|
* remains in the Configured state. Otherwise, the device responds with a Request
|
|
* Error.
|
|
*
|
|
* (3) USB Spec 2.0, section 9.4.2 specifies the format of the GET_CONFIGURATION request.
|
|
*
|
|
* (a) If 'wValue' or 'wIndex' are non-zero or 'wLength' is not '1', then the device
|
|
* behavior is not specified.
|
|
*
|
|
* (b) If the device is in default state, the device behavior is not specified.
|
|
*
|
|
* (c) In address state a value of zero MUST be returned.
|
|
*
|
|
* (d) In configured state, the non-zero bConfigurationValue of the current configuration
|
|
* must be returned.
|
|
*
|
|
* (4) USB Spec 2.0, section 9.4.5 specifies the format of the GET_STATUS request.
|
|
*
|
|
* (a) If 'wValue' is non-zero or 'wLength is not equal to '2', or if wIndex is non-zero
|
|
* then the behavior of the device is not specified.
|
|
*
|
|
* (b) USB Spec 2, 0, figure 9-4 shows the format of information returned by the device
|
|
* for a GET_STATUS request.
|
|
*
|
|
* +====|====|====|====|====|====|====|========|=========+
|
|
* | D0 | D1 | D2 | D3 | D4 | D3 | D2 | D1 | D0 |
|
|
* |----------------------------------|--------|---------|
|
|
* | RESERVED (RESET TO ZERO) | Remote | Self |
|
|
* | | Wakeup | Powered |
|
|
* +==================================|========|=========+
|
|
*
|
|
* (1) The Self Powered field indicates whether the device is currently self-powered.
|
|
* If D0 is reset to zero, the device is bus-powered. If D0 is set to one, the
|
|
* device is self-powered. The Self Powered field may not be changed by the
|
|
* SetFeature() or ClearFeature() requests.
|
|
*
|
|
* (2) The Remote Wakeup field indicates whether the device is currently enabled to
|
|
* request remote wakeup. The default mode for devices that support remote wakeup
|
|
* is disabled. If D1 is reset to zero, the ability of the device to signal
|
|
* remote wakeup is disabled. If D1 is set to one, the ability of the device to
|
|
* signal remote wakeup is enabled. The Remote Wakeup field can be modified by
|
|
* the SetFeature() and ClearFeature() requests using the DEVICE_REMOTE_WAKEUP
|
|
* feature selector. This field is reset to zero when the device is reset.
|
|
*
|
|
* (5) USB Spec 2.0, section 9.4.1/9.4.9 specifies the format of the CLEAR_FEATURE/SET_FEATURE
|
|
* request.
|
|
*
|
|
* (a) If 'wLength' or 'wIndex' are non-zero, then the device behavior is not specified.
|
|
*
|
|
* (b) The device CLEAR_FEATURE request is only valid when the device is in the
|
|
* configured state.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN USBD_StdReqDev (USBD_DEV *p_dev,
|
|
CPU_INT08U request)
|
|
{
|
|
USBD_DRV *p_drv;
|
|
USBD_DRV_API *p_drv_api;
|
|
CPU_INT08U cfg_nbr;
|
|
CPU_BOOLEAN valid;
|
|
CPU_BOOLEAN addr_set;
|
|
CPU_BOOLEAN dev_to_host;
|
|
CPU_INT08U dev_addr;
|
|
USBD_ERR err;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqDevNbr);
|
|
|
|
dev_to_host = DEF_BIT_IS_SET(p_dev->SetupReq.bmRequestType, USBD_REQ_DIR_BIT);
|
|
valid = DEF_FAIL;
|
|
|
|
switch (request) {
|
|
case USBD_REQ_GET_DESCRIPTOR: /* ------------------ GET DESCRIPTOR ------------------ */
|
|
if (dev_to_host != DEF_YES) {
|
|
break;
|
|
}
|
|
|
|
valid = USBD_StdReqDescGet(p_dev);
|
|
break;
|
|
|
|
|
|
case USBD_REQ_SET_ADDRESS: /* -------------------- SET ADDRESS ------------------- */
|
|
if (dev_to_host != DEF_NO) {
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqSetAddrNbr);
|
|
|
|
dev_addr = (CPU_INT08U)(p_dev->SetupReq.wValue & /* Get dev addr (see Note #1a). */
|
|
DEF_INT_08_MASK);
|
|
|
|
USBD_DBG_CORE_STD_ARG(" Set Address", dev_addr);
|
|
|
|
if ((dev_addr > 127u) || /* Validate request values. (see Note #1b). */
|
|
(p_dev->SetupReq.wIndex != 0u) ||
|
|
(p_dev->SetupReq.wLength != 0u)) {
|
|
break;
|
|
}
|
|
|
|
p_drv = &p_dev->Drv;
|
|
p_drv_api = p_dev->Drv.API_Ptr;
|
|
switch (p_dev->State) {
|
|
case USBD_DEV_STATE_DEFAULT:
|
|
if (dev_addr > 0u) { /* See Note #1c. */
|
|
if (p_drv_api->AddrSet != (void*)0) {
|
|
addr_set = p_drv_api->AddrSet(p_drv, dev_addr);
|
|
if (addr_set == DEF_FAIL) {
|
|
USBD_DBG_CORE_STD_ERR(" Set Address", USBD_ERR_FAIL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER(); /* Set dev in addressed state. */
|
|
p_dev->Addr = dev_addr;
|
|
p_dev->State = USBD_DEV_STATE_ADDRESSED;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_ADDRESSED: /* See Note #1c. */
|
|
if (dev_addr == 0u) { /* If dev addr is zero ... */
|
|
/* ... set addr in dev drv. */
|
|
if (p_drv_api->AddrSet != (void*)0) {
|
|
addr_set = p_drv_api->AddrSet(p_drv, 0u);
|
|
if (addr_set == DEF_FAIL) {
|
|
USBD_DBG_CORE_STD_ERR(" Set Address", USBD_ERR_FAIL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER(); /* Dev enters default state. */
|
|
p_dev->Addr = 0u;
|
|
p_dev->State = USBD_DEV_STATE_DEFAULT;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
valid = DEF_OK;
|
|
|
|
} else {
|
|
/* ... remains in addressed state and set new addr. */
|
|
if (p_drv_api->AddrSet != (void*)0) {
|
|
addr_set = p_drv_api->AddrSet(p_drv, dev_addr);
|
|
if (addr_set == DEF_FAIL) {
|
|
USBD_DBG_CORE_STD_ERR(" Set Address", USBD_ERR_FAIL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->Addr = dev_addr;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_NONE:
|
|
case USBD_DEV_STATE_INIT:
|
|
case USBD_DEV_STATE_ATTACHED:
|
|
case USBD_DEV_STATE_CONFIGURED:
|
|
case USBD_DEV_STATE_SUSPENDED:
|
|
default:
|
|
USBD_DBG_CORE_STD_ERR(" Set Address", USBD_ERR_DEV_INVALID_STATE);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_REQ_SET_CONFIGURATION: /* ----------------- SET CONFIGURATION ---------------- */
|
|
if (dev_to_host != DEF_NO) {
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqSetCfgNbr);
|
|
|
|
if (((p_dev->SetupReq.wValue & 0xFF00u) != 0u) && /* Validate request values (see Note #2b). */
|
|
(p_dev->SetupReq.wIndex != 0u) &&
|
|
(p_dev->SetupReq.wLength != 0u)) {
|
|
break;
|
|
}
|
|
/* Get cfg value. */
|
|
cfg_nbr = (CPU_INT08U)(p_dev->SetupReq.wValue & DEF_INT_08_MASK);
|
|
USBD_DBG_CORE_STD_ARG(" Set Configuration", cfg_nbr);
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
/* Cfg value MUST exists. */
|
|
if ((cfg_nbr > p_dev->CfgHS_TotalNbr) &&
|
|
(p_dev->Spd == USBD_DEV_SPD_HIGH)) {
|
|
USBD_DBG_CORE_STD_ERR(" Set Configuration", USBD_ERR_CFG_INVALID_NBR);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if ((cfg_nbr > p_dev->CfgFS_TotalNbr) &&
|
|
(p_dev->Spd == USBD_DEV_SPD_FULL)) {
|
|
USBD_DBG_CORE_STD_ERR(" Set Configuration", USBD_ERR_CFG_INVALID_NBR);
|
|
break;
|
|
}
|
|
|
|
switch (p_dev->State) {
|
|
case USBD_DEV_STATE_ADDRESSED: /* See Note #2e. */
|
|
if (cfg_nbr > 0u) {
|
|
USBD_CfgOpen(p_dev, /* Open cfg. */
|
|
(cfg_nbr - 1u),
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
USBD_DBG_CORE_STD_ERR(" Set Configuration", err);
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
} else {
|
|
valid = DEF_OK; /* Remain in addressed state. */
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_CONFIGURED: /* See Note #2f. */
|
|
if (cfg_nbr > 0u) {
|
|
if (p_dev->CfgCurNbr == (cfg_nbr - 1u)) {
|
|
valid = DEF_OK;
|
|
break;
|
|
}
|
|
|
|
USBD_CfgClose(p_dev); /* Close curr cfg. */
|
|
|
|
USBD_CfgOpen(p_dev, /* Open cfg. */
|
|
(cfg_nbr - 1u),
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
USBD_DBG_CORE_STD_ERR(" Set Configuration", err);
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
|
|
} else {
|
|
USBD_CfgClose(p_dev); /* Close curr cfg. */
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->State = USBD_DEV_STATE_ADDRESSED;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_NONE:
|
|
case USBD_DEV_STATE_INIT:
|
|
case USBD_DEV_STATE_ATTACHED:
|
|
case USBD_DEV_STATE_DEFAULT:
|
|
case USBD_DEV_STATE_SUSPENDED:
|
|
default:
|
|
USBD_DBG_CORE_STD_ERR(" Set Configuration", USBD_ERR_DEV_INVALID_STATE);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_REQ_GET_CONFIGURATION: /* ----------------- GET CONFIGURATION ---------------- */
|
|
if (dev_to_host != DEF_YES) {
|
|
break;
|
|
}
|
|
|
|
if ((p_dev->SetupReq.wLength != 1u) && /* Validate request values (see Note #3a). */
|
|
(p_dev->SetupReq.wIndex != 0u) &&
|
|
(p_dev->SetupReq.wValue != 0u)) {
|
|
break;
|
|
}
|
|
|
|
switch (p_dev->State) {
|
|
case USBD_DEV_STATE_ADDRESSED: /* See Note #3b. */
|
|
cfg_nbr = 0u;
|
|
USBD_DBG_CORE_STD_ARG(" Get Configuration", cfg_nbr);
|
|
|
|
p_dev->CtrlStatusBufPtr[0u] = cfg_nbr; /* Uses Ctrl status buf to follow USB mem alignment. */
|
|
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
(void *)&p_dev->CtrlStatusBufPtr[0u],
|
|
1u,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_CONFIGURED: /* See Note #3c. */
|
|
if (p_dev->CfgCurPtr == (USBD_CFG *)0) {
|
|
break;
|
|
}
|
|
|
|
cfg_nbr = p_dev->CfgCurNbr + 1u;
|
|
USBD_DBG_CORE_STD_ARG(" Get Configuration", cfg_nbr);
|
|
|
|
p_dev->CtrlStatusBufPtr[0u] = cfg_nbr; /* Uses Ctrl status buf to follow USB mem alignment. */
|
|
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
(void *)&p_dev->CtrlStatusBufPtr[0u],
|
|
1u,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_NONE:
|
|
case USBD_DEV_STATE_INIT:
|
|
case USBD_DEV_STATE_ATTACHED:
|
|
case USBD_DEV_STATE_DEFAULT:
|
|
case USBD_DEV_STATE_SUSPENDED:
|
|
default:
|
|
USBD_DBG_CORE_STD_ERR(" Get Configuration", USBD_ERR_DEV_INVALID_STATE);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_REQ_GET_STATUS: /* -------------------- GET STATUS -------------------- */
|
|
if (dev_to_host != DEF_YES) {
|
|
break;
|
|
}
|
|
|
|
if ((p_dev->SetupReq.wLength != 2u) && /* Validate request values (see Note #4a). */
|
|
(p_dev->SetupReq.wIndex != 0u) &&
|
|
(p_dev->SetupReq.wValue != 0u)) {
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_CORE_STD(" Get Status (Device)");
|
|
p_dev->CtrlStatusBufPtr[0u] = DEF_BIT_NONE;
|
|
p_dev->CtrlStatusBufPtr[1u] = DEF_BIT_NONE;
|
|
|
|
switch (p_dev->State) {
|
|
case USBD_DEV_STATE_ADDRESSED: /* See Note #4b. */
|
|
if (p_dev->SelfPwr == DEF_YES) {
|
|
p_dev->CtrlStatusBufPtr[0u] |= DEF_BIT_00;
|
|
}
|
|
if (p_dev->RemoteWakeup == DEF_ENABLED) {
|
|
p_dev->CtrlStatusBufPtr[0u] |= DEF_BIT_01;
|
|
}
|
|
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
(void *)&p_dev->CtrlStatusBufPtr[0u],
|
|
2u,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_CONFIGURED:
|
|
if (p_dev->CfgCurPtr != (USBD_CFG *)0) {
|
|
if (DEF_BIT_IS_SET(p_dev->CfgCurPtr->Attrib, USBD_DEV_ATTRIB_SELF_POWERED)) {
|
|
p_dev->CtrlStatusBufPtr[0u] |= DEF_BIT_00;
|
|
}
|
|
if (DEF_BIT_IS_SET(p_dev->CfgCurPtr->Attrib, USBD_DEV_ATTRIB_REMOTE_WAKEUP)) {
|
|
p_dev->CtrlStatusBufPtr[0u] |= DEF_BIT_01;
|
|
}
|
|
}
|
|
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
(void *)&p_dev->CtrlStatusBufPtr[0u],
|
|
2u,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_NONE:
|
|
case USBD_DEV_STATE_INIT:
|
|
case USBD_DEV_STATE_ATTACHED:
|
|
case USBD_DEV_STATE_DEFAULT:
|
|
case USBD_DEV_STATE_SUSPENDED:
|
|
default:
|
|
USBD_DBG_CORE_STD_ERR(" Get Status (Device)", USBD_ERR_DEV_INVALID_STATE);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_REQ_CLEAR_FEATURE: /* ----------------- SET/CLEAR FEATURE ---------------- */
|
|
case USBD_REQ_SET_FEATURE:
|
|
if (dev_to_host != DEF_NO) {
|
|
break;
|
|
}
|
|
|
|
if ((p_dev->SetupReq.wLength != 0u) && /* Validate request values. */
|
|
(p_dev->SetupReq.wIndex != 0u)) {
|
|
break;
|
|
}
|
|
|
|
if (request == USBD_REQ_CLEAR_FEATURE) {
|
|
USBD_DBG_CORE_STD(" Clear Feature (Device)");
|
|
} else {
|
|
USBD_DBG_CORE_STD(" Set Feature (Device)");
|
|
}
|
|
|
|
switch (p_dev->State) {
|
|
case USBD_DEV_STATE_CONFIGURED:
|
|
if (p_dev->CfgCurPtr == (USBD_CFG *)0) {
|
|
break;
|
|
}
|
|
|
|
if ((p_dev->SetupReq.wValue == USBD_FEATURE_SEL_DEVICE_REMOTE_WAKEUP) &&
|
|
(DEF_BIT_IS_SET(p_dev->CfgCurPtr->Attrib, USBD_DEV_ATTRIB_REMOTE_WAKEUP))) {
|
|
p_dev->RemoteWakeup = (request == USBD_REQ_CLEAR_FEATURE) ? DEF_DISABLED : DEF_ENABLED;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_NONE:
|
|
case USBD_DEV_STATE_INIT:
|
|
case USBD_DEV_STATE_ATTACHED:
|
|
case USBD_DEV_STATE_DEFAULT:
|
|
case USBD_DEV_STATE_ADDRESSED:
|
|
case USBD_DEV_STATE_SUSPENDED:
|
|
default:
|
|
if (request == USBD_REQ_CLEAR_FEATURE) {
|
|
USBD_DBG_CORE_STD_ERR(" Clear Feature (Device)", USBD_ERR_DEV_INVALID_STATE);
|
|
} else {
|
|
USBD_DBG_CORE_STD_ERR(" Set Feature (Device)", USBD_ERR_DEV_INVALID_STATE);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_STATS_DEV_INC_IF_TRUE(p_dev->Nbr, StdReqDevStallNbr, (valid == DEF_FAIL));
|
|
|
|
return (valid);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqIF()
|
|
*
|
|
* Description : Process device standard request (Interface).
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* request USB device request.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : (1) USB Spec 2.0, section 9.4.10 specifies the format of the SET_INTERFACE request.
|
|
*
|
|
* This request allows the host to select an alternate setting for the specified
|
|
* interface:
|
|
*
|
|
* (a) Some USB devices have configurations with interfaces that have mutually
|
|
* exclusive settings. This request allows the host to select the desired
|
|
* alternate setting. If a device only supports a default setting for the
|
|
* specified interface, then a STALL may be returned in the Status stage of
|
|
* the request. This request cannot be used to change the set of configured
|
|
* interfaces (the SetConfiguration() request must be used instead).
|
|
*
|
|
* (2) USB Spec 2.0, section 9.4.4 specifies the format of the GET_INTERFACE request.
|
|
* This request returns the selected alternate setting for the specified interface.
|
|
*
|
|
* (a) If 'wValue' is non-zero or 'wLength' is not '1', then the device behavior is
|
|
* not specified.
|
|
*
|
|
* (b) The GET_INTERFACE request is only valid when the device is in the configured
|
|
* state.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN USBD_StdReqIF (USBD_DEV *p_dev,
|
|
CPU_INT08U request)
|
|
{
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
USBD_CLASS_DRV *p_class_drv;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U if_alt_nbr;
|
|
CPU_BOOLEAN valid;
|
|
CPU_BOOLEAN dev_to_host;
|
|
CPU_INT16U req_len;
|
|
USBD_ERR err;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqIF_Nbr);
|
|
|
|
p_cfg = p_dev->CfgCurPtr;
|
|
if (p_cfg == (USBD_CFG *) 0) {
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqIF_StallNbr);
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
if_nbr = (CPU_INT08U)(p_dev->SetupReq.wIndex & DEF_INT_08_MASK);
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
if (p_if == (USBD_IF *)0) {
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqIF_StallNbr);
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
dev_to_host = DEF_BIT_IS_SET(p_dev->SetupReq.bmRequestType, USBD_REQ_DIR_BIT);
|
|
valid = DEF_FAIL;
|
|
|
|
switch (request) {
|
|
case USBD_REQ_GET_STATUS:
|
|
if (dev_to_host != DEF_YES) {
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_CORE_STD_ARG(" Get Status (Interface) IF ", if_nbr);
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_ADDRESSED) &&
|
|
(p_dev->State != USBD_DEV_STATE_CONFIGURED)) {
|
|
break;
|
|
}
|
|
|
|
if ((p_dev->State == USBD_DEV_STATE_ADDRESSED) &&
|
|
(if_nbr != 0u)) {
|
|
break;
|
|
}
|
|
|
|
p_dev->CtrlStatusBufPtr[0u] = DEF_BIT_NONE;
|
|
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
(void *)&p_dev->CtrlStatusBufPtr[0u],
|
|
1u,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_REQ_CLEAR_FEATURE:
|
|
case USBD_REQ_SET_FEATURE:
|
|
if (dev_to_host != DEF_NO) {
|
|
break;
|
|
}
|
|
|
|
if (request == USBD_REQ_CLEAR_FEATURE) {
|
|
USBD_DBG_CORE_STD_ARG(" Clear Feature (Interface)", if_nbr);
|
|
} else {
|
|
USBD_DBG_CORE_STD_ARG(" Set Feature (Interface)", if_nbr);
|
|
}
|
|
|
|
if ((p_dev->State != USBD_DEV_STATE_ADDRESSED) &&
|
|
(p_dev->State != USBD_DEV_STATE_CONFIGURED)) {
|
|
break;
|
|
}
|
|
|
|
if ((p_dev->State == USBD_DEV_STATE_ADDRESSED) &&
|
|
(if_nbr != 0u)) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_REQ_GET_DESCRIPTOR:
|
|
if (dev_to_host != DEF_YES) {
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_CORE_STD_ARG(" Get Descriptor (Interface) IF ", if_nbr);
|
|
|
|
p_class_drv = p_if->ClassDrvPtr;
|
|
if (p_class_drv->IF_Req == (void *)0) {
|
|
break;
|
|
}
|
|
|
|
req_len = p_dev->SetupReq.wLength;
|
|
USBD_DescWrStart(p_dev, req_len);
|
|
|
|
err = USBD_ERR_NONE;
|
|
p_dev->DescBufErrPtr = &err;
|
|
|
|
valid = p_class_drv->IF_Req(p_dev->Nbr,
|
|
&p_dev->SetupReq,
|
|
p_if->ClassArgPtr);
|
|
if (valid == DEF_OK) {
|
|
USBD_DescWrStop(p_dev, &err);
|
|
if (err != USBD_ERR_NONE) {
|
|
valid = DEF_FAIL;
|
|
}
|
|
}
|
|
p_dev->DescBufErrPtr = (USBD_ERR *)0;
|
|
break;
|
|
|
|
|
|
case USBD_REQ_GET_INTERFACE:
|
|
if (dev_to_host != DEF_YES) {
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_CORE_STD_ARG(" Get Interface IF ", if_nbr);
|
|
|
|
if (p_dev->State != USBD_DEV_STATE_CONFIGURED) {
|
|
break;
|
|
}
|
|
|
|
p_dev->CtrlStatusBufPtr[0u] = p_if->AltCur;
|
|
|
|
USBD_DBG_CORE_STD_ARG(" Alt", p_if->AltCur);
|
|
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
(void *)&p_dev->CtrlStatusBufPtr[0u],
|
|
1u,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_REQ_SET_INTERFACE:
|
|
if (dev_to_host != DEF_NO) {
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_CORE_STD_ARG(" Set Interface IF ", if_nbr);
|
|
|
|
if (p_dev->State != USBD_DEV_STATE_CONFIGURED) {
|
|
break;
|
|
}
|
|
/* Get IF alt setting nbr. */
|
|
if_alt_nbr = (CPU_INT08U)(p_dev->SetupReq.wValue & DEF_INT_08_MASK);
|
|
p_if_alt = USBD_IF_AltRefGet(p_if, if_alt_nbr);
|
|
|
|
USBD_DBG_CORE_STD_ARG(" Alt", if_alt_nbr);
|
|
|
|
if (p_if_alt == (USBD_IF_ALT *)0) {
|
|
USBD_DBG_CORE_STD_ERR(" Set Interface", USBD_ERR_IF_ALT_INVALID_NBR);
|
|
break;
|
|
}
|
|
|
|
if (p_if_alt == p_if->AltCurPtr) { /* If alt setting is the same as the cur one,... */
|
|
valid = DEF_OK; /* ...no further processing is needed. */
|
|
break;
|
|
}
|
|
|
|
USBD_IF_AltClose(p_dev, p_if->AltCurPtr); /* Close the cur alt setting. */
|
|
|
|
USBD_IF_AltOpen(p_dev, if_nbr, p_if_alt, &err); /* Open the new alt setting. */
|
|
if (err != USBD_ERR_NONE) { /* Re-open curr IF alt setting, in case it fails. */
|
|
USBD_IF_AltOpen(p_dev, p_if->AltCur, p_if->AltCurPtr, &err);
|
|
break;
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER(); /* Set IF alt setting. */
|
|
p_if->AltCurPtr = p_if_alt;
|
|
p_if->AltCur = if_alt_nbr;
|
|
CPU_CRITICAL_EXIT();
|
|
/* Notify class that IF or alt IF has been updated. */
|
|
if (p_if->ClassDrvPtr->AltSettingUpdate != (void *)0) {
|
|
p_if->ClassDrvPtr->AltSettingUpdate(p_dev->Nbr,
|
|
p_dev->CfgCurNbr,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
p_if->ClassArgPtr,
|
|
p_if_alt->ClassArgPtr);
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
default:
|
|
p_class_drv = p_if->ClassDrvPtr;
|
|
if (p_class_drv->IF_Req == (void *)0) {
|
|
break;
|
|
}
|
|
|
|
valid = p_class_drv->IF_Req(p_dev->Nbr,
|
|
&p_dev->SetupReq,
|
|
p_if->ClassArgPtr);
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_STATS_DEV_INC_IF_TRUE(p_dev->Nbr, StdReqIF_StallNbr, (valid == DEF_FAIL));
|
|
|
|
return (valid);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqEP()
|
|
*
|
|
* Description : Process device standard request (Endpoint).
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* request USB device request.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN USBD_StdReqEP (const USBD_DEV *p_dev,
|
|
CPU_INT08U request)
|
|
{
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_alt_if;
|
|
CPU_BOOLEAN ep_is_stall;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U ep_addr;
|
|
CPU_INT08U ep_phy_nbr;
|
|
CPU_BOOLEAN valid;
|
|
CPU_BOOLEAN dev_to_host;
|
|
CPU_INT08U feature;
|
|
USBD_ERR err;
|
|
|
|
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqEP_Nbr);
|
|
|
|
ep_addr = (CPU_INT08U)(p_dev->SetupReq.wIndex & DEF_INT_08_MASK);
|
|
feature = (CPU_INT08U)(p_dev->SetupReq.wValue & DEF_INT_08_MASK);
|
|
dev_to_host = DEF_BIT_IS_SET(p_dev->SetupReq.bmRequestType, USBD_REQ_DIR_BIT);
|
|
valid = DEF_FAIL;
|
|
|
|
switch (request) {
|
|
case USBD_REQ_CLEAR_FEATURE:
|
|
case USBD_REQ_SET_FEATURE:
|
|
if (dev_to_host != DEF_NO) {
|
|
break;
|
|
}
|
|
|
|
switch (p_dev->State) {
|
|
case USBD_DEV_STATE_ADDRESSED:
|
|
if (((ep_addr == 0x80u) ||
|
|
(ep_addr == 0x00u)) &&
|
|
(feature == USBD_FEATURE_SEL_ENDPOINT_HALT)) {
|
|
if (request == USBD_REQ_CLEAR_FEATURE) {
|
|
USBD_DBG_CORE_STD_ARG(" Clear Feature (EP)(STALL)", USBD_EP_ADDR_TO_LOG(ep_addr));
|
|
|
|
USBD_EP_Stall(p_dev->Nbr,
|
|
ep_addr,
|
|
DEF_CLR,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
} else {
|
|
USBD_DBG_CORE_STD_ARG(" Set Feature (EP)(STALL)", USBD_EP_ADDR_TO_LOG(ep_addr));
|
|
|
|
USBD_EP_Stall(p_dev->Nbr,
|
|
ep_addr,
|
|
DEF_SET,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_CONFIGURED:
|
|
if (feature == USBD_FEATURE_SEL_ENDPOINT_HALT) {
|
|
if (request == USBD_REQ_CLEAR_FEATURE) {
|
|
USBD_DBG_CORE_STD_ARG(" Clear Feature (EP)(STALL)", USBD_EP_ADDR_TO_LOG(ep_addr));
|
|
|
|
USBD_EP_Stall(p_dev->Nbr,
|
|
ep_addr,
|
|
DEF_CLR,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
} else {
|
|
USBD_DBG_CORE_STD_ARG(" Set Feature (EP)(STALL)", USBD_EP_ADDR_TO_LOG(ep_addr));
|
|
|
|
USBD_EP_Stall(p_dev->Nbr,
|
|
ep_addr,
|
|
DEF_SET,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
|
|
if_nbr = p_dev->EP_IF_Tbl[ep_phy_nbr];
|
|
p_if = USBD_IF_RefGet(p_dev->CfgCurPtr, if_nbr);
|
|
p_alt_if = p_if->AltCurPtr;
|
|
|
|
/* Notify class that EP state has been updated. */
|
|
if (p_if->ClassDrvPtr->EP_StateUpdate != (void *)0) {
|
|
p_if->ClassDrvPtr->EP_StateUpdate(p_dev->Nbr,
|
|
p_dev->CfgCurNbr,
|
|
if_nbr,
|
|
p_if->AltCur,
|
|
ep_addr,
|
|
p_if->ClassArgPtr,
|
|
p_alt_if->ClassArgPtr);
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_NONE:
|
|
case USBD_DEV_STATE_INIT:
|
|
case USBD_DEV_STATE_ATTACHED:
|
|
case USBD_DEV_STATE_DEFAULT:
|
|
case USBD_DEV_STATE_SUSPENDED:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_REQ_GET_STATUS:
|
|
if (dev_to_host != DEF_YES) {
|
|
break;
|
|
}
|
|
|
|
p_dev->CtrlStatusBufPtr[0u] = DEF_BIT_NONE;
|
|
p_dev->CtrlStatusBufPtr[1u] = DEF_BIT_NONE;
|
|
|
|
switch (p_dev->State) {
|
|
case USBD_DEV_STATE_ADDRESSED:
|
|
if ((ep_addr == 0x80u) ||
|
|
(ep_addr == 0x00u)) {
|
|
USBD_DBG_CORE_STD_ARG(" Get Status (EP)(STALL)", USBD_EP_ADDR_TO_LOG(ep_addr));
|
|
ep_is_stall = USBD_EP_IsStalled(p_dev->Nbr, ep_addr, &err);
|
|
if (ep_is_stall == DEF_TRUE) {
|
|
p_dev->CtrlStatusBufPtr[0u] = DEF_BIT_00;
|
|
p_dev->CtrlStatusBufPtr[1u] = DEF_BIT_NONE;
|
|
}
|
|
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
(void *)&p_dev->CtrlStatusBufPtr[0u],
|
|
2u,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_CONFIGURED:
|
|
USBD_DBG_CORE_STD_ARG(" Get Status (EP)(STALL)", USBD_EP_ADDR_TO_LOG(ep_addr));
|
|
ep_is_stall = USBD_EP_IsStalled(p_dev->Nbr, ep_addr, &err);
|
|
if (ep_is_stall == DEF_TRUE) {
|
|
p_dev->CtrlStatusBufPtr[0u] = DEF_BIT_00;
|
|
p_dev->CtrlStatusBufPtr[1u] = DEF_BIT_NONE;
|
|
}
|
|
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
(void *)&p_dev->CtrlStatusBufPtr[0],
|
|
2u,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
valid = DEF_OK;
|
|
break;
|
|
|
|
|
|
case USBD_DEV_STATE_NONE:
|
|
case USBD_DEV_STATE_INIT:
|
|
case USBD_DEV_STATE_ATTACHED:
|
|
case USBD_DEV_STATE_DEFAULT:
|
|
case USBD_DEV_STATE_SUSPENDED:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
USBD_DBG_STATS_DEV_INC_IF_TRUE(p_dev->Nbr, StdReqEP_StallNbr, (valid == DEF_FAIL));
|
|
|
|
return (valid);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqClass()
|
|
*
|
|
* Description : Class standard request handler.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN USBD_StdReqClass (const USBD_DEV *p_dev)
|
|
{
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_CLASS_DRV *p_class_drv;
|
|
CPU_INT08U recipient;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U ep_addr;
|
|
CPU_INT08U ep_phy_nbr;
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqClassNbr);
|
|
|
|
p_cfg = p_dev->CfgCurPtr;
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
recipient = p_dev->SetupReq.bmRequestType & USBD_REQ_RECIPIENT_MASK;
|
|
|
|
if (recipient == USBD_REQ_RECIPIENT_INTERFACE) {
|
|
if_nbr = (CPU_INT08U)(p_dev->SetupReq.wIndex & DEF_INT_08_MASK);
|
|
} else {
|
|
ep_addr = (CPU_INT08U)(p_dev->SetupReq.wIndex & DEF_INT_08_MASK);
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
|
|
if_nbr = p_dev->EP_IF_Tbl[ep_phy_nbr];
|
|
}
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
if (p_if == (USBD_IF *)0) {
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqClassStallNbr);
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
p_class_drv = p_if->ClassDrvPtr;
|
|
if (p_class_drv->ClassReq == (void *)0) {
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, StdReqClassStallNbr);
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
valid = p_class_drv->ClassReq(p_dev->Nbr,
|
|
&p_dev->SetupReq,
|
|
p_if->ClassArgPtr);
|
|
|
|
USBD_DBG_STATS_DEV_INC_IF_TRUE(p_dev->Nbr, StdReqClassStallNbr, (valid == DEF_FAIL));
|
|
|
|
return (valid);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqVendor()
|
|
*
|
|
* Description : Vendor standard request handler.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN USBD_StdReqVendor (const USBD_DEV *p_dev)
|
|
{
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_CLASS_DRV *p_class_drv;
|
|
CPU_INT08U recipient;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U ep_addr;
|
|
CPU_INT08U ep_phy_nbr;
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
p_cfg = p_dev->CfgCurPtr;
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
recipient = p_dev->SetupReq.bmRequestType & USBD_REQ_RECIPIENT_MASK;
|
|
|
|
if (recipient == USBD_REQ_RECIPIENT_INTERFACE) {
|
|
if_nbr = (CPU_INT08U)(p_dev->SetupReq.wIndex & DEF_INT_08_MASK);
|
|
} else {
|
|
ep_addr = (CPU_INT08U)(p_dev->SetupReq.wIndex & DEF_INT_08_MASK);
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
|
|
if_nbr = p_dev->EP_IF_Tbl[ep_phy_nbr];
|
|
}
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
if (p_if == (USBD_IF *)0) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
p_class_drv = p_if->ClassDrvPtr;
|
|
if (p_class_drv->VendorReq == (void *)0) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
valid = p_class_drv->VendorReq(p_dev->Nbr,
|
|
&p_dev->SetupReq,
|
|
p_if->ClassArgPtr);
|
|
|
|
return (valid);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqDevMS()
|
|
*
|
|
* Description : Microsoft descriptor request handler (when recipient is device).
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : (1) For more information on Microsoft OS decriptors, see
|
|
* 'http://msdn.microsoft.com/en-us/library/windows/hardware/gg463179.aspx'.
|
|
*
|
|
* (2) Page feature is not supported so Microsoft OS descriptors have their length limited
|
|
* to 64Kbytes.
|
|
*********************************************************************************************************
|
|
*/
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
static CPU_BOOLEAN USBD_StdReqDevMS (const USBD_DEV *p_dev)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U max_if;
|
|
CPU_INT08U if_ix;
|
|
CPU_INT08U compat_id_ix;
|
|
CPU_INT08U subcompat_id_ix;
|
|
CPU_INT08U section_cnt;
|
|
CPU_INT16U feature;
|
|
CPU_INT16U len;
|
|
CPU_INT08U cfg_nbr = 0u;
|
|
CPU_INT32U desc_len;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_CLASS_DRV *p_class_drv;
|
|
|
|
|
|
valid = DEF_FAIL;
|
|
feature = p_dev->SetupReq.wIndex;
|
|
if_nbr = (CPU_INT08U)(p_dev->SetupReq.wValue & DEF_INT_08_MASK);
|
|
len = p_dev->SetupReq.wLength;
|
|
|
|
/* Use 1st cfg as Microsoft doesn't specify cfg in ... */
|
|
/* ... setup pkt. */
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (p_dev->Spd == USBD_DEV_SPD_HIGH) {
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr | USBD_CFG_NBR_SPD_BIT);
|
|
} else {
|
|
#endif
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr);
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
switch (feature) {
|
|
case USBD_MS_OS_FEATURE_COMPAT_ID: /* See note (1). */
|
|
/* ----------------- SEND DESC HEADER ----------------- */
|
|
/* Compute length of descriptor. */
|
|
desc_len = USBD_MS_OS_DESC_COMPAT_ID_HDR_LEN;
|
|
section_cnt = 0u;
|
|
if (if_nbr == 0u) { /* If req IF == 0, sends all dev compat IDs. */
|
|
max_if = p_cfg->IF_NbrTotal;
|
|
} else {
|
|
max_if = if_nbr + 1u;
|
|
}
|
|
|
|
for (if_ix = if_nbr; if_ix < max_if; if_ix++) {
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_ix);
|
|
p_class_drv = p_if->ClassDrvPtr;
|
|
if (p_class_drv->MS_GetCompatID != DEF_NULL) {
|
|
|
|
compat_id_ix = p_class_drv->MS_GetCompatID( p_dev->Nbr,
|
|
&subcompat_id_ix,
|
|
p_if->ClassArgPtr);
|
|
if (compat_id_ix != USBD_MS_OS_COMPAT_ID_NONE) {
|
|
desc_len += USBD_MS_OS_DESC_COMPAT_ID_SECTION_LEN;
|
|
section_cnt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
USBD_DescWrStart((USBD_DEV *)p_dev, desc_len); /* Wr desc hdr. */
|
|
|
|
USBD_DescWr32(p_dev->Nbr, desc_len);
|
|
USBD_DescWr16(p_dev->Nbr, USBD_MS_OS_DESC_VER_1_0);
|
|
USBD_DescWr16(p_dev->Nbr, feature);
|
|
USBD_DescWr08(p_dev->Nbr, section_cnt);
|
|
USBD_DescWr32(p_dev->Nbr, 0u); /* Add 7 null bytes (reserved). */
|
|
USBD_DescWr16(p_dev->Nbr, 0u);
|
|
USBD_DescWr08(p_dev->Nbr, 0u);
|
|
|
|
|
|
/* ---------------- SEND DESC SECTIONS ---------------- */
|
|
if (len != USBD_MS_OS_DESC_COMPAT_ID_HDR_VER_1_0) {/* If req len = version, only send desc hdr. */
|
|
|
|
for (if_ix = if_nbr; if_ix < max_if; if_ix++) {
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_ix);
|
|
if (p_if->ClassDrvPtr->MS_GetCompatID != DEF_NULL) {
|
|
|
|
compat_id_ix = p_if->ClassDrvPtr->MS_GetCompatID( p_dev->Nbr,
|
|
&subcompat_id_ix,
|
|
p_if->ClassArgPtr);
|
|
if (compat_id_ix != USBD_MS_OS_COMPAT_ID_NONE) {
|
|
USBD_DescWr08(p_dev->Nbr, if_ix);
|
|
USBD_DescWr08(p_dev->Nbr, 0x01u);
|
|
|
|
USBD_DescWr( p_dev->Nbr,
|
|
(CPU_INT08U *)USBD_MS_CompatID[compat_id_ix],
|
|
8u);
|
|
|
|
USBD_DescWr( p_dev->Nbr,
|
|
(CPU_INT08U *)USBD_MS_SubCompatID[subcompat_id_ix],
|
|
8u);
|
|
|
|
USBD_DescWr32(p_dev->Nbr, 0u); /* Add 6 null bytes (reserved). */
|
|
USBD_DescWr16(p_dev->Nbr, 0u);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
USBD_DescWrStop((USBD_DEV *)p_dev, p_dev->DescBufErrPtr);
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (valid);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqIF_MS()
|
|
*
|
|
* Description : Microsoft descriptor request handler (when recipient is interface).
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : (1) For more information on Microsoft OS decriptors, see
|
|
* 'http://msdn.microsoft.com/en-us/library/windows/hardware/gg463179.aspx'.
|
|
*
|
|
* (2) Page feature is not supported so Microsoft OS descriptors have their length limited
|
|
* to 64Kbytes.
|
|
*********************************************************************************************************
|
|
*/
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
static CPU_BOOLEAN USBD_StdReqIF_MS (const USBD_DEV *p_dev)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U section_cnt;
|
|
CPU_INT08U ext_property_cnt;
|
|
CPU_INT08U ext_property_ix;
|
|
CPU_INT16U feature;
|
|
CPU_INT16U len;
|
|
CPU_INT32U desc_len;
|
|
CPU_INT08U cfg_nbr = 0u;
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_CLASS_DRV *p_class_drv;
|
|
USBD_MS_OS_EXT_PROPERTY *p_ext_property;
|
|
|
|
|
|
valid = DEF_FAIL;
|
|
feature = p_dev->SetupReq.wIndex;
|
|
if_nbr = (CPU_INT08U)(p_dev->SetupReq.wValue & DEF_INT_08_MASK);
|
|
len = p_dev->SetupReq.wLength;
|
|
|
|
/* Use 1st cfg as Microsoft doesn't specify cfg in ... */
|
|
/* ... setup pkt. */
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (p_dev->Spd == USBD_DEV_SPD_HIGH) {
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr | USBD_CFG_NBR_SPD_BIT);
|
|
} else {
|
|
#endif
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr);
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
return (DEF_FAIL);
|
|
}
|
|
|
|
switch (feature) {
|
|
case USBD_MS_OS_FEATURE_EXT_PROPERTIES: /* See note (1). */
|
|
/* ----------------- SEND DESC HEADER ----------------- */
|
|
/* Compute length of descriptor. */
|
|
desc_len = USBD_MS_OS_DESC_EXT_PROPERTIES_HDR_LEN;
|
|
section_cnt = 0u;
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
p_class_drv = p_if->ClassDrvPtr;
|
|
if (p_class_drv->MS_GetExtPropertyTbl != DEF_NULL) {
|
|
|
|
ext_property_cnt = p_class_drv->MS_GetExtPropertyTbl( p_dev->Nbr,
|
|
&p_ext_property,
|
|
p_if->ClassArgPtr);
|
|
for (ext_property_ix = 0u; ext_property_ix < ext_property_cnt; ext_property_ix++) {
|
|
desc_len += USBD_MS_OS_DESC_EXT_PROPERTIES_SECTION_HDR_LEN;
|
|
desc_len += p_ext_property->PropertyNameLen;
|
|
desc_len += p_ext_property->PropertyLen;
|
|
desc_len += 6u;
|
|
|
|
section_cnt++;
|
|
p_ext_property++;
|
|
}
|
|
}
|
|
|
|
USBD_DescWrStart((USBD_DEV *)p_dev, desc_len);
|
|
|
|
USBD_DescWr32(p_dev->Nbr, desc_len);
|
|
USBD_DescWr16(p_dev->Nbr, USBD_MS_OS_DESC_VER_1_0);
|
|
USBD_DescWr16(p_dev->Nbr, feature);
|
|
USBD_DescWr16(p_dev->Nbr, section_cnt);
|
|
|
|
|
|
/* ---------------- SEND DESC SECTIONS ---------------- */
|
|
/* If req len = version, only send desc hdr. */
|
|
if (len != USBD_MS_OS_DESC_EXT_PROPERTIES_HDR_VER_1_0) {
|
|
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
if (p_class_drv->MS_GetExtPropertyTbl != DEF_NULL) {
|
|
|
|
ext_property_cnt = p_class_drv->MS_GetExtPropertyTbl( p_dev->Nbr,
|
|
&p_ext_property,
|
|
p_if->ClassArgPtr);
|
|
for (ext_property_ix = 0u; ext_property_ix < ext_property_cnt; ext_property_ix++) {
|
|
|
|
/* Compute desc section len. */
|
|
desc_len = USBD_MS_OS_DESC_EXT_PROPERTIES_SECTION_HDR_LEN;
|
|
desc_len += p_ext_property->PropertyNameLen;
|
|
desc_len += p_ext_property->PropertyLen;
|
|
desc_len += 6u;
|
|
|
|
/* Wr desc section. */
|
|
USBD_DescWr32(p_dev->Nbr, desc_len);
|
|
USBD_DescWr32(p_dev->Nbr, p_ext_property->PropertyType);
|
|
|
|
USBD_DescWr16(p_dev->Nbr, p_ext_property->PropertyNameLen);
|
|
USBD_DescWr ( p_dev->Nbr,
|
|
(CPU_INT08U *)p_ext_property->PropertyNamePtr,
|
|
p_ext_property->PropertyNameLen);
|
|
|
|
USBD_DescWr32(p_dev->Nbr, p_ext_property->PropertyLen);
|
|
USBD_DescWr ( p_dev->Nbr,
|
|
(CPU_INT08U *)p_ext_property->PropertyPtr,
|
|
p_ext_property->PropertyLen);
|
|
|
|
p_ext_property++;
|
|
}
|
|
}
|
|
}
|
|
|
|
USBD_DescWrStop((USBD_DEV *)p_dev, p_dev->DescBufErrPtr);
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (valid);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqDescGet()
|
|
*
|
|
* Description : GET_DESCRIPTOR standard request handler.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_BOOLEAN USBD_StdReqDescGet (USBD_DEV *p_dev)
|
|
{
|
|
CPU_INT08U desc_type;
|
|
CPU_INT08U desc_ix;
|
|
CPU_INT16U req_len;
|
|
CPU_BOOLEAN valid;
|
|
USBD_ERR err;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
USBD_DRV *p_drv;
|
|
#endif
|
|
|
|
desc_type = (CPU_INT08U)((p_dev->SetupReq.wValue >> 8u) & DEF_INT_08_MASK);
|
|
desc_ix = (CPU_INT08U)( p_dev->SetupReq.wValue & DEF_INT_08_MASK);
|
|
valid = DEF_FAIL;
|
|
req_len = p_dev->SetupReq.wLength;
|
|
p_dev->ActualBufPtr = p_dev->DescBufPtr; /* Set the desc buf as the current buf. */
|
|
p_dev->DescBufMaxLen = USBD_CFG_DESC_BUF_LEN; /* Set the max len for the desc buf. */
|
|
p_dev->DescBufErrPtr = &err;
|
|
|
|
switch (desc_type) {
|
|
case USBD_DESC_TYPE_DEVICE: /* ----------------- DEVICE DESCRIPTOR ---------------- */
|
|
USBD_DBG_CORE_STD(" Get Descriptor (Device)");
|
|
USBD_DevDescSend(p_dev,
|
|
DEF_NO,
|
|
req_len,
|
|
&err);
|
|
if (err == USBD_ERR_NONE) {
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DESC_TYPE_CONFIGURATION: /* ------------- CONFIGURATION DESCRIPTOR ------------- */
|
|
USBD_DBG_CORE_STD_ARG(" Get Descriptor (Configuration)", desc_ix);
|
|
USBD_CfgDescSend(p_dev,
|
|
desc_ix,
|
|
DEF_NO,
|
|
req_len,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
USBD_DBG_CORE_STD_ERR(" Get Descriptor (Configuration)", err);
|
|
} else {
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DESC_TYPE_STRING: /* ---------------- STRING DESCRIPTOR ----------------- */
|
|
USBD_DBG_CORE_STD_ARG(" Get Descriptor (String)", desc_ix);
|
|
USBD_StrDescSend(p_dev,
|
|
desc_ix,
|
|
req_len,
|
|
&err);
|
|
if (err == USBD_ERR_NONE) {
|
|
valid = DEF_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_DESC_TYPE_DEVICE_QUALIFIER: /* ----------- DEVICE QUALIFIER DESCRIPTOR ------------ */
|
|
USBD_DBG_CORE_STD(" Get Descriptor (Device Qualifier)");
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
p_drv = &p_dev->Drv;
|
|
|
|
if (p_drv->CfgPtr->Spd == USBD_DEV_SPD_FULL) { /* Chk if dev only supports FS. */
|
|
break;
|
|
}
|
|
|
|
USBD_DevDescSend(p_dev,
|
|
DEF_YES,
|
|
req_len,
|
|
&err);
|
|
if (err == USBD_ERR_NONE) {
|
|
valid = DEF_OK;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
|
|
case USBD_DESC_TYPE_OTHER_SPEED_CONFIGURATION: /* ------- OTHER-SPEED CONFIGURATION DESCRIPTOR ------- */
|
|
USBD_DBG_CORE_STD(" Get Descriptor (Other Speed)");
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
p_drv = &p_dev->Drv;
|
|
|
|
if (p_drv->CfgPtr->Spd == USBD_DEV_SPD_FULL) {
|
|
break;
|
|
}
|
|
|
|
USBD_CfgDescSend(p_dev,
|
|
desc_ix,
|
|
DEF_YES,
|
|
req_len,
|
|
&err);
|
|
if (err == USBD_ERR_NONE) {
|
|
valid = DEF_OK;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
|
|
default :
|
|
break;
|
|
}
|
|
|
|
p_dev->DescBufErrPtr = (USBD_ERR *)0;
|
|
return (valid);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StdReqDescSet()
|
|
*
|
|
* Description : SET_DESCRIPTOR standard request handler.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
|
|
*
|
|
* DEF_FAIL, otherwise.
|
|
*
|
|
* Note(s) : (1) $$$$ SET_DESCRIPTOR MAY be implemented in future versions.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if 0
|
|
static CPU_BOOLEAN USBD_StdReqDescSet (USBD_DEV *p_dev)
|
|
{
|
|
(void)p_dev;
|
|
|
|
return (DEF_FAIL);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CfgClose()
|
|
*
|
|
* Description : Close current device configuration.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue and
|
|
* 'USBD_DevStop()' function.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_CfgClose (USBD_DEV *p_dev)
|
|
{
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
USBD_DRV_API *p_drv_api;
|
|
CPU_INT08U if_nbr;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_cfg = p_dev->CfgCurPtr;
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
return;
|
|
}
|
|
|
|
for (if_nbr = 0u; if_nbr < p_cfg->IF_NbrTotal; if_nbr++) {
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
if (p_if == (USBD_IF *)0) {
|
|
return;
|
|
}
|
|
|
|
p_if_alt = p_if->AltCurPtr;
|
|
if (p_if_alt == (USBD_IF_ALT *)0) {
|
|
return;
|
|
}
|
|
|
|
if (p_if->ClassDrvPtr->Disconn != (void *)0) {
|
|
p_if->ClassDrvPtr->Disconn(p_dev->Nbr, /* Notify class that cfg is not active. */
|
|
p_dev->CfgCurNbr,
|
|
p_if->ClassArgPtr);
|
|
}
|
|
|
|
USBD_IF_AltClose(p_dev, p_if_alt);
|
|
|
|
p_if_alt = USBD_IF_AltRefGet(p_if, 0u);
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_if->AltCurPtr = p_if_alt;
|
|
p_if->AltCur = 0u;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
p_drv_api = p_dev->Drv.API_Ptr;
|
|
if (p_drv_api->CfgClr != (void*)0) {
|
|
p_drv_api->CfgClr(&p_dev->Drv, p_dev->CfgCurNbr); /* Clr cfg in the driver. */
|
|
}
|
|
|
|
if (p_dev->BusFnctsPtr->CfgClr != (void *)0) {
|
|
/* Notify app about clr cfg. */
|
|
p_dev->BusFnctsPtr->CfgClr(p_dev->Nbr, p_dev->CfgCurNbr);
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->CfgCurPtr = (USBD_CFG *)0;
|
|
p_dev->CfgCurNbr = USBD_CFG_NBR_NONE;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CfgOpen()
|
|
*
|
|
* Description : Open specified configuration.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Configuration successfully opened.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
* USBD_ERR_IF_INVALID_NBR Invalid interface number.
|
|
* USBD_ERR_IF_ALT_INVALID_NBR Invalid interface alternate setting number.
|
|
* USBD_ERR_CFG_SET_FAIL Device driver set configuration failed.
|
|
*
|
|
* - RETURNED BY USBD_IF_AltOpen() -
|
|
* See USBD_IF_AltOpen() for additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_CfgOpen (USBD_DEV *p_dev,
|
|
CPU_INT08U cfg_nbr,
|
|
USBD_ERR *p_err)
|
|
|
|
{
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_IF_ALT *p_if_alt;
|
|
USBD_DRV_API *p_drv_api;
|
|
CPU_INT08U if_nbr;
|
|
CPU_BOOLEAN cfg_set;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (p_dev->Spd == USBD_DEV_SPD_HIGH) {
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr | USBD_CFG_NBR_SPD_BIT);
|
|
} else {
|
|
#endif
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr);
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
for (if_nbr = 0u; if_nbr < p_cfg->IF_NbrTotal; if_nbr++) {
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
if (p_if == (USBD_IF *)0) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
p_if_alt = p_if->AltCurPtr;
|
|
if (p_if_alt == (USBD_IF_ALT *)0) {
|
|
*p_err = USBD_ERR_IF_ALT_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
USBD_IF_AltOpen(p_dev,
|
|
if_nbr,
|
|
p_if_alt,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->CfgCurPtr = p_cfg;
|
|
p_dev->CfgCurNbr = cfg_nbr;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
p_drv_api = p_dev->Drv.API_Ptr;
|
|
if (p_drv_api->CfgSet != (void*)0) {
|
|
cfg_set = p_drv_api->CfgSet(&p_dev->Drv, cfg_nbr); /* Set cfg in the drv. */
|
|
if (cfg_set == DEF_FAIL) {
|
|
*p_err = USBD_ERR_CFG_SET_FAIL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->State = USBD_DEV_STATE_CONFIGURED;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
for (if_nbr = 0u; if_nbr < p_cfg->IF_NbrTotal; if_nbr++) {
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
if (p_if == (USBD_IF *)0) {
|
|
*p_err = USBD_ERR_IF_INVALID_NBR;
|
|
return;
|
|
} else {
|
|
|
|
if (p_if->ClassDrvPtr->Conn != (void *)0) {
|
|
p_if->ClassDrvPtr->Conn(p_dev->Nbr, /* Notify class that cfg is active. */
|
|
cfg_nbr,
|
|
p_if->ClassArgPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p_dev->BusFnctsPtr->CfgSet != (void *)0) {
|
|
p_dev->BusFnctsPtr->CfgSet(p_dev->Nbr, cfg_nbr); /* Notify app about set cfg. */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevDescSend()
|
|
*
|
|
* Description : Send device configuration descriptor.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* other Other speed configuration :
|
|
*
|
|
* DEF_YES Current speed.
|
|
* DEF_NO Other operational speed.
|
|
*
|
|
* req_len Requested length by the host.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device configuration successfully sent.
|
|
* USBD_ERR_DEV_INVALID_SPD Invalid speed; configuration descriptor not available.
|
|
*
|
|
* - RETURNED BY USBD_DescWrStop() -
|
|
* See USBD_DescWrStop() for additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB Spec 2.0 table 9-8 describes the standard device descriptor.
|
|
*
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | Offset | Field | Size | Value | Description |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 0 | bLength | 1 | Number | Size of this descriptor |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 1 | bDescriptorType | 1 | Const | DEVICE Descriptor Type |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 2 | bcdUSB | 2 | BCD USB | Specification release number |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 4 | bDeviceClass | 1 | Class | Class code. |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 5 | bDeviceSubClass | 1 | SubClass | Subclass code. |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 6 | bDeviceProtocol | 1 | Protocol | Protocol code. |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 7 | bMaxPacketSize0 | 1 | Number | Max packet size for EP zero |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 8 | idVendor | 2 | ID | Vendor ID |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 10 | idProduct | 2 | ID | Product ID |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 12 | bcdDevice | 2 | BCD | Dev release number in BCD format |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 14 | iManufacturer | 1 | Index | Index of manufacturer string |
|
|
* +--------+--------------------+-------+----------+-----------------------------------+
|
|
* | 15 | iProduct | 1 | Index | Index of product string |
|
|
* +--------|--------------------|-------|----------|-----------------------------------+
|
|
* | 16 | iSerialNumber | 1 | Index | Index of serial number string |
|
|
* +--------|--------------------|-------|----------|-----------------------------------+
|
|
* | 17 | bNumConfigurations | 1 | Number | Number of possible configurations |
|
|
* +--------|--------------------|-------|----------|-----------------------------------+
|
|
*
|
|
* (2) To enable host to identify devices that use the Interface Association descriptor the
|
|
* device descriptor should contain the following values.
|
|
*********************************************************************************************************
|
|
*/
|
|
static void USBD_DevDescSend (USBD_DEV *p_dev,
|
|
CPU_BOOLEAN other,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_CFG *p_cfg;
|
|
CPU_BOOLEAN if_grp_en;
|
|
CPU_INT08U cfg_nbr;
|
|
CPU_INT08U cfg_nbr_spd;
|
|
CPU_INT08U cfg_nbr_total;
|
|
CPU_INT08U str_ix;
|
|
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_DISABLED)
|
|
(void)other;
|
|
#endif
|
|
|
|
if_grp_en = DEF_NO;
|
|
*p_err = USBD_ERR_NONE;
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (other == DEF_NO) {
|
|
#endif
|
|
USBD_DescWrStart(p_dev, req_len);
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_DEV); /* Desc len. */
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_DEVICE); /* Dev desc type. */
|
|
USBD_DescWrReq16(p_dev, 0x200u); /* USB spec release nbr in BCD fmt (2.00). */
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (p_dev->Spd == USBD_DEV_SPD_FULL) {
|
|
#endif
|
|
cfg_nbr_spd = DEF_BIT_NONE;
|
|
cfg_nbr_total = p_dev->CfgFS_TotalNbr;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
} else {
|
|
cfg_nbr_spd = USBD_CFG_NBR_SPD_BIT;
|
|
cfg_nbr_total = p_dev->CfgHS_TotalNbr;
|
|
}
|
|
#endif
|
|
|
|
cfg_nbr = 0u;
|
|
while ((cfg_nbr < cfg_nbr_total) &&
|
|
(if_grp_en == DEF_NO)) {
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr | cfg_nbr_spd);
|
|
if (p_cfg != (USBD_CFG *)0) {
|
|
if (p_cfg->IF_GrpNbrTotal > 0u) {
|
|
if_grp_en = DEF_YES;
|
|
}
|
|
}
|
|
|
|
cfg_nbr++;
|
|
}
|
|
|
|
if (if_grp_en == DEF_NO) {
|
|
/* Dev class is specified in IF desc. */
|
|
USBD_DescWrReq08(p_dev, USBD_CLASS_CODE_USE_IF_DESC);
|
|
USBD_DescWrReq08(p_dev, USBD_SUBCLASS_CODE_USE_IF_DESC);
|
|
USBD_DescWrReq08(p_dev, USBD_PROTOCOL_CODE_USE_IF_DESC);
|
|
} else {
|
|
/* Multi-Interface function dev class. */
|
|
USBD_DescWrReq08(p_dev, USBD_CLASS_CODE_MISCELLANEOUS);
|
|
USBD_DescWrReq08(p_dev, USBD_SUBCLASS_CODE_USE_COMMON_CLASS);
|
|
USBD_DescWrReq08(p_dev, USBD_PROTOCOL_CODE_USE_IAD);
|
|
}
|
|
/* Set max pkt size for ctrl EP. */
|
|
USBD_DescWrReq08(p_dev, (CPU_INT08U)p_dev->EP_CtrlMaxPktSize);
|
|
/* Set vendor id, product id and dev id. */
|
|
USBD_DescWrReq16(p_dev, p_dev->DevCfgPtr->VendorID);
|
|
USBD_DescWrReq16(p_dev, p_dev->DevCfgPtr->ProductID);
|
|
USBD_DescWrReq16(p_dev, p_dev->DevCfgPtr->DeviceBCD);
|
|
|
|
str_ix = USBD_StrDescIxGet(p_dev, p_dev->DevCfgPtr->ManufacturerStrPtr);
|
|
USBD_DescWrReq08(p_dev, str_ix);
|
|
str_ix = USBD_StrDescIxGet(p_dev, p_dev->DevCfgPtr->ProductStrPtr);
|
|
USBD_DescWrReq08(p_dev, str_ix);
|
|
str_ix = USBD_StrDescIxGet(p_dev, p_dev->DevCfgPtr->SerialNbrStrPtr);
|
|
USBD_DescWrReq08(p_dev, str_ix);
|
|
USBD_DescWrReq08(p_dev, cfg_nbr_total);
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
} else {
|
|
|
|
if (p_dev->Drv.CfgPtr->Spd != USBD_DEV_SPD_HIGH) {
|
|
*p_err = USBD_ERR_DEV_INVALID_SPD;
|
|
return;
|
|
}
|
|
USBD_DescWrStart(p_dev, req_len);
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_DEV_QUAL); /* Desc len. */
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_DEVICE_QUALIFIER);
|
|
USBD_DescWrReq16(p_dev, 0x200u); /* USB spec release nbr in BCD fmt (2.00). */
|
|
|
|
if (p_dev->Spd == USBD_DEV_SPD_HIGH) {
|
|
cfg_nbr_spd = DEF_BIT_NONE;
|
|
cfg_nbr_total = p_dev->CfgFS_TotalNbr;
|
|
} else {
|
|
cfg_nbr_spd = USBD_CFG_NBR_SPD_BIT;
|
|
cfg_nbr_total = p_dev->CfgHS_TotalNbr;
|
|
}
|
|
|
|
cfg_nbr = 0u;
|
|
while ((cfg_nbr < cfg_nbr_total) &&
|
|
(if_grp_en == DEF_NO)) {
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr | cfg_nbr_spd);
|
|
if (p_cfg != (USBD_CFG *)0) {
|
|
if (p_cfg->IF_GrpNbrTotal > 0u) {
|
|
if_grp_en = DEF_YES;
|
|
}
|
|
cfg_nbr++;
|
|
}
|
|
}
|
|
if (if_grp_en == DEF_NO) {
|
|
/* Dev class is specified in IF desc. */
|
|
USBD_DescWrReq08(p_dev, USBD_CLASS_CODE_USE_IF_DESC);
|
|
USBD_DescWrReq08(p_dev, USBD_SUBCLASS_CODE_USE_IF_DESC);
|
|
USBD_DescWrReq08(p_dev, USBD_PROTOCOL_CODE_USE_IF_DESC);
|
|
} else {
|
|
/* Multi-Interface function dev class. */
|
|
USBD_DescWrReq08(p_dev, USBD_CLASS_CODE_MISCELLANEOUS);
|
|
USBD_DescWrReq08(p_dev, USBD_SUBCLASS_CODE_USE_COMMON_CLASS);
|
|
USBD_DescWrReq08(p_dev, USBD_PROTOCOL_CODE_USE_IAD);
|
|
}
|
|
/* Set max pkt size for ctrl EP. */
|
|
USBD_DescWrReq08(p_dev, (CPU_INT08U)p_dev->EP_CtrlMaxPktSize);
|
|
USBD_DescWrReq08(p_dev, cfg_nbr_total);
|
|
USBD_DescWrReq08(p_dev, 0u);
|
|
}
|
|
#endif
|
|
|
|
USBD_DescWrStop(p_dev, p_err);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CfgDescSend()
|
|
*
|
|
* Description : Send configuration descriptor.
|
|
*
|
|
* Argument(s) : p_dev Pointer to device struct.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* other Other speed configuration :
|
|
*
|
|
* DEF_NO Descriptor is build for the current speed.
|
|
* DEF_YES Descriptor is build for the other speed.
|
|
*
|
|
* req_len Requested length by the host.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Device configuration successfully sent.
|
|
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
|
|
*
|
|
* - RETURNED BY USBD_DescWrStop() -
|
|
* See USBD_DescWrStop() for additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_CfgDescSend (USBD_DEV *p_dev,
|
|
CPU_INT08U cfg_nbr,
|
|
CPU_BOOLEAN other,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_CFG *p_cfg;
|
|
USBD_IF *p_if;
|
|
USBD_EP_INFO *p_ep;
|
|
USBD_IF_ALT *p_if_alt;
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
USBD_IF_GRP *p_if_grp;
|
|
#endif
|
|
USBD_CLASS_DRV *p_if_drv;
|
|
CPU_INT08U cfg_nbr_cur;
|
|
CPU_INT08U ep_nbr;
|
|
CPU_INT08U if_nbr;
|
|
CPU_INT08U if_total;
|
|
CPU_INT08U if_grp_cur;
|
|
CPU_INT08U if_alt_nbr;
|
|
CPU_INT08U str_ix;
|
|
CPU_INT08U attrib;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
CPU_INT32U ep_alloc_map;
|
|
#endif
|
|
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (p_dev->Spd == USBD_DEV_SPD_HIGH) {
|
|
cfg_nbr_cur = cfg_nbr | USBD_CFG_NBR_SPD_BIT;
|
|
} else {
|
|
#endif
|
|
cfg_nbr_cur = cfg_nbr;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr_cur);
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED) /* other will always be DEF_NO when HS is disabled. */
|
|
if (other == DEF_YES) {
|
|
if (p_cfg->CfgOtherSpd == USBD_CFG_NBR_NONE) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
|
|
cfg_nbr_cur = p_cfg->CfgOtherSpd;
|
|
|
|
p_cfg = USBD_CfgRefGet(p_dev, cfg_nbr_cur); /* Retrieve cfg struct for other spd. */
|
|
if (p_cfg == (USBD_CFG *)0) {
|
|
*p_err = USBD_ERR_CFG_INVALID_NBR;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
p_cfg->DescLen = USBD_DESC_LEN_CFG; /* Init cfg desc len. */
|
|
|
|
USBD_DescWrStart(p_dev, req_len);
|
|
/* ---------- BUILD CONFIGURATION DESCRIPTOR ---------- */
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_CFG); /* Desc len. */
|
|
if (other == DEF_YES) {
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_OTHER_SPEED_CONFIGURATION);
|
|
} else {
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_CONFIGURATION); /* Desc type. */
|
|
}
|
|
|
|
if_total = p_cfg->IF_NbrTotal;
|
|
if_grp_cur = USBD_IF_GRP_NBR_NONE;
|
|
|
|
for (if_nbr = 0u; if_nbr < if_total; if_nbr++) {
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
p_if_drv = p_if->ClassDrvPtr;
|
|
|
|
if ((p_if->GrpNbr != if_grp_cur ) &&
|
|
(p_if->GrpNbr != USBD_IF_GRP_NBR_NONE)) {
|
|
/* Add IF assoc desc len. */
|
|
p_cfg->DescLen += USBD_DESC_LEN_IF_ASSOCIATION;
|
|
if_grp_cur = p_if->GrpNbr;
|
|
}
|
|
|
|
p_cfg->DescLen += (USBD_DESC_LEN_IF * p_if->AltNbrTotal);
|
|
|
|
for (if_alt_nbr = 0u; if_alt_nbr < p_if->AltNbrTotal; if_alt_nbr++) {
|
|
p_if_alt = USBD_IF_AltRefGet(p_if, if_alt_nbr);
|
|
p_cfg->DescLen += (USBD_DESC_LEN_EP * p_if_alt->EP_NbrTotal);
|
|
|
|
if (p_if_drv->IF_DescSizeGet != (void *)0) { /* Add IF functional desc len. */
|
|
p_cfg->DescLen += p_if_drv->IF_DescSizeGet(p_dev->Nbr,
|
|
cfg_nbr_cur,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
p_if->ClassArgPtr,
|
|
p_if_alt->ClassArgPtr);
|
|
}
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
ep_alloc_map = p_if_alt->EP_TblMap;
|
|
while (ep_alloc_map != DEF_BIT_NONE) {
|
|
ep_nbr = (CPU_INT08U)CPU_CntTrailZeros32(ep_alloc_map);
|
|
p_ep = p_if_alt->EP_TblPtrs[ep_nbr];
|
|
|
|
if (p_if_drv->EP_DescSizeGet != (void *)0) { /* Add EP functional desc len. */
|
|
p_cfg->DescLen += p_if_drv->EP_DescSizeGet(p_dev->Nbr,
|
|
cfg_nbr_cur,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
p_ep->Addr,
|
|
p_if->ClassArgPtr,
|
|
p_if_alt->ClassArgPtr);
|
|
}
|
|
|
|
if ((p_if->ClassCode == USBD_CLASS_CODE_AUDIO) &&
|
|
(p_if->ClassProtocolCode == 0u ) &&
|
|
(((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_ISOC) ||
|
|
((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_INTR))) {
|
|
p_cfg->DescLen += 2u; /* EP desc on audio class v1.0 has 2 additional fields. */
|
|
}
|
|
|
|
DEF_BIT_CLR(ep_alloc_map, DEF_BIT32(ep_nbr));
|
|
}
|
|
#else
|
|
p_ep = p_if_alt->EP_HeadPtr;
|
|
|
|
for (ep_nbr = 0u; ep_nbr < p_if_alt->EP_NbrTotal; ep_nbr++) {
|
|
if (p_if_drv->EP_DescSizeGet != (void *)0) {
|
|
p_cfg->DescLen += p_if_drv->EP_DescSizeGet(p_dev->Nbr,
|
|
cfg_nbr_cur,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
p_ep->Addr,
|
|
p_if->ClassArgPtr,
|
|
p_if_alt->ClassArgPtr);
|
|
}
|
|
|
|
if ((p_if->ClassCode == USBD_CLASS_CODE_AUDIO) &&
|
|
(p_if->ClassProtocolCode == 0u ) &&
|
|
(((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_ISOC) ||
|
|
((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_INTR))) {
|
|
p_cfg->DescLen += 2u; /* EP desc on audio class v1.0 has 2 additional fields. */
|
|
}
|
|
|
|
p_ep = p_ep->NextPtr;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
/* ------------------ BUILD CFG DESC ------------------ */
|
|
USBD_DescWrReq16(p_dev, p_cfg->DescLen); /* Desc len. */
|
|
USBD_DescWrReq08(p_dev, p_cfg->IF_NbrTotal); /* Nbr of IF. */
|
|
USBD_DescWrReq08(p_dev, cfg_nbr + 1u); /* Cfg ix. */
|
|
|
|
str_ix = USBD_StrDescIxGet(p_dev, p_cfg->NamePtr); /* Add str ix. */
|
|
USBD_DescWrReq08(p_dev, str_ix);
|
|
|
|
attrib = USBD_CFG_DESC_RSVD_SET;
|
|
if (DEF_BIT_IS_SET(p_cfg->Attrib, USBD_DEV_ATTRIB_SELF_POWERED)) {
|
|
DEF_BIT_SET(attrib, USBD_CFG_DESC_SELF_POWERED);
|
|
}
|
|
if (DEF_BIT_IS_SET(p_cfg->Attrib, USBD_DEV_ATTRIB_REMOTE_WAKEUP)) {
|
|
DEF_BIT_SET(attrib, USBD_CFG_DESC_REMOTE_WAKEUP);
|
|
}
|
|
USBD_DescWrReq08(p_dev, attrib);
|
|
USBD_DescWrReq08(p_dev, (CPU_INT08U)((p_cfg->MaxPwr + 1u) / 2u));
|
|
|
|
/* ------------ BUILD INTERFACE DESCRIPTOR ------------ */
|
|
if_total = p_cfg->IF_NbrTotal;
|
|
if_grp_cur = USBD_IF_GRP_NBR_NONE;
|
|
|
|
for (if_nbr = 0u; if_nbr < if_total; if_nbr++) {
|
|
p_if = USBD_IF_RefGet(p_cfg, if_nbr);
|
|
p_if_drv = p_if->ClassDrvPtr;
|
|
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
if ((p_if->GrpNbr != if_grp_cur ) &&
|
|
(p_if->GrpNbr != USBD_IF_GRP_NBR_NONE)) {
|
|
/* Add IF assoc desc (IAD). */
|
|
p_if_grp = USBD_IF_GrpRefGet(p_cfg, p_if->GrpNbr);
|
|
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_IF_ASSOCIATION);
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_IAD);
|
|
USBD_DescWrReq08(p_dev, p_if_grp->IF_Start);
|
|
USBD_DescWrReq08(p_dev, p_if_grp->IF_Cnt);
|
|
USBD_DescWrReq08(p_dev, p_if_grp->ClassCode);
|
|
USBD_DescWrReq08(p_dev, p_if_grp->ClassSubCode);
|
|
USBD_DescWrReq08(p_dev, p_if_grp->ClassProtocolCode);
|
|
|
|
str_ix = USBD_StrDescIxGet(p_dev, p_if_grp->NamePtr);
|
|
USBD_DescWrReq08(p_dev, str_ix);
|
|
if_grp_cur = p_if->GrpNbr;
|
|
}
|
|
#endif
|
|
/* Add IF/alt settings desc. */
|
|
for (if_alt_nbr = 0u; if_alt_nbr < p_if->AltNbrTotal; if_alt_nbr++) {
|
|
p_if_alt = USBD_IF_AltRefGet(p_if, if_alt_nbr);
|
|
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_IF);
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_INTERFACE);
|
|
USBD_DescWrReq08(p_dev, if_nbr);
|
|
USBD_DescWrReq08(p_dev, if_alt_nbr);
|
|
USBD_DescWrReq08(p_dev, p_if_alt->EP_NbrTotal);
|
|
USBD_DescWrReq08(p_dev, p_if->ClassCode);
|
|
USBD_DescWrReq08(p_dev, p_if->ClassSubCode);
|
|
USBD_DescWrReq08(p_dev, p_if->ClassProtocolCode);
|
|
|
|
str_ix = USBD_StrDescIxGet(p_dev, p_if_alt->NamePtr);
|
|
USBD_DescWrReq08(p_dev, str_ix);
|
|
|
|
if (p_if_drv->IF_Desc != (void *)0) { /* Add class specific IF desc. */
|
|
p_if_drv->IF_Desc(p_dev->Nbr,
|
|
cfg_nbr_cur,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
p_if->ClassArgPtr,
|
|
p_if_alt->ClassArgPtr);
|
|
}
|
|
/* ------------------- BUILD EP DESC ------------------ */
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
ep_alloc_map = p_if_alt->EP_TblMap;
|
|
while (ep_alloc_map != DEF_BIT_NONE) {
|
|
ep_nbr = (CPU_INT08U)CPU_CntTrailZeros32(ep_alloc_map);
|
|
p_ep = p_if_alt->EP_TblPtrs[ep_nbr];
|
|
|
|
if ((p_if->ClassCode == USBD_CLASS_CODE_AUDIO) &&
|
|
(p_if->ClassProtocolCode == 0u ) &&
|
|
(((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_ISOC) ||
|
|
((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_INTR))) {
|
|
/* EP desc on audio class v1.0 has 2 additional fields. */
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_EP + 2u);
|
|
} else {
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_EP);
|
|
}
|
|
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_ENDPOINT);
|
|
USBD_DescWrReq08(p_dev, p_ep->Addr);
|
|
USBD_DescWrReq08(p_dev, p_ep->Attrib);
|
|
USBD_DescWrReq16(p_dev, p_ep->MaxPktSize);
|
|
USBD_DescWrReq08(p_dev, p_ep->Interval);
|
|
|
|
if ((p_if->ClassCode == USBD_CLASS_CODE_AUDIO) &&
|
|
(p_if->ClassProtocolCode == 0u ) &&
|
|
(((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_ISOC) ||
|
|
((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_INTR))) {
|
|
/* EP desc on audio class v1.0 has 2 additional fields. */
|
|
USBD_DescWrReq08(p_dev, p_ep->SyncRefresh);
|
|
USBD_DescWrReq08(p_dev, p_ep->SyncAddr);
|
|
}
|
|
|
|
if (p_if_drv->EP_Desc != (void *)0) {
|
|
p_if_drv->EP_Desc(p_dev->Nbr, /* Add class specific EP desc. */
|
|
cfg_nbr_cur,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
p_ep->Addr,
|
|
p_if->ClassArgPtr,
|
|
p_if_alt->ClassArgPtr);
|
|
}
|
|
|
|
DEF_BIT_CLR(ep_alloc_map, DEF_BIT32(ep_nbr));
|
|
}
|
|
#else
|
|
p_ep = p_if_alt->EP_HeadPtr;
|
|
|
|
for (ep_nbr = 0u; ep_nbr < p_if_alt->EP_NbrTotal; ep_nbr++) {
|
|
if ((p_if->ClassCode == USBD_CLASS_CODE_AUDIO) &&
|
|
(p_if->ClassProtocolCode == 0u ) &&
|
|
(((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_ISOC) ||
|
|
((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_INTR))) {
|
|
/* EP desc on audio class v1.0 has 2 additional fields. */
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_EP + 2u);
|
|
} else {
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_LEN_EP);
|
|
}
|
|
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_ENDPOINT);
|
|
USBD_DescWrReq08(p_dev, p_ep->Addr);
|
|
USBD_DescWrReq08(p_dev, p_ep->Attrib);
|
|
USBD_DescWrReq16(p_dev, p_ep->MaxPktSize);
|
|
USBD_DescWrReq08(p_dev, p_ep->Interval);
|
|
|
|
if ((p_if->ClassCode == USBD_CLASS_CODE_AUDIO) &&
|
|
(p_if->ClassProtocolCode == 0u ) &&
|
|
(((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_ISOC) ||
|
|
((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_INTR))) {
|
|
/* EP desc on audio class v1.0 has 2 additional fields. */
|
|
USBD_DescWrReq08(p_dev, p_ep->SyncRefresh);
|
|
USBD_DescWrReq08(p_dev, p_ep->SyncAddr);
|
|
}
|
|
|
|
if (p_if_drv->EP_Desc != (void *)0) {
|
|
p_if_drv->EP_Desc(p_dev->Nbr, /* Add class specific EP desc. */
|
|
cfg_nbr_cur,
|
|
if_nbr,
|
|
if_alt_nbr,
|
|
p_ep->Addr,
|
|
p_if->ClassArgPtr,
|
|
p_if_alt->ClassArgPtr);
|
|
}
|
|
|
|
p_ep = p_ep->NextPtr;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
USBD_DescWrStop(p_dev, p_err);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StrDescSend()
|
|
*
|
|
* Description : Send string descriptor.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* str_ix String index.
|
|
*
|
|
* req_len Requested length by the host.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE String descriptor successfully sent.
|
|
* USBD_ERR_NULL_PTR String NOT available.
|
|
*
|
|
* - RETURNED BY USBD_DescWrStop() -
|
|
* See USBD_DescWrStop() for additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_StrDescSend (USBD_DEV *p_dev,
|
|
CPU_INT08U str_ix,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
const CPU_CHAR *p_str;
|
|
CPU_SIZE_T len;
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
CPU_INT08U ix;
|
|
#endif
|
|
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
|
|
USBD_DescWrStart(p_dev, req_len);
|
|
|
|
switch (str_ix) {
|
|
case 0u:
|
|
USBD_DescWrReq08(p_dev, 4u);
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_STRING);
|
|
USBD_DescWrReq16(p_dev, p_dev->DevCfgPtr->LangId);
|
|
break;
|
|
|
|
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
|
|
case USBD_STR_MS_OS_IX:
|
|
USBD_DescWrReq08(p_dev, USBD_STR_MS_OS_LEN);
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_STRING);
|
|
|
|
for (ix = 0u; ix < 7u; ix++) {
|
|
USBD_DescWrReq08(p_dev, (CPU_INT08U)USBD_StrMS_Signature[ix]);
|
|
USBD_DescWrReq08(p_dev, 0u);
|
|
}
|
|
|
|
USBD_DescWrReq08(p_dev, p_dev->StrMS_VendorCode);
|
|
USBD_DescWrReq08(p_dev, 0u);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
p_str = USBD_StrDescGet(p_dev, str_ix - 1u);
|
|
if (p_str != (CPU_CHAR *)0) {
|
|
len = Str_Len(p_str);
|
|
len = (2u * len) + 2u;
|
|
len = DEF_MIN(len, DEF_INT_08U_MAX_VAL);
|
|
len = len - (len % 2u);
|
|
|
|
USBD_DescWrReq08(p_dev, (CPU_INT08U)len);
|
|
USBD_DescWrReq08(p_dev, USBD_DESC_TYPE_STRING);
|
|
|
|
while (*p_str != '\0') {
|
|
USBD_DescWrReq08(p_dev, (CPU_INT08U)*p_str);
|
|
USBD_DescWrReq08(p_dev, 0u);
|
|
|
|
p_str++;
|
|
}
|
|
} else {
|
|
*p_err = USBD_ERR_NULL_PTR;
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
USBD_DescWrStop(p_dev, p_err);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWrStart()
|
|
*
|
|
* Description : Start write operation in the descriptor buffer.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* req_len Requested length by the host.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_DescWrStart (USBD_DEV *p_dev,
|
|
CPU_INT16U req_len)
|
|
{
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->DescBufIx = 0u;
|
|
p_dev->DescBufReqLen = req_len;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWrStop()
|
|
*
|
|
* Description : Stop write operation in the descriptor buffer.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Write operation successfully completed.
|
|
*
|
|
* - RETURNED BY USBD_EP_CtrlTx() -
|
|
* See USBD_EP_CtrlTx() for additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) This function might be called in two contexts: when a Get Descriptor standard request
|
|
* is received, or when a driver supporting standard request auto-reply queries the
|
|
* device, a configuration or a string descriptor. The descriptor needs to be sent on
|
|
* control endpoint 0 only if this function is called for a Get Descriptor standard
|
|
* request. If the function is called when a driver needs the descriptor, nothing has to
|
|
* be done.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_DescWrStop (USBD_DEV *p_dev,
|
|
USBD_ERR *p_err)
|
|
{
|
|
if (*p_err == USBD_ERR_NONE) {
|
|
if (p_dev->ActualBufPtr == p_dev->DescBufPtr) { /* See Note #1. */
|
|
if (p_dev->DescBufIx > 0u) {
|
|
(void)USBD_CtrlTx( p_dev->Nbr,
|
|
&p_dev->DescBufPtr[0u],
|
|
(CPU_INT32U)p_dev->DescBufIx,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
(p_dev->DescBufReqLen > 0u) ? DEF_YES : DEF_NO,
|
|
p_err);
|
|
} else {
|
|
*p_err = USBD_ERR_NONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWrReq08()
|
|
*
|
|
* Description : Write 8-bit value in the descriptor buffer.
|
|
*
|
|
* Argument(s) : p_dev Pointer to device.
|
|
* ----- Argument validated by the caller(s).
|
|
*
|
|
* val 8-bit value.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_DescWrReq08 (USBD_DEV *p_dev,
|
|
CPU_INT08U val)
|
|
{
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
USBD_DescWrReq(p_dev, &val, 1u);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWr16()
|
|
*
|
|
* Description : Write 16-bit value in the descriptor buffer.
|
|
*
|
|
* Argument(s) : p_dev Pointer to device.
|
|
* ----- Argument validated by the caller(s)
|
|
*
|
|
* val 16-bit value.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_DescWrReq16 (USBD_DEV *p_dev,
|
|
CPU_INT16U val)
|
|
{
|
|
if (*(p_dev->DescBufErrPtr) == USBD_ERR_NONE) {
|
|
CPU_INT08U buf[2u];
|
|
|
|
|
|
buf[0u] = (CPU_INT08U)( val & DEF_INT_08_MASK);
|
|
buf[1u] = (CPU_INT08U)((val >> 8u) & DEF_INT_08_MASK);
|
|
|
|
USBD_DescWrReq(p_dev, &buf[0u], 2u);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DescWrReq()
|
|
*
|
|
* Description : USB device configuration write request.
|
|
*
|
|
* Argument(s) : p_dev Pointer to device.
|
|
* ----- Argument validated by the caller(s)
|
|
*
|
|
* p_buf Pointer to data buffer.
|
|
* ----- Argument validated by the caller(s).
|
|
*
|
|
* len Buffer length.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) This function might be called in two contexts: when a Get Descriptor standard request
|
|
* is received, or when a driver supporting standard request auto-reply queries the
|
|
* device, a configuration or a string descriptor. In the Get Descriptor standard
|
|
* request case, if the buffer is full, a transfer on control endpoint 0 is done, before
|
|
* resuming to fill the buffer. In the case of a driver supporting standard request
|
|
* auto-reply, if the buffer is full, an error is set and the function exits.
|
|
*
|
|
* (2) If an error is reported by USBD_CtrlTx() during the construction of the descriptor,
|
|
* this pointer will store the error code, stop the rest of the data phase, skip the
|
|
* status phase and ensure that the control endpoint 0 is stalled to notify the host
|
|
* that an error has occurred.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_DescWrReq ( USBD_DEV *p_dev,
|
|
const CPU_INT08U *p_buf,
|
|
CPU_INT16U len)
|
|
{
|
|
CPU_INT08U *p_desc;
|
|
CPU_INT08U buf_cur_ix;
|
|
CPU_INT16U len_req;
|
|
USBD_ERR err;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_desc = p_dev->ActualBufPtr;
|
|
buf_cur_ix = p_dev->DescBufIx;
|
|
len_req = p_dev->DescBufReqLen;
|
|
err = USBD_ERR_NONE;
|
|
|
|
while ((len_req != 0u) &&
|
|
(len != 0u)) {
|
|
if (buf_cur_ix >= p_dev->DescBufMaxLen) {
|
|
if (p_dev->ActualBufPtr == p_dev->DescBufPtr) { /* Send data in response to std req. See Note #1. */
|
|
buf_cur_ix = 0u;
|
|
(void)USBD_CtrlTx(p_dev->Nbr,
|
|
&p_dev->DescBufPtr[0u],
|
|
USBD_CFG_DESC_BUF_LEN,
|
|
USBD_CFG_CTRL_REQ_TIMEOUT_mS,
|
|
DEF_NO,
|
|
&err);
|
|
if (err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
} else { /* Buf provided by driver is too small. See Note #1. */
|
|
len_req = 0u;
|
|
err = USBD_ERR_ALLOC;
|
|
}
|
|
} else {
|
|
p_desc[buf_cur_ix] = *p_buf;
|
|
p_buf++;
|
|
len--;
|
|
len_req--;
|
|
buf_cur_ix++;
|
|
}
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->DescBufIx = buf_cur_ix;
|
|
p_dev->DescBufReqLen = len_req;
|
|
if (p_dev->DescBufErrPtr != (USBD_ERR *)0) {
|
|
*(p_dev->DescBufErrPtr) = err; /* See Note #2. */
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DevRefGet()
|
|
*
|
|
* Description : Get device structure.
|
|
*
|
|
* Argument(s) : dev_nbr Device number.
|
|
*
|
|
* Return(s) : Pointer to device structure, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBD_DEV *USBD_DevRefGet (CPU_INT08U dev_nbr)
|
|
{
|
|
USBD_DEV *p_dev;
|
|
|
|
|
|
if (dev_nbr >= USBD_DevNbrNext) { /* Chk if dev nbr is valid. */
|
|
return ((USBD_DEV *)0);
|
|
}
|
|
|
|
p_dev = &USBD_DevTbl[dev_nbr]; /* Get dev struct. */
|
|
return (p_dev);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CfgRefGet()
|
|
*
|
|
* Description : Get configuration structure.
|
|
*
|
|
* Argument(s) : p_dev Pointer to device struct.
|
|
*
|
|
* cfg_nbr Configuration number.
|
|
*
|
|
* Return(s) : Pointer to configuration structure, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBD_CFG *USBD_CfgRefGet (const USBD_DEV *p_dev,
|
|
CPU_INT08U cfg_nbr)
|
|
{
|
|
USBD_CFG *p_cfg;
|
|
CPU_INT08U cfg_val;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
CPU_INT08U cfg_ix;
|
|
#endif
|
|
|
|
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED) /* USBD_CFG_NBR_SPD_BIT will always be clear in FS. */
|
|
cfg_val = cfg_nbr & ~USBD_CFG_NBR_SPD_BIT;
|
|
#else
|
|
cfg_val = cfg_nbr;
|
|
#endif
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Array implementation. */
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (DEF_BIT_IS_SET(cfg_nbr, USBD_CFG_NBR_SPD_BIT) == DEF_YES) {
|
|
if (cfg_val >= p_dev->CfgHS_TotalNbr) { /* Chk if cfg nbr is valid. */
|
|
return ((USBD_CFG *)0);
|
|
}
|
|
p_cfg = p_dev->CfgHS_SpdTblPtrs[cfg_val]; /* Get HS cfg struct. */
|
|
|
|
} else {
|
|
#endif
|
|
if (cfg_val >= p_dev->CfgFS_TotalNbr) { /* Chk if cfg nbr is valid. */
|
|
return ((USBD_CFG *)0);
|
|
}
|
|
p_cfg = p_dev->CfgFS_SpdTblPtrs[cfg_val]; /* Get FS cfg struct. */
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
#else
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
if (DEF_BIT_IS_SET(cfg_nbr, USBD_CFG_NBR_SPD_BIT)) { /* Linked-list implementation. */
|
|
if (cfg_val >= p_dev->CfgHS_TotalNbr) { /* Chk if cfg nbr is valid. */
|
|
return ((USBD_CFG *)0);
|
|
}
|
|
p_cfg = p_dev->CfgHS_HeadPtr;
|
|
|
|
} else {
|
|
#endif
|
|
if (cfg_val >= p_dev->CfgFS_TotalNbr) { /* Chk if cfg nbr is valid. */
|
|
return ((USBD_CFG *)0);
|
|
}
|
|
p_cfg = p_dev->CfgFS_HeadPtr;
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
}
|
|
#endif
|
|
|
|
for (cfg_ix = 0u; cfg_ix < cfg_val; cfg_ix++) { /* Iterate thru list until to get cfg struct. */
|
|
p_cfg = p_cfg->NextPtr;
|
|
}
|
|
#endif
|
|
|
|
return (p_cfg);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_EventSet()
|
|
*
|
|
* Description : Send an event to the core task.
|
|
*
|
|
* Argument(s) : p_drv Pointer to device driver.
|
|
*
|
|
* event Event code :
|
|
*
|
|
* USBD_EVENT_BUS_RESET Reset.
|
|
* USBD_EVENT_BUS_SUSPEND Suspend.
|
|
* USBD_EVENT_BUS_RESUME Resume.
|
|
* USBD_EVENT_BUS_CONN Connect.
|
|
* USBD_EVENT_BUS_DISCONN Disconnect.
|
|
* USBD_EVENT_BUS_HS High speed.
|
|
* USBD_EVENT_EP Endpoint.
|
|
* USBD_EVENT_SETUP Setup.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_EventSet (USBD_DRV *p_drv,
|
|
USBD_EVENT_CODE event)
|
|
{
|
|
USBD_CORE_EVENT *p_core_event;
|
|
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (p_drv == (USBD_DRV *)0) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
p_core_event = USBD_CoreEventGet();
|
|
if (p_core_event == (USBD_CORE_EVENT *)0) {
|
|
return;
|
|
}
|
|
|
|
p_core_event->Type = event;
|
|
p_core_event->DrvPtr = p_drv;
|
|
p_core_event->Err = USBD_ERR_NONE;
|
|
|
|
USBD_OS_CoreEventPut(p_core_event);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CoreTaskHandler()
|
|
*
|
|
* Description : Process all core events and core operations.
|
|
*
|
|
* Argument(s) : none.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_CoreTaskHandler (void)
|
|
{
|
|
USBD_CORE_EVENT *p_core_event;
|
|
USBD_DEV *p_dev;
|
|
USBD_DRV *p_drv;
|
|
CPU_INT08U ep_addr;
|
|
USBD_EVENT_CODE event;
|
|
USBD_ERR err;
|
|
|
|
|
|
while (DEF_TRUE) {
|
|
/* Wait for an event. */
|
|
p_core_event = (USBD_CORE_EVENT *)USBD_OS_CoreEventGet(0u, &err);
|
|
if (p_core_event != (USBD_CORE_EVENT *)0) {
|
|
event = p_core_event->Type;
|
|
p_drv = p_core_event->DrvPtr;
|
|
p_dev = USBD_DevRefGet(p_drv->DevNbr);
|
|
|
|
if (p_dev != (USBD_DEV *)0) {
|
|
if (p_dev->State != USBD_DEV_STATE_STOPPING) {
|
|
|
|
switch (event) { /* Decode event. */
|
|
case USBD_EVENT_BUS_RESET: /* -------------------- BUS EVENTS -------------------- */
|
|
case USBD_EVENT_BUS_RESUME:
|
|
case USBD_EVENT_BUS_CONN:
|
|
case USBD_EVENT_BUS_HS:
|
|
case USBD_EVENT_BUS_SUSPEND:
|
|
case USBD_EVENT_BUS_DISCONN:
|
|
USBD_EventProcess(p_dev, event);
|
|
break;
|
|
|
|
case USBD_EVENT_EP: /* ------------------ ENDPOINT EVENTS ----------------- */
|
|
if (p_dev->State == USBD_DEV_STATE_SUSPENDED) {
|
|
p_dev->State = p_dev->StatePrev;
|
|
}
|
|
ep_addr = p_core_event->EP_Addr;
|
|
USBD_EP_XferAsyncProcess(p_drv, ep_addr, p_core_event->Err);
|
|
break;
|
|
|
|
case USBD_EVENT_SETUP: /* ------------------- SETUP EVENTS ------------------- */
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, DevSetupEventNbr);
|
|
if (p_dev->State == USBD_DEV_STATE_SUSPENDED) {
|
|
p_dev->State = p_dev->StatePrev;
|
|
}
|
|
USBD_StdReqHandler(p_dev);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
USBD_CoreEventFree(p_core_event); /* Return event to free pool. */
|
|
}
|
|
#if !CONFIG_OS
|
|
else {
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_EventProcess()
|
|
*
|
|
* Description : Process bus related events.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* event Bus related events :
|
|
*
|
|
* USBD_EVENT_BUS_RESET Reset.
|
|
* USBD_EVENT_BUS_SUSPEND Suspend.
|
|
* USBD_EVENT_BUS_RESUME Resume.
|
|
* USBD_EVENT_BUS_CONN Connect.
|
|
* USBD_EVENT_BUS_DISCONN Disconnect.
|
|
* USBD_EVENT_BUS_HS High speed.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) This prevents a suspend event to overwrite the internal status with a suspend state in
|
|
* the case of multiple suspend events in a row.
|
|
*
|
|
* (2) USB Spec 2.0 section 9.1.1.6 states "When suspended, the USB device maintains any
|
|
* internal status, including its address and configuration."
|
|
*
|
|
* (3) A suspend event is usually followed by a resume event when the bus activity comes back.
|
|
* But in some cases, after a suspend event, a reset event can be notified to the Core
|
|
* before a resume event. Thus, the internal state of the device should not be changed
|
|
* to the previous one.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_EventProcess (USBD_DEV *p_dev,
|
|
USBD_EVENT_CODE event)
|
|
{
|
|
USBD_BUS_FNCTS *p_bus_fnct;
|
|
USBD_ERR err;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_bus_fnct = p_dev->BusFnctsPtr;
|
|
|
|
switch (event) {
|
|
case USBD_EVENT_BUS_RESET: /* -------------------- RESET EVENT ------------------- */
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, DevResetEventNbr);
|
|
USBD_DBG_CORE_BUS("Bus Reset");
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (p_dev->ConnStatus == DEF_FALSE) {
|
|
p_dev->ConnStatus = DEF_TRUE;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
if (p_bus_fnct->Conn != (void *)0) {
|
|
p_bus_fnct->Conn(p_dev->Nbr); /* Call application connect callback. */
|
|
}
|
|
} else {
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
USBD_CtrlClose(p_dev->Nbr, &err); /* Close ctrl EP. */
|
|
|
|
if (p_dev->CfgCurNbr != USBD_CFG_NBR_NONE) {
|
|
USBD_CfgClose(p_dev); /* Close curr cfg. */
|
|
}
|
|
|
|
USBD_CtrlOpen(p_dev->Nbr, /* Open ctrl EP. */
|
|
p_dev->EP_CtrlMaxPktSize,
|
|
&err);
|
|
|
|
CPU_CRITICAL_ENTER(); /* Set dev in default state, reset dev speed. */
|
|
p_dev->Addr = 0u;
|
|
p_dev->State = USBD_DEV_STATE_DEFAULT;
|
|
p_dev->Spd = USBD_DEV_SPD_FULL;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
if (p_bus_fnct->Reset != (void *)0) {
|
|
p_bus_fnct->Reset(p_dev->Nbr); /* Call application reset callback. */
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_EVENT_BUS_SUSPEND: /* ------------------- SUSPEND EVENT ------------------ */
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, DevSuspendEventNbr);
|
|
USBD_DBG_CORE_BUS("Bus Suspend");
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (p_dev->State != USBD_DEV_STATE_SUSPENDED) { /* See Note #1. */
|
|
p_dev->StatePrev = p_dev->State; /* Save cur state (see Note #2). */
|
|
}
|
|
p_dev->State = USBD_DEV_STATE_SUSPENDED; /* Set suspended state. */
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
if (p_bus_fnct->Suspend != (void *)0) {
|
|
p_bus_fnct->Suspend(p_dev->Nbr); /* Call application suspend callback. */
|
|
}
|
|
break;
|
|
|
|
case USBD_EVENT_BUS_RESUME: /* ------------------- RESUME EVENT ------------------- */
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, DevResumeEventNbr);
|
|
USBD_DBG_CORE_BUS("Bus Resume");
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (p_dev->State == USBD_DEV_STATE_SUSPENDED) { /* See Note #3. */
|
|
p_dev->State = p_dev->StatePrev; /* Restore prev state. */
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
if (p_bus_fnct->Resume != (void *)0) {
|
|
p_bus_fnct->Resume(p_dev->Nbr); /* Call application resume callback. */
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_EVENT_BUS_CONN: /* ------------------- CONNECT EVENT ------------------ */
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, DevConnEventNbr);
|
|
USBD_DBG_CORE_BUS("Bus Connect");
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->State = USBD_DEV_STATE_ATTACHED; /* Set attached state. */
|
|
p_dev->ConnStatus = DEF_TRUE;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
if (p_bus_fnct->Conn != (void *)0) {
|
|
p_bus_fnct->Conn(p_dev->Nbr); /* Call application connect callback. */
|
|
}
|
|
break;
|
|
|
|
case USBD_EVENT_BUS_DISCONN: /* ----------------- DISCONNECT EVENT ----------------- */
|
|
USBD_DBG_STATS_DEV_INC(p_dev->Nbr, DevDisconnEventNbr);
|
|
USBD_DBG_CORE_BUS("Bus Disconnect");
|
|
|
|
USBD_CtrlClose(p_dev->Nbr, &err); /* Close ctrl EP. */
|
|
|
|
if (p_dev->CfgCurNbr != USBD_CFG_NBR_NONE) {
|
|
USBD_CfgClose(p_dev); /* Close curr cfg. */
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->Addr = 0u; /* Set default address. */
|
|
p_dev->State = USBD_DEV_STATE_INIT; /* Dev is not attached. */
|
|
p_dev->CfgCurNbr = USBD_CFG_NBR_NONE; /* No active cfg. */
|
|
p_dev->ConnStatus = DEF_FALSE;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
if (p_bus_fnct->Disconn != (void *)0) {
|
|
p_bus_fnct->Disconn(p_dev->Nbr); /* Call application disconnect callback. */
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_EVENT_BUS_HS: /* ------------ HIGH-SPEED HANDSHAKE EVENT ------------ */
|
|
USBD_DBG_CORE_BUS("High Speed detection");
|
|
#if (USBD_CFG_HS_EN == DEF_ENABLED)
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->Spd = USBD_DEV_SPD_HIGH;
|
|
if (p_dev->State == USBD_DEV_STATE_SUSPENDED) {
|
|
p_dev->State = p_dev->StatePrev;
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
#endif
|
|
break;
|
|
|
|
case USBD_EVENT_EP:
|
|
case USBD_EVENT_SETUP:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CoreEventGet()
|
|
*
|
|
* Description : Get a new core event from the pool.
|
|
*
|
|
* Argument(s) : none.
|
|
*
|
|
* Return(s) : Pointer to core event, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBD_CORE_EVENT *USBD_CoreEventGet (void)
|
|
{
|
|
USBD_CORE_EVENT *p_core_event;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (USBD_CoreEventPoolIx < 1u) { /* Chk if core event is avail. */
|
|
CPU_CRITICAL_EXIT();
|
|
return ((USBD_CORE_EVENT *)0);
|
|
}
|
|
|
|
USBD_CoreEventPoolIx--;
|
|
p_core_event = USBD_CoreEventPoolPtrs[USBD_CoreEventPoolIx];
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
return (p_core_event);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_CoreEventFree()
|
|
*
|
|
* Description : Return a core event to the pool.
|
|
*
|
|
* Argument(s) : p_core_event Pointer to core event.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_CoreEventFree (USBD_CORE_EVENT *p_core_event)
|
|
{
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (USBD_CoreEventPoolIx == USBD_CORE_EVENT_NBR_TOTAL) {
|
|
CPU_CRITICAL_EXIT();
|
|
return;
|
|
}
|
|
|
|
USBD_CoreEventPoolPtrs[USBD_CoreEventPoolIx] = p_core_event;
|
|
USBD_CoreEventPoolIx++;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IF_RefGet()
|
|
*
|
|
* Description : Get interface structure.
|
|
*
|
|
* Argument(s) : p_cfg Pointer to configuration structure.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* Return(s) : Pointer to interface structure, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBD_IF *USBD_IF_RefGet (const USBD_CFG *p_cfg,
|
|
CPU_INT08U if_nbr)
|
|
{
|
|
USBD_IF *p_if;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
CPU_INT08U if_ix;
|
|
#endif
|
|
|
|
if (if_nbr >= p_cfg->IF_NbrTotal) { /* Chk if IF nbr is valid. */
|
|
return ((USBD_IF *)0);
|
|
}
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Get IF struct. */
|
|
p_if = p_cfg->IF_TblPtrs[if_nbr];
|
|
#else
|
|
p_if = p_cfg->IF_HeadPtr;
|
|
|
|
for (if_ix = 0u; if_ix < if_nbr; if_ix++) {
|
|
p_if = p_if->NextPtr;
|
|
}
|
|
#endif
|
|
|
|
return (p_if);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IF_AltRefGet()
|
|
*
|
|
* Description : Get alternate setting interface structure.
|
|
*
|
|
* Argument(s) : p_if Pointer to interface structure.
|
|
*
|
|
* if_alt_nbr Alternate setting interface number.
|
|
*
|
|
* Return(s) : Pointer to alternate setting interface structure, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static USBD_IF_ALT *USBD_IF_AltRefGet (const USBD_IF *p_if,
|
|
CPU_INT08U if_alt_nbr)
|
|
{
|
|
USBD_IF_ALT *p_if_alt;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
CPU_INT08U if_alt_ix;
|
|
#endif
|
|
|
|
if (if_alt_nbr >= p_if->AltNbrTotal) { /* Chk alt setting nbr. */
|
|
return ((USBD_IF_ALT *)0);
|
|
}
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED) /* Get alt IF struct. */
|
|
p_if_alt = p_if->AltTblPtrs[if_alt_nbr];
|
|
#else
|
|
p_if_alt = p_if->AltHeadPtr;
|
|
|
|
for (if_alt_ix = 0u; if_alt_ix < if_alt_nbr; if_alt_ix++) {
|
|
p_if_alt = p_if_alt->NextPtr;
|
|
}
|
|
#endif
|
|
|
|
return (p_if_alt);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IF_AltOpen()
|
|
*
|
|
* Description : Open all endpoints from the specified alternate setting.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* p_if_alt Pointer to alternate setting interface.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE Alternate setting successfully opened.
|
|
*
|
|
* - RETURNED BY USBD_EP_Open() -
|
|
* See USBD_EP_Open() for additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_IF_AltOpen ( USBD_DEV *p_dev,
|
|
CPU_INT08U if_nbr,
|
|
const USBD_IF_ALT *p_if_alt,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_INT08U ep_nbr;
|
|
CPU_INT08U ep_phy_nbr;
|
|
CPU_BOOLEAN valid;
|
|
USBD_EP_INFO *p_ep;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
CPU_INT32U ep_alloc_map;
|
|
#endif
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
valid = DEF_OK;
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
ep_alloc_map = p_if_alt->EP_TblMap;
|
|
while (ep_alloc_map != DEF_BIT_NONE) {
|
|
ep_nbr = (CPU_INT08U)CPU_CntTrailZeros32(ep_alloc_map);
|
|
p_ep = p_if_alt->EP_TblPtrs[ep_nbr];
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(p_ep->Addr);
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->EP_IF_Tbl[ep_phy_nbr] = if_nbr;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
USBD_EP_Open(&p_dev->Drv,
|
|
p_ep->Addr,
|
|
p_ep->MaxPktSize,
|
|
p_ep->Attrib,
|
|
p_ep->Interval,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
valid = DEF_FAIL;
|
|
break;
|
|
}
|
|
|
|
DEF_BIT_CLR(ep_alloc_map, DEF_BIT32(ep_nbr));
|
|
}
|
|
#else
|
|
p_ep = p_if_alt->EP_HeadPtr;
|
|
|
|
for (ep_nbr = 0u; ep_nbr < p_if_alt->EP_NbrTotal; ep_nbr++) {
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(p_ep->Addr);
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->EP_IF_Tbl[ep_phy_nbr] = if_nbr;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
USBD_EP_Open(&p_dev->Drv,
|
|
p_ep->Addr,
|
|
p_ep->MaxPktSize,
|
|
p_ep->Attrib,
|
|
p_ep->Interval,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
valid = DEF_FAIL;
|
|
break;
|
|
}
|
|
|
|
p_ep = p_ep->NextPtr;
|
|
}
|
|
#endif
|
|
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
USBD_IF_AltClose(p_dev, p_if_alt);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IF_AltClose()
|
|
*
|
|
* Description : Close all endpoints from the specified alternate setting.
|
|
*
|
|
* Argument(s) : p_dev Pointer to USB device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* p_if_alt Pointer to alternate setting interface.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_IF_AltClose ( USBD_DEV *p_dev,
|
|
const USBD_IF_ALT *p_if_alt)
|
|
{
|
|
CPU_INT08U ep_nbr;
|
|
CPU_INT08U ep_phy_nbr;
|
|
USBD_EP_INFO *p_ep;
|
|
USBD_ERR err;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
CPU_INT32U ep_alloc_map;
|
|
#endif
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
ep_alloc_map = p_if_alt->EP_TblMap;
|
|
while (ep_alloc_map != DEF_BIT_NONE) {
|
|
ep_nbr = (CPU_INT08U)CPU_CntTrailZeros32(ep_alloc_map);
|
|
p_ep = p_if_alt->EP_TblPtrs[ep_nbr];
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(p_ep->Addr);
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->EP_IF_Tbl[ep_phy_nbr] = USBD_IF_NBR_NONE;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
USBD_EP_Close(&p_dev->Drv,
|
|
p_ep->Addr,
|
|
&err);
|
|
|
|
DEF_BIT_CLR(ep_alloc_map, DEF_BIT32(ep_nbr));
|
|
}
|
|
#else
|
|
p_ep = p_if_alt->EP_HeadPtr;
|
|
|
|
for (ep_nbr = 0u; ep_nbr < p_if_alt->EP_NbrTotal; ep_nbr++) {
|
|
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(p_ep->Addr);
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_dev->EP_IF_Tbl[ep_phy_nbr] = USBD_IF_NBR_NONE;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
USBD_EP_Close(&p_dev->Drv,
|
|
p_ep->Addr,
|
|
&err);
|
|
|
|
p_ep = p_ep->NextPtr;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_IF_GrpRefGet()
|
|
*
|
|
* Description : Get interface group structure.
|
|
*
|
|
* Argument(s) : p_cfg Pointer to configuration structure.
|
|
*
|
|
* if_grp_nbr Interface number.
|
|
*
|
|
* Return(s) : Pointer to interface group structure, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_MAX_NBR_IF_GRP > 0)
|
|
static USBD_IF_GRP *USBD_IF_GrpRefGet (const USBD_CFG *p_cfg,
|
|
CPU_INT08U if_grp_nbr)
|
|
{
|
|
USBD_IF_GRP *p_if_grp;
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_DISABLED)
|
|
CPU_INT08U if_grp_ix;
|
|
#endif
|
|
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (if_grp_nbr >= p_cfg->IF_GrpNbrTotal) {
|
|
return ((USBD_IF_GRP *)0);
|
|
}
|
|
#endif
|
|
|
|
#if (USBD_CFG_OPTIMIZE_SPD == DEF_ENABLED)
|
|
p_if_grp = p_cfg->IF_GrpTblPtrs[if_grp_nbr];
|
|
#else
|
|
p_if_grp = p_cfg->IF_GrpHeadPtr;
|
|
|
|
for (if_grp_ix = 0u; if_grp_ix < if_grp_nbr; if_grp_ix++) {
|
|
p_if_grp = p_if_grp->NextPtr;
|
|
}
|
|
#endif
|
|
|
|
return (p_if_grp);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StrDescAdd()
|
|
*
|
|
* Description : Add string to USB device.
|
|
*
|
|
* Argument(s) : p_dev Pointer to device structure.
|
|
* ----- Argument validated in the caller(s).
|
|
*
|
|
* p_str Pointer to string to add (see Note #1).
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function :
|
|
*
|
|
* USBD_ERR_NONE String successfully added.
|
|
* USBD_ERR_ALLOC String cannot be stored in strings table.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) USB spec 2.0 chapter 9.5 states "Where appropriate, descriptors contain references
|
|
* to string descriptors that provide displayable information describing a descriptor
|
|
* in human-readable form. The inclusion of string descriptors is optional. However,
|
|
* the reference fields within descriptors are mandatory. If a device does not support
|
|
* string descriptors, string reference fields must be reset to zero to indicate no
|
|
* string descriptor is available.
|
|
*
|
|
* Since string descriptors are optional, 'p_str' could be a NULL pointer.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
static void USBD_StrDescAdd ( USBD_DEV *p_dev,
|
|
const CPU_CHAR *p_str,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_INT08U str_ix;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
if (p_str == (CPU_CHAR *)0) { /* Return if NULL ptr. */
|
|
return;
|
|
}
|
|
|
|
for (str_ix = 0u; str_ix < p_dev->StrMaxIx; str_ix++) {
|
|
if (p_str == p_dev->StrDesc_Tbl[str_ix]) { /* Str already stored in tbl. */
|
|
*p_err = USBD_ERR_NONE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
str_ix = p_dev->StrMaxIx; /* Get curr str tbl ix. */
|
|
|
|
if (str_ix >= USBD_CFG_MAX_NBR_STR) { /* Chk if str can be stored in tbl. */
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_ALLOC;
|
|
return;
|
|
}
|
|
|
|
p_dev->StrDesc_Tbl[str_ix] = p_str;
|
|
p_dev->StrMaxIx++;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StrDescGet()
|
|
*
|
|
* Description : Get string pointer.
|
|
*
|
|
* Argument(s) : p_dev Pointer to device.
|
|
* ----- Argument validate by the caller(s).
|
|
*
|
|
* str_nbr Number of the string to obtain.
|
|
*
|
|
* Return(s) : Pointer to requested string, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static const CPU_CHAR *USBD_StrDescGet (const USBD_DEV *p_dev,
|
|
CPU_INT08U str_nbr)
|
|
{
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
const CPU_CHAR *p_str;
|
|
|
|
|
|
if (str_nbr > p_dev->StrMaxIx) {
|
|
return ((CPU_CHAR *)0);
|
|
}
|
|
|
|
p_str = p_dev->StrDesc_Tbl[str_nbr];
|
|
return (p_str);
|
|
#else
|
|
(void)p_dev;
|
|
(void)str_nbr;
|
|
|
|
return ((CPU_CHAR *)0);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_StrDescIxGet()
|
|
*
|
|
* Description : Get string index.
|
|
*
|
|
* Argument(s) : p_dev Pointer to device.
|
|
* ----- Argument validated in 'USBD_DevSetupPkt()' before posting the event to queue.
|
|
*
|
|
* p_str Pointer to string.
|
|
*
|
|
* Return(s) : String index.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static CPU_INT08U USBD_StrDescIxGet (const USBD_DEV *p_dev,
|
|
const CPU_CHAR *p_str)
|
|
{
|
|
#if (USBD_CFG_MAX_NBR_STR > 0u)
|
|
CPU_INT08U str_ix;
|
|
|
|
|
|
if (p_str == (CPU_CHAR *)0) { /* Return if a NULL pointer. */
|
|
return (0u);
|
|
}
|
|
|
|
for (str_ix = 0u; str_ix < p_dev->StrMaxIx; str_ix++) {
|
|
if (p_str == p_dev->StrDesc_Tbl[str_ix]) { /* Str already stored in tbl. */
|
|
return (str_ix + 1u);
|
|
}
|
|
}
|
|
#else
|
|
(void)p_dev;
|
|
(void)p_str;
|
|
#endif
|
|
|
|
return (0u);
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Dbg()
|
|
*
|
|
* Description : Debug standard request.
|
|
*
|
|
* Argument(s) : p_msg Debug message to display.
|
|
*
|
|
* ep_addr Endpoint address.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* err Error code associated with the debug message.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
void USBD_Dbg (const CPU_CHAR *p_msg,
|
|
CPU_INT08U ep_addr,
|
|
CPU_INT08U if_nbr,
|
|
USBD_ERR err)
|
|
{
|
|
USBD_DBG_EVENT *p_event;
|
|
|
|
|
|
if (p_msg == (const CPU_CHAR *)0) {
|
|
return;
|
|
}
|
|
|
|
p_event = USBD_DbgEventGet();
|
|
if (p_event != (USBD_DBG_EVENT *)0) {
|
|
p_event->MsgPtr = p_msg;
|
|
p_event->EP_Addr = ep_addr;
|
|
p_event->IF_Nbr = if_nbr;
|
|
p_event->ArgEn = DEF_NO;
|
|
p_event->Err = err;
|
|
USBD_DbgEventPut(p_event);
|
|
USBD_OS_DbgEventRdy();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DbgArg()
|
|
*
|
|
* Description : Debug standard request with argument.
|
|
*
|
|
* Argument(s) : p_msg Debug message to display.
|
|
*
|
|
* ep_addr Endpoint address.
|
|
*
|
|
* if_nbr Interface number.
|
|
*
|
|
* arg Argument associated with the debug message.
|
|
*
|
|
* err Error code associated with the debug message.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
void USBD_DbgArg (const CPU_CHAR *p_msg,
|
|
CPU_INT08U ep_addr,
|
|
CPU_INT08U if_nbr,
|
|
CPU_INT32U arg,
|
|
USBD_ERR err)
|
|
{
|
|
USBD_DBG_EVENT *p_event;
|
|
|
|
|
|
if (p_msg == (const CPU_CHAR *)0) {
|
|
return;
|
|
}
|
|
|
|
p_event = USBD_DbgEventGet();
|
|
if (p_event != (USBD_DBG_EVENT *)0) {
|
|
p_event->MsgPtr = p_msg;
|
|
p_event->EP_Addr = ep_addr;
|
|
p_event->IF_Nbr = if_nbr;
|
|
p_event->ArgEn = DEF_YES;
|
|
p_event->Arg = arg;
|
|
p_event->Err = err;
|
|
USBD_DbgEventPut(p_event);
|
|
USBD_OS_DbgEventRdy();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DbgTaskHandler()
|
|
*
|
|
* Description : Debug event process task.
|
|
*
|
|
* Argument(s) : none.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) This task processes all the debug events queued by the device stack.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
void USBD_DbgTaskHandler (void)
|
|
{
|
|
USBD_DBG_EVENT *p_event;
|
|
CPU_INT32U ts_us;
|
|
CPU_TS_TMR_FREQ ts_freq;
|
|
CPU_INT32U ts_freq_mul;
|
|
CPU_INT32U ts_freq_div;
|
|
CPU_INT32U dbg_cnt_cur;
|
|
CPU_INT32U dbg_cnt_skip;
|
|
CPU_CHAR str[10u];
|
|
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
|
|
CPU_ERR err;
|
|
#endif
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
dbg_cnt_cur = 0u;
|
|
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
|
|
ts_freq = CPU_TS_TmrFreqGet(&err); /* Get TS clk freq. */
|
|
if (ts_freq == 0u) {
|
|
ts_freq = 1u;
|
|
}
|
|
#else
|
|
ts_freq = 1u;
|
|
#endif
|
|
/* Balanced division to convert TS to microseconds. */
|
|
if ((ts_freq % 1000000) == 0) { /* Favor power of 10 TS freq. */
|
|
ts_freq_mul = 1;
|
|
ts_freq_div = ts_freq / 1000000;
|
|
} else if ((ts_freq % 100000) == 0) {
|
|
ts_freq_mul = 10;
|
|
ts_freq_div = ts_freq / 100000;
|
|
} else if ((ts_freq % 10000) == 0) {
|
|
ts_freq_mul = 100;
|
|
ts_freq_div = ts_freq / 10000;
|
|
} else if ((ts_freq % 1000) == 0) {
|
|
ts_freq_mul = 1000;
|
|
ts_freq_div = ts_freq / 1000;
|
|
} else if ((ts_freq % 100) == 0) {
|
|
ts_freq_mul = 10000;
|
|
ts_freq_div = ts_freq / 100;
|
|
/* If not power of 10, keep some accuracy and ... */
|
|
} else if (ts_freq >= 1000000) { /* ... minimize overflow. */
|
|
ts_freq_mul = 100;
|
|
ts_freq_div = (ts_freq + 5000) / 10000;
|
|
} else if (ts_freq >= 100000) {
|
|
ts_freq_mul = 1000;
|
|
ts_freq_div = (ts_freq + 500) / 1000;
|
|
} else {
|
|
ts_freq_mul = 10000;
|
|
ts_freq_div = (ts_freq + 50) / 100;
|
|
}
|
|
|
|
while (DEF_TRUE) {
|
|
USBD_OS_DbgEventWait(); /* Wait until event is avail. */
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_event = USBD_DbgEventHeadPtr;
|
|
if (USBD_DbgEventHeadPtr != (USBD_DBG_EVENT *)0) {
|
|
USBD_DbgEventHeadPtr = p_event->NextPtr;
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
if (p_event != (USBD_DBG_EVENT *)0) {
|
|
if (p_event->Cnt > dbg_cnt_cur) { /* Events skipped if event cnt greater than curr ctr. */
|
|
dbg_cnt_skip = p_event->Cnt - dbg_cnt_cur;
|
|
USBD_Trace("USB ");
|
|
(void)Str_FmtNbr_Int32U(dbg_cnt_skip,
|
|
DEF_INT_32U_NBR_DIG_MAX,
|
|
DEF_NBR_BASE_DEC,
|
|
'\0',
|
|
DEF_NO,
|
|
DEF_YES,
|
|
&str[0u]);
|
|
USBD_Trace(str);
|
|
USBD_Trace(" Skipped event(s) \n\r");
|
|
dbg_cnt_cur = p_event->Cnt; /* Match event ctr. */
|
|
}
|
|
|
|
dbg_cnt_cur++;
|
|
|
|
ts_us = (p_event->Ts * ts_freq_mul) / ts_freq_div; /* Convert TS to microseconds. */
|
|
|
|
USBD_Trace("USB ");
|
|
(void)Str_FmtNbr_Int32U(ts_us,
|
|
DEF_INT_32U_NBR_DIG_MAX,
|
|
DEF_NBR_BASE_DEC,
|
|
' ',
|
|
DEF_NO,
|
|
DEF_YES,
|
|
&str[0u]);
|
|
USBD_Trace(str);
|
|
USBD_Trace(" ");
|
|
if (p_event->EP_Addr != USBD_EP_ADDR_NONE) {
|
|
(void)Str_FmtNbr_Int32U((CPU_INT32U)p_event->EP_Addr,
|
|
2u,
|
|
DEF_NBR_BASE_HEX,
|
|
' ',
|
|
DEF_NO,
|
|
DEF_YES,
|
|
&str[0u]);
|
|
USBD_Trace(str);
|
|
USBD_Trace(" ");
|
|
} else {
|
|
USBD_Trace(" ");
|
|
}
|
|
|
|
if (p_event->IF_Nbr != USBD_IF_NBR_NONE) {
|
|
(void)Str_FmtNbr_Int32U((CPU_INT32U)p_event->IF_Nbr,
|
|
DEF_INT_08U_NBR_DIG_MAX,
|
|
DEF_NBR_BASE_DEC,
|
|
'\0',
|
|
DEF_NO,
|
|
DEF_YES,
|
|
&str[0u]);
|
|
USBD_Trace(str);
|
|
USBD_Trace(" ");
|
|
} else {
|
|
USBD_Trace(" ");
|
|
}
|
|
|
|
|
|
USBD_Trace(p_event->MsgPtr);
|
|
if (p_event->Err != USBD_ERR_NONE) {
|
|
(void)Str_FmtNbr_Int32U((CPU_INT32U)p_event->Err,
|
|
DEF_INT_16U_NBR_DIG_MAX,
|
|
DEF_NBR_BASE_DEC,
|
|
'\0',
|
|
DEF_NO,
|
|
DEF_YES,
|
|
&str[0u]);
|
|
USBD_Trace(str);
|
|
USBD_Trace(" ");
|
|
}
|
|
|
|
if (p_event->ArgEn == DEF_YES) {
|
|
(void)Str_FmtNbr_Int32U(p_event->Arg,
|
|
DEF_INT_32U_NBR_DIG_MAX,
|
|
DEF_NBR_BASE_DEC,
|
|
'\0',
|
|
DEF_NO,
|
|
DEF_YES,
|
|
&str[0u]);
|
|
|
|
USBD_Trace(" ");
|
|
USBD_Trace(str);
|
|
USBD_Trace(" ");
|
|
}
|
|
USBD_Trace("\n\r");
|
|
|
|
USBD_DbgEventFree(p_event); /* Return debug event to free pool. */
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DbgEventGet()
|
|
*
|
|
* Description : Get debug event object.
|
|
*
|
|
* Argument(s) : none.
|
|
*
|
|
* Return(s) : Pointer to debug event object, if NO error(s).
|
|
*
|
|
* Pointer to NULL, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
static USBD_DBG_EVENT *USBD_DbgEventGet (void)
|
|
{
|
|
USBD_DBG_EVENT *p_event;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (USBD_DbgEventFreePtr == (USBD_DBG_EVENT *)0) {
|
|
USBD_DbgEventCtr++;
|
|
CPU_CRITICAL_EXIT();
|
|
return ((USBD_DBG_EVENT *)0);
|
|
}
|
|
|
|
p_event = USBD_DbgEventFreePtr;
|
|
p_event->Cnt = USBD_DbgEventCtr;
|
|
USBD_DbgEventFreePtr = USBD_DbgEventFreePtr->NextPtr;
|
|
USBD_DbgEventCtr++;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
|
|
p_event->Ts = CPU_TS_Get32();
|
|
#else
|
|
p_event->Ts = 0u;
|
|
#endif
|
|
p_event->NextPtr = (USBD_DBG_EVENT *)0;
|
|
|
|
return (p_event);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DbgEventPut()
|
|
*
|
|
* Description : Queue a debug event object.
|
|
*
|
|
* Argument(s) : p_event Pointer to debug event to queue.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
static void USBD_DbgEventPut (USBD_DBG_EVENT *p_event)
|
|
{
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (USBD_DbgEventHeadPtr == (USBD_DBG_EVENT *)0) {
|
|
USBD_DbgEventHeadPtr = p_event;
|
|
} else {
|
|
USBD_DbgEventTailPtr->NextPtr = p_event;
|
|
}
|
|
USBD_DbgEventTailPtr = p_event;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_DbgEventFree()
|
|
*
|
|
* Description : Return a debug event object to the free list.
|
|
*
|
|
* Argument(s) : p_event Pointer to debug event to return.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
|
|
static void USBD_DbgEventFree (USBD_DBG_EVENT *p_event)
|
|
{
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (USBD_DbgEventFreePtr == (USBD_DBG_EVENT *)0) {
|
|
p_event->NextPtr = (USBD_DBG_EVENT *)0;
|
|
} else {
|
|
p_event->NextPtr = USBD_DbgEventFreePtr;
|
|
}
|
|
USBD_DbgEventFreePtr = p_event;
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
#endif
|