diff --git a/drivers/usbdev/Kconfig b/drivers/usbdev/Kconfig index 47039ad1bef..df5d800e6a7 100644 --- a/drivers/usbdev/Kconfig +++ b/drivers/usbdev/Kconfig @@ -674,3 +674,166 @@ menuconfig RNDIS This option is not automatically selected because it may be that you have an additional network device that requires the early up_netinitialize() call. + +menuconfig NET_CDCECM + bool "CDC-ECM Ethernet-over-USB" + default n + select NETDEVICES + select NET + select NET_ETHERNET + ---help--- + References: + - "Universal Serial Bus - Communications Class - Subclass + Specification for Ethernet Control Model Devices, + Revision 1.2, Feburary 9, 2007" + + This option may require CONFIG_NETDEV_LATEINIT=y, otherwise the + power-up initialization may call the non-existent up_netinitialize(). + This option is not automatically selected because it may be that + you have an additional network device that requires the early + up_netinitialize() call. + +if NET_CDCECM + +menuconfig CDCECM_COMPOSITE + bool "CDC/ECM 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 !CDCECM_COMPOSITE + +# In a composite device the EP0 config comes from the composite device +# and the EP-Number is configured dynamically via composite_initialize + +config CDCECM_EP0MAXPACKET + int "Endpoint 0 max packet size" + default 64 + ---help--- + Endpoint 0 max packet size. Default 64. + +config CDCECM_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 # !CDCECM_COMPOSITE + +config CDCECM_EPINTIN_FSSIZE + int "Interupt 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 CDCECM_EPINTIN_HSSIZE + int "Interupt 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 !CDCECM_COMPOSITE + +# In a composite device the EP-Number is configured dynamically via +# composite_initialize + +config CDCECM_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 # !CDCECM_COMPOSITE + +config CDCECM_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 CDCECM_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 !CDCECM_COMPOSITE + +# In a composite device the EP-Number is configured dynamically via +# composite_initialize + +config CDCECM_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 # !CDCECM_COMPOSITE + +config CDCECM_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 CDCECM_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 !CDCECM_COMPOSITE + +# In a composite device the Vendor- and Product-ID is given by the composite +# device + +config CDCECM_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 ECM default VID. + +config CDCECM_PRODUCTID + hex "Product ID" + default 0xa4a2 + ---help--- + The product ID code/string. Default 0xa4a2 and "CDC/ECM Ethernet" + 0xa4a2 was selected for compatibility with the Linux CDC ECM + default PID. + +config CDCECM_VENDORSTR + string "Vendor string" + default "NuttX" + +config CDCECM_PRODUCTSTR + string "Product string" + default "CDC/ECM Ethernet" + +endif # !CDCECM_COMPOSITE +endif # CDCECM diff --git a/drivers/usbdev/Make.defs b/drivers/usbdev/Make.defs index b43d4e4340f..af53cde9193 100644 --- a/drivers/usbdev/Make.defs +++ b/drivers/usbdev/Make.defs @@ -61,6 +61,10 @@ ifeq ($(CONFIG_RNDIS),y) CSRCS += rndis.c endif +ifeq ($(CONFIG_NET_CDCECM),y) + CSRCS += cdcecm.c +endif + CSRCS += usbdev_trace.c usbdev_trprintf.c # Include USB device build support diff --git a/drivers/usbdev/cdcecm.c b/drivers/usbdev/cdcecm.c new file mode 100644 index 00000000000..19b4a272eb8 --- /dev/null +++ b/drivers/usbdev/cdcecm.c @@ -0,0 +1,2367 @@ +/**************************************************************************** + * drivers/net/cdcecm.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Authors: Michael Jung + * + * References: + * [CDCECM1.2] Universal Serial Bus - Communications Class - Subclass + * Specification for Ethernet Control Model Devices - Rev 1.2 + * + * This driver derives in part from the NuttX CDC/ACM driver: + * + * Copyright (C) 2011-2013, 2016-2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * and also from the NuttX RNDIS driver: + * + * Copyright (C) 2011-2017 Gregory Nutt. All rights reserved. + * Authors: Sakari Kapanen , + * Petteri Aimonen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * 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 + +#include "cdcecm.h" + +#ifdef CONFIG_NET_CDCECM + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Work queue support is required. */ + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE) +#else + +/* 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 +#endif + +/* CONFIG_CDCECM_NINTERFACES determines the number of physical interfaces + * that will be supported. + */ + +#ifndef CONFIG_CDCECM_NINTERFACES +# define CONFIG_CDCECM_NINTERFACES 1 +#endif + +/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */ + +#define CDCECM_WDDELAY (1*CLK_TCK) + +/* TX timeout = 1 minute */ + +#define CDCECM_TXTIMEOUT (60*CLK_TCK) + +/* This is a helper pointer for accessing the contents of the Ethernet header */ + +#define BUF ((struct eth_hdr_s *)self->dev.d_buf) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The cdcecm_driver_s encapsulates all state information for a single hardware + * interface + */ + +struct cdcecm_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_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 */ + + uint8_t pktbuf[CONFIG_NET_ETH_PKTSIZE + CONFIG_NET_GUARDSIZE]; + + struct usbdev_req_s *rdreq; /* Single read request */ + bool rxpending; /* Packet available in rdreq */ + + struct usbdev_req_s *wrreq; /* Single write request */ + sem_t wrreq_idle; /* Is the wrreq available? */ + bool txdone; /* Did a write request complete? */ + + /* Network device */ + + bool bifup; /* true:ifup false:ifdown */ + WDOG_ID txpoll; /* TX poll timer */ + struct work_s irqwork; /* For deferring interrupt work + * to the work queue */ + struct work_s pollwork; /* For deferring poll work to + * the work queue */ + + /* This holds the information visible to the NuttX network */ + + struct net_driver_s dev; /* Interface understood by the + * network */ + bool registered; /* netdev is currently registered */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Network Device ***********************************************************/ + +/* Common TX logic */ + +static int cdcecm_transmit(FAR struct cdcecm_driver_s *priv); +static int cdcecm_txpoll(FAR struct net_driver_s *dev); + +/* Interrupt handling */ + +static void cdcecm_reply(struct cdcecm_driver_s *priv); +static void cdcecm_receive(FAR struct cdcecm_driver_s *priv); +static void cdcecm_txdone(FAR struct cdcecm_driver_s *priv); + +static void cdcecm_interrupt_work(FAR void *arg); + +/* Watchdog timer expirations */ + +static void cdcecm_poll_work(FAR void *arg); +static void cdcecm_poll_expiry(int argc, wdparm_t arg, ...); + +/* NuttX callback functions */ + +static int cdcecm_ifup(FAR struct net_driver_s *dev); +static int cdcecm_ifdown(FAR struct net_driver_s *dev); + +static void cdcecm_txavail_work(FAR void *arg); +static int cdcecm_txavail(FAR struct net_driver_s *dev); + +#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +static int cdcecm_addmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac); +#ifdef CONFIG_NET_IGMP +static int cdcecm_rmmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac); +#endif +#ifdef CONFIG_NET_ICMPv6 +static void cdcecm_ipv6multicast(FAR struct cdcecm_driver_s *priv); +#endif +#endif +#ifdef CONFIG_NETDEV_IOCTL +static int cdcecm_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif + +/* USB Device Class Driver **************************************************/ + +/* USB Device Class methods */ + +static int cdcecm_bind(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev); + +static void cdcecm_unbind(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev); + +static int cdcecm_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 cdcecm_disconnect(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev); + +/* USB Device Class helpers */ + +static struct usbdev_req_s *cdcecm_allocreq(FAR struct usbdev_ep_s *ep, + uint16_t len); +static void cdcecm_freereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); + +static void cdcecm_ep0incomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); +static void cdcecm_rdcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); +static void cdcecm_wrcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); + +static void cdcecm_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 = +{ + cdcecm_bind, + cdcecm_unbind, + cdcecm_setup, + cdcecm_disconnect, + NULL, + NULL +}; + +#ifndef CONFIG_CDCECM_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_ECM, + CDC_PROTO_NONE, + CONFIG_CDCECM_EP0MAXPACKET, + { + LSBYTE(CONFIG_CDCECM_VENDORID), + MSBYTE(CONFIG_CDCECM_VENDORID) + }, + { + LSBYTE(CONFIG_CDCECM_PRODUCTID), + MSBYTE(CONFIG_CDCECM_PRODUCTID) + }, + { + LSBYTE(CDCECM_VERSIONNO), + MSBYTE(CDCECM_VERSIONNO) + }, + CDCECM_MANUFACTURERSTRID, + CDCECM_PRODUCTSTRID, + CDCECM_SERIALSTRID, + CDCECM_NCONFIGS +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_transmit(FAR struct cdcecm_driver_s *self) +{ + /* Wait until the USB device request for Ethernet frame transmissions becomes + * available. + */ + + while (nxsem_wait(&self->wrreq_idle) != OK) + { + } + + /* Increment statistics */ + + NETDEV_TXPACKETS(self->dev); + + /* Send the packet: address=priv->dev.d_buf, length=priv->dev.d_len */ + + memcpy(self->wrreq->buf, self->dev.d_buf, self->dev.d_len); + self->wrreq->len = self->dev.d_len; + + return EP_SUBMIT(self->epbulkin, self->wrreq); +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_txpoll(FAR struct net_driver_s *dev) +{ + FAR struct cdcecm_driver_s *priv = + (FAR struct cdcecm_driver_s *)dev->d_private; + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Look up the destination MAC address and add it to the Ethernet + * header. + */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) +#endif + { + arp_out(&priv->dev); + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + neighbor_out(&priv->dev); + } +#endif /* CONFIG_NET_IPv6 */ + + /* Send the packet */ + + cdcecm_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; + } + + /* If zero is returned, the polling will continue until all connections + * have been examined. + */ + + return 0; +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_reply(struct cdcecm_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) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + /* Check for an outgoing IPv4 packet */ + + if (IFF_IS_IPv4(priv->dev.d_flags)) +#endif + { + arp_out(&priv->dev); + } +#endif + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + /* Otherwise, it must be an outgoing IPv6 packet */ + + else +#endif + { + neighbor_out(&priv->dev); + } +#endif + + /* And send the packet */ + + cdcecm_transmit(priv); + } +} + +/**************************************************************************** + * Name: cdcecm_receive + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void cdcecm_receive(FAR struct cdcecm_driver_s *self) +{ + /* Check for errors and update statistics */ + + /* Check if the packet is a valid size for the network buffer + * configuration. + */ + + /* 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, self->rdreq->xfrd); + self->dev.d_len = self->rdreq->xfrd; + +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the packet 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); + + /* Handle ARP on input, then dispatch IPv4 packet to the network + * layer. + */ + + arp_ipin(&self->dev); + ipv4_input(&self->dev); + + /* Check for a reply to the IPv4 packet */ + + cdcecm_reply(self); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("Iv6 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 */ + + cdcecm_reply(self); + } + else +#endif +#ifdef CONFIG_NET_ARP + if (BUF->type == htons(ETHTYPE_ARP)) + { + /* Dispatch ARP packet to the network layer */ + + arp_arpin(&self->dev); + NETDEV_RXARP(&self->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a value > 0. + */ + + if (self->dev.d_len > 0) + { + cdcecm_transmit(self); + } + } + else +#endif + { + NETDEV_RXDROPPED(&self->dev); + } +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_txdone(FAR struct cdcecm_driver_s *priv) +{ + /* Check for errors and update statistics */ + + NETDEV_TXDONE(priv->dev); + + /* In any event, poll the network for new TX data */ + + (void)devif_poll(&priv->dev, cdcecm_txpoll); +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_interrupt_work(FAR void *arg) +{ + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_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 cdcecm_receive() */ + + if (self->rxpending) + { + cdcecm_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 cdcecm_txdone. + * This may disable further Tx interrupts if there are no pending + * transmissions. + */ + + if (self->txdone) + { + flags = enter_critical_section(); + self->txdone = false; + leave_critical_section(flags); + + cdcecm_txdone(self); + } + + net_unlock(); +} + +/**************************************************************************** + * Name: cdcecm_poll_work + * + * Description: + * Perform periodic polling from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Run on a work queue thread. + * + ****************************************************************************/ + +static void cdcecm_poll_work(FAR void *arg) +{ + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)arg; + + ninfo("rxpending: %d, txdone: %d\n", self->rxpending, self->txdone); + + /* 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(); + + /* Perform the poll. We are always able to accept another packet, since + * cdcecm_transmit will just wait until the USB device write request will + * become available. + */ + + (void)devif_timer(&self->dev, cdcecm_txpoll); + + /* Setup the watchdog poll timer again */ + + (void)wd_start(self->txpoll, CDCECM_WDDELAY, cdcecm_poll_expiry, 1, + (wdparm_t)self); + + net_unlock(); +} + +/**************************************************************************** + * Name: cdcecm_poll_expiry + * + * Description: + * Periodic timer handler. Called from the timer interrupt handler. + * + * Input Parameters: + * argc - The number of available arguments + * arg - The first argument + * + * Returned Value: + * None + * + * Assumptions: + * Runs in the context of a the timer interrupt handler. Local + * interrupts are disabled by the interrupt logic. + * + ****************************************************************************/ + +static void cdcecm_poll_expiry(int argc, wdparm_t arg, ...) +{ + FAR struct cdcecm_driver_s *priv = (FAR struct cdcecm_driver_s *)arg; + + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(ETHWORK, &priv->pollwork, cdcecm_poll_work, priv, 0); +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_ifup(FAR struct net_driver_s *dev) +{ + FAR struct cdcecm_driver_s *priv = + (FAR struct cdcecm_driver_s *)dev->d_private; + +#ifdef CONFIG_NET_IPv4 + ninfo("Bringing up: %d.%d.%d.%d\n", + dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff, + (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24); +#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 + + /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */ + + /* Instantiate the MAC address from priv->dev.d_mac.ether.ether_addr_octet */ + +#ifdef CONFIG_NET_ICMPv6 + /* Set up IPv6 multicast address filtering */ + + cdcecm_ipv6multicast(priv); +#endif + + /* Set and activate a timer process */ + + (void)wd_start(priv->txpoll, CDCECM_WDDELAY, cdcecm_poll_expiry, 1, + (wdparm_t)priv); + + priv->bifup = true; + return OK; +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_ifdown(FAR struct net_driver_s *dev) +{ + FAR struct cdcecm_driver_s *priv = + (FAR struct cdcecm_driver_s *)dev->d_private; + irqstate_t flags; + + /* Disable the Ethernet interrupt */ + + flags = enter_critical_section(); + + /* Cancel the TX poll timer and TX timeout timers */ + + wd_cancel(priv->txpoll); + + /* Put the EMAC in its reset, non-operational state. This should be + * a known configuration that will guarantee the cdcecm_ifup() always + * successfully brings the interface back up. + */ + + /* Mark the device "down" */ + + priv->bifup = false; + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_txavail_work(FAR void *arg) +{ + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_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) + { + (void)devif_poll(&self->dev, cdcecm_txpoll); + } + + net_unlock(); +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_txavail(FAR struct net_driver_s *dev) +{ + FAR struct cdcecm_driver_s *priv = + (FAR struct cdcecm_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, cdcecm_txavail_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Name: cdcecm_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_IGMP) || defined(CONFIG_NET_ICMPv6) +static int cdcecm_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) +{ + FAR struct cdcecm_driver_s *priv = + (FAR struct cdcecm_driver_s *)dev->d_private; + + /* Add the MAC address to the hardware multicast routing table */ + + UNUSED(priv); /* Not yet implemented */ + return OK; +} +#endif + +/**************************************************************************** + * Name: cdcecm_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_IGMP +static int cdcecm_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) +{ + FAR struct cdcecm_driver_s *priv = + (FAR struct cdcecm_driver_s *)dev->d_private; + + /* Add the MAC address to the hardware multicast routing table */ + + UNUSED(priv); /* Not yet implemented */ + return OK; +} +#endif + +/**************************************************************************** + * Name: cdcecm_ipv6multicast + * + * Description: + * Configure the IPv6 multicast MAC address. + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6 +static void cdcecm_ipv6multicast(FAR struct cdcecm_driver_s *priv) +{ + FAR struct net_driver_s *dev; + uint16_t tmp16; + uint8_t mac[6]; + + /* For ICMPv6, we need to add the IPv6 multicast address + * + * For IPv6 multicast addresses, the Ethernet MAC is derived by + * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00, + * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map + * to the Ethernet MAC address 33:33:00:01:00:03. + * + * NOTES: This appears correct for the ICMPv6 Router Solicitation + * Message, but the ICMPv6 Neighbor Solicitation message seems to + * use 33:33:ff:01:00:03. + */ + + mac[0] = 0x33; + mac[1] = 0x33; + + dev = &priv->dev; + tmp16 = dev->d_ipv6addr[6]; + mac[2] = 0xff; + mac[3] = tmp16 >> 8; + + tmp16 = dev->d_ipv6addr[7]; + mac[4] = tmp16 & 0xff; + mac[5] = tmp16 >> 8; + + ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + (void)cdcecm_addmac(dev, mac); + +#ifdef CONFIG_NET_ICMPv6_AUTOCONF + /* Add the IPv6 all link-local nodes Ethernet address. This is the + * address that we expect to receive ICMPv6 Router Advertisement + * packets. + */ + + (void)cdcecm_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet); + +#endif /* CONFIG_NET_ICMPv6_AUTOCONF */ + +#ifdef CONFIG_NET_ICMPv6_ROUTER + /* Add the IPv6 all link-local routers Ethernet address. This is the + * address that we expect to receive ICMPv6 Router Solicitation + * packets. + */ + + (void)cdcecm_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet); + +#endif /* CONFIG_NET_ICMPv6_ROUTER */ +} +#endif /* CONFIG_NET_ICMPv6 */ + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg) +{ + FAR struct cdcecm_driver_s *priv = + (FAR struct cdcecm_driver_s *)dev->d_private; + int ret; + + /* Decode and dispatch the driver-specific IOCTL command */ + + switch (cmd) + { + /* Add cases here to support the IOCTL commands */ + + default: + nerr("ERROR: Unrecognized IOCTL command: %d\n", command); + return -ENOTTY; /* Special return value for this case */ + } + + return OK; +} +#endif + +/**************************************************************************** + * USB Device Class Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcecm_ep0incomplete + * + * Description: + * Handle completion of EP0 control operations + * + ****************************************************************************/ + +static void cdcecm_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: cdcecm_rdcomplete + * + * Description: + * Handle completion of read request on the bulk OUT endpoint. + * + ****************************************************************************/ + +static void cdcecm_rdcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_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, cdcecm_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: cdcecm_wrcomplete + * + * Description: + * Handle completion of write request. This function probably executes + * in the context of an interrupt handler. + * + ****************************************************************************/ + +static void cdcecm_wrcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_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, cdcecm_interrupt_work, self, 0); +} + +/**************************************************************************** + * Name: cdcecm_allocreq + * + * Description: + * Allocate a request instance along with its buffer + * + ****************************************************************************/ + +static struct usbdev_req_s *cdcecm_allocreq(FAR struct usbdev_ep_s *ep, + uint16_t len) +{ + FAR struct usbdev_req_s *req; + + req = EP_ALLOCREQ(ep); + + if (req != NULL) + { + req->len = len; + req->buf = EP_ALLOCBUFFER(ep, len); + req->flags = USBDEV_REQFLAGS_NULLPKT; + + if (req->buf == NULL) + { + EP_FREEREQ(ep, req); + req = NULL; + } + } + + return req; +} + +/**************************************************************************** + * Name: cdcecm_freereq + * + * Description: + * Free a request instance along with its buffer + * + ****************************************************************************/ + +static void cdcecm_freereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + if (ep != NULL && req != NULL) + { + if (req->buf != NULL) + { + EP_FREEBUFFER(ep, req->buf); + } + + EP_FREEREQ(ep, req); + } +} + +/****************************************************************************** + * Name: cdcecm_resetconfig + * + * Description: + * Mark the device as not configured and disable all endpoints. + * + ******************************************************************************/ + +static void cdcecm_resetconfig(FAR struct cdcecm_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); + } +} + +/****************************************************************************** + * Name: cdcecm_setconfig + * + * Set the device configuration by allocating and configuring endpoints and + * by allocating and queue read and write requests. + * + ****************************************************************************/ + +static int cdcecm_setconfig(FAR struct cdcecm_driver_s *self, uint8_t config) +{ + struct usb_epdesc_s epdesc; + int ret = OK; + + if (config == self->config) + { + return OK; + } + + cdcecm_resetconfig(self); + + if (config == CDCECM_CONFIGID_NONE) + { + return OK; + } + + if (config != CDCECM_CONFIGID) + { + return -EINVAL; + } + + cdcecm_mkepdesc(CDCECM_EP_INTIN_IDX, &epdesc, &self->devinfo, false); + ret = EP_CONFIGURE(self->epint, &epdesc, false); + + if (ret < 0) + { + goto error; + } + + self->epint->priv = self; + + cdcecm_mkepdesc(CDCECM_EP_BULKIN_IDX, &epdesc, &self->devinfo, false); + ret = EP_CONFIGURE(self->epbulkin, &epdesc, false); + + if (ret < 0) + { + goto error; + } + + self->epbulkin->priv = self; + + cdcecm_mkepdesc(CDCECM_EP_BULKOUT_IDX, &epdesc, &self->devinfo, false); + 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 = cdcecm_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: + cdcecm_resetconfig(self); + return ret; +} + +/****************************************************************************** + * Name: cdcecm_setinterface + * + ****************************************************************************/ + +static int cdcecm_setinterface(FAR struct cdcecm_driver_s *self, + uint16_t interface, uint16_t altsetting) +{ + uinfo("interface: %hu, altsetting: %hu\n", interface, altsetting); + return OK; +} + +/**************************************************************************** + * Name: cdcecm_mkstrdesc + * + * Description: + * Construct a string descriptor + * + ****************************************************************************/ + +static int cdcecm_mkstrdesc(uint8_t id, FAR struct usb_strdesc_s *strdesc) +{ + const char *str; + int len; + int ndata; + int i; + + switch (id) + { +#ifndef CONFIG_CDCECM_COMPOSITE + case 0: + { + /* Descriptor 0 is the language id */ + + strdesc->len = 4; + strdesc->type = USB_DESC_TYPE_STRING; + strdesc->data[0] = LSBYTE(CDCECM_STR_LANGUAGE); + strdesc->data[1] = MSBYTE(CDCECM_STR_LANGUAGE); + return 4; + } + + case CDCECM_MANUFACTURERSTRID: + str = CONFIG_CDCECM_VENDORSTR; + break; + + case CDCECM_PRODUCTSTRID: + str = CONFIG_CDCECM_PRODUCTSTR; + break; + + case CDCECM_SERIALSTRID: + str = "0"; + break; + + case CDCECM_CONFIGSTRID: + str = "Default"; + break; +#endif + + case CDCECM_MACSTRID: + str = "020000112233"; + break; + + default: + uwarn("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) + { + strdesc->data[ndata] = str[i]; + strdesc->data[ndata + 1] = 0; + } + + strdesc->len = ndata + 2; + strdesc->type = USB_DESC_TYPE_STRING; + return strdesc->len; +} + +/**************************************************************************** + * Name: cdcecm_mkepdesc + * + * Description: + * Construct the endpoint descriptor + * + ****************************************************************************/ + +static void cdcecm_mkepdesc(int epidx, + FAR struct usb_epdesc_s *epdesc, + FAR struct usbdev_devinfo_s *devinfo, + bool hispeed) +{ + uint16_t intin_mxpktsz = CONFIG_CDCECM_EPINTIN_FSSIZE; + uint16_t bulkout_mxpktsz = CONFIG_CDCECM_EPBULKOUT_FSSIZE; + uint16_t bulkin_mxpktsz = CONFIG_CDCECM_EPBULKIN_FSSIZE; + +#ifdef CONFIG_USBDEV_DUALSPEED + if (hispeed) + { + intin_mxpktsz = CONFIG_CDCECM_EPINTIN_HSSIZE; + bulkout_mxpktsz = CONFIG_CDCECM_EPBULKOUT_HSSIZE; + bulkin_mxpktsz = CONFIG_CDCECM_EPBULKIN_HSSIZE; + } +#else + UNUSED(hispeed); +#endif + + epdesc->len = USB_SIZEOF_EPDESC; /* Descriptor length */ + epdesc->type = USB_DESC_TYPE_ENDPOINT; /* Descriptor type */ + + switch (epidx) + { + case CDCECM_EP_INTIN_IDX: /* Interrupt IN endpoint */ + { + epdesc->addr = USB_DIR_IN | devinfo->epno[CDCECM_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 CDCECM_EP_BULKIN_IDX: + { + epdesc->addr = USB_DIR_IN | devinfo->epno[CDCECM_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 CDCECM_EP_BULKOUT_IDX: + { + epdesc->addr = USB_DIR_OUT | devinfo->epno[CDCECM_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: + DEBUGASSERT(false); + } +} + +/**************************************************************************** + * Name: cdcecm_mkcfgdesc + * + * Description: + * Construct the config descriptor + * + ****************************************************************************/ + +static int16_t cdcecm_mkcfgdesc(FAR uint8_t *desc, + FAR struct usbdev_devinfo_s *devinfo) +{ + FAR struct usb_cfgdesc_s *cfgdesc = NULL; + int16_t len = 0; + +#ifndef CONFIG_CDCECM_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 + CDCECM_CONFIGSTRID; + cfgdesc->attr = USB_CONFIG_ATTR_ONE | CDCECM_SELFPOWERED | + CDCECM_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 = (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_ECM; /* 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 = (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_ECM; + 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 usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)desc; + + cdcecm_mkepdesc(CDCECM_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 = (FAR struct usb_ifdesc_s *)desc; + + 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 = CDC_SUBCLASS_ECM; + ifdesc->protocol = CDC_PROTO_NONE; + ifdesc->iif = 0; + + desc += USB_SIZEOF_IFDESC; + } + + len += USB_SIZEOF_IFDESC; + + if (desc) + { + FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)desc; + + 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 = CDC_SUBCLASS_ECM; + ifdesc->protocol = CDC_PROTO_NONE; + 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; + + cdcecm_mkepdesc(CDCECM_EP_BULKIN_IDX, epdesc, devinfo, false); + desc += USB_SIZEOF_EPDESC; + } + + len += USB_SIZEOF_EPDESC; + + if (desc) + { + FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)desc; + + cdcecm_mkepdesc(CDCECM_EP_BULKOUT_IDX, epdesc, devinfo, false); + 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: cdcecm_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 cdcecm_getdescriptor(FAR struct cdcecm_driver_s *self, uint8_t type, + uint8_t index, FAR void *desc) +{ + uinfo("type: 0x%02hhx, index: 0x%02hhx\n", type, index); + + switch (type) + { +#ifndef CONFIG_CDCECM_COMPOSITE + case USB_DESC_TYPE_DEVICE: + { + memcpy(desc, &g_devdesc, sizeof(g_devdesc)); + return (int)sizeof(g_devdesc); + } + break; +#endif + + case USB_DESC_TYPE_CONFIG: + { + return cdcecm_mkcfgdesc((FAR uint8_t *)desc, &self->devinfo); + } + break; + + case USB_DESC_TYPE_STRING: + { + return cdcecm_mkstrdesc(index, (FAR struct usb_strdesc_s *)desc); + } + break; + + default: + uwarn("Unsupported descriptor type: 0x%02hhx\n", type); + break; + } + + return -ENOTSUP; +} + +/**************************************************************************** + * USB Device Class Methods + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcecm_bind + * + * Description: + * Invoked when the driver is bound to an USB device + * + ****************************************************************************/ + +static int cdcecm_bind(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev) +{ + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)driver; + int ret = OK; + + uinfo("\n"); + + dev->ep0->priv = self; + + /* Preallocate control request */ + + self->ctrlreq = cdcecm_allocreq(dev->ep0, CDCECM_MXDESCLEN); + + if (self->ctrlreq == NULL) + { + ret = -ENOMEM; + goto error; + } + + self->ctrlreq->callback = cdcecm_ep0incomplete; + + self->epint = DEV_ALLOCEP(dev, USB_DIR_IN | self->devinfo.epno[CDCECM_EP_INTIN_IDX], + true, USB_EP_ATTR_XFER_INT); + self->epbulkin = DEV_ALLOCEP(dev, USB_DIR_IN | self->devinfo.epno[CDCECM_EP_BULKIN_IDX], + true, USB_EP_ATTR_XFER_BULK); + self->epbulkout = DEV_ALLOCEP(dev, USB_DIR_OUT | self->devinfo.epno[CDCECM_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 read requests. The buffer size is one full packet. */ + + self->rdreq = cdcecm_allocreq(self->epbulkout, + CONFIG_NET_ETH_PKTSIZE + CONFIG_NET_GUARDSIZE); + if (self->rdreq == NULL) + { + uerr("Out of memory\n"); + ret = -ENOMEM; + goto error; + } + + self->rdreq->callback = cdcecm_rdcomplete; + + /* Pre-allocate a single write request. Buffer size is one full packet. */ + + self->wrreq = cdcecm_allocreq(self->epbulkin, + CONFIG_NET_ETH_PKTSIZE + CONFIG_NET_GUARDSIZE); + if (self->wrreq == NULL) + { + uerr("Out of memory\n"); + ret = -ENOMEM; + goto error; + } + + self->wrreq->callback = cdcecm_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_CDCECM_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("cdcecm_bind failed! ret: %d\n", ret); + cdcecm_unbind(driver, dev); + return ret; +} + +static void cdcecm_unbind(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev) +{ + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_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) + */ + + cdcecm_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) + { + cdcecm_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) + { + cdcecm_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) + { + cdcecm_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 cdcecm_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 cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)driver; + uint16_t value = GETUINT16(ctrl->value); + uint16_t index = GETUINT16(ctrl->index); + uint16_t len = GETUINT16(ctrl->len); + int ret = -EOPNOTSUPP; + + uinfo("\n"); + + 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 = cdcecm_getdescriptor(self, desctype, descindex, self->ctrlreq->buf); + } + break; + + case USB_REQ_SETCONFIGURATION: + ret = cdcecm_setconfig(self, value); + break; + + case USB_REQ_SETINTERFACE: + ret = cdcecm_setinterface(self, index, value); + break; + + default: + uwarn("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 CDCECM 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; + + default: + uwarn("Unsupported class req: 0x%02hhx\n", ctrl->req); + break; + } + } + else + { + uwarn("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; + cdcecm_ep0incomplete(dev->ep0, ctrlreq); + } + } + + return ret; +} + +static void cdcecm_disconnect(FAR struct usbdevclass_driver_s *driver, + FAR struct usbdev_s *dev) +{ + uinfo("\n"); +} + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_classobject(int minor, FAR struct usbdev_devinfo_s *devinfo, + FAR struct usbdevclass_driver_s **classdev) +{ + FAR struct cdcecm_driver_s *self; + int ret; + + /* Initialize the driver structure */ + + self = kmm_zalloc(sizeof(struct cdcecm_driver_s)); + if (!self) + { + nerr("Out of memory!\n"); + return -ENOMEM; + } + + /* Network device initialization */ + + self->dev.d_buf = self->pktbuf; + self->dev.d_ifup = cdcecm_ifup; /* I/F up (new IP address) callback */ + self->dev.d_ifdown = cdcecm_ifdown; /* I/F down callback */ + self->dev.d_txavail = cdcecm_txavail; /* New TX data callback */ +#ifdef CONFIG_NET_IGMP + self->dev.d_addmac = cdcecm_addmac; /* Add multicast MAC address */ + self->dev.d_rmmac = cdcecm_rmmac; /* Remove multicast MAC address */ +#endif +#ifdef CONFIG_NETDEV_IOCTL + self->dev.d_ioctl = cdcecm_ioctl; /* Handle network IOCTL commands */ +#endif + self->dev.d_private = (FAR void *)self; /* Used to recover private state from dev */ + + /* Create a watchdog for timing polling for and timing of transmissions */ + + self->txpoll = wd_create(); /* Create periodic poll timer */ + + DEBUGASSERT(self->txpoll != NULL); + + /* 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 cdcecm_ifdown(). + */ + + cdcecm_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); + return ret; + } + + self->registered = true; + + *classdev = (FAR struct usbdevclass_driver_s *)self; + return ret; +} + +/**************************************************************************** + * Name: cdcecm_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 cdcacm_classobject() + * handle - The opaque handle representing the class object returned by + * a previous call to cdcacm_initialize(). + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_CDCECM_COMPOSITE +void cdcecm_uninitialize(FAR struct usbdevclass_driver_s *classdev) +#else +void cdcecm_uninitialize(FAR void *handle) +#endif +{ +#ifdef CONFIG_CDCECM_COMPOSITE + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)classdev; +#else + FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)handle; +#endif + int ret; + +#ifdef CONFIG_CDCECM_COMPOSITE + /* Check for pass 2 uninitialization. We did most of the work on the + * first pass uninitialization. + */ + + if (!self->registered) + { + /* In this second and final pass, all that remains to be done is to + * free the memory resources. + */ + + kmm_free(self); + return; + } +#endif + + /* Un-register the CDC/ECM netdev device */ + + ret = netdev_unregister(&self->dev); + if (ret < 0) + { + nerr("ERROR: netdev_unregister failed. ret: %d\n", ret); + } + + /* For the case of the composite driver, there is a two pass + * uninitialization sequence. We cannot yet free the driver structure. + * We will do that on the second pass. We mark the fact that we have + * already uninitialized by setting the registered flag to false. + * If/when we are called again, then we will free the memory resources. + */ + + self->registered = false; /* Successfully unregistered netdev */ + + /* Unregister the driver (unless we are a part of a composite device). The + * device unregister logic will (1) return all of the requests to us then + * (2) call the unbind method. + * + * The same thing will happen in the composite case except that: (1) the + * composite driver will call usbdev_unregister() which will (2) return the + * requests for all members of the composite, and (3) call the unbind + * method in the composite device which will (4) call the unbind method + * for this device. + */ + +#ifndef CONFIG_CDCECM_COMPOSITE + usbdev_unregister(&self->usbdev); + + /* And free the driver structure */ + + kmm_free(self); +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_uninitialize(). + * + * Returned Value: + * Zero (OK) means that the driver was successfully registered. On any + * failure, a negated errno value is returned. + * + ****************************************************************************/ + +#ifndef CONFIG_CDCECM_COMPOSITE +int cdcecm_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[CDCECM_EP_INTIN_IDX] = CONFIG_CDCECM_EPINTIN; + devinfo.epno[CDCECM_EP_BULKIN_IDX] = CONFIG_CDCECM_EPBULKIN; + devinfo.epno[CDCECM_EP_BULKOUT_IDX] = CONFIG_CDCECM_EPBULKOUT; + + ret = cdcecm_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: cdcecm_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_CDCECM_COMPOSITE +void cdcecm_get_composite_devdesc(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 = cdcecm_mkcfgdesc; + dev->mkstrdesc = cdcecm_mkstrdesc; + dev->classobject = cdcecm_classobject; + dev->uninitialize = cdcecm_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 the config descriptor */ + +#ifdef CONFIG_USBDEV_DUALSPEED + dev->cfgdescsize = cdcecm_mkcfgdesc(NULL, NULL, USB_SPEED_UNKNOWN, 0); +#else + dev->cfgdescsize = cdcecm_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_CDCECM_COMPOSITE */ + +#endif /* CONFIG_NET_CDCECM */ diff --git a/drivers/usbdev/cdcecm.h b/drivers/usbdev/cdcecm.h new file mode 100644 index 00000000000..10e07c458f1 --- /dev/null +++ b/drivers/usbdev/cdcecm.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * drivers/usbdev/cdcecm.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Michael Jung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __DRIVERS_USBDEV_CDCECM_H +#define __DRIVERS_USBDEV_CDCECM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CDCECM_VERSIONNO (0x0100) +#define CDCECM_MXDESCLEN (80) +#define CDCECM_MAXSTRLEN (CDCECM_MXDESCLEN - 2) +#define CDCECM_NCONFIGS (1) +#define CDCECM_NINTERFACES (2) +#define CDCECM_NUM_EPS (3) + +#define CDCECM_MANUFACTURERSTRID (1) +#define CDCECM_PRODUCTSTRID (2) +#define CDCECM_SERIALSTRID (3) +#define CDCECM_CONFIGSTRID (4) +#define CDCECM_MACSTRID (5) +#define CDCECM_NSTRIDS (5) + +#define CDCECM_STR_LANGUAGE (0x0409) /* en-us */ + +#define CDCECM_CONFIGID_NONE (0) +#define CDCECM_CONFIGID (1) + +#define CDCECM_SELFPOWERED (0) +#define CDCECM_REMOTEWAKEUP (0) + +#ifndef MIN +# define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +#endif /* __DRIVERS_USBDEV_CDCECM_H */ diff --git a/include/nuttx/usb/cdcecm.h b/include/nuttx/usb/cdcecm.h new file mode 100644 index 00000000000..e398fd556f7 --- /dev/null +++ b/include/nuttx/usb/cdcecm.h @@ -0,0 +1,120 @@ +/******************************************************************************* + * include/nuttx/usb/cdcecm.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Michael Jung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef __INCLUDE_NUTTX_USB_CDCECM_H +#define __INCLUDE_NUTTX_USB_CDCECM_H + +/****************************************************************************** + * Included Files + ******************************************************************************/ + +#include + +#ifdef CONFIG_CDCECM_COMPOSITE +# include +#endif + +/**************************************************************************** + * Preprocessor definitions + ****************************************************************************/ + +#define CDCECM_EP_INTIN_IDX (0) +#define CDCECM_EP_BULKIN_IDX (1) +#define CDCECM_EP_BULKOUT_IDX (2) + +/****************************************************************************** + * Public Data + ******************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +# define EXTERN extern "C" +extern "C" +{ +#else +# define EXTERN extern +#endif + +/****************************************************************************** + * Public Functions + ******************************************************************************/ + +/**************************************************************************** + * Name: cdcecm_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 cdcecm_uninitialize(). + * + * Returned Value: + * Zero (OK) means that the driver was successfully registered. On any + * failure, a negated errno value is returned. + * + ****************************************************************************/ + +#if !defined(CONFIG_CDCECM_COMPOSITE) +int cdcecm_initialize(int minor, FAR void **handle); +#endif + +/**************************************************************************** + * Name: cdcecm_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_CDCECM_COMPOSITE +void cdcecm_get_composite_devdesc(struct composite_devdesc_s *dev); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_USB_CDCECM_H */