You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/06/15 13:13:28 UTC
[incubator-nuttx] 01/06: Add support to CDC-MBIM USB host driver
This is an automated email from the ASF dual-hosted git repository.
gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 07c0faff59b4505f48296282f0d403abf0030868
Author: Adam Porter <po...@gmail.com>
AuthorDate: Sun Jun 14 10:10:11 2020 -0300
Add support to CDC-MBIM USB host driver
This driver was created by Adam Porter and posted on NuttX
mailing list at Google Group on Nov 14 2019
---
drivers/usbhost/Kconfig | 9 +
drivers/usbhost/Make.defs | 4 +
drivers/usbhost/usbhost_cdcmbim.c | 2578 +++++++++++++++++++++++++++++++++++++
include/nuttx/net/net.h | 3 +-
include/nuttx/usb/cdc.h | 221 ++--
net/netdev/netdev_register.c | 6 +
6 files changed, 2740 insertions(+), 81 deletions(-)
diff --git a/drivers/usbhost/Kconfig b/drivers/usbhost/Kconfig
index 0c389b3..d9678d1 100644
--- a/drivers/usbhost/Kconfig
+++ b/drivers/usbhost/Kconfig
@@ -262,6 +262,15 @@ config USBHOST_CDCACM_TXBUFSIZE
endif # USBHOST_CDCACM
+config USBHOST_CDCMBIM
+ bool "CDC/MBIM support"
+ default n
+ depends on USBHOST_HAVE_ASYNCH && !USBHOST_BULK_DISABLE && !USBHOST_INT_DISABLE
+ select USBHOST_ASYNCH
+ ---help---
+ Select this option to build in host support for CDC/MBIM network
+ devices.
+
config USBHOST_HIDKBD
bool "HID Keyboard Class Support"
default n
diff --git a/drivers/usbhost/Make.defs b/drivers/usbhost/Make.defs
index 332e83c..bf9320a 100644
--- a/drivers/usbhost/Make.defs
+++ b/drivers/usbhost/Make.defs
@@ -58,6 +58,10 @@ ifeq ($(CONFIG_USBHOST_CDCACM),y)
CSRCS += usbhost_cdcacm.c
endif
+ifeq ($(CONFIG_USBHOST_CDCMBIM),y)
+CSRCS += usbhost_cdcmbim.c
+endif
+
ifeq ($(CONFIG_USBHOST_HIDKBD),y)
CSRCS += usbhost_hidkbd.c
endif
diff --git a/drivers/usbhost/usbhost_cdcmbim.c b/drivers/usbhost/usbhost_cdcmbim.c
new file mode 100644
index 0000000..75e5539
--- /dev/null
+++ b/drivers/usbhost/usbhost_cdcmbim.c
@@ -0,0 +1,2578 @@
+/****************************************************************************
+ * drivers/usbhost/usbhost_cdcmbim.c
+ *
+ * Copyright (C) 2018 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gn...@nuttx.org>
+ * Adam Porter <po...@gmail.com>
+ *
+ * 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 <nuttx/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+#include <poll.h>
+#include <fcntl.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/kthread.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/netdev.h>
+
+#include <nuttx/usb/cdc.h>
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbhost.h>
+
+#define CDCMBIM_NETBUF_SIZE 8192
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* put in cdc.h */
+#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00
+#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01
+#define USB_CDC_GET_NTB_PARAMETERS 0x80
+#define USB_CDC_SET_NTB_INPUT_SIZE 0x86
+#define USB_CDC_SET_NTB_FORMAT 0x83
+#define USB_CDC_SET_CRC_MODE 0x8a
+#define USB_CDC_SET_MAX_DATAGRAM_SIZE 0x88
+
+#define USB_CDC_NCM_NTB16_SUPPORTED (1 << 0)
+#define USB_CDC_NCM_NTB32_SUPPORTED (1 << 1)
+#define USB_CDC_NCM_NTB16_FORMAT 0x00
+#define USB_CDC_NCM_NTB32_FORMAT 0x01
+#define USB_CDC_NCM_DATAGRAM_FORMAT_CRC 0x30
+#define USB_CDC_NCM_DATAGRAM_FORMAT_NOCRC 0x31
+#define USB_CDC_NCM_CRC_NOT_APPENDED 0x00
+#define USB_CDC_NCM_CRC_APPENDED 0x01
+#define USB_CDC_NCM_NTH16_SIGNATURE 0x484D434E
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+#ifndef CONFIG_USBHOST_ASYNCH
+# warning Asynchronous transfer support is required (CONFIG_USBHOST_ASYNCH)
+#endif
+
+#ifdef CONFIG_USBHOST_CDCMBIM_NTDELAY
+# define USBHOST_CDCMBIM_NTDELAY MSEC2TICK(CONFIG_USBHOST_CDCMBIM_NTDELAY)
+#else
+# define USBHOST_CDCMBIM_NTDELAY MSEC2TICK(200)
+#endif
+
+#ifndef CONFIG_USBHOST_CDCMBIM_NPOLLWAITERS
+# define CONFIG_USBHOST_CDCMBIM_NPOLLWAITERS 1
+#endif
+
+/* Driver support ***********************************************************/
+
+/* This format is used to construct the /dev/cdc-wdm[n] device driver path.
+ * It defined here so that it will be used consistently in all places.
+ */
+
+#define DEV_FORMAT "/dev/cdc-wdm%d"
+#define DEV_NAMELEN 16
+
+/* Used in usbhost_cfgdesc() */
+
+#define USBHOST_CTRLIFFOUND 0x01
+#define USBHOST_DATAIFFOUND 0x02
+#define USBHOST_INTRIFFOUND 0x04
+#define USBHOST_BINFOUND 0x08
+#define USBHOST_BOUTFOUND 0x10
+#define USBHOST_ALLFOUND 0x1f
+
+#define USBHOST_MAX_CREFS 0x7fff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct usb_cdc_ncm_nth16_s
+{
+ uint8_t signature[4];
+ uint8_t length[2];
+ uint8_t sequence[2];
+ uint8_t block_length[2];
+ uint8_t ndp_index[2];
+};
+
+struct usb_cdc_ncm_dpe16_s
+{
+ uint8_t index[2];
+ uint8_t length[2];
+};
+
+struct usb_cdc_ncm_ndp16_s
+{
+ uint8_t signature[4];
+ uint8_t length[2];
+ uint8_t next_ndp_index[2];
+ struct usb_cdc_ncm_dpe16_s dpe16[0];
+};
+
+struct usb_cdc_ncm_ntb_params_s
+{
+ uint8_t length[2];
+ uint8_t ntb_formats_supported[2];
+ uint8_t ntb_in_max_size[4];
+ uint8_t ndp_in_divisor[2];
+ uint8_t ndp_in_payload_remainder[2];
+ uint8_t ndp_in_alignment[2];
+ uint8_t reserved[2];
+ uint8_t ntb_out_max_size[4];
+ uint8_t ndp_out_divisor[2];
+ uint8_t ndp_out_payload_remainder[2];
+ uint8_t ndp_out_alignment[2];
+ uint8_t ntb_out_max_datagrams[2];
+};
+
+struct usb_csifdesc_s
+{
+ uint8_t len;
+ uint8_t type;
+ uint8_t subtype;
+};
+
+struct usb_mbim_desc_s
+{
+ uint8_t len;
+ uint8_t type;
+ uint8_t subtype;
+ uint8_t mbim_version[2];
+ uint8_t max_ctrl_message[2];
+ uint8_t num_filters;
+ uint8_t max_filter_size;
+ uint8_t max_segment_size[2];
+ uint8_t network_capabilities;
+};
+
+/* This structure contains the internal, private state of the USB host class
+ * driver.
+ */
+
+struct usbhost_cdcmbim_s
+{
+ /* This is the externally visible portion of the state */
+
+ struct usbhost_class_s usbclass;
+
+ /* The remainder of the fields are provided to the class driver */
+
+ uint8_t minor; /* Minor number identifying the /dev/cdc-wdm[n] device */
+ volatile bool disconnected; /* TRUE: Device has been disconnected */
+ uint16_t ctrlif; /* Control interface number */
+ uint16_t dataif; /* Data interface number */
+ int16_t crefs; /* Reference count on the driver instance */
+ sem_t exclsem; /* Used to maintain mutual exclusive access */
+ struct work_s ntwork; /* Notification work */
+ struct work_s comm_rxwork; /* Communication interface RX work */
+ struct work_s bulk_rxwork;
+ struct work_s txpollwork;
+ struct work_s destroywork;
+ int16_t nnbytes; /* Number of bytes received in notification */
+ int16_t bulkinbytes;
+ uint16_t comm_rxlen; /* Number of bytes in the RX buffer */
+ uint16_t comm_rxmsgs; /* Number of messages available to be read */
+ uint16_t comm_rxpos; /* Read position for input buffer */
+ uint16_t maxctrlsize; /* Maximum size of a ctrl request */
+ uint16_t maxintsize; /* Maximum size of interrupt IN packet */
+ uint32_t maxntbin; /* Maximum size of NTB IN message */
+ uint32_t maxntbout; /* Maximum size of NTB OUT message */
+ FAR uint8_t *ctrlreq; /* Allocated ctrl request structure */
+ FAR uint8_t *data_txbuf; /* Allocated TX buffer for network datagrams */
+ FAR uint8_t *data_rxbuf; /* Allocated RX buffer for network datagrams */
+ FAR uint8_t *comm_rxbuf; /* Allocated RX buffer comm IN messages */
+ FAR uint8_t *notification; /* Allocated RX buffer for async notifications */
+ FAR uint8_t *rxnetbuf; /* Allocated RX buffer for NTB frames */
+ FAR uint8_t *txnetbuf; /* Allocated TX buffer for NTB frames */
+ usbhost_ep_t intin; /* Interrupt endpoint */
+ usbhost_ep_t bulkin; /* Bulk IN endpoint */
+ usbhost_ep_t bulkout; /* Bulk OUT endpoint */
+ uint16_t bulkmxpacket; /* Max packet size for Bulk OUT endpoint */
+ uint16_t ntbseq; /* NTB sequence number */
+
+ struct pollfd *fds[CONFIG_USBHOST_CDCMBIM_NPOLLWAITERS];
+
+ /* Network device members */
+
+ WDOG_ID txpoll; /* TX poll timer */
+ bool bifup; /* true:ifup false:ifdown */
+ struct net_driver_s netdev; /* Interface understood by the network */
+ uint8_t txpktbuf[MAX_NETDEV_PKTSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Semaphores */
+
+static void usbhost_takesem(sem_t *sem);
+#define usbhost_givesem(s) nxsem_post(s);
+
+/* Polling support */
+
+static void usbhost_pollnotify(FAR struct usbhost_cdcmbim_s *priv);
+
+/* Memory allocation services */
+
+static inline FAR struct usbhost_cdcmbim_s *usbhost_allocclass(void);
+static inline void usbhost_freeclass(FAR struct usbhost_cdcmbim_s *usbclass);
+
+/* Device name management */
+
+static int usbhost_allocdevno(FAR struct usbhost_cdcmbim_s *priv);
+static void usbhost_freedevno(FAR struct usbhost_cdcmbim_s *priv);
+static inline void usbhost_mkdevname(FAR struct usbhost_cdcmbim_s *priv,
+ FAR char *devname);
+
+/* Worker thread actions */
+
+static void usbhost_notification_work(FAR void *arg);
+static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes);
+static void usbhost_rxdata_work(FAR void *arg);
+static void usbhost_bulkin_work(FAR void *arg);
+
+static void usbhost_destroy(FAR void *arg);
+
+/* Helpers for usbhost_connect() */
+
+static int usbhost_cfgdesc(FAR struct usbhost_cdcmbim_s *priv,
+ FAR const uint8_t *configdesc, int desclen);
+static inline int usbhost_devinit(FAR struct usbhost_cdcmbim_s *priv);
+
+/* (Little Endian) Data helpers */
+
+static inline uint16_t usbhost_getle16(const uint8_t *val);
+static inline void usbhost_putle16(uint8_t *dest, uint16_t val);
+static inline uint32_t usbhost_getle32(const uint8_t *val);
+static void usbhost_putle32(uint8_t *dest, uint32_t val);
+
+/* Buffer memory management */
+
+static int usbhost_alloc_buffers(FAR struct usbhost_cdcmbim_s *priv);
+static void usbhost_free_buffers(FAR struct usbhost_cdcmbim_s *priv);
+
+/* struct usbhost_registry_s methods */
+
+static struct usbhost_class_s
+ *usbhost_create(FAR struct usbhost_hubport_s *hport,
+ FAR const struct usbhost_id_s *id);
+
+/* struct usbhost_class_s methods */
+
+static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
+ FAR const uint8_t *configdesc, int desclen);
+static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass);
+
+/* Control interface driver methods */
+
+static ssize_t cdcwdm_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+static ssize_t cdcwdm_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen);
+static int cdcwdm_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup);
+
+/* NuttX network callback functions */
+
+static int cdcmbim_ifup(struct net_driver_s *dev);
+static int cdcmbim_ifdown(struct net_driver_s *dev);
+static int cdcmbim_txavail(struct net_driver_s *dev);
+
+/* Network support functions */
+
+static void cdcmbim_receive(struct usbhost_cdcmbim_s *priv, uint8_t *buf,
+ size_t len);
+
+static int cdcmbim_txpoll(struct net_driver_s *dev);
+static void cdcmbim_txpoll_work(void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This structure provides the registry entry ID information that will be
+ * used to associate the USB class driver to a connected USB device.
+ */
+
+static const struct usbhost_id_s g_id =
+{
+ USB_CLASS_CDC, /* base */
+ CDC_SUBCLASS_MBIM, /* subclass */
+ 0, /* proto */
+ 0, /* vid */
+ 0 /* pid */
+};
+
+/* This is the USB host storage class's registry entry */
+
+static struct usbhost_registry_s g_cdcmbim =
+{
+ NULL, /* flink */
+ usbhost_create, /* create */
+ 1, /* nids */
+ &g_id /* id[] */
+};
+
+/* File operations for control channel */
+
+static const struct file_operations cdcwdm_fops =
+{
+ NULL, /* open */
+ NULL, /* close */
+ cdcwdm_read, /* read */
+ cdcwdm_write, /* write */
+ NULL, /* seek */
+ NULL, /* ioctl */
+ cdcwdm_poll /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+ , NULL /* unlink */
+#endif
+};
+
+/* This is a bitmap that is used to allocate device names /dev/cdc-wdm[n]. */
+
+static uint32_t g_devinuse;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int usbhost_ctrl_cmd(FAR struct usbhost_cdcmbim_s *priv,
+ uint8_t type, uint8_t req, uint16_t value,
+ uint16_t iface, uint8_t *payload, uint16_t len)
+{
+ FAR struct usbhost_hubport_s *hport;
+ struct usb_ctrlreq_s *ctrlreq;
+ int ret;
+
+ hport = priv->usbclass.hport;
+
+ ctrlreq = (struct usb_ctrlreq_s *)priv->ctrlreq;
+ ctrlreq->type = type;
+ ctrlreq->req = req;
+
+ usbhost_putle16(ctrlreq->value, value);
+ usbhost_putle16(ctrlreq->index, iface);
+ usbhost_putle16(ctrlreq->len, len);
+
+ if (type & USB_REQ_DIR_IN)
+ {
+ ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, payload);
+ }
+ else
+ {
+ ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, payload);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_pollnotify
+ ****************************************************************************/
+
+static void usbhost_pollnotify(FAR struct usbhost_cdcmbim_s *priv)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_USBHOST_CDCMBIM_NPOLLWAITERS; i++)
+ {
+ struct pollfd *fds = priv->fds[i];
+ if (fds)
+ {
+ fds->revents |= (fds->events & POLLIN);
+ if (fds->revents != 0)
+ {
+ uinfo("Report events: %02x\n", fds->revents);
+ nxsem_post(fds->sem);
+ }
+ }
+ }
+}
+
+static ssize_t usbhost_readmessage(FAR struct usbhost_cdcmbim_s *priv,
+ FAR char *buffer, size_t buflen)
+{
+ irqstate_t flags;
+ ssize_t ret = -EAGAIN;
+
+ flags = enter_critical_section();
+
+ if (priv->comm_rxlen > 0)
+ {
+ if (buflen > priv->comm_rxlen)
+ {
+ buflen = priv->comm_rxlen;
+ }
+
+ ret = buflen;
+
+ memcpy(buffer, priv->comm_rxbuf + priv->comm_rxpos, buflen);
+ priv->comm_rxlen -= buflen;
+ priv->comm_rxpos += buflen;
+ if (priv->comm_rxlen == 0)
+ {
+ priv->comm_rxpos = 0;
+ }
+ }
+
+ leave_critical_section(flags);
+ return ret;
+}
+
+static ssize_t cdcwdm_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
+{
+ FAR struct inode *inode;
+ FAR struct usbhost_cdcmbim_s *priv;
+ ssize_t ret;
+
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ usbhost_takesem(&priv->exclsem);
+
+ if (priv->disconnected)
+ {
+ ret = -ENODEV;
+ goto errout;
+ }
+
+ ret = usbhost_readmessage(priv, buffer, buflen);
+ if (ret < 0)
+ {
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ ret = -EAGAIN;
+ goto errout;
+ }
+
+ /* wait for a message
+ * TODO blocking read
+ */
+
+ ret = 0;
+ goto errout;
+ }
+
+ /* If this message has been completely read and there are more
+ * messages available, read the next message into the input buffer.
+ */
+
+ if (priv->comm_rxlen == 0 && priv->comm_rxmsgs > 0)
+ {
+ uinfo("Reading next message\n");
+ if (work_available(&priv->comm_rxwork))
+ {
+ (void)work_queue(LPWORK, &priv->comm_rxwork,
+ (worker_t)usbhost_rxdata_work,
+ priv, 0);
+ }
+ }
+
+errout:
+ usbhost_givesem(&priv->exclsem);
+ return ret;
+}
+
+static ssize_t cdcwdm_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen)
+{
+ FAR struct inode *inode;
+ FAR struct usbhost_cdcmbim_s *priv;
+ int ret;
+
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ if (priv->disconnected)
+ {
+ return -ENODEV;
+ }
+
+ if (buflen > priv->maxctrlsize)
+ {
+ buflen = priv->maxctrlsize;
+ }
+
+ /* Make sure that we have exclusive access to the private data
+ * structure. There may now be other tasks with the character driver
+ * open and actively trying to interact with the class driver.
+ */
+
+ usbhost_takesem(&priv->exclsem);
+
+ ret = usbhost_ctrl_cmd(priv,
+ USB_REQ_DIR_OUT | USB_REQ_TYPE_CLASS |
+ USB_REQ_RECIPIENT_INTERFACE,
+ USB_CDC_SEND_ENCAPSULATED_COMMAND,
+ 0, priv->ctrlif, (uint8_t *)buffer, buflen);
+
+ usbhost_givesem(&priv->exclsem);
+
+ if (ret)
+ {
+ return ret;
+ }
+
+ uinfo("wrote %u bytes\n", buflen);
+
+ return buflen;
+}
+
+/****************************************************************************
+ * Name: usbhost_poll
+ *
+ * Description:
+ * Standard character driver poll method.
+ *
+ ****************************************************************************/
+
+static int cdcwdm_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup)
+{
+ FAR struct inode *inode;
+ FAR struct usbhost_cdcmbim_s *priv;
+ int ret = OK;
+ int i;
+
+ DEBUGASSERT(filep && filep->f_inode && fds);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Make sure that we have exclusive access to the private data structure */
+
+ DEBUGASSERT(priv);
+ usbhost_takesem(&priv->exclsem);
+
+ if (priv->disconnected)
+ {
+ ret = -ENODEV;
+ }
+ else if (setup)
+ {
+ /* This is a request to set up the poll. Find an available slot for
+ * the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_USBHOST_CDCMBIM_NPOLLWAITERS; i++)
+ {
+ /* Find an available slot */
+
+ if (!priv->fds[i])
+ {
+ /* Bind the poll structure and this slot */
+
+ priv->fds[i] = fds;
+ fds->priv = &priv->fds[i];
+ break;
+ }
+ }
+
+ if (i >= CONFIG_USBHOST_CDCMBIM_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should we immediately notify on any of the requested events? Notify
+ * the POLLIN event if there is a buffered message.
+ */
+
+ if (priv->comm_rxlen > 0)
+ {
+ usbhost_pollnotify(priv);
+ }
+ }
+ else
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+ DEBUGASSERT(slot);
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ nxsem_post(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_takesem
+ *
+ * Description:
+ * This is just a wrapper to handle the annoying behavior of semaphore
+ * waits that return due to the receipt of a signal.
+ *
+ ****************************************************************************/
+
+static void usbhost_takesem(sem_t *sem)
+{
+ int ret;
+
+ do
+ {
+ /* Take the semaphore (perhaps waiting) */
+
+ ret = nxsem_wait(sem);
+
+ /* The only case that an error should occur here is if the wait was
+ * awakened by a signal.
+ */
+
+ DEBUGASSERT(ret == OK || ret == -EINTR);
+ }
+ while (ret == -EINTR);
+}
+
+/****************************************************************************
+ * Name: usbhost_allocclass
+ *
+ * Description:
+ * This is really part of the logic that implements the create() method
+ * of struct usbhost_registry_s. This function allocates memory for one
+ * new class instance.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * On success, this function will return a non-NULL instance of struct
+ * usbhost_class_s. NULL is returned on failure; this function will
+ * will fail only if there are insufficient resources to create another
+ * USB host class instance.
+ *
+ ****************************************************************************/
+
+static inline FAR struct usbhost_cdcmbim_s *usbhost_allocclass(void)
+{
+ FAR struct usbhost_cdcmbim_s *priv;
+
+ DEBUGASSERT(!up_interrupt_context());
+ priv = (FAR struct usbhost_cdcmbim_s *)kmm_malloc(
+ sizeof(struct usbhost_cdcmbim_s));
+ uinfo("Allocated: %p\n", priv);
+ return priv;
+}
+
+/****************************************************************************
+ * Name: usbhost_freeclass
+ *
+ * Description:
+ * Free a class instance previously allocated by usbhost_allocclass().
+ *
+ * Input Parameters:
+ * usbclass - A reference to the class instance to be freed.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static inline void usbhost_freeclass(FAR struct usbhost_cdcmbim_s *usbclass)
+{
+ DEBUGASSERT(usbclass != NULL);
+
+ /* Free the class instance (perhaps calling sched_kmm_free() in case we are
+ * executing from an interrupt handler.
+ */
+
+ uinfo("Freeing: %p\n", usbclass);
+ kmm_free(usbclass);
+}
+
+/****************************************************************************
+ * Name: Device name management
+ *
+ * Description:
+ * Some tiny functions to coordinate management of device names.
+ *
+ ****************************************************************************/
+
+static int usbhost_allocdevno(FAR struct usbhost_cdcmbim_s *priv)
+{
+ irqstate_t flags;
+ int devno;
+
+ flags = enter_critical_section();
+ for (devno = 0; devno < 32; devno++)
+ {
+ uint32_t bitno = 1 << devno;
+ if ((g_devinuse & bitno) == 0)
+ {
+ g_devinuse |= bitno;
+ priv->minor = devno;
+ leave_critical_section(flags);
+ return OK;
+ }
+ }
+
+ leave_critical_section(flags);
+ return -EMFILE;
+}
+
+static void usbhost_freedevno(FAR struct usbhost_cdcmbim_s *priv)
+{
+ int devno = priv->minor;
+
+ if (devno >= 0 && devno < 26)
+ {
+ irqstate_t flags = enter_critical_section();
+ g_devinuse &= ~(1 << devno);
+ leave_critical_section(flags);
+ }
+}
+
+static inline void usbhost_mkdevname(FAR struct usbhost_cdcmbim_s *priv,
+ FAR char *devname)
+{
+ (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->minor);
+}
+
+static void usbhost_bulkin_callback(FAR void *arg, ssize_t nbytes)
+{
+ struct usbhost_cdcmbim_s *priv = (struct usbhost_cdcmbim_s *)arg;
+ uint32_t delay = 0;
+
+ DEBUGASSERT(priv);
+
+ if (priv->disconnected)
+ {
+ return;
+ }
+
+ priv->bulkinbytes = (int16_t)nbytes;
+
+ if (nbytes < 0)
+ {
+ if (nbytes != -EAGAIN)
+ {
+ uerr("ERROR: Transfer failed: %d\n", nbytes);
+ }
+
+ delay = MSEC2TICK(30);
+ }
+
+ if (work_available(&priv->bulk_rxwork))
+ {
+ (void)work_queue(LPWORK, &priv->bulk_rxwork,
+ (worker_t)usbhost_bulkin_work, priv, delay);
+ }
+}
+
+static void usbhost_bulkin_work(FAR void *arg)
+{
+ struct usbhost_cdcmbim_s *priv;
+ struct usbhost_hubport_s *hport;
+ uint16_t ndpoffset;
+ uint16_t dgram_len;
+ uint16_t dgram_off;
+
+ priv = (struct usbhost_cdcmbim_s *)arg;
+ DEBUGASSERT(priv);
+
+ hport = priv->usbclass.hport;
+ DEBUGASSERT(hport);
+
+ if (priv->disconnected || !priv->bifup)
+ {
+ return;
+ }
+
+ usbhost_takesem(&priv->exclsem);
+
+ if (priv->bulkinbytes < (int16_t)(sizeof(struct usb_cdc_ncm_nth16_s) +
+ sizeof(struct usb_cdc_ncm_ndp16_s)))
+ {
+ goto out;
+ }
+
+ /* Parse the NTB header */
+
+ struct usb_cdc_ncm_nth16_s *nth =
+ (struct usb_cdc_ncm_nth16_s *)priv->rxnetbuf;
+
+ if (usbhost_getle32(nth->signature) != USB_CDC_NCM_NTH16_SIGNATURE)
+ {
+ uerr("Invalid NTH signature\n");
+ goto out;
+ }
+
+ uint16_t block_len = usbhost_getle16(nth->block_length);
+
+ if (block_len > priv->bulkinbytes)
+ {
+ uerr("Block length larger than rx buffer\n");
+ goto out;
+ }
+
+ ndpoffset = usbhost_getle16(nth->ndp_index);
+ if (ndpoffset > priv->bulkinbytes)
+ {
+ uerr("NDP offset too far %u > %u\n", ndpoffset, priv->bulkinbytes);
+ goto out;
+ }
+
+ /* Parse each NDP */
+
+ do
+ {
+ struct usb_cdc_ncm_ndp16_s *ndp
+ = (struct usb_cdc_ncm_ndp16_s *)(priv->rxnetbuf + ndpoffset);
+
+ ndpoffset = usbhost_getle16(ndp->next_ndp_index);
+
+ /* Parse each DPE */
+
+ struct usb_cdc_ncm_dpe16_s *dpe;
+
+ for (dpe = ndp->dpe16; usbhost_getle16(dpe->index); dpe++)
+ {
+ dgram_off = usbhost_getle16(dpe->index);
+ dgram_len = usbhost_getle16(dpe->length);
+
+ if (dgram_off + dgram_len <= priv->bulkinbytes)
+ {
+ cdcmbim_receive(priv, priv->rxnetbuf + dgram_off, dgram_len);
+ }
+ }
+ }
+ while (ndpoffset);
+
+out:
+ DRVR_ASYNCH(hport->drvr, priv->bulkin,
+ (uint8_t *)priv->rxnetbuf, CDCMBIM_NETBUF_SIZE,
+ usbhost_bulkin_callback, priv);
+ usbhost_givesem(&priv->exclsem);
+}
+
+/****************************************************************************
+ * Name: usbhost_rxdata_work
+ *
+ * Description:
+ * Read an available comm message into the RX buffer
+ *
+ * Input Parameters:
+ * arg - Driver private data
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_rxdata_work(FAR void *arg)
+{
+ FAR struct usbhost_cdcmbim_s *priv;
+ int ret;
+
+ priv = (FAR struct usbhost_cdcmbim_s *)arg;
+ DEBUGASSERT(priv);
+
+ /* Are we still connected? */
+
+ if (priv->disconnected)
+ {
+ return;
+ }
+
+ usbhost_takesem(&priv->exclsem);
+
+ ret = usbhost_ctrl_cmd(priv,
+ USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS |
+ USB_REQ_RECIPIENT_INTERFACE,
+ USB_CDC_GET_ENCAPSULATED_RESPONSE,
+ 0, priv->ctrlif, priv->comm_rxbuf,
+ priv->maxctrlsize);
+ if (ret)
+ {
+ uerr("Failed to read message: %d\n", ret);
+ goto errout;
+ }
+
+ /* Would be nice if CTRLIN would return how many bytes it read... */
+
+ struct mbim_header_s
+ {
+ uint8_t type[4];
+ uint8_t len[4];
+ };
+
+ struct mbim_header_s *hdr = (struct mbim_header_s *)priv->comm_rxbuf;
+ uint32_t len = usbhost_getle32(hdr->len);
+
+ if (len > priv->maxctrlsize)
+ {
+ uerr("Read invalid MBIM packet\n");
+ goto errout;
+ }
+
+ uinfo("Read MBIM message %u bytes\n", len);
+
+ priv->comm_rxlen = len;
+ priv->comm_rxmsgs--;
+
+ /* Notify any poll waiters we have data */
+
+ usbhost_pollnotify(priv);
+
+errout:
+ usbhost_givesem(&priv->exclsem);
+}
+
+/****************************************************************************
+ * Name: usbhost_notification_work
+ *
+ * Description:
+ * Handle receipt of an asynchronous notification from the CDC device
+ *
+ * Input Parameters:
+ * arg - The argument provided with the asynchronous I/O was setup
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Probably called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static void usbhost_notification_work(FAR void *arg)
+{
+ FAR struct usbhost_cdcmbim_s *priv;
+ FAR struct usbhost_hubport_s *hport;
+ FAR struct cdc_notification_s *inmsg;
+ int ret;
+
+ priv = (FAR struct usbhost_cdcmbim_s *)arg;
+ DEBUGASSERT(priv);
+
+ hport = priv->usbclass.hport;
+ DEBUGASSERT(hport);
+
+ /* Are we still connected? */
+
+ if (!priv->disconnected && priv->intin)
+ {
+ /* Yes.. Was an interrupt IN message received correctly? */
+
+ if (priv->nnbytes >= 0)
+ {
+ /* Yes.. decode the notification */
+
+ inmsg = (FAR struct cdc_notification_s *)priv->notification;
+
+ /* We care only about the ResponseAvailable notification */
+
+ if ((inmsg->type == (USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS |
+ USB_REQ_RECIPIENT_INTERFACE)) &&
+ (inmsg->notification == ACM_RESPONSE_AVAILABLE))
+ {
+ priv->comm_rxmsgs++;
+
+ /* If this is the only message available, read it */
+
+ if (priv->comm_rxmsgs == 1)
+ {
+ if (work_available(&priv->comm_rxwork))
+ {
+ (void)work_queue(LPWORK, &priv->comm_rxwork,
+ (worker_t)usbhost_rxdata_work,
+ priv, 0);
+ }
+ }
+ }
+ }
+
+ /* Setup to receive the next notification */
+
+ ret = DRVR_ASYNCH(hport->drvr, priv->intin,
+ (FAR uint8_t *)priv->notification,
+ SIZEOF_NOTIFICATION_S(0),
+ usbhost_notification_callback,
+ priv);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_ASYNCH failed: %d\n", ret);
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: usbhost_notification_callback
+ *
+ * Description:
+ * Handle receipt of Response Available from the CDC/MBIM device
+ *
+ * Input Parameters:
+ * arg - The argument provided with the asynchronous I/O was setup
+ * nbytes - The number of bytes actually transferred (or a negated errno
+ * value;
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Probably called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes)
+{
+ FAR struct usbhost_cdcmbim_s *priv = (FAR struct usbhost_cdcmbim_s *)arg;
+ uint32_t delay = 0;
+
+ DEBUGASSERT(priv);
+
+ /* Are we still connected? */
+
+ if (!priv->disconnected)
+ {
+ /* Check for a failure. On higher end host controllers, the
+ * asynchronous transfer will pend until data is available (OHCI and
+ * EHCI). On lower end host controllers (like STM32 and EFM32), the
+ * transfer will fail immediately when the device NAKs the first
+ * attempted interrupt IN transfer (with nbytes == -EAGAIN). In that
+ * case (or in the case of other errors), we must fall back to
+ * polling.
+ */
+
+ DEBUGASSERT(nbytes >= INT16_MIN && nbytes <= INT16_MAX);
+ priv->nnbytes = (int16_t)nbytes;
+
+ if (nbytes < 0)
+ {
+ /* This debug output is good to know, but really a nuisance for
+ * those configurations where we have to fall back to polling.
+ * FIX: Don't output the message is the result is -EAGAIN.
+ */
+
+#if defined(CONFIG_DEBUG_USB) && !defined(CONFIG_DEBUG_INFO)
+ if (nbytes != -EAGAIN)
+#endif
+ {
+ uerr("ERROR: Transfer failed: %d\n", nbytes);
+ }
+
+ /* We don't know the nature of the failure, but we need to do all
+ * that we can do to avoid a CPU hog error loop.
+ *
+ * Use the low-priority work queue and delay polling for the next
+ * event. We want to use as little CPU bandwidth as possible in
+ * this case.
+ */
+
+ delay = USBHOST_CDCMBIM_NTDELAY;
+ }
+
+ /* Make sure that the work structure available. There is a remote
+ * chance that this may collide with a device disconnection event.
+ */
+
+ if (work_available(&priv->ntwork))
+ {
+ (void)work_queue(LPWORK, &priv->ntwork,
+ (worker_t)usbhost_notification_work,
+ priv, delay);
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: usbhost_destroy
+ *
+ * Description:
+ * The USB device has been disconnected and the reference count on the USB
+ * host class instance has gone to 1.. Time to destroy the USB host class
+ * instance.
+ *
+ * Input Parameters:
+ * arg - A reference to the class instance to be destroyed.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_destroy(FAR void *arg)
+{
+ FAR struct usbhost_cdcmbim_s *priv = (FAR struct usbhost_cdcmbim_s *)arg;
+ FAR struct usbhost_hubport_s *hport;
+ FAR struct usbhost_driver_s *drvr;
+
+ DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
+ hport = priv->usbclass.hport;
+
+ DEBUGASSERT(hport->drvr);
+ drvr = hport->drvr;
+
+ uinfo("crefs: %d\n", priv->crefs);
+
+ /* Unregister the driver */
+
+ /* Release the device name used by this connection */
+
+ usbhost_freedevno(priv);
+
+ /* Free the endpoints */
+
+ if (priv->intin)
+ {
+ DRVR_EPFREE(hport->drvr, priv->intin);
+ }
+
+ if (priv->bulkin)
+ {
+ DRVR_EPFREE(hport->drvr, priv->bulkin);
+ }
+
+ if (priv->bulkout)
+ {
+ DRVR_EPFREE(hport->drvr, priv->bulkout);
+ }
+
+ /* Free any transfer buffers */
+
+ usbhost_free_buffers(priv);
+
+ /* Free the function address assigned to this device */
+
+ usbhost_devaddr_destroy(hport, hport->funcaddr);
+ hport->funcaddr = 0;
+
+ /* Destroy the semaphores */
+
+ /* Disconnect the USB host device */
+
+ DRVR_DISCONNECT(drvr, hport);
+
+ /* And free the class instance. Hmmm.. this may execute on the worker
+ * thread and the work structure is part of what is getting freed. That
+ * should be okay because once the work contained is removed from the
+ * queue, it should not longer be accessed by the worker thread.
+ */
+
+ usbhost_freeclass(priv);
+}
+
+/****************************************************************************
+ * Name: usbhost_cfgdesc
+ *
+ * Description:
+ * This function implements the connect() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to provide the device's configuration
+ * descriptor to the class so that the class may initialize properly
+ *
+ * Input Parameters:
+ * priv - The USB host class instance.
+ * configdesc - A pointer to a uint8_t buffer container the configuration
+ * descriptor.
+ * desclen - The length in bytes of the configuration descriptor.
+ *
+ * Returned Value:
+ * On success, zero (OK) is returned. On a failure, a negated errno value
+ * is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * This function will *not* be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int usbhost_cfgdesc(FAR struct usbhost_cdcmbim_s *priv,
+ FAR const uint8_t *configdesc, int desclen)
+{
+ FAR struct usbhost_hubport_s *hport;
+ FAR struct usb_cfgdesc_s *cfgdesc;
+ FAR struct usb_desc_s *desc;
+ FAR struct usbhost_epdesc_s bindesc;
+ FAR struct usbhost_epdesc_s boutdesc;
+ FAR struct usbhost_epdesc_s iindesc;
+ int remaining;
+ uint8_t found = 0;
+ int ret;
+
+ DEBUGASSERT(priv != NULL && priv->usbclass.hport &&
+ configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s));
+ hport = priv->usbclass.hport;
+
+ /* Verify that we were passed a configuration descriptor */
+
+ cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
+ if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
+ {
+ return -EINVAL;
+ }
+
+ /* Get the total length of the configuration descriptor (little endian).
+ * It might be a good check to get the number of interfaces here too.
+ */
+
+ remaining = (int)usbhost_getle16(cfgdesc->totallen);
+
+ /* Skip to the next entry descriptor */
+
+ configdesc += cfgdesc->len;
+ remaining -= cfgdesc->len;
+
+ /* Loop where there are more descriptors to examine */
+
+ while (remaining >= sizeof(struct usb_desc_s))
+ {
+ /* What is the next descriptor? */
+
+ desc = (FAR struct usb_desc_s *)configdesc;
+ switch (desc->type)
+ {
+ case USB_DESC_TYPE_CSINTERFACE:
+ {
+ FAR struct usb_csifdesc_s *csdesc = (FAR struct usb_csifdesc_s *)
+ desc;
+
+ /* MBIM functional descriptor */
+
+ if (csdesc->subtype == CDC_DSUBTYPE_MBIM)
+ {
+ FAR struct usb_mbim_desc_s *mbim =
+ (FAR struct usb_mbim_desc_s *)desc;
+
+ priv->maxctrlsize = usbhost_getle16(mbim->max_ctrl_message);
+ uinfo("MBIM max control size: %u\n", priv->maxctrlsize);
+ uinfo("MBIM max segment size: %u\n",
+ usbhost_getle16(mbim->max_segment_size));
+ }
+ }
+ break;
+
+ /* Interface descriptor. We really should get the number of endpoints
+ * from this descriptor too.
+ */
+
+ case USB_DESC_TYPE_INTERFACE:
+ {
+ FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)
+ configdesc;
+
+ uinfo("Interface descriptor\n");
+ DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);
+
+ /* Is this the control interface? */
+
+ if (ifdesc->classid == CDC_CLASS_COMM &&
+ ifdesc->subclass == CDC_SUBCLASS_MBIM &&
+ ifdesc->protocol == CDC_PROTO_NONE)
+ {
+ priv->ctrlif = ifdesc->ifno;
+ found |= USBHOST_CTRLIFFOUND;
+ }
+
+ /* Is this the data interface? */
+
+ else if (ifdesc->classid == USB_CLASS_CDC_DATA &&
+ ifdesc->subclass == CDC_SUBCLASS_NONE &&
+ ifdesc->protocol == CDC_DATA_PROTO_NTB)
+ {
+ priv->dataif = ifdesc->ifno;
+ found |= USBHOST_DATAIFFOUND;
+ }
+ }
+ break;
+
+ /* Endpoint descriptor. Here, we expect two bulk endpoints, an IN
+ * and an OUT.
+ */
+
+ case USB_DESC_TYPE_ENDPOINT:
+ {
+ FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)
+ configdesc;
+
+ uinfo("Endpoint descriptor\n");
+ DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);
+
+ /* Check for interrupt endpoint */
+
+ if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) ==
+ USB_EP_ATTR_XFER_INT)
+ {
+ if (USB_ISEPIN(epdesc->addr))
+ {
+ found |= USBHOST_INTRIFFOUND;
+ iindesc.hport = hport;
+ iindesc.addr = epdesc->addr &
+ USB_EP_ADDR_NUMBER_MASK;
+ iindesc.in = true;
+ iindesc.xfrtype = USB_EP_ATTR_XFER_INT;
+ iindesc.interval = epdesc->interval;
+ iindesc.mxpacketsize =
+ usbhost_getle16(epdesc->mxpacketsize);
+ uinfo("Interrupt IN EP addr:%d mxpacketsize:%d\n",
+ iindesc.addr, iindesc.mxpacketsize);
+
+ priv->maxintsize = iindesc.mxpacketsize;
+ }
+ }
+
+ /* Check for a bulk endpoint. */
+
+ else if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) ==
+ USB_EP_ATTR_XFER_BULK)
+ {
+ /* Yes.. it is a bulk endpoint. IN or OUT? */
+
+ if (USB_ISEPOUT(epdesc->addr))
+ {
+ /* It is an OUT bulk endpoint. There should be only one
+ * bulk OUT endpoint.
+ */
+
+ if ((found & USBHOST_BOUTFOUND) != 0)
+ {
+ /* Oops.. more than one endpoint. We don't know
+ * what to do with this.
+ */
+
+ return -EINVAL;
+ }
+
+ found |= USBHOST_BOUTFOUND;
+
+ /* Save the bulk OUT endpoint information */
+
+ boutdesc.hport = hport;
+ boutdesc.addr = epdesc->addr &
+ USB_EP_ADDR_NUMBER_MASK;
+ boutdesc.in = false;
+ boutdesc.xfrtype = USB_EP_ATTR_XFER_BULK;
+ boutdesc.interval = epdesc->interval;
+ boutdesc.mxpacketsize =
+ usbhost_getle16(epdesc->mxpacketsize);
+ uinfo("Bulk OUT EP addr:%d mxpacketsize:%d\n",
+ boutdesc.addr, boutdesc.mxpacketsize);
+
+ priv->bulkmxpacket = boutdesc.mxpacketsize;
+ }
+ else
+ {
+ /* It is an IN bulk endpoint. There should be only one
+ * bulk IN endpoint.
+ */
+
+ if ((found & USBHOST_BINFOUND) != 0)
+ {
+ /* Oops.. more than one endpoint. We don't know
+ * what to do with this.
+ */
+
+ return -EINVAL;
+ }
+
+ found |= USBHOST_BINFOUND;
+
+ /* Save the bulk IN endpoint information */
+
+ bindesc.hport = hport;
+ bindesc.addr = epdesc->addr &
+ USB_EP_ADDR_NUMBER_MASK;
+ bindesc.in = 1;
+ bindesc.xfrtype = USB_EP_ATTR_XFER_BULK;
+ bindesc.interval = epdesc->interval;
+ bindesc.mxpacketsize =
+ usbhost_getle16(epdesc->mxpacketsize);
+ uinfo("Bulk IN EP addr:%d mxpacketsize:%d\n",
+ bindesc.addr, bindesc.mxpacketsize);
+ }
+ }
+ }
+ break;
+
+ /* Other descriptors are just ignored for now */
+
+ default:
+ break;
+ }
+
+ /* If we found everything we need with this interface, then break out
+ * of the loop early.
+ */
+
+ if (found == USBHOST_ALLFOUND)
+ {
+ break;
+ }
+
+ /* Increment the address of the next descriptor */
+
+ configdesc += desc->len;
+ remaining -= desc->len;
+ }
+
+ /* Sanity checking... did we find all of things that we need? */
+
+ if (found != USBHOST_ALLFOUND)
+ {
+ uerr("ERROR: Found CTRLIF:%s DATAIF: %s BIN:%s BOUT:%s\n",
+ (found & USBHOST_CTRLIFFOUND) != 0 ? "YES" : "NO",
+ (found & USBHOST_DATAIFFOUND) != 0 ? "YES" : "NO",
+ (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO",
+ (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO");
+ return -EINVAL;
+ }
+
+ /* We are good... Allocate the endpoints */
+
+ ret = DRVR_EPALLOC(hport->drvr, &boutdesc, &priv->bulkout);
+ if (ret < 0)
+ {
+ uerr("ERROR: Failed to allocate Bulk OUT endpoint\n");
+ return ret;
+ }
+
+ ret = DRVR_EPALLOC(hport->drvr, &bindesc, &priv->bulkin);
+ if (ret < 0)
+ {
+ uerr("ERROR: Failed to allocate Bulk IN endpoint\n");
+ (void)DRVR_EPFREE(hport->drvr, priv->bulkout);
+ return ret;
+ }
+
+ ret = DRVR_EPALLOC(hport->drvr, &iindesc, &priv->intin);
+ if (ret < 0)
+ {
+ uerr("ERROR: Failed to allocate Interrupt IN endpoint\n");
+ (void)DRVR_EPFREE(hport->drvr, priv->bulkout);
+ (void)DRVR_EPFREE(hport->drvr, priv->bulkin);
+ return ret;
+ }
+
+ uinfo("Endpoints allocated\n");
+ return OK;
+}
+
+static int usbhost_setinterface(FAR struct usbhost_cdcmbim_s *priv,
+ uint16_t iface, uint16_t setting)
+{
+ return usbhost_ctrl_cmd(priv,
+ USB_REQ_DIR_OUT | USB_REQ_RECIPIENT_INTERFACE,
+ USB_REQ_SETINTERFACE, setting, iface, NULL, 0);
+}
+
+static int cdc_ncm_set_ntb_input_size(FAR struct usbhost_cdcmbim_s *priv,
+ uint32_t size)
+{
+ uint8_t buf[4];
+
+ usbhost_putle32(buf, size);
+
+ return usbhost_ctrl_cmd(priv,
+ USB_REQ_DIR_OUT | USB_REQ_TYPE_CLASS |
+ USB_REQ_RECIPIENT_INTERFACE,
+ USB_CDC_SET_NTB_INPUT_SIZE,
+ 0, priv->ctrlif, buf, 4);
+}
+
+#if 0
+static int cdc_ncm_set_max_dgram_size(FAR struct usbhost_cdcmbim_s *priv,
+ uint16_t size)
+{
+ uint8_t buf[2];
+
+ usbhost_putle16(buf, size);
+
+ return usbhost_ctrl_cmd(priv,
+ USB_REQ_DIR_OUT | USB_REQ_TYPE_CLASS |
+ USB_REQ_RECIPIENT_INTERFACE,
+ USB_CDC_SET_MAX_DATAGRAM_SIZE,
+ 0, priv->ctrlif, buf, 2);
+}
+#endif
+
+static int cdc_ncm_read_parameters(FAR struct usbhost_cdcmbim_s *priv)
+{
+ struct usb_cdc_ncm_ntb_params_s params;
+ int ret;
+
+ ret = usbhost_ctrl_cmd(priv,
+ USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS |
+ USB_REQ_RECIPIENT_INTERFACE,
+ USB_CDC_GET_NTB_PARAMETERS,
+ 0, priv->ctrlif, (uint8_t *)¶ms,
+ sizeof(params));
+ if (ret == OK)
+ {
+ priv->maxntbin = usbhost_getle32(params.ntb_in_max_size);
+ priv->maxntbout = usbhost_getle32(params.ntb_out_max_size);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_devinit
+ *
+ * Description:
+ * The USB device has been successfully connected. This completes the
+ * initialization operations. It is first called after the
+ * configuration descriptor has been received.
+ *
+ * This function is called from the connect() method. This function always
+ * executes on the thread of the caller of connect().
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static inline int usbhost_devinit(FAR struct usbhost_cdcmbim_s *priv)
+{
+ FAR struct usbhost_hubport_s *hport;
+ int ret = OK;
+
+ hport = priv->usbclass.hport;
+
+ /* Increment the reference count. This will prevent usbhost_destroy() from
+ * being called asynchronously if the device is removed.
+ */
+
+ priv->crefs++;
+ DEBUGASSERT(priv->crefs == 2);
+
+ /* Configure the device */
+
+ /* Set aside transfer buffers for exclusive use by the class driver */
+
+ ret = usbhost_alloc_buffers(priv);
+ if (ret)
+ {
+ uerr("ERROR: failed to allocate buffers\n");
+ return ret;
+ }
+
+ priv->ntbseq = 0;
+
+ /* Read NCM parameters */
+
+ ret = cdc_ncm_read_parameters(priv);
+ if (ret)
+ {
+ uerr("ERROR: failed to read NCM parameters: %d\n", ret);
+ }
+
+ /* Set alternate setting 1 on data interface */
+
+ usbhost_setinterface(priv, priv->dataif, 1);
+
+ /* Set NTB Input size to length of rx buffer */
+
+ ret = cdc_ncm_set_ntb_input_size(priv, CDCMBIM_NETBUF_SIZE);
+ if (ret)
+ {
+ printf("set NTB input size failed: %d\n", ret);
+ }
+
+ #if 0
+ /* Set max datagram size to MTU */
+
+ ret = cdc_ncm_set_max_dgram_size(priv, 2048);
+ if (ret)
+ {
+ printf("Failed to set max dgram size: %d\n", ret);
+ }
+ #endif
+
+ /* Register the driver */
+
+ if (ret >= 0)
+ {
+ char devname[DEV_NAMELEN];
+
+ uinfo("Register character driver\n");
+ usbhost_mkdevname(priv, devname);
+ ret = register_driver(devname, &cdcwdm_fops, 0666, priv);
+ }
+
+ if (priv->intin)
+ {
+ /* Begin monitoring of message available events */
+
+ uinfo("Start notification monitoring\n");
+ ret = DRVR_ASYNCH(hport->drvr, priv->intin,
+ (FAR uint8_t *)priv->notification,
+ SIZEOF_NOTIFICATION_S(0),
+ usbhost_notification_callback,
+ priv);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_ASYNCH failed on intin: %d\n", ret);
+ }
+ }
+
+ /* Setup the network interface */
+
+ memset(&priv->netdev, 0, sizeof(struct net_driver_s));
+ priv->netdev.d_ifup = cdcmbim_ifup;
+ priv->netdev.d_ifdown = cdcmbim_ifdown;
+ priv->netdev.d_txavail = cdcmbim_txavail;
+ priv->netdev.d_private = priv;
+ priv->txpoll = wd_create();
+
+ /* Register the network device */
+
+ (void)netdev_register(&priv->netdev, NET_LL_MBIM);
+
+ /* Check if we successfully initialized. We now have to be concerned
+ * about asynchronous modification of crefs because the character
+ * driver has been registerd.
+ */
+
+ if (ret >= 0)
+ {
+ usbhost_takesem(&priv->exclsem);
+ DEBUGASSERT(priv->crefs >= 2);
+
+ /* Handle a corner case where (1) open() has been called so the
+ * reference count is > 2, but the device has been disconnected.
+ * In this case, the class instance needs to persist until close()
+ * is called.
+ */
+
+ if (priv->crefs <= 2 && priv->disconnected)
+ {
+ /* We don't have to give the semaphore because it will be
+ * destroyed when usb_destroy is called.
+ */
+
+ ret = -ENODEV;
+ }
+ else
+ {
+ /* Ready for normal operation */
+
+ uinfo("Successfully initialized\n");
+ priv->crefs--;
+ usbhost_givesem(&priv->exclsem);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_getle16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Value:
+ * A uint16_t representing the whole 16-bit integer value
+ *
+ ****************************************************************************/
+
+static inline uint16_t usbhost_getle16(const uint8_t *val)
+{
+ return (uint16_t)val[1] << 8 | (uint16_t)val[0];
+}
+
+/****************************************************************************
+ * Name: usbhost_putle16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 16-bit value to be saved.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putle16(uint8_t *dest, uint16_t val)
+{
+ dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
+ dest[1] = val >> 8;
+}
+
+/****************************************************************************
+ * Name: usbhost_getle32
+ *
+ * Description:
+ * Get a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the big endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static inline uint32_t usbhost_getle32(const uint8_t *val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ return (uint32_t)usbhost_getle16(&val[2]) << 16 |
+ (uint32_t)usbhost_getle16(val);
+}
+
+/****************************************************************************
+ * Name: usbhost_putle32
+ *
+ * Description:
+ * Put a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_putle32(uint8_t *dest, uint32_t val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ usbhost_putle16(dest, (uint16_t)(val & 0xffff));
+ usbhost_putle16(dest + 2, (uint16_t)(val >> 16));
+}
+
+/****************************************************************************
+ * Name: usbhost_alloc_buffers
+ *
+ * Description:
+ * Allocate transfer buffer memory.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Value:
+ * On success, zero (OK) is returned. On failure, an negated errno value
+ * is returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static int usbhost_alloc_buffers(FAR struct usbhost_cdcmbim_s *priv)
+{
+ FAR struct usbhost_hubport_s *hport;
+ size_t maxlen;
+ int ret;
+
+ DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL &&
+ priv->ctrlreq == NULL);
+ hport = priv->usbclass.hport;
+
+ /* Allocate memory for control requests */
+
+ ret = DRVR_ALLOC(hport->drvr, (FAR uint8_t **)&priv->ctrlreq, &maxlen);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_ALLOC of ctrlreq failed: %d\n", ret);
+ goto errout;
+ }
+
+ DEBUGASSERT(maxlen >= sizeof(struct usb_ctrlreq_s));
+
+ /* Allocate buffer for receiving encapsulated data */
+
+ ret = DRVR_IOALLOC(hport->drvr, &priv->comm_rxbuf, priv->maxctrlsize);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_IOALLOC of comm_rxbuf failed: %d (%d bytes)\n", ret,
+ priv->maxctrlsize);
+ goto errout;
+ }
+
+ /* Allocate buffer for interrupt IN notifications */
+
+ ret = DRVR_IOALLOC(hport->drvr, &priv->notification, priv->maxintsize);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_IOALLOC of notification buf failed: %d (%d bytes)\n",
+ ret, priv->maxintsize);
+ goto errout;
+ }
+
+ ret = DRVR_IOALLOC(hport->drvr, &priv->rxnetbuf, CDCMBIM_NETBUF_SIZE);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_IOALLOC of net rx buf failed: %d (%d bytes)\n",
+ ret, 2048);
+ goto errout;
+ }
+
+ ret = DRVR_IOALLOC(hport->drvr, &priv->txnetbuf, 2048);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_IOALLOC of net tx buf failed: %d (%d bytes)\n",
+ ret, 2048);
+ goto errout;
+ }
+
+ return OK;
+
+errout:
+ usbhost_free_buffers(priv);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_free_buffers
+ *
+ * Description:
+ * Free transfer buffer memory.
+ *
+ * Input Parameters:
+ * priv - A reference to the class instance.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void usbhost_free_buffers(FAR struct usbhost_cdcmbim_s *priv)
+{
+ FAR struct usbhost_hubport_s *hport;
+
+ DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL);
+ hport = priv->usbclass.hport;
+
+ if (priv->ctrlreq)
+ {
+ (void)DRVR_FREE(hport->drvr, priv->ctrlreq);
+ }
+
+ if (priv->comm_rxbuf)
+ {
+ (void)DRVR_IOFREE(hport->drvr, priv->comm_rxbuf);
+ }
+
+ if (priv->notification)
+ {
+ (void)DRVR_IOFREE(hport->drvr, priv->notification);
+ }
+
+ if (priv->rxnetbuf)
+ {
+ (void)DRVR_IOFREE(hport->drvr, priv->rxnetbuf);
+ }
+
+ if (priv->txnetbuf)
+ {
+ (void)DRVR_IOFREE(hport->drvr, priv->txnetbuf);
+ }
+
+ priv->ctrlreq = NULL;
+ priv->comm_rxbuf = NULL;
+ priv->notification = NULL;
+ priv->rxnetbuf = NULL;
+ priv->txnetbuf = NULL;
+}
+
+/****************************************************************************
+ * struct usbhost_registry_s methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_create
+ *
+ * Description:
+ * This function implements the create() method of struct
+ * usbhost_registry_s.
+ * The create() method is a callback into the class implementation. It is
+ * used to (1) create a new instance of the USB host class state and to (2)
+ * bind a USB host driver "session" to the class instance. Use of this
+ * create() method will support environments where there may be multiple
+ * USB ports and multiple USB devices simultaneously connected.
+ *
+ * Input Parameters:
+ * hport - The hub hat manages the new class instance.
+ * id - In the case where the device supports multiple base classes,
+ * subclasses, or protocols, this specifies which to configure for.
+ *
+ * Returned Value:
+ * On success, this function will return a non-NULL instance of struct
+ * usbhost_class_s that can be used by the USB host driver to communicate
+ * with the USB host class. NULL is returned on failure; this function
+ * will fail only if the hport input parameter is NULL or if there are
+ * insufficient resources to create another USB host class instance.
+ *
+ ****************************************************************************/
+
+static FAR struct usbhost_class_s
+ *usbhost_create(FAR struct usbhost_hubport_s *hport,
+ FAR const struct usbhost_id_s *id)
+{
+ FAR struct usbhost_cdcmbim_s *priv;
+
+ /* Allocate a USB host class instance */
+
+ priv = usbhost_allocclass();
+ if (priv)
+ {
+ /* Initialize the allocated storage class instance */
+
+ memset(priv, 0, sizeof(struct usbhost_cdcmbim_s));
+
+ /* Assign a device number to this class instance */
+
+ if (usbhost_allocdevno(priv) == OK)
+ {
+ /* Initialize class method function pointers */
+
+ priv->usbclass.hport = hport;
+ priv->usbclass.connect = usbhost_connect;
+ priv->usbclass.disconnected = usbhost_disconnected;
+
+ /* The initial reference count is 1... One reference is held by
+ * the driver.
+ */
+
+ priv->crefs = 1;
+
+ /* Initialize semaphores (this works in the interrupt context) */
+
+ nxsem_init(&priv->exclsem, 0, 1);
+
+ /* Return the instance of the USB class driver */
+
+ return &priv->usbclass;
+ }
+ }
+
+ /* An error occurred. Free the allocation and return NULL on all failures */
+
+ if (priv)
+ {
+ usbhost_freeclass(priv);
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ * struct usbhost_class_s methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_connect
+ *
+ * Description:
+ * This function implements the connect() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to provide the device's configuration
+ * descriptor to the class so that the class may initialize properly
+ *
+ * Input Parameters:
+ * usbclass - The USB host class entry previously obtained from a call to
+ * create().
+ * configdesc - A pointer to a uint8_t buffer container the configuration
+ * descriptor.
+ * desclen - The length in bytes of the configuration descriptor.
+ *
+ * Returned Value:
+ * On success, zero (OK) is returned. On a failure, a negated errno value
+ * is returned indicating the nature of the failure
+ *
+ * NOTE that the class instance remains valid upon return with a failure.
+ * It is the responsibility of the higher level enumeration logic to call
+ * CLASS_DISCONNECTED to free up the class driver resources.
+ *
+ * Assumptions:
+ * - This function will *not* be called from an interrupt handler.
+ * - If this function returns an error, the USB host controller driver
+ * must call to DISCONNECTED method to recover from the error
+ *
+ ****************************************************************************/
+
+static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
+ FAR const uint8_t *configdesc, int desclen)
+{
+ FAR struct usbhost_cdcmbim_s *priv = (FAR struct usbhost_cdcmbim_s *)
+ usbclass;
+ int ret;
+
+ DEBUGASSERT(priv != NULL &&
+ configdesc != NULL &&
+ desclen >= sizeof(struct usb_cfgdesc_s));
+
+ /* Parse the configuration descriptor to get the endpoints */
+
+ ret = usbhost_cfgdesc(priv, configdesc, desclen);
+ if (ret < 0)
+ {
+ uerr("ERROR: usbhost_cfgdesc() failed: %d\n", ret);
+ }
+ else
+ {
+ /* Now configure the device and register the NuttX driver */
+
+ ret = usbhost_devinit(priv);
+ if (ret < 0)
+ {
+ uerr("ERROR: usbhost_devinit() failed: %d\n", ret);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usbhost_disconnected
+ *
+ * Description:
+ * This function implements the disconnected() method of struct
+ * usbhost_class_s. This method is a callback into the class
+ * implementation. It is used to inform the class that the USB device has
+ * been disconnected.
+ *
+ * Input Parameters:
+ * usbclass - The USB host class entry previously obtained from a call to
+ * create().
+ *
+ * Returned Value:
+ * On success, zero (OK) is returned. On a failure, a negated errno value
+ * is returned indicating the nature of the failure
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static int usbhost_disconnected(struct usbhost_class_s *usbclass)
+{
+ FAR struct usbhost_cdcmbim_s *priv = (FAR struct usbhost_cdcmbim_s *)
+ usbclass;
+ irqstate_t flags;
+
+ DEBUGASSERT(priv != NULL);
+
+ /* Set an indication to any users of the device that the device is no
+ * longer available.
+ */
+
+ flags = enter_critical_section();
+ priv->disconnected = true;
+
+ /* Now check the number of references on the class instance. If it is one,
+ * then we can free the class instance now. Otherwise, we will have to
+ * wait until the holders of the references free them by closing the
+ * block driver.
+ */
+
+ uinfo("crefs: %d\n", priv->crefs);
+ if (priv->crefs == 1)
+ {
+ /* Destroy the class instance. If we are executing from an interrupt
+ * handler, then defer the destruction to the worker thread.
+ * Otherwise, destroy the instance now.
+ */
+
+ if (up_interrupt_context())
+ {
+ /* Destroy the instance on the worker thread. */
+
+ uinfo("Queuing destruction: worker %p->%p\n",
+ priv->destroywork.worker, usbhost_destroy);
+ DEBUGASSERT(priv->destroywork.worker == NULL);
+ (void)work_queue(LPWORK, &priv->destroywork,
+ usbhost_destroy, priv, 0);
+ }
+ else
+ {
+ /* Do the work now */
+
+ usbhost_destroy(priv);
+ }
+ }
+
+ leave_critical_section(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: cdcmbim_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 cdcmbim_transmit(struct usbhost_cdcmbim_s *priv)
+{
+ struct usbhost_hubport_s *hport;
+ struct usb_cdc_ncm_nth16_s *nth;
+ struct usb_cdc_ncm_ndp16_s *ndp;
+ struct usb_cdc_ncm_dpe16_s *dpe;
+ ssize_t ret;
+ uint16_t len = 0;
+
+ hport = priv->usbclass.hport;
+
+ uinfo("transmit packet: %d bytes\n", priv->netdev.d_len);
+
+ /* Increment statistics */
+
+ NETDEV_TXPACKETS(&priv->netdev);
+
+ len = sizeof(struct usb_cdc_ncm_nth16_s);
+ nth = (struct usb_cdc_ncm_nth16_s *)priv->txnetbuf;
+
+ /* Begin filling NTH */
+
+ usbhost_putle32(nth->signature, USB_CDC_NCM_NTH16_SIGNATURE);
+ usbhost_putle16(nth->length, sizeof(struct usb_cdc_ncm_nth16_s));
+ usbhost_putle16(nth->sequence, priv->ntbseq++);
+
+ /* Payload directly after NTH */
+
+ memcpy(priv->txnetbuf + len, priv->netdev.d_buf, priv->netdev.d_len);
+ len += priv->netdev.d_len;
+
+ /* NDP after payload */
+
+ ndp = (struct usb_cdc_ncm_ndp16_s *)(priv->txnetbuf + len);
+ usbhost_putle16(ndp->next_ndp_index, 0);
+ usbhost_putle32(ndp->signature, 0x00535049);
+ usbhost_putle16(ndp->length, 16); /* NDP + (2 * DPE) */
+
+ /* Fill NDP and block info in NTH */
+
+ usbhost_putle16(nth->ndp_index, len);
+ len += 16;
+ usbhost_putle16(nth->block_length, len);
+
+ /* Fill first DPE */
+
+ dpe = ndp->dpe16;
+ usbhost_putle16(dpe->index, 0x0c);
+ usbhost_putle16(dpe->length, priv->netdev.d_len);
+
+ /* NULL DPE */
+
+ dpe++;
+ usbhost_putle16(dpe->index, 0);
+ usbhost_putle16(dpe->length, 0);
+
+ ret = DRVR_TRANSFER(hport->drvr, priv->bulkout, priv->txnetbuf, len);
+ if (ret < 0)
+ {
+ uerr("transfer returned error: %d\n", ret);
+ return ret;
+ }
+
+ /* If frame is multiple of wMaxPacketSize we must send a ZLP */
+
+ if ((len % priv->bulkmxpacket) == 0)
+ {
+ ret = DRVR_TRANSFER(hport->drvr, priv->bulkout,
+ priv->txnetbuf, 0);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_TRANSFER for ZLP failed: %d\n", (int)ret);
+ }
+ }
+
+ NETDEV_TXDONE(&priv->netdev);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: cdcmbim_receive
+ *
+ * Description:
+ * Handle a received packet.
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static void cdcmbim_receive(struct usbhost_cdcmbim_s *priv,
+ uint8_t *buf, size_t len)
+{
+ uinfo("received packet: %d len\n", len);
+
+ net_lock();
+
+ NETDEV_RXPACKETS(&priv->netdev);
+
+ /* Any ACK or other response packet generated by the network stack
+ * will always be shorter than the received packet, therefore it is
+ * safe to pass the received frame buffer directly.
+ */
+
+ priv->netdev.d_buf = buf;
+ priv->netdev.d_len = len;
+
+ switch (((struct ipv4_hdr_s *)buf)->vhl & IP_VERSION_MASK)
+ {
+#ifdef CONFIG_NET_IPv4
+ case IPv4_VERSION:
+ NETDEV_RXIPV4(&priv->netdev);
+ ipv4_input(&priv->netdev);
+
+ if (priv->netdev.d_len > 0)
+ {
+ cdcmbim_transmit(priv);
+ }
+ break;
+#endif
+#ifdef CONFIG_NET_IPv6
+ case IPv6_VERSION:
+ NETDEV_RXIPV6(&priv->netdev);
+ ipv6_input(&priv->netdev);
+
+ if (priv->dev.d_len > 0)
+ {
+ cdcmbim_transmit(priv);
+ }
+ break;
+#endif
+ default:
+ NETDEV_RXERRORS(dev);
+ }
+
+ net_unlock();
+}
+
+static void cdcmbim_txpoll_expiry(int argc, wdparm_t arg, ...)
+{
+ struct usbhost_cdcmbim_s *priv = (struct usbhost_cdcmbim_s *)arg;
+
+ work_queue(LPWORK, &priv->txpollwork, cdcmbim_txpoll_work, priv, 0);
+}
+
+static void cdcmbim_txpoll_work(void *arg)
+{
+ struct usbhost_cdcmbim_s *priv = (struct usbhost_cdcmbim_s *)arg;
+
+ net_lock();
+ priv->netdev.d_buf = priv->txpktbuf;
+
+ (void)devif_timer(&priv->netdev, cdcmbim_txpoll);
+
+ /* setup the watchdog poll timer again */
+
+ (void)wd_start(priv->txpoll, (1 * CLK_TCK),
+ cdcmbim_txpoll_expiry, 1, priv);
+ net_unlock();
+}
+
+/****************************************************************************
+ * Name: cdcmbim_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 timesout 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 cdcmbim_txpoll(struct net_driver_s *dev)
+{
+ struct usbhost_cdcmbim_s *priv = (struct usbhost_cdcmbim_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.
+ */
+
+ DEBUGASSERT(priv->netdev.d_buf == priv->txpktbuf);
+
+ usbhost_takesem(&priv->exclsem);
+
+ if (priv->netdev.d_len > 0)
+ {
+ if (!devif_loopback(&priv->netdev))
+ {
+ /* Send the packet */
+
+ cdcmbim_transmit(priv);
+ }
+ }
+
+ usbhost_givesem(&priv->exclsem);
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: cdcmbim_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the MBIM interface when an IP address is
+ * provided
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cdcmbim_ifup(struct net_driver_s *dev)
+{
+ struct usbhost_cdcmbim_s *priv = (struct usbhost_cdcmbim_s *)
+ dev->d_private;
+ struct usbhost_hubport_s *hport = priv->usbclass.hport;
+ int ret;
+
+#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
+
+ /* Start RX asynch on bulk in */
+
+ if (priv->bulkin)
+ {
+ ret = DRVR_ASYNCH(hport->drvr, priv->bulkin,
+ priv->rxnetbuf, CDCMBIM_NETBUF_SIZE,
+ usbhost_bulkin_callback, priv);
+ if (ret < 0)
+ {
+ uerr("ERROR: DRVR_ASYNCH failed on bulkin: %d\n", ret);
+ }
+ }
+
+ /* Start network TX poll */
+
+ (void)wd_start(priv->txpoll, (1 * CLK_TCK), cdcmbim_txpoll_expiry, 1,
+ (wdparm_t)priv);
+ priv->bifup = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: cdcmbim_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cdcmbim_ifdown(struct net_driver_s *dev)
+{
+ struct usbhost_cdcmbim_s *priv = (struct usbhost_cdcmbim_s *)
+ dev->d_private;
+ irqstate_t flags;
+
+ flags = enter_critical_section();
+
+ wd_cancel(priv->txpoll);
+
+ /* Mark the device "down" */
+
+ priv->bifup = false;
+
+ leave_critical_section(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: cdcmbim_txavail_work
+ *
+ * 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:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static void cdcmbim_txavail_work(void *arg)
+{
+ struct usbhost_cdcmbim_s *priv = (struct usbhost_cdcmbim_s *)arg;
+
+ net_lock();
+
+ priv->netdev.d_buf = priv->txpktbuf;
+
+ if (priv->bifup)
+ {
+ (void)devif_poll(&priv->netdev, cdcmbim_txpoll);
+ }
+
+ net_unlock();
+}
+
+/****************************************************************************
+ * Name: cdcmbim_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:
+ * Called from the network stack with the network locked.
+ *
+ ****************************************************************************/
+
+static int cdcmbim_txavail(struct net_driver_s *dev)
+{
+ struct usbhost_cdcmbim_s *priv = (struct usbhost_cdcmbim_s *)
+ dev->d_private;
+
+ if (work_available(&priv->txpollwork))
+ {
+ work_queue(LPWORK, &priv->txpollwork, cdcmbim_txavail_work, priv, 0);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: usbhost_cdcmbim_initialize
+ *
+ * Description:
+ * Initialize the USB class driver. This function should be called
+ * be platform-specific code in order to initialize and register support
+ * for the USB host class device.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * On success this function will return zero (OK); A negated errno value
+ * will be returned on failure.
+ *
+ ****************************************************************************/
+
+int usbhost_cdcmbim_initialize(void)
+{
+ /* Perform any one-time initialization of the class implementation */
+
+ /* Advertise our availability to support CDC MBIM devices */
+
+ return usbhost_registerclass(&g_cdcmbim);
+}
diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h
index 8218c0c..3bb4808 100644
--- a/include/nuttx/net/net.h
+++ b/include/nuttx/net/net.h
@@ -155,7 +155,8 @@ enum net_lltype_e
NET_LL_BLUETOOTH, /* Bluetooth */
NET_LL_IEEE80211, /* IEEE 802.11 */
NET_LL_IEEE802154, /* IEEE 802.15.4 MAC */
- NET_LL_PKTRADIO /* Non-standard packet radio */
+ NET_LL_PKTRADIO, /* Non-standard packet radio */
+ NET_LL_MBIM
};
/* This defines a bitmap big enough for one bit for each socket option */
diff --git a/include/nuttx/usb/cdc.h b/include/nuttx/usb/cdc.h
index e2b13a1..9030ecf 100644
--- a/include/nuttx/usb/cdc.h
+++ b/include/nuttx/usb/cdc.h
@@ -1,4 +1,4 @@
-/********************************************************************************************
+/*****************************************************************************
* include/nuttx/usb/cdc.h
*
* Copyright (C) 2011, 2017 Gregory Nutt. All rights reserved.
@@ -34,30 +34,38 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- ********************************************************************************************/
+ *****************************************************************************/
#ifndef __INCLUDE_NUTTX_USB_CDC_H
#define __INCLUDE_NUTTX_USB_CDC_H
-/********************************************************************************************
+/*****************************************************************************
* Included Files
- ********************************************************************************************/
+ *****************************************************************************/
#include <nuttx/config.h>
-/********************************************************************************************
+/*****************************************************************************
* Pre-processor Definitions
- ********************************************************************************************/
-/* Device Class Codes ***********************************************************************/
-/* Table 14: Communication Device Class Code (see definition USB_CLASS_CDC in usb.h) */
-/* Table 18: Data Interface Class Code (see definition USB_CLASS_CDC_DATA in usb.h) */
+ *****************************************************************************/
+
+/* Device Class Codes ********************************************************/
+
+/* Table 14: Communication Device Class Code (see definition USB_CLASS_CDC in
+ * usb.h)
+ */
+
+/* Table 18: Data Interface Class Code (see definition USB_CLASS_CDC_DATA in
+ * usb.h)
+ */
+
+/* Communication Interface Class Codes ***************************************/
-/* Communication Interface Class Codes ******************************************************/
/* Table 15: Communication Interface Class Code */
#define CDC_CLASS_COMM 0x02 /* Communication Interface Class */
-/* Communication Interface Sub-Class Codes **************************************************/
+/* Communication Interface Sub-Class Codes ***********************************/
#define CDC_SUBCLASS_NONE 0x00 /* Reserved */
#define CDC_SUBCLASS_DLC 0x01 /* Direct Line Control Model */
@@ -67,26 +75,35 @@
#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-0x7f Reserved (future use) */
+ /* 0x08-0x0d Reserved (future use) */
+#define CDC_SUBCLASS_MBIM 0x0e /* MBIM Control Model */
+ /* 0x0f-0x7f Reserved (future use) */
/* 0x80-0xfe Reserved (vendor specific) */
-/* Communication Interface Class Protocol Codes ********************************************/
+
+/* Communication Interface Class Protocol Codes ******************************/
+
/* Table 17: Communication Interface Class Control Protocol Codes */
#define CDC_PROTO_NONE 0x00 /* No class specific protocol required */
-#define CDC_PROTO_ATM 0x01 /* Common AT commands (also known as Hayes compatible) */
+#define CDC_PROTO_ATM 0x01 /* Common AT commands (also known as Hayes
+ * compatible)
+ */
/* 0x02-0xfe Reserved (future use) */
#define CDC_PROTO_VENDOR 0xff /* Vendor-specific */
-/* Data Interface Sub-Class Codes ***********************************************************/
+/* Data Interface Sub-Class Codes ********************************************/
+
/* None defined, should be zero */
#define CDC_DATA_SUBCLASS_NONE 0x00
-/* Date Interface Class Protocol Codes ******************************************************/
+/* Date Interface Class Protocol Codes ***************************************/
+
/* Table 19: Data Interface Class Protocol Codes */
#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_ISDN 0x30 /* Physical interface protocol for ISDN BRI */
#define CDC_DATA_PROTO_HDLC 0x31 /* HDLC */
#define CDC_DATA_PROTO_TRANSP 0x32 /* Transparent */
@@ -107,7 +124,8 @@
*/
#define CDC_DATA_PROTO_VENDOR 0xff /* Vendor-specific */
-/* Requests and Notifications ***************************************************************/
+/* Requests and Notifications ************************************************/
+
/* Table 2: Requests, Direct Line Control Model */
#define DLC_SET_AUX_LINE_STATE 0x10 /* Request to connect or disconnect secondary jack from
@@ -128,6 +146,7 @@
#define DLC_RING_AUX_JACK 0x15 /* Request for a ring signal to be generated on secondary
* phone jack. (Optional)
*/
+
/* Table 3: Notifications, Direct Line Control Model */
#define DLC_AUX_JACK_HOOK_STATE 0x08 /* Indicates hook state of secondary device plugged
@@ -136,6 +155,7 @@
#define DLC_RING_DETECT 0x09 /* Message to notify host that ring voltage was
* detected on POTS interface. (Required)
*/
+
/* Table 4: Requests, Abstract Control Model */
#define ACM_SEND_COMMAND 0x00 /* Issues a command in the format of the supported
@@ -164,6 +184,7 @@
*/
#define ACM_SEND_BREAK 0x23 /* Sends special carrier
*/
+
/* Table 5: Notifications, Abstract Control Model */
#define ACM_NETWORK_CONNECTION 0x00 /* Notification to host of network connection status.
@@ -175,6 +196,7 @@
#define ACM_SERIAL_STATE 0x20 /* Returns the current state of the carrier detect, DSR,
* break, and ring signal. (Optional)
*/
+
/* Table 6: Requests, Telephone Control Model */
#define TCM_SET_COMM_FEATURE 0x02 /* Used to set a unique communication feature, which is
@@ -208,12 +230,14 @@
*/
#define TCM_DIAL_DIGITS 0x36 /* Dials digits on the network connection. (Required)
*/
+
/* Table 7: Notifications, Telephone Control Model */
#define TCM_CALL_STATE_CHANGE 0x28 /* DReports a state change on a call. (Required)
*/
#define TCM_LINE_STATE_CHANGE 0x29 /* DReports a state change on a line. (Optional)
*/
+
/* Table 8: Requests, Multi-Channel Model */
#define MCM_SET_UNIT_PARAM 0x37 /* Used to set a Unit specific parameter (Optional)
@@ -223,11 +247,13 @@
#define MCM_CLEAR_UNIT_PARAM 0x39 /* Used to set a Unit specific parameter to its default
* state. (Optional)
*/
+
/* Table 9: Request, CAPI Control Model */
#define CAPI_GET_PROFILE 0x3a /* Returns the implemented capabilities of the device
* (Required)
*/
+
/* Table 10: Requests, Ethernet Networking Control Model */
#define ECM_SEND_COMMAND 0x00 /* Issues a command in the format of the supported
@@ -267,6 +293,7 @@
* transmitted, frames received, and bad frames received.
* (Optional)
*/
+
/* Table 11: Notifications, Ethernet Networking Control Model */
#define ECM_NETWORK_CONNECTION 0x00 /* Reports whether or not the physical layer (modem,
@@ -277,6 +304,7 @@
*/
#define ECM_SPEED_CHANGE 0x2a /* Reports a change in upstream or downstream (Required)
*/
+
/* Table 12: Requests, ATM Networking Control Model */
#define ATM_SEND_COMMAND 0x00 /* Issues a command in the format of the supported control
@@ -302,6 +330,7 @@
#define ATM_GET_VC_STATISTICS 0x53 /* Retrieves statistics from the ATM Networking device for
* a particular VPI/VCI. (Optional)
*/
+
/* Table 13: Requests, Ethernet and ATM Networking Control Model */
#define ATM_NETWORK_CONNECTION 0x00 /* Reports whether or not the physical layer (modem,
@@ -317,7 +346,8 @@
*/
#define ECM_SPEED_CHANGE ATM_SPEED_CHANGE
-/* Descriptors ******************************************************************************/
+/* Descriptors ***************************************************************/
+
/* Table 25: bDescriptor SubType in Functional Descriptors */
#define CDC_DSUBTYPE_HDR 0x00 /* Header Functional Descriptor, which marks the
@@ -340,6 +370,7 @@
#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_MBIM 0x1b /* MBIM Functional Descriptor */
/* 0x11-0xff Reserved (future use) */
/* Table 42: Ethernet Statistics Capabilities */
@@ -390,6 +421,7 @@
#define FEATURE_COUNTRY_SETTING 0x02 /* Country code in hexadecimal format as defined in
* ISO 3166
*/
+
/* Table 49: POTS Relay Configuration Values */
#define POTS_ON_HOOK 0x0000
@@ -515,18 +547,22 @@
* been detected with HEC errors in the cell
* header and successfully corrected.
*/
+
/* Table 66: ATM VC Selector Codes */
-#define VC_US_CELLS_SENT 0x01 /* The number of cells that have been sent upstream to
- * the WAN link for the specified VPI/VCI since the
- * device has been powered on or reset
+#define VC_US_CELLS_SENT 0x01 /* The number of cells that have been
+ * sent upstream to the WAN link for the
+ * specified VPI/VCI since the device
+ * has been powered on or reset
*/
-#define VC_DS_CELLS_RECEIVED 0x02 /* The number of cells that have been received
- * downstream from the WAN link for the specified
- * VPI/VCI since the device has been
- * powered on or reset
+#define VC_DS_CELLS_RECEIVED 0x02 /* The number of cells that have been
+ * received downstream from the WAN link
+ * for the specified VPI/VCI since the
+ * device has been powered on or reset
*/
-/* Notifications ****************************************************************************/
+
+/* Notifications *************************************************************/
+
/* Table 69: UART State Bitmap Values */
#define CDC_UART_RXCARRIER (1 << 0) /* bRxCarrier State of receiver carrier detection
@@ -554,12 +590,14 @@
#define CDCACM_UART_DCD CDC_UART_RXCARRIER
#define CDCACM_UART_DSR CDC_UART_TXCARRIER
-/* "SerialState is used like a real interrupt status register. Once a notification has been
- * sent, the device will reset and reevaluate the different signals. For the consistent
- * signals like carrier detect or transmission carrier, this will mean another notification
- * will not be generated until there is a state change. For the irregular signals like
- * break, the incoming ring signal, or the overrun error state, this will reset their values
- * to zero and again will not send another notification until their state changes."
+/* "SerialState is used like a real interrupt status register. Once a
+ * notification has been sent, the device will reset and reevaluate the
+ * different signals. For the consistent signals like carrier detect or
+ * transmission carrier, this will mean another notification will not be
+ * generated until there is a state change. For the irregular signals like
+ * break, the incoming ring signal, or the overrun error state, this will
+ * reset their values to zero and again will not send another notification
+ * until their state changes."
*/
#define CDC_UART_CONSISTENT (CDC_UART_RXCARRIER | CDC_UART_TXCARRIER)
@@ -583,9 +621,9 @@
#define CDC_LINEST_OFFHOOK 0x0002 /* Hook-switch has gone off hook */
#define CDC_LINEST_ONHOOK 0x0003 /* Hook-switch has gone on hook */
-/********************************************************************************************
+/*****************************************************************************
* Public Types
- ********************************************************************************************/
+ *****************************************************************************/
/* Table 1: Data Class Protocol Wrapper */
@@ -594,10 +632,13 @@ struct cdc_protowrapper_s
uint8_t size[2]; /* Size of wrapper in bytes */
uint8_t dstproto; /* bDstProtocol, Destination protocol ID */
uint8_t srcproto; /* bSrcProtocol, Source protocol ID */
- uint8_t data[1]; /* Data payload, actual size depends of size of the wrapper */
+ uint8_t data[1]; /* Data payload, actual size depends of size of the
+ * wrapper
+ */
};
-/* Functional Descriptors *******************************************************************/
+/* Functional Descriptors ****************************************************/
+
/* Table 23: Functional Descriptor General Format */
struct cdc_funcdesc_s
@@ -758,14 +799,18 @@ struct cdc_usbterm_funcdesc_s
struct cdc_netchan_funcdesc_s
{
uint8_t size; /* bFunctionLength, Size of this descriptor */
- uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */
- uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_NETCHAN as defined in Table 25 */
- uint8_t id; /* bEntityId, Constant uniquely identifying the Terminal */
- uint8_t name; /* iName, Index of string descriptor, describing the name of the Network
- * Channel Terminal
- */
- uint8_t index; /* bChannelIndex, The channel index of the associated network channel */
- uint8_t phyif; /* bPhysicalInterface, Type of physical interface */
+ uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */
+ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_NETCHAN as defined in
+ * Table 25 */
+ uint8_t id; /* bEntityId, Constant uniquely identifying the
+ * Terminal */
+ uint8_t name; /* iName, Index of string descriptor, describing the name
+ * of the Network Channel Terminal
+ */
+ uint8_t index; /* bChannelIndex, The channel index of the associated
+ * network channel
+ */
+ uint8_t phyif; /* bPhysicalInterface, Type of physical interface */
};
#define SIZEOF_NETCHAN_FUNCDESC 7
@@ -827,22 +872,28 @@ struct cdc_capi_funcdesc_s
struct cdc_ecm_funcdesc_s
{
- uint8_t size; /* bFunctionLength, Size of this descriptor */
- uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */
- uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_ECM as defined in Table 25 */
- uint8_t mac; /* iMACAddress, Index of the 48bit Ethernet MAC address string descriptor */
- uint8_t stats[4]; /* bmEthernetStatistics, Indicates which Ethernet statistics functions
- * the device collects. See Table 42.
- */
- uint8_t maxseg[2]; /* wMaxSegmentSize, The maximum segment size that the Ethernet device is
- * capable of supporting.
- */
- uint8_t nmcflts[2]; /* wNumberMCFilters, Contains the number of multicast filters that can be
- * configured by the host.
- */
- uint8_t npwrflts; /* bNumberPowerFilters, Contains the number of pattern filters that are
- * available for causing wake-up of the host.
- */
+ uint8_t size; /* bFunctionLength, Size of this descriptor */
+ uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */
+ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_ECM as defined in
+ * Table 25.
+ */
+ uint8_t mac; /* iMACAddress, Index of the 48bit Ethernet MAC address
+ * string descriptor.
+ */
+ uint8_t stats[4]; /* bmEthernetStatistics, Indicates which Ethernet
+ * statistics functions the device collects.
+ * See Table 42.
+ */
+ uint8_t maxseg[2]; /* wMaxSegmentSize, The maximum segment size that the
+ * Ethernet device is capable of supporting.
+ */
+ uint8_t nmcflts[2]; /* wNumberMCFilters, Contains the number of multicast
+ * filters that can be configured by the host.
+ */
+ uint8_t npwrflts; /* bNumberPowerFilters, Contains the number of pattern
+ * filters that are available for causing wake-up of
+ * the host.
+ */
};
#define SIZEOF_ECM_FUNCDESC 13
@@ -853,26 +904,33 @@ struct cdc_atm_funcdesc_s
{
uint8_t size; /* bFunctionLength, Size of this descriptor */
uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */
- uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_ATM as defined in Table 25 */
- uint8_t endid; /* iEndSystemIdentifier, Index of End System Identifier string descriptor */
- uint8_t datacaps; /* bmDataCapabilities, The ATM data types the device supports */
- uint8_t devstats; /* bmATMDeviceStatistics, Indicates which optional statistics functions the
- * device collects.
+ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_ATM as defined in
+ * Table 25.
+ */
+ uint8_t endid; /* iEndSystemIdentifier, Index of End System Identifier
+ * string descriptor.
*/
- uint8_t mxseg2[2]; /* wType2MaxSegmentSize, The maximum segment size that the Type 2 device is
- * capable of supporting.
+ uint8_t datacaps; /* bmDataCapabilities, The ATM data types the device
+ * supports.
*/
- uint8_t mxseg3[2]; /* wType3MaxSegmentSize, The maximum segment size that the Type 3 device is
- * capable of supporting
+ uint8_t devstats; /* bmATMDeviceStatistics, Indicates which optional
+ * statistics functions the device collects.
*/
- uint8_t mxvc[2]; /* wMaxVC, The maximum number of simultaneous virtual circuits the device is
- * capable of supporting
+ uint8_t mxseg2[2]; /* wType2MaxSegmentSize, The maximum segment size that
+ * the Type 2 device is capable of supporting.
+ */
+ uint8_t mxseg3[2]; /* wType3MaxSegmentSize, The maximum segment size that
+ * the Type 3 device is capable of supporting
+ */
+ uint8_t mxvc[2]; /* wMaxVC, The maximum number of simultaneous virtual
+ * circuits the device is capable of supporting
*/
};
#define SIZEOF_ATM_FUNCDESC 12
-/* Descriptor Data Structures ***************************************************************/
+/* Descriptor Data Structures ************************************************/
+
/* Table 50: Line Coding Structure */
struct cdc_linecoding_s
@@ -890,15 +948,15 @@ struct cdc_linecoding_s
struct cdc_linestatus_s
{
uint8_t size[2]; /* wLength, Size of this structure, in bytes */
- uint8_t ringer[4]; /* dwRingerBitmap, Ringer Configuration bitmap for this line */
+ uint8_t ringer[4]; /* dwRingerBitmap, Ringer Conf bitmap for this line */
uint8_t line[4]; /* dwLineState, Defines current state of the line */
- uint32_t call[1]; /* dwCallStateN, Defines current state of call N on the line */
+ uint32_t call[1]; /* dwCallStateN, Current state of call N on the line */
};
-/* Messages are formatted as a standardized 8-byte header, followed by a variable-length
- * data field. The header identifies the kind of notification, and the interface associated
- * with the notification; it also indicates the length of the variable length portion of
- * the message
+/* Messages are formatted as a standardized 8-byte header, followed by a
+ * variable-length data field. The header identifies the kind of notification,
+ * and the interface associated with the notification; it also indicates the
+ * length of the variable length portion of the message
*/
struct cdc_notification_s
@@ -918,12 +976,15 @@ struct cdc_notification_s
struct cdc_unitparm_s
{
uint8_t id; /* bEntityId, Unit ID */
- uint8_t index; /* bParameterIndex, A zero based value indicating Unit parameter index */
+ uint8_t index; /* bParameterIndex, A zero based value indicating Unit
+ * parameter index
+ */
};
/* Table 61: Power Management Pattern Filter Structure */
-/* Notification Data Structures *************************************************************/
+/* Notification Data Structures **********************************************/
+
/* Table 72: ConnectionSpeedChange Data Structure */
struct cdc_speedchange_s
diff --git a/net/netdev/netdev_register.c b/net/netdev/netdev_register.c
index 85483dc..d8ef03f 100644
--- a/net/netdev/netdev_register.c
+++ b/net/netdev/netdev_register.c
@@ -55,6 +55,7 @@
#define NETDEV_PAN_FORMAT "pan%d"
#define NETDEV_WLAN_FORMAT "wlan%d"
#define NETDEV_WPAN_FORMAT "wpan%d"
+#define NETDEV_MBIM_FORMAT "wwan%d"
#if defined(CONFIG_DRIVERS_IEEE80211) /* Usually also has CONFIG_NET_ETHERNET */
# define NETDEV_DEFAULT_FORMAT NETDEV_WLAN_FORMAT
@@ -313,6 +314,11 @@ int netdev_register(FAR struct net_driver_s *dev, enum net_lltype_e lltype)
devfmt = NETDEV_TUN_FORMAT;
break;
#endif
+ case NET_LL_MBIM:
+ dev->d_llhdrlen = 0;
+ dev->d_pktsize = 1200;
+ devfmt = NETDEV_MBIM_FORMAT;
+ break;
default:
nerr("ERROR: Unrecognized link type: %d\n", lltype);