1678 lines
61 KiB
C
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;
|
|
}
|
|
|