Files
test/middleware/usb/uC-USBD/Class/CDC/NCM/usbd_ncm.c
2025-11-07 20:19:23 +08:00

1678 lines
61 KiB
C

/*
*********************************************************************************************************
* uC/USB-Device
* The Embedded USB Device Stack
*
* Copyright (c) 2021 Semidrive Semiconductor
*
* All rights reserved.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* USB COMMUNICATIONS DEVICE CLASS (CDC)
* NETWORK CONTROL MODEL (NCM)
* USB Ethernet
*
* Filename : usbd_ncm.c
* Version : V1.00.00
*********************************************************************************************************
* Note(s) : (1) This implementation is compliant with the NCM subclass specification revision 1.0
* (Errata 1), November 24, 2010.
* (2) This implementation is designed for FreeRTOS and LwIP.
* (3) This implementation supports only NTB16. SetEthernetPacketFilter isn't supported yet.
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#include <FreeRTOS.h>
#include <event_groups.h>
#include <semphr.h>
#include <lib_math.h>
#include <lwip/netifapi.h>
#include "usbd_ncm.h"
/*
*********************************************************************************************************
* MODULE
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
#define USBD_NCM_CTRL_REQ_TIMEOUT_mS 5000u
#define USBD_CDC_NCM_MAX_RETRY_CNT 3u /* Max nbr of retry when Rx buf submission fails.*/
#define NCM_NOTIFY_INTERVAL_MS (1u << 5)
/*
* CDC NCM NOTIFICATIONS DEFINES
*/
#define USBD_NCM_NTF_CONNECTED 1u /* NETWORK_CONNECTION : connected. */
#define USBD_NCM_NTF_DISCONNECT 0u /* NETWORK_CONNECTION : disconnect */
#define USBD_NCM_NOTIFY_BUF_SIZE (USBD_CDC_NOTIFICATION_HEADER + \
USBD_CDC_NTF_SPEED_DATA_LEN)
/*
* CDC NCM transfer headers, CDC NCM subclass 3.2
*/
#define USB_CDC_NCM_NTH16_SIGN 0x484D434E /* NCMH */
#define USB_CDC_NCM_NTH32_SIGN 0x686D636E /* ncmh */
/*
* CDC NCM datagram pointers, CDC NCM subclass 3.3
*/
#define USB_CDC_NCM_NDP16_CRC_SIGN 0x314D434E /* NCM1 */
#define USB_CDC_NCM_NDP16_NOCRC_SIGN 0x304D434E /* NCM0 */
#define USB_CDC_NCM_NDP32_CRC_SIGN 0x316D636E /* ncm1 */
#define USB_CDC_NCM_NDP32_NOCRC_SIGN 0x306D636E /* ncm0 */
/*
* We cannot group frames so use just the minimal size which ok to put
* one max-size ethernet frame.
* If the host can group frames, allow it to do that, 16K is selected,
* because it's used by default by the current linux host driver
*/
#define NTB_DEFAULT_IN_SIZE (USBD_CDC_NCM_CFG_TX_BUF_LEN * 1024u)
#define NTB_OUT_SIZE (USBD_CDC_NCM_CFG_RX_BUF_LEN * 1024u)
/*
*********************************************************************************************************
* LOCAL CONSTANTS
*********************************************************************************************************
*/
static const struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
.wLength = MEM_VAL_HOST_TO_LITTLE_16(sizeof(ntb_parameters)),
.bmNtbFormatsSupported = MEM_VAL_HOST_TO_LITTLE_16(USB_CDC_NCM_NTB16_SUPPORTED),
.dwNtbInMaxSize = MEM_VAL_HOST_TO_LITTLE_32(NTB_DEFAULT_IN_SIZE),
.wNdpInDivisor = MEM_VAL_HOST_TO_LITTLE_16(4),
.wNdpInPayloadRemainder = MEM_VAL_HOST_TO_LITTLE_16(0),
.wNdpInAlignment = MEM_VAL_HOST_TO_LITTLE_16(4),
.dwNtbOutMaxSize = MEM_VAL_HOST_TO_LITTLE_32(NTB_OUT_SIZE),
.wNdpOutDivisor = MEM_VAL_HOST_TO_LITTLE_16(4),
.wNdpOutPayloadRemainder = MEM_VAL_HOST_TO_LITTLE_16(0),
.wNdpOutAlignment = MEM_VAL_HOST_TO_LITTLE_16(4),
/* wNtbOutMaxDatagrams is default to 0. */
};
static struct usb_cdc_ether_desc ecm_desc = {
.bLength = sizeof ecm_desc,
.bDescriptorType = USBD_CDC_DESC_TYPE_CS_IF,
.bDescriptorSubType = USBD_CDC_DESC_SUBTYPE_ETHER_NET,
/* this descriptor actually adds value, surprise! */
/* .iMACAddress = DYNAMIC */
.bmEthernetStatistics = MEM_VAL_HOST_TO_LITTLE_32(0), /* no statistics */
.wMaxSegmentSize = MEM_VAL_HOST_TO_LITTLE_16(ETH_FRAME_LEN),
.wNumberMCFilters = MEM_VAL_HOST_TO_LITTLE_16(0),
.bNumberPowerFilters = 0,
};
static const struct usb_cdc_ncm_desc ncm_desc = {
.bLength = sizeof ncm_desc,
.bDescriptorType = USBD_CDC_DESC_TYPE_CS_IF,
.bDescriptorSubType = USBD_CDC_DESC_SUBTYPE_NCM,
.bcdNcmVersion = MEM_VAL_HOST_TO_LITTLE_16(0x0100),
/* No optional capabilities. */
.bmNetworkCapabilities = 0,
};
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
/* Temporary. Considering DeviceInterfaceGUID. */
static const CPU_INT08U USBD_NCM_MsExtPropName[] = {
'L', 0, 'a', 0, 'b', 0, 'e', 0, 'l', 0, 0, 0
};
static const CPU_INT08U USBD_NCM_MsExtPropStr[] = {
'C', 0, 'D', 0, 'C', 0, ' ', 0, 'N', 0, 'C', 0, 'M', 0, ' ', 0,
'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 0 , 0
};
static USBD_MS_OS_EXT_PROPERTY USBD_NCM_MsExtProp[] = {
{
USBD_MS_OS_PROPERTY_TYPE_REG_SZ,
USBD_NCM_MsExtPropName, sizeof(USBD_NCM_MsExtPropName),
USBD_NCM_MsExtPropStr, sizeof(USBD_NCM_MsExtPropStr)
}
};
#endif
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
enum usbd_ncm_state {
USBD_NCM_STATE_NONE = 0,
USBD_NCM_STATE_INIT,
USBD_NCM_STATE_CFG
};
typedef CPU_INT08U USBD_NCM_STATE;
typedef struct usb_cdc_ncm_nth16 {
CPU_INT32U dwSignature;
CPU_INT16U wHeaderLength;
CPU_INT16U wSequence;
CPU_INT16U wBlockLength;
CPU_INT16U wNdpIndex;
} USBD_NCM_NTH16;
/* 16-bit NCM Datagram Pointer Entry */
typedef struct usb_cdc_ncm_dpe16 {
CPU_INT16U wDatagramIndex;
CPU_INT16U wDatagramLength;
} USBD_NCM_DPE16;
/* 16-bit NCM Datagram Pointer Table */
typedef struct usb_cdc_ncm_ndp16 {
CPU_INT32U dwSignature;
CPU_INT16U wLength;
CPU_INT16U wNextNdpIndex;
USBD_NCM_DPE16 dpe16[];
} USBD_NCM_NDP16;
typedef struct usbd_ncm_pbuf {
struct pbuf_custom p;
} USBD_NCM_PBUF;
/*
*********************************************************************************************************
* CDC NCM ETHERNET CTRL DATA TYPE
*********************************************************************************************************
*/
typedef struct usbd_ncm_ctrl { /* --------- NCM SUBCLASS CONTROL INFORMATION --------- */
CPU_INT16U Mps; /* Data Endpoint max packet size. */
CPU_INT08U Nbr; /* CDC dev nbr. */
USBD_NCM_STATE State;
CPU_INT16U Sequence;
CPU_INT16U MaxInSize;
CPU_INT08U *NotifyBuf;
struct net_driver_s *net_drv;
CPU_CHAR ethaddr[(ETH_ALEN + 1) * 2];
SemaphoreHandle_t StateLockHandle; /* Handle on lock for class instance state. */
#if NCM_TX_WAIT_BUF_IDLE
EventGroupHandle_t TxCmplHandle;
CPU_INT08U TxWaitCnt; /* Tx thread waiting flag. */
#endif
CPU_INT08U RxErrCnt; /* Cnt of Rx error. */
CPU_INT08U *RxBuf[2];
CPU_INT08U *TxBuf;
CPU_INT08U *TxBufNext;
CPU_INT32U TxBufPos;
/* Datagram Pointer Entries of an NDP. */
USBD_NCM_DPE16 dpe16[USBD_CDC_NCM_CFG_TX_DPE_LEN];
CPU_INT08U TxDpePos;
} USBD_NCM_CTRL;
/*
*********************************************************************************************************
* LOCAL TABLES
*********************************************************************************************************
*/
#ifdef USBD_NCM_RX_ZERO_COPY
LWIP_MEMPOOL_DECLARE(NCM_RX_POOL, (USBD_CDC_NCM_CFG_MAX_NBR_DEV * 16u), sizeof(USBD_NCM_PBUF), "NCM RX PBUF pool");
#endif
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
static USBD_NCM_CTRL USBD_NCM_CtrlTbl[USBD_CDC_NCM_CFG_MAX_NBR_DEV];
static CPU_INT08U USBD_NCM_CtrlNbrNext;
/*
*********************************************************************************************************
* LOCAL MACRO'S
*********************************************************************************************************
*/
#define ncm_err(x, ...) ssdk_printf(SSDK_CRIT, "[NCM]" x "\r\n", ##__VA_ARGS__)
#define ncm_log(x, ...) ssdk_printf(SSDK_INFO, "[NCM]" x "\r\n", ##__VA_ARGS__)
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static CPU_BOOLEAN USBD_NCM_MgmtReq ( CPU_INT08U dev_nbr,
const USBD_SETUP_REQ *p_setup_req,
void *p_subclass_arg);
static void USBD_NCM_NotifyCmpl ( CPU_INT08U dev_nbr,
void *p_subclass_arg);
static void USBD_NCM_FnctDesc ( CPU_INT08U dev_nbr,
void *p_subclass_arg,
CPU_INT08U first_dci_if_nbr);
static CPU_INT16U USBD_NCM_FnctDescSizeGet( CPU_INT08U dev_nbr,
void *p_subclass_arg);
static void USBD_NCM_AltSetUpdate ( CPU_INT08U dev_nbr,
void *p_subclass_arg,
CPU_INT08U if_alt_nbr);
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
static CPU_INT08U USBD_NCM_MsExtPropTblGet( CPU_INT08U dev_nbr,
void *p_subclass_arg,
USBD_MS_OS_EXT_PROPERTY **pp_ext_property_tbl);
#endif
static void USBD_NCM_TxDataAddNdp ( USBD_NCM_CTRL *p_ctrl);
static void USBD_NCM_CommStart ( USBD_NCM_CTRL *p_ctrl,
USBD_ERR *p_err);
static void USBD_NCM_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_NCM_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);
/*
*********************************************************************************************************
* CDC NCM CLASS DRIVER
*********************************************************************************************************
*/
static USBD_CDC_SUBCLASS_DRV USBD_ACM_SerialDrv = {
USBD_NCM_MgmtReq,
USBD_NCM_NotifyCmpl,
USBD_NCM_FnctDesc,
USBD_NCM_FnctDescSizeGet,
USBD_NCM_AltSetUpdate,
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
USBD_NCM_MsExtPropTblGet
#endif
};
/*
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INTERFACE DRIVER
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* APPLICATION FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
static inline CPU_CHAR USBD_NCM_Hex2Char (CPU_INT08U hex)
{
if (hex > 9u) {
return hex + ('A' - 0xA);
} else {
return hex + '0';
}
}
static inline void USBD_NCM_StateLock (USBD_NCM_CTRL *p_ctrl)
{
xSemaphoreTake(p_ctrl->StateLockHandle, portMAX_DELAY);
}
static inline void USBD_NCM_StateUnlock (USBD_NCM_CTRL *p_ctrl)
{
xSemaphoreGive(p_ctrl->StateLockHandle);
}
/*
*********************************************************************************************************
* USBD_NCM_Init()
*
* Description : Initialize CDC NCM usb ethernet subclass.
*
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE CDC NCM subclass initialized successfully.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_NCM_Init (USBD_ERR *p_err)
{
CPU_INT08U ix;
USBD_NCM_CTRL *p_ctrl;
USBD_NCM_NTH16 *nth;
LIB_ERR err_lib;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
#endif
#ifdef USBD_NCM_RX_ZERO_COPY
LWIP_MEMPOOL_INIT(NCM_RX_POOL);
#endif
/* Init NCM ctrl. */
for (ix = 0u; ix < USBD_CDC_NCM_CFG_MAX_NBR_DEV; ix++) {
p_ctrl = &USBD_NCM_CtrlTbl[ix];
Mem_Clr(p_ctrl, sizeof(*p_ctrl));
p_ctrl->Nbr = USBD_CDC_NBR_NONE;
p_ctrl->Mps = 64u;
p_ctrl->MaxInSize = NTB_DEFAULT_IN_SIZE;
Mem_Set(p_ctrl->ethaddr, '0', ETH_ALEN * 2);
p_ctrl->ethaddr[ETH_ALEN * 2] = 0;
p_ctrl->StateLockHandle = xSemaphoreCreateMutex();
if (!p_ctrl->StateLockHandle) {
*p_err = USBD_ERR_OS_SIGNAL_CREATE;
return;
}
p_ctrl->NotifyBuf = (CPU_INT08U *)Mem_HeapAlloc( USBD_NCM_NOTIFY_BUF_SIZE,
USBD_CFG_BUF_ALIGN_OCTETS,
(CPU_SIZE_T *)DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return;
}
Mem_Clr((void *)&p_ctrl->NotifyBuf[0], USBD_NCM_NOTIFY_BUF_SIZE);
p_ctrl->TxBuf = (CPU_INT08U *)Mem_HeapAlloc( (NTB_DEFAULT_IN_SIZE + NTB_OUT_SIZE) * 2,
CONFIG_ARCH_CACHE_LINE,
(CPU_SIZE_T *)DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return;
}
p_ctrl->TxBufNext = p_ctrl->TxBuf + NTB_DEFAULT_IN_SIZE;
p_ctrl->RxBuf[0] = p_ctrl->TxBufNext + NTB_DEFAULT_IN_SIZE;
p_ctrl->RxBuf[1] = p_ctrl->RxBuf[0] + NTB_OUT_SIZE;
nth = (USBD_NCM_NTH16 *)p_ctrl->TxBuf;
nth->dwSignature = MEM_VAL_HOST_TO_LITTLE_32(USB_CDC_NCM_NTH16_SIGN);
nth->wHeaderLength = MEM_VAL_HOST_TO_LITTLE_16(sizeof(*nth));
Mem_Copy(p_ctrl->TxBufNext, nth, sizeof(nth->dwSignature) + sizeof(nth->wHeaderLength));
}
USBD_NCM_CtrlNbrNext = 0u;
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_NCM_Add()
*
* Description : Add a new instance of the CDC NCM usb ethernet subclass.
*
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE CDC NCM subclass instance successfully added.
* USBD_ERR_ALLOC CDC NCM subclass instance NOT available.
*
* ---------- RETURNED BY USBD_CDC_Add() : ----------
* USBD_ERR_ALLOC CDC class instance NOT available.
*
* ------ RETURNED BY USBD_CDC_DataIF_Add() : -------
* USBD_ERR_ALLOC Data interface instance NOT available.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr/'isoc_en'.
*
* Return(s) : CDC NCM usb ethernet subclass instance number.
*
* Note(s) : none.
*********************************************************************************************************
*/
CPU_INT08U USBD_NCM_Add (USBD_ERR *p_err)
{
USBD_NCM_CTRL *p_ctrl;
CPU_INT08U subclass_nbr;
CPU_INT08U class_nbr;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(0);
}
#endif
CPU_CRITICAL_ENTER();
subclass_nbr = USBD_NCM_CtrlNbrNext; /* Alloc new CDC ACM serial emulation subclass. */
if (subclass_nbr >= USBD_CDC_NCM_CFG_MAX_NBR_DEV) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CDC_SUBCLASS_INSTANCE_ALLOC;
return (USBD_NCM_NBR_NONE);
}
USBD_NCM_CtrlNbrNext++;
CPU_CRITICAL_EXIT();
/* Init control struct. */
p_ctrl = &USBD_NCM_CtrlTbl[subclass_nbr];
/* Create new CDC device. */
class_nbr = USBD_CDC_Add( USBD_CDC_SUBCLASS_NCM,
&USBD_ACM_SerialDrv,
(void *)p_ctrl,
USBD_CDC_COMM_PROTOCOL_NONE,
DEF_ENABLED,
NCM_NOTIFY_INTERVAL_MS,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (USBD_NCM_NBR_NONE);
}
/* Add data IF class to CDC device. */
(void)USBD_CDC_DataIF_Add(class_nbr,
DEF_DISABLED,
USBD_CDC_DATA_PROTOCOL_NTB,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (USBD_NCM_NBR_NONE);
}
p_ctrl->Nbr = class_nbr;
*p_err = USBD_ERR_NONE;
return (subclass_nbr);
}
/*
*********************************************************************************************************
* USBD_NCM_CfgAdd()
*
* Description : Add CDC NCM subclass class instance into USB device configuration.
*
* Argument(s) : subclass_nbr CDC NCM usb ethernet subclass instance number.
*
* dev_nbr Device number.
*
* cfg_nbr Configuration index to add new test class interface to.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE CDC NCM subclass configuration
* successfully added.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'subclass_nbr'.
*
* -------- RETURNED BY USBD_CDC_CfgAdd() : --------
* USBD_ERR_ALLOC CDC class communication instances NOT available.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'interval'.
*
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_INVALID_CLASS_STATE Invalid class state.
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
* USBD_ERR_IF_ALLOC Interfaces NOT available.
* USBD_ERR_IF_ALT_ALLOC Interface alternate settings NOT available.
* USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available.
* USBD_ERR_EP_ALLOC Endpoints NOT available.
*
* Return(s) : DEF_YES, if CDC NCM usb ethernet subclass instance added to USB device configuration
* successfully.
*
* DEF_NO, otherwise.
*
* Note(s) : none.
*********************************************************************************************************
*/
CPU_BOOLEAN USBD_NCM_CfgAdd (CPU_INT08U subclass_nbr,
CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
USBD_ERR *p_err)
{
USBD_NCM_CTRL *p_ctrl;
#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
if (subclass_nbr >= USBD_NCM_CtrlNbrNext) {
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return (DEF_NO);
}
p_ctrl = &USBD_NCM_CtrlTbl[subclass_nbr];
(void)USBD_CDC_CfgAdd(p_ctrl->Nbr,
dev_nbr,
cfg_nbr,
"CDC NCM Comm",
"CDC NCM Data",
"CDC NCM",
p_err);
if (*p_err != USBD_ERR_NONE) {
return (DEF_NO);
}
USBD_StrAdd(dev_nbr,
p_ctrl->ethaddr, /* String descriptor for ethernet MAC address. */
p_err);
if (*p_err != USBD_ERR_NONE) {
return (DEF_NO);
}
return (DEF_YES);
}
/*
*********************************************************************************************************
* USBD_NCM_Bind()
*
* Description : Bind CDC NCM class instance with network driver and MAC address.
*
* Argument(s) : subclass_nbr CDC NCM usb ethernet subclass instance number.
*
* nd Pointer to network driver structure.
*
* mac_addr Pointer to Ethernet MAC address buffer.
*
*
* Return(s) : DEF_YES, if CDC NCM usb ethernet subclass instance bound with network driver successfully.
*
* DEF_NO, otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_BOOLEAN USBD_NCM_Bind (CPU_INT08U subclass_nbr,
struct net_driver_s *nd,
CPU_INT08U *mac_addr)
{
USBD_NCM_CTRL *p_ctrl;
CPU_INT08U ix;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (!nd || !mac_addr) {
return (DEF_NO);
}
#endif
if (subclass_nbr >= USBD_NCM_CtrlNbrNext) {
return (DEF_NO);
}
p_ctrl = &USBD_NCM_CtrlTbl[subclass_nbr];
p_ctrl->net_drv = nd;
/* Refer to Description of iMACAddress, chapter 5.4, ECM Specification. */
for (ix = 0u; ix < ETH_ALEN; ix++) {
p_ctrl->ethaddr[ix * 2u] = USBD_NCM_Hex2Char(mac_addr[ix] >> 4);
p_ctrl->ethaddr[ix * 2u + 1u] = USBD_NCM_Hex2Char(mac_addr[ix] & 0xFu);
}
p_ctrl->ethaddr[ETH_ALEN * 2] = 0;
return (DEF_YES);
}
/*
*********************************************************************************************************
* USBD_NCM_MgmtReq()
*
* Description : CDC NCM usb ethernet class management request.
*
* Argument(s) : dev_nbr Device number.
*
* p_setup_req Pointer to setup request structure.
*
* p_subclass_arg Pointer to subclass argument.
*
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
*
* DEF_FAIL, otherwise.
*
* Note(s) : (1) CDC NCM defines many requests in section 6.2 of the specification.
* Only the "required" 3 requests are implemented.
*
* (a) GET_NTB_PARAMETERS Requests the function to report parameters that characterize
* the Network Control Block.
*
* (b) GET_NTB_INPUT_SIZE Get the current value of maximum NTB input size.
*
* (c) SET_NTB_INPUT_SIZE Selects the maximum size of NTBs to be transmitted by the
* function over the bulk IN pipe.
*
* See 'Universal Serial Bus Communications Class Subclass Specification for Network Control
* Model Devices 24/11/2010, Version 1.0', section 6.2 for more details about NCM requests.
*
*********************************************************************************************************
*/
static CPU_BOOLEAN USBD_NCM_MgmtReq ( CPU_INT08U dev_nbr,
const USBD_SETUP_REQ *p_setup_req,
void *p_subclass_arg)
{
USBD_NCM_CTRL *p_ctrl;
CPU_INT16U request_code;
CPU_INT16U request_len;
CPU_INT32U request_ntbsz;
CPU_BOOLEAN valid;
USBD_ERR err;
p_ctrl = (USBD_NCM_CTRL *)p_subclass_arg;
valid = DEF_FAIL;
request_code = (p_setup_req->bmRequestType << 8) | p_setup_req->bRequest;
switch (request_code) {
case ((USBD_REQ_DIR_DEVICE_TO_HOST | USBD_REQ_TYPE_CLASS | USBD_REQ_RECIPIENT_INTERFACE) << 8)
| USBD_CDC_REQ_GET_NTB_PARAM:
if (p_setup_req->wLength == 0 || p_setup_req->wValue != 0)
break;
request_len = p_setup_req->wLength > sizeof ntb_parameters ?
sizeof ntb_parameters : p_setup_req->wLength;
(void)USBD_CtrlTx( dev_nbr,
(void *)&ntb_parameters,
request_len,
USBD_NCM_CTRL_REQ_TIMEOUT_mS,
DEF_NO,
&err);
if (err == USBD_ERR_NONE) {
valid = DEF_OK;
}
ncm_log("Host asked NTB parameters");
break;
case ((USBD_REQ_DIR_DEVICE_TO_HOST | USBD_REQ_TYPE_CLASS | USBD_REQ_RECIPIENT_INTERFACE) << 8)
| USBD_CDC_REQ_GET_NTB_INPUT_SIZE:
if (p_setup_req->wLength < 4u || p_setup_req->wValue != 0)
break;
request_ntbsz = MEM_VAL_HOST_TO_LITTLE_32(p_ctrl->MaxInSize);
(void)USBD_CtrlTx( dev_nbr,
(void *)&request_ntbsz,
4u,
USBD_NCM_CTRL_REQ_TIMEOUT_mS,
DEF_NO,
&err);
if (err == USBD_ERR_NONE) {
valid = DEF_OK;
}
ncm_log("Host asked INPUT SIZE, sending %d", p_ctrl->MaxInSize);
break;
case ((USBD_REQ_DIR_HOST_TO_DEVICE | USBD_REQ_TYPE_CLASS | USBD_REQ_RECIPIENT_INTERFACE) << 8)
| USBD_CDC_REQ_SET_NTB_INPUT_SIZE:
if (p_setup_req->wLength != 4u || p_setup_req->wValue != 0)
break;
(void)USBD_CtrlRx( dev_nbr,
(void *)&request_ntbsz,
4u,
USBD_NCM_CTRL_REQ_TIMEOUT_mS,
&err);
if (err == USBD_ERR_NONE) {
request_ntbsz = MEM_VAL_LITTLE_TO_HOST_32(request_ntbsz);
if (request_ntbsz < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
request_ntbsz > NTB_DEFAULT_IN_SIZE) {
break;
}
ncm_log("Host Set NTB INPUT SIZE %d", request_ntbsz);
p_ctrl->MaxInSize = request_ntbsz;
valid = DEF_OK;
}
break;
default:
break;
}
return (valid);
}
/*
*********************************************************************************************************
* USBD_NCM_NotifyCmpl()
*
* Description : NCM subclass notification complete callback.
*
* Argument(s) : dev_nbr Device number.
*
* p_subclass_arg Pointer to NCM subclass notification complete callback argument.
*
* Return(s) : none.
*
* Note(s) : (1) Look up NetworkConnectionChange format in section 6.3.1, 'Universal Serial Bus
* Class Definitions for Communications Devices 03/11/2010, Version 1.2'.
*********************************************************************************************************
*/
static void USBD_NCM_NotifyCmpl (CPU_INT08U dev_nbr,
void *p_subclass_arg)
{
USBD_NCM_CTRL *p_ctrl;
CPU_INT08U bNotificationCode;
USBD_ERR err;
(void)dev_nbr;
p_ctrl = (USBD_NCM_CTRL *)p_subclass_arg;
/* Get previous notification code. */
bNotificationCode = p_ctrl->NotifyBuf[1u];
if (bNotificationCode == USBD_CDC_NTF_SPEED_CHANGE) {
(void)USBD_CDC_Notify( p_ctrl->Nbr,
USBD_CDC_NTF_NETWORK_CONNECTION,
USBD_NCM_NTF_CONNECTED,
&p_ctrl->NotifyBuf[0],
0u,
&err);
}
return;
}
/*
*********************************************************************************************************
* USBD_NCM_AltSetUpdate()
*
* Description : Notify class that CDC NCM Data interface alternate setting has been updated.
*
* Argument(s) : dev_nbr Device number.
*
* p_subclass_arg Pointer to subclass argument.
*
* if_alt_nbr Interface alternate setting number.
*
* Return(s) : None.
*
* Note(s) : (1) Alternate Settings 0 resets data pipes and NCM settings.
* Alternate Settings 1 enables the NCM data function.
*
* See 'Universal Serial Bus Communications Class Subclass Specification for Network Control
* Model Devices 24/11/2010, Version 1.0', section 7.2 for more details about Using
* Alternate Settings to Reset an NCM Function.
*
* (2) Look up ConnectionSpeedChange format in section 6.3.3, 'Universal Serial Bus Class Definitions
* for Communications Devices 03/11/2010, Version 1.2'.
*
*********************************************************************************************************
*/
static void USBD_NCM_AltSetUpdate (CPU_INT08U dev_nbr,
void *p_subclass_arg,
CPU_INT08U if_alt_nbr)
{
USBD_NCM_CTRL *p_ctrl;
USBD_ERR err;
CPU_INT32U *speed;
p_ctrl = (USBD_NCM_CTRL *)p_subclass_arg;
if (if_alt_nbr == 0u) { /* Default IF has NO EP. */
netifapi_netif_set_link_down(&p_ctrl->net_drv->d_netif);
USBD_NCM_StateLock(p_ctrl);
p_ctrl->State = USBD_NCM_STATE_INIT;
p_ctrl->MaxInSize = NTB_DEFAULT_IN_SIZE;
p_ctrl->Sequence = 0;
USBD_NCM_StateUnlock(p_ctrl);
ncm_log("Set Alt0, Eth down");
} else { /* Operational IF. */
speed = (CPU_INT32U *)&p_ctrl->NotifyBuf[USBD_CDC_NOTIFICATION_HEADER];
if (USBD_DevSpdGet(dev_nbr, &err) == USBD_DEV_SPD_HIGH) {
p_ctrl->Mps = 512u;
*speed = MEM_VAL_HOST_TO_LITTLE_32(150u * 1000 * 1000);
} else {
p_ctrl->Mps = 64u;
*speed = MEM_VAL_HOST_TO_LITTLE_32(10u * 1000 * 1000);
}
speed[1] = speed[0];
USBD_CDC_Notify(p_ctrl->Nbr,
USBD_CDC_NTF_SPEED_CHANGE,
0,
&p_ctrl->NotifyBuf[0],
USBD_CDC_NTF_SPEED_DATA_LEN,
&err);
USBD_NCM_StateLock(p_ctrl);
p_ctrl->State = USBD_NCM_STATE_CFG;
USBD_NCM_CommStart(p_ctrl, &err);
USBD_NCM_StateUnlock(p_ctrl);
if (err == USBD_ERR_NONE) {
netifapi_netif_set_link_up(&p_ctrl->net_drv->d_netif);
}
ncm_log("Set Alt1, Eth up");
}
}
/*
*********************************************************************************************************
* USBD_NCM_FnctDesc()
*
* Description : CDC Subclass interface descriptor callback.
*
* Argument(s) : dev_nbr Device number.
*
* p_subclass_arg Pointer to subclass argument.
*
* first_dci_if_nbr Unused for CDC NCM.
*
* Return(s) : none.
*
* Note(s) : none.
*
*********************************************************************************************************
*/
static void USBD_NCM_FnctDesc (CPU_INT08U dev_nbr,
void *p_subclass_arg,
CPU_INT08U first_dci_if_nbr)
{
USBD_NCM_CTRL *p_ctrl = (USBD_NCM_CTRL *)p_subclass_arg;
ecm_desc.iMACAddress = USBD_StrIxGet(dev_nbr, p_ctrl->ethaddr);
USBD_DescWr(dev_nbr, (CPU_INT08U *)&ecm_desc, sizeof(ecm_desc));
USBD_DescWr(dev_nbr, (CPU_INT08U *)&ncm_desc, sizeof(ncm_desc));
return;
}
/*
*********************************************************************************************************
* USBD_NCM_FnctDescSizeGet()
*
* Description : Retrieve the size of the CDC subclass interface descriptor.
*
* Argument(s) : dev_nbr Device number.
*
* p_subclass_arg Pointer to subclass argument.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
static CPU_INT16U USBD_NCM_FnctDescSizeGet (CPU_INT08U dev_nbr,
void *p_subclass_arg)
{
(void)dev_nbr;
(void)p_subclass_arg;
return (sizeof(ecm_desc) + sizeof(ncm_desc));
}
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
static CPU_INT08U USBD_NCM_MsExtPropTblGet(CPU_INT08U dev_nbr,
void *p_subclass_arg,
USBD_MS_OS_EXT_PROPERTY **pp_ext_property_tbl)
{
(void)dev_nbr;
(void)p_subclass_arg;
*pp_ext_property_tbl = USBD_NCM_MsExtProp;
return sizeof(USBD_NCM_MsExtProp) / sizeof(USBD_NCM_MsExtProp[0]);
}
#endif
/*
*********************************************************************************************************
* USBD_NCM_PbufCopy()
*
* Description : Copy pbuf data to the current TxBuf and record its position and length.
*
* Argument(s) : p_ctrl Pointer to NCM class instance control structure.
*
* p Pointer to data to be transmitted.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_BOOLEAN USBD_NCM_PbufCopy (USBD_NCM_CTRL *p_ctrl, struct pbuf *p)
{
struct pbuf *q;
CPU_INT32U tx_len;
tx_len = (CPU_INT32U)p_ctrl->TxBufPos + MATH_ROUND_INC_UP_PWR2(p->tot_len, 4u);
tx_len += sizeof(USBD_NCM_NDP16) + (p_ctrl->TxDpePos + 2u) * sizeof(USBD_NCM_DPE16);
if (tx_len > (CPU_INT32U)p_ctrl->MaxInSize) {
return 0;
}
p_ctrl->dpe16[p_ctrl->TxDpePos].wDatagramIndex = MEM_VAL_HOST_TO_LITTLE_16(p_ctrl->TxBufPos);
p_ctrl->dpe16[p_ctrl->TxDpePos].wDatagramLength = MEM_VAL_HOST_TO_LITTLE_16(p->tot_len);
p_ctrl->TxDpePos++;
for (q = p; q != NULL; q = q->next) {
Mem_Copy((uint8_t *)(p_ctrl->TxBuf + p_ctrl->TxBufPos),
(uint8_t *)((uint8_t *)q->payload),
q->len);
p_ctrl->TxBufPos += q->len;
}
p_ctrl->TxBufPos = MATH_ROUND_INC_UP_PWR2(p_ctrl->TxBufPos, 4u);
if (p_ctrl->TxDpePos == USBD_CDC_NCM_CFG_TX_DPE_LEN) {
USBD_NCM_TxDataAddNdp(p_ctrl);
}
return 1;
}
/*
*********************************************************************************************************
* USBD_NCM_TxDataAddNdp()
*
* Description : Write the buffered Datagram Pointer Entries to TxBuf in NDP16 format.
*
* Argument(s) : p_ctrl Pointer to NCM class instance control structure.
*
* Return(s) : None.
*
* Note(s) : (1) Look up NDP16 format in section 3.3.1, 'Universal Serial Bus Communications Class
* Subclass Specification for Network Control Model Devices 24/11/2010, Version 1.0'.
*********************************************************************************************************
*/
static void USBD_NCM_TxDataAddNdp (USBD_NCM_CTRL *p_ctrl)
{
USBD_NCM_NTH16 *nth;
USBD_NCM_NDP16 *ndp;
CPU_INT16U ndp_len;
if (!p_ctrl->TxDpePos)
return;
nth = (USBD_NCM_NTH16 *)p_ctrl->TxBuf;
if (!nth->wNdpIndex) {
nth->wNdpIndex = MEM_VAL_HOST_TO_LITTLE_16(p_ctrl->TxBufPos);
} else {
ndp = (USBD_NCM_NDP16 *)(p_ctrl->TxBuf + MEM_VAL_LITTLE_TO_HOST_16(nth->wNdpIndex));
while (ndp->wNextNdpIndex) {
ndp = (USBD_NCM_NDP16 *)(p_ctrl->TxBuf + MEM_VAL_LITTLE_TO_HOST_16(ndp->wNextNdpIndex));
}
ndp->wNextNdpIndex = MEM_VAL_HOST_TO_LITTLE_16(p_ctrl->TxBufPos);
}
ndp_len = sizeof(*ndp) + (p_ctrl->TxDpePos + 1u) * sizeof(USBD_NCM_DPE16);
ndp = (USBD_NCM_NDP16 *)(p_ctrl->TxBuf + p_ctrl->TxBufPos);
p_ctrl->TxBufPos += ndp_len;
ndp->dwSignature = MEM_VAL_HOST_TO_LITTLE_32(USB_CDC_NCM_NDP16_NOCRC_SIGN);
ndp->wLength = MEM_VAL_HOST_TO_LITTLE_16(ndp_len);
ndp->wNextNdpIndex = 0;
Mem_Copy(&ndp->dpe16[0], &p_ctrl->dpe16[0], p_ctrl->TxDpePos * sizeof(USBD_NCM_DPE16));
/* Add Tail Datagram Pointer Entry. */
ndp->dpe16[p_ctrl->TxDpePos].wDatagramIndex = 0;
ndp->dpe16[p_ctrl->TxDpePos].wDatagramLength = 0;
/* Clear TxDpe buffer. */
p_ctrl->TxDpePos = 0;
return;
}
/*
*********************************************************************************************************
* USBD_NCM_TxDataPktSubmit()
*
* Description : Pack the NCM Transfer Block (NTB) and then send it to the host.
*
* Argument(s) : p_ctrl Pointer to NCM 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_BulkTxAsync()-
* See USBD_BulkTxAsync() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_NCM_TxDataPktSubmit (USBD_NCM_CTRL *p_ctrl,
USBD_ERR *p_err)
{
USBD_NCM_NTH16 *nth;
CPU_INT32U tx_len;
USBD_NCM_TxDataAddNdp(p_ctrl);
tx_len = p_ctrl->TxBufPos;
if (p_ctrl->TxBufPos < p_ctrl->MaxInSize) {
if (!(p_ctrl->TxBufPos & (p_ctrl->Mps - 1))) {
tx_len ++;
}
}
nth = (USBD_NCM_NTH16 *)p_ctrl->TxBuf;
nth->wSequence = MEM_VAL_HOST_TO_LITTLE_16(p_ctrl->Sequence);
nth->wBlockLength = MEM_VAL_HOST_TO_LITTLE_16(tx_len);
USBD_CDC_DataTxAsync( p_ctrl->Nbr,
0u,
p_ctrl->TxBuf,
tx_len,
USBD_NCM_TxCmpl,
(void *)p_ctrl,
p_err);
if (*p_err != USBD_ERR_NONE) {
ncm_err("Unexpected Tx start err %d", *p_err);
return;
}
p_ctrl->TxBuf = p_ctrl->TxBufNext;
p_ctrl->TxBufNext = NULL;
p_ctrl->Sequence++;
nth = (USBD_NCM_NTH16 *)p_ctrl->TxBuf;
nth->wBlockLength = 0;
nth->wNdpIndex = 0;
p_ctrl->TxBufPos = sizeof(*nth);
return;
}
/*
*********************************************************************************************************
* USBD_NCM_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_NCM_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)
{
USBD_NCM_CTRL *p_ctrl = (USBD_NCM_CTRL *)p_arg;
USBD_ERR err_submit;
(void)dev_nbr;
(void)ep_addr;
(void)buf_len;
(void)xfer_len;
if ((err != USBD_ERR_NONE) && (err != USBD_ERR_EP_ABORT)) {
ncm_err("Unexpected Tx Cmpl err %d", err);
}
USBD_NCM_StateLock(p_ctrl);
ASSERT(!p_ctrl->TxBufNext);
p_ctrl->TxBufNext = p_buf;
if ((err != USBD_ERR_EP_ABORT) &&
(p_ctrl->TxBufPos > sizeof(USBD_NCM_NTH16)) &&
(p_ctrl->State == USBD_NCM_STATE_CFG)) {
USBD_NCM_TxDataPktSubmit(p_ctrl, &err_submit);
}
USBD_NCM_StateUnlock(p_ctrl);
return;
}
/*
*********************************************************************************************************
* USBD_NCM_TxDatagramSubmit()
*
* Description : Submit an Ethernet Frame to the current Tx buffer. If the Bulk IN EP is idle, start the transfer.
*
* Argument(s) : class_nbr NCM Class instance number.
*
* p Pointer to the Ethernet Frame buffer.
*
* 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'.
* USBD_ERR_INVALID_CLASS_STATE Invalid interface state.
*
* -RETURNED BY USBD_NCM_TxDataPktSubmit()-
* See USBD_NCM_TxDataPktSubmit() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
void USBD_NCM_TxDatagramSubmit (CPU_INT08U subclass_nbr,
struct pbuf *p,
USBD_ERR *p_err)
{
USBD_NCM_CTRL *p_ctrl;
CPU_BOOLEAN pbf_copy;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (!p_err || !p) { /* Validate error ptr. */
CPU_SW_EXCEPTION(0);
return;
}
#endif
if (subclass_nbr >= USBD_NCM_CtrlNbrNext) {
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return ;
}
if ((p->tot_len > ETH_FRAME_LEN) || (p->tot_len < ETH_HLEN)) {
*p_err = USBD_ERR_INVALID_ARG;
return ;
}
p_ctrl = &USBD_NCM_CtrlTbl[subclass_nbr];
USBD_NCM_StateLock(p_ctrl);
if (p_ctrl->State != USBD_NCM_STATE_CFG) {
USBD_NCM_StateUnlock(p_ctrl);
*p_err = USBD_ERR_INVALID_CLASS_STATE;
return;
}
pbf_copy = USBD_NCM_PbufCopy(p_ctrl, p);
/* If the Bulk IN EP is idle, start sending the NTB. */
if (p_ctrl->TxBufNext) {
USBD_NCM_TxDataPktSubmit(p_ctrl, p_err);
if (*p_err != USBD_ERR_NONE) {
USBD_NCM_StateUnlock(p_ctrl);
return;
}
if (!pbf_copy) {
pbf_copy = USBD_NCM_PbufCopy(p_ctrl, p);
}
}
USBD_NCM_StateUnlock(p_ctrl);
if (!pbf_copy) {
*p_err = USBD_ERR_ALLOC;
} else {
*p_err = USBD_ERR_NONE;
}
return;
}
/*
*********************************************************************************************************
* USBD_NCM_PbufFree()
*
* Description : Free no-longer-used USBD_NCM_PBUF memory.
*
* Argument(s) : p Pointer to pbuf memory to be freed.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
#ifdef USBD_NCM_RX_ZERO_COPY
static void USBD_NCM_PbufFree (struct pbuf *p)
{
USBD_NCM_PBUF *ncm_pbuf = (USBD_NCM_PBUF *)p;
LWIP_MEMPOOL_FREE(NCM_RX_POOL, ncm_pbuf);
#error "Do not release RxBuf[] before its all pbuf are freed."
return;
}
#endif
/*
*********************************************************************************************************
* USBD_NCM_RxHandle()
*
* Description : Handle received NCM Transfer Block (NTB).
*
* Argument(s) : p_ctrl Pointer to NCM class instance control structure.
*
* buf_len Receive buffer length.
*
* xfer_len Number of octets received.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_NCM_RxHandle (USBD_NCM_CTRL *p_ctrl,
void *p_buf,
CPU_INT32U xfer_len)
{
USBD_NCM_NTH16 *p_nth;
USBD_NCM_NDP16 *p_ndp;
struct pbuf *pbf;
CPU_INT32U block_len;
CPU_INT32U ndp_index;
CPU_INT32U ndp_len;
CPU_INT08U crc_len = 0;
CPU_INT32U dg_index;
CPU_INT32U index;
CPU_INT32U dg_len;
p_nth = p_buf;
if (MEM_VAL_LITTLE_TO_HOST_32(p_nth->dwSignature) != USB_CDC_NCM_NTH16_SIGN) {
return;
}
if (MEM_VAL_LITTLE_TO_HOST_16(p_nth->wHeaderLength) != sizeof(USBD_NCM_NTH16)) {
return;
}
block_len = MEM_VAL_LITTLE_TO_HOST_16(p_nth->wBlockLength);
if (block_len > xfer_len) {
return;
}
ndp_index = MEM_VAL_LITTLE_TO_HOST_16(p_nth->wNdpIndex);
/* Run through all the NDP's in the NTB */
do {
/*
* NCM 3.2
* dwNdpIndex
*/
if ((ndp_index & 0x3u) ||
(ndp_index < sizeof(USBD_NCM_NTH16)) ||
(ndp_index + sizeof(USBD_NCM_NDP16) > block_len)) {
return;
}
p_ndp = (USBD_NCM_NDP16 *)((CPU_INT08U *)p_buf + ndp_index);
/*
* walk through NDP
* dwSignature
*/
if (MEM_VAL_LITTLE_TO_HOST_32(p_ndp->dwSignature) == USB_CDC_NCM_NDP16_CRC_SIGN) {
crc_len = 4u;
} else if (MEM_VAL_LITTLE_TO_HOST_32(p_ndp->dwSignature) == USB_CDC_NCM_NDP16_NOCRC_SIGN) {
crc_len = 0u;
} else {
return;
}
ndp_len = MEM_VAL_LITTLE_TO_HOST_16(p_ndp->wLength);
/*
* NCM 3.3.1
* wLength
* entry is 2 items
* item size is 16/32 bits, opts->dgram_item_len * 2 bytes
* minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
* Each entry is a dgram index and a dgram length.
*/
if ((ndp_len & 3u) ||
(ndp_len < sizeof(USBD_NCM_NDP16) + 2 * sizeof(struct usb_cdc_ncm_dpe16))) {
return;
}
/* Check for another NDP (d)wNextNdpIndex */
ndp_index = MEM_VAL_LITTLE_TO_HOST_16(p_ndp->wNextNdpIndex);
ndp_len = (ndp_len - sizeof(USBD_NCM_NDP16)) / sizeof(struct usb_cdc_ncm_dpe16);
dg_index = 0;
do {
index = MEM_VAL_LITTLE_TO_HOST_16(p_ndp->dpe16[dg_index].wDatagramIndex);
dg_len = MEM_VAL_LITTLE_TO_HOST_16(p_ndp->dpe16[dg_index].wDatagramLength);
/* wDatagramIndex[0] */
if ((index < sizeof(USBD_NCM_NTH16)) ||
(index + dg_len > block_len)) {
return;
}
dg_len -= crc_len;
if ((dg_len < ETH_HLEN) || (dg_len > ETH_FRAME_LEN)) {
return;
}
#ifndef USBD_NCM_RX_ZERO_COPY
pbf = pbuf_alloc(PBUF_RAW, dg_len, PBUF_POOL);
if (pbf) {
CPU_INT32U offset = index;
struct pbuf *q = NULL;
for (q = pbf; q != NULL; q = q->next) {
memcpy((CPU_INT08U *)q->payload, ((CPU_INT08U *)p_buf + offset),
q->len);
offset += q->len;
}
} else {
ncm_err("pbuf alloc fail");
return;
}
#else
USBD_NCM_PBUF *ncm_pbuf = (USBD_NCM_PBUF *)LWIP_MEMPOOL_ALLOC(NCM_RX_POOL);
if (!ncm_pbuf) {
ncm_err("NCM_RX_POOL alloc fail");
return;
}
ncm_pbuf->p.custom_free_function = USBD_NCM_PbufFree;
pbf = pbuf_alloced_custom( PBUF_RAW,
dg_len,
PBUF_REF,
&ncm_pbuf->p,
(CPU_INT08U *)p_buf + index,
dg_len);
#endif
netdev_input(p_ctrl->net_drv, pbf);
dg_index++;
if (!p_ndp->dpe16[dg_index].wDatagramIndex || !p_ndp->dpe16[dg_index].wDatagramLength) {
break;
}
} while (dg_index < ndp_len);
} while (ndp_index);
return;
}
/*
*********************************************************************************************************
* USBD_NCM_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_NCM_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_INT08U buf_ix = 0u;
USBD_NCM_CTRL *p_ctrl = (USBD_NCM_CTRL *)p_arg;
USBD_ERR err_usbd;
(void)dev_nbr;
(void)ep_addr;
(void)buf_len;
/* Find the corresponding position to p_buf. */
if (!p_ctrl->RxBuf[0]) {
buf_ix = 0;
} else if (!p_ctrl->RxBuf[1]) {
buf_ix = 1;
}
switch (err) { /* Chk errors. */
case USBD_ERR_NONE:
p_ctrl->RxErrCnt = 0u;
buf_ix = 1u - buf_ix;
break;
case USBD_ERR_EP_ABORT:
p_ctrl->RxBuf[buf_ix] = p_buf;
/* Detect disconnection or reset. */
if (USBD_CDC_IsConn(p_ctrl->Nbr) != DEF_YES) {
netifapi_netif_set_link_down(&p_ctrl->net_drv->d_netif);
ncm_log("Disconnected, Eth down");
USBD_NCM_StateLock(p_ctrl);
p_ctrl->State = USBD_NCM_STATE_INIT;
p_ctrl->MaxInSize = NTB_DEFAULT_IN_SIZE;
p_ctrl->Sequence = 0;
USBD_NCM_StateUnlock(p_ctrl);
}
return;
default:
ncm_err("Unexpected Rx Cmpl err %d", err);
p_ctrl->RxBuf[buf_ix] = p_buf;
p_ctrl->RxErrCnt ++; /* Retry a few times. */
if (p_ctrl->RxErrCnt > USBD_CDC_NCM_MAX_RETRY_CNT) {
return;
}
break;
}
if (p_ctrl->State == USBD_NCM_STATE_CFG) {
USBD_CDC_DataRxAsync( p_ctrl->Nbr,
0u,
p_ctrl->RxBuf[buf_ix],
NTB_OUT_SIZE,
USBD_NCM_RxCmpl,
(void *)p_ctrl,
&err_usbd);
if (err_usbd != USBD_ERR_NONE) {
ncm_err("Unexpected Rx start err %d", err_usbd);
} else {
p_ctrl->RxBuf[buf_ix] = NULL;
}
}
if (err == USBD_ERR_NONE) {
USBD_NCM_RxHandle(p_ctrl, p_buf, xfer_len);
if (!p_ctrl->RxBuf[0]) {
p_ctrl->RxBuf[0] = p_buf;
} else if (!p_ctrl->RxBuf[1]) {
p_ctrl->RxBuf[1] = p_buf;
}
}
return;
}
/*
*********************************************************************************************************
* USBD_NCM_CommStart()
*
* Description : Starts communication on given class instance.
*
* Argument(s) : p_ctrl Pointer to NCM 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 NCM must be locked by caller function.
*********************************************************************************************************
*/
static void USBD_NCM_CommStart (USBD_NCM_CTRL *p_ctrl,
USBD_ERR *p_err)
{
USBD_NCM_NTH16 *nth;
ASSERT(p_ctrl->TxBuf && p_ctrl->TxBufNext);
ASSERT(p_ctrl->RxBuf[0] && p_ctrl->RxBuf[1]);
nth = (USBD_NCM_NTH16 *)p_ctrl->TxBuf;
nth->wBlockLength = 0;
nth->wNdpIndex = 0;
p_ctrl->TxBufPos = sizeof(*nth);
p_ctrl->TxDpePos = 0;
p_ctrl->RxErrCnt = 0u;
USBD_CDC_DataRxAsync( p_ctrl->Nbr,
0u,
p_ctrl->RxBuf[0],
NTB_OUT_SIZE,
USBD_NCM_RxCmpl,
(void *)p_ctrl,
p_err);
if (*p_err != USBD_ERR_NONE) {
ncm_err("Unexpected Comm start err %d", *p_err);
} else {
p_ctrl->RxBuf[0] = NULL;
}
return;
}