diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2008-10-06 23:12:09 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2008-10-06 23:12:09 +0000 |
commit | 83c1bf7f781802063f669a943d2c6097f7c64940 (patch) | |
tree | 93ad4d4bd27197beb9c828484a8eb4ca404970e3 | |
parent | 9a5e94e2222d2d5c5b613c0fec682643b2821c10 (diff) | |
download | px4-nuttx-83c1bf7f781802063f669a943d2c6097f7c64940.tar.gz px4-nuttx-83c1bf7f781802063f669a943d2c6097f7c64940.tar.bz2 px4-nuttx-83c1bf7f781802063f669a943d2c6097f7c64940.zip |
Add USB serial driver
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@984 42af7a65-404d-4744-a932-0658087f49c3
-rw-r--r-- | nuttx/ChangeLog | 3 | ||||
-rw-r--r-- | nuttx/Documentation/NuttX.html | 3 | ||||
-rw-r--r-- | nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c | 60 | ||||
-rw-r--r-- | nuttx/configs/mcu123-lpc214x/nsh/defconfig | 4 | ||||
-rw-r--r-- | nuttx/configs/mcu123-lpc214x/ostest/defconfig | 4 | ||||
-rw-r--r-- | nuttx/configs/ntosd-dm320/nettest/defconfig | 4 | ||||
-rw-r--r-- | nuttx/configs/ntosd-dm320/nsh/defconfig | 4 | ||||
-rw-r--r-- | nuttx/configs/ntosd-dm320/ostest/defconfig | 4 | ||||
-rw-r--r-- | nuttx/configs/ntosd-dm320/udp/defconfig | 4 | ||||
-rw-r--r-- | nuttx/configs/ntosd-dm320/uip/defconfig | 4 | ||||
-rw-r--r-- | nuttx/drivers/usbdev/Make.defs | 2 | ||||
-rw-r--r-- | nuttx/drivers/usbdev/usbdev_serial.c | 1860 | ||||
-rw-r--r-- | nuttx/include/nuttx/usbdev_trace.h | 50 |
13 files changed, 1946 insertions, 60 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index 5c57f6aea..29199d4a1 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -485,6 +485,9 @@ * Add an option to set aside a separate stack for interrupt handling (ARM only). This is useful when memory is constrained, there are multiple tasks, and the interrupt stack requirement is high (as when USB is enabled). + * Basic LPC214x USB device side driver basically functional (but probably still buggy) + * Initial USB serial class device side driver check in (not well tested at initial checkin) + * Add LPC214x USB serial configuration; Add examples/usbserial test (still a work in progress) diff --git a/nuttx/Documentation/NuttX.html b/nuttx/Documentation/NuttX.html index b405c52df..9e4b2ce0a 100644 --- a/nuttx/Documentation/NuttX.html +++ b/nuttx/Documentation/NuttX.html @@ -1075,6 +1075,9 @@ nuttx-0.3.16 2008-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> * Add an option to set aside a separate stack for interrupt handling (ARM only). This is useful when memory is constrained, there are multiple tasks, and the interrupt stack requirement is high (as when USB is enabled). + * Basic LPC214x USB device side driver basically functional (but probably still buggy) + * Initial USB serial class device side driver check in (not well tested at initial checkin) + * Add LPC214x USB serial configuration; Add examples/usbserial test (still a work in progress) pascal-0.1.3 2008-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> diff --git a/nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c b/nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c index b2780c775..4141f4a39 100644 --- a/nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c +++ b/nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c @@ -1014,11 +1014,21 @@ static int lpc214x_wrrequest(struct lpc214x_ep_s *privep) uvdbg("len=%d xfrd=%d nullpkt=%d\n", privreq->req.len, privreq->req.xfrd, privep->txnullpkt); - /* Ignore any attempt to send a zero length packet */ + /* Ignore any attempt to send a zero length packet on anything but EP0IN */ if (privreq->req.len == 0) { - usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EPINNULLPACKET), 0); + if (privep->epphy == LPC214X_EP0_IN) + { + lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0); + } + else + { + usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EPINNULLPACKET), 0); + } + + /* In any event, the request is complete */ + lpc214x_reqcomplete(privep, OK); return OK; } @@ -1390,6 +1400,8 @@ static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv, usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DISPATCH), 0); if (priv && priv->driver) { + /* Forward to the control request to the class driver implementation */ + ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl); if (ret < 0) { @@ -1414,7 +1426,9 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) struct lpc214x_ep_s *ep0 = &priv->eplist[LPC214X_EP0_OUT]; struct lpc214x_req_s *privreq = lpc214x_rqpeek(ep0); struct usb_ctrlreq_s ctrl; + uint16 value; uint16 index; + uint16 len; ubyte epphy; ubyte response[2]; int ret; @@ -1454,9 +1468,14 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) return; } + /* And 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, - GETUINT16(ctrl.value), GETUINT16(ctrl.index), GETUINT16(ctrl.len)); + ctrl.type, ctrl.req, value, index, len); /* Dispatch any non-standard requests */ @@ -1481,8 +1500,8 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) */ usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETSTATUS), 0); - if (!priv->paddrset || GETUINT16(ctrl.len) != 2 || - (ctrl.type & USB_REQ_DIR_IN) == 0 || GETUINT16(ctrl.value) != 0) + if (!priv->paddrset || len != 2 || + (ctrl.type & USB_REQ_DIR_IN) == 0 || value != 0) { priv->stalled = 1; } @@ -1493,7 +1512,7 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) case USB_REQ_RECIPIENT_ENDPOINT: { usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPGETSTATUS), 0); - epphy = USB_EPNO(GETUINT16(ctrl.index)) << 1; + epphy = USB_EPNO(index) << 1; if (epphy < LPC214X_NPHYSENDPOINTS) { if ((lpc214x_usbcmd(CMD_USB_EP_SELECT|epphy, 0) & CMD_USB_EPSELECT_ST) != 0) @@ -1518,7 +1537,6 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) case USB_REQ_RECIPIENT_DEVICE: { - index = GETUINT16(ctrl.index); if (index == 0) { usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVGETSTATUS), 0); @@ -1573,10 +1591,10 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) { lpc214x_dispatchrequest(priv, &ctrl); } - else if (priv->paddrset && GETUINT16(ctrl.value) == USB_FEATURE_ENDPOINTHALT && - GETUINT16(ctrl.index) < LPC214X_NLOGENDPOINTS && GETUINT16(ctrl.len) == 0) + else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && + index < LPC214X_NLOGENDPOINTS && len == 0) { - ubyte epphys = LPC214X_EP_LOG2PHY(GETUINT16(ctrl.index)); + ubyte epphys = LPC214X_EP_LOG2PHY(index); priv->eplist[epphys].halted = 0; lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0); priv->ep0state = LPC214X_EP0STATUSIN; @@ -1599,18 +1617,18 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SETFEATURE), 0); if (((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) && - GETUINT16(ctrl.value) == USB_FEATURE_TESTMODE) + value == USB_FEATURE_TESTMODE) { - uvdbg("test mode: %d\n", GETUINT16(ctrl.index)); + uvdbg("test mode: %d\n", index); } else if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) { lpc214x_dispatchrequest(priv, &ctrl); } - else if (priv->paddrset && GETUINT16(ctrl.value) == USB_FEATURE_ENDPOINTHALT && - GETUINT16(ctrl.index) < LPC214X_NLOGENDPOINTS && GETUINT16(ctrl.len) == 0) + else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && + index < LPC214X_NLOGENDPOINTS && len == 0) { - ubyte epphys = LPC214X_EP_LOG2PHY(GETUINT16(ctrl.index)); + ubyte epphys = LPC214X_EP_LOG2PHY(index); priv->eplist[epphys].halted = 1; lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0); priv->ep0state = LPC214X_EP0STATUSIN; @@ -1631,10 +1649,9 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) * len: 0; data = none */ - usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0SETUPSETADDRESS), GETUINT16(ctrl.value)); + usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0SETUPSETADDRESS), value); if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && - GETUINT16(ctrl.index) == 0 && GETUINT16(ctrl.len) == 0 && - GETUINT16(ctrl.value) < 128) + index == 0 && len == 0 && value < 128) { /* Save the address. We cannot actually change to the next address until * the completion of the status phase. @@ -1699,8 +1716,7 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) { usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETCONFIG), 0); if (priv->paddrset && (ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && - GETUINT16(ctrl.value) == 0 && GETUINT16(ctrl.index) == 0 && - GETUINT16(ctrl.len) == 1) + value == 0 && index == 0 && len == 1) { lpc214x_dispatchrequest(priv, &ctrl); } @@ -1721,7 +1737,7 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) { usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SETCONFIG), 0); if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && - GETUINT16(ctrl.index) == 0 && GETUINT16(ctrl.len) == 0) + index == 0 && len == 0) { lpc214x_dispatchrequest(priv, &ctrl); } diff --git a/nuttx/configs/mcu123-lpc214x/nsh/defconfig b/nuttx/configs/mcu123-lpc214x/nsh/defconfig index cf194adf1..266577649 100644 --- a/nuttx/configs/mcu123-lpc214x/nsh/defconfig +++ b/nuttx/configs/mcu123-lpc214x/nsh/defconfig @@ -398,8 +398,8 @@ CONFIG_USBSER_EPOUT=1 CONFIG_USBSER_EPIN=2 CONFIG_USBSER_NWRREQS=4 CONFIG_USBSER_NRDREQS=4 -CONFIG_USBSER_VENDORID=0x0525 -CONFIG_USBSER_PRODUCTID=0xa4a6 +CONFIG_USBSER_VENDORID=0x067b +CONFIG_USBSER_PRODUCTID=0x2303 CONFIG_USBSER_VENDORSTR="Nuttx" CONFIG_USBSER_PRODUCTSTR="USBdev Serial" CONFIG_USBSER_RXBUFSIZE=512 diff --git a/nuttx/configs/mcu123-lpc214x/ostest/defconfig b/nuttx/configs/mcu123-lpc214x/ostest/defconfig index fdbabd3ed..f0fe2f49a 100644 --- a/nuttx/configs/mcu123-lpc214x/ostest/defconfig +++ b/nuttx/configs/mcu123-lpc214x/ostest/defconfig @@ -398,8 +398,8 @@ CONFIG_USBSER_EPOUT=1 CONFIG_USBSER_EPIN=2 CONFIG_USBSER_NWRREQS=4 CONFIG_USBSER_NRDREQS=4 -CONFIG_USBSER_VENDORID=0x0525 -CONFIG_USBSER_PRODUCTID=0xa4a6 +CONFIG_USBSER_VENDORID=0x067b +CONFIG_USBSER_PRODUCTID=0x2303 CONFIG_USBSER_VENDORSTR="Nuttx" CONFIG_USBSER_PRODUCTSTR="USBdev Serial" CONFIG_USBSER_RXBUFSIZE=512 diff --git a/nuttx/configs/ntosd-dm320/nettest/defconfig b/nuttx/configs/ntosd-dm320/nettest/defconfig index a17635949..934b6d6d8 100644 --- a/nuttx/configs/ntosd-dm320/nettest/defconfig +++ b/nuttx/configs/ntosd-dm320/nettest/defconfig @@ -375,8 +375,8 @@ CONFIG_USBSER_EPOUT=1 CONFIG_USBSER_EPIN=2 CONFIG_USBSER_NWRREQS=4 CONFIG_USBSER_NRDREQS=4 -CONFIG_USBSER_VENDORID=0x0525 -CONFIG_USBSER_PRODUCTID=0xa4a6 +CONFIG_USBSER_VENDORID=0x067b +CONFIG_USBSER_PRODUCTID=0x2303 CONFIG_USBSER_VENDORSTR="Nuttx" CONFIG_USBSER_PRODUCTSTR="USBdev Serial" CONFIG_USBSER_RXBUFSIZE=512 diff --git a/nuttx/configs/ntosd-dm320/nsh/defconfig b/nuttx/configs/ntosd-dm320/nsh/defconfig index 4fb6c47bb..bfe69b341 100644 --- a/nuttx/configs/ntosd-dm320/nsh/defconfig +++ b/nuttx/configs/ntosd-dm320/nsh/defconfig @@ -383,8 +383,8 @@ CONFIG_USBSER_EPOUT=1 CONFIG_USBSER_EPIN=2 CONFIG_USBSER_NWRREQS=4 CONFIG_USBSER_NRDREQS=4 -CONFIG_USBSER_VENDORID=0x0525 -CONFIG_USBSER_PRODUCTID=0xa4a6 +CONFIG_USBSER_VENDORID=0x067b +CONFIG_USBSER_PRODUCTID=0x2303 CONFIG_USBSER_VENDORSTR="Nuttx" CONFIG_USBSER_PRODUCTSTR="USBdev Serial" CONFIG_USBSER_RXBUFSIZE=512 diff --git a/nuttx/configs/ntosd-dm320/ostest/defconfig b/nuttx/configs/ntosd-dm320/ostest/defconfig index a8ac562d8..b50367d0d 100644 --- a/nuttx/configs/ntosd-dm320/ostest/defconfig +++ b/nuttx/configs/ntosd-dm320/ostest/defconfig @@ -375,8 +375,8 @@ CONFIG_USBSER_EPOUT=1 CONFIG_USBSER_EPIN=2 CONFIG_USBSER_NWRREQS=4 CONFIG_USBSER_NRDREQS=4 -CONFIG_USBSER_VENDORID=0x0525 -CONFIG_USBSER_PRODUCTID=0xa4a6 +CONFIG_USBSER_VENDORID=0x067b +CONFIG_USBSER_PRODUCTID=0x2303 CONFIG_USBSER_VENDORSTR="Nuttx" CONFIG_USBSER_PRODUCTSTR="USBdev Serial" CONFIG_USBSER_RXBUFSIZE=512 diff --git a/nuttx/configs/ntosd-dm320/udp/defconfig b/nuttx/configs/ntosd-dm320/udp/defconfig index d4c02d7ec..d90659a4d 100644 --- a/nuttx/configs/ntosd-dm320/udp/defconfig +++ b/nuttx/configs/ntosd-dm320/udp/defconfig @@ -375,8 +375,8 @@ CONFIG_USBSER_EPOUT=1 CONFIG_USBSER_EPIN=2 CONFIG_USBSER_NWRREQS=4 CONFIG_USBSER_NRDREQS=4 -CONFIG_USBSER_VENDORID=0x0525 -CONFIG_USBSER_PRODUCTID=0xa4a6 +CONFIG_USBSER_VENDORID=0x067b +CONFIG_USBSER_PRODUCTID=0x2303 CONFIG_USBSER_VENDORSTR="Nuttx" CONFIG_USBSER_PRODUCTSTR="USBdev Serial" CONFIG_USBSER_RXBUFSIZE=512 diff --git a/nuttx/configs/ntosd-dm320/uip/defconfig b/nuttx/configs/ntosd-dm320/uip/defconfig index 9f19366e8..b69b86749 100644 --- a/nuttx/configs/ntosd-dm320/uip/defconfig +++ b/nuttx/configs/ntosd-dm320/uip/defconfig @@ -375,8 +375,8 @@ CONFIG_USBSER_EPOUT=1 CONFIG_USBSER_EPIN=2 CONFIG_USBSER_NWRREQS=4 CONFIG_USBSER_NRDREQS=4 -CONFIG_USBSER_VENDORID=0x0525 -CONFIG_USBSER_PRODUCTID=0xa4a6 +CONFIG_USBSER_VENDORID=0x067b +CONFIG_USBSER_PRODUCTID=0x2303 CONFIG_USBSER_VENDORSTR="Nuttx" CONFIG_USBSER_PRODUCTSTR="USBdev Serial" CONFIG_USBSER_RXBUFSIZE=512 diff --git a/nuttx/drivers/usbdev/Make.defs b/nuttx/drivers/usbdev/Make.defs index da1af40ae..dd2149ffa 100644 --- a/nuttx/drivers/usbdev/Make.defs +++ b/nuttx/drivers/usbdev/Make.defs @@ -37,6 +37,6 @@ USBDEV_ASRCS = USBDEV_CSRCS = ifeq ($(CONFIG_USBDEV),y) -USBDEV_CSRCS += usbdev_trace.c +USBDEV_CSRCS += usbdev_serial.c usbdev_trace.c endif diff --git a/nuttx/drivers/usbdev/usbdev_serial.c b/nuttx/drivers/usbdev/usbdev_serial.c new file mode 100644 index 000000000..1ee3d307e --- /dev/null +++ b/nuttx/drivers/usbdev/usbdev_serial.c @@ -0,0 +1,1860 @@ +/**************************************************************************** + * drivers/usbdev/usbdev_serial.c + * + * Copyright (C) 2008 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * This a work-alike clone Prolific PL2303 + * + * 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 <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/arch.h> +#include <nuttx/serial.h> +#include <nuttx/usb.h> +#include <nuttx/usbdev.h> +#include <nuttx/usbdev_trace.h> + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Number of requests in the write queue */ + +#ifndef CONFIG_USBSER_NWRREQS +# define CONFIG_USBSER_NWRREQS 4 +#endif + +/* Number of requests in the read queue */ + +#ifndef CONFIG_USBSER_NRDREQS +# define CONFIG_USBSER_NRDREQS 4 +#endif + +/* Write buffer size */ + +#ifndef CONFIG_USBSER_WRBUFFERSIZE +# define CONFIG_USBSER_WRBUFFERSIZE 1024 +#endif + +/* Logical endpoint numbers / max packet sizes */ + +#ifndef CONFIG_USBSER_EPIN +# define CONFIG_USBSER_EPIN 2 +#endif + +#ifndef CONFIG_USBSER_EPOUT +# define CONFIG_USBSER_EPOUT 1 +#endif + +#ifndef CONFIG_USBSER_EP0MAXPACKET +# define CONFIG_USBSER_EP0MAXPACKET 64 +#endif + +/* Vendor and product IDs and strings */ + +#ifndef CONFIG_USBSER_VENDORID +# define CONFIG_USBSER_VENDORID 0x067b +#endif + +#ifndef CONFIG_USBSER_PRODUCTID +# define CONFIG_USBSER_PRODUCTID 0x2303 +#endif + +#ifndef CONFIG_USBSER_VENDORSTR +# warning "No Vendor string specified" +# define CONFIG_USBSER_VENDORSTR "NuttX" +#endif + +#ifndef CONFIG_USBSER_PRODUCTSTR +# warning "No Product string specified" +# define CONFIG_USBSER_PRODUCTSTR "USBdev Serial" +#endif + +#undef CONFIG_USBSER_SERIALSTR +#define CONFIG_USBSER_SERIALSTR "0" + +#undef CONFIG_USBSER_CONFIGSTR +#define CONFIG_USBSER_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 + +/* These settings are not modifiable via the NuttX configuration */ + +#define USBSER_VERSIONNO (0x0202) /* Device version number */ +#define USBSER_CONFIGIDNONE (0) /* Config ID means to return to address mode */ +#define USBSER_CONFIGID (1) /* The only supported configuration ID */ +#define USBSER_NCONFIGS (1) /* Number of configurations supported */ +#define USBSER_INTERFACEID (0) +#define USBSER_ALTINTERFACEID (0) +#define USBSER_NINTERFACES (1) /* Number of interfaces in the configuration */ +#define USBSER_NENDPOINTS (3) /* Number of endpoints in the interface */ + +/* Endpoint configuration */ + +#define USBSER_EPINTIN_ADDR (USB_DIR_IN|1) +#define USBSER_EPINTIN_ATTR (USB_EP_ATTR_XFER_INT) +#define USBSER_EPINTIN_MXPACKET (10) +#define USBSER_EPOUTBULK_ADDR (2) +#define USBSER_EPOUTBULK_ATTR (USB_EP_ATTR_XFER_BULK) +#define USBSER_EPINBULK_ADDR (USB_DIR_IN|3) +#define USBSER_EPINBULK_ATTR (USB_EP_ATTR_XFER_BULK) + +/* Vender specific control requests */ + +#define PL2303_CONTROL_TYPE 0x20 +#define PL2303_SETLINEREQUEST 0x20 /* OUT, Recipient interface */ +#define PL2303_GETLINEREQUEST 0x21 /* IN, Recipient interface */ +#define PL2303_SETCONTROLREQUEST 0x22 /* OUT, Recipient interface */ +#define PL2303_BREAKREQUEST 0x23 /* OUT, Recipient interface */ + +/* Vendor read/write */ + +#define PL2303_RWREQUEST_TYPE 0x40 +#define PL2303_RWREQUEST 0x01 /* IN/OUT, Recipient device */ + +/* Values *********************************************************************/ + +/* String language */ + +#define USBSER_STR_LANGUAGE 0x0409 /* en-us */ + +/* Descriptor strings */ + +#define USBSER_MANUFACTURERSTRID 1 +#define USBSER_PRODUCTSTRID 2 +#define USBSER_SERIALSTRID 3 +#define USBSER_CONFIGSTRID 4 + +/* Buffer big enough for any of our descriptors */ + +#define USBSER_MXDESCLEN 256 + +/* 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 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Container to support a list of requests */ + +struct usbser_req_s +{ + struct usbser_req_s *flink; /* Implements a singly linked list */ + struct usbdev_req_s *req; +}; + +/* This structure describes the internal state of the driver */ + +struct usbser_dev_s +{ + struct uart_dev_s serdev; /* Serial device structure */ + struct usbdev_s *usbdev; /* usbdev driver pointer */ + ubyte config; /* Configuration number */ + ubyte nwralloc; /* Number of write requests allocated */ + ubyte nwrq; /* Number of queue write requests */ + boolean open; /* TRUE: Driver has been opened */ + boolean wravail; /* TRUE: write data is buffered */ + ubyte linest[7]; /* Fake line status */ + struct usbdev_ep_s *epintin; /* Address of Interrupt IN endpoint */ + struct usbdev_ep_s *epbulkin; /* Address of Bulk IN endpoint */ + struct usbdev_ep_s *epbulkout; /* Address of Bulk OUT endpoint */ + struct usbdev_req_s *ctrlreq; /* Control request */ + struct sq_queue_s reqlist; /* List of write request containers */ + + /* Pre-allocated write requests (linked in reqlist) */ + + struct usbser_req_s wrreqs[CONFIG_USBSER_NWRREQS]; + + /* Serial I/O buffers */ + + char rxbuffer[CONFIG_USBSER_RXBUFSIZE]; + char txbuffer[CONFIG_USBSER_TXBUFSIZE]; +}; + +/* The internal version of the class driver */ + +struct usbser_driver_s +{ + struct usbdevclass_driver_s drvr; + struct usbser_dev_s *dev; +}; + +/* This is what is allocated */ + +struct usbser_alloc_s +{ + struct usbser_dev_s dev; + struct usbser_driver_s drvr; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Transfer helpers *********************************************************/ + +static uint16 usbclass_fillpacket(FAR struct usbser_dev_s *priv, + char *packet, uint16 size); +static int usbclass_sndpacket(FAR struct usbser_dev_s *priv); +static int usbclass_recvpacket(FAR struct usbser_dev_s *priv, + char *packet, uint16 size); + +/* Request helpers *********************************************************/ + +static struct usbdev_req_s *usbclass_allocreq(FAR struct usbdev_ep_s *ep, + uint16 len); +static void usbclass_freereq(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); + +/* Configuration ***********************************************************/ + +static int usbclass_mkstrdesc(ubyte id, struct usb_strdesc_s *strdesc); +static void usbclass_mkepdesc(ubyte addr, ubyte attr, uint16 mxpacket, + struct usb_epdesc_s *desc); +#ifdef CONFIG_USBDEV_DUALSPEED +static sint16 usbclass_mkcfgdesc(ubyte *buf, ubyte speed); +#else +static sint16 usbclass_mkcfgdesc(ubyte *buf); +#endif +static void usbclass_resetconfig(FAR struct usbser_dev_s *priv); +static int usbclass_setconfig(FAR struct usbser_dev_s *priv, + ubyte config); + +/* Completion event handlers ***********************************************/ + +static void usbclass_setupcomplete(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static void usbclass_wrcomplete(FAR struct usbdev_ep_s *ep, + 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, boolean enable); +static void usbser_txint(FAR struct uart_dev_s *dev, boolean enable); +static boolean 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_PER_INTERFACE, /* class */ + 0, /* subclass */ + 0, /* protocol */ + CONFIG_USBSER_EP0MAXPACKET, /* maxpacketsize */ + {LSBYTE(CONFIG_USBSER_VENDORID), /* vendor */ + MSBYTE(CONFIG_USBSER_VENDORID)}, + {LSBYTE(CONFIG_USBSER_PRODUCTID), /* product */ + MSBYTE(CONFIG_USBSER_PRODUCTID)}, + {LSBYTE(USBSER_VERSIONNO), /* device */ + MSBYTE(USBSER_VERSIONNO)}, + USBSER_MANUFACTURERSTRID, /* imfgr */ + USBSER_PRODUCTSTRID, /* iproduct */ + USBSER_SERIALSTRID, /* serno */ + USBSER_NCONFIGS /* nconfigs */ +}; + +static const struct usb_cfgdesc_s g_cfgdesc = +{ + USB_SIZEOF_CFGDESC, /* len */ + USB_DESC_TYPE_CONFIG, /* type */ + {0, 0}, /* totallen -- to be provided */ + USBSER_NINTERFACES, /* ninterfaces */ + USBSER_CONFIGID, /* cfgvalue */ + USBSER_CONFIGSTRID, /* icfg */ + USB_CONFIG_ATTR_ONE|SELFPOWERED|REMOTEWAKEUP, /* attr */ + (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* mxpower */ +}; + +static const struct usb_ifdesc_s g_ifdesc = +{ + USB_SIZEOF_IFDESC, /* len */ + USB_DESC_TYPE_INTERFACE, /* type */ + 0, /* ifno */ + 0, /* alt */ + USBSER_NENDPOINTS, /* neps */ + USB_CLASS_VENDOR_SPEC, /* class */ + 0, /* subclass */ + 0, /* protocol */ + USBSER_CONFIGSTRID /* iif */ +}; + +#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_USBSER_EP0MAXPACKET, /* mxpacketsize */ + USBSER_NCONFIGS, /* nconfigs */ + 0, /* reserved */ +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/************************************************************************************ + * Name: usbclass_fillpacket + * + * Description: + * If there is data to send, a packet is built in 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 usbclass_fillpacket(FAR struct usbser_dev_s *priv, char *packet, uint16 size) +{ + uart_dev_t *serdev = &priv->serdev; + struct uart_buffer_s *xmit = &serdev->xmit; + irqstate_t flags; + uint16 nbytes = 0; + + /* Disable interrupts */ + + flags = irqsave(); + + /* Transfer bytes while we have bytes available and there is room in the packet */ + + while (xmit->head != xmit->tail && nbytes < size) + { + *packet++ = xmit->buffer[xmit->tail]; + nbytes++; + + /* Increment the tail pointer */ + + if (++(xmit->tail) >= xmit->size) + { + xmit->tail = 0; + } + + /* Check if we have to wake up the serial driver */ + + if (serdev->xmitwaiting) + { + serdev->xmitwaiting = FALSE; + sem_post(&serdev->xmitsem); + } + } + + /* When all of the characters have been sent from the buffer + * disable the "TX interrupt". + */ + + if (xmit->head == xmit->tail) + { + priv->wravail = FALSE; + uart_disabletxint(serdev); + } + + irqrestore(flags); + return nbytes; +} + +/************************************************************************************ + * Name: usbclass_sndpacket + * + * Description: + * This function obtains write requests, transfers the TX data into the packet, + * and submits the packets 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) +{ + struct usbdev_ep_s *ep; + struct usbdev_req_s *req; + struct usbser_req_s *reqcontainer; + irqstate_t flags; + int len; + int ret = OK; + +#ifdef CONFIG_DEBUG + if (priv == NULL) + { + 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_fillpacket() + * is unable to fill the packet with data (i.e., untilthere is no more data + * to be sent). + */ + + while (priv->reqlist.head) + { + /* Peek at the request in the container at the head of the list */ + + req = (struct usbdev_req_s *)priv->reqlist.head; + + /* Fill the packet with serial TX data */ + + len = usbclass_fillpacket(priv, req->buf, ep->maxpacket); + if (len > 0) + { + /* Then submit the request to the endpoint */ + + req->len = len; + req->private = reqcontainer; + ret = EP_SUBMIT(ep, req); + if (ret != OK) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SUBMITFAIL), (uint16)-ret); + break; + } + + /* Remove the empty contained from the request list */ + + reqcontainer = (struct usbser_req_s *)sq_remfirst(&priv->reqlist); + priv->nwrq--; + } + 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. + * + ************************************************************************************/ + +static int usbclass_recvpacket(FAR struct usbser_dev_s *priv, char *packet, uint16 size) +{ + uart_dev_t *serdev = &priv->serdev; + struct uart_buffer_s *recv = &serdev->recv; + uint16 nexthead; + + /* Get the next head index */ + + nexthead = recv->head + 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 && size > 0) + { + /* Copy one byte to the head of the circular RX buffer */ + + recv->buffer[recv->head] = *packet++; + + /* Update counts and indices */ + + recv->head = nexthead; + size--; + if (++nexthead >= recv->size) + { + nexthead = 0; + } + + /* Wake up the serial driver if it is waiting for incoming data */ + + if (serdev->recvwaiting) + { + serdev->recvwaiting = FALSE; + sem_post(&serdev->recvsem); + } + } + 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 len) +{ + 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, 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(ubyte 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(USBSER_STR_LANGUAGE); + strdesc->data[1] = MSBYTE(USBSER_STR_LANGUAGE); + return 4; + } + + case USBSER_MANUFACTURERSTRID: + str = CONFIG_USBSER_VENDORSTR; + break; + + case USBSER_PRODUCTSTRID: + str = CONFIG_USBSER_PRODUCTSTR; + break; + + case USBSER_SERIALSTRID: + str = CONFIG_USBSER_SERIALSTR; + break; + + case USBSER_CONFIGSTRID: + str = CONFIG_USBSER_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_mkepdesc + * + * Description: + * Construct the endpoint descriptor + * + ****************************************************************************/ + +static void usbclass_mkepdesc(ubyte addr, ubyte attr, uint16 mxpacket, + struct usb_epdesc_s *desc) +{ + /* Format the endpoint descriptor */ + + desc->len = USB_SIZEOF_EPDESC; + desc->type = USB_DESC_TYPE_ENDPOINT; + desc->addr = addr; + desc->attr = attr; + desc->mxpacketsize[0] = LSBYTE(mxpacket); + desc->mxpacketsize[1] = MSBYTE(mxpacket); + desc->interval = 0; +} + +/**************************************************************************** + * Name: usbclass_mkcfgdesc + * + * Description: + * Construct the configuration descriptor + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DUALSPEED +static sint16 usbclass_mkcfgdesc(ubyte *buf, ubyte speed) +#else +static sint16 usbclass_mkcfgdesc(ubyte *buf) +#endif +{ + struct usb_cfgdesc_s *cfgdesc = (struct usb_cfgdesc_s*)buf; +#ifdef CONFIG_USBDEV_DUALSPEED + boolean highspeed = (speed == USB_SPEED_HIGH); +#endif + uint16 bulkmxpacket; + uint16 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 + USBSER_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 two endpoint configurations. First, check for switches + * between high and full speed + */ + +#ifdef CONFIG_USBDEV_DUALSPEED + if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG) + { + hispeed = !hispeed; + } +#endif + +#ifdef CONFIG_USBDEV_DUALSPEED + if (hispeed) + { + bulkmxpacket = 512; + } + else +#endif + { + bulkmxpacket = 64; + } + + usbclass_mkepdesc(USBSER_EPINTIN_ADDR, USBSER_EPINTIN_ATTR, + USBSER_EPINTIN_MXPACKET, (struct usb_epdesc_s*)buf); + buf += USB_SIZEOF_EPDESC; + usbclass_mkepdesc(USBSER_EPOUTBULK_ADDR, USBSER_EPOUTBULK_ATTR, + bulkmxpacket, (struct usb_epdesc_s*)buf); + buf += USB_SIZEOF_EPDESC; + usbclass_mkepdesc(USBSER_EPINBULK_ADDR, USBSER_EPINBULK_ATTR, + bulkmxpacket, (struct usb_epdesc_s*)buf); + + /* 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) +{ + FAR struct usbdev_s *dev = priv->usbdev; + struct usbser_req_s *reqcontainer; + irqstate_t flags; + + /* Are we configured? */ + + if (priv->config != USBSER_CONFIGIDNONE) + { + priv->config = USBSER_CONFIGIDNONE; + + /* Free write requests that are not in use */ + + flags = irqsave(); + 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->nwralloc--; /* Number of write requests allocated */ + priv->nwrq--; /* Number of write requests queued */ + } + } + irqrestore(flags); + + /* Disable and free endpoints. This should force completion of all pending + * transfers. + */ + + if (priv->epintin) + { + EP_DISABLE(priv->epintin); + DEV_FREEEP(dev, priv->epintin); + priv->epintin = NULL; + } + + if (priv->epbulkin) + { + EP_DISABLE(priv->epbulkin); + DEV_FREEEP(dev, priv->epbulkin); + priv->epbulkin = NULL; + } + + if (priv->epbulkout) + { + EP_DISABLE(priv->epbulkout); + DEV_FREEEP(dev, priv->epbulkout); + priv->epbulkout = NULL; + } + } +} + +/**************************************************************************** + * 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, ubyte config) +{ + struct usbdev_s *dev = priv->usbdev; + struct usbdev_req_s *req; + struct usbser_req_s *reqcontainer; + struct usb_epdesc_s epdesc; + irqstate_t flags; + uint16 bulkmxpacket; + int i; + int ret = 0; + +#if CONFIG_DEBUG + if (priv == NULL) + { + return -ENODEV; + } +#endif + + if (config == priv->config) + { + /* Already configured */ + + return 0; + } + + /* Discard the previous configuration data */ + + usbclass_resetconfig(priv); + + /* Was this a request to simply discard the current configuration? */ + + if (config != USBSER_CONFIGIDNONE) + { + return 0; + } + + /* The only configuration that we accept is ourt BULK configuration */ + + if (config != USBSER_CONFIGID) + { + return -EINVAL; + } + + /* Configure the IN interrupt endpoint */ + + priv->epintin = DEV_ALLOCEP(dev, 0, TRUE, USB_EP_ATTR_XFER_INT); + if (!priv->epintin) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INALLOCEPFAIL), 0); + ret = -ENODEV; + goto errout; + } + + usbclass_mkepdesc(USBSER_EPINTIN_ADDR, USBSER_EPINTIN_ATTR, + USBSER_EPINTIN_MXPACKET, &epdesc); + + ret = EP_CONFIGURE(priv->epintin, &epdesc); + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INCONFIGEPFAIL), 0); + goto errout; + } + + priv->epintin->private = priv; + + /* Configure the IN bulk endpoint */ + + priv->epbulkin = DEV_ALLOCEP(dev, 0, TRUE, USB_EP_ATTR_XFER_BULK); + if (!priv->epbulkin) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INALLOCEPFAIL), 0); + ret = -ENODEV; + goto errout; + } + +#ifdef CONFIG_USBDEV_DUALSPEED + if (dev->speed == USB_SPEED_HIGH) + { + bulkmxpacket = 512; + } + else +#endif + { + bulkmxpacket = 64; + } + + usbclass_mkepdesc(USBSER_EPINBULK_ADDR, USBSER_EPINBULK_ATTR, + bulkmxpacket, &epdesc); + + ret = EP_CONFIGURE(priv->epbulkin, &epdesc); + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INCONFIGEPFAIL), 0); + goto errout; + } + + priv->epbulkin->private = priv; + + /* Configure the OUT bulk endpoint */ + + priv->epbulkout = DEV_ALLOCEP(dev, 0, FALSE, USB_EP_ATTR_XFER_BULK); + if (!priv->epbulkout) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_OUTALLOCEPFAIL), 0); + ret = -ENODEV; + goto errout; + } + + usbclass_mkepdesc(USBSER_EPOUTBULK_ADDR, USBSER_EPOUTBULK_ATTR, + bulkmxpacket, &epdesc); + + ret = EP_CONFIGURE(priv->epbulkout, &epdesc); + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_OUTCONFIGEPFAIL), 0); + goto errout; + } + + priv->epbulkout->private = priv; + + /* Allocate and queue read requests */ + + for (i = 0; i < CONFIG_USBSER_NRDREQS && ret == 0; i++) + { + req = usbclass_allocreq(priv->epbulkout, priv->epbulkout->maxpacket); + if (ret == 0) + { + req->callback = usbclass_rdcomplete; + ret = EP_SUBMIT(priv->epbulkout, req); + if (ret != OK) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16)-ret); + } + } + else + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), (uint16)-ret); + usbclass_resetconfig(priv); + return -ENOMEM; + } + } + + /* Allocate write request containers and put in a free list */ + + for (i = 0; i < CONFIG_USBSER_NWRREQS; i++) + { + reqcontainer = &priv->wrreqs[i]; + reqcontainer->req = usbclass_allocreq(priv->epbulkin, priv->epbulkin->maxpacket); + if (reqcontainer->req == NULL) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRALLOCREQ), (uint16)-ret); + usbclass_resetconfig(priv); + return -ENOMEM; + } + reqcontainer->req->private = reqcontainer; + reqcontainer->req->callback = usbclass_wrcomplete; + + flags = irqsave(); + sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist); + + priv->nwralloc++; /* Count of write requests allocated */ + priv->nwrq++; /* Count of write requests available */ + irqrestore(flags); + } + + priv->config = config; + return OK; + +errout: + usbclass_resetconfig(priv); + return ret; +} + +/**************************************************************************** + * Name: usbclass_setupcomplete + * + * Description: + * Handle completion of EP0 control operations + * + ****************************************************************************/ + +static void usbclass_setupcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + if (req->result || req->xfrd != req->len) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_REQRESULT), (uint16)-req->result); + } +} + +/**************************************************************************** + * Name: usbclass_rdcomplete + * + * Description: + * Handle completion of read request + * + ****************************************************************************/ + +static void usbclass_rdcomplete(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct usbser_dev_s *priv = (FAR struct usbser_dev_s*)ep->private; + int ret; + + if (priv != NULL) + { + switch (req->result) + { + case 0: /* Normal completion */ + usbclass_recvpacket(priv, req->buf, req->xfrd); + break; + + case -ESHUTDOWN: /* Disconnection */ + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0); + usbclass_freereq(ep, req); + return; + + default: + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDUNEXPECTED), (uint16)-req->result); + break; + }; + + /* Requeue the read request */ + + req->len = ep->maxpacket; + ret = EP_SUBMIT(ep, req); + if (ret != OK) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16)-req->result); + } + } +} + +/**************************************************************************** + * 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, struct usbdev_req_s *req) +{ + struct usbser_dev_s *priv = (FAR struct usbser_dev_s *)ep->private; + struct usbser_req_s *reqcontainer = req->private; + irqstate_t flags; + + if (priv != NULL) + { + switch (req->result) + { + case 0: /* Normal completion */ + break; + + case -ESHUTDOWN: /* Disconnection */ + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRSHUTDOWN), 0); + usbclass_freereq(ep, req); + priv->nwralloc--; /* Number of write requests allocated */ + return; + + default: + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRUNEXPECTED), (uint16)-req->result); + break; + } + + /* Put write request back into the free list */ + + if (reqcontainer == NULL) + { + flags = irqsave(); + sq_addlast((sq_entry_t*)reqcontainer, &priv->reqlist); + priv->nwrq++; + irqrestore(flags); + + /* And send another packet if: TX output is enabled */ + + usbclass_sndpacket(priv); + } + } +} + +/**************************************************************************** + * 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) +{ + struct usbser_dev_s *priv = ((struct usbser_driver_s*)driver)->dev; + + usbtrace(TRACE_CLASSBIND, 0); + + /* Preallocate control request */ + + priv->ctrlreq = usbclass_allocreq(dev->ep0, USBSER_MXDESCLEN); + if (priv->ctrlreq == NULL) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCCTRLREQ), 0); + usbclass_unbind(dev); + return -ENOMEM; + } + priv->ctrlreq->callback = usbclass_setupcomplete; + + /* Bind the structures */ + + priv->usbdev = dev; + dev->ep0->private = priv; + return OK; +} + +/**************************************************************************** + * Name: usbclass_unbind + * + * Description: + * Invoked when the driver is unbound from a USB device driver + * + ****************************************************************************/ + +static void usbclass_unbind(FAR struct usbdev_s *dev) +{ + struct usbser_dev_s *priv; + + usbtrace(TRACE_CLASSUNBIND, 0); + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + priv = (FAR struct usbser_dev_s *)dev->ep0->private; + +#ifdef CONFIG_DEBUG + if (!priv) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0); + return; + } +#endif + + if (priv != NULL) + { + if (priv->ctrlreq != NULL) + { + usbclass_freereq(dev->ep0, priv->ctrlreq); + } + + /* 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) +{ + struct usbser_dev_s *priv; + struct usbdev_req_s *ctrlreq; + uint16 value; + uint16 index; + uint16 len; + int ret = -EOPNOTSUPP; + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0 || !ctrl) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); + return -EINVAL; + } +#endif + usbtrace(TRACE_CLASSSETUP, ctrl->req); + priv = (FAR struct usbser_dev_s *)dev->ep0->private; + +#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); + + switch (ctrl->type & USB_REQ_TYPE_MASK) + { + 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, len); +#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(USBSER_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) + { + *(ubyte*)ctrlreq->buf = priv->config; + ret = 1; + } + } + break; + + case USB_REQ_SETINTERFACE: + { + if (ctrl->type == USB_REQ_RECIPIENT_INTERFACE) + { + if (priv->config == USBSER_CONFIGID && + index == USBSER_INTERFACEID && + value == USBSER_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 == USBSER_CONFIGIDNONE) + { + if (index != USBSER_INTERFACEID) + { + ret = -EDOM; + } + else + { + *(ubyte*) ctrlreq->buf = USBSER_ALTINTERFACEID; + ret = 1; + } + } + } + break; + + default: + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req); + break; + } + } + break; + + case PL2303_CONTROL_TYPE: + { + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_INTERFACE) + { + switch (ctrl->req) + { + case PL2303_SETLINEREQUEST: + { + memcpy(priv->linest, ctrlreq->buf, min(len, 7)); + ret = 0; + } + break; + + + case PL2303_GETLINEREQUEST: + { + memcpy(ctrlreq->buf, priv->linest, 7); + ret = 7; + } + break; + + case PL2303_SETCONTROLREQUEST: + case PL2303_BREAKREQUEST: + { + ret = 0; + } + break; + + default: + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDCTRLREQ), ctrl->type); + break; + } + } + } + break; + + case PL2303_RWREQUEST_TYPE: + { + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) + { + if (ctrl->req == PL2303_RWREQUEST) + { + if ((ctrl->type & USB_DIR_IN) != 0) + { + *(uint32*)ctrlreq->buf = 0xdeadbeef; + ret = 4; + } + else + { + ret = 0; + } + } + else + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDRWREQ), ctrl->type); + } + } + } + break; + + default: + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type); + break; + } + + /* Respond with data transfer before status phase? */ + + if (ret >= 0) + { + ctrlreq->len = min(len, ret); + ret = EP_SUBMIT(dev->ep0, ctrlreq); + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ), (uint16)-ret); + ctrlreq->result = OK; + usbclass_setupcomplete(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) +{ + struct usbser_dev_s *priv; + irqstate_t flags; + + usbtrace(TRACE_CLASSDISCONNECT, 0); + +#ifdef CONFIG_DEBUG + if (!dev || !dev->ep0) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + priv = (FAR struct usbser_dev_s *)dev->ep0->private; + +#ifdef CONFIG_DEBUG + if (!priv) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0); + return; + } +#endif + + 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); +} + +/**************************************************************************** + * 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) +{ + struct usbser_dev_s *priv = (FAR struct usbser_dev_s*)dev->priv; + +#if CONFIG_DEBUG + if (!priv) + { + return -EIO; + } + + if (priv->config == USBSER_CONFIGIDNONE) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_SETUPNOTCONNECTED), 0); + return -ENODEV; + } +#endif + + priv->open = TRUE; + 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) +{ + struct usbser_dev_s *priv = (FAR struct usbser_dev_s*)dev->priv; + irqstate_t flags; + +#if CONFIG_DEBUG + if (!priv) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0); + return; + } +#endif + + flags = irqsave(); + +#if CONFIG_DEBUG + if (!priv->open) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALREADYCLOSED), 0); + goto errout; + } +#endif + + /* Make sure that we are disconnected from the host */ + + usbclass_resetconfig(priv); + priv->open = FALSE; + +errout: + irqrestore(flags); +} + +/**************************************************************************** + * Name: usbser_attach + * + * Description: + * Does not apply to the USB serial class device + * + ****************************************************************************/ + +static int usbser_attach(FAR struct uart_dev_s *dev) +{ + 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) +{ +} + +/**************************************************************************** + * 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, boolean enable) +{ + struct usbser_dev_s *priv = (FAR struct usbser_dev_s*)dev->priv; + +#if CONFIG_DEBUG + if (!priv) + { + return; + } +#endif + + /* I think we can simulate this behavior by stalling and/or resuming the + * OUT endpoint. + */ + + if (enable) + { + EP_RESUME(priv->epbulkout); + } + else + { + EP_STALL(priv->epbulkout); + } +} + +/**************************************************************************** + * 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, boolean enable) +{ + struct usbser_dev_s *priv = (FAR struct usbser_dev_s*)dev->priv; + +#if CONFIG_DEBUG + if (!priv) + { + return; + } +#endif + + /* Save the TX enable/disable state */ + + priv->wravail = enable; + + /* If the new state is enabled and if there is data in the XMIT buffer, + * send the next packet now. + */ + + 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 boolean usbser_txempty(FAR struct uart_dev_s *dev) +{ + struct usbser_dev_s *priv = (FAR struct usbser_dev_s*)dev->priv; + +#if CONFIG_DEBUG + if (!priv) + { + 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 >= priv->nwralloc; +} + +/**************************************************************************** + * 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*)malloc(sizeof(struct usbser_alloc_s)); + if (!alloc) + { + 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_USBSER_RXBUFSIZE; + priv->serdev.recv.buffer = priv->rxbuffer; + priv->serdev.xmit.size = CONFIG_USBSER_RXBUFSIZE; + priv->serdev.xmit.buffer = priv->rxbuffer; + priv->serdev.ops = &g_uartops; + + /* 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(USBSER_TRACEERR_DEVREGISTER), (uint16)-ret); + goto errout_with_alloc; + } + + /* Register the USB serial console */ + +#ifdef CONFIG_USBSER_CONSOLE + g_usbserialport.isconsole = TRUE; + ret = uart_register("/dev/console", &pri->serdev); + if (ret < 0) + { + usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONSOLEREGISTER), (uint16)-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(USBSER_TRACEERR_UARTREGISTER), (uint16)-ret); + goto errout_with_class; + } + return OK; + +errout_with_class: + usbdev_unregister(&drvr->drvr); +errout_with_alloc: + free(alloc); + return ret; +} diff --git a/nuttx/include/nuttx/usbdev_trace.h b/nuttx/include/nuttx/usbdev_trace.h index 804ea2047..abb6bc720 100644 --- a/nuttx/include/nuttx/usbdev_trace.h +++ b/nuttx/include/nuttx/usbdev_trace.h @@ -136,29 +136,33 @@ /* Values of the class error ID used by the USB serial driver */ -#define USBSER_TRACEERR_ALLOCCTRLREQ 0x0001 -#define USBSER_TRACEERR_ALREADYCLOSED 0x0002 -#define USBSER_TRACEERR_CONSOLEREGISTER 0x0003 -#define USBSER_TRACEERR_DEVREGISTER 0x0004 -#define USBSER_TRACEERR_EPRESPQ 0x0005 -#define USBSER_TRACEERR_GETUNKNOWNDESC 0x0006 -#define USBSER_TRACEERR_INALLOCEPFAIL 0x0007 -#define USBSER_TRACEERR_INCONFIGEPFAIL 0x0008 -#define USBSER_TRACEERR_INVALIDARG 0x0009 -#define USBSER_TRACEERR_EP0NOTBOUND 0x000a -#define USBSER_TRACEERR_OUTALLOCEPFAIL 0x000b -#define USBSER_TRACEERR_OUTCONFIGEPFAIL 0x000c -#define USBSER_TRACEERR_RDALLOCREQ 0x000d -#define USBSER_TRACEERR_RDSHUTDOWN 0x000e -#define USBSER_TRACEERR_RDSUBMIT 0x000f -#define USBSER_TRACEERR_RDUNEXPECTED 0x0010 -#define USBSER_TRACEERR_REQRESULT 0x0011 -#define USBSER_TRACEERR_SETUPNOTCONNECTED 0x0012 -#define USBSER_TRACEERR_SUBMITFAIL 0x0013 -#define USBSER_TRACEERR_UARTREGISTER 0x0014 -#define USBSER_TRACEERR_WRALLOCREQ 0x0015 -#define USBSER_TRACEERR_WRSHUTDOWN 0x0016 -#define USBSER_TRACEERR_WRUNEXPECTED 0x0017 +#define USBSER_TRACEERR_ALLOCCTRLREQ 0x0001 +#define USBSER_TRACEERR_ALREADYCLOSED 0x0002 +#define USBSER_TRACEERR_CONSOLEREGISTER 0x0003 +#define USBSER_TRACEERR_DEVREGISTER 0x0004 +#define USBSER_TRACEERR_EPRESPQ 0x0005 +#define USBSER_TRACEERR_GETUNKNOWNDESC 0x0006 +#define USBSER_TRACEERR_INALLOCEPFAIL 0x0007 +#define USBSER_TRACEERR_INCONFIGEPFAIL 0x0008 +#define USBSER_TRACEERR_INVALIDARG 0x0009 +#define USBSER_TRACEERR_EP0NOTBOUND 0x000a +#define USBSER_TRACEERR_OUTALLOCEPFAIL 0x000b +#define USBSER_TRACEERR_OUTCONFIGEPFAIL 0x000c +#define USBSER_TRACEERR_RDALLOCREQ 0x000d +#define USBSER_TRACEERR_RDSHUTDOWN 0x000e +#define USBSER_TRACEERR_RDSUBMIT 0x000f +#define USBSER_TRACEERR_RDUNEXPECTED 0x0010 +#define USBSER_TRACEERR_REQRESULT 0x0011 +#define USBSER_TRACEERR_SETUPNOTCONNECTED 0x0012 +#define USBSER_TRACEERR_SUBMITFAIL 0x0013 +#define USBSER_TRACEERR_UARTREGISTER 0x0014 +#define USBSER_TRACEERR_UNSUPPORTEDCTRLREQ 0x0015 +#define USBSER_TRACEERR_UNSUPPORTEDRWREQ 0x0016 +#define USBSER_TRACEERR_UNSUPPORTEDSTDREQ 0x0017 +#define USBSER_TRACEERR_UNSUPPORTEDTYPE 0x0018 +#define USBSER_TRACEERR_WRALLOCREQ 0x0019 +#define USBSER_TRACEERR_WRSHUTDOWN 0x001a +#define USBSER_TRACEERR_WRUNEXPECTED 0x001b /**************************************************************************** * Public Types |