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