Files
E3/e3_176_ref/middleware/usb/uC-USBD/Source/usbd_ep.c
2025-10-21 19:40:27 +08:00

4573 lines
176 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.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* USB DEVICE ENDPOINT OPERATIONS
*
* Filename : usbd_ep.c
* Version : V4.06.01
*********************************************************************************************************
* Note(s) : (1) High-speed isochronous transfer not supported.
*
* (2) 'goto' statements were used in this software module. Their usage is restricted to
* cleanup purposes in exceptional program flow (e.g. error handling), in compliance
* with CERT MEM12-C and MISRA C:2012 rules 15.2, 15.3 and 15.4.
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#include "usbd_core.h"
#include "usbd_internal.h"
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
#define USBD_EP_ADDR_CTRL_OUT 0x00u
#define USBD_EP_ADDR_CTRL_IN 0x80u
#define USBD_URB_MAX_NBR (USBD_CFG_MAX_NBR_URB_EXTRA + \
USBD_CFG_MAX_NBR_EP_OPEN)
#define USBD_URB_FLAG_XFER_END DEF_BIT_00 /* Flag indicating if xfer requires a ZLP to complete. */
#define USBD_URB_FLAG_EXTRA_URB DEF_BIT_01 /* Flag indicating if the URB is an 'extra' URB. */
/*
*********************************************************************************************************
* LOCAL CONSTANTS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* ENDPOINT STATES
*********************************************************************************************************
*/
typedef enum usbd_ep_state {
USBD_EP_STATE_CLOSE = 0,
USBD_EP_STATE_OPEN,
USBD_EP_STATE_STALL
} USBD_EP_STATE;
/*
*********************************************************************************************************
* TRANSFER STATES
*
* Note(s): (1) If an asynchronous transfer cannot be fully queued in the driver, no more transfer can be
* queued, to respect the transfers sequence.
* For example, if a driver can queue only 512 bytes at once and the class/application needs
* to queue 518 bytes, the first 512 bytes will be queued and it will be impossible to queue
* another transaction. The remaining 6 bytes will only be queued when the previous (512
* bytes) transaction completes. The state of the endpoint will be changed to
* USBD_EP_XFER_TYPE_ASYNC_PARTIAL and other transfers could be queued after this one.
*********************************************************************************************************
*/
typedef enum usbd_xfer_state {
USBD_XFER_STATE_NONE = 0, /* No xfer in progress. */
USBD_XFER_STATE_SYNC, /* Sync xfer in progress. */
USBD_XFER_STATE_ASYNC, /* Async xfer(s) in progress. */
USBD_XFER_STATE_ASYNC_PARTIAL /* Partial async xfer(s) in progress (see Note #1). */
} USBD_XFER_STATE;
/*
*********************************************************************************************************
* URB STATES
*********************************************************************************************************
*/
typedef enum usbd_urb_state {
USBD_URB_STATE_IDLE = 0, /* URB is in the memory pool, not used by any EP. */
USBD_URB_STATE_XFER_SYNC, /* URB is used for a sync xfer. */
USBD_URB_STATE_XFER_ASYNC /* URB is used for an async xfer. */
} USBD_URB_STATE;
/*
*********************************************************************************************************
* ENDPOINT USB REQUEST BLOCK DATA TYPE
*
* Note(s): (1) The 'Flags' field is used as a bitmap. The following bits are used:
*
* D7..2 Reserved (reset to zero)
* D1 End-of-transfer:
* If this bit is set and transfer length is multiple of maximum packet
* size, a zero-length packet is transferred to indicate a short transfer to
* the host.
* D0 Extra URB:
* If this bit is set, it indicates that this URB is considered an 'extra'
* URB, that is shared amongst all endpoints. If this bit is cleared, it
* indicates that this URB is 'reserved' to allow every endpoint to have at
* least one URB available at any time.
*
*********************************************************************************************************
*/
typedef struct usbd_urb {
CPU_INT08U *BufPtr; /* Pointer to buffer. */
CPU_INT32U BufLen; /* Buffer length. */
CPU_INT32U XferLen; /* Length that has been transferred. */
CPU_INT32U NextXferLen; /* Length of the next transfer. */
CPU_INT08U Flags; /* Flags (see Note #1). */
USBD_URB_STATE State; /* State of the transaction. */
USBD_ASYNC_FNCT AsyncFnct; /* Asynchronous notification function. */
void *AsyncFnctArg; /* Asynchronous function argument. */
USBD_ERR Err; /* Error passed to callback, if any. */
struct usbd_urb *NextPtr; /* Pointer to next URB in list. */
} USBD_URB;
/*
*********************************************************************************************************
* ENDPOINT DATA TYPE
*********************************************************************************************************
*/
typedef struct usbd_ep {
USBD_EP_STATE State; /* EP state. */
USBD_XFER_STATE XferState; /* Xfer state. */
CPU_INT08U Addr; /* Address. */
CPU_INT08U Attrib; /* Attributes. */
CPU_INT16U MaxPktSize; /* Maximum packet size. */
CPU_INT08U Interval; /* Interval. */
CPU_INT08U TransPerFrame; /* Transaction per microframe (HS only). */
CPU_INT08U Ix; /* Allocation index. */
#if (USBD_CFG_MAX_NBR_URB_EXTRA > 0u)
CPU_BOOLEAN URB_MainAvail; /* Flag indicating if main URB associated to EP avail. */
#endif
USBD_URB *URB_HeadPtr; /* USB request block head of the list. */
USBD_URB *URB_TailPtr; /* USB request block tail of the list. */
} USBD_EP;
/*
*********************************************************************************************************
* LOCAL MACROS
*********************************************************************************************************
*/
#if (USBD_CFG_DBG_TRACE_EN == DEF_ENABLED)
#define USBD_DBG_EP(msg, ep_addr) USBD_DBG_GENERIC((msg), \
(ep_addr), \
USBD_IF_NBR_NONE)
#define USBD_ERR_LINE_0(x) #x
#define USBD_ERR_LINE(x) USBD_ERR_LINE_0(x)
#define USBD_DBG_EP_ERR(msg, ep_addr, err) USBD_DBG_GENERIC_ERR( msg " line: "USBD_ERR_LINE(__LINE__)" error: ", \
ep_addr, \
USBD_IF_NBR_NONE, \
(err))
#define USBD_DBG_EP_ARG(msg, ep_addr, arg) USBD_DBG_GENERIC_ARG((msg), \
ep_addr, \
USBD_IF_NBR_NONE, \
(arg))
#else
#define USBD_DBG_EP(msg, ep_addr)
#define USBD_DBG_EP_ERR(msg, ep_addr, err)
#define USBD_DBG_EP_ARG(msg, ep_addr, arg)
#endif
/*
*********************************************************************************************************
* LOCAL TABLES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
/* Endpoint structures table. */
static USBD_EP USBD_EP_Tbl[USBD_CFG_MAX_NBR_DEV][USBD_CFG_MAX_NBR_EP_OPEN];
static USBD_EP *USBD_EP_TblPtrs[USBD_CFG_MAX_NBR_DEV][USBD_EP_MAX_NBR];
static CPU_INT08U USBD_EP_OpenCtr[USBD_CFG_MAX_NBR_DEV];
static CPU_INT32U USBD_EP_OpenBitMap[USBD_CFG_MAX_NBR_DEV];
static USBD_URB USBD_URB_Tbl[USBD_CFG_MAX_NBR_DEV][USBD_URB_MAX_NBR];
static USBD_URB *USBD_URB_TblPtr[USBD_CFG_MAX_NBR_DEV];
#if (USBD_CFG_MAX_NBR_URB_EXTRA > 0u)
static CPU_INT08U USBD_URB_ExtraCtr[USBD_CFG_MAX_NBR_DEV]; /* Nbr of extra URB currently used. */
#endif
#if (USBD_CFG_DBG_STATS_EN == DEF_ENABLED)
USBD_DBG_STATS_EP USBD_DbgStatsEP_Tbl[USBD_CFG_MAX_NBR_DEV][USBD_CFG_MAX_NBR_EP_OPEN];
#endif
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void USBD_EP_RxStartAsyncProcess(USBD_DRV *p_drv,
USBD_EP *p_ep,
USBD_URB *p_urb,
CPU_INT08U *p_buf_cur,
CPU_INT32U len,
USBD_ERR *p_err);
static void USBD_EP_TxAsyncProcess (USBD_DRV *p_drv,
USBD_EP *p_ep,
USBD_URB *p_urb,
CPU_INT08U *p_buf_cur,
CPU_INT32U len,
USBD_ERR *p_err);
static CPU_INT32U USBD_EP_Rx (USBD_DRV *p_drv,
USBD_EP *p_ep,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
CPU_INT16U timeout_ms,
USBD_ERR *p_err);
static CPU_INT32U USBD_EP_Tx (USBD_DRV *p_drv,
USBD_EP *p_ep,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
CPU_INT16U timeout_ms,
CPU_BOOLEAN end,
USBD_ERR *p_err);
static USBD_URB *USBD_EP_URB_Abort (USBD_DRV *p_drv,
USBD_EP *p_ep,
USBD_ERR *p_err);
static USBD_URB *USBD_URB_AsyncCmpl (USBD_EP *p_ep,
USBD_ERR err);
static void USBD_URB_AsyncEnd (CPU_INT08U dev_nbr,
USBD_EP *p_ep,
USBD_URB *p_urb_head);
static void USBD_URB_Free (CPU_INT08U dev_nbr,
USBD_EP *p_ep,
USBD_URB *p_urb);
static USBD_URB *USBD_URB_Get (CPU_INT08U dev_nbr,
USBD_EP *p_ep,
USBD_ERR *p_err);
static void USBD_URB_Queue (USBD_EP *p_ep,
USBD_URB *p_urb);
static void USBD_URB_Dequeue (USBD_EP *p_ep);
/*
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* BULK TRANSFER FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_BulkRx()
*
* Description : Receive data on Bulk OUT endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to destination buffer to receive data (see Note #1).
*
* buf_len Number of octets to receive.
*
* timeout_ms Timeout in milliseconds.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully received.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Rx() -
* See USBD_EP_Rx() for additional return error codes.
*
* Return(s) : Number of octets received, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) Receive buffer must be at least aligned on a word.
*********************************************************************************************************
*/
CPU_INT32U USBD_BulkRx (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
USBD_DEV_STATE state;
CPU_INT32U xfer_len;
CPU_INT08U ep_phy_nbr;
USBD_DBG_STATS_DEV_INC(dev_nbr, BulkRxSyncExecNbr);
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (0u);
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return (0u);
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return (0u);
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return (0u);
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_BULK) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_OUT)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return (0u);
}
xfer_len = USBD_EP_Rx( p_drv, /* Call generic EP rx fnct. */
p_ep,
p_buf,
buf_len,
(USBD_ASYNC_FNCT)0,
(void *)0,
timeout_ms,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, BulkRxSyncSuccessNbr, (*p_err == USBD_ERR_NONE));
return (xfer_len);
}
/*
*********************************************************************************************************
* USBD_BulkRxAsync()
*
* Description : Receive data on Bulk OUT endpoint asynchronously.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to destination buffer to receive data (see Note #1).
*
* buf_len Number of octets to receive.
*
* async_fnct Function that will be invoked upon completion of receive operation.
*
* p_async_arg Pointer to argument that will be passed as parameter of 'async_fnct'.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully received.
* USBD_ERR_NULL_PTR Parameter 'async_fnct' is a null pointer.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Rx() -
* See USBD_EP_Rx() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) Receive buffer must be at least aligned on a word.
*********************************************************************************************************
*/
void USBD_BulkRxAsync (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
USBD_DEV_STATE state;
CPU_INT08U ep_phy_nbr;
USBD_DBG_STATS_DEV_INC(dev_nbr, BulkRxAsyncExecNbr);
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (async_fnct == (USBD_ASYNC_FNCT)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return;
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_BULK) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_OUT)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return;
}
(void)USBD_EP_Rx(p_drv, /* Call generic EP rx fnct. */
p_ep,
p_buf,
buf_len,
async_fnct,
p_async_arg,
0u,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, BulkRxAsyncSuccessNbr, (*p_err == USBD_ERR_NONE));
}
/*
*********************************************************************************************************
* USBD_BulkTx()
*
* Description : Send data on Bulk IN endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to buffer of data that will be transmitted (see Note #2).
*
* buf_len Number of octets to transmit.
*
* timeout_ms Timeout in milliseconds.
*
* end End-of-transfer flag (see Note #3).
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully transmitted.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Tx() -
* See USBD_EP_Tx() for additional return error codes.
*
* Return(s) : Number of octets transmitted, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) This function SHOULD NOT be called from interrupt service routine (ISR).
*
* (2) Transmit buffer must be at least aligned on a word.
*
* (3) If end-of-transfer is set and transfer length is multiple of maximum packet size,
* a zero-length packet is transferred to indicate a short transfer to the host.
*********************************************************************************************************
*/
CPU_INT32U USBD_BulkTx (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
CPU_BOOLEAN end,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
USBD_DEV_STATE state;
CPU_INT32U xfer_len;
CPU_INT08U ep_phy_nbr;
USBD_DBG_STATS_DEV_INC(dev_nbr, BulkTxSyncExecNbr);
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (0u);
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return (0u);
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return (0u);
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return (0u);
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_BULK) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_IN)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return (0u);
}
xfer_len = USBD_EP_Tx( p_drv, /* Call generic EP tx fnct. */
p_ep,
p_buf,
buf_len,
(USBD_ASYNC_FNCT)0,
(void *)0,
timeout_ms,
end,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, BulkTxSyncSuccessNbr, (*p_err == USBD_ERR_NONE));
return (xfer_len);
}
/*
*********************************************************************************************************
* USBD_BulkTxAsync()
*
* Description : Send data on Bulk IN endpoint asynchronously.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to buffer of data that will be transmitted (see Note #1).
*
* buf_len Number of octets to transmit.
*
* async_fnct Function that will be invoked upon completion of transmit operation.
*
* p_async_arg Pointer to argument that will be passed as parameter of 'async_fnct'.
*
* end End-of-transfer flag (see Note #2).
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully transmitted.
* USBD_ERR_NULL_PTR Parameter 'async_fnct' is a null pointer.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Tx() -
* See USBD_EP_Tx() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) Transmit buffer must be at least aligned on a word.
*
* (2) If end-of-transfer is set and transfer length is multiple of maximum packet size,
* a zero-length packet is transferred to indicate a short transfer to the host.
*********************************************************************************************************
*/
void USBD_BulkTxAsync (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
CPU_BOOLEAN end,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
USBD_DEV_STATE state;
CPU_INT08U ep_phy_nbr;
USBD_DBG_STATS_DEV_INC(dev_nbr, BulkTxAsyncExecNbr);
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (async_fnct == (USBD_ASYNC_FNCT)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return;
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_BULK) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_IN)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return;
}
(void)USBD_EP_Tx(p_drv,
p_ep,
p_buf,
buf_len,
async_fnct,
p_async_arg,
0u,
end,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, BulkTxAsyncSuccessNbr, (*p_err == USBD_ERR_NONE));
}
/*
*********************************************************************************************************
*********************************************************************************************************
* INTERRUPT TRANSFER FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_IntrRx()
*
* Description : Receive data on Interrupt OUT endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to destination buffer to receive data (see Note #2).
*
* buf_len Number of octets to receive.
*
* timeout_ms Timeout in milliseconds.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully received.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Rx() -
* See USBD_EP_Rx() for additional return error codes.
*
* Return(s) : Number of octets received, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) This function SHOULD NOT be called from interrupt service routine (ISR).
*
* (2) Receive buffer must be at least aligned on a word.
*********************************************************************************************************
*/
CPU_INT32U USBD_IntrRx (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
USBD_DEV_STATE state;
CPU_INT32U xfer_len;
CPU_INT08U ep_phy_nbr;
USBD_DBG_STATS_DEV_INC(dev_nbr, IntrRxSyncExecNbr);
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (0u);
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return (0u);
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return (0u);
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return (0u);
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_INTR) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_OUT)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return (0u);
}
xfer_len = USBD_EP_Rx( p_drv,
p_ep,
p_buf,
buf_len,
(USBD_ASYNC_FNCT)0,
(void *)0,
timeout_ms,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, IntrRxSyncSuccessNbr, (*p_err == USBD_ERR_NONE));
return (xfer_len);
}
/*
*********************************************************************************************************
* USBD_IntrRxAsync()
*
* Description : Receive data on Interrupt OUT endpoint asynchronously.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to destination buffer to receive data (see Note #1).
*
* buf_len Number of octets to receive.
*
* async_fnct Function that will be invoked upon completion of receive operation.
*
* p_async_arg Pointer to argument that will be passed as parameter of 'async_fnct'.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully received.
* USBD_ERR_NULL_PTR Parameter 'async_fnct' is a null pointer.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Rx() -
* See USBD_EP_Rx() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) Receive buffer must be at least aligned on a word.
*********************************************************************************************************
*/
void USBD_IntrRxAsync (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
CPU_INT08U ep_phy_nbr;
USBD_DEV_STATE state;
USBD_DBG_STATS_DEV_INC(dev_nbr, IntrRxAsyncExecNbr);
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (async_fnct == (USBD_ASYNC_FNCT)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return;
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_INTR) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_OUT)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return;
}
(void)USBD_EP_Rx(p_drv, /* Call generic EP rx fnct. */
p_ep,
p_buf,
buf_len,
async_fnct,
p_async_arg,
0u,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, IntrRxAsyncSuccessNbr, (*p_err == USBD_ERR_NONE));
}
/*
*********************************************************************************************************
* USBD_EP_IntrTx()
*
* Description : Send data on Interrupt IN endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to buffer of data that will be transmitted (see Note #2).
*
* buf_len Number of octets to transmit.
*
* timeout_ms Timeout in milliseconds.
*
* end End-of-transfer flag (see Note #3).
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully transmitted.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Tx() -
* See USBD_EP_Tx() for additional return error codes.
*
* Return(s) : Number of octets transmitted, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) This function SHOULD NOT be called from interrupt service routine (ISR).
*
* (2) Transmit buffer must be at least aligned on a word.
*
* (3) If end-of-transfer is set and transfer length is multiple of maximum packet size,
* a zero-length packet is transferred to indicate a short transfer to the host.
*********************************************************************************************************
*/
CPU_INT32U USBD_IntrTx (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
CPU_BOOLEAN end,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
CPU_INT08U ep_phy_nbr;
CPU_INT32U xfer_len;
USBD_DEV_STATE state;
USBD_DBG_STATS_DEV_INC(dev_nbr, IntrTxSyncExecNbr);
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (0u);
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return (0u);
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return (0u);
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return (0u);
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_INTR) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_IN)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return (0u);
}
xfer_len = USBD_EP_Tx( p_drv,
p_ep,
p_buf,
buf_len,
(USBD_ASYNC_FNCT)0,
(void *)0,
timeout_ms,
end,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, IntrTxSyncSuccessNbr, (*p_err == USBD_ERR_NONE));
return (xfer_len);
}
/*
*********************************************************************************************************
* USBD_IntrTxAsync()
*
* Description : Send data on Interrupt IN endpoint asynchronously.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to buffer of data that will be transmitted (see Note #1).
*
* buf_len Number of octets to transmit.
*
* async_fnct Function that will be invoked upon completion of transmit operation.
*
* p_async_arg Pointer to argument that will be passed as parameter of 'async_fnct'.
*
* end End-of-transfer flag (see Note #2).
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully transmitted.
* USBD_ERR_NULL_PTR Parameter 'async_fnct' is a null pointer.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Tx() -
* See USBD_EP_Tx() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) Transmit buffer must be at least aligned on a word.
*
* (2) If end-of-transfer is set and transfer length is multiple of maximum packet size,
* a zero-length packet is transferred to indicate a short transfer to the host.
*********************************************************************************************************
*/
void USBD_IntrTxAsync (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
CPU_BOOLEAN end,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
CPU_INT08U ep_phy_nbr;
USBD_DEV_STATE state;
USBD_DBG_STATS_DEV_INC(dev_nbr, IntrTxAsyncExecNbr);
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (async_fnct == (USBD_ASYNC_FNCT)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return;
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_INTR) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_IN)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return;
}
(void)USBD_EP_Tx(p_drv,
p_ep,
p_buf,
buf_len,
async_fnct,
p_async_arg,
0u,
end,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, IntrTxAsyncSuccessNbr, (*p_err == USBD_ERR_NONE));
}
/*
*********************************************************************************************************
* USBD_IsocRxAsync()
*
* Description : Receive data on isochronous OUT endpoint asynchronously.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to destination buffer to receive data (see Note #1).
*
* buf_len Number of octets to receive.
*
* async_fnct Function that will be invoked upon completion of receive operation.
*
* p_async_arg Pointer to argument that will be passed as parameter of 'async_fnct'.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully received.
* USBD_ERR_NULL_PTR Parameter 'async_fnct' is a null pointer.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Rx() -
* See USBD_EP_Rx() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) Receive buffer must be at least aligned on a word.
*********************************************************************************************************
*/
#if (USBD_CFG_EP_ISOC_EN == DEF_ENABLED)
void USBD_IsocRxAsync (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
USBD_DEV_STATE state;
CPU_INT08U ep_phy_nbr;
USBD_DBG_STATS_DEV_INC(dev_nbr, IsocRxAsyncExecNbr);
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (async_fnct == (USBD_ASYNC_FNCT)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return;
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_ISOC) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_OUT)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return;
}
(void)USBD_EP_Rx(p_drv, /* Call generic EP rx fnct. */
p_ep,
p_buf,
buf_len,
async_fnct,
p_async_arg,
0u,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, IsocRxAsyncSuccessNbr, (*p_err == USBD_ERR_NONE));
}
#endif
/*
*********************************************************************************************************
* USBD_IsocTxAsync()
*
* Description : Send data on isochronous IN endpoint asynchronously.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_buf Pointer to buffer of data that will be transmitted (see Note #1).
*
* buf_len Number of octets to transmit.
*
* async_fnct Function that will be invoked upon completion of transmit operation.
*
* p_async_arg Pointer to argument that will be passed as parameter of 'async_fnct'.
**
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully transmitted.
* USBD_ERR_NULL_PTR Parameter 'async_fnct' is a null pointer.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Tx() -
* See USBD_EP_Tx() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) Transmit buffer must be at least aligned on a word.
*********************************************************************************************************
*/
#if (USBD_CFG_EP_ISOC_EN == DEF_ENABLED)
void USBD_IsocTxAsync (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
USBD_DEV_STATE state;
CPU_INT08U ep_phy_nbr;
USBD_DBG_STATS_DEV_INC(dev_nbr, IsocTxAsyncExecNbr);
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (async_fnct == (USBD_ASYNC_FNCT)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
state = USBD_DevStateGet(dev_nbr, p_err);
if (state != USBD_DEV_STATE_CONFIGURED) { /* EP transfers are ONLY allowed in cfg'd state. */
*p_err = USBD_ERR_DEV_INVALID_STATE;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return;
}
/* Chk EP attrib. */
if (((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_ISOC) ||
((ep_addr & USBD_EP_DIR_MASK) != USBD_EP_DIR_IN)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return;
}
(void)USBD_EP_Tx(p_drv,
p_ep,
p_buf,
buf_len,
async_fnct,
p_async_arg,
0u,
DEF_NO,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, IsocTxAsyncSuccessNbr, (*p_err == USBD_ERR_NONE));
}
#endif
/*
*********************************************************************************************************
* USBD_CtrlTxStatus()
*
* Description : Handle status stage from host on control (EP0) IN endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* timeout_ms Timeout in milliseconds.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Status stage successfully completed.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* default/addressed/configured state.
*
* - RETURNED BY USBD_DevStateGet() -
* See USBD_DevStateGet() for additional return error codes.
*
* - RETURNED BY USBD_EP_TxZLP() -
* See USBD_EP_TxZLP() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_CtrlTxStatus (CPU_INT08U dev_nbr,
CPU_INT16U timeout_ms,
USBD_ERR *p_err)
{
USBD_DEV_STATE state;
USBD_DBG_STATS_DEV_INC(dev_nbr, CtrlTxStatusExecNbr);
state = USBD_DevStateGet(dev_nbr, p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if ((state != USBD_DEV_STATE_DEFAULT) &&
(state != USBD_DEV_STATE_ADDRESSED) &&
(state != USBD_DEV_STATE_CONFIGURED)) {
*p_err = USBD_ERR_DEV_INVALID_STATE;
return;
}
USBD_EP_TxZLP(dev_nbr,
USBD_EP_ADDR_CTRL_IN,
timeout_ms,
p_err);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, CtrlTxStatusSuccessNbr, (*p_err == USBD_ERR_NONE));
}
/*
*********************************************************************************************************
* USBD_CtrlRx()
*
* Description : Receive data on Control OUT endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* p_buf Pointer to destination buffer to receive data.
*
* buf_len Number of octets to receive.
*
* timeout_ms Timeout in milliseconds.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully received.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* default/addressed/configured state.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Rx() -
* See USBD_EP_Rx() for additional return error codes.
*
* Return(s) : Number of octets received, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : none.
*********************************************************************************************************
*/
CPU_INT32U USBD_CtrlRx (CPU_INT08U dev_nbr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
USBD_ERR *p_err)
{
USBD_DRV *p_drv;
USBD_EP *p_ep;
CPU_INT08U ep_phy_nbr;
USBD_DEV_STATE state;
CPU_INT32U xfer_len;
USBD_DBG_STATS_DEV_INC(dev_nbr, CtrlRxSyncExecNbr);
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (0u);
}
state = USBD_DevStateGet(dev_nbr, p_err);
if ((state != USBD_DEV_STATE_DEFAULT) &&
(state != USBD_DEV_STATE_ADDRESSED) &&
(state != USBD_DEV_STATE_CONFIGURED)) {
*p_err = USBD_ERR_DEV_INVALID_STATE;
return (0u);
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(USBD_EP_ADDR_CTRL_OUT);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return (0u);
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return (0u);
}
/* Chk EP attrib. */
if ((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_CTRL) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return (0u);
}
xfer_len = USBD_EP_Rx( p_drv,
p_ep,
p_buf,
buf_len,
(USBD_ASYNC_FNCT)0,
(void *)0,
timeout_ms,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, CtrlRxSyncSuccessNbr, (*p_err == USBD_ERR_NONE));
return (xfer_len);
}
/*
*********************************************************************************************************
* USBD_CtrlTx()
*
* Description : Send data on Control IN endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* p_buf Pointer to buffer of data that will be sent.
*
* buf_len Number of octets to transmit.
*
* timeout_ms Timeout in milliseconds.
*
* end End-of-transfer flag (see Note #1).
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully transmitted.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* configured state.
* USBD_ERR_INVALID_ARG Invalid argument(s): 'buf_len'.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_Tx() -
* See USBD_EP_Tx() for additional return error codes.
*
* Return(s) : Number of octets transmitted, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) If end-of-transfer is set and transfer length is multiple of maximum packet size,
* a zero-length packet is transferred to indicate a short transfer to the host.
*********************************************************************************************************
*/
CPU_INT32U USBD_CtrlTx (CPU_INT08U dev_nbr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT16U timeout_ms,
CPU_BOOLEAN end,
USBD_ERR *p_err)
{
USBD_DRV *p_drv;
USBD_EP *p_ep;
CPU_INT08U ep_phy_nbr;
USBD_DEV_STATE state;
CPU_INT32U xfer_len;
USBD_DBG_STATS_DEV_INC(dev_nbr, CtrlTxSyncExecNbr);
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (0u);
}
state = USBD_DevStateGet(dev_nbr, p_err);
if ((state != USBD_DEV_STATE_DEFAULT) &&
(state != USBD_DEV_STATE_ADDRESSED) &&
(state != USBD_DEV_STATE_CONFIGURED)) {
*p_err = USBD_ERR_DEV_INVALID_STATE;
return (0u);
}
if (buf_len == 0u) {
*p_err = USBD_ERR_INVALID_ARG;
return (0u);
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(USBD_EP_ADDR_CTRL_IN);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return (0u);
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
if (p_ep->State != USBD_EP_STATE_OPEN) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return (0u);
}
/* Chk EP attrib. */
if ((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_CTRL) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_TYPE;
return (0u);
}
xfer_len = USBD_EP_Tx( p_drv,
p_ep,
p_buf,
buf_len,
(USBD_ASYNC_FNCT)0,
(void *)0,
timeout_ms,
end,
p_err);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, CtrlTxSyncSuccessNbr, (*p_err == USBD_ERR_NONE));
return (xfer_len);
}
/*
*********************************************************************************************************
* USBD_CtrlRxStatus()
*
* Description : Handle status stage from host on control (EP0) OUT endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* timeout_ms Timeout in milliseconds.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Status stage successfully completed.
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
* default/addressed/configured state.
*
* - RETURNED BY USBD_DevStateGet() -
* See USBD_DevStateGet() for additional return error codes.
*
* - RETURNED BY USBD_EP_RxZLP() -
* See USBD_EP_RxZLP() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_CtrlRxStatus (CPU_INT08U dev_nbr,
CPU_INT16U timeout_ms,
USBD_ERR *p_err)
{
USBD_DEV_STATE state;
USBD_DBG_STATS_DEV_INC(dev_nbr, CtrlRxStatusExecNbr);
state = USBD_DevStateGet(dev_nbr, p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if ((state != USBD_DEV_STATE_DEFAULT) &&
(state != USBD_DEV_STATE_ADDRESSED) &&
(state != USBD_DEV_STATE_CONFIGURED)) {
*p_err = USBD_ERR_DEV_INVALID_STATE;
return;
}
USBD_EP_RxZLP(dev_nbr,
USBD_EP_ADDR_CTRL_OUT,
timeout_ms,
p_err);
USBD_DBG_STATS_DEV_INC_IF_TRUE(dev_nbr, CtrlRxStatusSuccessNbr, (*p_err == USBD_ERR_NONE));
}
/*
*********************************************************************************************************
*********************************************************************************************************
* CONTROL ENDPOINT FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_CtrlOpen()
*
* Description : Open control endpoints.
*
* Argument(s) : dev_nbr Device number.
*
* max_pkt_size Maximum packet size.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Control endpoint successfully opened.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
*
* - RETURNED BY USBD_EP_Open() -
* See USBD_EP_Open() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_CtrlOpen (CPU_INT08U dev_nbr,
CPU_INT16U max_pkt_size,
USBD_ERR *p_err)
{
USBD_DRV *p_drv;
USBD_ERR local_err;
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
USBD_EP_Open(p_drv,
USBD_EP_ADDR_CTRL_IN,
max_pkt_size,
USBD_EP_TYPE_CTRL,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
USBD_EP_Open(p_drv,
USBD_EP_ADDR_CTRL_OUT,
max_pkt_size,
USBD_EP_TYPE_CTRL,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
USBD_EP_Close(p_drv, USBD_EP_ADDR_CTRL_IN, &local_err);
return;
}
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_CtrlClose()
*
* Description : Close control endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Control endpoint successfully closed.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
*
* - RETURNED BY USBD_EP_Close() -
* See USBD_EP_Close() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_CtrlClose (CPU_INT08U dev_nbr,
USBD_ERR *p_err)
{
USBD_DRV *p_drv;
USBD_ERR err_in;
USBD_ERR err_out;
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
USBD_EP_Close(p_drv, USBD_EP_ADDR_CTRL_IN, &err_in);
USBD_EP_Close(p_drv, USBD_EP_ADDR_CTRL_OUT, &err_out);
if (err_in != USBD_ERR_NONE) {
*p_err = err_in;
} else {
*p_err = err_out;
}
}
/*
*********************************************************************************************************
* USBD_CtrlStall()
*
* Description : Stall control endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Control endpoint successfully stalled.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_STALL Device driver stall endpoint failed.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : None.
*********************************************************************************************************
*/
void USBD_CtrlStall (CPU_INT08U dev_nbr,
USBD_ERR *p_err)
{
USBD_EP *p_ep_out;
USBD_EP *p_ep_in;
USBD_DRV *p_drv;
USBD_DRV_API *p_drv_api;
CPU_INT08U ep_phy_nbr;
CPU_BOOLEAN stall_in;
CPU_BOOLEAN stall_out;
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(USBD_EP_ADDR_CTRL_OUT);
p_ep_out = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(USBD_EP_ADDR_CTRL_IN);
p_ep_in = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if ((p_ep_out == (USBD_EP *)0) ||
(p_ep_in == (USBD_EP *)0)) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep_out->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep_in->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep_out->Ix);
return;
}
if ((p_ep_out->State == USBD_EP_STATE_OPEN) &&
(p_ep_in->State == USBD_EP_STATE_OPEN)) {
p_drv_api = p_drv->API_Ptr; /* Get dev drv API struct. */
stall_in = p_drv_api->EP_Stall(p_drv,
USBD_EP_ADDR_CTRL_IN,
DEF_SET);
stall_out = p_drv_api->EP_Stall(p_drv,
USBD_EP_ADDR_CTRL_OUT,
DEF_SET);
if ((stall_in == DEF_FAIL) ||
(stall_out == DEF_FAIL)) {
*p_err = USBD_ERR_EP_STALL;
} else {
*p_err = USBD_ERR_NONE;
}
} else {
*p_err = USBD_ERR_EP_INVALID_STATE;
}
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep_in->Ix);
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep_out->Ix);
}
/*
*********************************************************************************************************
*********************************************************************************************************
* GENERAL ENDPOINT FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_EP_Init()
*
* Description : Initialize endpoint structures.
*
* Argument(s) : none.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_EP_Init (void)
{
USBD_EP *p_ep;
USBD_URB *p_urb;
CPU_INT08U ep_ix;
CPU_INT08U dev_nbr;
CPU_INT16U urb_ix;
for (dev_nbr = 0u; dev_nbr < USBD_CFG_MAX_NBR_DEV; dev_nbr++) {
for (ep_ix = 0u; ep_ix < USBD_EP_MAX_NBR; ep_ix++) {
if (ep_ix < USBD_CFG_MAX_NBR_EP_OPEN) {
p_ep = &USBD_EP_Tbl[dev_nbr][ep_ix];
p_ep->Addr = USBD_EP_ADDR_NONE;
p_ep->Attrib = DEF_BIT_NONE;
p_ep->MaxPktSize = 0u;
p_ep->Interval = 0u;
p_ep->Ix = 0u;
#if (USBD_CFG_MAX_NBR_URB_EXTRA > 0u)
p_ep->URB_MainAvail = DEF_YES;
#endif
p_ep->URB_HeadPtr = (USBD_URB *)0;
p_ep->URB_TailPtr = (USBD_URB *)0;
USBD_DBG_STATS_EP_RESET(dev_nbr, ep_ix);
}
USBD_EP_TblPtrs[dev_nbr][ep_ix] = (USBD_EP *)0;
}
USBD_URB_TblPtr[dev_nbr] = &USBD_URB_Tbl[dev_nbr][0];
for (urb_ix = 0u; urb_ix < USBD_URB_MAX_NBR; urb_ix++) {
p_urb = &USBD_URB_Tbl[dev_nbr][urb_ix];
p_urb->BufPtr = (CPU_INT08U *)0;
p_urb->BufLen = 0u;
p_urb->XferLen = 0u;
p_urb->NextXferLen = 0u;
p_urb->Flags = 0u;
p_urb->State = USBD_URB_STATE_IDLE;
p_urb->AsyncFnct = (USBD_ASYNC_FNCT)0;
p_urb->AsyncFnctArg = (void *)0;
p_urb->Err = USBD_ERR_NONE;
if (urb_ix < (USBD_URB_MAX_NBR - 1)) {
p_urb->NextPtr = &USBD_URB_Tbl[dev_nbr][urb_ix + 1];
} else {
p_urb->NextPtr = (USBD_URB *)0;
}
}
USBD_EP_OpenCtr[dev_nbr] = 0u;
USBD_EP_OpenBitMap[dev_nbr] = DEF_INT_32_MASK;
#if (USBD_CFG_MAX_NBR_URB_EXTRA > 0u)
USBD_URB_ExtraCtr[dev_nbr] = 0u;
#endif
}
}
/*
*********************************************************************************************************
* USBD_EP_XferAsyncProcess()
*
* Description : Read/write data asynchronously from/to non-control endpoints.
*
* Argument(s) : p_drv Pointer to device driver structure.
* ---- Argument checked by caller.
*
* ep_addr Endpoint address.
*
* xfer_err Error code returned by the USB device driver.
*
* Return(s) : none.
*
* Note(s) : (1) A USB device driver can notify the core about the Tx transfer completion using
* USBD_EP_TxCmpl() or USBD_EP_TxCmplExt(). The latter function allows to report a
* specific error code whereas USBD_EP_TxCmpl() reports only a successful transfer.
* In the case of an asynchronous transfer, the error code reported by the USB device
* driver must be tested. In case of an error condition, the asynchronous transfer
* is marked as completed and the associated callback is called by the core task.
*
* (2) This condition covers also the case where the transfer length is multiple of the
* maximum packet size. In that case, host sends a zero-length packet considered as
* a short packet for the condition.
*********************************************************************************************************
*/
void USBD_EP_XferAsyncProcess (USBD_DRV *p_drv,
CPU_INT08U ep_addr,
USBD_ERR xfer_err)
{
CPU_INT08U ep_phy_nbr;
USBD_EP *p_ep;
USBD_DRV_API *p_drv_api;
CPU_BOOLEAN ep_dir_in;
USBD_ERR local_err;
USBD_URB *p_urb;
USBD_URB *p_urb_cmpl;
CPU_INT08U *p_buf_cur;
CPU_INT32U xfer_len;
CPU_INT32U xfer_rem;
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[p_drv->DevNbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
return;
}
p_drv_api = p_drv->API_Ptr;
ep_dir_in = USBD_EP_IS_IN(p_ep->Addr);
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
&local_err);
if (local_err != USBD_ERR_NONE) {
return;
}
if (p_ep->XferState == USBD_XFER_STATE_NONE) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
return;
}
p_urb = p_ep->URB_HeadPtr;
if (p_urb == (USBD_URB *)0) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_EP("USBD_EP_Process(): no URB to process", ep_addr);
return;
}
if ((p_urb->State == USBD_URB_STATE_IDLE) ||
(p_urb->State == USBD_URB_STATE_XFER_SYNC)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_DBG_EP("USBD_EP_Process(): incorrect URB state", ep_addr);
return;
}
p_urb_cmpl = (USBD_URB *)0;
if (xfer_err == USBD_ERR_NONE) { /* See Note #1. */
xfer_rem = p_urb->BufLen - p_urb->XferLen;
p_buf_cur = &p_urb->BufPtr[p_urb->XferLen];
if (ep_dir_in == DEF_YES) { /* ------------------- IN TRANSFER -------------------- */
if (xfer_rem > 0u) { /* Another transaction must be done. */
USBD_EP_TxAsyncProcess(p_drv,
p_ep,
p_urb,
p_buf_cur,
xfer_rem,
&local_err);
if (local_err != USBD_ERR_NONE) {
p_urb_cmpl = USBD_URB_AsyncCmpl(p_ep, local_err);
}
} else if ((DEF_BIT_IS_SET(p_urb->Flags, USBD_URB_FLAG_XFER_END) == DEF_YES) &&
(p_urb->XferLen % p_ep->MaxPktSize == 0u) &&
(p_urb->XferLen != 0u)) {
/* $$$$ This case should be tested more thoroughly. */
/* Send ZLP if needed, at end of xfer. */
DEF_BIT_CLR(p_urb->Flags, USBD_URB_FLAG_XFER_END);
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxZLP_Nbr);
p_drv_api->EP_TxZLP(p_drv, p_ep->Addr, &local_err);
if (local_err != USBD_ERR_NONE) {
p_urb_cmpl = USBD_URB_AsyncCmpl(p_ep, local_err);
}
USBD_DBG_STATS_EP_INC_IF_TRUE(p_drv->DevNbr, p_ep->Ix, DrvTxZLP_Nbr, (local_err == USBD_ERR_NONE));
} else { /* Xfer is completed. */
p_urb_cmpl = USBD_URB_AsyncCmpl(p_ep, USBD_ERR_NONE);
}
} else { /* ------------------- OUT TRANSFER ------------------- */
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvRxNbr);
xfer_len = p_drv_api->EP_Rx(p_drv,
p_ep->Addr,
p_buf_cur,
p_urb->NextXferLen,
&local_err);
if (local_err != USBD_ERR_NONE) {
p_urb_cmpl = USBD_URB_AsyncCmpl(p_ep, local_err);
} else {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvRxSuccessNbr);
p_urb->XferLen += xfer_len;
if ((xfer_len == 0u) || /* Rx'd a ZLP. */
(xfer_len < p_urb->NextXferLen) || /* Rx'd a short pkt (see Note #2). */
(p_urb->XferLen == p_urb->BufLen)) { /* All bytes rx'd. */
/* Xfer finished. */
p_urb_cmpl = USBD_URB_AsyncCmpl(p_ep, USBD_ERR_NONE);
} else {
p_buf_cur = &p_urb->BufPtr[p_urb->XferLen]; /* Xfer not finished. */
xfer_len = p_urb->BufLen - p_urb->XferLen;
USBD_EP_RxStartAsyncProcess(p_drv,
p_ep,
p_urb,
p_buf_cur,
xfer_len,
&local_err);
if (local_err != USBD_ERR_NONE) {
p_urb_cmpl = USBD_URB_AsyncCmpl(p_ep, local_err);
}
}
}
}
} else {
p_urb_cmpl = USBD_URB_AsyncCmpl(p_ep, xfer_err);
}
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
if (p_urb_cmpl != (USBD_URB *)0) {
USBD_URB_AsyncEnd(p_drv->DevNbr, p_ep, p_urb_cmpl); /* Execute callback and free aborted URB(s), if any. */
}
}
/*
*********************************************************************************************************
* USBD_EP_Open()
*
* Description : Open non-control endpoint.
*
* Argument(s) : p_drv Pointer to device driver structure.
*
* ep_addr Endpoint address.
*
* max_pkt_size Maximum packet size.
*
* attrib Endpoint attributes.
*
* interval Endpoint polling interval.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Endpoint successfully opened.
* USBD_ERR_NULL_PTR Argument 'p_drv' passed a NULL pointer.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available.
*
* - RETURNED BY USBD_OS_EP_SignalCreate() -
* See USBD_OS_EP_SignalCreate() for additional return error codes.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_Open()' -
* See specific driver(s) 'p_drv_api->EP_Open()' for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_EP_Open (USBD_DRV *p_drv,
CPU_INT08U ep_addr,
CPU_INT16U max_pkt_size,
CPU_INT08U attrib,
CPU_INT08U interval,
USBD_ERR *p_err)
{
USBD_DRV_API *p_drv_api;
USBD_EP *p_ep;
CPU_INT08U ep_bit;
CPU_INT08U ep_ix;
CPU_INT08U ep_phy_nbr;
CPU_INT08U dev_nbr;
CPU_INT08U transaction_frame;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
dev_nbr = p_drv->DevNbr;
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep != (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
CPU_CRITICAL_ENTER();
if (USBD_EP_OpenCtr[dev_nbr] == USBD_CFG_MAX_NBR_EP_OPEN) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_EP_NONE_AVAIL;
return;
}
ep_bit = (CPU_INT08U)(USBD_EP_MAX_NBR - 1u - CPU_CntLeadZeros32(USBD_EP_OpenBitMap[dev_nbr]));
DEF_BIT_CLR(USBD_EP_OpenBitMap[dev_nbr], DEF_BIT32(ep_bit));
USBD_EP_OpenCtr[dev_nbr]++;
CPU_CRITICAL_EXIT();
ep_ix = USBD_EP_MAX_NBR - 1u - ep_bit;
USBD_OS_EP_SignalCreate(p_drv->DevNbr, ep_ix, p_err);
if (*p_err != USBD_ERR_NONE) {
goto end_clean;
}
USBD_OS_EP_LockCreate(p_drv->DevNbr, ep_ix, p_err);
if (*p_err != USBD_ERR_NONE) {
goto end_signal_clean;
}
transaction_frame = (max_pkt_size >> 11u) & 0x3;
transaction_frame += 1u;
p_drv_api = p_drv->API_Ptr;
p_drv_api->EP_Open(p_drv, /* Open EP in dev drv. */
ep_addr,
attrib & USBD_EP_TYPE_MASK,
max_pkt_size & 0x7FF, /* Mask out transactions per microframe. */
transaction_frame,
p_err);
if (*p_err != USBD_ERR_NONE) {
goto end_lock_signal_clean;
}
p_ep = &USBD_EP_Tbl[dev_nbr][ep_ix];
CPU_CRITICAL_ENTER();
p_ep->Addr = ep_addr;
p_ep->Attrib = attrib;
p_ep->MaxPktSize = max_pkt_size;
p_ep->Interval = interval;
p_ep->State = USBD_EP_STATE_OPEN;
p_ep->XferState = USBD_XFER_STATE_NONE;
p_ep->Ix = ep_ix;
USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr] = p_ep;
CPU_CRITICAL_EXIT();
#if (USBD_CFG_DBG_STATS_EN == DEF_ENABLED)
/* Reset stats only if EP address changed. */
if (ep_addr != USBD_DBG_STATS_EP_GET(dev_nbr, ep_ix, Addr)) {
USBD_DBG_STATS_EP_RESET(dev_nbr, ep_ix);
USBD_DBG_STATS_EP_SET_ADDR(dev_nbr, ep_ix, ep_addr);
}
#endif
USBD_DBG_STATS_EP_INC(dev_nbr, ep_ix, EP_OpenNbr);
USBD_DBG_EP("EP Open", ep_addr);
*p_err = USBD_ERR_NONE;
return;
end_lock_signal_clean:
USBD_OS_EP_LockDel(p_drv->DevNbr, ep_ix);
end_signal_clean:
USBD_OS_EP_SignalDel(p_drv->DevNbr, ep_ix);
end_clean:
CPU_CRITICAL_ENTER();
DEF_BIT_SET(USBD_EP_OpenBitMap[dev_nbr], DEF_BIT32(ep_bit));
USBD_EP_OpenCtr[dev_nbr] -= 1u;
CPU_CRITICAL_EXIT();
USBD_DBG_EP_ERR("EP Open", ep_addr, *p_err);
return;
}
/*
*********************************************************************************************************
* USBD_EP_MaxPktSizeGet()
*
* Description : Retrieve endpoint maximum packet size.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Endpoint maximum packet size successfully retrieved.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
*
* Return(s) : Maximum packet size, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : none.
*********************************************************************************************************
*/
CPU_INT16U USBD_EP_MaxPktSizeGet (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
CPU_INT08U ep_phy_nbr;
CPU_INT16U max_pkt_len;
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (0u);
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return (0u);
}
max_pkt_len = p_ep->MaxPktSize & 0x7FF; /* Mask out transactions per microframe. */
return (max_pkt_len);
}
/*
*********************************************************************************************************
* USBD_EP_MaxNbrOpenGet()
*
* Description : Retrieve maximum number of opened endpoints.
*
* Argument(s) : dev_nbr Device number.
*
* Return(s) : Maximum number of opened endpoints, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : none.
*********************************************************************************************************
*/
CPU_INT08U USBD_EP_MaxNbrOpenGet (CPU_INT08U dev_nbr)
{
USBD_DRV *p_drv;
CPU_INT08U nbr_open;
p_drv = USBD_DrvRefGet(dev_nbr); /* Get drv struct. */
if (p_drv == (USBD_DRV *)0) {
return (0u);
}
nbr_open = USBD_EP_OpenCtr[dev_nbr];
return (nbr_open);
}
/*
*********************************************************************************************************
* USBD_EP_Abort()
*
* Description : Abort I/O transfer on endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Transfer successfully aborted.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_URB_Abort() -
* See USBD_EP_URB_Abort() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_EP_Abort (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_DRV *p_drv;
CPU_INT08U ep_phy_nbr;
USBD_URB *p_urb_head_aborted;
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
USBD_DBG_STATS_EP_INC(dev_nbr, p_ep->Ix, EP_AbortExecNbr);
if ((p_ep->State != USBD_EP_STATE_OPEN) &&
(p_ep->State != USBD_EP_STATE_STALL)) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_EP_INVALID_STATE;
return;
}
p_urb_head_aborted = USBD_EP_URB_Abort(p_drv, /* Abort xfers in progress, keep ptr to head of list. */
p_ep,
p_err);
USBD_DBG_STATS_EP_INC_IF_TRUE(dev_nbr, p_ep->Ix, EP_AbortSuccessNbr, (*p_err == USBD_ERR_NONE));
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
if (p_urb_head_aborted != (USBD_URB *)0) {
USBD_URB_AsyncEnd(dev_nbr, p_ep, p_urb_head_aborted); /* Execute callback and free aborted URB(s), if any. */
}
}
/*
*********************************************************************************************************
* USBD_EP_Close()
*
* Description : Close non-control endpoint.
*
* Argument(s) : p_drv Pointer to device driver structure.
*
* ep_addr Endpoint address.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Endpoint successfully closed.
* USBD_ERR_NULL_PTR Argument 'p_drv' passed a NULL pointer.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_URB_Abort() -
* See USBD_EP_URB_Abort() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_EP_Close (USBD_DRV *p_drv,
CPU_INT08U ep_addr,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
CPU_INT08U dev_nbr;
CPU_INT08U ep_bit;
CPU_INT08U ep_phy_nbr;
USBD_URB *p_urb_head_aborted;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
dev_nbr = p_drv->DevNbr;
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_ep->State == USBD_EP_STATE_CLOSE) {
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
*p_err = USBD_ERR_NONE;
return;
}
USBD_DBG_STATS_EP_INC(dev_nbr, p_ep->Ix, EP_CloseExecNbr);
ep_bit = USBD_EP_MAX_NBR - 1u - p_ep->Ix;
p_urb_head_aborted = USBD_EP_URB_Abort(p_drv, /* Abort xfers in progress, keep ptr to head of list. */
p_ep,
p_err);
p_ep->State = USBD_EP_STATE_CLOSE;
CPU_CRITICAL_ENTER();
DEF_BIT_SET(USBD_EP_OpenBitMap[dev_nbr], DEF_BIT32(ep_bit));
USBD_EP_OpenCtr[dev_nbr]--;
CPU_CRITICAL_EXIT();
p_drv->API_Ptr->EP_Close(p_drv, ep_addr);
p_ep->XferState = USBD_XFER_STATE_NONE;
USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr] = (USBD_EP *)0;
USBD_DBG_STATS_EP_INC_IF_TRUE(dev_nbr, p_ep->Ix, EP_CloseSuccessNbr, (*p_err == USBD_ERR_NONE));
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
USBD_OS_EP_SignalDel(p_drv->DevNbr, p_ep->Ix);
USBD_OS_EP_LockDel (p_drv->DevNbr, p_ep->Ix);
if (p_urb_head_aborted != (USBD_URB *)0) {
USBD_URB_AsyncEnd(dev_nbr, p_ep, p_urb_head_aborted); /* Execute callback and free aborted URB(s), if any. */
}
}
/*
*********************************************************************************************************
* USBD_EP_Stall()
*
* Description : Stall non-control endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* state Endpoint stall state.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Endpoint successfully stalled.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_STALL Device driver endpoint stall failed.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_EP_URB_Abort() -
* See USBD_EP_URB_Abort() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : None.
*********************************************************************************************************
*/
void USBD_EP_Stall (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
CPU_BOOLEAN state,
USBD_ERR *p_err)
{
USBD_DRV *p_drv;
CPU_INT08U ep_phy_nbr;
USBD_EP *p_ep;
USBD_URB *p_urb_head_aborted;
CPU_BOOLEAN valid;
#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_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
p_urb_head_aborted = (USBD_URB *)0;
switch (p_ep->State) {
case USBD_EP_STATE_OPEN:
if (state == DEF_SET) {
p_urb_head_aborted = USBD_EP_URB_Abort(p_drv, /* Abort xfers in progress, keep ptr to head of list. */
p_ep,
p_err);
if (*p_err != USBD_ERR_NONE) {
break;
}
p_ep->State = USBD_EP_STATE_STALL;
}
valid = p_drv->API_Ptr->EP_Stall(p_drv, p_ep->Addr, state);
if (valid == DEF_FAIL) {
*p_err = USBD_ERR_EP_STALL;
} else {
*p_err = USBD_ERR_NONE;
}
break;
case USBD_EP_STATE_STALL:
if (state == DEF_CLR) {
valid = p_drv->API_Ptr->EP_Stall(p_drv, p_ep->Addr, DEF_CLR);
if (valid == DEF_FAIL) {
*p_err = USBD_ERR_EP_STALL;
} else {
p_ep->State = USBD_EP_STATE_OPEN;
*p_err = USBD_ERR_NONE;
}
}
break;
case USBD_EP_STATE_CLOSE:
default:
*p_err = USBD_ERR_EP_INVALID_STATE;
break;
}
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
if (p_urb_head_aborted != (USBD_URB *)0) {
USBD_URB_AsyncEnd(dev_nbr, p_ep, p_urb_head_aborted); /* Execute callback and free aborted URB(s), if any. */
}
}
/*
*********************************************************************************************************
* USBD_EP_IsStalled()
*
* Description : Get stall status of non-control endpoint.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Stall state successfully retrieved.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
*
* Return(s) : DEF_TRUE, if endpoint is stalled.
*
* DEF_FALSE, otherwise.
*
* Note(s) : none.
*********************************************************************************************************
*/
CPU_BOOLEAN USBD_EP_IsStalled (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
USBD_ERR *p_err)
{
USBD_DRV *p_drv;
USBD_EP *p_ep;
CPU_INT08U ep_phy_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(DEF_NO);
}
#endif
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (DEF_NO);
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return (DEF_NO);
}
*p_err = USBD_ERR_NONE;
if (p_ep->State == USBD_EP_STATE_STALL) {
return (DEF_YES);
} else {
return (DEF_NO);
}
}
/*
*********************************************************************************************************
* USBD_EP_RxCmpl()
*
* Description : Notify USB stack that packet receive has completed.
*
* Argument(s) : p_drv Pointer to device driver structure.
*
* ep_log_nbr Endpoint logical number.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_EP_RxCmpl (USBD_DRV *p_drv,
CPU_INT08U ep_log_nbr)
{
USBD_EP *p_ep;
CPU_INT08U ep_phy_nbr;
USBD_ERR err;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_drv == (USBD_DRV *)0) {
return;
}
#endif
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(USBD_EP_LOG_TO_ADDR_OUT(ep_log_nbr));
p_ep = USBD_EP_TblPtrs[p_drv->DevNbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, RxCmplErrNbr);
return;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, RxCmplNbr);
if (p_ep->XferState == USBD_XFER_STATE_SYNC) {
USBD_OS_EP_SignalPost(p_drv->DevNbr, p_ep->Ix, &err);
} else if ((p_ep->XferState == USBD_XFER_STATE_ASYNC) ||
(p_ep->XferState == USBD_XFER_STATE_ASYNC_PARTIAL)) {
USBD_EventEP(p_drv, p_ep->Addr, USBD_ERR_NONE);
} else {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, RxCmplErrNbr);
USBD_DBG_EP("USBD_EP_RxCmpl(): incorrect XferState", p_ep->Addr);
}
}
/*
*********************************************************************************************************
* USBD_EP_TxCmpl()
*
* Description : Notify USB stack that packet transmit has completed.
*
* Argument(s) : p_drv Pointer to device driver structure.
*
* ep_log_nbr Endpoint logical number.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_EP_TxCmpl (USBD_DRV *p_drv,
CPU_INT08U ep_log_nbr)
{
USBD_EP *p_ep;
CPU_INT08U ep_phy_nbr;
USBD_ERR err;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_drv == (USBD_DRV *)0) {
return;
}
#endif
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(USBD_EP_LOG_TO_ADDR_IN(ep_log_nbr));
p_ep = USBD_EP_TblPtrs[p_drv->DevNbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxCmplErrNbr);
return;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxCmplNbr);
if (p_ep->XferState == USBD_XFER_STATE_SYNC) {
USBD_OS_EP_SignalPost(p_drv->DevNbr, p_ep->Ix, &err);
} else if ((p_ep->XferState == USBD_XFER_STATE_ASYNC) ||
(p_ep->XferState == USBD_XFER_STATE_ASYNC_PARTIAL)) {
USBD_EventEP(p_drv, p_ep->Addr, USBD_ERR_NONE);
} else {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxCmplErrNbr);
USBD_DBG_EP("USBD_EP_TxCmpl(): incorrect XferState", p_ep->Addr);
}
}
/*
*********************************************************************************************************
* USBD_EP_TxCmplExt()
*
* Description : Notify USB stack that packet transmit has completed (see Note #1).
*
* Argument(s) : p_drv Pointer to device driver structure.
*
* ep_log_nbr Endpoint logical number.
*
* err Error code returned by the USB driver.
*
* Return(s) : none.
*
* Note(s) : (1) This function is an alternative to the function USBD_EP_TxCmpl() so that a USB device
* driver can return to the core an error code upon the Tx transfer completion.
*********************************************************************************************************
*/
void USBD_EP_TxCmplExt (USBD_DRV *p_drv,
CPU_INT08U ep_log_nbr,
USBD_ERR xfer_err)
{
USBD_EP *p_ep;
CPU_INT08U ep_phy_nbr;
USBD_ERR local_err;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_drv == (USBD_DRV *)0) {
return;
}
#endif
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(USBD_EP_LOG_TO_ADDR_IN(ep_log_nbr));
p_ep = USBD_EP_TblPtrs[p_drv->DevNbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxCmplErrNbr);
return;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxCmplNbr);
if (p_ep->XferState == USBD_XFER_STATE_SYNC) {
USBD_OS_EP_SignalAbort(p_drv->DevNbr, p_ep->Ix, &local_err);
if (local_err != USBD_ERR_NONE) {
USBD_DBG_EP_ERR("USBD_EP_TxCmplExt()", p_ep->Addr, local_err);
}
} else if ((p_ep->XferState == USBD_XFER_STATE_ASYNC) ||
(p_ep->XferState == USBD_XFER_STATE_ASYNC_PARTIAL)) {
USBD_EventEP(p_drv, p_ep->Addr, xfer_err);
} else {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxCmplErrNbr);
USBD_DBG_EP("USBD_EP_TxCmplExt(): incorrect XferState", p_ep->Addr);
}
}
/*
*********************************************************************************************************
* USBD_EP_TxZLP()
*
* Description : Send zero-length packet to the host.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* timeout_ms Timeout in milliseconds.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully transferred.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
* USBD_ERR_EP_IO_PENDING Data already queued on this endpoint.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_URB_Get() -
* See USBD_URB_Get() for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_TxZLP()' -
* See specific driver(s) 'p_drv_api->EP_TxZLP()' for additional return error codes.
*
* - RETURNED BY USBD_OS_EP_SignalPend() -
* See USBD_OS_EP_SignalPend() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) This function should only be called during a synchronous transfer.
*********************************************************************************************************
*/
void USBD_EP_TxZLP (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
CPU_INT16U timeout_ms,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_URB *p_urb;
USBD_DRV *p_drv;
USBD_DRV_API *p_drv_api;
CPU_INT08U ep_phy_nbr;
USBD_ERR local_err;
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
USBD_DBG_STATS_EP_INC(dev_nbr, p_ep->Ix, TxZLP_ExecNbr);
if (p_ep->State != USBD_EP_STATE_OPEN) {
*p_err = USBD_ERR_EP_INVALID_STATE;
goto lock_release;
}
/* Chk EP attrib. */
if ((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_ISOC) {
*p_err = USBD_ERR_EP_INVALID_TYPE;
goto lock_release;
}
if (p_ep->XferState != USBD_XFER_STATE_NONE) {
*p_err = USBD_ERR_EP_IO_PENDING;
goto lock_release;
}
p_urb = USBD_URB_Get(dev_nbr, p_ep, p_err);
if (*p_err != USBD_ERR_NONE) {
goto lock_release;
}
p_urb->State = USBD_URB_STATE_XFER_SYNC; /* Only State needs to be set to indicate sync xfer. */
p_ep->XferState = USBD_XFER_STATE_SYNC; /* Set XferState before submitting xfer. */
USBD_URB_Queue(p_ep, p_urb);
p_drv_api = p_drv->API_Ptr; /* Get dev drv API struct. */
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxZLP_Nbr);
p_drv_api->EP_TxZLP(p_drv, ep_addr, p_err);
if (*p_err == USBD_ERR_NONE) {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxZLP_SuccessNbr);
USBD_OS_EP_LockRelease(p_drv->DevNbr, /* Unlock before pending on completion. */
p_ep->Ix);
USBD_OS_EP_SignalPend(dev_nbr, p_ep->Ix, timeout_ms, p_err);
USBD_OS_EP_LockAcquire(p_drv->DevNbr, /* Re-lock EP after xfer completion. */
p_ep->Ix,
0u,
&local_err);
if (local_err != USBD_ERR_NONE) {
*p_err = USBD_ERR_OS_FAIL;
} else if (*p_err == USBD_ERR_OS_TIMEOUT) {
p_drv_api->EP_Abort(p_drv, ep_addr);
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxSyncTimeoutErrNbr);
}
}
USBD_URB_Dequeue(p_ep);
USBD_URB_Free(dev_nbr, p_ep, p_urb);
USBD_DBG_STATS_EP_INC_IF_TRUE(dev_nbr, p_ep->Ix, TxZLP_SuccessNbr, (*p_err == USBD_ERR_NONE));
lock_release:
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
}
/*
*********************************************************************************************************
* USBD_EP_RxZLP()
*
* Description : Receive zero-length packet from the host.
*
* Argument(s) : dev_nbr Device number.
*
* ep_addr Endpoint address.
*
* timeout_ms Timeout in milliseconds.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Data successfully transferred.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
* USBD_ERR_EP_IO_PENDING Data already queued on this endpoint.
*
* - RETURNED BY USBD_OS_EP_LockAcquire() -
* See USBD_OS_EP_LockAcquire() for additional return error codes.
*
* - RETURNED BY USBD_URB_Get() -
* See USBD_URB_Get() for additional return error codes.
*
* - RETURNED BY USBD_OS_EP_SignalPend() -
* See USBD_OS_EP_SignalPend() for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_RxStart()' -
* See specific driver(s) 'p_drv_api->EP_RxStart()' for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_RxZLP()' -
* See specific driver(s) 'p_drv_api->EP_RxZLP()' for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) This function should only be called during a synchronous transfer.
*********************************************************************************************************
*/
void USBD_EP_RxZLP (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
CPU_INT16U timeout_ms,
USBD_ERR *p_err)
{
USBD_EP *p_ep;
USBD_URB *p_urb;
USBD_DRV *p_drv;
USBD_DRV_API *p_drv_api;
CPU_INT08U ep_phy_nbr;
USBD_ERR local_err;
p_drv = USBD_DrvRefGet(dev_nbr); /* Get dev struct. */
if (p_drv == (USBD_DRV *)0) {
*p_err = USBD_ERR_DEV_INVALID_NBR;
return;
}
ep_phy_nbr = USBD_EP_ADDR_TO_PHY(ep_addr);
p_ep = USBD_EP_TblPtrs[dev_nbr][ep_phy_nbr];
if (p_ep == (USBD_EP *)0) {
*p_err = USBD_ERR_EP_INVALID_ADDR;
return;
}
USBD_OS_EP_LockAcquire(p_drv->DevNbr,
p_ep->Ix,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
USBD_DBG_STATS_EP_INC(dev_nbr, p_ep->Ix, RxZLP_ExecNbr);
if (p_ep->State != USBD_EP_STATE_OPEN) {
*p_err = USBD_ERR_EP_INVALID_STATE;
goto lock_release;
}
if ((p_ep->Attrib & USBD_EP_TYPE_MASK) == USBD_EP_TYPE_ISOC) {
*p_err = USBD_ERR_EP_INVALID_TYPE;
goto lock_release;
}
if (p_ep->XferState != USBD_XFER_STATE_NONE) {
*p_err = USBD_ERR_EP_IO_PENDING;
goto lock_release;
}
p_urb = USBD_URB_Get(dev_nbr, p_ep, p_err);
if (*p_err != USBD_ERR_NONE) {
goto lock_release;
}
p_urb->State = USBD_URB_STATE_XFER_SYNC; /* Only State needs to be set to indicate sync xfer. */
p_ep->XferState = USBD_XFER_STATE_SYNC; /* Set XferState before submitting xfer. */
USBD_URB_Queue(p_ep, p_urb);
p_drv_api = p_drv->API_Ptr; /* Get dev drv API struct. */
USBD_DBG_STATS_EP_INC(dev_nbr, p_ep->Ix, DrvRxStartNbr);
(void)p_drv_api->EP_RxStart( p_drv,
ep_addr,
(CPU_INT08U *)0u,
0u,
p_err);
if (*p_err == USBD_ERR_NONE) {
USBD_DBG_STATS_EP_INC(dev_nbr, p_ep->Ix, DrvRxStartSuccessNbr);
USBD_OS_EP_LockRelease(p_drv->DevNbr, /* Unlock before pending on completion. */
p_ep->Ix);
USBD_OS_EP_SignalPend(dev_nbr, p_ep->Ix, timeout_ms, p_err);
USBD_OS_EP_LockAcquire(p_drv->DevNbr, /* Re-lock EP after xfer completion. */
p_ep->Ix,
0u,
&local_err);
if ((*p_err == USBD_ERR_NONE) &&
(local_err == USBD_ERR_NONE)) {
USBD_DBG_STATS_EP_INC(dev_nbr, p_ep->Ix, DrvRxZLP_Nbr);
p_drv_api->EP_RxZLP(p_drv,
ep_addr,
p_err);
USBD_DBG_STATS_EP_INC_IF_TRUE(dev_nbr, p_ep->Ix, DrvRxZLP_SuccessNbr, (*p_err == USBD_ERR_NONE));
} else if (*p_err == USBD_ERR_OS_TIMEOUT) {
p_drv_api->EP_Abort(p_drv, ep_addr);
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, RxSyncTimeoutErrNbr);
} else if (local_err != USBD_ERR_NONE) {
*p_err = USBD_ERR_OS_FAIL;
}
}
USBD_URB_Dequeue(p_ep);
USBD_URB_Free(dev_nbr, p_ep, p_urb);
USBD_DBG_STATS_EP_INC_IF_TRUE(dev_nbr, p_ep->Ix, RxZLP_SuccessNbr, (*p_err == USBD_ERR_NONE));
lock_release:
USBD_OS_EP_LockRelease(p_drv->DevNbr,
p_ep->Ix);
}
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_EP_RxStartAsyncProcess()
*
* Description : Process driver's asynchronous RxStart operation.
*
* Argument(s) : p_drv Pointer to device driver structure.
*
* p_ep Pointer to endpoint on which data will be received.
*
* p_urb Pointer to USB request block.
*
* p_buf_cur Pointer to source buffer to receive data.
*
* len Number of octets to receive.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Receive successfully configured.
*
* - RETURNED BY 'p_drv_api->EP_RxStart()' -
* See specific driver(s) 'p_drv_api->EP_RxStart()' for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) Endpoint must be locked when calling this function.
*********************************************************************************************************
*/
static void USBD_EP_RxStartAsyncProcess (USBD_DRV *p_drv,
USBD_EP *p_ep,
USBD_URB *p_urb,
CPU_INT08U *p_buf_cur,
CPU_INT32U len,
USBD_ERR *p_err)
{
USBD_DRV_API *p_drv_api;
CPU_SR_ALLOC();
p_drv_api = p_drv->API_Ptr; /* Get dev drv API struct. */
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvRxStartNbr);
p_urb->NextXferLen = p_drv_api->EP_RxStart(p_drv,
p_ep->Addr,
p_buf_cur,
len,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvRxStartSuccessNbr);
if (p_urb->NextXferLen != len) {
CPU_CRITICAL_ENTER();
p_ep->XferState = USBD_XFER_STATE_ASYNC_PARTIAL; /* Xfer will have to be done in many transactions. */
CPU_CRITICAL_EXIT();
}
}
/*
*********************************************************************************************************
* USBD_EP_TxAsyncProcess()
*
* Description : Process driver's asynchronous Tx operation.
*
* Argument(s) : p_drv Pointer to device driver structure.
*
* p_ep Pointer to endpoint on which data will be transmitted.
*
* p_urb Pointer to USB request block.
*
* p_buf_cur Pointer to source buffer to transmit data.
*
* len Number of octets to transmit.
*
* p_err Pointer to variable that will receive return error code from this function :
*
* USBD_ERR_NONE Transmit successfully configured.
* USBD_ERR_TX Generic Tx error.
*
* - RETURNED BY 'p_drv_api->EP_Tx()' -
* See specific driver(s) 'p_drv_api->EP_Tx()' for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_TxStart()' -
* See specific driver(s) 'p_drv_api->EP_TxStart()' for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : (1) Endpoint must be locked when calling this function.
*********************************************************************************************************
*/
static void USBD_EP_TxAsyncProcess (USBD_DRV *p_drv,
USBD_EP *p_ep,
USBD_URB *p_urb,
CPU_INT08U *p_buf_cur,
CPU_INT32U len,
USBD_ERR *p_err)
{
USBD_DRV_API *p_drv_api;
p_drv_api = p_drv->API_Ptr; /* Get dev drv API struct. */
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxNbr);
p_urb->NextXferLen = p_drv_api->EP_Tx(p_drv,
p_ep->Addr,
p_buf_cur,
len,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_urb->NextXferLen == len) { /* Xfer can be done is a single transaction. */
p_ep->XferState = USBD_XFER_STATE_ASYNC;
} else if ((p_ep->Attrib & USBD_EP_TYPE_MASK) != USBD_EP_TYPE_ISOC) {
p_ep->XferState = USBD_XFER_STATE_ASYNC_PARTIAL; /* Xfer will have to be done in many transactions. */
} else {
*p_err = USBD_ERR_TX; /* Cannot split xfer on isoc EP. */
return;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxSuccessNbr);
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxStartNbr);
p_drv_api->EP_TxStart(p_drv,
p_ep->Addr,
p_buf_cur,
p_urb->NextXferLen,
p_err);
if (*p_err == USBD_ERR_NONE) {
p_urb->XferLen += p_urb->NextXferLen; /* Error not accounted on total xfer len. */
p_urb->NextXferLen = 0u;
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxStartSuccessNbr);
}
return;
}
/*
*********************************************************************************************************
* USBD_EP_Rx()
*
* Description : Receive data on OUT endpoint.This function should not be called from Interrupt Context.
*
* Argument(s) : p_drv Pointer to device driver structure.
* ----- Argument checked by caller.
*
* p_ep Pointer to endpoint on which data will be received.
* ---- Argument checked by caller.
*
* p_buf Pointer to destination buffer to receive data.
*
* buf_len Number of octets to receive.
*
* async_fnct Function that will be invoked upon completion of receive operation.
*
* p_async_arg Pointer to argument that will be passed as parameter of 'async_fnct'.
*
* timeout_ms Timeout in milliseconds.
*
* p_err Pointer to variable that will receive return error code from this function :
* ----- Argument checked by caller.
* USBD_ERR_NONE Data successfully received.
* USBD_ERR_NULL_PTR Null pointer passed as argument.
* USBD_ERR_EP_IO_PENDING Transfer already in progress on endpoint.
* USBD_ERR_OS_FAIL OS operation failed.
* USBD_ERR_RX Generic Rx error.
*
* - RETURNED BY USBD_URB_Get() -
* See USBD_URB_Get() for additional return error codes.
*
* - RETURNED BY USBD_EP_RxStartAsyncProcess() -
* See USBD_EP_RxStartAsyncProcess() for additional return error codes.
*
* - RETURNED BY USBD_OS_EP_SignalPend() -
* See USBD_OS_EP_SignalPend() for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_RxStart()' -
* See specific driver(s) 'p_drv_api->EP_RxStart()' for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_Rx()' -
* See specific driver(s) 'p_drv_api->EP_Rx()' for additional return error codes.
*
* Return(s) : Number of octets received, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) This function SHOULD NOT be called from interrupt service routine (ISR).
*
* (2) Endpoint must be locked when calling this function.
*
* (3) During a synchronous transfer, endpoint is unlocked before pending on transfer
* completion to be able to abort. Since the endpoint is already locked when this
* function is called (see callers functions), it releases the lock before pending and
* re-locks once the transfer completes.
*
* (4) This condition covers also the case where the transfer length is multiple of the
* maximum packet size. In that case, host sends a zero-length packet considered as
* a short packet for the condition.
*********************************************************************************************************
*/
static CPU_INT32U USBD_EP_Rx (USBD_DRV *p_drv,
USBD_EP *p_ep,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
CPU_INT16U timeout_ms,
USBD_ERR *p_err)
{
USBD_URB *p_urb;
USBD_DRV_API *p_drv_api;
USBD_XFER_STATE prev_xfer_state;
CPU_INT08U *p_buf_cur;
CPU_INT32U xfer_len;
CPU_INT32U xfer_tot;
CPU_INT32U prev_xfer_len;
USBD_ERR local_err;
if ((buf_len != 0u) &&
(p_buf == (void *)0)) {
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
if (async_fnct == (USBD_ASYNC_FNCT)0) {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, RxSyncExecNbr);
if (p_ep->XferState != USBD_XFER_STATE_NONE) {
*p_err = USBD_ERR_EP_IO_PENDING;
return (0u);
}
} else {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, RxAsyncExecNbr);
if ((p_ep->XferState != USBD_XFER_STATE_NONE) &&
(p_ep->XferState != USBD_XFER_STATE_ASYNC)) {
*p_err = USBD_ERR_EP_IO_PENDING;
return (0u);
}
}
p_urb = USBD_URB_Get(p_drv->DevNbr, p_ep, p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
p_urb->BufPtr = (CPU_INT08U *)p_buf; /* Init 'p_urb' fields. */
p_urb->BufLen = buf_len;
p_urb->XferLen = 0u;
p_urb->NextXferLen = 0u;
p_urb->AsyncFnct = async_fnct;
p_urb->AsyncFnctArg = p_async_arg;
p_urb->Err = USBD_ERR_NONE;
p_urb->NextPtr = (USBD_URB *)0;
if (async_fnct != (USBD_ASYNC_FNCT)0) { /* -------------------- ASYNC XFER -------------------- */
p_urb->State = USBD_URB_STATE_XFER_ASYNC;
prev_xfer_state = p_ep->XferState; /* Keep prev XferState, to restore in case of err. */
p_ep->XferState = USBD_XFER_STATE_ASYNC; /* Set XferState before submitting the xfer. */
USBD_EP_RxStartAsyncProcess(p_drv,
p_ep,
p_urb,
p_urb->BufPtr,
p_urb->BufLen,
p_err);
if (*p_err == USBD_ERR_NONE) {
USBD_URB_Queue(p_ep, p_urb); /* If no err, queue URB. */
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, RxAsyncSuccessNbr);
} else {
p_ep->XferState = prev_xfer_state; /* If an err occured, restore prev XferState. */
USBD_URB_Free(p_drv->DevNbr, p_ep, p_urb); /* Free URB. */
}
return (0u);
}
p_ep->XferState = USBD_XFER_STATE_SYNC; /* -------------------- SYNC XFER --------------------- */
p_urb->State = USBD_URB_STATE_XFER_SYNC;
USBD_URB_Queue(p_ep, p_urb);
p_drv_api = p_drv->API_Ptr; /* Get dev drv API struct. */
p_urb->NextXferLen = p_urb->BufLen;
*p_err = USBD_ERR_NONE;
while ((*p_err == USBD_ERR_NONE) &&
( p_urb->NextXferLen > 0u)) {
p_buf_cur = &p_urb->BufPtr[p_urb->XferLen];
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvRxStartNbr);
p_urb->NextXferLen = p_drv_api->EP_RxStart(p_drv,
p_ep->Addr,
p_buf_cur,
p_urb->NextXferLen,
p_err);
if (*p_err != USBD_ERR_NONE) {
break;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvRxStartSuccessNbr);
USBD_OS_EP_LockRelease(p_drv->DevNbr, /* Unlock before pending on completion. See Note #3. */
p_ep->Ix);
USBD_OS_EP_SignalPend(p_drv->DevNbr, p_ep->Ix, timeout_ms, p_err);
USBD_OS_EP_LockAcquire(p_drv->DevNbr, /* Re-lock EP after xfer completion. See Note #3. */
p_ep->Ix,
0u,
&local_err);
if (*p_err == USBD_ERR_OS_TIMEOUT) {
p_drv_api->EP_Abort(p_drv, p_ep->Addr);
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, RxSyncTimeoutErrNbr);
break;
} else if (*p_err != USBD_ERR_NONE) {
break;
} else if (local_err != USBD_ERR_NONE) {
*p_err = USBD_ERR_OS_FAIL;
break;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvRxNbr);
xfer_len = p_drv_api->EP_Rx(p_drv,
p_ep->Addr,
p_buf_cur,
p_urb->NextXferLen,
p_err);
if (*p_err != USBD_ERR_NONE) {
break;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvRxSuccessNbr);
if (xfer_len > p_urb->NextXferLen) { /* Rx'd more data than what was expected. */
p_urb->XferLen += p_urb->NextXferLen;
*p_err = USBD_ERR_RX;
} else {
p_urb->XferLen += xfer_len;
prev_xfer_len = p_urb->NextXferLen;
p_urb->NextXferLen = p_urb->BufLen - p_urb->XferLen;
if ((xfer_len == 0u) || /* Rx'd a ZLP. */
(xfer_len < prev_xfer_len)) { /* Rx'd a short pkt (see Note #4). */
p_urb->NextXferLen = 0u;
}
}
}
xfer_tot = p_urb->XferLen;
USBD_URB_Dequeue(p_ep);
USBD_URB_Free(p_drv->DevNbr, p_ep, p_urb);
USBD_DBG_STATS_EP_INC_IF_TRUE(p_drv->DevNbr, p_ep->Ix, RxSyncSuccessNbr, (*p_err == USBD_ERR_NONE));
return (xfer_tot);
}
/*
*********************************************************************************************************
* USBD_EP_Tx()
*
* Description : Send data on IN endpoints.
*
* Argument(s) : p_drv Pointer to device driver structure.
* ----- Argument checked by caller.
*
* p_ep Pointer to endpoint on which data will be sent.
* ---- Argument checked by caller.
*
* p_buf Pointer to buffer of data that will be sent.
*
* buf_len Number of octets to transmit.
*
* async_fnct Function that will be invoked upon completion of transmit operation.
*
* p_async_arg Pointer to argument that will be passed as parameter of 'async_fnct'.
*
* timeout_ms Timeout in milliseconds.
*
* end End-of-transfer flag (see Note #2).
*
* p_err Pointer to variable that will receive return error code from this function :
* ----- Argument checked by caller.
*
* USBD_ERR_NONE Data successfully transmitted.
* USBD_ERR_NULL_PTR Null pointer passed as argument.
* USBD_ERR_EP_IO_PENDING Transfer already in progress on endpoint.
* USBD_ERR_OS_FAIL OS operation failed.
*
* - RETURNED BY USBD_URB_Get() -
* See USBD_URB_Get() for additional return error codes.
*
* - RETURNED BY USBD_EP_TxAsyncProcess() -
* See USBD_EP_TxAsyncProcess() for additional return error codes.
*
* - RETURNED BY USBD_OS_EP_SignalPend() -
* See USBD_OS_EP_SignalPend() for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_Tx()' -
* See specific driver(s) 'p_drv_api->EP_Tx()' for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_TxStart()' -
* See specific driver(s) 'p_drv_api->EP_TxStart()' for additional return error codes.
*
* - RETURNED BY 'p_drv_api->EP_TxZLP()' -
* See specific driver(s) 'p_drv_api->EP_TxZLP()' for additional return error codes.
*
* Return(s) : Number of octets transmitted, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) This function SHOULD NOT be called from interrupt service routine (ISR).
*
* (2) If end-of-transfer is set and transfer length is multiple of maximum packet size,
* a zero-length packet is transferred to indicate a short transfer to the host.
*
* (3) Endpoint must be locked when calling this function.
*
* (4) During a synchronous transfer, endpoint is unlocked before pending on transfer
* completion to be able to abort. Since the endpoint is already locked when this
* function is called (see callers functions), it releases the lock before pending and
* re-locks once the transfer completes.
*********************************************************************************************************
*/
static CPU_INT32U USBD_EP_Tx (USBD_DRV *p_drv,
USBD_EP *p_ep,
void *p_buf,
CPU_INT32U buf_len,
USBD_ASYNC_FNCT async_fnct,
void *p_async_arg,
CPU_INT16U timeout_ms,
CPU_BOOLEAN end,
USBD_ERR *p_err)
{
USBD_URB *p_urb;
USBD_DRV_API *p_drv_api;
USBD_XFER_STATE prev_xfer_state;
CPU_INT08U *p_buf_cur;
CPU_INT32U xfer_rem;
CPU_INT32U xfer_tot;
USBD_ERR local_err;
CPU_BOOLEAN zlp_flag = DEF_NO;
if ((buf_len != 0u) &&
(p_buf == (void *)0)) {
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
if (async_fnct == (USBD_ASYNC_FNCT)0) {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxSyncExecNbr);
if (p_ep->XferState != USBD_XFER_STATE_NONE) {
*p_err = USBD_ERR_EP_IO_PENDING;
return (0u);
}
} else {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxAsyncExecNbr);
if ((p_ep->XferState != USBD_XFER_STATE_NONE) &&
(p_ep->XferState != USBD_XFER_STATE_ASYNC)) {
*p_err = USBD_ERR_EP_IO_PENDING;
return (0u);
}
}
if (buf_len == 0u) {
zlp_flag = DEF_YES;
}
p_urb = USBD_URB_Get(p_drv->DevNbr, p_ep, p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
p_urb->BufPtr = (CPU_INT08U *)p_buf; /* Init 'p_urb' fields. */
p_urb->BufLen = buf_len;
p_urb->XferLen = 0u;
p_urb->NextXferLen = 0u;
p_urb->AsyncFnct = async_fnct;
p_urb->AsyncFnctArg = p_async_arg;
p_urb->Err = USBD_ERR_NONE;
p_urb->NextPtr = (USBD_URB *)0;
if (end == DEF_YES) {
DEF_BIT_SET(p_urb->Flags, USBD_URB_FLAG_XFER_END);
}
if (async_fnct != (USBD_ASYNC_FNCT)0) { /* -------------------- ASYNC XFER -------------------- */
p_urb->State = USBD_URB_STATE_XFER_ASYNC;
prev_xfer_state = p_ep->XferState; /* Keep prev XferState, to restore in case of err. */
p_ep->XferState = USBD_XFER_STATE_ASYNC; /* Set XferState before submitting the xfer. */
USBD_EP_TxAsyncProcess(p_drv,
p_ep,
p_urb,
p_urb->BufPtr,
p_urb->BufLen,
p_err);
if (*p_err == USBD_ERR_NONE) {
USBD_URB_Queue(p_ep, p_urb); /* If no err, queue URB. */
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxAsyncSuccessNbr);
} else {
p_ep->XferState = prev_xfer_state; /* If an err occured, restore prev XferState. */
USBD_URB_Free(p_drv->DevNbr, p_ep, p_urb); /* Free URB. */
}
return (0u);
}
p_ep->XferState = USBD_XFER_STATE_SYNC; /* -------------------- SYNC XFER --------------------- */
p_urb->State = USBD_URB_STATE_XFER_SYNC;
USBD_URB_Queue(p_ep, p_urb);
p_drv_api = p_drv->API_Ptr; /* Get dev drv API struct. */
xfer_rem = p_urb->BufLen;
*p_err = USBD_ERR_NONE;
while ((*p_err == USBD_ERR_NONE) &&
((xfer_rem > 0u) ||
(zlp_flag == DEF_YES))) {
if (zlp_flag == DEF_NO) {
p_buf_cur = &p_urb->BufPtr[p_urb->XferLen];
} else {
p_buf_cur = (CPU_INT08U *)0;
zlp_flag = DEF_NO; /* If Tx ZLP, loop done only once. */
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxNbr);
p_urb->NextXferLen = p_drv_api->EP_Tx(p_drv,
p_ep->Addr,
p_buf_cur,
xfer_rem,
p_err);
if (*p_err != USBD_ERR_NONE) {
break;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxSuccessNbr);
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxStartNbr);
p_drv_api->EP_TxStart(p_drv,
p_ep->Addr,
p_buf_cur,
p_urb->NextXferLen,
p_err);
if (*p_err != USBD_ERR_NONE) {
break;
}
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxStartSuccessNbr);
USBD_OS_EP_LockRelease(p_drv->DevNbr, /* Unlock before pending on completion. See Note #4. */
p_ep->Ix);
USBD_OS_EP_SignalPend(p_drv->DevNbr, p_ep->Ix, timeout_ms, p_err);
USBD_OS_EP_LockAcquire(p_drv->DevNbr, /* Re-lock EP after xfer completion. See Note #4. */
p_ep->Ix,
0u,
&local_err);
if (*p_err == USBD_ERR_OS_TIMEOUT) {
p_drv_api->EP_Abort(p_drv, p_ep->Addr);
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, TxSyncTimeoutErrNbr);
break;
} else if (*p_err != USBD_ERR_NONE) {
break;
} else if (local_err != USBD_ERR_NONE) {
*p_err = USBD_ERR_OS_FAIL;
break;
}
p_urb->XferLen += p_urb->NextXferLen;
xfer_rem -= p_urb->NextXferLen;
}
xfer_tot = p_urb->XferLen;
if (( end == DEF_YES) &&
(*p_err == USBD_ERR_NONE) &&
((p_urb->BufLen % p_ep->MaxPktSize) == 0u) &&
( p_urb->BufLen != 0u)) {
/* $$$$ This case should be tested more thoroughly. */
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxZLP_Nbr);
p_drv_api->EP_TxZLP(p_drv, p_ep->Addr, p_err);
if (*p_err == USBD_ERR_NONE) {
USBD_DBG_STATS_EP_INC(p_drv->DevNbr, p_ep->Ix, DrvTxZLP_SuccessNbr);
USBD_OS_EP_LockRelease(p_drv->DevNbr, /* Unlock before pending on completion. See Note #4. */
p_ep->Ix);
USBD_OS_EP_SignalPend(p_drv->DevNbr, p_ep->Ix, timeout_ms, p_err);
USBD_OS_EP_LockAcquire(p_drv->DevNbr, /* Re-lock EP after xfer completion. See Note #4. */
p_ep->Ix,
0u,
&local_err);
if (local_err != USBD_ERR_NONE) {
*p_err = USBD_ERR_OS_FAIL;
}
}
}
USBD_URB_Dequeue(p_ep);
USBD_URB_Free(p_drv->DevNbr, p_ep, p_urb);
USBD_DBG_STATS_EP_INC_IF_TRUE(p_drv->DevNbr, p_ep->Ix, TxSyncSuccessNbr, (*p_err == USBD_ERR_NONE));
return (xfer_tot);
}
/*
*********************************************************************************************************
* USBD_EP_URB_Abort()
*
* Description : Aborts endpoint's URB(s). Does not free the URB(s), see Note #1.
*
* Argument(s) : p_drv Pointer to device driver structure.
* ---- Argument checked by caller.
*
* p_ep Pointer to endpoint on which data will be sent.
* ---- Argument checked by caller.
* p_err Pointer to variable that will receive return error code from this function :
* ---- Argument checked by caller.
*
* USBD_ERR_NONE URB successfully aborted.
* USBD_ERR_EP_ABORT Device driver abort endpoint failed.
*
* - RETURNED BY USBD_OS_EP_SignalAbort() -
* See USBD_OS_EP_SignalAbort() for additional return error codes.
*
* Return(s) : Pointer to head of completed URB list, if asynchronous transfer in progress.
*
* Pointer to NULL, otherwise.
*
* Note(s) : (1) If a synchronous transfer was in progress, a single URB was aborted and must be
* freed by calling USBD_URB_Free(). If an asynchronous transfer was in progress, it
* is possible that multiple URBs were aborted and must be freed by calling
* USBD_URB_AsyncEnd() with the pointer to the head of the aborted URB(s) list returned
* by this function as a parameter.
*
* (2) Endpoint must be locked when calling this function.
*********************************************************************************************************
*/
static USBD_URB *USBD_EP_URB_Abort (USBD_DRV *p_drv,
USBD_EP *p_ep,
USBD_ERR *p_err)
{
USBD_DRV_API *p_drv_api;
CPU_BOOLEAN abort_ok;
USBD_URB *p_urb_head;
USBD_URB *p_urb;
USBD_URB *p_urb_cur;
p_drv_api = p_drv->API_Ptr; /* Get dev drv API struct. */
p_urb_head = (USBD_URB *)0;
p_urb_cur = (USBD_URB *)0;
switch (p_ep->XferState) {
case USBD_XFER_STATE_NONE:
abort_ok = DEF_OK;
break;
case USBD_XFER_STATE_ASYNC:
case USBD_XFER_STATE_ASYNC_PARTIAL:
p_urb = p_ep->URB_HeadPtr;
while(p_urb != (USBD_URB *)0) {
p_urb = USBD_URB_AsyncCmpl(p_ep, USBD_ERR_EP_ABORT);
if (p_urb_head == (USBD_URB *)0) {
p_urb_head = p_urb;
} else {
p_urb_cur->NextPtr = p_urb;
}
p_urb_cur = p_urb;
}
abort_ok = p_drv->API_Ptr->EP_Abort(p_drv, p_ep->Addr); /* Call drv's abort fnct. */
break;
case USBD_XFER_STATE_SYNC:
USBD_OS_EP_SignalAbort(p_drv->DevNbr, p_ep->Ix, p_err);
abort_ok = p_drv_api->EP_Abort(p_drv, p_ep->Addr);
break;
default:
abort_ok = DEF_FAIL;
break;
}
if (abort_ok == DEF_OK) {
*p_err = USBD_ERR_NONE;
} else {
*p_err = USBD_ERR_EP_ABORT;
}
return (p_urb_head); /* See Note #1. */
}
/*
*********************************************************************************************************
* USBD_URB_AsyncCmpl()
*
* Description : Notify URB completion to asynchronous callback.
*
* Argument(s) : p_ep Pointer to endpoint on which transfer has completed.
* ---- Argument checked by caller.
*
* err Error associated with transfer.
*
* Return(s) : Pointer to completed URB, if any.
*
* Pointer to NULL, otherwise.
*
* Note(s) : (1) Endpoint must be locked when calling this function.
*
* (2) Endpoint must have an asynchronous transfer in progress.
*********************************************************************************************************
*/
static USBD_URB *USBD_URB_AsyncCmpl (USBD_EP *p_ep,
USBD_ERR err)
{
USBD_URB *p_urb;
p_urb = p_ep->URB_HeadPtr; /* Get head URB for EP. */
if (p_urb == (USBD_URB *)0) {
return (p_urb);
}
USBD_URB_Dequeue(p_ep); /* Dequeue first URB from EP. */
p_urb->Err = err; /* Set err for curr URB. */
p_urb->NextPtr = (USBD_URB *)0; /* Remove links with 'p_ep' URB linked list. */
return (p_urb);
}
/*
*********************************************************************************************************
* USBD_URB_AsyncEnd()
*
* Description : Execute callback associated with each USB request block in the linked list and free them.
*
* Argument(s) : dev_nbr Device number.
* ------- Argument checked by caller.
*
* p_ep Pointer to endpoint structure.
* ---- Argument checked by caller.
*
* p_urb_head Pointer to head of USB request block linked list.
* ----- Argument checked by caller.
*
* Return(s) : none.
*
* Note(s) : (1) Endpoint must NOT be locked when calling this function.
*********************************************************************************************************
*/
static void USBD_URB_AsyncEnd (CPU_INT08U dev_nbr,
USBD_EP *p_ep,
USBD_URB *p_urb_head)
{
USBD_URB *p_urb_cur;
USBD_URB *p_urb_next;
void *p_buf;
CPU_INT32U buf_len;
CPU_INT32U xfer_len;
USBD_ASYNC_FNCT async_fnct;
void *p_async_arg;
USBD_ERR err;
p_urb_cur = p_urb_head;
while (p_urb_cur != (USBD_URB *)0) { /* Iterate through linked list. */
p_buf = (void *)p_urb_cur->BufPtr;
buf_len = p_urb_cur->BufLen;
xfer_len = p_urb_cur->XferLen;
async_fnct = p_urb_cur->AsyncFnct;
p_async_arg = p_urb_cur->AsyncFnctArg;
err = p_urb_cur->Err;
p_urb_next = p_urb_cur->NextPtr;
USBD_URB_Free(dev_nbr, p_ep, p_urb_cur); /* Free URB to pool. */
async_fnct(dev_nbr, /* Execute callback fnct. */
p_ep->Addr,
p_buf,
buf_len,
xfer_len,
p_async_arg,
err);
p_urb_cur = p_urb_next;
}
}
/*
*********************************************************************************************************
* USBD_URB_Free()
*
* Description : Free URB to URB pool.
*
* Argument(s) : dev_nbr Device number.
* ------- Argument checked by caller.
*
* p_ep Pointer to endpoint structure.
* ---- Argument checked by caller.
*
* p_urb Pointer to USB request block.
* ----- Argument checked by caller.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
static void USBD_URB_Free (CPU_INT08U dev_nbr,
USBD_EP *p_ep,
USBD_URB *p_urb)
{
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
p_urb->State = USBD_URB_STATE_IDLE;
p_urb->NextPtr = USBD_URB_TblPtr[dev_nbr];
USBD_URB_TblPtr[dev_nbr] = p_urb;
#if (USBD_CFG_MAX_NBR_URB_EXTRA > 0u)
if (DEF_BIT_IS_SET(p_urb->Flags, USBD_URB_FLAG_EXTRA_URB)) {
/* If the URB freed is an 'extra' URB, dec ctr. */
USBD_URB_ExtraCtr[dev_nbr]--;
} else {
p_ep->URB_MainAvail = DEF_YES;
}
#else
(void)p_ep;
#endif
CPU_CRITICAL_EXIT();
}
/*
*********************************************************************************************************
* USBD_URB_Get()
*
* Description : Get URB from URB pool.
*
* Argument(s) : dev_nbr Device number.
* ------- Argument checked by caller.
*
* p_ep Pointer to endpoint structure.
* ---- Argument checked by caller.
*
* p_err Pointer to variable that will receive return error code from this function :
* ---- Argument checked by caller.
*
* USBD_ERR_NONE URB successfully returned.
* USBD_ERR_EP_QUEUING No URB available in the pool.
*
* Return(s) : Pointer to USB request block, if NO error(s).
*
* Pointer to NULL, otherwise.
*
* Note(s) : none.
*********************************************************************************************************
*/
static USBD_URB *USBD_URB_Get (CPU_INT08U dev_nbr,
USBD_EP *p_ep,
USBD_ERR *p_err)
{
CPU_BOOLEAN ep_empty;
USBD_URB *p_urb;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
ep_empty = ((p_ep->URB_HeadPtr == (USBD_URB *)0) && (p_ep->URB_TailPtr == (USBD_URB *)0)) ? DEF_YES : DEF_NO;
#if (USBD_CFG_MAX_NBR_URB_EXTRA > 0u)
/* Check if EP is empty, if enough URB rem or if main...*/
/* ...URB avail. */
if ((ep_empty == DEF_YES) ||
(USBD_URB_ExtraCtr[dev_nbr] < USBD_CFG_MAX_NBR_URB_EXTRA) ||
(p_ep->URB_MainAvail == DEF_YES)) {
#else
if (ep_empty == DEF_YES) { /* Check if EP is empty. */
#endif
p_urb = USBD_URB_TblPtr[dev_nbr];
if (p_urb != (USBD_URB *)0) {
USBD_URB_TblPtr[dev_nbr] = p_urb->NextPtr;
p_urb->NextPtr = (USBD_URB *)0;
p_urb->Flags = 0u;
#if (USBD_CFG_MAX_NBR_URB_EXTRA > 0u)
if ((ep_empty == DEF_NO) &&
(p_ep->URB_MainAvail == DEF_NO)) {
/* If the EP already has an URB in progress, inc ... */
USBD_URB_ExtraCtr[dev_nbr]++; /* ... ctr and mark the URB as an 'extra' URB. */
DEF_BIT_SET(p_urb->Flags, USBD_URB_FLAG_EXTRA_URB);
} else if (p_ep->URB_MainAvail == DEF_YES) {
p_ep->URB_MainAvail = DEF_NO;
}
#endif
*p_err = USBD_ERR_NONE;
} else {
*p_err = USBD_ERR_EP_QUEUING;
p_urb = (USBD_URB *)0;
}
} else {
*p_err = USBD_ERR_EP_QUEUING;
p_urb = (USBD_URB *)0;
}
CPU_CRITICAL_EXIT();
return (p_urb);
}
/*
*********************************************************************************************************
* USBD_URB_Queue()
*
* Description : Queue USB request block into endpoint.
*
* Argument(s) : p_ep Pointer to endpoint structure.
* ---- Argument checked by caller.
*
* p_urb Pointer to USB request block.
* ----- Argument checked by caller.
*
* Return(s) : none.
*
* Note(s) : (1) Endpoint must be locked when calling this function.
*********************************************************************************************************
*/
static void USBD_URB_Queue (USBD_EP *p_ep,
USBD_URB *p_urb)
{
p_urb->NextPtr = (USBD_URB *)0;
if (p_ep->URB_TailPtr == (USBD_URB *)0) { /* Q is empty. */
p_ep->URB_HeadPtr = p_urb;
p_ep->URB_TailPtr = p_urb;
} else { /* Q is not empty. */
p_ep->URB_TailPtr->NextPtr = p_urb;
p_ep->URB_TailPtr = p_urb;
}
return;
}
/*
*********************************************************************************************************
* USBD_URB_Dequeue()
*
* Description : Dequeue head USB request block from endpoint.
*
* Argument(s) : p_ep Pointer to endpoint structure.
* ---- Argument checked by caller.
*
* Return(s) : none.
*
* Note(s) : (1) Endpoint must be locked when calling this function.
*********************************************************************************************************
*/
static void USBD_URB_Dequeue (USBD_EP *p_ep)
{
USBD_URB *p_urb;
p_urb = p_ep->URB_HeadPtr;
if (p_urb == (USBD_URB *)0) {
return;
}
if (p_urb->NextPtr == (USBD_URB *)0) { /* Only one URB is queued. */
p_ep->URB_HeadPtr = (USBD_URB *)0;
p_ep->URB_TailPtr = (USBD_URB *)0;
p_ep->XferState = USBD_XFER_STATE_NONE;
} else {
p_ep->URB_HeadPtr = p_urb->NextPtr;
}
}