From 911a1d2fafcd6eff4c99765b184a6e82f816b312 Mon Sep 17 00:00:00 2001 From: patacongo Date: Mon, 12 Sep 2011 23:16:16 +0000 Subject: Working toward CDC serial USB device git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3952 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/drivers/usbdev/cdc_serial.c | 2259 ++++++++++++++++++++++++++++++++++ nuttx/include/nuttx/usb/cdc.h | 45 +- nuttx/include/nuttx/usb/cdc_serial.h | 91 +- nuttx/include/nuttx/usb/usb.h | 24 +- 4 files changed, 2376 insertions(+), 43 deletions(-) create mode 100644 nuttx/drivers/usbdev/cdc_serial.c diff --git a/nuttx/drivers/usbdev/cdc_serial.c b/nuttx/drivers/usbdev/cdc_serial.c new file mode 100644 index 000000000..21f3680db --- /dev/null +++ b/nuttx/drivers/usbdev/cdc_serial.c @@ -0,0 +1,2259 @@ +/**************************************************************************** + * drivers/usbdev/cdc_serial.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Descriptors ****************************************************************/ +/* These settings are not modifiable via the NuttX configuration */ + +#define CDC_VERSIONNO 0x010a /* CDC version number 1.10 */ +#define CDCSER_CONFIGIDNONE (0) /* Config ID means to return to address mode */ +#define CDCSER_INTERFACEID (0) +#define CDCSER_ALTINTERFACEID (0) + +/* Device descriptor values */ + +#define CDCSER_VERSIONNO (0x0101) /* Device version number 1.1 */ +#define CDCSER_NCONFIGS (1) /* Number of configurations supported */ + +/* Configuration descriptor values */ + +#define CDCSER_NINTERFACES (2) /* Number of interfaces in the configuration */ +#define CDCSER_CONFIGID (1) /* The only supported configuration ID */ + +/* Endpoint configuration */ + +#define CDCSER_EPINTIN_ADDR (USB_DIR_IN|CONFIG_CDCSER_EPINTIN) +#define CDCSER_EPINTIN_ATTR (USB_EP_ATTR_XFER_INT) + +#define CDCSER_EPOUTBULK_ADDR (CONFIG_CDCSER_EPBULKOUT) +#define CDCSER_EPOUTBULK_ATTR (USB_EP_ATTR_XFER_BULK) + +#define CDCSER_EPINBULK_ADDR (USB_DIR_IN|CONFIG_CDCSER_EPBULKIN) +#define CDCSER_EPINBULK_ATTR (USB_EP_ATTR_XFER_BULK) + +/* String language */ + +#define CDCSER_STR_LANGUAGE (0x0409) /* en-us */ + +/* Descriptor strings */ + +#define CDCSER_MANUFACTURERSTRID (1) +#define CDCSER_PRODUCTSTRID (2) +#define CDCSER_SERIALSTRID (3) +#define CDCSER_CONFIGSTRID (4) + +/* Number of individual descriptors in the configuration descriptor */ + +#define CDCSER_CFGGROUP_SIZE (9) + +/* The size of the config descriptor: (9 + 2*9 + 3*7 + 4 + 5 + 5) = 62 */ + +#define SIZEOF_CDCSER_CFGDESC \ + (USB_SIZEOF_CFGDESC + 2*USB_SIZEOF_IFDESC + 3*USB_SIZEOF_EPDESC + SIZEOF_ACM_FUNCDESC + SIZEOF_HDR_FUNCDESC + SIZEOF_UNION_FUNCDESC(1)) + +/* Buffer big enough for any of our descriptors (the config descriptor is the + * biggest). + */ + +#define CDCSER_MXDESCLEN (64) + +/* Misc Macros ****************************************************************/ +/* min/max macros */ + +#ifndef min +# define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef max +# define max(a,b) ((a)>(b)?(a):(b)) +#endif + +/* Trace values *************************************************************/ + +#define CDCSER_CLASSAPI_SETUP TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_SETUP) +#define CDCSER_CLASSAPI_SHUTDOWN TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_SHUTDOWN) +#define CDCSER_CLASSAPI_ATTACH TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_ATTACH) +#define CDCSER_CLASSAPI_DETACH TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_DETACH) +#define CDCSER_CLASSAPI_IOCTL TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_IOCTL) +#define CDCSER_CLASSAPI_RECEIVE TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_RECEIVE) +#define CDCSER_CLASSAPI_RXINT TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_RXINT) +#define CDCSER_CLASSAPI_RXAVAILABLE TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_RXAVAILABLE) +#define CDCSER_CLASSAPI_SEND TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_SEND) +#define CDCSER_CLASSAPI_TXINT TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_TXINT) +#define CDCSER_CLASSAPI_TXREADY TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_TXREADY) +#define CDCSER_CLASSAPI_TXEMPTY TRACE_EVENT(TRACE_CLASSAPI_ID, CDCSER_TRACECLASSAPI_TXEMPTY) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Container to support a list of requests */ + +struct usbser_req_s +{ + FAR struct usbser_req_s *flink; /* Implements a singly linked list */ + FAR struct usbdev_req_s *req; /* The contained request */ +}; + +/* This structure describes the internal state of the driver */ + +struct usbser_dev_s +{ + FAR struct uart_dev_s serdev; /* Serial device structure */ + FAR struct usbdev_s *usbdev; /* usbdev driver pointer */ + + uint8_t config; /* Configuration number */ + uint8_t nwrq; /* Number of queue write requests (in reqlist)*/ + uint8_t nrdq; /* Number of queue read requests (in epbulkout) */ + bool rxenabled; /* true: UART RX "interrupts" enabled */ + uint8_t linest[7]; /* Fake line status */ + int16_t rxhead; /* Working head; used when rx int disabled */ + + FAR struct usbdev_ep_s *epintin; /* Interrupt IN endpoint structure */ + FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */ + FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */ + FAR struct usbdev_req_s *ctrlreq; /* Control request */ + struct sq_queue_s reqlist; /* List of write request containers */ + + /* Pre-allocated write request containers. The write requests will + * be linked in a free list (reqlist), and used to send requests to + * EPBULKIN; Read requests will be queued in the EBULKOUT. + */ + + struct usbser_req_s wrreqs[CONFIG_CDCSER_NWRREQS]; + struct usbser_req_s rdreqs[CONFIG_CDCSER_NWRREQS]; + + /* Serial I/O buffers */ + + char rxbuffer[CONFIG_CDCSER_RXBUFSIZE]; + char txbuffer[CONFIG_CDCSER_TXBUFSIZE]; +}; + +/* The internal version of the class driver */ + +struct usbser_driver_s +{ + struct usbdevclass_driver_s drvr; + FAR struct usbser_dev_s *dev; +}; + +/* This is what is allocated */ + +struct usbser_alloc_s +{ + struct usbser_dev_s dev; + struct usbser_driver_s drvr; +}; + +/* Describes one description in the group of descriptors forming the + * total configuration descriptor. + */ + +struct cfgdecsc_group_s +{ + FAR void *desc; + unsigned int size; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Transfer helpers *********************************************************/ + +static uint16_t usbclass_fillrequest(FAR struct usbser_dev_s *priv, + uint8_t *reqbuf, uint16_t reqlen); +static int usbclass_sndpacket(FAR struct usbser_dev_s *priv); +static inline int usbclass_recvpacket(FAR struct usbser_dev_s *priv, + uint8_t *reqbuf, uint16_t reqlen); + +/* Request helpers *********************************************************/ + +static struct usbdev_req_s *usbclass_allocreq(FAR struct usbdev_ep_s *ep, + uint16_t len); +static void usbclass_freereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); + +/* Configuration ***********************************************************/ + +static int usbclass_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc); +#ifdef CONFIG_USBDEV_DUALSPEED +static void usbclass_mkepbulkdesc(const struct usb_epdesc_s *indesc, + uint16_t mxpacket, struct usb_epdesc_s *outdesc); +static int16_t usbclass_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type); +#else +static int16_t usbclass_mkcfgdesc(uint8_t *buf); +#endif +static void usbclass_resetconfig(FAR struct usbser_dev_s *priv); +static int usbclass_setconfig(FAR struct usbser_dev_s *priv, + uint8_t config); + +/* Completion event handlers ***********************************************/ + +static void usbclass_ep0incomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); +static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); +static void usbclass_wrcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req); + +/* USB class device ********************************************************/ + +static int usbclass_bind(FAR struct usbdev_s *dev, + FAR struct usbdevclass_driver_s *driver); +static void usbclass_unbind(FAR struct usbdev_s *dev); +static int usbclass_setup(FAR struct usbdev_s *dev, + const struct usb_ctrlreq_s *ctrl); +static void usbclass_disconnect(FAR struct usbdev_s *dev); + +/* Serial port *************************************************************/ + +static int usbser_setup(FAR struct uart_dev_s *dev); +static void usbser_shutdown(FAR struct uart_dev_s *dev); +static int usbser_attach(FAR struct uart_dev_s *dev); +static void usbser_detach(FAR struct uart_dev_s *dev); +static void usbser_rxint(FAR struct uart_dev_s *dev, bool enable); +static void usbser_txint(FAR struct uart_dev_s *dev, bool enable); +static bool usbser_txempty(FAR struct uart_dev_s *dev); + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/* USB class device ********************************************************/ + +static const struct usbdevclass_driverops_s g_driverops = +{ + usbclass_bind, /* bind */ + usbclass_unbind, /* unbind */ + usbclass_setup, /* setup */ + usbclass_disconnect, /* disconnect */ + NULL, /* suspend */ + NULL, /* resume */ +}; + +/* Serial port *************************************************************/ + +static const struct uart_ops_s g_uartops = +{ + usbser_setup, /* setup */ + usbser_shutdown, /* shutdown */ + usbser_attach, /* attach */ + usbser_detach, /* detach */ + NULL, /* ioctl */ + NULL, /* receive */ + usbser_rxint, /* rxinit */ + NULL, /* rxavailable */ + NULL, /* send */ + usbser_txint, /* txinit */ + NULL, /* txready */ + usbser_txempty /* txempty */ +}; + +/* USB descriptor templates these will be copied and modified **************/ + +static const struct usb_devdesc_s g_devdesc = +{ + USB_SIZEOF_DEVDESC, /* len */ + USB_DESC_TYPE_DEVICE, /* type */ + {LSBYTE(0x0200), MSBYTE(0x0200)}, /* usb */ + USB_CLASS_CDC, /* class */ + CDC_SUBCLASS_NONE, /* subclass */ + CDC_PROTO_NONE, /* protocol */ + CONFIG_CDCSER_EP0MAXPACKET, /* maxpacketsize */ + { LSBYTE(CONFIG_CDCSER_VENDORID), /* vendor */ + MSBYTE(CONFIG_CDCSER_VENDORID) }, + { LSBYTE(CONFIG_CDCSER_PRODUCTID), /* product */ + MSBYTE(CONFIG_CDCSER_PRODUCTID) }, + { LSBYTE(CDCSER_VERSIONNO), /* device */ + MSBYTE(CDCSER_VERSIONNO) }, + CDCSER_MANUFACTURERSTRID, /* imfgr */ + CDCSER_PRODUCTSTRID, /* iproduct */ + CDCSER_SERIALSTRID, /* serno */ + CDCSER_NCONFIGS /* nconfigs */ +}; + +/* Configuration descriptor */ + +static const struct usb_cfgdesc_s g_cfgdesc; +{ + USB_SIZEOF_CFGDESC, /* len */ + USB_DESC_TYPE_CONFIG, /* type */ + { + LSBYTE(SIZEOF_CDCSER_CFGDESC), /* LS totallen */ + MSBYTE(SIZEOF_CDCSER_CFGDESC) /* MS totallen */ + }, + CDCSER_NINTERFACES, /* ninterfaces */ + CDCSER_CONFIGID, /* cfgvalue */ + CDCSER_CONFIGSTRID, /* icfg */ + USB_CONFIG_ATTR_ONE|SELFPOWERED|REMOTEWAKEUP, /* attr */ + (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* mxpower */ +}; + +/* Notification interface */ + +static const struct usb_ifdesc_s g_notifdesc = +{ + USB_SIZEOF_IFDESC, /* len */ + USB_DESC_TYPE_INTERFACE, /* type */ + 0, /* ifno */ + 0, /* alt */ + 1, /* neps */ + USB_CLASS_CDC, /* class */ + CDC_SUBCLASS_ACM, /* subclass */ + CDC_PROTO_ATM, /* proto */ + CDCSER_NOTIFSTRID /* iif */ +}; + +/* Header functional descriptor */ + +static const struct cdc_hdr_funcdesc_s g_funchdr = +{ + SIZEOF_HDR_FUNCDESC, /* size */ + USB_DESC_TYPE_CSINTERFACE, /* type */ + CDC_DSUBTYPE_HDR, /* subtype */ + { + LSBYTE(CDC_VERSIONNO), /* LS cdc */ + MSBYTE(CDC_VERSIONNO) /* MS cdc */ + } +}; + +/* ACM functional descriptor */ + +static const struct cdc_acm_funcdesc_s g_acmfunc = +{ + SIZEOF_ACM_FUNCDESC, /* size */ + USB_DESC_TYPE_CSINTERFACE, /* type */ + CDC_DSUBTYPE_ACM, /* subtype */ + 0x06 /* caps */ +}; + +/* Union functional descriptor */ + +static const struct cdc_union_funcdesc_s g_unionfunc = +{ + SIZEOF_UNION_FUNCDESC(1), /* size */ + USB_DESC_TYPE_CSINTERFACE, /* type */ + CDC_DSUBTYPE_UNION, /* subtype */ + 0, /* master */ + 1 /* slave[0] */ +}; + +/* Interrupt IN endpoint descriptor */ + +static const struct usb_epdesc_s g_epintindesc = +{ + USB_SIZEOF_EPDESC, /* len */ + USB_DESC_TYPE_ENDPOINT, /* type */ + CDCSER_EPINTIN_ADDR, /* addr */ + CDCSER_EPINTIN_ATTR, /* attr */ + { LSBYTE(CONFIG_CDCSER_EPINTIN_SIZE), /* maxpacket */ + MSBYTE(CONFIG_CDCSER_EPINTIN_SIZE) + }, + 0xff /* interval */ +}; + +/* Data interface descriptor */ + +static const struct usb_ifdesc_s g_dataifdesc = +{ + USB_SIZEOF_IFDESC, /* len */ + USB_DESC_TYPE_INTERFACE, /* type */ + 1, /* ifno */ + 0, /* alt */ + 2, /* neps */ + USB_CLASS_CDC_DATA, /* class */ + CDC_DATA_SUBCLASS_NONE, /* subclass */ + CDC_DATA_PROTO_NONE, /* proto */ + CDCSER_DATAIFSTRID /* iif */ +}; + +/* Bulk OUT endpoint descriptor */ + +static const struct usb_epdesc_s g_epbulkoutdesc = +{ + USB_SIZEOF_EPDESC, /* len */ + USB_DESC_TYPE_ENDPOINT, /* type */ + CDCSER_EPOUTBULK_ADDR, /* addr */ + CDCSER_EPINTIN_ATTR, /* attr */ + { LSBYTE(CONFIG_CDCSER_BULKOUT_EPSIZE), /* maxpacket */ + MSBYTE(CONFIG_CDCSER_BULKOUT_EPSIZE) + }, + 1 /* interval */ +}; + +/* Bulk IN endpoint descriptor */ + +static const struct usb_epdesc_s g_epbulkindesc = +{ + USB_SIZEOF_EPDESC, /* len */ + USB_DESC_TYPE_ENDPOINT, /* type */ + CDCSER_EPINBULK_ADDR, /* addr */ + CDCSER_EPINBULK_ATTR, /* attr */ + { LSBYTE(CONFIG_CDCSER_BULKIN_EPSIZE), /* maxpacket */ + MSBYTE(CONFIG_CDCSER_BULKIN_EPSIZE) + }, + 1 /* interval */ +}; + +/* The components of the the configuration descriptor are maintained as + * a collection of separate descriptor structure coordinated by the + * following array. These descriptors could have been combined into + * one larger "super" configuration descriptor structure. However, I + * have concerns about compiler-dependent alignment and packing. Since + * the individual structures consist only of byte types, alignment and + * packing is not an issue. And since the are concatentated at run time + * instead of compile time, there should no issues there either. + */ + +static const struct cfgdecsc_group_s g_cfggroup[CDCSER_CFGGROUP_SIZE] = { + { + (FAR void *)&g_cfgdesc, /* 1. Configuration descriptor */ + USB_SIZEOF_CFGDESC + }, + { + (FAR void *)&g_notifdesc, /* 2. Notification interface */ + USB_SIZEOF_IFDESC + }, + { + (FAR void *)&g_funchdr, /* 3. Header functional descriptor */ + SIZEOF_HDR_FUNCDESC + }, + { + (FAR void *)&g_acmfunc, /* 4. ACM functional descriptor */ + SIZEOF_ACM_FUNCDESC + }, + { + (FAR void *)&g_unionfunc, /* 5. Union functional descriptor */ + SIZEOF_UNION_FUNCDESC(1) + }, + { + (FAR void *)&g_epintindesc, /* 6. Interrupt IN endpoint descriptor */ + USB_SIZEOF_EPDESC + }, + { + (FAR void *)&g_dataifdesc, /* 7. Data interface descriptor */ + USB_SIZEOF_IFDESC + }, + { + (FAR void *)&g_epbulkoutdesc, /* 8. Bulk OUT endpoint descriptor */ + USB_SIZEOF_EPDESC + }, + { + (FAR void *)&g_epbulkindesc, /* 9. Bulk OUT endpoint descriptor */ + USB_SIZEOF_EPDESC + } +}; + +#ifdef CONFIG_USBDEV_DUALSPEED +static const struct usb_qualdesc_s g_qualdesc = +{ + USB_SIZEOF_QUALDESC, /* len */ + USB_DESC_TYPE_DEVICEQUALIFIER, /* type */ + {LSBYTE(0x0200), MSBYTE(0x0200) }, /* USB */ + USB_CLASS_VENDOR_SPEC, /* class */ + 0, /* subclass */ + 0, /* protocol */ + CONFIG_CDCSER_EP0MAXPACKET, /* mxpacketsize */ + CDCSER_NCONFIGS, /* nconfigs */ + 0, /* reserved */ +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/************************************************************************************ + * Name: usbclass_fillrequest + * + * Description: + * If there is data to send it is copied to the given buffer. Called either + * to initiate the first write operation, or from the completion interrupt handler + * service consecutive write operations. + * + * NOTE: The USB serial driver does not use the serial drivers uart_xmitchars() + * API. That logic is essentially duplicated here because unlike UART hardware, + * we need to be able to handle writes not byte-by-byte, but packet-by-packet. + * Unfortunately, that decision also exposes some internals of the serial driver + * in the following. + * + ************************************************************************************/ + +static uint16_t usbclass_fillrequest(FAR struct usbser_dev_s *priv, uint8_t *reqbuf, + uint16_t reqlen) +{ + FAR uart_dev_t *serdev = &priv->serdev; + FAR struct uart_buffer_s *xmit = &serdev->xmit; + irqstate_t flags; + uint16_t nbytes = 0; + + /* Disable interrupts */ + + flags = irqsave(); + + /* Transfer bytes while we have bytes available and there is room in the request */ + + while (xmit->head != xmit->tail && nbytes < reqlen) + { + *reqbuf++ = xmit->buffer[xmit->tail]; + nbytes++; + + /* Increment the tail pointer */ + + if (++(xmit->tail) >= xmit->size) + { + xmit->tail = 0; + } + } + + /* When all of the characters have been sent from the buffer + * disable the "TX interrupt". + */ + + if (xmit->head == xmit->tail) + { + uart_disabletxint(serdev); + } + + /* If any bytes were removed from the buffer, inform any waiters + * there there is space available. + */ + + if (nbytes) + { + uart_datasent(serdev); + } + + irqrestore(flags); + return nbytes; +} + +/************************************************************************************ + * Name: usbclass_sndpacket + * + * Description: + * This function obtains write requests, transfers the TX data into the request, + * and submits the requests to the USB controller. This continues untils either + * (1) there are no further packets available, or (2) thre is not further data + * to send. + * + ************************************************************************************/ + +static int usbclass_sndpacket(FAR struct usbser_dev_s *priv) +{ + FAR struct usbdev_ep_s *ep; + FAR struct usbdev_req_s *req; + FAR struct usbser_req_s *reqcontainer; + irqstate_t flags; + int len; + int ret = OK; + +#ifdef CONFIG_DEBUG + if (priv == NULL) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return -ENODEV; + } +#endif + + flags = irqsave(); + + /* Use our IN endpoint for the transfer */ + + ep = priv->epbulkin; + + /* Loop until either (1) we run out or write requests, or (2) usbclass_fillrequest() + * is unable to fill the request with data (i.e., untilthere is no more data + * to be sent). + */ + + uvdbg("head=%d tail=%d nwrq=%d empty=%d\n", + priv->serdev.xmit.head, priv->serdev.xmit.tail, + priv->nwrq, sq_empty(&priv->reqlist)); + + while (!sq_empty(&priv->reqlist)) + { + /* Peek at the request in the container at the head of the list */ + + reqcontainer = (struct usbser_req_s *)sq_peek(&priv->reqlist); + req = reqcontainer->req; + + /* Fill the request with serial TX data */ + + len = usbclass_fillrequest(priv, req->buf, req->len); + if (len > 0) + { + /* Remove the empty container from the request list */ + + (void)sq_remfirst(&priv->reqlist); + priv->nwrq--; + + /* Then submit the request to the endpoint */ + + req->len = len; + req->priv = reqcontainer; + req->flags = USBDEV_REQFLAGS_NULLPKT; + ret = EP_SUBMIT(ep, req); + if (ret != OK) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_SUBMITFAIL), (uint16_t)-ret); + break; + } + } + else + { + break; + } + } + + irqrestore(flags); + return ret; +} + +/************************************************************************************ + * Name: usbclass_recvpacket + * + * Description: + * A normal completion event was received by the read completion handler at the + * interrupt level (with interrupts disabled). This function handles the USB packet + * and provides the received data to the uart RX buffer. + * + * Assumptions: + * Called from the USB interrupt handler with interrupts disabled. + * + ************************************************************************************/ + +static inline int usbclass_recvpacket(FAR struct usbser_dev_s *priv, + uint8_t *reqbuf, uint16_t reqlen) +{ + FAR uart_dev_t *serdev = &priv->serdev; + FAR struct uart_buffer_s *recv = &serdev->recv; + uint16_t currhead; + uint16_t nexthead; + uint16_t nbytes = 0; + + /* Get the next head index. During the time that RX interrupts are disabled, the + * the serial driver will be extracting data from the circular buffer and modifying + * recv.tail. During this time, we should avoid modifying recv.head; Instead we will + * use a shadow copy of the index. When interrupts are restored, the real recv.head + * will be updated with this indes. + */ + + if (priv->rxenabled) + { + currhead = recv->head; + } + else + { + currhead = priv->rxhead; + } + + /* Pre-calculate the head index and check for wrap around. We need to do this + * so that we can determine if the circular buffer will overrun BEFORE we + * overrun the buffer! + */ + + nexthead = currhead + 1; + if (nexthead >= recv->size) + { + nexthead = 0; + } + + /* Then copy data into the RX buffer until either: (1) all of the data has been + * copied, or (2) the RX buffer is full. NOTE: If the RX buffer becomes full, + * then we have overrun the serial driver and data will be lost. + */ + + while (nexthead != recv->tail && nbytes < reqlen) + { + /* Copy one byte to the head of the circular RX buffer */ + + recv->buffer[currhead] = *reqbuf++; + + /* Update counts and indices */ + + currhead = nexthead; + nbytes++; + + /* Increment the head index and check for wrap around */ + + nexthead = currhead + 1; + if (nexthead >= recv->size) + { + nexthead = 0; + } + } + + /* Write back the head pointer using the shadow index if RX "interrupts" + * are disabled. + */ + + if (priv->rxenabled) + { + recv->head = currhead; + } + else + { + priv->rxhead = currhead; + } + + /* If data was added to the incoming serial buffer, then wake up any + * threads is waiting for incoming data. If we are running in an interrupt + * handler, then the serial driver will not run until the interrupt handler + * returns. + */ + + if (priv->rxenabled && nbytes > 0) + { + uart_datareceived(serdev); + } + + /* Return an error if the entire packet could not be transferred */ + + if (nbytes < reqlen) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_RXOVERRUN), 0); + return -ENOSPC; + } + return OK; +} + +/**************************************************************************** + * Name: usbclass_allocreq + * + * Description: + * Allocate a request instance along with its buffer + * + ****************************************************************************/ + +static struct usbdev_req_s *usbclass_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); + if (!req->buf) + { + EP_FREEREQ(ep, req); + req = NULL; + } + } + return req; +} + +/**************************************************************************** + * Name: usbclass_freereq + * + * Description: + * Free a request instance along with its buffer + * + ****************************************************************************/ + +static void usbclass_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: usbclass_mkstrdesc + * + * Description: + * Construct a string descriptor + * + ****************************************************************************/ + +static int usbclass_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc) +{ + const char *str; + int len; + int ndata; + int i; + + switch (id) + { + case 0: + { + /* Descriptor 0 is the language id */ + + strdesc->len = 4; + strdesc->type = USB_DESC_TYPE_STRING; + strdesc->data[0] = LSBYTE(CDCSER_STR_LANGUAGE); + strdesc->data[1] = MSBYTE(CDCSER_STR_LANGUAGE); + return 4; + } + + case CDCSER_MANUFACTURERSTRID: + str = CONFIG_CDCSER_VENDORSTR; + break; + + case CDCSER_PRODUCTSTRID: + str = CONFIG_CDCSER_PRODUCTSTR; + break; + + case CDCSER_SERIALSTRID: + str = CONFIG_CDCSER_SERIALSTR; + break; + + case CDCSER_CONFIGSTRID: + str = CONFIG_CDCSER_CONFIGSTR; + break; + + default: + 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); + 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: usbclass_mkepbulkdesc + * + * Description: + * Construct the endpoint descriptor + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DUALSPEED +static inline void usbclass_mkepbulkdesc(const FAR struct usb_epdesc_s *indesc, + uint16_t mxpacket, + FAR struct usb_epdesc_s *outdesc) +{ + /* Copy the canned descriptor */ + + memcpy(outdesc, indesc, USB_SIZEOF_EPDESC); + + /* Then add the correct max packet size */ + + outdesc->mxpacketsize[0] = LSBYTE(mxpacket); + outdesc->mxpacketsize[1] = MSBYTE(mxpacket); +} +#endif + +/**************************************************************************** + * Name: usbclass_mkcfgdesc + * + * Description: + * Construct the configuration descriptor + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DUALSPEED +static int16_t usbclass_mkcfgdesc(uint8_t *buf, uint8_t speed, uint8_t type) +#else +static int16_t usbclass_mkcfgdesc(uint8_t *buf) +#endif +{ + FAR struct usb_cfgdesc_s *cfgdesc = (struct usb_cfgdesc_s*)buf; +#ifdef CONFIG_USBDEV_DUALSPEED + bool hispeed = (speed == USB_SPEED_HIGH); + uint16_t bulkmxpacket; +#endif + uint16_t totallen; + + /* This is the total length of the configuration (not necessarily the + * size that we will be sending now. + */ + + totallen = USB_SIZEOF_CFGDESC + USB_SIZEOF_IFDESC + CDCSER_NENDPOINTS * USB_SIZEOF_EPDESC; + + /* Configuration descriptor -- Copy the canned descriptor and fill in the + * type (we'll also need to update the size below + */ + + memcpy(cfgdesc, &g_cfgdesc, USB_SIZEOF_CFGDESC); + buf += USB_SIZEOF_CFGDESC; + + /* Copy the canned interface descriptor */ + + memcpy(buf, &g_ifdesc, USB_SIZEOF_IFDESC); + buf += USB_SIZEOF_IFDESC; + + /* Make the three endpoint configurations. First, check for switches + * between high and full speed + */ + +#ifdef CONFIG_USBDEV_DUALSPEED + if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG) + { + hispeed = !hispeed; + } +#endif + + memcpy(buf, &g_epintindesc, USB_SIZEOF_EPDESC); + buf += USB_SIZEOF_EPDESC; + +#ifdef CONFIG_USBDEV_DUALSPEED + if (hispeed) + { + bulkmxpacket = 512; + } + else + { + bulkmxpacket = 64; + } + + usbclass_mkepbulkdesc(&g_epbulkoutdesc, bulkmxpacket, (struct usb_epdesc_s*)buf); + buf += USB_SIZEOF_EPDESC; + usbclass_mkepbulkdesc(&g_epbulkindesc, bulkmxpacket, (struct usb_epdesc_s*)buf); +#else + memcpy(buf, &g_epbulkoutdesc, USB_SIZEOF_EPDESC); + buf += USB_SIZEOF_EPDESC; + memcpy(buf, &g_epbulkindesc, USB_SIZEOF_EPDESC); +#endif + + /* Finally, fill in the total size of the configuration descriptor */ + + cfgdesc->totallen[0] = LSBYTE(totallen); + cfgdesc->totallen[1] = MSBYTE(totallen); + return totallen; +} + +/**************************************************************************** + * Name: usbclass_resetconfig + * + * Description: + * Mark the device as not configured and disable all endpoints. + * + ****************************************************************************/ + +static void usbclass_resetconfig(FAR struct usbser_dev_s *priv) +{ + /* Are we configured? */ + + if (priv->config != CDCSER_CONFIGIDNONE) + { + /* Yes.. but not anymore */ + + priv->config = CDCSER_CONFIGIDNONE; + + /* Disable endpoints. This should force completion of all pending + * transfers. + */ + + EP_DISABLE(priv->epintin); + EP_DISABLE(priv->epbulkin); + EP_DISABLE(priv->epbulkout); + } +} + +/**************************************************************************** + * Name: usbclass_setconfig + * + * Description: + * Set the device configuration by allocating and configuring endpoints and + * by allocating and queue read and write requests. + * + ****************************************************************************/ + +static int usbclass_setconfig(FAR struct usbser_dev_s *priv, uint8_t config) +{ + FAR struct usbdev_req_s *req; +#ifdef CONFIG_USBDEV_DUALSPEED + struct usb_epdesc_s epdesc; + uint16_t bulkmxpacket; +#endif + int i; + int ret = 0; + +#if CONFIG_DEBUG + if (priv == NULL) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return -EIO; + } +#endif + + if (config == priv->config) + { + /* Already configured -- Do nothing */ + + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_ALREADYCONFIGURED), 0); + return 0; + } + + /* Discard the previous configuration data */ + + usbclass_resetconfig(priv); + + /* Was this a request to simply discard the current configuration? */ + + if (config == CDCSER_CONFIGIDNONE) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_CONFIGNONE), 0); + return 0; + } + + /* We only accept one configuration */ + + if (config != CDCSER_CONFIGID) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_CONFIGIDBAD), 0); + return -EINVAL; + } + + /* Configure the IN interrupt endpoint */ + + ret = EP_CONFIGURE(priv->epintin, &g_epintindesc, false); + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EPINTINCONFIGFAIL), 0); + goto errout; + } + priv->epintin->priv = priv; + + /* Configure the IN bulk endpoint */ + +#ifdef CONFIG_USBDEV_DUALSPEED + if (priv->usbdev->speed == USB_SPEED_HIGH) + { + bulkmxpacket = 512; + } + else + { + bulkmxpacket = 64; + } + + usbclass_mkepbulkdesc(&g_epbulkindesc, bulkmxpacket, &epdesc); + ret = EP_CONFIGURE(priv->epbulkin, &epdesc, false); +#else + ret = EP_CONFIGURE(priv->epbulkin, &g_epbulkindesc, false); +#endif + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EPBULKINCONFIGFAIL), 0); + goto errout; + } + + priv->epbulkin->priv = priv; + + /* Configure the OUT bulk endpoint */ + +#ifdef CONFIG_USBDEV_DUALSPEED + usbclass_mkepbulkdesc(&g_epbulkoutdesc, bulkmxpacket, &epdesc); + ret = EP_CONFIGURE(priv->epbulkout, &epdesc, true); +#else + ret = EP_CONFIGURE(priv->epbulkout, &g_epbulkoutdesc, true); +#endif + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EPBULKOUTCONFIGFAIL), 0); + goto errout; + } + + priv->epbulkout->priv = priv; + + /* Queue read requests in the bulk OUT endpoint */ + + DEBUGASSERT(priv->nrdq == 0); + for (i = 0; i < CONFIG_CDCSER_NRDREQS; i++) + { + req = priv->rdreqs[i].req; + req->callback = usbclass_rdcomplete; + ret = EP_SUBMIT(priv->epbulkout, req); + if (ret != OK) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_RDSUBMIT), (uint16_t)-ret); + goto errout; + } + priv->nrdq++; + } + + priv->config = config; + return OK; + +errout: + usbclass_resetconfig(priv); + return ret; +} + +/**************************************************************************** + * Name: usbclass_ep0incomplete + * + * Description: + * Handle completion of EP0 control operations + * + ****************************************************************************/ + +static void usbclass_ep0incomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + if (req->result || req->xfrd != req->len) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_REQRESULT), (uint16_t)-req->result); + } +} + +/**************************************************************************** + * Name: usbclass_rdcomplete + * + * Description: + * Handle completion of read request on the bulk OUT endpoint. This + * is handled like the receipt of serial data on the "UART" + * + ****************************************************************************/ + +static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct usbser_dev_s *priv; + irqstate_t flags; + int ret; + + /* Sanity check */ + +#ifdef CONFIG_DEBUG + if (!ep || !ep->priv || !req) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract references to private data */ + + priv = (FAR struct usbser_dev_s*)ep->priv; + + /* Process the received data unless this is some unusual condition */ + + flags = irqsave(); + switch (req->result) + { + case 0: /* Normal completion */ + usbtrace(TRACE_CLASSRDCOMPLETE, priv->nrdq); + usbclass_recvpacket(priv, req->buf, req->xfrd); + break; + + case -ESHUTDOWN: /* Disconnection */ + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_RDSHUTDOWN), 0); + priv->nrdq--; + irqrestore(flags); + return; + + default: /* Some other error occurred */ + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_RDUNEXPECTED), (uint16_t)-req->result); + break; + }; + + /* Requeue the read request */ + +#ifdef CONFIG_CDCSER_BULKREQLEN + req->len = max(CONFIG_CDCSER_BULKREQLEN, ep->maxpacket); +#else + req->len = ep->maxpacket; +#endif + + ret = EP_SUBMIT(ep, req); + if (ret != OK) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_RDSUBMIT), (uint16_t)-req->result); + } + irqrestore(flags); +} + +/**************************************************************************** + * Name: usbclass_wrcomplete + * + * Description: + * Handle completion of write request. This function probably executes + * in the context of an interrupt handler. + * + ****************************************************************************/ + +static void usbclass_wrcomplete(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct usbser_dev_s *priv; + FAR struct usbser_req_s *reqcontainer; + irqstate_t flags; + + /* Sanity check */ + +#ifdef CONFIG_DEBUG + if (!ep || !ep->priv || !req || !req->priv) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract references to our private data */ + + priv = (FAR struct usbser_dev_s *)ep->priv; + reqcontainer = (FAR struct usbser_req_s *)req->priv; + + /* Return the write request to the free list */ + + flags = irqsave(); + sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist); + priv->nwrq++; + irqrestore(flags); + + /* Send the next packet unless this was some unusual termination + * condition + */ + + switch (req->result) + { + case OK: /* Normal completion */ + usbtrace(TRACE_CLASSWRCOMPLETE, priv->nwrq); + usbclass_sndpacket(priv); + break; + + case -ESHUTDOWN: /* Disconnection */ + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_WRSHUTDOWN), priv->nwrq); + break; + + default: /* Some other error occurred */ + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_WRUNEXPECTED), (uint16_t)-req->result); + break; + } +} + +/**************************************************************************** + * USB Class Driver Methods + ****************************************************************************/ + +/**************************************************************************** + * Name: usbclass_bind + * + * Description: + * Invoked when the driver is bound to a USB device driver + * + ****************************************************************************/ + +static int usbclass_bind(FAR struct usbdev_s *dev, FAR struct usbdevclass_driver_s *driver) +{ + FAR struct usbser_dev_s *priv = ((struct usbser_driver_s*)driver)->dev; + FAR struct usbser_req_s *reqcontainer; + irqstate_t flags; + uint16_t reqlen; + int ret; + int i; + + usbtrace(TRACE_CLASSBIND, 0); + + /* Bind the structures */ + + priv->usbdev = dev; + dev->ep0->priv = priv; + + /* Preallocate control request */ + + priv->ctrlreq = usbclass_allocreq(dev->ep0, CDCSER_MXDESCLEN); + if (priv->ctrlreq == NULL) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_ALLOCCTRLREQ), 0); + ret = -ENOMEM; + goto errout; + } + priv->ctrlreq->callback = usbclass_ep0incomplete; + + /* Pre-allocate all endpoints... the endpoints will not be functional + * until the SET CONFIGURATION request is processed in usbclass_setconfig. + * This is done here because there may be calls to kmalloc and the SET + * CONFIGURATION processing probably occurrs within interrupt handling + * logic where kmalloc calls will fail. + */ + + /* Pre-allocate the IN interrupt endpoint */ + + priv->epintin = DEV_ALLOCEP(dev, CDCSER_EPINTIN_ADDR, true, USB_EP_ATTR_XFER_INT); + if (!priv->epintin) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EPINTINALLOCFAIL), 0); + ret = -ENODEV; + goto errout; + } + priv->epintin->priv = priv; + + /* Pre-allocate the IN bulk endpoint */ + + priv->epbulkin = DEV_ALLOCEP(dev, CDCSER_EPINBULK_ADDR, true, USB_EP_ATTR_XFER_BULK); + if (!priv->epbulkin) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EPBULKINALLOCFAIL), 0); + ret = -ENODEV; + goto errout; + } + priv->epbulkin->priv = priv; + + /* Pre-allocate the OUT bulk endpoint */ + + priv->epbulkout = DEV_ALLOCEP(dev, CDCSER_EPOUTBULK_ADDR, false, USB_EP_ATTR_XFER_BULK); + if (!priv->epbulkout) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EPBULKOUTALLOCFAIL), 0); + ret = -ENODEV; + goto errout; + } + priv->epbulkout->priv = priv; + + /* Pre-allocate read requests */ + +#ifdef CONFIG_CDCSER_BULKREQLEN + reqlen = max(CONFIG_CDCSER_BULKREQLEN, priv->epbulkout->maxpacket); +#else + reqlen = priv->epbulkout->maxpacket; +#endif + + for (i = 0; i < CONFIG_CDCSER_NRDREQS; i++) + { + reqcontainer = &priv->rdreqs[i]; + reqcontainer->req = usbclass_allocreq(priv->epbulkout, reqlen); + if (reqcontainer->req == NULL) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_RDALLOCREQ), -ENOMEM); + ret = -ENOMEM; + goto errout; + } + reqcontainer->req->priv = reqcontainer; + reqcontainer->req->callback = usbclass_rdcomplete; + } + + /* Pre-allocate write request containers and put in a free list */ + +#ifdef CONFIG_CDCSER_BULKREQLEN + reqlen = max(CONFIG_CDCSER_BULKREQLEN, priv->epbulkin->maxpacket); +#else + reqlen = priv->epbulkin->maxpacket; +#endif + + for (i = 0; i < CONFIG_CDCSER_NWRREQS; i++) + { + reqcontainer = &priv->wrreqs[i]; + reqcontainer->req = usbclass_allocreq(priv->epbulkin, reqlen); + if (reqcontainer->req == NULL) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_WRALLOCREQ), -ENOMEM); + ret = -ENOMEM; + goto errout; + } + reqcontainer->req->priv = reqcontainer; + reqcontainer->req->callback = usbclass_wrcomplete; + + flags = irqsave(); + sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist); + priv->nwrq++; /* Count of write requests available */ + irqrestore(flags); + } + + /* Report if we are selfpowered */ + +#ifdef CONFIG_USBDEV_SELFPOWERED + DEV_SETSELFPOWERED(dev); +#endif + + /* And pull-up the data line for the soft connect function */ + + DEV_CONNECT(dev); + return OK; + +errout: + usbclass_unbind(dev); + return ret; +} + +/**************************************************************************** + * Name: usbclass_unbind + * + * Description: + * Invoked when the driver is unbound from a USB device driver + * + ****************************************************************************/ + +static void usbclass_unbind(FAR struct usbdev_s *dev) +{ + FAR struct usbser_dev_s *priv; + FAR struct usbser_req_s *reqcontainer; + irqstate_t flags; + int i; + + usbtrace(TRACE_CLASSUNBIND, 0); + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract reference to private data */ + + priv = (FAR struct usbser_dev_s *)dev->ep0->priv; + +#ifdef CONFIG_DEBUG + if (!priv) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EP0NOTBOUND), 0); + return; + } +#endif + + /* Make sure that we are not already unbound */ + + if (priv != NULL) + { + /* 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 usbclass_resetconfig + * should cause the endpoints to immediately terminate all + * transfers and return the requests to us (with result == -ESHUTDOWN) + */ + + usbclass_resetconfig(priv); + up_mdelay(50); + + /* Free the interrupt IN endpoint */ + + if (priv->epintin) + { + DEV_FREEEP(dev, priv->epintin); + priv->epintin = NULL; + } + + /* Free the bulk IN endpoint */ + + if (priv->epbulkin) + { + DEV_FREEEP(dev, priv->epbulkin); + priv->epbulkin = NULL; + } + + /* Free the pre-allocated control request */ + + if (priv->ctrlreq != NULL) + { + usbclass_freereq(dev->ep0, priv->ctrlreq); + priv->ctrlreq = NULL; + } + + /* Free pre-allocated read requests (which should all have + * been returned to the free list at this time -- we don't check) + */ + + DEBUGASSERT(priv->nrdq == 0); + for (i = 0; i < CONFIG_CDCSER_NRDREQS; i++) + { + reqcontainer = &priv->rdreqs[i]; + if (reqcontainer->req) + { + usbclass_freereq(priv->epbulkout, reqcontainer->req); + reqcontainer->req = NULL; + } + } + + /* Free the bulk OUT endpoint */ + + if (priv->epbulkout) + { + DEV_FREEEP(dev, priv->epbulkout); + priv->epbulkout = NULL; + } + + /* Free write requests that are not in use (which should be all + * of them + */ + + flags = irqsave(); + DEBUGASSERT(priv->nwrq == CONFIG_CDCSER_NWRREQS); + while (!sq_empty(&priv->reqlist)) + { + reqcontainer = (struct usbser_req_s *)sq_remfirst(&priv->reqlist); + if (reqcontainer->req != NULL) + { + usbclass_freereq(priv->epbulkin, reqcontainer->req); + priv->nwrq--; /* Number of write requests queued */ + } + } + DEBUGASSERT(priv->nwrq == 0); + irqrestore(flags); + } + + /* Clear out all data in the circular buffer */ + + priv->serdev.xmit.head = 0; + priv->serdev.xmit.tail = 0; +} + +/**************************************************************************** + * Name: usbclass_setup + * + * Description: + * Invoked for ep0 control requests. This function probably executes + * in the context of an interrupt handler. + * + ****************************************************************************/ + +static int usbclass_setup(FAR struct usbdev_s *dev, const struct usb_ctrlreq_s *ctrl) +{ + FAR struct usbser_dev_s *priv; + FAR struct usbdev_req_s *ctrlreq; + uint16_t value; + uint16_t index; + uint16_t len; + int ret = -EOPNOTSUPP; + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0 || !ctrl) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return -EIO; + } +#endif + + /* Extract reference to private data */ + + usbtrace(TRACE_CLASSSETUP, ctrl->req); + priv = (FAR struct usbser_dev_s *)dev->ep0->priv; + +#ifdef CONFIG_DEBUG + if (!priv || !priv->ctrlreq) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EP0NOTBOUND), 0); + return -ENODEV; + } +#endif + ctrlreq = priv->ctrlreq; + + /* Extract the little-endian 16-bit values to host order */ + + value = GETUINT16(ctrl->value); + index = GETUINT16(ctrl->index); + len = GETUINT16(ctrl->len); + + uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", + ctrl->type, ctrl->req, value, index, len); + + switch (ctrl->type & USB_REQ_TYPE_MASK) + { + /*********************************************************************** + * Standard Requests + ***********************************************************************/ + + case USB_REQ_TYPE_STANDARD: + { + switch (ctrl->req) + { + case USB_REQ_GETDESCRIPTOR: + { + /* The value field specifies the descriptor type in the MS byte and the + * descriptor index in the LS byte (order is little endian) + */ + + switch (ctrl->value[1]) + { + case USB_DESC_TYPE_DEVICE: + { + ret = USB_SIZEOF_DEVDESC; + memcpy(ctrlreq->buf, &g_devdesc, ret); + } + break; + +#ifdef CONFIG_USBDEV_DUALSPEED + case USB_DESC_TYPE_DEVICEQUALIFIER: + { + ret = USB_SIZEOF_QUALDESC; + memcpy(ctrlreq->buf, &g_qualdesc, ret); + } + break; + + case USB_DESC_TYPE_OTHERSPEEDCONFIG: +#endif /* CONFIG_USBDEV_DUALSPEED */ + + case USB_DESC_TYPE_CONFIG: + { +#ifdef CONFIG_USBDEV_DUALSPEED + ret = usbclass_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->req); +#else + ret = usbclass_mkcfgdesc(ctrlreq->buf); +#endif + } + break; + + case USB_DESC_TYPE_STRING: + { + /* index == language code. */ + + ret = usbclass_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *)ctrlreq->buf); + } + break; + + default: + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_GETUNKNOWNDESC), value); + } + break; + } + } + break; + + case USB_REQ_SETCONFIGURATION: + { + if (ctrl->type == 0) + { + ret = usbclass_setconfig(priv, value); + } + } + break; + + case USB_REQ_GETCONFIGURATION: + { + if (ctrl->type == USB_DIR_IN) + { + *(uint8_t*)ctrlreq->buf = priv->config; + ret = 1; + } + } + break; + + case USB_REQ_SETINTERFACE: + { + if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE) + { + if (priv->config == CDCSER_CONFIGID && + index == CDCSER_INTERFACEID && + value == CDCSER_ALTINTERFACEID) + { + usbclass_resetconfig(priv); + usbclass_setconfig(priv, priv->config); + ret = 0; + } + } + } + break; + + case USB_REQ_GETINTERFACE: + { + if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) && + priv->config == CDCSER_CONFIGIDNONE) + { + if (index != CDCSER_INTERFACEID) + { + ret = -EDOM; + } + else + { + *(uint8_t*) ctrlreq->buf = CDCSER_ALTINTERFACEID; + ret = 1; + } + } + } + break; + + default: + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); + break; + } + } + break; + + /*********************************************************************** + * PL2303 Vendor-Specific Requests + ***********************************************************************/ + + case CDC_CONTROL_TYPE: + { + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_INTERFACE) + { + switch (ctrl->req) + { + case CDC_SETLINEREQUEST: + { + memcpy(priv->linest, ctrlreq->buf, min(len, 7)); + ret = 0; + } + break; + + + case CDC_GETLINEREQUEST: + { + memcpy(ctrlreq->buf, priv->linest, 7); + ret = 7; + } + break; + + case CDC_SETCONTROLREQUEST: + case CDC_BREAKREQUEST: + { + ret = 0; + } + break; + + default: + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_UNSUPPORTEDCTRLREQ), ctrl->type); + break; + } + } + } + break; + + case CDC_RWREQUEST_TYPE: + { + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) + { + if (ctrl->req == CDC_RWREQUEST) + { + if ((ctrl->type & USB_DIR_IN) != 0) + { + *(uint32_t*)ctrlreq->buf = 0xdeadbeef; + ret = 4; + } + else + { + ret = 0; + } + } + else + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_UNSUPPORTEDRWREQ), ctrl->type); + } + } + } + break; + + default: + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type); + break; + } + + /* Respond to the setup command if data was returned. On an error return + * value (ret < 0), the USB driver will stall. + */ + + if (ret >= 0) + { + ctrlreq->len = min(len, ret); + ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT; + ret = EP_SUBMIT(dev->ep0, ctrlreq); + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EPRESPQ), (uint16_t)-ret); + ctrlreq->result = OK; + usbclass_ep0incomplete(dev->ep0, ctrlreq); + } + } + + return ret; +} + +/**************************************************************************** + * Name: usbclass_disconnect + * + * Description: + * Invoked after all transfers have been stopped, when the host is + * disconnected. This function is probably called from the context of an + * interrupt handler. + * + ****************************************************************************/ + +static void usbclass_disconnect(FAR struct usbdev_s *dev) +{ + FAR struct usbser_dev_s *priv; + irqstate_t flags; + + usbtrace(TRACE_CLASSDISCONNECT, 0); + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract reference to private data */ + + priv = (FAR struct usbser_dev_s *)dev->ep0->priv; + +#ifdef CONFIG_DEBUG + if (!priv) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_EP0NOTBOUND), 0); + return; + } +#endif + + /* Reset the configuration */ + + flags = irqsave(); + usbclass_resetconfig(priv); + + /* Clear out all data in the circular buffer */ + + priv->serdev.xmit.head = 0; + priv->serdev.xmit.tail = 0; + irqrestore(flags); + + /* Perform the soft connect function so that we will we can be + * re-enumerated. + */ + + DEV_CONNECT(dev); +} + +/**************************************************************************** + * Serial Device Methods + ****************************************************************************/ + +/**************************************************************************** + * Name: usbser_setup + * + * Description: + * This method is called the first time that the serial port is opened. + * + ****************************************************************************/ + +static int usbser_setup(FAR struct uart_dev_s *dev) +{ + FAR struct usbser_dev_s *priv; + + usbtrace(CDCSER_CLASSAPI_SETUP, 0); + + /* Sanity check */ + +#if CONFIG_DEBUG + if (!dev || !dev->priv) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return -EIO; + } +#endif + + /* Extract reference to private data */ + + priv = (FAR struct usbser_dev_s*)dev->priv; + + /* Check if we have been configured */ + + if (priv->config == CDCSER_CONFIGIDNONE) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_SETUPNOTCONNECTED), 0); + return -ENOTCONN; + } + + return OK; +} + +/**************************************************************************** + * Name: usbser_shutdown + * + * Description: + * This method is called when the serial port is closed. This operation + * is very simple for the USB serial backend because the serial driver + * has already assured that the TX data has full drained -- it calls + * usbser_txempty() until that function returns true before calling this + * function. + * + ****************************************************************************/ + +static void usbser_shutdown(FAR struct uart_dev_s *dev) +{ + usbtrace(CDCSER_CLASSAPI_SHUTDOWN, 0); + + /* Sanity check */ + +#if CONFIG_DEBUG + if (!dev || !dev->priv) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + } +#endif +} + +/**************************************************************************** + * Name: usbser_attach + * + * Description: + * Does not apply to the USB serial class device + * + ****************************************************************************/ + +static int usbser_attach(FAR struct uart_dev_s *dev) +{ + usbtrace(CDCSER_CLASSAPI_ATTACH, 0); + return OK; +} + +/**************************************************************************** + * Name: usbser_detach + * + * Description: +* Does not apply to the USB serial class device + * + ****************************************************************************/ + +static void usbser_detach(FAR struct uart_dev_s *dev) +{ + usbtrace(CDCSER_CLASSAPI_DETACH, 0); +} + +/**************************************************************************** + * Name: usbser_rxint + * + * Description: + * Called by the serial driver to enable or disable RX interrupts. We, of + * course, have no RX interrupts but must behave consistently. This method + * is called under the conditions: + * + * 1. With enable==true when the port is opened (just after usbser_setup + * and usbser_attach are called called) + * 2. With enable==false while transferring data from the RX buffer + * 2. With enable==true while waiting for more incoming data + * 3. With enable==false when the port is closed (just before usbser_detach + * and usbser_shutdown are called). + * + ****************************************************************************/ + +static void usbser_rxint(FAR struct uart_dev_s *dev, bool enable) +{ + FAR struct usbser_dev_s *priv; + FAR uart_dev_t *serdev; + irqstate_t flags; + + usbtrace(CDCSER_CLASSAPI_RXINT, (uint16_t)enable); + + /* Sanity check */ + +#if CONFIG_DEBUG + if (!dev || !dev->priv) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract reference to private data */ + + priv = (FAR struct usbser_dev_s*)dev->priv; + serdev = &priv->serdev; + + /* We need exclusive access to the RX buffer and private structure + * in the following. + */ + + flags = irqsave(); + if (enable) + { + /* RX "interrupts" are enabled. Is this a transition from disabled + * to enabled state? + */ + + if (!priv->rxenabled) + { + /* Yes. During the time that RX interrupts are disabled, the + * the serial driver will be extracting data from the circular + * buffer and modifying recv.tail. During this time, we + * should avoid modifying recv.head; When interrupts are restored, + * we can update the head pointer for all of the data that we + * put into cicular buffer while "interrupts" were disabled. + */ + + if (priv->rxhead != serdev->recv.head) + { + serdev->recv.head = priv->rxhead; + + /* Yes... signal the availability of new data */ + + uart_datareceived(serdev); + } + + /* RX "interrupts are no longer disabled */ + + priv->rxenabled = true; + } + } + + /* RX "interrupts" are disabled. Is this a transition from enabled + * to disabled state? + */ + + else if (priv->rxenabled) + { + /* Yes. During the time that RX interrupts are disabled, the + * the serial driver will be extracting data from the circular + * buffer and modifying recv.tail. During this time, we + * should avoid modifying recv.head; When interrupts are disabled, + * we use a shadow index and continue adding data to the circular + * buffer. + */ + + priv->rxhead = serdev->recv.head; + priv->rxenabled = false; + } + irqrestore(flags); +} + +/**************************************************************************** + * Name: usbser_txint + * + * Description: + * Called by the serial driver to enable or disable TX interrupts. We, of + * course, have no TX interrupts but must behave consistently. Initially, + * TX interrupts are disabled. This method is called under the conditions: + * + * 1. With enable==false while transferring data into the TX buffer + * 2. With enable==true when data may be taken from the buffer. + * 3. With enable==false when the TX buffer is empty + * + ****************************************************************************/ + +static void usbser_txint(FAR struct uart_dev_s *dev, bool enable) +{ + FAR struct usbser_dev_s *priv; + + usbtrace(CDCSER_CLASSAPI_TXINT, (uint16_t)enable); + + /* Sanity checks */ + +#if CONFIG_DEBUG + if (!dev || !dev->priv) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + /* Extract references to private data */ + + priv = (FAR struct usbser_dev_s*)dev->priv; + + /* If the new state is enabled and if there is data in the XMIT buffer, + * send the next packet now. + */ + + uvdbg("enable=%d head=%d tail=%d\n", + enable, priv->serdev.xmit.head, priv->serdev.xmit.tail); + + if (enable && priv->serdev.xmit.head != priv->serdev.xmit.tail) + { + usbclass_sndpacket(priv); + } +} + +/**************************************************************************** + * Name: usbser_txempty + * + * Description: + * Return true when all data has been sent. This is called from the + * serial driver when the driver is closed. It will call this API + * periodically until it reports true. NOTE that the serial driver takes all + * responsibility for flushing TX data through the hardware so we can be + * a bit sloppy about that. + * + ****************************************************************************/ + +static bool usbser_txempty(FAR struct uart_dev_s *dev) +{ + FAR struct usbser_dev_s *priv = (FAR struct usbser_dev_s*)dev->priv; + + usbtrace(CDCSER_CLASSAPI_TXEMPTY, 0); + +#if CONFIG_DEBUG + if (!priv) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_INVALIDARG), 0); + return true; + } +#endif + + /* When all of the allocated write requests have been returned to the + * reqlist, then there is no longer any TX data in flight. + */ + + return priv->nwrq >= CONFIG_CDCSER_NWRREQS; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usbdev_serialinitialize + * + * Description: + * Register USB serial port (and USB serial console if so configured). + * + ****************************************************************************/ + +int usbdev_serialinitialize(int minor) +{ + FAR struct usbser_alloc_s *alloc; + FAR struct usbser_dev_s *priv; + FAR struct usbser_driver_s *drvr; + char devname[16]; + int ret; + + /* Allocate the structures needed */ + + alloc = (FAR struct usbser_alloc_s*)kmalloc(sizeof(struct usbser_alloc_s)); + if (!alloc) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_ALLOCDEVSTRUCT), 0); + return -ENOMEM; + } + + /* Convenience pointers into the allocated blob */ + + priv = &alloc->dev; + drvr = &alloc->drvr; + + /* Initialize the USB serial driver structure */ + + memset(priv, 0, sizeof(struct usbser_dev_s)); + sq_init(&priv->reqlist); + + /* Fake line status */ + + priv->linest[0] = (115200) & 0xff; /* Baud=115200 */ + priv->linest[1] = (115200 >> 8) & 0xff; + priv->linest[2] = (115200 >> 16) & 0xff; + priv->linest[3] = (115200 >> 24) & 0xff; + priv->linest[4] = 0; /* One stop bit */ + priv->linest[5] = 0; /* No parity */ + priv->linest[6] = 8; /*8 data bits */ + + /* Initialize the serial driver sub-structure */ + + priv->serdev.recv.size = CONFIG_CDCSER_RXBUFSIZE; + priv->serdev.recv.buffer = priv->rxbuffer; + priv->serdev.xmit.size = CONFIG_CDCSER_TXBUFSIZE; + priv->serdev.xmit.buffer = priv->txbuffer; + priv->serdev.ops = &g_uartops; + priv->serdev.priv = priv; + + /* Initialize the USB class driver structure */ + +#ifdef CONFIG_USBDEV_DUALSPEED + drvr->drvr.speed = USB_SPEED_HIGH; +#else + drvr->drvr.speed = USB_SPEED_FULL; +#endif + drvr->drvr.ops = &g_driverops; + drvr->dev = priv; + + /* Register the USB serial class driver */ + + ret = usbdev_register(&drvr->drvr); + if (ret) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_DEVREGISTER), (uint16_t)-ret); + goto errout_with_alloc; + } + + /* Register the USB serial console */ + +#ifdef CONFIG_CDCSER_CONSOLE + g_usbserialport.isconsole = true; + ret = uart_register("/dev/console", &pri->serdev); + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_CONSOLEREGISTER), (uint16_t)-ret); + goto errout_with_class; + } +#endif + + /* Register the single port supported by this implementation */ + + sprintf(devname, "/dev/ttyUSB%d", minor); + ret = uart_register(devname, &priv->serdev); + if (ret) + { + usbtrace(TRACE_CLSERROR(CDCSER_TRACEERR_UARTREGISTER), (uint16_t)-ret); + goto errout_with_class; + } + return OK; + +errout_with_class: + usbdev_unregister(&drvr->drvr); +errout_with_alloc: + kfree(alloc); + return ret; +} diff --git a/nuttx/include/nuttx/usb/cdc.h b/nuttx/include/nuttx/usb/cdc.h index 87161ecaa..a6fee1817 100755 --- a/nuttx/include/nuttx/usb/cdc.h +++ b/nuttx/include/nuttx/usb/cdc.h @@ -318,11 +318,6 @@ #define ECM_SPEED_CHANGE ATM_SPEED_CHANGE /* Descriptors ******************************************************************************/ -/* Table 24: Type Values for the bDescriptorType Field */ - -#define CS_INTERFACE 0x24 -#define CS_ENDPOINT 0x25 - /* Table 25: bDescriptor SubType in Functional Descriptors */ #define CDC_DSUBTYPE_HDR 0x00 /* Header Functional Descriptor, which marks the @@ -577,7 +572,7 @@ struct cdc_protowrapper_s struct cdc_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t data[1]; /* Function-specific data follows */ }; @@ -586,7 +581,7 @@ struct cdc_funcdesc_s struct cdc_hdr_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_HDR as defined in Table 25 */ uint8_t cdc[2]; /* bcdCDC, USB Class Definitions for Communication Devices Specification release * number in binary-coded decimal. @@ -599,7 +594,7 @@ struct cdc_hdr_funcdesc_s struct cdc_callmgmt_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_CALLMGMT as defined in Table 25 */ uint8_t caps; /* bmCapabilities: Bit encoded */ uint8_t ifno; /* bDataInterface, Interface number of Data Class interface @@ -613,7 +608,7 @@ struct cdc_callmgmt_funcdesc_s struct cdc_acm_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_ACM as defined in Table 25 */ uint8_t caps; /* bmCapabilities: Bit encoded */ }; @@ -624,7 +619,7 @@ struct cdc_acm_funcdesc_s struct cdc_dlc_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_DLC as defined in Table 25 */ uint8_t caps; /* bmCapabilities: Bit encoded */ }; @@ -635,7 +630,7 @@ struct cdc_dlc_funcdesc_s struct cdc_tcmr_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_TCMRINGER as defined in Table 25 */ uint8_t volsteps; /* bRingerVolSteps, Number of discrete steps in volume supported * by the ringer. @@ -649,7 +644,7 @@ struct cdc_tcmr_funcdesc_s struct cdc_tcmops_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_TCMOPS as defined in Table 25 */ uint8_t caps; /* bmCapabilities: Bit encoded */ }; @@ -660,7 +655,7 @@ struct cdc_tcmops_funcdesc_s struct cdc_tcmc_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_TCMCALL as defined in Table 25 */ uint8_t caps; /* bmCapabilities: Bit encoded */ }; @@ -671,7 +666,7 @@ struct cdc_tcmc_funcdesc_s struct cdc_union_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_UNION as defined in Table 25 */ uint8_t master; /* bMasterInterface: The interface number of the Communication or Data * Class interface, designated as the master or controlling interface @@ -681,14 +676,14 @@ struct cdc_union_funcdesc_s * interface in the union */ }; -#define SIZEOF_CALLMGMT_FUNCDESC(n) ((n)+4) +#define SIZEOF_UNION_FUNCDESC(n) ((n)+4) /* Table 34: Country Selection Functional Descriptor */ struct cdc_country_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_COUNTRY as defined in Table 25 */ uint8_t reldate; /* iCountryCodeRelDate: Index of a string giving the release date for the * implemented ISO 3166 Country Codes @@ -697,14 +692,14 @@ struct cdc_country_funcdesc_s * release date as specified in offset 3 for Nth country supported */ }; -#define SIZEOF_CONTRY_FUNCDESC(n) (sizeof(uint16_t)*(n) + 4) +#define SIZEOF_COUNTRY_FUNCDESC(n) (sizeof(uint16_t)*(n) + 4) /* Table 35: USB Terminal Functional Descriptor */ struct cdc_usbterm_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_USBTERM as defined in Table 25 */ uint8_t id; /* bEntityId, Constant uniquely identifying the Terminal */ uint8_t ifno; /* bInInterfaceNo, The input interface number of the associated @@ -723,7 +718,7 @@ struct cdc_usbterm_funcdesc_s struct cdc_netchan_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + 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 @@ -739,7 +734,7 @@ struct cdc_netchan_funcdesc_s struct cdc_protounit_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_PROTOUNIT as defined in Table 25 */ uint8_t id; /* bEntityId, Constant uniquely identifying the Unit */ uint8_t proto; /* bProtocol, Protocol code as defined in Table 19 */ @@ -752,7 +747,7 @@ struct cdc_protounit_funcdesc_s struct cdc_extunit_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_EXTUNIT as defined in Table 25 */ uint8_t id; /* bEntityId, Constant uniquely identifying the Extension Unit */ uint8_t code; /* bExtensionCode, Vendor specific code identifying the Extension Unit */ @@ -766,7 +761,7 @@ struct cdc_extunit_funcdesc_s struct cdc_mcm_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_MCM as defined in Table 25 */ uint8_t caps; /* bmCapabilities: Bit encoded */ }; @@ -777,7 +772,7 @@ struct cdc_mcm_funcdesc_s struct cdc_capi_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + uint8_t type; /* bDescriptorType, USB_DESC_TYPE_CSINTERFACE */ uint8_t subtype; /* bDescriptorSubType, CDC_DSUBTYPE_CAPI as defined in Table 25 */ uint8_t caps; /* bmCapabilities: Bit encoded */ }; @@ -788,7 +783,7 @@ struct cdc_capi_funcdesc_s struct cdc_ecm_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + 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 teh 48bit Ethernet MAC address string descriptor */ uint8_t stats[4]; /* bmEthernetStatistics, Indicates which Ethernet statistics functions @@ -811,7 +806,7 @@ struct cdc_ecm_funcdesc_s struct cdc_atm_funcdesc_s { uint8_t size; /* bFunctionLength, Size of this descriptor */ - uint8_t type; /* bDescriptorType, CS_INTERFACE, as defined in Table 24 */ + 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 */ diff --git a/nuttx/include/nuttx/usb/cdc_serial.h b/nuttx/include/nuttx/usb/cdc_serial.h index c42205c66..b516070eb 100644 --- a/nuttx/include/nuttx/usb/cdc_serial.h +++ b/nuttx/include/nuttx/usb/cdc_serial.h @@ -41,45 +41,112 @@ ****************************************************************************/ #include +#include /**************************************************************************** * Preprocessor definitions ****************************************************************************/ /* Configuration ************************************************************/ +/* EP0 max packet size */ + +#ifndef CONFIG_CDCSER_EP0MAXPACKET +# define CONFIG_CDCSER_EP0MAXPACKET 8 +#endif + /* Endpoint number and size (in bytes) of the CDC serial device-to-host (IN) * notification interrupt endpoint. */ -#ifndef CONFIG_CDCSER_INTIN_EP -# define CONFIG_CDCSER_INTIN_EP 2 +#ifndef CONFIG_CDCSER_EPINTIN +# define CONFIG_CDCSER_EPINTIN 2 #endif -#ifndef CONFIG_CDCSER_INTIN_EPSIZE -# define CONFIG_CDCSER_INTIN_EPSIZE 8 +#ifndef CONFIG_CDCSER_EPINTIN_SIZE +# define CONFIG_CDCSER_EPINTIN_SIZE 8 #endif /* Endpoint number and size (in bytes) of the CDC device-to-host (IN) data * bulk endpoint */ -#ifndef CONFIG_CDCSER_BULKIN_EP -# define CONFIG_CDCSER_BULKIN_EP 3 +#ifndef CONFIG_CDCSER_EPBULKIN +# define CONFIG_CDCSER_EPBULKIN 3 #endif -#ifndef CONFIG_CDCSER_BULKIN_EPSIZE -# define CONFIG_CDCSER_BULKIN_EPSIZE 16 +#ifndef CONFIG_CDCSER_EPBULKIN_SIZE +# define CONFIG_CDCSER_EPBULKIN_SIZE 16 #endif /* Endpoint number and size (in bytes) of the CDC host-to-device (OUT) data * bulk endpoint */ -#ifndef CONFIG_CDCSER_BULKOUT_EP -# define CONFIG_CDCSER_BULKOUT_EP 4 +#ifndef CONFIG_CDCSER_EPBULKOUT +# define CONFIG_CDCSER_EPBULKOUT 4 +#endif + +#ifndef CONFIG_CDCSER_EPBULKOUT_SIZE +# define CONFIG_CDCSER_EPBULKOUT_SIZE 16 +#endif + +/* Number of requests in the write queue */ + +#ifndef CONFIG_CDCSER_NWRREQS +# define CONFIG_CDCSER_NWRREQS 4 +#endif + +/* Number of requests in the read queue */ + +#ifndef CONFIG_CDCSER_NRDREQS +# define CONFIG_CDCSER_NRDREQS 4 +#endif + +/* Write buffer size */ + +#ifndef CONFIG_CDCSER_WRBUFFERSIZE +# define CONFIG_CDCSER_WRBUFFERSIZE 1024 +#endif + +/* Vendor and product IDs and strings */ + +#ifndef CONFIG_CDCSER_VENDORID +# define CONFIG_CDCSER_VENDORID 0x03eb +#endif + +#ifndef CONFIG_CDCSER_PRODUCTID +# define CONFIG_CDCSER_PRODUCTID 0x204b +#endif + +#ifndef CONFIG_CDCSER_VENDORSTR +# define CONFIG_CDCSER_VENDORSTR "NuttX" +#endif + +#ifndef CONFIG_CDCSER_PRODUCTSTR +# define CONFIG_CDCSER_PRODUCTSTR "USBdev Serial" +#endif + +#undef CONFIG_CDCSER_SERIALSTR +#define CONFIG_CDCSER_SERIALSTR "0" + +#undef CONFIG_CDCSER_CONFIGSTR +#define CONFIG_CDCSER_CONFIGSTR "Bulk" + +/* USB Controller */ + +#ifndef CONFIG_USBDEV_SELFPOWERED +# define SELFPOWERED USB_CONFIG_ATT_SELFPOWER +#else +# define SELFPOWERED (0) +#endif + +#ifndef CONFIG_USBDEV_REMOTEWAKEUP +# define REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP +#else +# define REMOTEWAKEUP (0) #endif -#ifndef CONFIG_CDCSER_BULKOUT_EPSIZE -# define CONFIG_CDCSER_BULKOUT_EPSIZE 16 +#ifndef CONFIG_USBDEV_MAXPOWER +# define CONFIG_USBDEV_MAXPOWER 100 #endif /**************************************************************************** diff --git a/nuttx/include/nuttx/usb/usb.h b/nuttx/include/nuttx/usb/usb.h index 125605cc0..9458d3eba 100644 --- a/nuttx/include/nuttx/usb/usb.h +++ b/nuttx/include/nuttx/usb/usb.h @@ -188,12 +188,24 @@ /* Endpoint descriptor attributes */ -#define USB_EP_ATTR_XFERTYPE_MASK (0x03) -#define USB_EP_ATTR_XFER_CONTROL (0x00) -#define USB_EP_ATTR_XFER_ISOC (0x01) -#define USB_EP_ATTR_XFER_BULK (0x02) -#define USB_EP_ATTR_XFER_INT (0x03) -#define USB_EP_ATTR_MAX_ADJUSTABLE (0x80) +#define USB_EP_ATTR_XFERTYPE_SHIFT (0) +#define USB_EP_ATTR_XFERTYPE_MASK (3 << USB_EP_ATTR_XFERTYPE_SHIFT) +# define USB_EP_ATTR_XFER_CONTROL (0 << USB_EP_ATTR_XFERTYPE_SHIFT) +# define USB_EP_ATTR_XFER_ISOC (1 << USB_EP_ATTR_XFERTYPE_SHIFT) +# define USB_EP_ATTR_XFER_BULK (2 << USB_EP_ATTR_XFERTYPE_SHIFT) +# define USB_EP_ATTR_XFER_INT (3 << USB_EP_ATTR_XFERTYPE_SHIFT) +#define USB_EP_ATTR_SYNC_SHIFT (2) +#define USB_EP_ATTR_SYNC_MASK (3 << USB_EP_ATTR_SYNC_SHIFT) +# define USB_EP_ATTR_NO_SYNC (0 << USB_EP_ATTR_SYNC_SHIFT) +# define USB_EP_ATTR_ASYNC (1 << USB_EP_ATTR_SYNC_SHIFT) +# define USB_EP_ATTR_ADAPTIVE (2 << USB_EP_ATTR_SYNC_SHIFT) +# define USB_EP_ATTR_SYNC (3 << USB_EP_ATTR_SYNC_SHIFT) +#define USB_EP_ATTR_USAGE_SHIFT (4) +#define USB_EP_ATTR_USAGE_MASK (3 << USB_EP_ATTR_USAGE_SHIFT) +# define USB_EP_ATTR_USAGE_DATA (0 << USB_EP_ATTR_USAGE_SHIFT) +# define USB_EP_ATTR_USAGE_FEEDBACK (1 << USB_EP_ATTR_USAGE_SHIFT) +# define USB_EP_ATTR_USAGE_IMPLICIT (2 << USB_EP_ATTR_USAGE_SHIFT) +#define USB_EP_ATTR_MAX_ADJUSTABLE (1 << 7) /************************************************************************************ * Public Types -- cgit v1.2.3