/* ********************************************************************************************************* * 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; } }