Files
base/middleware/usb/uC-USBD/Class/CDC-EEM/usbd_cdc_eem.c
2025-11-07 09:57:14 +08:00

1968 lines
75 KiB
C

/*
*********************************************************************************************************
* uC/USB-Device
* The Embedded USB Device Stack
*
* Copyright 2004-2021 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* USB COMMUNICATIONS DEVICE CLASS (CDC)
* ETHERNET EMULATION MODEL (EEM)
*
* Filename : usbd_cdc_eem.c
* Version : V4.06.01
*********************************************************************************************************
* Note(s) : (1) This implementation is compliant with the CDC-EEM specification "Universal Serial
* Bus Communications Class Subclass Specification for Ethernet Emulation Model
* Devices" revision 1.0. February 2, 2005.
*
* (2) This class implementation does NOT use the CDC base class implementation as
* CDC-EEM specification does not follow the CDC specification revision 1.2
* December 6, 2012.
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#define USBD_CDC_EEM_MODULE
#include <lib_mem.h>
#include <usbd_cfg.h>
#include <KAL/kal.h>
#include "usbd_cdc_eem.h"
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
/* Dflt Rx buffer qty is 1. */
#ifndef USBD_CDC_EEM_CFG_RX_BUF_QTY_PER_DEV
#define USBD_CDC_EEM_CFG_RX_BUF_QTY_PER_DEV 1u
#endif
/* Max nbr of comm struct. */
#define USBD_CDC_EEM_COMM_NBR_MAX (USBD_CDC_EEM_CFG_MAX_NBR_DEV * \
USBD_CDC_EEM_CFG_MAX_NBR_CFG)
#define USBD_CDC_EEM_MAX_RETRY_CNT 3u /* Max nbr of retry when Rx buf submission fails.*/
#define USBD_CDC_EEM_SUBCLASS_CODE 0x0C
#define USBD_CDC_EEM_PROTOCOL_CODE 0x07
/*
*********************************************************************************************************
* ZERO LENGTH EEM (ZLE) SPECIAL PACKET
*
* Note(s) : (1) ZLE packet is defined in "Universal Serial Bus Communications Class Subclass
* Specification for Ethernet Emulation Model Devices" revision 1.0. February 2, 2005,
* section 5.1.2.3.1.
*********************************************************************************************************
*/
#define USBD_CDC_EEM_HDR_ZLE 0x0000
/*
*********************************************************************************************************
* PACKET TYPES
*
* Note(s) : (1) Packet types are defined in "Universal Serial Bus Communications Class Subclass
* Specification for Ethernet Emulation Model Devices" revision 1.0. February 2, 2005,
* section 5.1.2.
*********************************************************************************************************
*/
#define USBD_CDC_EEM_PKT_TYPE_MASK DEF_BIT_15
#define USBD_CDC_EEM_PKT_TYPE_PAYLOAD 0u
#define USBD_CDC_EEM_PKT_TYPE_CMD 1u
/*
*********************************************************************************************************
* EEM PAYLOAD PACKET HEADER DEFINITIONS
*
* Note(s) : (1) Payload header format is defined in "Universal Serial Bus Communications Class Subclass
* Specification for Ethernet Emulation Model Devices" revision 1.0. February 2, 2005,
* section 5.1.2.1.
*
* (a) Payload packets header have the following format:
*
* +---------+----------------------------------+
* | Bit | 15 | 14 | 13..0 |
* +---------+----------------------------------+
* | Content | bmType | bmCRC | Length of |
* | | (0) | | Ethernet frame |
* +---------+----------------------------------+
*
* (1) bmCRC indicates whether the Ethernet frame contains a valid CRC or a CRC set
* to 0xDEADBEEF.
*********************************************************************************************************
*/
/* ----------------- PAYLOAD PKT CRC ------------------ */
#define USBD_CDC_EEM_PAYLOAD_CRC_MASK DEF_BIT_14
#define USBD_CDC_EEM_PAYLOAD_CRC_NOT_CALC 0u
#define USBD_CDC_EEM_PAYLOAD_CRC_CALC 1u
/* ----------------- PAYLOAD PKT LEN ------------------ */
#define USBD_CDC_EEM_PAYLOAD_LEN_MASK DEF_BIT_FIELD(14u, 0u)
/*
*********************************************************************************************************
* EEM COMMAND PACKET HEADER DEFINITIONS
*
* Note(s) : (1) Command header format is defined in "Universal Serial Bus Communications Class Subclass
* Specification for Ethernet Emulation Model Devices" revision 1.0. February 2, 2005,
* section 5.1.2.2.
*
* (a) Command packets header have the following format:
*
* +---------+------------------------------------------------+
* | Bit | 15 | 14 | 13..11 | 10..0 |
* +---------+------------------------------------------------+
* | Content | bmType | bmReserved | bmEEMCmd | bmEEMCmdParam |
* | | (1) | (0) | | |
* +---------+------------------------------------------------+
*
* (1) bmEEMCmd gives the code of the command to execute.
*
* (2) bmEEMCmdParam gives extra information for the command execution. Format depends
* on the command code.
*********************************************************************************************************
*/
/* ------------------- CMD RSVD BIT ------------------- */
#define USBD_CDC_EEM_CMD_RSVD_MASK DEF_BIT_14
#define USBD_CDC_EEM_CMD_RSVD_OK 0u
/* ------------------- CMD PKT CODE ------------------- */
#define USBD_CDC_EEM_CMD_CODE_MASK DEF_BIT_FIELD(3u, 11u)
#define USBD_CDC_EEM_CMD_CODE_ECHO 0u
#define USBD_CDC_EEM_CMD_CODE_ECHO_RESP 1u
#define USBD_CDC_EEM_CMD_CODE_SUSP_HINT 2u
#define USBD_CDC_EEM_CMD_CODE_RESP_HINT 3u
#define USBD_CDC_EEM_CMD_CODE_RESP_CMPL_HINT 4u
#define USBD_CDC_EEM_CMD_CODE_TICKLE 5u
/* ------------------- CMD PKT PARAM ------------------- */
#define USBD_CDC_EEM_CMD_PARAM_MASK DEF_BIT_FIELD(11u, 0u)
/* ------------------- ECHO CMD LEN ------------------- */
#define USBD_CDC_EEM_CMD_PARAM_ECHO_LEN_MASK DEF_BIT_FIELD(10u, 0u)
/*
*********************************************************************************************************
* LOCAL CONSTANTS
*********************************************************************************************************
*/
/* Dflt CRC value. */
static const CPU_INT08U USBD_CDC_EEM_PayloadCRC[] = {0xDE, 0xAD, 0xBE, 0xEF};
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* FORWARD DECLARATIONS
*********************************************************************************************************
*/
typedef struct usbd_cdc_eem_ctrl USBD_CDC_EEM_CTRL; /* Forward declaration. */
/*
*********************************************************************************************************
* CDC-EEM CLASS STATES
*********************************************************************************************************
*/
typedef enum usbd_cdc_eem_state {
USBD_CDC_EEM_STATE_NONE = 0,
USBD_CDC_EEM_STATE_INIT,
USBD_CDC_EEM_STATE_CFG
} USBD_CDC_EEM_STATE;
/*
*********************************************************************************************************
* CDC-EEM CLASS BUFFER QUEUE ENTRY
*********************************************************************************************************
*/
typedef struct usbd_cdc_eem_buf_entry {
CPU_INT08U *BufPtr; /* Pointer to buffer. */
CPU_INT16U BufLen; /* Buffer length in bytes. */
CPU_BOOLEAN CrcComputed; /* Flag indicates if CRC computed for this buf. */
} USBD_CDC_EEM_BUF_ENTRY;
/*
*********************************************************************************************************
* CDC-EEM CLASS COMM INFO
*********************************************************************************************************
*/
typedef struct usbd_cdc_eem_comm {
USBD_CDC_EEM_CTRL *CtrlPtr; /* Ptr to ctrl info. */
/* Avail EP for comm: Bulk (and Intr) */
CPU_INT08U DataInEpAddr; /* Address of Bulk IN EP. */
CPU_INT08U DataOutEpAddr; /* Address of Bulk OUT EP. */
} USBD_CDC_EEM_COMM;
/*
*********************************************************************************************************
* CDC-EEM CLASS BUFFER QUEUE
*********************************************************************************************************
*/
typedef struct usbd_cdc_eem_buf_q {
USBD_CDC_EEM_BUF_ENTRY *Tbl; /* Ptr to table of buffer entry. */
CPU_INT08U InIdx; /* In Q index. */
CPU_INT08U OutIdx; /* Out Q index. */
CPU_INT08U Cnt; /* Nbr of elements in Q. */
CPU_INT08U Size; /* Size of Q. */
} USBD_CDC_EEM_BUF_Q;
/*
*********************************************************************************************************
* CDC-EEM CLASS CTRL INFO
*********************************************************************************************************
*/
struct usbd_cdc_eem_ctrl {
CPU_INT08U DevNbr; /* Dev nbr to which this class instance is associated. */
CPU_INT08U ClassNbr; /* Class instacne number. */
USBD_CDC_EEM_COMM *CommPtr; /* Ptr to comm. */
USBD_CDC_EEM_STATE State; /* Class instance current state. */
CPU_INT08U StartCnt; /* Start cnt. */
KAL_LOCK_HANDLE StateLockHandle; /* Handle on lock for class instance state. */
CPU_INT08U RxErrCnt; /* Cnt of Rx error. */
/* Ptr to Rx buffer table. */
CPU_INT08U *RxBufPtrTbl[USBD_CDC_EEM_CFG_RX_BUF_QTY_PER_DEV];
CPU_INT08U *BufEchoPtr; /* Ptr to buffer that contains echo data. */
USBD_CDC_EEM_DRV *DrvPtr; /* Ptr to network driver. */
void *DrvArgPtr; /* Arg of network driver. */
/* ----------------- RX STATE MACHINE ----------------- */
CPU_INT16U CurHdr; /* Hdr of current buffer. */
CPU_INT32U CurBufLenRem; /* Remaining length of current buffer. */
CPU_INT08U *CurBufPtr; /* Pointer to current network buffer. */
CPU_INT16U CurBufIx; /* Index of network buffer to write. */
CPU_BOOLEAN CurBufCrcComputed; /* Flag indicates if CRC is computed on current buf. */
/* ---------------- NETWORK BUFFER QS ----------------- */
USBD_CDC_EEM_BUF_Q RxBufQ; /* Rx buffer Q. */
USBD_CDC_EEM_BUF_Q TxBufQ; /* Tx buffer Q. */
CPU_BOOLEAN TxInProgress; /* Flag that indicates if a Tx is in progress. */
};
/*
*********************************************************************************************************
* LOCAL TABLES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
/* CDC EEM class instances array. */
static USBD_CDC_EEM_CTRL USBD_CDC_EEM_CtrlTbl[USBD_CDC_EEM_CFG_MAX_NBR_DEV];
static CPU_INT08U USBD_CDC_EEM_CtrlNbrNext;
/* CDC EEM class comm array. */
static USBD_CDC_EEM_COMM USBD_CDC_EEM_CommTbl[USBD_CDC_EEM_COMM_NBR_MAX];
static CPU_INT08U USBD_CDC_EEM_CommNbrNext;
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void USBD_CDC_EEM_Conn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg);
static void USBD_CDC_EEM_Disconn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg);
static void USBD_CDC_EEM_CommStart (USBD_CDC_EEM_CTRL *p_ctrl,
USBD_ERR *p_err);
static void USBD_CDC_EEM_RxCmpl (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT32U xfer_len,
void *p_arg,
USBD_ERR err);
static void USBD_CDC_EEM_TxCmpl (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT32U xfer_len,
void *p_arg,
USBD_ERR err);
static void USBD_CDC_EEM_TxBufSubmit(USBD_CDC_EEM_CTRL *p_ctrl,
CPU_INT08U *p_buf,
CPU_INT16U buf_len,
USBD_ERR *p_err);
static void USBD_CDC_EEM_BufQ_Add (USBD_CDC_EEM_BUF_Q *p_buf_q,
CPU_INT08U *p_buf,
CPU_INT16U buf_len,
CPU_BOOLEAN crc_computed);
static CPU_INT08U *USBD_CDC_EEM_BufQ_Get (USBD_CDC_EEM_BUF_Q *p_buf_q,
CPU_INT16U *p_buf_len,
CPU_BOOLEAN *p_crc_computed);
static void USBD_CDC_EEM_StateLock (USBD_CDC_EEM_CTRL *p_ctrl,
USBD_ERR *p_err);
static void USBD_CDC_EEM_StateUnlock(USBD_CDC_EEM_CTRL *p_ctrl,
USBD_ERR *p_err);
/*
*********************************************************************************************************
* CDC-EEM CLASS DRIVER
*********************************************************************************************************
*/
static USBD_CLASS_DRV USBD_CDC_EEM_Drv = {
USBD_CDC_EEM_Conn,
USBD_CDC_EEM_Disconn,
DEF_NULL,
DEF_NULL,
DEF_NULL,
DEF_NULL,
DEF_NULL,
DEF_NULL,
DEF_NULL,
DEF_NULL,
DEF_NULL,
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
DEF_NULL,
DEF_NULL,
#endif
};
/*
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* GLOBAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_CDC_EEM_Init()
*
* Description : Initializes internal structures and variables used by the CDC EEM class.
*
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_OS_SIGNAL_CREATE Unable to create state lock.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
void USBD_CDC_EEM_Init (USBD_ERR *p_err)
{
CPU_INT08U ix;
USBD_CDC_EEM_CTRL *p_ctrl;
USBD_CDC_EEM_COMM *p_comm;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == DEF_NULL) {
CPU_SW_EXCEPTION(;);
}
#endif
for (ix = 0u; ix < USBD_CDC_EEM_CFG_MAX_NBR_DEV; ix++) { /* Init ctrl struct. */
KAL_ERR err_kal;
p_ctrl = &USBD_CDC_EEM_CtrlTbl[ix];
p_ctrl->DevNbr = USBD_DEV_NBR_NONE;
p_ctrl->State = USBD_CDC_EEM_STATE_NONE;
p_ctrl->ClassNbr = USBD_CLASS_NBR_NONE;
p_ctrl->CommPtr = DEF_NULL;
p_ctrl->DrvPtr = DEF_NULL;
p_ctrl->DrvArgPtr = DEF_NULL;
p_ctrl->StartCnt = 0u;
p_ctrl->StateLockHandle = KAL_LockCreate("USBD - CDC EEM State lock",
DEF_NULL,
&err_kal);
if (err_kal != KAL_ERR_NONE) {
*p_err = USBD_ERR_OS_SIGNAL_CREATE;
return;
}
}
for (ix = 0u; ix < USBD_CDC_EEM_COMM_NBR_MAX; ix++) { /* Init comm struct. */
p_comm = &USBD_CDC_EEM_CommTbl[ix];
p_comm->CtrlPtr = DEF_NULL;
p_comm->DataInEpAddr = USBD_EP_ADDR_NONE;
p_comm->DataOutEpAddr = USBD_EP_ADDR_NONE;
}
USBD_CDC_EEM_CtrlNbrNext = 0u;
USBD_CDC_EEM_CommNbrNext = 0u;
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_Add()
*
* Description : Adds a new instance of the CDC EEM class.
*
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_ALLOC No more class instance structure available.
*
*
* Return(s) : Class instance number, if NO error(s).
*
* USBD_CLASS_NBR_NONE, otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_INT08U USBD_CDC_EEM_Add (USBD_ERR *p_err)
{
CPU_INT08U class_nbr;
CPU_INT08U buf_cnt;
LIB_ERR err_lib;
USBD_CDC_EEM_CTRL *p_ctrl;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == DEF_NULL) {
CPU_SW_EXCEPTION(USBD_CLASS_NBR_NONE);
}
#endif
CPU_CRITICAL_ENTER();
class_nbr = USBD_CDC_EEM_CtrlNbrNext;
if (class_nbr >= USBD_CDC_EEM_CFG_MAX_NBR_DEV) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_ALLOC;
return (USBD_CLASS_NBR_NONE);
}
USBD_CDC_EEM_CtrlNbrNext++;
CPU_CRITICAL_EXIT();
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
/* Alloc buffer used by echo response command. */
p_ctrl->BufEchoPtr = (CPU_INT08U *)Mem_HeapAlloc(USBD_CDC_EEM_CFG_ECHO_BUF_LEN,
USBD_CFG_BUF_ALIGN_OCTETS,
DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return (USBD_CLASS_NBR_NONE);
}
/* Allocate Rx buffers. */
for (buf_cnt = 0u; buf_cnt < USBD_CDC_EEM_CFG_RX_BUF_QTY_PER_DEV; buf_cnt++) {
p_ctrl->RxBufPtrTbl[buf_cnt] = (CPU_INT08U *)Mem_HeapAlloc(USBD_CDC_EEM_CFG_RX_BUF_LEN,
USBD_CFG_BUF_ALIGN_OCTETS,
DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return (USBD_CLASS_NBR_NONE);
}
}
*p_err = USBD_ERR_NONE;
return (class_nbr);
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_CfgAdd()
*
* Description : Add CDC-EEM class instance into the specified configuration.
*
* Argument(s) : class_nbr Class instance number.
*
* dev_nbr Device number.
*
* cfg_nbr Configuration index to add CDC-EEM class instance to.
*
* p_if_name Pointer to string that contains name of the CDC-EEM interface.
* Can be DEF_NULL.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'.
* USBD_ERR_ALLOC No more class communication structure available.
*
* -RETURNED BY USBD_IF_Add()-
* See USBD_IF_Add() for additional return error codes.
*
* -RETURNED BY USBD_BulkAdd()-
* See USBD_BulkAdd() for additional return error codes.
*
* Return(s) : none.
*
* Note(s) : None.
*********************************************************************************************************
*/
void USBD_CDC_EEM_CfgAdd ( CPU_INT08U class_nbr,
CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
const CPU_CHAR *p_if_name,
USBD_ERR *p_err)
{
USBD_CDC_EEM_CTRL *p_ctrl;
USBD_CDC_EEM_COMM *p_comm;
CPU_INT08U if_nbr;
CPU_INT08U ep_addr;
CPU_INT16U comm_nbr;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == DEF_NULL) {
CPU_SW_EXCEPTION(;);
}
if (class_nbr >= USBD_CDC_EEM_CtrlNbrNext) {
*p_err = USBD_ERR_INVALID_ARG;
return;
}
#endif
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
CPU_CRITICAL_ENTER();
if ((p_ctrl->DevNbr != USBD_DEV_NBR_NONE) && /* Chk if class is associated with a different dev. */
(p_ctrl->DevNbr != dev_nbr)) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_INVALID_ARG;
return;
}
p_ctrl->DevNbr = dev_nbr;
comm_nbr = USBD_CDC_EEM_CommNbrNext;
if (comm_nbr >= USBD_CDC_EEM_COMM_NBR_MAX) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_ALLOC;
return;
}
USBD_CDC_EEM_CommNbrNext++;
CPU_CRITICAL_EXIT();
p_comm = &USBD_CDC_EEM_CommTbl[comm_nbr];
/* ------------------ BUILD USB FNCT ------------------ */
if_nbr = USBD_IF_Add( dev_nbr,
cfg_nbr,
&USBD_CDC_EEM_Drv,
(void *)p_comm,
DEF_NULL,
USBD_CLASS_CODE_CDC_CONTROL,
USBD_CDC_EEM_SUBCLASS_CODE,
USBD_CDC_EEM_PROTOCOL_CODE,
p_if_name,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
ep_addr = USBD_BulkAdd(dev_nbr,
cfg_nbr,
if_nbr,
0u,
DEF_YES,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
p_comm->DataInEpAddr = ep_addr;
ep_addr = USBD_BulkAdd(dev_nbr,
cfg_nbr,
if_nbr,
0u,
DEF_NO,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
p_comm->DataOutEpAddr = ep_addr;
CPU_CRITICAL_ENTER();
p_ctrl->State = USBD_CDC_EEM_STATE_INIT;
p_ctrl->ClassNbr = class_nbr;
p_ctrl->CommPtr = DEF_NULL;
CPU_CRITICAL_EXIT();
p_comm->CtrlPtr = p_ctrl;
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_IsConn()
*
* Description : Gets the CDC-EEM class instance connection state.
*
* Argument(s) : class_nbr Class instance number.
*
* Return(s) : DEF_YES, if class instance is connected.
*
* DEF_NO, otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_BOOLEAN USBD_CDC_EEM_IsConn (CPU_INT08U class_nbr)
{
USBD_DEV_STATE dev_state;
USBD_CDC_EEM_CTRL *p_ctrl;
USBD_CDC_EEM_STATE class_state;
USBD_ERR err;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_CDC_EEM_CtrlNbrNext) {
CPU_CRITICAL_EXIT();
return (DEF_NO);
}
CPU_CRITICAL_EXIT();
}
#endif
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
dev_state = USBD_DevStateGet(p_ctrl->DevNbr, &err);
if (err != USBD_ERR_NONE) {
return (DEF_NO);
}
USBD_CDC_EEM_StateLock(p_ctrl, &err);
if (err != USBD_ERR_NONE) {
return (DEF_NO);
}
class_state = p_ctrl->State;
USBD_CDC_EEM_StateUnlock(p_ctrl, &err);
if ((err == USBD_ERR_NONE ) &&
(dev_state == USBD_DEV_STATE_CONFIGURED) &&
(class_state == USBD_CDC_EEM_STATE_CFG )) {
return (DEF_YES);
} else {
return (DEF_NO);
}
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_InstanceInit()
*
* Description : Initializes CDC-EEM class instance according to network driver needs.
*
* Argument(s) : class_nbr Class instance number.
*
* p_cfg Pointer to CDC-EEM configuration structure.
*
* p_cdc_eem_drv Pointer to CDC-EEM driver structure.
*
* p_arg Pointer to CDC-EEM driver argument.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'
* /'p_cfg'/'p_cdc_eem_drv'.
* USBD_ERR_NULL_PTR Invalid null pointer passed to 'p_cfg'.
* USBD_ERR_ALLOC Unable to allocate Rx and or Tx queue(s).
* USBD_ERR_INVALID_CLASS_STATE Class instance laready configured.
*
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
void USBD_CDC_EEM_InstanceInit (CPU_INT08U class_nbr,
USBD_CDC_EEM_CFG *p_cfg,
USBD_CDC_EEM_DRV *p_cdc_eem_drv,
void *p_arg,
USBD_ERR *p_err)
{
LIB_ERR err_lib;
USBD_CDC_EEM_CTRL *p_ctrl;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) {
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_CDC_EEM_CtrlNbrNext) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_INVALID_ARG;
return;
}
CPU_CRITICAL_EXIT();
if (p_cfg == DEF_NULL) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
if ((p_cfg->RxBufQSize < 1u) ||
(p_cfg->TxBufQSize < 2u)) {
*p_err = USBD_ERR_INVALID_ARG;
return;
}
if (p_cdc_eem_drv == DEF_NULL) {
*p_err = USBD_ERR_INVALID_ARG;
return;
}
}
#endif
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_ctrl->DrvPtr != DEF_NULL) { /* Ensure class instance is not already configured. */
*p_err = USBD_ERR_INVALID_CLASS_STATE;
return;
}
#endif
p_ctrl->DrvPtr = p_cdc_eem_drv;
p_ctrl->DrvArgPtr = p_arg;
/* ---------------- ALLOC TX AND RX QS ---------------- */
p_ctrl->TxBufQ.Size = p_cfg->TxBufQSize + 1u; /* Add 1 entry for echo buf. */
p_ctrl->TxBufQ.Tbl = (USBD_CDC_EEM_BUF_ENTRY *)Mem_HeapAlloc(sizeof(USBD_CDC_EEM_BUF_ENTRY) * p_ctrl->TxBufQ.Size,
sizeof(CPU_ALIGN),
DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return;
}
p_ctrl->RxBufQ.Size = p_cfg->RxBufQSize;
p_ctrl->RxBufQ.Tbl = (USBD_CDC_EEM_BUF_ENTRY *)Mem_HeapAlloc(sizeof(USBD_CDC_EEM_BUF_ENTRY) * p_ctrl->RxBufQ.Size,
sizeof(CPU_ALIGN),
DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return;
}
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_Start()
*
* Description : Starts communication on given CDC-EEM class instance.
*
* Argument(s) : class_nbr Class instance number.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'.
*
* -RETURNED BY USBD_CDC_EEM_StateLock()-
* See USBD_CDC_EEM_StateLock() for additional return error codes.
*
* -RETURNED BY USBD_CDC_EEM_CommStart()-
* See USBD_CDC_EEM_CommStart() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : (1) This function will start communication only if class instance is connected.
*********************************************************************************************************
*/
void USBD_CDC_EEM_Start (CPU_INT08U class_nbr,
USBD_ERR *p_err)
{
USBD_CDC_EEM_CTRL *p_ctrl;
USBD_ERR err_unlock;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) {
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_CDC_EEM_CtrlNbrNext) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_INVALID_ARG;
return;
}
CPU_CRITICAL_EXIT();
}
#endif
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
USBD_CDC_EEM_StateLock(p_ctrl, p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
p_ctrl->StartCnt++;
if ((p_ctrl->StartCnt == 1u) && /* If net drv started and class connected, start comm. */
(p_ctrl->State == USBD_CDC_EEM_STATE_CFG)) {
USBD_CDC_EEM_CommStart(p_ctrl, p_err);
} else {
*p_err = USBD_ERR_NONE;
}
USBD_CDC_EEM_StateUnlock(p_ctrl, &err_unlock);
(void)err_unlock;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_Stop()
*
* Description : Stops communication on given CDC-EEM class instance.
*
* Argument(s) : class_nbr Class instance number.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'.
*
* -RETURNED BY USBD_CDC_EEM_StateLock()-
* See USBD_CDC_EEM_StateLock() for additional return error codes.
*
* -RETURNED BY USBD_CDC_EEM_StateUnlock()-
* See USBD_CDC_EEM_StateUnlock() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : (1) This function will stop communication only if class instance is connected.
*********************************************************************************************************
*/
void USBD_CDC_EEM_Stop (CPU_INT08U class_nbr,
USBD_ERR *p_err)
{
USBD_CDC_EEM_CTRL *p_ctrl;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) {
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_CDC_EEM_CtrlNbrNext) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_INVALID_ARG;
return;
}
CPU_CRITICAL_EXIT();
}
#endif
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
USBD_CDC_EEM_StateLock(p_ctrl, p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if (p_ctrl->StartCnt > 0u) {
p_ctrl->StartCnt--;
}
if ((p_ctrl->StartCnt == 0u) && /* If class conn still connected, stop comm. */
(p_ctrl->State == USBD_CDC_EEM_STATE_CFG)) {
USBD_ERR err_abort;
USBD_EP_Abort(p_ctrl->DevNbr,
p_ctrl->CommPtr->DataInEpAddr,
&err_abort);
USBD_EP_Abort(p_ctrl->DevNbr,
p_ctrl->CommPtr->DataOutEpAddr,
&err_abort);
(void)err_abort;
}
USBD_CDC_EEM_StateUnlock(p_ctrl, p_err);
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_DevNbrGet()
*
* Description : Gets device number associated to this CDC-EEM class instance.
*
* Argument(s) : class_nbr Class instance number.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'.
*
* Return(s) : Device number.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_INT08U USBD_CDC_EEM_DevNbrGet (CPU_INT08U class_nbr,
USBD_ERR *p_err)
{
USBD_CDC_EEM_CTRL *p_ctrl;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_CDC_EEM_CtrlNbrNext) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_INVALID_ARG;
return (USBD_DEV_NBR_NONE);
}
CPU_CRITICAL_EXIT();
}
#endif
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
*p_err = USBD_ERR_NONE;
return (p_ctrl->DevNbr);
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_RxDataPktGet()
*
* Description : Gets first received data packet from received queue.
*
* Argument(s) : class_nbr Class instance number.
*
* p_rx_len Pointer to variable that will receive the length of the received
* buffer in bytes.
*
* p_crc_computed Pointer to variable that will receive the status of the ethernet CRC
* in buffer. Can be DEF_NULL.
*
* DEF_YES CRC is computed.
* DEF_NO CRC is not computed. Last 4 bytes of buffer are set to
* 0xDEADBEEF.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'.
* USBD_ERR_NULL_PTR Invalid null pointer passed to 'p_rx_len'.
* USBD_ERR_RX No rx buffer available.
*
* Return(s) : Pointer to received buffer, if successful.
*
* DEF_NULL, otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_INT08U *USBD_CDC_EEM_RxDataPktGet (CPU_INT08U class_nbr,
CPU_INT16U *p_rx_len,
CPU_BOOLEAN *p_crc_computed,
USBD_ERR *p_err)
{
CPU_INT08U *p_buf;
USBD_CDC_EEM_CTRL *p_ctrl;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == DEF_NULL) {
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_CDC_EEM_CtrlNbrNext) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_DEV_INVALID_NBR;
return (DEF_NULL);
}
CPU_CRITICAL_EXIT();
if (p_rx_len == DEF_NULL) {
*p_err = USBD_ERR_NULL_PTR;
return (DEF_NULL);
}
#endif
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
CPU_CRITICAL_ENTER(); /* Get next buffer if any. */
p_buf = USBD_CDC_EEM_BufQ_Get(&p_ctrl->RxBufQ,
p_rx_len,
p_crc_computed);
CPU_CRITICAL_EXIT();
*p_err = (p_buf != DEF_NULL) ? USBD_ERR_NONE : USBD_ERR_RX;
return (p_buf);
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_TxDataPktSubmit()
*
* Description : Submits a Tx buffer.
*
* Argument(s) : class_nbr Class instance number.
*
* p_buf Pointer to buffer to submit.
*
* buf_len Buffer length in bytes.
*
* crc_computed Flag that indicates if submitted buffer contains a computed ethernet CRC.
*
* DEF_YES Buffer contains a valid ethernet CRC in its last 4 bytes.
* DEF_NO Buffer does not contain a valid ethernet CRC.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'.
* USBD_ERR_NULL_PTR Invalid null pointer passed to 'p_buf'.
*
* -RETURNED BY USBD_CDC_EEM_TxBufSubmit()-
* See USBD_CDC_EEM_TxBufSubmit() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : (1) Buffers submitted using this function MUST have a padding of 2 bytes at the
* beginning.
*********************************************************************************************************
*/
void USBD_CDC_EEM_TxDataPktSubmit (CPU_INT08U class_nbr,
CPU_INT08U *p_buf,
CPU_INT32U buf_len,
CPU_BOOLEAN crc_computed,
USBD_ERR *p_err)
{
CPU_INT16U hdr;
USBD_CDC_EEM_CTRL *p_ctrl;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) {
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_CDC_EEM_CtrlNbrNext) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_INVALID_ARG;
return;
}
CPU_CRITICAL_EXIT();
if ((p_buf == DEF_NULL) &&
(buf_len != 0u)) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
}
#endif
p_ctrl = &USBD_CDC_EEM_CtrlTbl[class_nbr];
/* Prepare header. */
hdr = DEF_BIT_FIELD_ENC(USBD_CDC_EEM_PKT_TYPE_PAYLOAD, USBD_CDC_EEM_PKT_TYPE_MASK);
if (crc_computed == DEF_NO) {
/* Set CRC to 0xDEADBEEF as per CDC-EEM specification. */
Mem_Copy((void *)&p_buf[buf_len + USBD_CDC_EEM_HDR_LEN],
(void *) USBD_CDC_EEM_PayloadCRC,
4u);
hdr |= DEF_BIT_FIELD_ENC(USBD_CDC_EEM_PAYLOAD_CRC_NOT_CALC, USBD_CDC_EEM_PAYLOAD_CRC_MASK) |
DEF_BIT_FIELD_ENC(buf_len + 4u, USBD_CDC_EEM_PAYLOAD_LEN_MASK);
} else {
hdr |= DEF_BIT_FIELD_ENC(USBD_CDC_EEM_PAYLOAD_CRC_CALC, USBD_CDC_EEM_PAYLOAD_CRC_MASK) |
DEF_BIT_FIELD_ENC(buf_len, USBD_CDC_EEM_PAYLOAD_LEN_MASK);
}
((CPU_INT16U *)p_buf)[0u] = MEM_VAL_GET_INT16U_LITTLE(&hdr); /* Copy hdr to buffer. */
USBD_CDC_EEM_TxBufSubmit(p_ctrl, /* Send buffer to host. */
p_buf,
buf_len + USBD_CDC_EEM_HDR_LEN + 4u,
p_err);
}
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_CDC_EEM_Conn()
*
* Description : Notify class that configuration is active.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration index to add the interface to.
*
* p_if_class_arg Pointer to class argument.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_Conn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg)
{
USBD_CDC_EEM_COMM *p_comm = (USBD_CDC_EEM_COMM *)p_if_class_arg;
USBD_ERR err;
(void)dev_nbr;
(void)cfg_nbr;
USBD_CDC_EEM_StateLock(p_comm->CtrlPtr, &err);
p_comm->CtrlPtr->State = USBD_CDC_EEM_STATE_CFG;
p_comm->CtrlPtr->CommPtr = p_comm;
if (p_comm->CtrlPtr->StartCnt > 0u) { /* If class instance is started by net drv, start comm. */
USBD_CDC_EEM_CommStart(p_comm->CtrlPtr, &err);
}
USBD_CDC_EEM_StateUnlock(p_comm->CtrlPtr, &err);
(void)err;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_Disconn()
*
* Description : Notify class that configuration is not active.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration index to add the interface.
*
* p_if_class_arg Pointer to class argument.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_Disconn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg)
{
USBD_CDC_EEM_COMM *p_comm = (USBD_CDC_EEM_COMM *)p_if_class_arg;
USBD_ERR err_lock;
(void)dev_nbr;
(void)cfg_nbr;
USBD_CDC_EEM_StateLock(p_comm->CtrlPtr, &err_lock);
p_comm->CtrlPtr->State = USBD_CDC_EEM_STATE_INIT;
p_comm->CtrlPtr->CommPtr = DEF_NULL;
USBD_CDC_EEM_StateUnlock(p_comm->CtrlPtr, &err_lock);
(void)err_lock;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_CommStart()
*
* Description : Starts communication on given class instance.
*
* Argument(s) : p_ctrl Pointer to class instance control structure.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
*
* -RETURNED BY USBD_BulkRxAsync()-
* See USBD_BulkRxAsync() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : (1) State of CDC-EEM must be locked by caller function.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_CommStart (USBD_CDC_EEM_CTRL *p_ctrl,
USBD_ERR *p_err)
{
CPU_INT08U buf_cnt;
USBD_CDC_EEM_COMM *p_comm;
p_comm = p_ctrl->CommPtr;
/* Init Rx state machine. */
p_ctrl->CurBufLenRem = 0u;
p_ctrl->CurBufPtr = DEF_NULL;
p_ctrl->CurBufIx = 0u;
p_ctrl->CurBufCrcComputed = DEF_NO;
p_ctrl->RxErrCnt = 0u;
/* Init Tx and Rx Qs. */
p_ctrl->TxBufQ.InIdx = 0u;
p_ctrl->TxBufQ.OutIdx = 0u;
p_ctrl->TxBufQ.Cnt = 0u;
p_ctrl->TxInProgress = DEF_NO;
p_ctrl->RxBufQ.InIdx = 0u;
p_ctrl->RxBufQ.OutIdx = 0u;
p_ctrl->RxBufQ.Cnt = 0u;
/* Submit all avail Rx buffers. */
for (buf_cnt = 0u; buf_cnt < USBD_CDC_EEM_CFG_RX_BUF_QTY_PER_DEV; buf_cnt++) {
USBD_BulkRxAsync( p_ctrl->DevNbr,
p_comm->DataOutEpAddr,
p_ctrl->RxBufPtrTbl[buf_cnt],
USBD_CDC_EEM_CFG_RX_BUF_LEN,
USBD_CDC_EEM_RxCmpl,
(void *)p_ctrl,
p_err);
if (*p_err != USBD_ERR_NONE) {
break;
}
}
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_RxCmpl()
*
* Description : Inform the application about the Bulk OUT transfer completion.
*
* Argument(s) : dev_nbr Device number
*
* ep_addr Endpoint address.
*
* p_buf Pointer to the receive buffer.
*
* buf_len Receive buffer length.
*
* xfer_len Number of octets received.
*
* p_arg Additional argument provided by application.
*
* err Transfer status: success or error.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_RxCmpl (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT32U xfer_len,
void *p_arg,
USBD_ERR err)
{
CPU_INT16U buf_ix_cur = 0u;
USBD_CDC_EEM_CTRL *p_ctrl = (USBD_CDC_EEM_CTRL *)p_arg;
USBD_ERR err_usbd;
CPU_SR_ALLOC();
(void)dev_nbr;
(void)ep_addr;
(void)buf_len;
switch (err) { /* Chk errors. */
case USBD_ERR_NONE:
p_ctrl->RxErrCnt = 0u;
break;
case USBD_ERR_EP_ABORT:
return;
default:
p_ctrl->RxErrCnt++; /* Retry a few times. */
if (p_ctrl->RxErrCnt > USBD_CDC_EEM_MAX_RETRY_CNT) {
return;
}
goto resubmit;
}
if (p_ctrl->CurBufLenRem > 0u) { /* If previous received buffer was incomplete. */
CPU_INT16U len_to_copy = DEF_MIN(xfer_len, p_ctrl->CurBufLenRem);
if (p_ctrl->CurBufPtr != DEF_NULL) {
Mem_Copy((void *)&p_ctrl->CurBufPtr[p_ctrl->CurBufIx],
p_buf,
len_to_copy);
}
p_ctrl->CurBufIx += len_to_copy;
p_ctrl->CurBufLenRem -= len_to_copy;
buf_ix_cur += len_to_copy;
if (p_ctrl->CurBufLenRem != 0u) {
goto resubmit;
}
}
do {
CPU_INT08U cmd;
CPU_INT16U cmd_param;
if (p_ctrl->CurBufIx == 0u) { /* Get hdr only if not in data copy phase. */
p_ctrl->CurHdr = MEM_VAL_GET_INT16U_LITTLE(&((CPU_INT08U *)p_buf)[buf_ix_cur]);
}
/* Ensure not a Zero-Length EEM (ZLE) pkt. */
if (p_ctrl->CurHdr == USBD_CDC_EEM_HDR_ZLE) {
buf_ix_cur += USBD_CDC_EEM_HDR_LEN;
continue;
}
switch (DEF_BIT_FIELD_RD(p_ctrl->CurHdr, USBD_CDC_EEM_PKT_TYPE_MASK)) {
case USBD_CDC_EEM_PKT_TYPE_CMD: /* --------------------- EEM CMD ---------------------- */
/* Ensure rsvd bit is 0. */
if (DEF_BIT_FIELD_RD(p_ctrl->CurHdr, USBD_CDC_EEM_CMD_RSVD_MASK) != USBD_CDC_EEM_CMD_RSVD_OK) {
buf_ix_cur += USBD_CDC_EEM_HDR_LEN;
continue;
}
cmd = DEF_BIT_FIELD_RD(p_ctrl->CurHdr, USBD_CDC_EEM_CMD_CODE_MASK);
cmd_param = DEF_BIT_FIELD_RD(p_ctrl->CurHdr, USBD_CDC_EEM_CMD_PARAM_MASK);
switch (cmd) {
case USBD_CDC_EEM_CMD_CODE_ECHO: /* --------------------- ECHO CMD --------------------- */
if (p_ctrl->CurBufIx == 0u) {
CPU_INT16U len_to_copy;
buf_ix_cur += USBD_CDC_EEM_HDR_LEN;
p_ctrl->CurBufIx += USBD_CDC_EEM_HDR_LEN;
/* Copy echo buffer. */
p_ctrl->CurBufPtr = p_ctrl->BufEchoPtr;
p_ctrl->CurBufLenRem = DEF_MIN(DEF_BIT_FIELD_RD(cmd_param, USBD_CDC_EEM_CMD_PARAM_ECHO_LEN_MASK),
USBD_CDC_EEM_CFG_ECHO_BUF_LEN - USBD_CDC_EEM_HDR_LEN);
len_to_copy = DEF_MIN(p_ctrl->CurBufLenRem, (xfer_len - buf_ix_cur));
Mem_Copy((void *)&p_ctrl->CurBufPtr[p_ctrl->CurBufIx],
&((CPU_INT08U *)p_buf)[buf_ix_cur],
len_to_copy);
p_ctrl->CurBufIx += len_to_copy;
p_ctrl->CurBufLenRem -= len_to_copy;
buf_ix_cur += len_to_copy;
}
if (p_ctrl->CurBufLenRem == 0u) { /* If buf copy done, prepare echo resp reply. */
CPU_INT16U *p_hdr = &((CPU_INT16U *)p_ctrl->CurBufPtr)[0u];
/* Prepare hdr. */
*p_hdr = DEF_BIT_FIELD_ENC(USBD_CDC_EEM_PKT_TYPE_CMD, USBD_CDC_EEM_PKT_TYPE_MASK) |
DEF_BIT_FIELD_ENC(USBD_CDC_EEM_CMD_CODE_ECHO_RESP, USBD_CDC_EEM_CMD_CODE_MASK) |
DEF_BIT_FIELD_ENC(p_ctrl->CurBufIx - USBD_CDC_EEM_HDR_LEN, USBD_CDC_EEM_CMD_PARAM_ECHO_LEN_MASK);
USBD_CDC_EEM_TxBufSubmit(p_ctrl,
p_ctrl->CurBufPtr,
p_ctrl->CurBufIx,
&err_usbd);
p_ctrl->CurBufIx = 0u;
}
break;
case USBD_CDC_EEM_CMD_CODE_ECHO_RESP: /* ----------------- UNSUPPORTED CMDS ----------------- */
case USBD_CDC_EEM_CMD_CODE_TICKLE:
case USBD_CDC_EEM_CMD_CODE_SUSP_HINT:
case USBD_CDC_EEM_CMD_CODE_RESP_HINT:
case USBD_CDC_EEM_CMD_CODE_RESP_CMPL_HINT:
default:
buf_ix_cur+= USBD_CDC_EEM_HDR_LEN;
break;
}
break;
case USBD_CDC_EEM_PKT_TYPE_PAYLOAD: /* ------------------- EEM PAYLOADS ------------------- */
if (p_ctrl->CurBufIx == 0u) {
CPU_INT16U net_buf_len;
CPU_INT16U len_to_copy;
buf_ix_cur += USBD_CDC_EEM_HDR_LEN;
p_ctrl->CurBufPtr = p_ctrl->DrvPtr->RxBufGet(p_ctrl->ClassNbr,
p_ctrl->DrvArgPtr,
&net_buf_len);
p_ctrl->CurBufLenRem = DEF_BIT_FIELD_RD(p_ctrl->CurHdr, USBD_CDC_EEM_PAYLOAD_LEN_MASK);
if (DEF_BIT_FIELD_RD(p_ctrl->CurHdr, USBD_CDC_EEM_PAYLOAD_CRC_MASK) == USBD_CDC_EEM_PAYLOAD_CRC_CALC) {
p_ctrl->CurBufCrcComputed = DEF_YES;
} else {
p_ctrl->CurBufCrcComputed = DEF_NO;
}
/* Copy payload content to network drv's buf. */
len_to_copy = DEF_MIN(p_ctrl->CurBufLenRem, (xfer_len - buf_ix_cur));
if (p_ctrl->CurBufPtr != DEF_NULL) {
Mem_Copy((void *)&p_ctrl->CurBufPtr[p_ctrl->CurBufIx],
&((CPU_INT08U *)p_buf)[buf_ix_cur],
len_to_copy);
}
p_ctrl->CurBufIx += len_to_copy;
p_ctrl->CurBufLenRem -= len_to_copy;
buf_ix_cur += len_to_copy;
}
if (p_ctrl->CurBufLenRem == 0u) { /* If reception complete, submit buf to network drv. */
if (p_ctrl->CurBufPtr != DEF_NULL) {
CPU_CRITICAL_ENTER();
USBD_CDC_EEM_BufQ_Add(&p_ctrl->RxBufQ,
p_ctrl->CurBufPtr,
p_ctrl->CurBufIx,
p_ctrl->CurBufCrcComputed);
CPU_CRITICAL_EXIT();
p_ctrl->DrvPtr->RxBufRdy(p_ctrl->ClassNbr,
p_ctrl->DrvArgPtr);
}
p_ctrl->CurBufIx = 0u;
}
break;
default:
break;
}
} while (buf_ix_cur <= (xfer_len - USBD_CDC_EEM_HDR_LEN));
resubmit:
USBD_CDC_EEM_StateLock(p_ctrl, &err_usbd);
if ((p_ctrl->StartCnt > 0u) && /* Re-submit buf if still connected. */
(p_ctrl->State == USBD_CDC_EEM_STATE_CFG)) {
USBD_BulkRxAsync( p_ctrl->DevNbr,
p_ctrl->CommPtr->DataOutEpAddr,
p_buf,
USBD_CDC_EEM_CFG_RX_BUF_LEN,
USBD_CDC_EEM_RxCmpl,
(void *)p_ctrl,
&err_usbd);
}
USBD_CDC_EEM_StateUnlock(p_ctrl, &err_usbd);
(void)err_usbd;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_TxCmpl()
*
* Description : Inform the application about the Bulk IN transfer completion.
*
* Argument(s) : dev_nbr Device number
*
* ep_addr Endpoint address.
*
* p_buf Pointer to the receive buffer.
*
* buf_len Receive buffer length.
*
* xfer_len Number of octets sent.
*
* p_arg Additional argument provided by application.
*
* err Transfer status: success or error.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_TxCmpl (CPU_INT08U dev_nbr,
CPU_INT08U ep_addr,
void *p_buf,
CPU_INT32U buf_len,
CPU_INT32U xfer_len,
void *p_arg,
USBD_ERR err)
{
CPU_INT08U *p_next_buf;
CPU_INT16U next_buf_len;
USBD_CDC_EEM_CTRL *p_ctrl = (USBD_CDC_EEM_CTRL *)p_arg;
USBD_ERR err_lock;
CPU_SR_ALLOC();
(void)dev_nbr;
(void)ep_addr;
(void)xfer_len;
(void)err;
if ((p_buf != p_ctrl->BufEchoPtr) &&
(p_buf != DEF_NULL)) { /* Free Tx buf to network drv. */
p_ctrl->DrvPtr->TxBufFree( p_ctrl->ClassNbr,
p_ctrl->DrvArgPtr,
(CPU_INT08U *)p_buf,
buf_len);
}
USBD_CDC_EEM_StateLock(p_ctrl, &err_lock);
if ((p_ctrl->State != USBD_CDC_EEM_STATE_CFG) ||
(p_ctrl->StartCnt == 0u)) {
USBD_CDC_EEM_StateUnlock(p_ctrl, &err_lock);
return;
}
CPU_CRITICAL_ENTER(); /* Submit next buffer if any. */
p_next_buf = USBD_CDC_EEM_BufQ_Get(&p_ctrl->TxBufQ,
&next_buf_len,
DEF_NULL);
if (p_next_buf == DEF_NULL) {
p_ctrl->TxInProgress = DEF_NO;
}
CPU_CRITICAL_EXIT();
if (p_next_buf != DEF_NULL) {
USBD_ERR err_submit;
USBD_BulkTxAsync( p_ctrl->DevNbr,
p_ctrl->CommPtr->DataInEpAddr,
(void *)p_next_buf,
next_buf_len,
USBD_CDC_EEM_TxCmpl,
(void *)p_ctrl,
DEF_YES,
&err_submit);
(void)err_submit;
}
USBD_CDC_EEM_StateUnlock(p_ctrl, &err_lock);
(void)err_lock;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_TxBufSubmit()
*
* Description : Submits a transmit buffer.
*
* Argument(s) : p_ctrl Pointer to class instance control structure.
*
* p_buf Pointer to buffer that contains data to send.
*
* buf_len Length of buffer in bytes.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
*
* -RETURNED BY USBD_CDC_EEM_StateLock()-
* See USBD_CDC_EEM_StateLock() for additional return error codes.
*
* -RETURNED BY USBD_BulkTxAsync()-
* See USBD_BulkTxAsync() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_TxBufSubmit (USBD_CDC_EEM_CTRL *p_ctrl,
CPU_INT08U *p_buf,
CPU_INT16U buf_len,
USBD_ERR *p_err)
{
CPU_BOOLEAN tx_in_progress;
USBD_ERR err_unlock;
CPU_SR_ALLOC();
USBD_CDC_EEM_StateLock(p_ctrl, p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
if ((p_ctrl->State != USBD_CDC_EEM_STATE_CFG) ||
(p_ctrl->StartCnt == 0u)) {
USBD_CDC_EEM_StateUnlock(p_ctrl, &err_unlock);
*p_err = USBD_ERR_INVALID_CLASS_STATE;
return;
}
CPU_CRITICAL_ENTER();
tx_in_progress = p_ctrl->TxInProgress;
if (tx_in_progress == DEF_NO) {
p_ctrl->TxInProgress = DEF_YES; /* Submit buffer if no buffer in Q. */
} else {
USBD_CDC_EEM_BufQ_Add(&p_ctrl->TxBufQ, /* Add buffer to Q. */
p_buf,
buf_len,
DEF_NO);
}
CPU_CRITICAL_EXIT();
if (tx_in_progress == DEF_NO) {
USBD_BulkTxAsync( p_ctrl->DevNbr,
p_ctrl->CommPtr->DataInEpAddr,
(void *)p_buf,
buf_len,
USBD_CDC_EEM_TxCmpl,
(void *)p_ctrl,
DEF_YES,
p_err);
}
USBD_CDC_EEM_StateUnlock(p_ctrl, &err_unlock);
(void)err_unlock;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_BufQ_Add()
*
* Description : Adds a buffer to the tail of the Q.
*
* Argument(s) : p_buf_q Pointer to buffer Q.
*
* p_buf Pointer to buffer to add to Q.
*
* buf_len Length of buffer in bytes.
*
* crc_computed Flag that indicates if ethernet CRC is computed in submitted buffer.
*
* Return(s) : None.
*
* Note(s) : (1) This function must be called from within a critical section.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_BufQ_Add (USBD_CDC_EEM_BUF_Q *p_buf_q,
CPU_INT08U *p_buf,
CPU_INT16U buf_len,
CPU_BOOLEAN crc_computed)
{
if (p_buf_q->Cnt >= p_buf_q->Size) {
return;
}
p_buf_q->Tbl[p_buf_q->InIdx].BufPtr = p_buf;
p_buf_q->Tbl[p_buf_q->InIdx].BufLen = buf_len;
p_buf_q->Tbl[p_buf_q->InIdx].CrcComputed = crc_computed;
p_buf_q->InIdx++;
if (p_buf_q->InIdx >= p_buf_q->Size) {
p_buf_q->InIdx = 0u;
}
p_buf_q->Cnt++;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_BufQ_Get()
*
* Description : Gets a buffer from the head of the Q.
*
* Argument(s) : p_buf_q Pointer to buffer Q.
*
* p_buf_len Pointer to variable that will receive length of buffer in bytes.
*
* p_crc_computed Pointer to variable that will receive the CRC computes state of the
* buffer. Can be DEF_NULL if ignored.
*
* DEF_YES Buffer contains a valid/computed ethernet CRC.
* DEF_NO Buffer does not contains a valid/computed ethernet CRC.
*
* Return(s) : Pointer to buffer.
*
* Note(s) : (1) This function must be called from within a critical section.
*********************************************************************************************************
*/
static CPU_INT08U *USBD_CDC_EEM_BufQ_Get (USBD_CDC_EEM_BUF_Q *p_buf_q,
CPU_INT16U *p_buf_len,
CPU_BOOLEAN *p_crc_computed)
{
CPU_INT08U *p_buf;
if (p_buf_q->Cnt == 0u) {
return (DEF_NULL);
}
p_buf = p_buf_q->Tbl[p_buf_q->OutIdx].BufPtr;
*p_buf_len = p_buf_q->Tbl[p_buf_q->OutIdx].BufLen;
if (p_crc_computed != DEF_NULL) {
*p_crc_computed = p_buf_q->Tbl[p_buf_q->OutIdx].CrcComputed;
}
p_buf_q->OutIdx++;
if (p_buf_q->OutIdx >= p_buf_q->Size) {
p_buf_q->OutIdx = 0u;
}
p_buf_q->Cnt--;
return (p_buf);
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_StateLock()
*
* Description : Locks CDC-EEM class instance state.
*
* Argument(s) : p_ctrl Pointer to class instance control structure.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_OS_FAIL Lock failed.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_StateLock (USBD_CDC_EEM_CTRL *p_ctrl,
USBD_ERR *p_err)
{
KAL_ERR err_kal;
KAL_LockAcquire(p_ctrl->StateLockHandle,
KAL_OPT_PEND_NONE,
0u,
&err_kal);
*p_err = (err_kal == KAL_ERR_NONE) ? USBD_ERR_NONE : USBD_ERR_OS_FAIL;
}
/*
*********************************************************************************************************
* USBD_CDC_EEM_StateUnlock()
*
* Description : Unlocks CDC-EEM class instance state.
*
* Argument(s) : p_ctrl Pointer to class instance control structure.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Operation was successful.
* USBD_ERR_OS_FAIL Unlock failed.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_CDC_EEM_StateUnlock (USBD_CDC_EEM_CTRL *p_ctrl,
USBD_ERR *p_err)
{
KAL_ERR err_kal;
KAL_LockRelease(p_ctrl->StateLockHandle,
&err_kal);
*p_err = (err_kal == KAL_ERR_NONE) ? USBD_ERR_NONE : USBD_ERR_OS_FAIL;
}