From 4e79741e7db15b74b9df78219b8c9e3927689410 Mon Sep 17 00:00:00 2001 From: zhanghongyu Date: Mon, 18 Mar 2024 20:06:54 +0800 Subject: [PATCH] usbdev: add ncm driver The compilation and verification commands are shown below: ./tools/configure.sh sim:usbdev make -j sudo ./nuttx nsh> conn 2 nsh> dhcpd_start eth1 Signed-off-by: zhanghongyu --- arch/sim/src/sim/posix/sim_rawgadget.c | 6 +- boards/sim/sim/sim/configs/usbdev/defconfig | 34 +- boards/sim/sim/sim/src/sim_composite.c | 54 +- drivers/usbdev/CMakeLists.txt | 4 + drivers/usbdev/Kconfig | 165 ++ drivers/usbdev/Make.defs | 4 + drivers/usbdev/cdcecm.h | 3 + drivers/usbdev/cdcncm.c | 2733 +++++++++++++++++++ drivers/usbhost/usbhost_cdcmbim.c | 2 +- include/nuttx/usb/cdc.h | 41 +- include/nuttx/usb/cdcncm.h | 105 + include/nuttx/usb/usb.h | 33 +- 12 files changed, 3172 insertions(+), 12 deletions(-) create mode 100644 drivers/usbdev/cdcncm.c create mode 100644 include/nuttx/usb/cdcncm.h diff --git a/arch/sim/src/sim/posix/sim_rawgadget.c b/arch/sim/src/sim/posix/sim_rawgadget.c index 6baf308a88..e21f69ba3f 100644 --- a/arch/sim/src/sim/posix/sim_rawgadget.c +++ b/arch/sim/src/sim/posix/sim_rawgadget.c @@ -79,7 +79,11 @@ #define UDC_NAME_LENGTH_MAX 128 #define USB_RAW_EP0_MAX_LEN 256 -#define USB_RAW_EP_MAX_LEN 1024 +#ifdef CONFIG_NET_CDCNCM +# define USB_RAW_EP_MAX_LEN 16384 +#else +# define USB_RAW_EP_MAX_LEN 1024 +#endif #define USB_RAW_RX_BUF_NUM 8 diff --git a/boards/sim/sim/sim/configs/usbdev/defconfig b/boards/sim/sim/sim/configs/usbdev/defconfig index 2cd27f015a..28174f28b0 100644 --- a/boards/sim/sim/sim/configs/usbdev/defconfig +++ b/boards/sim/sim/sim/configs/usbdev/defconfig @@ -7,7 +7,6 @@ # # CONFIG_SIM_UART_DMA is not set CONFIG_ADBD_FILE_SERVICE=y -CONFIG_ADBD_SHELL_SERVICE=y CONFIG_ADBD_USB_BOARDCTL=y CONFIG_ADBD_USB_SERVER=y CONFIG_ARCH="sim" @@ -21,6 +20,7 @@ CONFIG_BUILTIN=y CONFIG_CDCACM=y CONFIG_CDCACM_COMPOSITE=y CONFIG_CDCECM_COMPOSITE=y +CONFIG_CDCNCM_COMPOSITE=y CONFIG_COMPOSITE_IAD=y CONFIG_COMPOSITE_PRODUCTID=0x0042 CONFIG_COMPOSITE_VENDORID=0x1630 @@ -35,25 +35,57 @@ CONFIG_DEBUG_USB_WARN=y CONFIG_EXAMPLES_DHCPD=y CONFIG_FS_PROCFS=y CONFIG_FS_TMPFS=y +CONFIG_HAVE_CXX=y +CONFIG_ICMPv6_AUTOCONF_RDNSS=y CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_IOB_NBUFFERS=512 +CONFIG_IOB_NCHAINS=36 CONFIG_LIBC_DLFCN=y CONFIG_LIBC_EXECFUNCS=y CONFIG_LIBUV=y CONFIG_NETDB_DNSCLIENT=y CONFIG_NETDB_DNSCLIENT_MAXRESPONSE=1024 CONFIG_NETDB_DNSSERVER_IPv4ADDR=0xDF050505 +CONFIG_NETDB_MAX_IPADDR=1 +CONFIG_NETDEV_PHY_IOCTL=y +CONFIG_NETDOWN_NOTIFIER=y +CONFIG_NETINIT_DRIPADDR=0x0a000101 +CONFIG_NETINIT_IPADDR=0x0a000102 CONFIG_NETUTILS_DHCPD=y CONFIG_NETUTILS_IPERF=y CONFIG_NETUTILS_TELNETC=y CONFIG_NETUTILS_TELNETD=y +CONFIG_NET_BINDTODEVICE=y CONFIG_NET_BROADCAST=y CONFIG_NET_CDCECM=y +CONFIG_NET_CDCNCM=y CONFIG_NET_ETH_PKTSIZE=1518 CONFIG_NET_ICMP_SOCKET=y +CONFIG_NET_ICMPv6=y +CONFIG_NET_ICMPv6_ALLOC_CONNS=1 +CONFIG_NET_ICMPv6_AUTOCONF=y +CONFIG_NET_ICMPv6_NEIGHBOR=y +CONFIG_NET_ICMPv6_ROUTER=y +CONFIG_NET_ICMPv6_SOCKET=y CONFIG_NET_IPFORWARD=y +CONFIG_NET_IPFORWARD_NSTRUCT=100 +CONFIG_NET_IPv6=y +CONFIG_NET_MAX_RECV_BUFSIZE=65535 +CONFIG_NET_MAX_SEND_BUFSIZE=65535 CONFIG_NET_NAT=y +CONFIG_NET_RECV_BUFSIZE=16384 +CONFIG_NET_SEND_BUFSIZE=16384 CONFIG_NET_TCP=y +CONFIG_NET_TCPBACKLOG=y +CONFIG_NET_TCP_ALLOC_CONNS=1 +CONFIG_NET_TCP_DELAYED_ACK=y +CONFIG_NET_TCP_KEEPALIVE=y +CONFIG_NET_TCP_NOTIFIER=y +CONFIG_NET_TCP_SELECTIVE_ACK=y +CONFIG_NET_TCP_WRITE_BUFFERS=y CONFIG_NET_UDP=y +CONFIG_NET_UDP_NOTIFIER=y +CONFIG_NET_UDP_WRITE_BUFFERS=y CONFIG_NSH_ARCHINIT=y CONFIG_NSH_BUILTIN_APPS=y CONFIG_NSH_READLINE=y diff --git a/boards/sim/sim/sim/src/sim_composite.c b/boards/sim/sim/sim/src/sim_composite.c index de32560f4b..8eb8868775 100644 --- a/boards/sim/sim/sim/src/sim_composite.c +++ b/boards/sim/sim/sim/src/sim_composite.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #if defined(CONFIG_BOARDCTL_USBDEVCTRL) && defined(CONFIG_USBDEV_COMPOSITE) @@ -210,6 +211,53 @@ static void *board_composite1_connect(int port) return composite_initialize(composite_getdevdescs(), dev, dev_idx); } +/**************************************************************************** + * Name: board_composite2_connect + * + * Description: + * Connect the USB composite device on the specified USB device port for + * configuration 2. + * + * Input Parameters: + * port - The USB device port. + * + * Returned Value: + * A non-NULL handle value is returned on success. NULL is returned on + * any failure. + * + ****************************************************************************/ + +static void *board_composite2_connect(int port) +{ + struct composite_devdesc_s dev[1]; + int dev_idx = 0; + +#ifdef CONFIG_NET_CDCNCM + /* Configure the CDC/NCM device */ + + cdcncm_get_composite_devdesc(&dev[dev_idx]); + + /* Interfaces */ + + dev[dev_idx].devinfo.ifnobase = 0; + dev[dev_idx].minor = 0; + + /* Strings */ + + dev[dev_idx].devinfo.strbase = COMPOSITE_NSTRIDS - 1; + + /* Endpoints */ + + dev[dev_idx].devinfo.epno[CDCNCM_EP_INTIN_IDX] = 5; + dev[dev_idx].devinfo.epno[CDCNCM_EP_BULKIN_IDX] = 6; + dev[dev_idx].devinfo.epno[CDCNCM_EP_BULKOUT_IDX] = 7; + + dev_idx += 1; +#endif + + return composite_initialize(composite_getdevdescs(), dev, dev_idx); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -251,10 +299,14 @@ void *board_composite_connect(int port, int configid) { return board_composite0_connect(port); } - else + else if (configid == 1) { return board_composite1_connect(port); } + else + { + return board_composite2_connect(port); + } } #endif /* CONFIG_BOARDCTL_USBDEVCTRL && CONFIG_USBDEV_COMPOSITE */ diff --git a/drivers/usbdev/CMakeLists.txt b/drivers/usbdev/CMakeLists.txt index ebe6311d56..ac6fc2ea9a 100644 --- a/drivers/usbdev/CMakeLists.txt +++ b/drivers/usbdev/CMakeLists.txt @@ -59,6 +59,10 @@ if(CONFIG_USBDEV) list(APPEND SRCS cdcecm.c) endif() + if(CONFIG_NET_CDCNCM) + list(APPEND SRCS cdcncm.c) + endif() + if(CONFIG_USBDEV_FS) list(APPEND SRCS usbdev_fs.c) endif() diff --git a/drivers/usbdev/Kconfig b/drivers/usbdev/Kconfig index bce9808b25..9fab49cd51 100644 --- a/drivers/usbdev/Kconfig +++ b/drivers/usbdev/Kconfig @@ -1226,6 +1226,171 @@ config CDCECM_PRODUCTSTR endif # !CDCECM_COMPOSITE endif # CDCECM + +menuconfig NET_CDCNCM + bool "CDC-NCM Ethernet-over-USB" + default n + depends on SCHED_WORKQUEUE + depends on NETDEVICES + depends on NET + depends on NET_ETHERNET + ---help--- + References: + - "Universal Serial Bus - Communications Class - Subclass + Specification for Ethernet Control Model Devices, + Revision 1.2, February 9, 2007" + + This option may require CONFIG_NETDEV_LATEINIT=y, otherwise the + power-up initialization may call the non-existent xxx_netinitialize(). + This option is not automatically selected because it may be that + you have an additional network device that requires the early + xxx_netinitialize() call. + +if NET_CDCNCM + +menuconfig CDCNCM_COMPOSITE + bool "CDC/NCM composite support" + default n + depends on USBDEV_COMPOSITE + ---help--- + Configure the CDC Ethernet Control Model driver as part of a + composite driver (only if USBDEV_COMPOSITE is also defined) + +if !CDCNCM_COMPOSITE + +# In a composite device the EP0 config comes from the composite device +# and the EP-Number is configured dynamically via composite_initialize + +config CDCNCM_EP0MAXPACKET + int "Endpoint 0 max packet size" + default 64 + ---help--- + Endpoint 0 max packet size. Default 64. + +config CDCNCM_EPINTIN + int "Interrupt IN endpoint number" + default 1 + ---help--- + The logical 7-bit address of a hardware endpoint that supports + interrupt IN operation. Default 1. + +endif # !CDCNCM_COMPOSITE + +config CDCNCM_EPINTIN_FSSIZE + int "Interrupt IN full speed MAXPACKET size" + default 16 + ---help--- + Max package size for the interrupt IN endpoint if full speed mode. + Default 16. + +if USBDEV_DUALSPEED + +config CDCNCM_EPINTIN_HSSIZE + int "Interrupt IN high speed MAXPACKET size" + default 64 + ---help--- + Max package size for the interrupt IN endpoint if high speed mode. + Default 64. + +endif # USBDEV_DUALSPEED + +if !CDCNCM_COMPOSITE + +# In a composite device the EP-Number is configured dynamically via +# composite_initialize + +config CDCNCM_EPBULKOUT + int "Bulk OUT endpoint number" + default 5 + ---help--- + The logical 7-bit address of a hardware endpoint that supports + bulk OUT operation. Default: 5 + +endif # !CDCNCM_COMPOSITE + +config CDCNCM_EPBULKOUT_FSSIZE + int "Bulk OUT full speed MAXPACKET size" + default 64 + ---help--- + Max package size for the bulk OUT endpoint if full speed mode. + Default 64. + +if USBDEV_DUALSPEED + +config CDCNCM_EPBULKOUT_HSSIZE + int "Bulk OUT out high speed MAXPACKET size" + default 512 + ---help--- + Max package size for the bulk OUT endpoint if high speed mode. + Default 512. + +endif # USBDEV_DUALSPEED + +if !CDCNCM_COMPOSITE + +# In a composite device the EP-Number is configured dynamically via +# composite_initialize + +config CDCNCM_EPBULKIN + int "Bulk IN endpoint number" + default 2 + ---help--- + The logical 7-bit address of a hardware endpoint that supports + bulk IN operation. Default: 2 + +endif # !CDCNCM_COMPOSITE + +config CDCNCM_EPBULKIN_FSSIZE + int "Bulk IN full speed MAXPACKET size" + default 64 + ---help--- + Max package size for the bulk IN endpoint if full speed mode. + Default 64. + +if USBDEV_DUALSPEED + +config CDCNCM_EPBULKIN_HSSIZE + int "Bulk IN high speed MAXPACKET size" + default 512 + ---help--- + Max package size for the bulk IN endpoint if high speed mode. + Default 512. + +endif # USBDEV_DUALSPEED + +if !CDCNCM_COMPOSITE + +# In a composite device the Vendor- and Product-ID is given by the composite +# device + +config CDCNCM_VENDORID + hex "Vendor ID" + default 0x0525 + ---help--- + The vendor ID code/string. Default 0x0525 and "NuttX" + 0x0525 is the Netchip vendor and should not be used in any + products. This default VID was selected for compatibility with + the Linux CDC NCM default VID. + +config CDCNCM_PRODUCTID + hex "Product ID" + default 0xa4a2 + ---help--- + The product ID code/string. Default 0xa4a2 and "CDC/NCM Ethernet" + 0xa4a2 was selected for compatibility with the Linux CDC NCM + default PID. + +config CDCNCM_VENDORSTR + string "Vendor string" + default "NuttX" + +config CDCNCM_PRODUCTSTR + string "Product string" + default "CDC/NCM Ethernet" + +endif # !CDCNCM_COMPOSITE +endif # CDCNCM + config USBDEV_FS bool default n diff --git a/drivers/usbdev/Make.defs b/drivers/usbdev/Make.defs index 7108c5d4ea..647a83792c 100644 --- a/drivers/usbdev/Make.defs +++ b/drivers/usbdev/Make.defs @@ -62,6 +62,10 @@ ifeq ($(CONFIG_NET_CDCECM),y) CSRCS += cdcecm.c endif +ifeq ($(CONFIG_NET_CDCNCM),y) + CSRCS += cdcncm.c +endif + ifeq ($(CONFIG_USBDEV_FS),y) CSRCS += usbdev_fs.c endif diff --git a/drivers/usbdev/cdcecm.h b/drivers/usbdev/cdcecm.h index 8888626a71..6795269ef3 100644 --- a/drivers/usbdev/cdcecm.h +++ b/drivers/usbdev/cdcecm.h @@ -58,4 +58,7 @@ #define CDCECM_SELFPOWERED (0) #define CDCECM_REMOTEWAKEUP (0) +#define CDCECM_LOW_BITRATE (19 * 64 * 1 * 1000 * 8) +#define CDCECM_HIGH_BITRATE (13 * 512 * 8 * 1000 * 8) + #endif /* __DRIVERS_USBDEV_CDCECM_H */ diff --git a/drivers/usbdev/cdcncm.c b/drivers/usbdev/cdcncm.c new file mode 100644 index 0000000000..038d12760d --- /dev/null +++ b/drivers/usbdev/cdcncm.c @@ -0,0 +1,2733 @@ +/**************************************************************************** + * drivers/usbdev/cdcncm.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* References: + * [CDCNCM1.2] Universal Serial Bus - Communications Class - Subclass + * Specification for Ethernet Control Model Devices - Rev 1.2 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_PKT +# include +#endif + +#ifdef CONFIG_BOARD_USBDEV_SERIALSTR +# include +#endif + +#include "cdcecm.h" + +#ifdef CONFIG_NET_CDCNCM + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The low priority work queue is preferred. If it is not enabled, LPWORK + * will be the same as HPWORK. NOTE: Use of the high priority work queue will + * have a negative impact on interrupt handling latency and overall system + * performance. This should be avoided. + */ + +#define ETHWORK LPWORK + +/* CONFIG_CDCECM_NINTERFACES determines the number of physical interfaces + * that will be supported. + */ + +#ifndef CONFIG_CDCECM_NINTERFACES +# define CONFIG_CDCECM_NINTERFACES 1 +#endif + +/* TX timeout = 1 minute */ + +#define CDCNCM_TXTIMEOUT (60*CLK_TCK) +#define CDCNCM_DGRAM_COMBINE_PERIOD 1 + +/* This is a helper pointer for accessing the contents of Ethernet header */ + +#define BUF ((FAR struct eth_hdr_s *)self->dev.d_buf) + +#define NTB_DEFAULT_IN_SIZE 16384 +#define NTB_OUT_SIZE 16384 +#define TX_MAX_NUM_DPE 32 + +/* NCM Transfer Block Parameter Structure */ + +#define CDC_NCM_NTB16_SUPPORTED (1 << 0) +#define CDC_NCM_NTB32_SUPPORTED (1 << 1) + +#define FORMATS_SUPPORTED (CDC_NCM_NTB16_SUPPORTED | \ + CDC_NCM_NTB32_SUPPORTED) + +#define CDC_NCM_NTH16_SIGN 0x484D434E /* NCMH */ +#define CDC_NCM_NTH32_SIGN 0x686D636E /* ncmh */ +#define CDC_NCM_NDP16_NOCRC_SIGN 0x304D434E /* NCM0 */ +#define CDC_NCM_NDP32_NOCRC_SIGN 0x306D636E /* ncm0 */ + +#define INIT_NDP16_OPTS { \ + .nthsign = CDC_NCM_NTH16_SIGN, \ + .ndpsign = CDC_NCM_NDP16_NOCRC_SIGN, \ + .nthsize = sizeof(struct cdc_ncm_nth16_s), \ + .ndpsize = sizeof(struct usb_cdc_ncm_ndp16_s), \ + .dpesize = sizeof(struct usb_cdc_ncm_dpe16_s), \ + .ndpalign = 4, \ + .dgramitemlen = 2, \ + .blocklen = 2, \ + .ndpindex = 2, \ + .reserved1 = 0, \ + .reserved2 = 0, \ + .nextndpindex = 2, \ + } + +#define INIT_NDP32_OPTS { \ + .nthsign = CDC_NCM_NTH32_SIGN, \ + .ndpsign = CDC_NCM_NDP32_NOCRC_SIGN, \ + .nthsize = sizeof(struct cdc_ncm_nth32_s), \ + .ndpsize = sizeof(struct usb_cdc_ncm_ndp32_s), \ + .dpesize = sizeof(struct usb_cdc_ncm_dpe32_s), \ + .ndpalign = 8, \ + .dgramitemlen = 4, \ + .blocklen = 4, \ + .ndpindex = 4, \ + .reserved1 = 2, \ + .reserved2 = 4, \ + .nextndpindex = 4, \ + } + +#define CDC_NCM_NCAP_ETH_FILTER (1 << 0) +#define NCAPS (CDC_NCM_NCAP_ETH_FILTER) + +#define NCM_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) +#define NCM_ALIGN(x, a) NCM_ALIGN_MASK((x), ((typeof(x))(a) - 1)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum ncm_notify_state_e +{ + NCM_NOTIFY_NONE, /* Don't notify */ + NCM_NOTIFY_CONNECT, /* Issue CONNECT next */ + NCM_NOTIFY_SPEED, /* Issue SPEED_CHANGE next */ +}; + +struct ndp_parser_opts_s +{ + uint32_t nthsign; /* NCM Transfer Header signature */ + uint32_t ndpsign; /* NCM Datagram Pointer signature */ + uint_fast8_t nthsize; /* The length of NTH */ + uint_fast8_t ndpsize; /* The length of NDP */ + uint_fast8_t dpesize; /* The length of NDP Entry */ + uint_fast8_t ndpalign; /* NDP alignment length */ + uint_fast8_t dgramitemlen; /* The length of index or length */ + uint_fast8_t blocklen; /* The length of current NTB */ + uint_fast8_t ndpindex; /* The offset of first NDP in current NTB */ + uint_fast8_t reserved1; /* Reserved1 */ + uint_fast8_t reserved2; /* Reserved2 */ + uint_fast8_t nextndpindex; /* The offset of next NDP in current NTB */ +}; + +/* NTH: NCM Transfer Header + * NDP: NCM Datagram Pointer + * DPE: NCM Datagram Pointer Entry + * +------------+ or +------------+ + * | NTH | | NTH | + * +------------+ +------------+ + * | NDP | | Datagrams | + * +------------+ +------------+ + * | Datagrams | | NDP | + * +------------+ +------------+ + * + * The layout of the NTB(NCM Transfer Block) structure in the NuttX system + * is as follows: + * +--------------------------+ + * |NTH : nth sign | + * | nth len | + * | sequence | + * | total len | + * | ndp index |----+ + * +--------------------------+ | + * |NDP: ndp sign |<---+ + * | ndp len | + * | next ndp index| + * | Datagram index|----+ + * | Datagram len | | + * | Datagram index|----|--+ + * | Datagram len | | | + * | Datagram index|----|--|--+ + * | Datagram len | | | | + * | 0 | Need to end with two zeros + * | 0 | Need to end with two zeros + * | ... [32] | | | | + * +--------------------------+ | | | + * |Datagrams: Datagram1 |<---+ | | + * | pad | | | + * | Datagram2 |<------+ | + * | pad | | + * | Datagram3 |<---------+ + * +--------------------------+ + */ + +begin_packed_struct struct cdc_ncm_nth16_s +{ + uint32_t sign; + uint16_t headerlen; + uint16_t seq; + uint16_t blocklen; + uint16_t ndpindex; +} end_packed_struct; + +begin_packed_struct struct cdc_ncm_nth32_s +{ + uint32_t sign; + uint16_t headerlen; + uint16_t seq; + uint32_t blocklen; + uint32_t ndpindex; +} end_packed_struct; + +/* 16-bit NCM Datagram Pointer Entry */ + +begin_packed_struct struct usb_cdc_ncm_dpe16_s +{ + uint16_t index; + uint16_t len; +} end_packed_struct; + +/* 16-bit NCM Datagram Pointer Table */ + +begin_packed_struct struct usb_cdc_ncm_ndp16_s +{ + uint32_t sign; + uint16_t len; + uint16_t nextndpindex; + + /* struct usb_cdc_ncm_dpe16_s dpe16[]; */ +} end_packed_struct; + +/* 32-bit NCM Datagram Pointer Entry */ + +begin_packed_struct struct usb_cdc_ncm_dpe32_s +{ + uint32_t index; + uint32_t len; +} end_packed_struct; + +/* 32-bit NCM Datagram Pointer Table */ + +begin_packed_struct struct usb_cdc_ncm_ndp32_s +{ + uint32_t sign; + uint16_t len; + uint16_t reserved1; + uint32_t nextndpindex; + uint32_t reserved2; + + /* struct usb_cdc_ncm_dpe32_s dpe32[]; */ +} end_packed_struct; + +begin_packed_struct struct usb_cdc_ncm_ntb_parameters_s +{ + uint16_t len; + uint16_t ntbsupported; + uint32_t ntbinmaxsize; + uint16_t ndpindivisor; + uint16_t ndpinpayloadremainder; + uint16_t ndpinalignment; + uint16_t padding; + uint32_t ntboutmaxsize; + uint16_t ndpoutdivisor; + uint16_t ndpoutpayloadremainder; + uint16_t ndpoutalignment; + uint16_t ntboutmaxdatagrams; +} end_packed_struct; + +/* The cdcncm_driver_s encapsulates all state information for a single + * hardware interface + */ + +struct cdcncm_driver_s +{ + /* USB CDC-ECM device */ + + struct usbdevclass_driver_s usbdev; /* USB device class vtable */ + struct usbdev_devinfo_s devinfo; + FAR struct usbdev_req_s *ctrlreq; /* Allocated control request */ + FAR struct usbdev_req_s *notifyreq; /* Allocated norify request */ + FAR struct usbdev_ep_s *epint; /* Interrupt IN endpoint */ + FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint */ + FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint */ + uint8_t config; /* Selected configuration number */ + + uint16_t pktbuf[(CONFIG_NET_ETH_PKTSIZE + + CONFIG_NET_GUARDSIZE + 1) / 2]; + + FAR struct usbdev_req_s *rdreq; /* Single read request */ + bool rxpending; /* Packet available in rdreq */ + + FAR struct usbdev_req_s *wrreq; /* Single write request */ + sem_t wrreq_idle; /* Is the wrreq available? */ + bool txdone; /* Did a write request complete? */ + enum ncm_notify_state_e notify; /* State of notify */ + FAR const struct ndp_parser_opts_s + *parseropts; /* Options currently used to parse NTB */ + uint32_t ndpsign; /* NDP signature */ + int dgramcount; /* The current tx cache dgram count */ + FAR uint8_t *dgramaddr; /* The next tx cache dgram address */ + + /* Network device */ + + bool bifup; /* true:ifup false:ifdown */ + struct work_s irqwork; /* For deferring interrupt work + * to the work queue */ + struct work_s notifywork; /* For deferring notify work + * to the work queue */ + struct work_s pollwork; /* For deferring poll work to + * the work queue */ + struct work_s delaywork; /* For deferring tx work + * to the work queue */ + + /* This holds the information visible to the NuttX network */ + + struct net_driver_s dev; /* Interface understood by the + * network */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Network Device ***********************************************************/ + +/* Common TX logic */ + +static int cdcncm_transmit(FAR struct cdcncm_driver_s *priv); +static int cdcncm_txpoll(FAR struct net_driver_s *dev); + +/* Interrupt handling */ + +static void cdcncm_reply(FAR struct cdcncm_driver_s *priv); +static void cdcncm_receive(FAR struct cdcncm_driver_s *priv); +static void cdcncm_txdone(FAR struct cdcncm_driver_s *priv); + +static void cdcncm_interrupt_work(FAR void *arg); + +/* NuttX callback functions */ + +static int cdcncm_ifup(FAR struct net_driver_s *dev); +static int cdcncm_ifdown(FAR struct net_driver_s *dev); + +static void cdcncm_txavail_work(FAR void *arg); +static int cdcncm_txavail(FAR struct net_driver_s *dev); + +#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) +static int cdcncm_addmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac); +#ifdef CONFIG_NET_MCASTGROUP +static int cdcncm_rmmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac); +#endif +#endif +#ifdef CONFIG_NETDEV_IOCTL +static int cdcncm_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif + +/* USB Device Class Driver **************************************************/ + +/* USB Device Class methods */ + +static int cdcncm_bind(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev); + +static void cdcncm_unbind(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev); + +static int cdcncm_setup(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev, + FAR const struct usb_ctrlreq_s *ctrl, + FAR uint8_t *dataout, size_t outlen); + +static void cdcncm_disconnect(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev); + +/* USB Device Class helpers */ + +static void cdcncm_ep0incomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); +static void cdcncm_rdcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); +static void cdcncm_wrcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); + +static void cdcncm_mkepdesc(int epidx, FAR struct usb_epdesc_s *epdesc, + FAR struct usbdev_devinfo_s *devinfo, + bool hispeed); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* USB Device Class Methods */ + +static const struct usbdevclass_driverops_s g_usbdevops = +{ + cdcncm_bind, + cdcncm_unbind, + cdcncm_setup, + cdcncm_disconnect, + NULL, + NULL +}; + +#ifndef CONFIG_CDCNCM_COMPOSITE +static const struct usb_devdesc_s g_devdesc = +{ + USB_SIZEOF_DEVDESC, + USB_DESC_TYPE_DEVICE, + { + LSBYTE(0x0200), + MSBYTE(0x0200) + }, + USB_CLASS_CDC, + CDC_SUBCLASS_NCM, + CDC_PROTO_NONE, + CONFIG_CDCNCM_EP0MAXPACKET, + { + LSBYTE(CONFIG_CDCNCM_VENDORID), + MSBYTE(CONFIG_CDCNCM_VENDORID) + }, + { + LSBYTE(CONFIG_CDCNCM_PRODUCTID), + MSBYTE(CONFIG_CDCNCM_PRODUCTID) + }, + { + LSBYTE(CDCECM_VERSIONNO), + MSBYTE(CDCECM_VERSIONNO) + }, + CDCNCM_MANUFACTURERSTRID, + CDCNCM_PRODUCTSTRID, + CDCNCM_SERIALSTRID, + CDCECM_NCONFIGS +}; +#endif + +static const struct ndp_parser_opts_s g_ndp16_opts = INIT_NDP16_OPTS; +static const struct ndp_parser_opts_s g_ndp32_opts = INIT_NDP32_OPTS; + +static const struct usb_cdc_ncm_ntb_parameters_s g_ntbparameters = +{ + .len = sizeof(g_ntbparameters), + .ntbsupported = FORMATS_SUPPORTED, + .ntbinmaxsize = NTB_DEFAULT_IN_SIZE, + .ndpindivisor = 4, + .ndpinpayloadremainder = 0, + .ndpinalignment = 4, + + .ntboutmaxsize = NTB_OUT_SIZE, + .ndpoutdivisor = 4, + .ndpoutpayloadremainder = 0, + .ndpoutalignment = 4, +}; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcncm_get + * + * Description: + * Read size length data from address and increases the address by the + * corresponding size + * + * Input Parameters: + * address - Pointer to address + * size - Size of data + * + * Returned Value: + * The read value + * + ****************************************************************************/ + +static inline uint32_t cdcncm_get(FAR uint8_t **address, size_t size) +{ + uint32_t value; + + switch (size) + { + case 2: + value = GETUINT16(*address); + break; + case 4: + value = GETUINT32(*address); + break; + default: + nerr("Wrong size cdcncm_get %zu\n", size); + } + + *address += size; + return value; +} + +/**************************************************************************** + * Name: cdcncm_put + * + * Description: + * Write size length data to address and increases the address by the + * corresponding size + * + * Input Parameters: + * address - Pointer to address + * size - Size of data + * value - Value of data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline +void cdcncm_put(FAR uint8_t **address, size_t size, uint32_t value) +{ + switch (size) + { + case 2: + PUTUINT16(*address, value); + break; + case 4: + PUTUINT32(*address, value); + break; + default: + uerr("Wrong cdcncm_put\n"); + } + + *address += size; +} + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcncm_transmit_format + * + * Description: + * Format the data to be sent + * + * Input Parameters: + * self - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void cdcncm_transmit_format(FAR struct cdcncm_driver_s *self) +{ + FAR const struct ndp_parser_opts_s *opts = self->parseropts; + const int div = g_ntbparameters.ndpindivisor; + const int rem = g_ntbparameters.ndpinpayloadremainder; + const int dgramidxlen = 2 * opts->dgramitemlen; + const int ndpalign = g_ntbparameters.ndpinalignment; + FAR uint8_t *tmp; + int ncblen; + int ndpindex; + + ncblen = opts->nthsize; + ndpindex = NCM_ALIGN(ncblen, ndpalign); + + if (self->dgramcount == 0) + { + /* Fill NCB */ + + tmp = self->wrreq->buf; + memset(tmp, 0, ncblen); + cdcncm_put(&tmp, 4, opts->nthsign); + cdcncm_put(&tmp, 2, opts->nthsize); + tmp += 2; /* Skip seq */ + tmp += opts->blocklen; /* Skip block len */ + cdcncm_put(&tmp, opts->ndpindex, ndpindex); + self->dgramaddr = self->wrreq->buf + ndpindex + + opts->ndpsize + (TX_MAX_NUM_DPE + 1) * dgramidxlen; + self->dgramaddr = (FAR uint8_t *)NCM_ALIGN((uintptr_t)self->dgramaddr, + div) + rem; + + /* Fill NDP */ + + tmp = self->wrreq->buf + ndpindex; + cdcncm_put(&tmp, 4, self->ndpsign); + tmp += 2 + opts->reserved1; + cdcncm_put(&tmp, opts->nextndpindex, 0); + } + + tmp = self->wrreq->buf + ndpindex + opts->ndpsize + + self->dgramcount * dgramidxlen; + cdcncm_put(&tmp, opts->dgramitemlen, self->dgramaddr - self->wrreq->buf); + cdcncm_put(&tmp, opts->dgramitemlen, self->dev.d_len); + + /* Fill IP packet: address=self->dev.d_buf, length=self->dev.d_len */ + + memcpy(self->dgramaddr, self->dev.d_buf, self->dev.d_len); + self->dgramaddr += self->dev.d_len; + self->dgramaddr = (FAR uint8_t *)NCM_ALIGN((uintptr_t)self->dgramaddr, + div) + rem; + + self->dgramcount++; +} + +/**************************************************************************** + * Name: cdcncm_transmit_work + * + * Description: + * Send NTB to the USB device for ethernet frame transmission + * + * Input Parameters: + * arg - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void cdcncm_transmit_work(FAR void *arg) +{ + FAR struct cdcncm_driver_s *self = arg; + FAR const struct ndp_parser_opts_s *opts = self->parseropts; + FAR uint8_t *tmp; + const int dgramidxlen = 2 * opts->dgramitemlen; + const int ndpalign = g_ntbparameters.ndpinalignment; + int ncblen; + int ndpindex; + int totallen; + + /* Wait until the USB device request for Ethernet frame transmissions + * becomes available. + */ + + while (nxsem_wait(&self->wrreq_idle) != OK) + { + } + + net_lock(); + + ncblen = opts->nthsize; + ndpindex = NCM_ALIGN(ncblen, ndpalign); + + /* Fill NCB */ + + tmp = self->wrreq->buf + 8; /* Offset to block length */ + totallen = self->dgramaddr - self->wrreq->buf; + cdcncm_put(&tmp, opts->blocklen, totallen); + + /* Fill NDP */ + + tmp = self->wrreq->buf + ndpindex + 4; /* Offset to ndp length */ + cdcncm_put(&tmp, 2, opts->ndpsize + (self->dgramcount + 1) * dgramidxlen); + + tmp += opts->reserved1 + opts->nextndpindex + opts->reserved2 + + self->dgramcount * dgramidxlen; + self->dgramcount = 0; + + cdcncm_put(&tmp, opts->dgramitemlen, 0); + cdcncm_put(&tmp, opts->dgramitemlen, 0); + + self->wrreq->len = totallen; + + EP_SUBMIT(self->epbulkin, self->wrreq); + + net_unlock(); +} + +/**************************************************************************** + * Name: cdcncm_transmit + * + * Description: + * Start hardware transmission. Called either from the txdone interrupt + * handling or from watchdog based polling + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int cdcncm_transmit(FAR struct cdcncm_driver_s *self) +{ + /* Increment statistics */ + + NETDEV_TXPACKETS(self->dev); + + cdcncm_transmit_format(self); + + if ((self->wrreq->buf + NTB_OUT_SIZE - self->dgramaddr < + self->dev.d_pktsize) || self->dgramcount >= TX_MAX_NUM_DPE) + { + work_cancel(ETHWORK, &self->delaywork); + cdcncm_transmit_work(self); + } + else + { + work_queue(ETHWORK, &self->delaywork, cdcncm_transmit_work, self, + MSEC2TICK(CDCNCM_DGRAM_COMBINE_PERIOD)); + } + + return OK; +} + +/**************************************************************************** + * Name: cdcncm_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send times out and the interface is + * reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int cdcncm_txpoll(FAR struct net_driver_s *dev) +{ + FAR struct cdcncm_driver_s *priv = + (FAR struct cdcncm_driver_s *)dev->d_private; + + /* Send the packet */ + + cdcncm_transmit(priv); + + /* Check if there is room in the device to hold another packet. If + * not, return a non-zero value to terminate the poll. + */ + + return 1; +} + +/**************************************************************************** + * Name: cdcncm_reply + * + * Description: + * After a packet has been received and dispatched to the network, it + * may return return with an outgoing packet. This function checks for + * that case and performs the transmission if necessary. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void cdcncm_reply(FAR struct cdcncm_driver_s *priv) +{ + /* If the packet dispatch resulted in data that should be sent out on the + * network, the field d_len will set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* And send the packet */ + + cdcncm_transmit(priv); + } +} + +/**************************************************************************** + * Name: cdcncm_packet_handler + * + * Description: + * Sends a single complete packet to the protocol stack + * + * Input Parameters: + * self - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void cdcncm_packet_handler(FAR struct cdcncm_driver_s *self) +{ + /* Check for errors and update statistics */ + +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the tap */ + + pkt_input(&self->dev); +#endif + + /* We only accept IP packets of the configured type and ARP packets */ + +#ifdef CONFIG_NET_IPv4 + if (BUF->type == HTONS(ETHTYPE_IP)) + { + ninfo("IPv4 frame\n"); + NETDEV_RXIPV4(&self->dev); + + /* Receive an IPv4 packet from the network device */ + + ipv4_input(&self->dev); + + /* Check for a reply to the IPv4 packet */ + + cdcncm_reply(self); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("IPv6 frame\n"); + NETDEV_RXIPV6(&self->dev); + + /* Dispatch IPv6 packet to the network layer */ + + ipv6_input(&self->dev); + + /* Check for a reply to the IPv6 packet */ + + cdcncm_reply(self); + } + else +#endif +#ifdef CONFIG_NET_ARP + if (BUF->type == HTONS(ETHTYPE_ARP)) + { + /* Dispatch ARP packet to the network layer */ + + arp_input(&self->dev); + NETDEV_RXARP(&self->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, d_len field will set to a value > 0. + */ + + if (self->dev.d_len > 0) + { + cdcncm_transmit(self); + } + } + else +#endif + { + NETDEV_RXDROPPED(&self->dev); + } +} + +/**************************************************************************** + * Name: cdcncm_receive + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Input Parameters: + * self - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void cdcncm_receive(FAR struct cdcncm_driver_s *self) +{ + FAR const struct ndp_parser_opts_s *opts = self->parseropts; + FAR uint8_t *tmp = self->rdreq->buf; + uint32_t ntbmax = g_ntbparameters.ntboutmaxsize; + uint32_t blocklen; + uint32_t ndplen; + int ndpindex; + int dgramcounter; + + /* Get signature */ + + if (GETUINT32(tmp) != opts->nthsign) + { + uerr("Wrong NTH SIGN, skblen %d\n", self->rdreq->xfrd); + return; + } + + tmp += 4; + + /* Get header len */ + + if (GETUINT16(tmp) != opts->nthsize) + { + uerr("Wrong NTB headersize\n"); + return; + } + + tmp += 4; /* Skip header len and seq */ + + blocklen = cdcncm_get(&tmp, opts->blocklen); + + /* Get block len */ + + if (blocklen > ntbmax) + { + uerr("OUT size exceeded\n"); + return; + } + + ndpindex = cdcncm_get(&tmp, opts->ndpindex); + + do + { + uint32_t index; + uint32_t dglen; + + if (((ndpindex % 4) != 0) || (ndpindex < opts->nthsize) || + (ndpindex > (blocklen - opts->ndpsize))) + { + uerr("Bad index: %#X\n", ndpindex); + return; + } + + tmp = self->rdreq->buf + ndpindex; + + if (GETUINT32(tmp) != self->ndpsign) + { + uerr("Wrong NDP SIGN\n"); + return; + } + + tmp += 4; + ndplen = cdcncm_get(&tmp, 2); + + if ((ndplen < opts->ndpsize + 2 * (opts->dgramitemlen * 2)) || + (ndplen % opts->ndpalign != 0)) + { + uerr("Bad NDP length: %x\n", ndplen); + return; + } + + tmp += opts->reserved1; + ndpindex = cdcncm_get(&tmp, opts->nextndpindex); + tmp += opts->reserved2; + + ndplen -= opts->ndpsize; + dgramcounter = 0; + do + { + index = cdcncm_get(&tmp, opts->dgramitemlen); + dglen = cdcncm_get(&tmp, opts->dgramitemlen); + + /* TODO: support CRC */ + + /* Check if the packet is a valid size for the network buffer + * configuration. + */ + + if (index == 0 || dglen == 0) + { + break; + } + + /* Copy the data data from the hardware to self->dev.d_buf. Set + * amount of data in self->dev.d_len + */ + + memcpy(self->dev.d_buf, self->rdreq->buf + index, dglen); + self->dev.d_len = dglen; + dgramcounter++; + cdcncm_packet_handler(self); + + ndplen -= 2 * (opts->dgramitemlen); + } + while (ndplen > 2 * (opts->dgramitemlen)); + } + while (ndpindex); +} + +/**************************************************************************** + * Name: cdcncm_txdone + * + * Description: + * An interrupt was received indicating that the last TX packet(s) is done + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void cdcncm_txdone(FAR struct cdcncm_driver_s *priv) +{ + /* Check for errors and update statistics */ + + NETDEV_TXDONE(priv->dev); + + /* In any event, poll the network for new TX data */ + + devif_poll(&priv->dev, cdcncm_txpoll); +} + +/**************************************************************************** + * Name: cdcncm_interrupt_work + * + * Description: + * Perform interrupt related work from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() was called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Runs on a worker thread. + * + ****************************************************************************/ + +static void cdcncm_interrupt_work(FAR void *arg) +{ + FAR struct cdcncm_driver_s *self = (FAR struct cdcncm_driver_s *)arg; + irqstate_t flags; + + /* Lock the network and serialize driver operations if necessary. + * NOTE: Serialization is only required in the case where the driver work + * is performed on an LP worker thread and where more than one LP worker + * thread has been configured. + */ + + net_lock(); + + /* Check if we received an incoming packet, if so, call cdcncm_receive() */ + + if (self->rxpending) + { + cdcncm_receive(self); + + flags = enter_critical_section(); + self->rxpending = false; + EP_SUBMIT(self->epbulkout, self->rdreq); + leave_critical_section(flags); + } + + /* Check if a packet transmission just completed. If so, call + * cdcncm_txdone. This may disable further Tx interrupts if there + * are no pending transmissions. + */ + + flags = enter_critical_section(); + if (self->txdone) + { + self->txdone = false; + leave_critical_section(flags); + + cdcncm_txdone(self); + } + else + { + leave_critical_section(flags); + } + + net_unlock(); +} + +/**************************************************************************** + * Name: cdcncm_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int cdcncm_ifup(FAR struct net_driver_s *dev) +{ + FAR struct cdcncm_driver_s *priv = + (FAR struct cdcncm_driver_s *)dev->d_private; + +#ifdef CONFIG_NET_IPv4 + ninfo("Bringing up: %u.%u.%u.%u\n", + ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr), + ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr)); +#endif +#ifdef CONFIG_NET_IPv6 + ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], + dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], + dev->d_ipv6addr[6], dev->d_ipv6addr[7]); +#endif + + priv->bifup = true; + return OK; +} + +/**************************************************************************** + * Name: cdcncm_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int cdcncm_ifdown(FAR struct net_driver_s *dev) +{ + FAR struct cdcncm_driver_s *priv = + (FAR struct cdcncm_driver_s *)dev->d_private; + irqstate_t flags; + + /* Disable the Ethernet interrupt */ + + flags = enter_critical_section(); + + /* Put the EMAC in its reset, non-operational state. This should be + * a known configuration that will guarantee the cdcncm_ifup() always + * successfully brings the interface back up. + */ + + /* Mark the device "down" */ + + priv->bifup = false; + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: cdcncm_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Input Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Runs on a work queue thread. + * + ****************************************************************************/ + +static void cdcncm_txavail_work(FAR void *arg) +{ + FAR struct cdcncm_driver_s *self = (FAR struct cdcncm_driver_s *)arg; + + /* Lock the network and serialize driver operations if necessary. + * NOTE: Serialization is only required in the case where the driver work + * is performed on an LP worker thread and where more than one LP worker + * thread has been configured. + */ + + net_lock(); + + /* Ignore the notification if the interface is not yet up */ + + if (self->bifup) + { + devif_poll(&self->dev, cdcncm_txpoll); + } + + net_unlock(); +} + +/**************************************************************************** + * Name: cdcncm_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int cdcncm_txavail(FAR struct net_driver_s *dev) +{ + FAR struct cdcncm_driver_s *priv = + (FAR struct cdcncm_driver_s *)dev->d_private; + + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->pollwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(ETHWORK, &priv->pollwork, cdcncm_txavail_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Name: cdcncm_addmac + * + * Description: + * NuttX Callback: Add the specified MAC address to the hardware multicast + * address filtering + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be added + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) +static int cdcncm_addmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac) +{ + return OK; +} +#endif + +/**************************************************************************** + * Name: cdcncm_rmmac + * + * Description: + * NuttX Callback: Remove the specified MAC address from the hardware + * multicast address filtering + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be removed + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MCASTGROUP +static int cdcncm_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) +{ + return OK; +} +#endif + +/**************************************************************************** + * Name: cdcncm_ioctl + * + * Description: + * Handle network IOCTL commands directed to this device. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * cmd - The IOCTL command + * arg - The argument for the IOCTL command + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_IOCTL +static int cdcncm_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg) +{ + return -ENOTTY; +} +#endif + +/**************************************************************************** + * USB Device Class Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcncm_ep0incomplete + * + * Description: + * Handle completion of EP0 control operations + * + ****************************************************************************/ + +static void cdcncm_ep0incomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + if (req->result || req->xfrd != req->len) + { + uerr("result: %hd, xfrd: %hu\n", req->result, req->xfrd); + } +} + +/**************************************************************************** + * Name: cdcncm_rdcomplete + * + * Description: + * Handle completion of read request on the bulk OUT endpoint. + * + ****************************************************************************/ + +static void cdcncm_rdcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct cdcncm_driver_s *self = (FAR struct cdcncm_driver_s *)ep->priv; + + uinfo("buf: %p, flags 0x%hhx, len %hu, xfrd %hu, result %hd\n", + req->buf, req->flags, req->len, req->xfrd, req->result); + + switch (req->result) + { + case 0: /* Normal completion */ + { + DEBUGASSERT(!self->rxpending); + self->rxpending = true; + work_queue(ETHWORK, &self->irqwork, + cdcncm_interrupt_work, self, 0); + } + break; + + case -ESHUTDOWN: /* Disconnection */ + break; + + default: /* Some other error occurred */ + { + uerr("req->result: %hd\n", req->result); + EP_SUBMIT(self->epbulkout, self->rdreq); + } + break; + } +} + +/**************************************************************************** + * Name: cdcncm_wrcomplete + * + * Description: + * Handle completion of write request. This function probably executes + * in the context of an interrupt handler. + * + ****************************************************************************/ + +static void cdcncm_wrcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct cdcncm_driver_s *self = (FAR struct cdcncm_driver_s *)ep->priv; + int rc; + + uinfo("buf: %p, flags 0x%hhx, len %hu, xfrd %hu, result %hd\n", + req->buf, req->flags, req->len, req->xfrd, req->result); + + /* The single USB device write request is available for upcoming + * transmissions again. + */ + + rc = nxsem_post(&self->wrreq_idle); + + if (rc != OK) + { + nerr("nxsem_post failed! rc: %d\n", rc); + } + + /* Inform the network layer that an Ethernet frame was transmitted. */ + + self->txdone = true; + work_queue(ETHWORK, &self->irqwork, cdcncm_interrupt_work, self, 0); +} + +/**************************************************************************** + * Name: cdcncm_resetconfig + * + * Description: + * Mark the device as not configured and disable all endpoints. + * + ****************************************************************************/ + +static void cdcncm_resetconfig(FAR struct cdcncm_driver_s *self) +{ + /* Are we configured? */ + + if (self->config != CDCECM_CONFIGID_NONE) + { + /* Yes.. but not anymore */ + + self->config = CDCECM_CONFIGID_NONE; + + /* Inform the networking layer that the link is down */ + + self->dev.d_ifdown(&self->dev); + + /* Disable endpoints. This should force completion of all pending + * transfers. + */ + + EP_DISABLE(self->epint); + EP_DISABLE(self->epbulkin); + EP_DISABLE(self->epbulkout); + } + + self->parseropts = &g_ndp16_opts; + self->ndpsign = self->parseropts->ndpsign; + self->notify = NCM_NOTIFY_NONE; +} + +/**************************************************************************** + * Name: cdcncm_setconfig + * + * Set the device configuration by allocating and configuring endpoints and + * by allocating and queue read and write requests. + * + ****************************************************************************/ + +static int cdcncm_setconfig(FAR struct cdcncm_driver_s *self, uint8_t config) +{ + struct usb_epdesc_s epdesc; + bool is_high_speed = self->usbdev.speed == USB_SPEED_HIGH; + int ret; + + if (config == self->config) + { + return OK; + } + + cdcncm_resetconfig(self); + + if (config == CDCECM_CONFIGID_NONE) + { + return OK; + } + + if (config != CDCECM_CONFIGID) + { + return -EINVAL; + } + + cdcncm_mkepdesc(CDCNCM_EP_INTIN_IDX, &epdesc, &self->devinfo, false); + ret = EP_CONFIGURE(self->epint, &epdesc, false); + + if (ret < 0) + { + goto error; + } + + self->epint->priv = self; + cdcncm_mkepdesc(CDCNCM_EP_BULKIN_IDX, + &epdesc, &self->devinfo, is_high_speed); + ret = EP_CONFIGURE(self->epbulkin, &epdesc, false); + + if (ret < 0) + { + goto error; + } + + self->epbulkin->priv = self; + + cdcncm_mkepdesc(CDCNCM_EP_BULKOUT_IDX, + &epdesc, &self->devinfo, is_high_speed); + ret = EP_CONFIGURE(self->epbulkout, &epdesc, true); + + if (ret < 0) + { + goto error; + } + + self->epbulkout->priv = self; + + /* Queue read requests in the bulk OUT endpoint */ + + DEBUGASSERT(!self->rxpending); + + self->rdreq->callback = cdcncm_rdcomplete, + ret = EP_SUBMIT(self->epbulkout, self->rdreq); + if (ret != OK) + { + uerr("EP_SUBMIT failed. ret %d\n", ret); + goto error; + } + + /* We are successfully configured */ + + self->config = config; + + /* Set client's MAC address */ + + memcpy(self->dev.d_mac.ether.ether_addr_octet, + "\x00\xe0\xde\xad\xbe\xef", IFHWADDRLEN); + + /* Report link up to networking layer */ + + if (self->dev.d_ifup(&self->dev) == OK) + { + self->dev.d_flags |= IFF_UP; + } + + return OK; + +error: + cdcncm_resetconfig(self); + return ret; +} + +/**************************************************************************** + * Name: ncm_notify + * + ****************************************************************************/ + +static int ncm_notify(FAR struct cdcncm_driver_s *self) +{ + FAR struct usb_ctrlreq_s *req = + (FAR struct usb_ctrlreq_s *)self->notifyreq->buf; + int ret = 0; + + switch (self->notify) + { + case NCM_NOTIFY_NONE: + return ret; + + case NCM_NOTIFY_CONNECT: + + /* Notifying the host of the NIC modification status */ + + req->req = NCM_NETWORK_CONNECTION; + req->value[0] = LSBYTE(IFF_IS_RUNNING(self->dev.d_flags)); + req->value[1] = MSBYTE(IFF_IS_RUNNING(self->dev.d_flags)); + req->len[0] = 0; + req->len[1] = 0; + ret = sizeof(*req); + + self->notify = NCM_NOTIFY_NONE; + break; + + case NCM_NOTIFY_SPEED: + { + FAR uint32_t *data; + + req->req = NCM_SPEED_CHANGE; + req->value[0] = LSBYTE(0); + req->value[1] = MSBYTE(0); + req->len[0] = LSBYTE(8); + req->len[1] = MSBYTE(8); + + /* SPEED_CHANGE data is up/down speeds in bits/sec */ + + data = (FAR uint32_t *)(self->ctrlreq->buf + sizeof(*req)); + data[0] = self->usbdev.speed == USB_SPEED_HIGH ? + CDCECM_HIGH_BITRATE : CDCECM_LOW_BITRATE; + data[1] = data[0]; + ret = sizeof(*req) + 8; + + self->notify = NCM_NOTIFY_CONNECT; + break; + } + } + + req->type = 0xa1; + req->index[0] = LSBYTE(self->devinfo.ifnobase); + req->index[1] = MSBYTE(self->devinfo.ifnobase); + + return ret; +} + +/**************************************************************************** + * Name: ncm_do_notify + * + ****************************************************************************/ + +static void ncm_do_notify(FAR void *arg) +{ + FAR struct cdcncm_driver_s *self = arg; + int ret; + + while (self->notify != NCM_NOTIFY_NONE) + { + ret = ncm_notify(self); + if (ret > 0) + { + FAR struct usbdev_req_s *notifyreq = self->notifyreq; + + notifyreq->len = ret; + notifyreq->flags = USBDEV_REQFLAGS_NULLPKT; + + EP_SUBMIT(self->epint, notifyreq); + } + } +} + +/**************************************************************************** + * Name: cdcncm_setinterface + * + ****************************************************************************/ + +static int cdcncm_setinterface(FAR struct cdcncm_driver_s *self, + uint16_t interface, uint16_t altsetting) +{ + if (interface == self->devinfo.ifnobase + 1) + { + if (altsetting) + { + self->notify = NCM_NOTIFY_SPEED; + } + + netdev_carrier_on(&self->dev); + work_queue(ETHWORK, &self->notifywork, ncm_do_notify, self, + MSEC2TICK(100)); + } + else + { + uerr("invailid interface %d\n", interface); + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: cdcncm_mkstrdesc + * + * Description: + * Construct a string descriptor + * + ****************************************************************************/ + +static int cdcncm_mkstrdesc(uint8_t id, FAR struct usb_strdesc_s *strdesc) +{ + FAR uint8_t *data = (FAR uint8_t *)(strdesc + 1); + FAR const char *str; + int len; + int ndata; + int i; + + switch (id) + { +#ifndef CONFIG_CDCNCM_COMPOSITE + case 0: + { + /* Descriptor 0 is the language id */ + + strdesc->len = 4; + strdesc->type = USB_DESC_TYPE_STRING; + data[0] = LSBYTE(CDCNCM_STR_LANGUAGE); + data[1] = MSBYTE(CDCNCM_STR_LANGUAGE); + return 4; + } + + case CDCNCM_MANUFACTURERSTRID: + str = CONFIG_CDCNCM_VENDORSTR; + break; + + case CDCNCM_PRODUCTSTRID: + str = CONFIG_CDCNCM_PRODUCTSTR; + break; + + case CDCNCM_SERIALSTRID: +#ifdef CONFIG_BOARD_USBDEV_SERIALSTR + str = board_usbdev_serialstr(); +#else + str = "0"; +#endif + break; + + case CDCNCM_CONFIGSTRID: + str = "Default"; + break; +#endif + + case CDCECM_MACSTRID: + str = "020000112233"; + break; + + default: + uerr("Unknown string descriptor index: %d\n", id); + return -EINVAL; + } + + /* The string is utf16-le. The poor man's utf-8 to utf16-le + * conversion below will only handle 7-bit en-us ascii + */ + + len = strlen(str); + if (len > (CDCECM_MAXSTRLEN / 2)) + { + len = (CDCECM_MAXSTRLEN / 2); + } + + for (i = 0, ndata = 0; i < len; i++, ndata += 2) + { + data[ndata] = str[i]; + data[ndata + 1] = 0; + } + + strdesc->len = ndata + 2; + strdesc->type = USB_DESC_TYPE_STRING; + return strdesc->len; +} + +/**************************************************************************** + * Name: cdcncm_mkepdesc + * + * Description: + * Construct the endpoint descriptor + * + ****************************************************************************/ + +static void cdcncm_mkepdesc(int epidx, FAR struct usb_epdesc_s *epdesc, + FAR struct usbdev_devinfo_s *devinfo, + bool hispeed) +{ + uint16_t intin_mxpktsz = CONFIG_CDCNCM_EPINTIN_FSSIZE; + uint16_t bulkout_mxpktsz = CONFIG_CDCNCM_EPBULKOUT_FSSIZE; + uint16_t bulkin_mxpktsz = CONFIG_CDCNCM_EPBULKIN_FSSIZE; + +#ifdef CONFIG_USBDEV_DUALSPEED + if (hispeed) + { + intin_mxpktsz = CONFIG_CDCNCM_EPINTIN_HSSIZE; + bulkout_mxpktsz = CONFIG_CDCNCM_EPBULKOUT_HSSIZE; + bulkin_mxpktsz = CONFIG_CDCNCM_EPBULKIN_HSSIZE; + } +#else + UNUSED(hispeed); +#endif + + epdesc->len = USB_SIZEOF_EPDESC; /* Descriptor length */ + epdesc->type = USB_DESC_TYPE_ENDPOINT; /* Descriptor type */ + + switch (epidx) + { + case CDCNCM_EP_INTIN_IDX: /* Interrupt IN endpoint */ + { + epdesc->addr = USB_DIR_IN | + devinfo->epno[CDCNCM_EP_INTIN_IDX]; + epdesc->attr = USB_EP_ATTR_XFER_INT; + epdesc->mxpacketsize[0] = LSBYTE(intin_mxpktsz); + epdesc->mxpacketsize[1] = MSBYTE(intin_mxpktsz); + epdesc->interval = 5; + } + break; + + case CDCNCM_EP_BULKIN_IDX: + { + epdesc->addr = USB_DIR_IN | + devinfo->epno[CDCNCM_EP_BULKIN_IDX]; + epdesc->attr = USB_EP_ATTR_XFER_BULK; + epdesc->mxpacketsize[0] = LSBYTE(bulkin_mxpktsz); + epdesc->mxpacketsize[1] = MSBYTE(bulkin_mxpktsz); + epdesc->interval = 0; + } + break; + + case CDCNCM_EP_BULKOUT_IDX: + { + epdesc->addr = USB_DIR_OUT | + devinfo->epno[CDCNCM_EP_BULKOUT_IDX]; + epdesc->attr = USB_EP_ATTR_XFER_BULK; + epdesc->mxpacketsize[0] = LSBYTE(bulkout_mxpktsz); + epdesc->mxpacketsize[1] = MSBYTE(bulkout_mxpktsz); + epdesc->interval = 0; + } + break; + + default: + DEBUGPANIC(); + } +} + +/**************************************************************************** + * Name: cdcncm_mkcfgdesc + * + * Description: + * Construct the config descriptor + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DUALSPEED +static int16_t cdcncm_mkcfgdesc(FAR uint8_t *desc, + FAR struct usbdev_devinfo_s *devinfo, + uint8_t speed, uint8_t type) +#else +static int16_t cdcncm_mkcfgdesc(FAR uint8_t *desc, + FAR struct usbdev_devinfo_s *devinfo) +#endif +{ + FAR struct usb_cfgdesc_s *cfgdesc = NULL; + int16_t len = 0; + bool is_high_speed = false; + +#ifdef CONFIG_USBDEV_DUALSPEED + is_high_speed = (speed == USB_SPEED_HIGH); + + /* Check for switches between high and full speed */ + + if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG) + { + is_high_speed = !is_high_speed; + } +#endif + +#ifndef CONFIG_CDCNCM_COMPOSITE + if (desc) + { + cfgdesc = (FAR struct usb_cfgdesc_s *)desc; + cfgdesc->len = USB_SIZEOF_CFGDESC; + cfgdesc->type = USB_DESC_TYPE_CONFIG; + cfgdesc->ninterfaces = CDCECM_NINTERFACES; + cfgdesc->cfgvalue = CDCECM_CONFIGID; + cfgdesc->icfg = devinfo->strbase + CDCNCM_CONFIGSTRID; + cfgdesc->attr = USB_CONFIG_ATTR_ONE | CDCNCM_SELFPOWERED | + CDCNCM_REMOTEWAKEUP; + cfgdesc->mxpower = (CONFIG_USBDEV_MAXPOWER + 1) / 2; + + desc += USB_SIZEOF_CFGDESC; + } + + len += USB_SIZEOF_CFGDESC; + +#elif defined(CONFIG_COMPOSITE_IAD) + + /* Interface association descriptor */ + + if (desc) + { + FAR struct usb_iaddesc_s *iaddesc; + + iaddesc = (FAR struct usb_iaddesc_s *)desc; + iaddesc->len = USB_SIZEOF_IADDESC; /* Descriptor length */ + iaddesc->type = USB_DESC_TYPE_INTERFACEASSOCIATION; /* Descriptor type */ + iaddesc->firstif = devinfo->ifnobase; /* Number of first interface of the function */ + iaddesc->nifs = devinfo->ninterfaces; /* Number of interfaces associated with the function */ + iaddesc->classid = USB_CLASS_CDC; /* Class code */ + iaddesc->subclass = CDC_SUBCLASS_NCM; /* Sub-class code */ + iaddesc->protocol = CDC_PROTO_NONE; /* Protocol code */ + iaddesc->ifunction = 0; /* Index to string identifying the function */ + + desc += USB_SIZEOF_IADDESC; + } + + len += USB_SIZEOF_IADDESC; +#endif + + /* Communications Class Interface */ + + if (desc) + { + FAR struct usb_ifdesc_s *ifdesc; + + ifdesc = (FAR struct usb_ifdesc_s *)desc; + ifdesc->len = USB_SIZEOF_IFDESC; + ifdesc->type = USB_DESC_TYPE_INTERFACE; + ifdesc->ifno = devinfo->ifnobase; + ifdesc->alt = 0; + ifdesc->neps = 1; + ifdesc->classid = USB_CLASS_CDC; + ifdesc->subclass = CDC_SUBCLASS_NCM; + ifdesc->protocol = CDC_PROTO_NONE; + ifdesc->iif = 0; + + desc += USB_SIZEOF_IFDESC; + } + + len += USB_SIZEOF_IFDESC; + + if (desc) + { + FAR struct cdc_hdr_funcdesc_s *hdrdesc; + + hdrdesc = (FAR struct cdc_hdr_funcdesc_s *)desc; + hdrdesc->size = SIZEOF_HDR_FUNCDESC; + hdrdesc->type = USB_DESC_TYPE_CSINTERFACE; + hdrdesc->subtype = CDC_DSUBTYPE_HDR; + hdrdesc->cdc[0] = LSBYTE(0x0110); + hdrdesc->cdc[1] = MSBYTE(0x0110); + + desc += SIZEOF_HDR_FUNCDESC; + } + + len += SIZEOF_HDR_FUNCDESC; + + if (desc) + { + FAR struct cdc_union_funcdesc_s *uniondesc; + + uniondesc = (FAR struct cdc_union_funcdesc_s *)desc; + uniondesc->size = SIZEOF_UNION_FUNCDESC(1); + uniondesc->type = USB_DESC_TYPE_CSINTERFACE; + uniondesc->subtype = CDC_DSUBTYPE_UNION; + uniondesc->master = devinfo->ifnobase; + uniondesc->slave[0] = devinfo->ifnobase + 1; + + desc += SIZEOF_UNION_FUNCDESC(1); + } + + len += SIZEOF_UNION_FUNCDESC(1); + + if (desc) + { + FAR struct cdc_ecm_funcdesc_s *ecmdesc; + + ecmdesc = (FAR struct cdc_ecm_funcdesc_s *)desc; + ecmdesc->size = SIZEOF_ECM_FUNCDESC; + ecmdesc->type = USB_DESC_TYPE_CSINTERFACE; + ecmdesc->subtype = CDC_DSUBTYPE_ECM; + ecmdesc->mac = devinfo->strbase + CDCECM_MACSTRID; + ecmdesc->stats[0] = 0; + ecmdesc->stats[1] = 0; + ecmdesc->stats[2] = 0; + ecmdesc->stats[3] = 0; + ecmdesc->maxseg[0] = LSBYTE(CONFIG_NET_ETH_PKTSIZE); + ecmdesc->maxseg[1] = MSBYTE(CONFIG_NET_ETH_PKTSIZE); + ecmdesc->nmcflts[0] = LSBYTE(0); + ecmdesc->nmcflts[1] = MSBYTE(0); + ecmdesc->npwrflts = 0; + + desc += SIZEOF_ECM_FUNCDESC; + } + + len += SIZEOF_ECM_FUNCDESC; + + if (desc) + { + FAR struct cdc_ncm_funcdesc_s *ncmdesc; + + ncmdesc = (FAR struct cdc_ncm_funcdesc_s *)desc; + ncmdesc->size = SIZEOF_NCM_FUNCDESC; + ncmdesc->type = USB_DESC_TYPE_CSINTERFACE; + ncmdesc->subtype = CDC_DSUBTYPE_NCM; + ncmdesc->version[0] = LSBYTE(CDCECM_VERSIONNO); + ncmdesc->version[1] = MSBYTE(CDCECM_VERSIONNO); + ncmdesc->netcaps = NCAPS; + + desc += SIZEOF_NCM_FUNCDESC; + } + + len += SIZEOF_NCM_FUNCDESC; + + if (desc) + { + FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)desc; + + cdcncm_mkepdesc(CDCNCM_EP_INTIN_IDX, epdesc, devinfo, false); + desc += USB_SIZEOF_EPDESC; + } + + len += USB_SIZEOF_EPDESC; + + /* Data Class Interface */ + + if (desc) + { + FAR struct usb_ifdesc_s *ifdesc; + + ifdesc = (FAR struct usb_ifdesc_s *)desc; + ifdesc->len = USB_SIZEOF_IFDESC; + ifdesc->type = USB_DESC_TYPE_INTERFACE; + ifdesc->ifno = devinfo->ifnobase + 1; + ifdesc->alt = 0; + ifdesc->neps = 0; + ifdesc->classid = USB_CLASS_CDC_DATA; + ifdesc->subclass = 0; + ifdesc->protocol = CDC_DATA_PROTO_NCMNTB; + ifdesc->iif = 0; + + desc += USB_SIZEOF_IFDESC; + } + + len += USB_SIZEOF_IFDESC; + + if (desc) + { + FAR struct usb_ifdesc_s *ifdesc; + + ifdesc = (FAR struct usb_ifdesc_s *)desc; + ifdesc->len = USB_SIZEOF_IFDESC; + ifdesc->type = USB_DESC_TYPE_INTERFACE; + ifdesc->ifno = devinfo->ifnobase + 1; + ifdesc->alt = 1; + ifdesc->neps = 2; + ifdesc->classid = USB_CLASS_CDC_DATA; + ifdesc->subclass = 0; + ifdesc->protocol = CDC_DATA_PROTO_NCMNTB; + ifdesc->iif = 0; + + desc += USB_SIZEOF_IFDESC; + } + + len += USB_SIZEOF_IFDESC; + + if (desc) + { + FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)desc; + + cdcncm_mkepdesc(CDCNCM_EP_BULKIN_IDX, epdesc, devinfo, is_high_speed); + desc += USB_SIZEOF_EPDESC; + } + + len += USB_SIZEOF_EPDESC; + + if (desc) + { + FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)desc; + + cdcncm_mkepdesc(CDCNCM_EP_BULKOUT_IDX, epdesc, devinfo, is_high_speed); + desc += USB_SIZEOF_EPDESC; + } + + len += USB_SIZEOF_EPDESC; + + if (cfgdesc) + { + cfgdesc->totallen[0] = LSBYTE(len); + cfgdesc->totallen[1] = MSBYTE(len); + } + + DEBUGASSERT(len <= CDCECM_MXDESCLEN); + return len; +} + +/**************************************************************************** + * Name: cdcncm_getdescriptor + * + * Description: + * Copy the USB CDC-ECM Device USB Descriptor of a given Type and a given + * Index into the provided Descriptor Buffer. + * + * Input Parameter: + * drvr - The USB Device Fuzzer Driver instance. + * type - The Type of USB Descriptor requested. + * index - The Index of the USB Descriptor requested. + * desc - The USB Descriptor is copied into this buffer, which must be at + * least CDCECM_MXDESCLEN bytes wide. + * + * Returned Value: + * The size in bytes of the requested USB Descriptor or a negated errno in + * case of failure. + * + ****************************************************************************/ + +static int cdcncm_getdescriptor(FAR struct cdcncm_driver_s *self, + uint8_t type, uint8_t index, FAR void *desc) +{ + switch (type) + { +#ifndef CONFIG_CDCNCM_COMPOSITE + case USB_DESC_TYPE_DEVICE: + { + memcpy(desc, &g_devdesc, sizeof(g_devdesc)); + return (int)sizeof(g_devdesc); + } + break; +#endif + +#ifdef CONFIG_USBDEV_DUALSPEED + case USB_DESC_TYPE_OTHERSPEEDCONFIG: +#endif /* CONFIG_USBDEV_DUALSPEED */ + case USB_DESC_TYPE_CONFIG: + { +#ifdef CONFIG_USBDEV_DUALSPEED + return cdcncm_mkcfgdesc((FAR uint8_t *)desc, &self->devinfo, + self->usbdev.speed, type); +#else + return cdcncm_mkcfgdesc((FAR uint8_t *)desc, &self->devinfo); +#endif + } + break; + + case USB_DESC_TYPE_STRING: + { + return cdcncm_mkstrdesc(index, (FAR struct usb_strdesc_s *)desc); + } + break; + + default: + uerr("Unsupported descriptor type: 0x%02hhx\n", type); + break; + } + + return -ENOTSUP; +} + +/**************************************************************************** + * USB Device Class Methods + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcncm_bind + * + * Description: + * Invoked when the driver is bound to an USB device + * + ****************************************************************************/ + +static int cdcncm_bind(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev) +{ + FAR struct cdcncm_driver_s *self = (FAR struct cdcncm_driver_s *)driver; + int ret = OK; + + uinfo("\n"); + +#ifndef CONFIG_CDCNCM_COMPOSITE + dev->ep0->priv = self; +#endif + + /* Preallocate control request */ + + self->ctrlreq = usbdev_allocreq(dev->ep0, CDCECM_MXDESCLEN); + + if (self->ctrlreq == NULL) + { + ret = -ENOMEM; + goto error; + } + + self->ctrlreq->callback = cdcncm_ep0incomplete; + + self->epint = DEV_ALLOCEP(dev, + USB_DIR_IN | + self->devinfo.epno[CDCNCM_EP_INTIN_IDX], + true, USB_EP_ATTR_XFER_INT); + self->epbulkin = DEV_ALLOCEP(dev, + USB_DIR_IN | + self->devinfo.epno[CDCNCM_EP_BULKIN_IDX], + true, USB_EP_ATTR_XFER_BULK); + self->epbulkout = DEV_ALLOCEP(dev, + USB_DIR_OUT | + self->devinfo.epno[CDCNCM_EP_BULKOUT_IDX], + false, USB_EP_ATTR_XFER_BULK); + + if (!self->epint || !self->epbulkin || !self->epbulkout) + { + uerr("Failed to allocate endpoints!\n"); + ret = -ENODEV; + goto error; + } + + self->epint->priv = self; + self->epbulkin->priv = self; + self->epbulkout->priv = self; + + /* Pre-allocate notify requests. The buffer size is CDCECM_MXDESCLEN. */ + + self->notifyreq = usbdev_allocreq(self->epint, CDCECM_MXDESCLEN); + if (self->notifyreq == NULL) + { + uerr("Out of memory\n"); + ret = -ENOMEM; + goto error; + } + + self->notifyreq->callback = cdcncm_wrcomplete; + + /* Pre-allocate read requests. The buffer size is NTB_DEFAULT_IN_SIZE. */ + + self->rdreq = usbdev_allocreq(self->epbulkout, NTB_DEFAULT_IN_SIZE); + if (self->rdreq == NULL) + { + uerr("Out of memory\n"); + ret = -ENOMEM; + goto error; + } + + self->rdreq->callback = cdcncm_rdcomplete; + + /* Pre-allocate a single write request. Buffer size is NTB_OUT_SIZE */ + + self->wrreq = usbdev_allocreq(self->epbulkin, NTB_OUT_SIZE); + if (self->wrreq == NULL) + { + uerr("Out of memory\n"); + ret = -ENOMEM; + goto error; + } + + self->wrreq->callback = cdcncm_wrcomplete; + + /* The single write request just allocated is available now. */ + + ret = nxsem_init(&self->wrreq_idle, 0, 1); + + if (ret != OK) + { + uerr("nxsem_init failed. ret: %d\n", ret); + goto error; + } + + self->txdone = false; + self->dev.d_len = 0; + +#ifndef CONFIG_CDCNCM_COMPOSITE +#ifdef CONFIG_USBDEV_SELFPOWERED + DEV_SETSELFPOWERED(dev); +#endif + + /* And pull-up the data line for the soft connect function (unless we are + * part of a composite device) + */ + + DEV_CONNECT(dev); +#endif + return OK; + +error: + uerr("cdcncm_bind failed! ret: %d\n", ret); + cdcncm_unbind(driver, dev); + return ret; +} + +static void cdcncm_unbind(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev) +{ + FAR struct cdcncm_driver_s *self = (FAR struct cdcncm_driver_s *)driver; + +#ifdef CONFIG_DEBUG_FEATURES + if (!driver || !dev) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Make sure that the endpoints have been unconfigured. If + * we were terminated gracefully, then the configuration should + * already have been reset. If not, then calling cdcacm_resetconfig + * should cause the endpoints to immediately terminate all + * transfers and return the requests to us (with result == -ESHUTDOWN) + */ + + cdcncm_resetconfig(self); + up_mdelay(50); + + /* Free the interrupt IN endpoint */ + + if (self->epint) + { + DEV_FREEEP(dev, self->epint); + self->epint = NULL; + } + + /* Free the pre-allocated control request */ + + if (self->ctrlreq != NULL) + { + usbdev_freereq(dev->ep0, self->ctrlreq); + self->ctrlreq = NULL; + } + + /* Free pre-allocated read requests (which should all have + * been returned to the free list at this time -- we don't check) + */ + + if (self->rdreq != NULL) + { + usbdev_freereq(self->epbulkout, self->rdreq); + self->rdreq = NULL; + } + + /* Free the bulk OUT endpoint */ + + if (self->epbulkout) + { + DEV_FREEEP(dev, self->epbulkout); + self->epbulkout = NULL; + } + + /* Free write requests that are not in use (which should be all + * of them) + */ + + if (self->wrreq != NULL) + { + usbdev_freereq(self->epbulkin, self->wrreq); + self->wrreq = NULL; + } + + /* Free the bulk IN endpoint */ + + if (self->epbulkin) + { + DEV_FREEEP(dev, self->epbulkin); + self->epbulkin = NULL; + } + + /* Clear out all data in the buffer */ + + self->dev.d_len = 0; +} + +static int cdcncm_setup(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev, + FAR const struct usb_ctrlreq_s *ctrl, + FAR uint8_t *dataout, + size_t outlen) +{ + FAR struct cdcncm_driver_s *self = (FAR struct cdcncm_driver_s *)driver; + uint16_t value = GETUINT16(ctrl->value); + uint16_t index = GETUINT16(ctrl->index); + uint16_t len = GETUINT16(ctrl->len); + int ret = -EOPNOTSUPP; + + if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) + { + switch (ctrl->req) + { + case USB_REQ_GETDESCRIPTOR: + { + uint8_t descindex = ctrl->value[0]; + uint8_t desctype = ctrl->value[1]; + + ret = cdcncm_getdescriptor(self, desctype, descindex, + self->ctrlreq->buf); + } + break; + + case USB_REQ_SETCONFIGURATION: + ret = cdcncm_setconfig(self, value); + break; + + case USB_REQ_SETINTERFACE: + ret = cdcncm_setinterface(self, index, value); + break; + + default: + uerr("Unsupported standard req: 0x%02hhx\n", ctrl->req); + break; + } + } + else if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS) + { + switch (ctrl->req) + { + case ECM_SET_PACKET_FILTER: + + /* SetEthernetPacketFilter is the only required CDCNCM subclass + * specific request, but it is still ok to always operate in + * promiscuous mode and rely on the host to do the filtering. + * This is especially true for our case: + * A simulated point-to-point connection. + */ + + uinfo("ECM_SET_PACKET_FILTER wValue: 0x%04hx, wIndex: 0x%04hx\n", + GETUINT16(ctrl->value), GETUINT16(ctrl->index)); + + ret = OK; + break; + + case NCM_GET_NTB_PARAMETERS: + if (len >= sizeof(g_ntbparameters)) + { + memcpy(self->ctrlreq->buf, &g_ntbparameters, + sizeof(g_ntbparameters)); + ret = sizeof(g_ntbparameters); + } + break; + + case NCM_SET_NTB_FORMAT: + if (len != 0 || index != self->devinfo.ifnobase) + break; + switch (value) + { + case 0x0000: + self->parseropts = &g_ndp16_opts; + uinfo("NCM16 selected\n"); + ret = 0; + break; + case 0x0001: + self->parseropts = &g_ndp32_opts; + uinfo("NCM32 selected\n"); + ret = 0; + break; + default: + break; + } + + break; + + case NCM_GET_NTB_INPUT_SIZE: + uinfo("NCM_GET_NTB_INPUT_SIZE len %d\n", len); + ret = 0; + break; + + case NCM_SET_NTB_INPUT_SIZE: + if (len == 4 && value == 0) + { + uinfo("NCM_SET_NTB_INPUT_SIZE len %d NTB input size %d\n", + len, *(int *)dataout); + ret = 0; + } + break; + + default: + uerr("Unsupported class req: 0x%02hhx\n", ctrl->req); + break; + } + } + else + { + uerr("Unsupported type: 0x%02hhx\n", ctrl->type); + } + + if (ret >= 0) + { + FAR struct usbdev_req_s *ctrlreq = self->ctrlreq; + + ctrlreq->len = MIN(len, ret); + ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; + + ret = EP_SUBMIT(dev->ep0, ctrlreq); + uinfo("EP_SUBMIT ret: %d\n", ret); + + if (ret < 0) + { + ctrlreq->result = OK; + cdcncm_ep0incomplete(dev->ep0, ctrlreq); + } + } + + return ret; +} + +static void cdcncm_disconnect(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev) +{ + uinfo("\n"); +} + +/**************************************************************************** + * Name: cdcncm_classobject + * + * Description: + * Register USB CDC/ECM and return the class object. + * + * Returned Value: + * A pointer to the allocated class object (NULL on failure). + * + ****************************************************************************/ + +static int cdcncm_classobject(int minor, + FAR struct usbdev_devinfo_s *devinfo, + FAR struct usbdevclass_driver_s **classdev) +{ + FAR struct cdcncm_driver_s *self; + int ret; + + /* Initialize the driver structure */ + + self = kmm_zalloc(sizeof(struct cdcncm_driver_s)); + if (!self) + { + nerr("Out of memory!\n"); + return -ENOMEM; + } + + /* Network device initialization */ + + self->dev.d_buf = (FAR uint8_t *)self->pktbuf; + self->dev.d_ifup = cdcncm_ifup; /* I/F up (new IP address) callback */ + self->dev.d_ifdown = cdcncm_ifdown; /* I/F down callback */ + self->dev.d_txavail = cdcncm_txavail; /* New TX data callback */ +#ifdef CONFIG_NET_MCASTGROUP + self->dev.d_addmac = cdcncm_addmac; /* Add multicast MAC address */ + self->dev.d_rmmac = cdcncm_rmmac; /* Remove multicast MAC address */ +#endif +#ifdef CONFIG_NETDEV_IOCTL + self->dev.d_ioctl = cdcncm_ioctl; /* Handle network IOCTL commands */ +#endif + self->dev.d_private = self; /* Used to recover private state from dev */ + + /* USB device initialization */ + +#ifdef CONFIG_USBDEV_DUALSPEED + self->usbdev.speed = USB_SPEED_HIGH; +#else + self->usbdev.speed = USB_SPEED_FULL; +#endif + self->usbdev.ops = &g_usbdevops; + + memcpy(&self->devinfo, devinfo, sizeof(struct usbdev_devinfo_s)); + + /* Put the interface in the down state. This usually amounts to resetting + * the device and/or calling cdcncm_ifdown(). + */ + + cdcncm_ifdown(&self->dev); + + /* Read the MAC address from the hardware into + * priv->dev.d_mac.ether.ether_addr_octet + * Applies only if the Ethernet MAC has its own internal address. + */ + + memcpy(self->dev.d_mac.ether.ether_addr_octet, + "\x00\xe0\xde\xad\xbe\xef", IFHWADDRLEN); + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + ret = netdev_register(&self->dev, NET_LL_ETHERNET); + if (ret < 0) + { + nerr("netdev_register failed. ret: %d\n", ret); + } + + *classdev = (FAR struct usbdevclass_driver_s *)self; + return ret; +} + +/**************************************************************************** + * Name: cdcncm_uninitialize + * + * Description: + * Un-initialize the USB CDC/ECM class driver. This function is used + * internally by the USB composite driver to uninitialize the CDC/ECM + * driver. This same interface is available (with an untyped input + * parameter) when the CDC/ECM driver is used standalone. + * + * Input Parameters: + * There is one parameter, it differs in typing depending upon whether the + * CDC/ECM driver is an internal part of a composite device, or a + * standalone USB driver: + * + * classdev - The class object returned by cdcncm_classobject() + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void cdcncm_uninitialize(FAR struct usbdevclass_driver_s *classdev) +{ + FAR struct cdcncm_driver_s *self = (FAR struct cdcncm_driver_s *)classdev; + int ret; + + /* Un-register the CDC/ECM netdev device */ + + ret = netdev_unregister(&self->dev); + if (ret < 0) + { + nerr("ERROR: netdev_unregister failed. ret: %d\n", ret); + } + +#ifndef CONFIG_CDCNCM_COMPOSITE + usbdev_unregister(&self->usbdev); +#endif + + /* And free the driver structure */ + + kmm_free(self); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcncm_initialize + * + * Description: + * Register CDC/ECM USB device interface. Register the corresponding + * network driver to NuttX and bring up the network. + * + * Input Parameters: + * minor - Device minor number. + * handle - An optional opaque reference to the CDC/ECM class object that + * may subsequently be used with cdcncm_uninitialize(). + * + * Returned Value: + * Zero (OK) means that the driver was successfully registered. On any + * failure, a negated errno value is returned. + * + ****************************************************************************/ + +#ifndef CONFIG_CDCNCM_COMPOSITE +int cdcncm_initialize(int minor, FAR void **handle) +{ + FAR struct usbdevclass_driver_s *drvr = NULL; + struct usbdev_devinfo_s devinfo; + int ret; + + memset(&devinfo, 0, sizeof(struct usbdev_devinfo_s)); + devinfo.ninterfaces = CDCECM_NINTERFACES; + devinfo.nstrings = CDCECM_NSTRIDS; + devinfo.nendpoints = CDCECM_NUM_EPS; + devinfo.epno[CDCNCM_EP_INTIN_IDX] = CONFIG_CDCNCM_EPINTIN; + devinfo.epno[CDCNCM_EP_BULKIN_IDX] = CONFIG_CDCNCM_EPBULKIN; + devinfo.epno[CDCNCM_EP_BULKOUT_IDX] = CONFIG_CDCNCM_EPBULKOUT; + + ret = cdcncm_classobject(minor, &devinfo, &drvr); + if (ret == OK) + { + ret = usbdev_register(drvr); + if (ret < 0) + { + uinfo("usbdev_register failed. ret %d\n", ret); + } + } + + if (handle) + { + *handle = (FAR void *)drvr; + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: cdcncm_get_composite_devdesc + * + * Description: + * Helper function to fill in some constants into the composite + * configuration struct. + * + * Input Parameters: + * dev - Pointer to the configuration struct we should fill + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_CDCNCM_COMPOSITE +void cdcncm_get_composite_devdesc(FAR struct composite_devdesc_s *dev) +{ + memset(dev, 0, sizeof(struct composite_devdesc_s)); + + /* The callback functions for the CDC/ECM class. + * + * classobject() and uninitialize() must be provided by board-specific + * logic + */ + + dev->mkconfdesc = cdcncm_mkcfgdesc; + dev->mkstrdesc = cdcncm_mkstrdesc; + dev->classobject = cdcncm_classobject; + dev->uninitialize = cdcncm_uninitialize; + + dev->nconfigs = CDCECM_NCONFIGS; /* Number of configurations supported */ + dev->configid = CDCECM_CONFIGID; /* The only supported configuration ID */ + + /* Let the construction function calculate the size of config descriptor */ + +#ifdef CONFIG_USBDEV_DUALSPEED + dev->cfgdescsize = cdcncm_mkcfgdesc(NULL, NULL, USB_SPEED_UNKNOWN, 0); +#else + dev->cfgdescsize = cdcncm_mkcfgdesc(NULL, NULL); +#endif + + /* Board-specific logic must provide the device minor */ + + /* Interfaces. + * + * ifnobase must be provided by board-specific logic + */ + + dev->devinfo.ninterfaces = CDCECM_NINTERFACES; /* Number of interfaces in the configuration */ + + /* Strings. + * + * strbase must be provided by board-specific logic + */ + + dev->devinfo.nstrings = CDCECM_NSTRIDS + 1; /* Number of Strings */ + + /* Endpoints. + * + * Endpoint numbers must be provided by board-specific logic. + */ + + dev->devinfo.nendpoints = CDCECM_NUM_EPS; +} +#endif /* CONFIG_CDCNCM_COMPOSITE */ + +#endif /* CONFIG_NET_CDCNCM */ diff --git a/drivers/usbhost/usbhost_cdcmbim.c b/drivers/usbhost/usbhost_cdcmbim.c index 2df0f29262..005bbdee8d 100644 --- a/drivers/usbhost/usbhost_cdcmbim.c +++ b/drivers/usbhost/usbhost_cdcmbim.c @@ -1253,7 +1253,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcmbim_s *priv, else if (ifdesc->classid == USB_CLASS_CDC_DATA && ifdesc->subclass == CDC_SUBCLASS_NONE && - ifdesc->protocol == CDC_DATA_PROTO_NTB) + ifdesc->protocol == CDC_DATA_PROTO_MBIMNTB) { priv->dataif = ifdesc->ifno; found |= USBHOST_DATAIFFOUND; diff --git a/include/nuttx/usb/cdc.h b/include/nuttx/usb/cdc.h index 362c3801fe..ebdecc7fb0 100644 --- a/include/nuttx/usb/cdc.h +++ b/include/nuttx/usb/cdc.h @@ -57,8 +57,9 @@ #define CDC_SUBCLASS_CAPI 0x05 /* CAPI Control Model */ #define CDC_SUBCLASS_ECM 0x06 /* Ethernet Networking Control Model */ #define CDC_SUBCLASS_ATM 0x07 /* ATM Networking Control Model */ - /* 0x08-0x0d Reserved (future use) */ -#define CDC_SUBCLASS_MBIM 0x0e /* MBIM Control Model */ + /* 0x08-0x0c Reserved (future use) */ +#define CDC_SUBCLASS_NCM 0x0d /* Network Control Model */ +#define CDC_SUBCLASS_MBIM 0x0e /* Mobile Broadband Interface Model */ /* 0x0f-0x7f Reserved (future use) */ /* 0x80-0xfe Reserved (vendor specific) */ @@ -85,7 +86,8 @@ #define CDC_DATA_PROTO_NONE 0x00 /* No class specific protocol required */ /* 0x01-0x2f Reserved (future use) */ -#define CDC_DATA_PROTO_NTB 0x02 /* Network Transfer Block protocol */ +#define CDC_DATA_PROTO_NCMNTB 0x01 /* NCM Network Transfer Block protocol */ +#define CDC_DATA_PROTO_MBIMNTB 0x02 /* MBIM Network Transfer Block protocol */ #define CDC_DATA_PROTO_ISDN 0x30 /* Physical interface protocol for ISDN BRI */ #define CDC_DATA_PROTO_HDLC 0x31 /* HDLC */ #define CDC_DATA_PROTO_TRANSP 0x32 /* Transparent */ @@ -328,6 +330,19 @@ */ #define ECM_SPEED_CHANGE ATM_SPEED_CHANGE +/* Table 14: Requests, Networking Control Model */ + +#define NCM_GET_NTB_PARAMETERS 0x80 +#define NCM_SET_NTB_FORMAT 0x84 +#define NCM_GET_NTB_INPUT_SIZE 0x85 +#define NCM_SET_NTB_INPUT_SIZE 0x86 +#define NCM_SET_CRC_MODE 0x8a + +/* Table 15: Notifications, Networking Control Model */ + +#define NCM_NETWORK_CONNECTION ECM_NETWORK_CONNECTION +#define NCM_SPEED_CHANGE ECM_SPEED_CHANGE + /* Descriptors ***************************************************************/ /* Table 25: bDescriptor SubType in Functional Descriptors */ @@ -352,8 +367,9 @@ #define CDC_DSUBTYPE_CAPI 0x0e /* CAPI Control Management Functional Descriptor */ #define CDC_DSUBTYPE_ECM 0x0f /* Ethernet Networking Functional Descriptor */ #define CDC_DSUBTYPE_ATM 0x10 /* ATM Networking Functional Descriptor */ +#define CDC_DSUBTYPE_NCM 0x1a /* Networking Functional Descriptor */ #define CDC_DSUBTYPE_MBIM 0x1b /* MBIM Functional Descriptor */ - /* 0x11-0xff Reserved (future use) */ + /* 0x11-0x19 and 0x1c-0xff Reserved (future use) */ /* Table 42: Ethernet Statistics Capabilities */ @@ -880,6 +896,23 @@ struct cdc_ecm_funcdesc_s #define SIZEOF_ECM_FUNCDESC 13 +/* Table 42: NCM Control Model Functional Descriptor */ + +struct cdc_ncm_funcdesc_s +{ + uint8_t size; /* bLength, Size of this descriptor */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ + uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_NCM as defined in + * Table 25. + */ + uint8_t version[2]; /* bcdNcmVersion, the NCM version 0x0100 */ + uint8_t netcaps; /* bmNetworkCapabilities, The NCM net types the device + * supports. + */ +}; + +#define SIZEOF_NCM_FUNCDESC 6 + /* Table 43: ATM Networking Functional Descriptor */ struct cdc_atm_funcdesc_s diff --git a/include/nuttx/usb/cdcncm.h b/include/nuttx/usb/cdcncm.h new file mode 100644 index 0000000000..6b2fd4f735 --- /dev/null +++ b/include/nuttx/usb/cdcncm.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * include/nuttx/usb/cdcncm.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_USB_CDCNCM_H +#define __INCLUDE_NUTTX_USB_CDCNCM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_CDCNCM_COMPOSITE +# include +#endif + +/**************************************************************************** + * Preprocessor definitions + ****************************************************************************/ + +#define CDCNCM_EP_INTIN_IDX (0) +#define CDCNCM_EP_BULKIN_IDX (1) +#define CDCNCM_EP_BULKOUT_IDX (2) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +# define EXTERN extern "C" +extern "C" +{ +#else +# define EXTERN extern +#endif + +/**************************************************************************** + * Public Functions Definitions + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcncm_initialize + * + * Description: + * Register CDC/NCM USB device interface. Register the corresponding + * network driver to NuttX and bring up the network. + * + * Input Parameters: + * minor - Device minor number. + * handle - An optional opaque reference to the CDC/NCM class object that + * may subsequently be used with cdcncm_uninitialize(). + * + * Returned Value: + * Zero (OK) means that the driver was successfully registered. On any + * failure, a negated errno value is returned. + * + ****************************************************************************/ + +#ifndef CONFIG_CDCNCM_COMPOSITE +int cdcncm_initialize(int minor, FAR void **handle); +#endif + +/**************************************************************************** + * Name: cdcncm_get_composite_devdesc + * + * Description: + * Helper function to fill in some constants into the composite + * configuration struct. + * + * Input Parameters: + * dev - Pointer to the configuration struct we should fill + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_CDCNCM_COMPOSITE +void cdcncm_get_composite_devdesc(FAR struct composite_devdesc_s *dev); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_USB_CDCNCM_H */ diff --git a/include/nuttx/usb/usb.h b/include/nuttx/usb/usb.h index 2b70e1d796..56c17043c7 100644 --- a/include/nuttx/usb/usb.h +++ b/include/nuttx/usb/usb.h @@ -65,10 +65,35 @@ #define MSBYTE(u16) ((u16) >> 8) /* Get MS byte from uint16_t */ #define LSBYTE(u16) ((u16) & 0xff) /* Get LS byte from uint16_t */ -#define GETUINT16(p) ((uint16_t)((uint16_t)(p[1] << 8) | (uint16_t)p[0])) -#define GETUINT32(p) ((uint32_t)((uint32_t)p[3] << 24) | \ - ((uint32_t)p[2] << 16) | \ - ((uint32_t)p[1] << 8) | (uint32_t)p[0]) +#define GETUINT16(p) ((uint16_t)(((uint16_t)(p)[1] << 8) | \ + (uint16_t)(p)[0])) + +#define PUTUINT16(p, v) do \ + { \ + uint8_t *__p; \ + __p = (FAR uint8_t *)(p); \ + *__p++ = ((uint16_t)(v) & 0xff); \ + *__p = ((uint16_t)(v) >> 8); \ + } \ + while (0) + +/* All 32-bit values must be little-endian */ + +#define GETUINT32(p) ((uint32_t)(((uint32_t)(p)[3] << 24) | \ + ((uint32_t)(p)[2] << 16) | \ + ((uint32_t)(p)[1] << 8) | \ + (uint32_t)(p)[0])) + +#define PUTUINT32(p, v) do \ + { \ + uint8_t *__p; \ + __p = (FAR uint8_t *)(p); \ + *__p++ = ((uint32_t)(v) & 0xff); \ + *__p++ = ((uint32_t)(v) >> 8) & 0xff; \ + *__p++ = ((uint32_t)(v) >> 16) & 0xff; \ + *__p = ((uint32_t)(v) >> 24); \ + } \ + while (0) /* USB directions (in endpoint addresses) */