aboutsummaryrefslogtreecommitdiff
path: root/nuttx/drivers/usbdev/cdcacm.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/drivers/usbdev/cdcacm.c')
-rw-r--r--nuttx/drivers/usbdev/cdcacm.c2329
1 files changed, 0 insertions, 2329 deletions
diff --git a/nuttx/drivers/usbdev/cdcacm.c b/nuttx/drivers/usbdev/cdcacm.c
deleted file mode 100644
index cb8679976..000000000
--- a/nuttx/drivers/usbdev/cdcacm.c
+++ /dev/null
@@ -1,2329 +0,0 @@
-/****************************************************************************
- * drivers/usbdev/cdcacm.c
- *
- * Copyright (C) 2011-2013 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * 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 <sys/types.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <semaphore.h>
-#include <string.h>
-#include <errno.h>
-#include <queue.h>
-#include <debug.h>
-
-#include <nuttx/kmalloc.h>
-#include <nuttx/arch.h>
-#include <nuttx/serial/serial.h>
-
-#include <nuttx/usb/usb.h>
-#include <nuttx/usb/cdc.h>
-#include <nuttx/usb/usbdev.h>
-#include <nuttx/usb/cdcacm.h>
-#include <nuttx/usb/usbdev_trace.h>
-
-#include "cdcacm.h"
-
-#ifdef CONFIG_USBMSC_COMPOSITE
-# include <nuttx/usb/composite.h>
-# include "composite.h"
-#endif
-
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-/****************************************************************************
- * Private Types
- ****************************************************************************/
-
-/* Container to support a list of requests */
-
-struct cdcacm_req_s
-{
- FAR struct cdcacm_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 cdcacm_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) */
- uint8_t minor; /* The device minor number */
- bool rxenabled; /* true: UART RX "interrupts" enabled */
- int16_t rxhead; /* Working head; used when rx int disabled */
-
- uint8_t ctrlline; /* Buffered control line state */
- struct cdc_linecoding_s linecoding; /* Buffered line status */
- cdcacm_callback_t callback; /* Serial event callback function */
-
- 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; /* Allocoated 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 cdcacm_req_s wrreqs[CONFIG_CDCACM_NWRREQS];
- struct cdcacm_req_s rdreqs[CONFIG_CDCACM_NWRREQS];
-
- /* Serial I/O buffers */
-
- char rxbuffer[CONFIG_CDCACM_RXBUFSIZE];
- char txbuffer[CONFIG_CDCACM_TXBUFSIZE];
-};
-
-/* The internal version of the class driver */
-
-struct cdcacm_driver_s
-{
- struct usbdevclass_driver_s drvr;
- FAR struct cdcacm_dev_s *dev;
-};
-
-/* This is what is allocated */
-
-struct cdcacm_alloc_s
-{
- struct cdcacm_dev_s dev;
- struct cdcacm_driver_s drvr;
-};
-
-/****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
-
-/* Transfer helpers *********************************************************/
-
-static uint16_t cdcacm_fillrequest(FAR struct cdcacm_dev_s *priv,
- uint8_t *reqbuf, uint16_t reqlen);
-static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv);
-static inline int cdcacm_recvpacket(FAR struct cdcacm_dev_s *priv,
- uint8_t *reqbuf, uint16_t reqlen);
-
-/* Request helpers *********************************************************/
-
-static struct usbdev_req_s *cdcacm_allocreq(FAR struct usbdev_ep_s *ep,
- uint16_t len);
-static void cdcacm_freereq(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req);
-
-/* Configuration ***********************************************************/
-
-static void cdcacm_resetconfig(FAR struct cdcacm_dev_s *priv);
-#ifdef CONFIG_USBDEV_DUALSPEED
-static int cdcacm_epconfigure(FAR struct usbdev_ep_s *ep,
- enum cdcacm_epdesc_e epid, uint16_t mxpacket, bool last);
-#endif
-static int cdcacm_setconfig(FAR struct cdcacm_dev_s *priv,
- uint8_t config);
-
-/* Completion event handlers ***********************************************/
-
-static void cdcacm_ep0incomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req);
-static void cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req);
-static void cdcacm_wrcomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req);
-
-/* USB class device ********************************************************/
-
-static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev);
-static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev);
-static int cdcacm_setup(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev,
- FAR const struct usb_ctrlreq_s *ctrl, FAR uint8_t *dataout,
- size_t outlen);
-static void cdcacm_disconnect(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev);
-#ifdef CONFIG_SERIAL_REMOVABLE
-static void cdcacm_suspend(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev);
-static void cdcacm_resume(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev);
-#endif
-
-/* UART Operations **********************************************************/
-
-static int cdcuart_setup(FAR struct uart_dev_s *dev);
-static void cdcuart_shutdown(FAR struct uart_dev_s *dev);
-static int cdcuart_attach(FAR struct uart_dev_s *dev);
-static void cdcuart_detach(FAR struct uart_dev_s *dev);
-static int cdcuart_ioctl(FAR struct file *filep,int cmd,unsigned long arg);
-static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable);
-static void cdcuart_txint(FAR struct uart_dev_s *dev, bool enable);
-static bool cdcuart_txempty(FAR struct uart_dev_s *dev);
-
-/****************************************************************************
- * Private Variables
- ****************************************************************************/
-/* USB class device *********************************************************/
-
-static const struct usbdevclass_driverops_s g_driverops =
-{
- cdcacm_bind, /* bind */
- cdcacm_unbind, /* unbind */
- cdcacm_setup, /* setup */
- cdcacm_disconnect, /* disconnect */
-#ifdef CONFIG_SERIAL_REMOVABLE
- cdcacm_suspend, /* suspend */
- cdcacm_resume, /* resume */
-#else
- NULL, /* suspend */
- NULL, /* resume */
-#endif
-};
-
-/* Serial port **************************************************************/
-
-static const struct uart_ops_s g_uartops =
-{
- cdcuart_setup, /* setup */
- cdcuart_shutdown, /* shutdown */
- cdcuart_attach, /* attach */
- cdcuart_detach, /* detach */
- cdcuart_ioctl, /* ioctl */
- NULL, /* receive */
- cdcuart_rxint, /* rxinit */
- NULL, /* rxavailable */
- NULL, /* send */
- cdcuart_txint, /* txinit */
- NULL, /* txready */
- cdcuart_txempty /* txempty */
-};
-
-/****************************************************************************
- * Private Functions
- ****************************************************************************/
-
-/****************************************************************************
- * Name: cdcacm_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 cdcacm_fillrequest(FAR struct cdcacm_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: cdcacm_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
- * no further data to send.
- *
- ****************************************************************************/
-
-static int cdcacm_sndpacket(FAR struct cdcacm_dev_s *priv)
-{
- FAR struct usbdev_ep_s *ep;
- FAR struct usbdev_req_s *req;
- FAR struct cdcacm_req_s *reqcontainer;
- uint16_t reqlen;
- irqstate_t flags;
- int len;
- int ret = OK;
-
-#ifdef CONFIG_DEBUG
- if (priv == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_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) cdcacm_fillrequest()
- * is unable to fill the request with data (i.e., until there 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));
-
- /* Get the maximum number of bytes that will fit into one bulk IN request */
-
-#ifdef CONFIG_CDCACM_BULKREQLEN
- reqlen = MAX(CONFIG_CDCACM_BULKREQLEN, ep->maxpacket);
-#else
- reqlen = ep->maxpacket;
-#endif
-
- while (!sq_empty(&priv->reqlist))
- {
- /* Peek at the request in the container at the head of the list */
-
- reqcontainer = (struct cdcacm_req_s *)sq_peek(&priv->reqlist);
- req = reqcontainer->req;
-
- /* Fill the request with serial TX data */
-
- len = cdcacm_fillrequest(priv, req->buf, reqlen);
- 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(USBSER_TRACEERR_SUBMITFAIL), (uint16_t)-ret);
- break;
- }
- }
- else
- {
- break;
- }
- }
-
- irqrestore(flags);
- return ret;
-}
-
-/****************************************************************************
- * Name: cdcacm_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 cdcacm_recvpacket(FAR struct cdcacm_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;
-
- uvdbg("head=%d tail=%d nrdq=%d reqlen=%d\n",
- priv->serdev.recv.head, priv->serdev.recv.tail, priv->nrdq, reqlen);
-
- /* 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(USBSER_TRACEERR_RXOVERRUN), 0);
- return -ENOSPC;
- }
- return OK;
-}
-
-/****************************************************************************
- * Name: cdcacm_allocreq
- *
- * Description:
- * Allocate a request instance along with its buffer
- *
- ****************************************************************************/
-
-static struct usbdev_req_s *cdcacm_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: cdcacm_freereq
- *
- * Description:
- * Free a request instance along with its buffer
- *
- ****************************************************************************/
-
-static void cdcacm_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: cdcacm_resetconfig
- *
- * Description:
- * Mark the device as not configured and disable all endpoints.
- *
- ****************************************************************************/
-
-static void cdcacm_resetconfig(FAR struct cdcacm_dev_s *priv)
-{
- /* Are we configured? */
-
- if (priv->config != CDCACM_CONFIGIDNONE)
- {
- /* Yes.. but not anymore */
-
- priv->config = CDCACM_CONFIGIDNONE;
-
- /* Inform the "upper half" driver that there is no (functional) USB
- * connection.
- */
-
-#ifdef CONFIG_SERIAL_REMOVABLE
- uart_connected(&priv->serdev, false);
-#endif
-
- /* Disable endpoints. This should force completion of all pending
- * transfers.
- */
-
- EP_DISABLE(priv->epintin);
- EP_DISABLE(priv->epbulkin);
- EP_DISABLE(priv->epbulkout);
- }
-}
-
-/****************************************************************************
- * Name: cdcacm_epconfigure
- *
- * Description:
- * Configure one endpoint.
- *
- ****************************************************************************/
-
-#ifdef CONFIG_USBDEV_DUALSPEED
-static int cdcacm_epconfigure(FAR struct usbdev_ep_s *ep,
- enum cdcacm_epdesc_e epid, uint16_t mxpacket,
- bool last)
-{
- struct usb_epdesc_s epdesc;
- cdcacm_mkepdesc(epid, mxpacket, &epdesc);
- return EP_CONFIGURE(ep, &epdesc, last);
-}
-#endif
-
-/****************************************************************************
- * Name: cdcacm_setconfig
- *
- * Description:
- * Set the device configuration by allocating and configuring endpoints and
- * by allocating and queue read and write requests.
- *
- ****************************************************************************/
-
-static int cdcacm_setconfig(FAR struct cdcacm_dev_s *priv, uint8_t config)
-{
- FAR struct usbdev_req_s *req;
- int i;
- int ret = 0;
-
-#if CONFIG_DEBUG
- if (priv == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return -EIO;
- }
-#endif
-
- if (config == priv->config)
- {
- /* Already configured -- Do nothing */
-
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALREADYCONFIGURED), 0);
- return 0;
- }
-
- /* Discard the previous configuration data */
-
- cdcacm_resetconfig(priv);
-
- /* Was this a request to simply discard the current configuration? */
-
- if (config == CDCACM_CONFIGIDNONE)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGNONE), 0);
- return 0;
- }
-
- /* We only accept one configuration */
-
- if (config != CDCACM_CONFIGID)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGIDBAD), 0);
- return -EINVAL;
- }
-
- /* Configure the IN interrupt endpoint */
-
-#ifdef CONFIG_USBDEV_DUALSPEED
- if (priv->usbdev->speed == USB_SPEED_HIGH)
- {
- ret = cdcacm_epconfigure(priv->epintin, CDCACM_EPINTIN,
- CONFIG_CDCACM_EPINTIN_HSSIZE, false);
- }
- else
-#endif
- {
- ret = EP_CONFIGURE(priv->epintin,
- cdcacm_getepdesc(CDCACM_EPINTIN), false);
- }
-
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_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)
- {
- ret = cdcacm_epconfigure(priv->epbulkin, CDCACM_EPBULKIN,
- CONFIG_CDCACM_EPBULKIN_HSSIZE, false);
- }
- else
-#endif
- {
- ret = EP_CONFIGURE(priv->epbulkin,
- cdcacm_getepdesc(CDCACM_EPBULKIN), false);
- }
-
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINCONFIGFAIL), 0);
- goto errout;
- }
-
- priv->epbulkin->priv = priv;
-
- /* Configure the OUT bulk endpoint */
-
-#ifdef CONFIG_USBDEV_DUALSPEED
- if (priv->usbdev->speed == USB_SPEED_HIGH)
- {
- ret = cdcacm_epconfigure(priv->epbulkout, CDCACM_EPBULKOUT,
- CONFIG_CDCACM_EPBULKOUT_HSSIZE, true);
- }
- else
-#endif
- {
- ret = EP_CONFIGURE(priv->epbulkout,
- cdcacm_getepdesc(CDCACM_EPBULKOUT), true);
- }
-
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_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_CDCACM_NRDREQS; i++)
- {
- req = priv->rdreqs[i].req;
- req->callback = cdcacm_rdcomplete;
- ret = EP_SUBMIT(priv->epbulkout, req);
- if (ret != OK)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-ret);
- goto errout;
- }
-
- priv->nrdq++;
- }
-
- /* We are successfully configured */
-
- priv->config = config;
-
- /* Inform the "upper half" driver that we are "open for business" */
-
-#ifdef CONFIG_SERIAL_REMOVABLE
- uart_connected(&priv->serdev, true);
-#endif
-
- return OK;
-
-errout:
- cdcacm_resetconfig(priv);
- return ret;
-}
-
-/****************************************************************************
- * Name: cdcacm_ep0incomplete
- *
- * Description:
- * Handle completion of EP0 control operations
- *
- ****************************************************************************/
-
-static void cdcacm_ep0incomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req)
-{
- if (req->result || req->xfrd != req->len)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_REQRESULT), (uint16_t)-req->result);
- }
-}
-
-/****************************************************************************
- * Name: cdcacm_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 cdcacm_rdcomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req)
-{
- FAR struct cdcacm_dev_s *priv;
- irqstate_t flags;
- int ret;
-
- /* Sanity check */
-
-#ifdef CONFIG_DEBUG
- if (!ep || !ep->priv || !req)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
-#endif
-
- /* Extract references to private data */
-
- priv = (FAR struct cdcacm_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);
- cdcacm_recvpacket(priv, req->buf, req->xfrd);
- break;
-
- case -ESHUTDOWN: /* Disconnection */
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0);
- priv->nrdq--;
- irqrestore(flags);
- return;
-
- default: /* Some other error occurred */
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDUNEXPECTED), (uint16_t)-req->result);
- break;
- };
-
- /* Requeue the read request */
-
-#ifdef CONFIG_CDCACM_BULKREQLEN
- req->len = MAX(CONFIG_CDCACM_BULKREQLEN, ep->maxpacket);
-#else
- req->len = ep->maxpacket;
-#endif
-
- ret = EP_SUBMIT(ep, req);
- if (ret != OK)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-req->result);
- }
-
- irqrestore(flags);
-}
-
-/****************************************************************************
- * Name: cdcacm_wrcomplete
- *
- * Description:
- * Handle completion of write request. This function probably executes
- * in the context of an interrupt handler.
- *
- ****************************************************************************/
-
-static void cdcacm_wrcomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req)
-{
- FAR struct cdcacm_dev_s *priv;
- FAR struct cdcacm_req_s *reqcontainer;
- irqstate_t flags;
-
- /* Sanity check */
-
-#ifdef CONFIG_DEBUG
- if (!ep || !ep->priv || !req || !req->priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
-#endif
-
- /* Extract references to our private data */
-
- priv = (FAR struct cdcacm_dev_s *)ep->priv;
- reqcontainer = (FAR struct cdcacm_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);
- cdcacm_sndpacket(priv);
- break;
-
- case -ESHUTDOWN: /* Disconnection */
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRSHUTDOWN), priv->nwrq);
- break;
-
- default: /* Some other error occurred */
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRUNEXPECTED), (uint16_t)-req->result);
- break;
- }
-}
-
-/****************************************************************************
- * USB Class Driver Methods
- ****************************************************************************/
-
-/****************************************************************************
- * Name: cdcacm_bind
- *
- * Description:
- * Invoked when the driver is bound to a USB device driver
- *
- ****************************************************************************/
-
-static int cdcacm_bind(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev)
-{
- FAR struct cdcacm_dev_s *priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
- FAR struct cdcacm_req_s *reqcontainer;
- irqstate_t flags;
- uint16_t reqlen;
- int ret;
- int i;
-
- usbtrace(TRACE_CLASSBIND, 0);
-
- /* Bind the structures */
-
- priv->usbdev = dev;
-
- /* Save the reference to our private data structure in EP0 so that it
- * can be recovered in ep0 completion events (Unless we are part of
- * a composite device and, in that case, the composite device owns
- * EP0).
- */
-
-#ifndef CONFIG_USBMSC_COMPOSITE
- dev->ep0->priv = priv;
-#endif
-
- /* Preallocate control request */
-
- priv->ctrlreq = cdcacm_allocreq(dev->ep0, CDCACM_MXDESCLEN);
- if (priv->ctrlreq == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCCTRLREQ), 0);
- ret = -ENOMEM;
- goto errout;
- }
-
- priv->ctrlreq->callback = cdcacm_ep0incomplete;
-
- /* Pre-allocate all endpoints... the endpoints will not be functional
- * until the SET CONFIGURATION request is processed in cdcacm_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, CDCACM_EPINTIN_ADDR, true, USB_EP_ATTR_XFER_INT);
- if (!priv->epintin)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPINTINALLOCFAIL), 0);
- ret = -ENODEV;
- goto errout;
- }
- priv->epintin->priv = priv;
-
- /* Pre-allocate the IN bulk endpoint */
-
- priv->epbulkin = DEV_ALLOCEP(dev, CDCACM_EPINBULK_ADDR, true, USB_EP_ATTR_XFER_BULK);
- if (!priv->epbulkin)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINALLOCFAIL), 0);
- ret = -ENODEV;
- goto errout;
- }
- priv->epbulkin->priv = priv;
-
- /* Pre-allocate the OUT bulk endpoint */
-
- priv->epbulkout = DEV_ALLOCEP(dev, CDCACM_EPOUTBULK_ADDR, false, USB_EP_ATTR_XFER_BULK);
- if (!priv->epbulkout)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTALLOCFAIL), 0);
- ret = -ENODEV;
- goto errout;
- }
- priv->epbulkout->priv = priv;
-
- /* Pre-allocate read requests */
-
-#ifdef CONFIG_CDCACM_BULKREQLEN
- reqlen = MAX(CONFIG_CDCACM_BULKREQLEN, priv->epbulkout->maxpacket);
-#else
- reqlen = priv->epbulkout->maxpacket;
-#endif
-
- for (i = 0; i < CONFIG_CDCACM_NRDREQS; i++)
- {
- reqcontainer = &priv->rdreqs[i];
- reqcontainer->req = cdcacm_allocreq(priv->epbulkout, reqlen);
- if (reqcontainer->req == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), -ENOMEM);
- ret = -ENOMEM;
- goto errout;
- }
- reqcontainer->req->priv = reqcontainer;
- reqcontainer->req->callback = cdcacm_rdcomplete;
- }
-
- /* Pre-allocate write request containers and put in a free list */
-
-#ifdef CONFIG_CDCACM_BULKREQLEN
- reqlen = MAX(CONFIG_CDCACM_BULKREQLEN, priv->epbulkin->maxpacket);
-#else
- reqlen = priv->epbulkin->maxpacket;
-#endif
-
- for (i = 0; i < CONFIG_CDCACM_NWRREQS; i++)
- {
- reqcontainer = &priv->wrreqs[i];
- reqcontainer->req = cdcacm_allocreq(priv->epbulkin, reqlen);
- if (reqcontainer->req == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRALLOCREQ), -ENOMEM);
- ret = -ENOMEM;
- goto errout;
- }
- reqcontainer->req->priv = reqcontainer;
- reqcontainer->req->callback = cdcacm_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 (unless we are part of a composite device) */
-
-#ifndef CONFIG_CDCACM_COMPOSITE
-#ifdef CONFIG_USBDEV_SELFPOWERED
- DEV_SETSELFPOWERED(dev);
-#endif
-
- /* And pull-up the data line for the soft connect function (unless we are
- * part of a composite device)
- */
-
- DEV_CONNECT(dev);
-#endif
- return OK;
-
-errout:
- cdcacm_unbind(driver, dev);
- return ret;
-}
-
-/****************************************************************************
- * Name: cdcacm_unbind
- *
- * Description:
- * Invoked when the driver is unbound from a USB device driver
- *
- ****************************************************************************/
-
-static void cdcacm_unbind(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev)
-{
- FAR struct cdcacm_dev_s *priv;
- FAR struct cdcacm_req_s *reqcontainer;
- irqstate_t flags;
- int i;
-
- usbtrace(TRACE_CLASSUNBIND, 0);
-
-#ifdef CONFIG_DEBUG
- if (!driver || !dev)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
-#endif
-
- /* Extract reference to private data */
-
- priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
-
-#ifdef CONFIG_DEBUG
- if (!priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_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 cdcacm_resetconfig
- * should cause the endpoints to immediately terminate all
- * transfers and return the requests to us (with result == -ESHUTDOWN)
- */
-
- cdcacm_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)
- {
- cdcacm_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_CDCACM_NRDREQS; i++)
- {
- reqcontainer = &priv->rdreqs[i];
- if (reqcontainer->req)
- {
- cdcacm_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_CDCACM_NWRREQS);
- while (!sq_empty(&priv->reqlist))
- {
- reqcontainer = (struct cdcacm_req_s *)sq_remfirst(&priv->reqlist);
- if (reqcontainer->req != NULL)
- {
- cdcacm_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: cdcacm_setup
- *
- * Description:
- * Invoked for ep0 control requests. This function probably executes
- * in the context of an interrupt handler.
- *
- ****************************************************************************/
-
-static int cdcacm_setup(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev,
- FAR const struct usb_ctrlreq_s *ctrl,
- FAR uint8_t *dataout, size_t outlen)
-{
- FAR struct cdcacm_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 (!driver || !dev || !ctrl)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return -EIO;
- }
-#endif
-
- /* Extract reference to private data */
-
- usbtrace(TRACE_CLASSSETUP, ctrl->req);
- priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
-
-#ifdef CONFIG_DEBUG
- if (!priv || !priv->ctrlreq)
- {
- usbtrace(TRACE_CLSERROR(USBSER_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);
-
- if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD)
- {
- /***********************************************************************
- * Standard Requests
- ***********************************************************************/
-
- 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])
- {
- /* If the serial device is used in as part of a composite device,
- * then the device descriptor is provided by logic in the composite
- * device implementation.
- */
-
-#ifndef CONFIG_CDCACM_COMPOSITE
- case USB_DESC_TYPE_DEVICE:
- {
- ret = USB_SIZEOF_DEVDESC;
- memcpy(ctrlreq->buf, cdcacm_getdevdesc(), ret);
- }
- break;
-#endif
-
- /* If the serial device is used in as part of a composite device,
- * then the device qualifier descriptor is provided by logic in the
- * composite device implementation.
- */
-
-#if !defined(CONFIG_CDCACM_COMPOSITE) && defined(CONFIG_USBDEV_DUALSPEED)
- case USB_DESC_TYPE_DEVICEQUALIFIER:
- {
- ret = USB_SIZEOF_QUALDESC;
- memcpy(ctrlreq->buf, cdcacm_getqualdesc(), ret);
- }
- break;
-
- case USB_DESC_TYPE_OTHERSPEEDCONFIG:
-#endif
-
- /* If the serial device is used in as part of a composite device,
- * then the configuration descriptor is provided by logic in the
- * composite device implementation.
- */
-
-#ifndef CONFIG_CDCACM_COMPOSITE
- case USB_DESC_TYPE_CONFIG:
- {
-#ifdef CONFIG_USBDEV_DUALSPEED
- ret = cdcacm_mkcfgdesc(ctrlreq->buf, dev->speed, ctrl->req);
-#else
- ret = cdcacm_mkcfgdesc(ctrlreq->buf);
-#endif
- }
- break;
-#endif
-
- /* If the serial device is used in as part of a composite device,
- * then the language string descriptor is provided by logic in the
- * composite device implementation.
- */
-
-#ifndef CONFIG_CDCACM_COMPOSITE
- case USB_DESC_TYPE_STRING:
- {
- /* index == language code. */
-
- ret = cdcacm_mkstrdesc(ctrl->value[0], (struct usb_strdesc_s *)ctrlreq->buf);
- }
- break;
-#endif
-
- default:
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_GETUNKNOWNDESC), value);
- }
- break;
- }
- }
- break;
-
- case USB_REQ_SETCONFIGURATION:
- {
- if (ctrl->type == 0)
- {
- ret = cdcacm_setconfig(priv, value);
- }
- }
- break;
-
- /* If the serial device is used in as part of a composite device,
- * then the overall composite class configuration is managed by logic
- * in the composite device implementation.
- */
-
-#ifndef CONFIG_CDCACM_COMPOSITE
- case USB_REQ_GETCONFIGURATION:
- {
- if (ctrl->type == USB_DIR_IN)
- {
- *(uint8_t*)ctrlreq->buf = priv->config;
- ret = 1;
- }
- }
- break;
-#endif
-
- case USB_REQ_SETINTERFACE:
- {
- if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE &&
- priv->config == CDCACM_CONFIGID)
- {
- if ((index == CDCACM_NOTIFID && value == CDCACM_NOTALTIFID) ||
- (index == CDCACM_DATAIFID && value == CDCACM_DATAALTIFID))
- {
- cdcacm_resetconfig(priv);
- cdcacm_setconfig(priv, priv->config);
- ret = 0;
- }
- }
- }
- break;
-
- case USB_REQ_GETINTERFACE:
- {
- if (ctrl->type == (USB_DIR_IN|USB_REQ_RECIPIENT_INTERFACE) &&
- priv->config == CDCACM_CONFIGIDNONE)
- {
- if ((index == CDCACM_NOTIFID && value == CDCACM_NOTALTIFID) ||
- (index == CDCACM_DATAIFID && value == CDCACM_DATAALTIFID))
- {
- *(uint8_t*) ctrlreq->buf = value;
- ret = 1;
- }
- else
- {
- ret = -EDOM;
- }
- }
- }
- break;
-
- default:
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req);
- break;
- }
- }
-
- else if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS)
- {
- /***********************************************************************
- * CDC ACM-Specific Requests
- ***********************************************************************/
-
- switch (ctrl->req)
- {
- /* ACM_GET_LINE_CODING requests current DTE rate, stop-bits, parity, and
- * number-of-character bits. (Optional)
- */
-
- case ACM_GET_LINE_CODING:
- {
- if (ctrl->type == (USB_DIR_IN|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE) &&
- index == CDCACM_NOTIFID)
- {
- /* Return the current line status from the private data structure */
-
- memcpy(ctrlreq->buf, &priv->linecoding, SIZEOF_CDC_LINECODING);
- ret = SIZEOF_CDC_LINECODING;
- }
- else
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
- }
- }
- break;
-
- /* ACM_SET_LINE_CODING configures DTE rate, stop-bits, parity, and
- * number-of-character bits. (Optional)
- */
-
- case ACM_SET_LINE_CODING:
- {
- if (ctrl->type == (USB_DIR_OUT|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE) &&
- len == SIZEOF_CDC_LINECODING && /* dataout && len == outlen && */
- index == CDCACM_NOTIFID)
- {
- /* Save the new line coding in the private data structure. NOTE:
- * that this is conditional now because not all device controller
- * drivers supported provisioni of EP0 OUT data with the setup
- * command.
- */
-
- if (dataout && len <= SIZEOF_CDC_LINECODING) /* REVISIT */
- {
- memcpy(&priv->linecoding, dataout, SIZEOF_CDC_LINECODING);
- }
- ret = 0;
-
- /* If there is a registered callback to receive line status info, then
- * callout now.
- */
-
- if (priv->callback)
- {
- priv->callback(CDCACM_EVENT_LINECODING);
- }
- }
- else
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
- }
- }
- break;
-
- /* ACM_SET_CTRL_LINE_STATE: RS-232 signal used to tell the DCE device the
- * DTE device is now present. (Optional)
- */
-
- case ACM_SET_CTRL_LINE_STATE:
- {
- if (ctrl->type == (USB_DIR_OUT|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE) &&
- index == CDCACM_NOTIFID)
- {
- /* Save the control line state in the private data structure. Only bits
- * 0 and 1 have meaning.
- */
-
- priv->ctrlline = value & 3;
- ret = 0;
-
- /* If there is a registered callback to receive control line status info,
- * then callout now.
- */
-
- if (priv->callback)
- {
- priv->callback(CDCACM_EVENT_CTRLLINE);
- }
- }
- else
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
- }
- }
- break;
-
- /* Sends special carrier*/
-
- case ACM_SEND_BREAK:
- {
- if (ctrl->type == (USB_DIR_OUT|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE) &&
- index == CDCACM_NOTIFID)
- {
- /* If there is a registered callback to handle the SendBreak request,
- * then callout now.
- */
-
- ret = 0;
- if (priv->callback)
- {
- priv->callback(CDCACM_EVENT_SENDBREAK);
- }
- }
- else
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->type);
- }
- }
- break;
-
- default:
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCLASSREQ), ctrl->req);
- break;
- }
- }
- else
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type);
- }
-
- /* Respond to the setup command if data was returned. On an error return
- * value (ret < 0), the USB driver will stall.
- */
-
- if (ret >= 0)
- {
- /* Configure the response */
-
- ctrlreq->len = MIN(len, ret);
- ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
-
- /* Send the response -- either directly to the USB controller or
- * indirectly in the case where this class is a member of a composite
- * device.
- */
-
-#ifndef CONFIG_CDCACM_COMPOSITE
- ret = EP_SUBMIT(dev->ep0, ctrlreq);
-#else
- ret = composite_ep0submit(driver, dev, ctrlreq);
-#endif
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ), (uint16_t)-ret);
- ctrlreq->result = OK;
- cdcacm_ep0incomplete(dev->ep0, ctrlreq);
- }
- }
- return ret;
-}
-
-/****************************************************************************
- * Name: cdcacm_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 cdcacm_disconnect(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev)
-{
- FAR struct cdcacm_dev_s *priv;
- irqstate_t flags;
-
- usbtrace(TRACE_CLASSDISCONNECT, 0);
-
-#ifdef CONFIG_DEBUG
- if (!driver || !dev || !dev->ep0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
-#endif
-
- /* Extract reference to private data */
-
- priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
-
-#ifdef CONFIG_DEBUG
- if (!priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
- return;
- }
-#endif
-
- /* Inform the "upper half serial driver that we have lost the USB serial
- * connection.
- */
-
- flags = irqsave();
-#ifdef CONFIG_SERIAL_REMOVABLE
- uart_connected(&priv->serdev, false);
-#endif
-
- /* Reset the configuration */
-
- cdcacm_resetconfig(priv);
-
- /* Clear out all outgoing 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 (unless we are part of a composite device)
- */
-
-#ifndef CONFIG_CDCACM_COMPOSITE
- DEV_CONNECT(dev);
-#endif
-}
-
-/****************************************************************************
- * Name: cdcacm_suspend
- *
- * Description:
- * Handle the USB suspend event.
- *
- ****************************************************************************/
-
-#ifdef CONFIG_SERIAL_REMOVABLE
-static void cdcacm_suspend(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev)
-{
- FAR struct cdcacm_dev_s *priv;
-
- usbtrace(TRACE_CLASSSUSPEND, 0);
-
-#ifdef CONFIG_DEBUG
- if (!driver || !dev)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
-#endif
-
- /* Extract reference to private data */
-
- priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
-
- /* And let the "upper half" driver now that we are suspended */
-
- uart_connected(&priv->serdev, false);
-}
-#endif
-
-/****************************************************************************
- * Name: cdcacm_resume
- *
- * Description:
- * Handle the USB resume event.
- *
- ****************************************************************************/
-
-#ifdef CONFIG_SERIAL_REMOVABLE
-static void cdcacm_resume(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev)
-{
- FAR struct cdcacm_dev_s *priv;
-
- usbtrace(TRACE_CLASSRESUME, 0);
-
-#ifdef CONFIG_DEBUG
- if (!driver || !dev)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
-#endif
-
- /* Extract reference to private data */
-
- priv = ((FAR struct cdcacm_driver_s*)driver)->dev;
-
- /* Are we still configured? */
-
- if (priv->config != CDCACM_CONFIGIDNONE)
- {
- /* Yes.. let the "upper half" know that have resumed */
-
- uart_connected(&priv->serdev, true);
- }
-}
-#endif
-
-/****************************************************************************
- * Serial Device Methods
- ****************************************************************************/
-
-/****************************************************************************
- * Name: cdcuart_setup
- *
- * Description:
- * This method is called the first time that the serial port is opened.
- *
- ****************************************************************************/
-
-static int cdcuart_setup(FAR struct uart_dev_s *dev)
-{
- FAR struct cdcacm_dev_s *priv;
-
- usbtrace(CDCACM_CLASSAPI_SETUP, 0);
-
- /* Sanity check */
-
-#if CONFIG_DEBUG
- if (!dev || !dev->priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return -EIO;
- }
-#endif
-
- /* Extract reference to private data */
-
- priv = (FAR struct cdcacm_dev_s*)dev->priv;
-
- /* Check if we have been configured */
-
- if (priv->config == CDCACM_CONFIGIDNONE)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SETUPNOTCONNECTED), 0);
- return -ENOTCONN;
- }
-
- return OK;
-}
-
-/****************************************************************************
- * Name: cdcuart_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
- * cdcuart_txempty() until that function returns true before calling this
- * function.
- *
- ****************************************************************************/
-
-static void cdcuart_shutdown(FAR struct uart_dev_s *dev)
-{
- usbtrace(CDCACM_CLASSAPI_SHUTDOWN, 0);
-
- /* Sanity check */
-
-#if CONFIG_DEBUG
- if (!dev || !dev->priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- }
-#endif
-}
-
-/****************************************************************************
- * Name: cdcuart_attach
- *
- * Description:
- * Does not apply to the USB serial class device
- *
- ****************************************************************************/
-
-static int cdcuart_attach(FAR struct uart_dev_s *dev)
-{
- usbtrace(CDCACM_CLASSAPI_ATTACH, 0);
- return OK;
-}
-
-/****************************************************************************
- * Name: cdcuart_detach
- *
- * Description:
-* Does not apply to the USB serial class device
- *
- ****************************************************************************/
-
-static void cdcuart_detach(FAR struct uart_dev_s *dev)
-{
- usbtrace(CDCACM_CLASSAPI_DETACH, 0);
-}
-
-/****************************************************************************
- * Name: cdcuart_ioctl
- *
- * Description:
- * All ioctl calls will be routed through this method
- *
- ****************************************************************************/
-
-static int cdcuart_ioctl(FAR struct file *filep,int cmd,unsigned long arg)
-{
- struct inode *inode = filep->f_inode;
- struct cdcacm_dev_s *priv = inode->i_private;
- int ret = OK;
-
- switch (cmd)
- {
- /* CAICO_REGISTERCB
- * Register a callback for serial event notification. Argument:
- * cdcacm_callback_t. See cdcacm_callback_t type definition below.
- * NOTE: The callback will most likely invoked at the interrupt level.
- * The called back function should, therefore, limit its operations to
- * invoking some kind of IPC to handle the serial event in some normal
- * task environment.
- */
-
- case CAIOC_REGISTERCB:
- {
- /* Save the new callback function */
-
- priv->callback = (cdcacm_callback_t)((uintptr_t)arg);
- }
- break;
-
- /* CAIOC_GETLINECODING
- * Get current line coding. Argument: struct cdc_linecoding_s*.
- * See include/nuttx/usb/cdc.h for structure definition. This IOCTL
- * should be called to get the data associated with the
- * CDCACM_EVENT_LINECODING event).
- */
-
- case CAIOC_GETLINECODING:
- {
- FAR struct cdc_linecoding_s *ptr = (FAR struct cdc_linecoding_s *)((uintptr_t)arg);
- if (ptr)
- {
- memcpy(ptr, &priv->linecoding, sizeof(struct cdc_linecoding_s));
- }
- else
- {
- ret = -EINVAL;
- }
- }
- break;
-
- /* CAIOC_GETCTRLLINE
- * Get control line status bits. Argument FAR int*. See
- * include/nuttx/usb/cdc.h for bit definitions. This IOCTL should be
- * called to get the data associated CDCACM_EVENT_CTRLLINE event.
- */
-
- case CAIOC_GETCTRLLINE:
- {
- FAR int *ptr = (FAR int *)((uintptr_t)arg);
- if (ptr)
- {
- *ptr = priv->ctrlline;
- }
- else
- {
- ret = -EINVAL;
- }
- }
- break;
-
- /* CAIOC_NOTIFY
- * Send a serial state to the host via the Interrupt IN endpoint.
- * Argument: int. This includes the current state of the carrier detect,
- * DSR, break, and ring signal. See "Table 69: UART State Bitmap Values"
- * and CDC_UART_definitions in include/nuttx/usb/cdc.h.
- */
-
- case CAIOC_NOTIFY:
- {
- /* Not yet implemented. I probably won't bother to implement until
- * I comr up with a usage model that needs it.
- *
- * Here is what the needs to be done:
- *
- * 1. Format and send a request header with:
- *
- * bmRequestType:
- * USB_REQ_DIR_IN|USB_REQ_TYPE_CLASS|USB_REQ_RECIPIENT_INTERFACE
- * bRequest: ACM_SERIAL_STATE
- * wValue: 0
- * wIndex: 0
- * wLength: Length of data
- *
- * 2. Followed by the notification data (in a separate packet)
- */
-
- ret = -ENOSYS;
- }
- break;
-
- default:
- ret = -ENOTTY;
- break;
- }
-
- return ret;
-}
-
-/****************************************************************************
- * Name: cdcuart_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 cdcuart_setup
- * and cdcuart_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 cdcuart_detach
- * and cdcuart_shutdown are called).
- *
- ****************************************************************************/
-
-static void cdcuart_rxint(FAR struct uart_dev_s *dev, bool enable)
-{
- FAR struct cdcacm_dev_s *priv;
- FAR uart_dev_t *serdev;
- irqstate_t flags;
-
- usbtrace(CDCACM_CLASSAPI_RXINT, (uint16_t)enable);
-
- /* Sanity check */
-
-#if CONFIG_DEBUG
- if (!dev || !dev->priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
-#endif
-
- /* Extract reference to private data */
-
- priv = (FAR struct cdcacm_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: cdcuart_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 cdcuart_txint(FAR struct uart_dev_s *dev, bool enable)
-{
- FAR struct cdcacm_dev_s *priv;
-
- usbtrace(CDCACM_CLASSAPI_TXINT, (uint16_t)enable);
-
- /* Sanity checks */
-
-#if CONFIG_DEBUG
- if (!dev || !dev->priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
-#endif
-
- /* Extract references to private data */
-
- priv = (FAR struct cdcacm_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)
- {
- cdcacm_sndpacket(priv);
- }
-}
-
-/****************************************************************************
- * Name: cdcuart_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 cdcuart_txempty(FAR struct uart_dev_s *dev)
-{
- FAR struct cdcacm_dev_s *priv = (FAR struct cdcacm_dev_s*)dev->priv;
-
- usbtrace(CDCACM_CLASSAPI_TXEMPTY, 0);
-
-#if CONFIG_DEBUG
- if (!priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_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_CDCACM_NWRREQS;
-}
-
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-/****************************************************************************
- * Name: cdcacm_classobject
- *
- * Description:
- * Register USB serial port (and USB serial console if so configured) and
- * return the class object.
- *
- * Input Parameter:
- * minor - Device minor number. E.g., minor 0 would correspond to
- * /dev/ttyACM0.
- * classdev - The location to return the CDC serial class' device
- * instance.
- *
- * Returned Value:
- * A pointer to the allocated class object (NULL on failure).
- *
- ****************************************************************************/
-
-#ifndef CONFIG_CDCACM_COMPOSITE
-static
-#endif
-int cdcacm_classobject(int minor, FAR struct usbdevclass_driver_s **classdev)
-{
- FAR struct cdcacm_alloc_s *alloc;
- FAR struct cdcacm_dev_s *priv;
- FAR struct cdcacm_driver_s *drvr;
- char devname[CDCACM_DEVNAME_SIZE];
- int ret;
-
- /* Allocate the structures needed */
-
- alloc = (FAR struct cdcacm_alloc_s*)kmalloc(sizeof(struct cdcacm_alloc_s));
- if (!alloc)
- {
- usbtrace(TRACE_CLSERROR(USBSER_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 cdcacm_dev_s));
- sq_init(&priv->reqlist);
-
- priv->minor = minor;
-
- /* Fake line status */
-
- priv->linecoding.baud[0] = (115200) & 0xff; /* Baud=115200 */
- priv->linecoding.baud[1] = (115200 >> 8) & 0xff;
- priv->linecoding.baud[2] = (115200 >> 16) & 0xff;
- priv->linecoding.baud[3] = (115200 >> 24) & 0xff;
- priv->linecoding.stop = CDC_CHFMT_STOP1; /* One stop bit */
- priv->linecoding.parity = CDC_PARITY_NONE; /* No parity */
- priv->linecoding.nbits = 8; /* 8 data bits */
-
- /* Initialize the serial driver sub-structure */
-
- /* The initial state is disconnected */
-
-#ifdef CONFIG_SERIAL_REMOVABLE
- priv->serdev.disconnected = true;
-#endif
- priv->serdev.recv.size = CONFIG_CDCACM_RXBUFSIZE;
- priv->serdev.recv.buffer = priv->rxbuffer;
- priv->serdev.xmit.size = CONFIG_CDCACM_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 console */
-
-#ifdef CONFIG_CDCACM_CONSOLE
- priv->serdev.isconsole = true;
- ret = uart_register("/dev/console", &priv->serdev);
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONSOLEREGISTER), (uint16_t)-ret);
- goto errout_with_class;
- }
-#endif
-
- /* Register the CDC/ACM TTY device */
-
- sprintf(devname, CDCACM_DEVNAME_FORMAT, minor);
- ret = uart_register(devname, &priv->serdev);
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UARTREGISTER), (uint16_t)-ret);
- goto errout_with_class;
- }
-
- *classdev = &drvr->drvr;
- return OK;
-
-errout_with_class:
- kfree(alloc);
- return ret;
-}
-
-/****************************************************************************
- * Name: cdcacm_initialize
- *
- * Description:
- * Register USB serial port (and USB serial console if so configured).
- *
- * Input Parameter:
- * minor - Device minor number. E.g., minor 0 would correspond to
- * /dev/ttyACM0.
- * handle - An optional opaque reference to the CDC/ACM class object that
- * may subsequently be used with cdcacm_uninitialize().
- *
- * Returned Value:
- * Zero (OK) means that the driver was successfully registered. On any
- * failure, a negated errno value is retured.
- *
- ****************************************************************************/
-
-#ifndef CONFIG_CDCACM_COMPOSITE
-int cdcacm_initialize(int minor, FAR void **handle)
-{
- FAR struct usbdevclass_driver_s *drvr = NULL;
- int ret;
-
- /* Get an instance of the serial driver class object */
-
- ret = cdcacm_classobject(minor, &drvr);
- if (ret == OK)
- {
- /* Register the USB serial class driver */
-
- ret = usbdev_register(drvr);
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_DEVREGISTER), (uint16_t)-ret);
- }
- }
-
- /* Return the driver instance (if any) if the caller has requested it
- * by provided a pointer to the location to return it.
- */
-
- if (handle)
- {
- *handle = (FAR void*)drvr;
- }
-
- return ret;
-}
-#endif
-
-/****************************************************************************
- * Name: cdcacm_uninitialize
- *
- * Description:
- * Un-initialize the USB storage class driver. This function is used
- * internally by the USB composite driver to unitialize the CDC/ACM
- * driver. This same interface is available (with an untyped input
- * parameter) when the CDC/ACM driver is used standalone.
- *
- * Input Parameters:
- * There is one parameter, it differs in typing depending upon whether the
- * CDC/ACM driver is an internal part of a composite device, or a standalone
- * USB driver:
- *
- * classdev - The class object returned by board_cdcclassobject() or
- * cdcacm_classobject()
- * handle - The opaque handle represetning the class object returned by
- * a previous call to cdcacm_initialize().
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
-
-#ifdef CONFIG_CDCACM_COMPOSITE
-void cdcacm_uninitialize(FAR struct usbdevclass_driver_s *classdev)
-#else
-void cdcacm_uninitialize(FAR void *handle)
-#endif
-{
-#ifdef CONFIG_CDCACM_COMPOSITE
- FAR struct cdcacm_driver_s *drvr = (FAR struct cdcacm_driver_s *)classdev;
-#else
- FAR struct cdcacm_driver_s *drvr = (FAR struct cdcacm_driver_s *)handle;
-#endif
- FAR struct cdcacm_dev_s *priv = drvr->dev;
- char devname[CDCACM_DEVNAME_SIZE];
- int ret;
-
- /* Un-register the CDC/ACM TTY device */
-
- sprintf(devname, CDCACM_DEVNAME_FORMAT, priv->minor);
- ret = unregister_driver(devname);
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UARTUNREGISTER), (uint16_t)-ret);
- }
-
- /* Unregister the driver (unless we are a part of a composite device). The
- * device unregister logic will (1) return all of the requests to us then
- * (2) all the unbind method.
- *
- * The same thing will happen in the composite case except that: (1) the
- * composite driver will call usbdev_unregister() which will (2) return the
- * requests for all members of the composite, and (3) call the unbind
- * method in the composite device which will (4) call the unbind method
- * for this device.
- */
-
-#ifndef CONFIG_CDCACM_COMPOSITE
- usbdev_unregister(&drvr->drvr);
-#endif
-
- /* And free the driver structure */
-
- kfree(priv);
-}