From 0798efef0c881858173d0001682526195e5a6b85 Mon Sep 17 00:00:00 2001 From: patacongo Date: Sun, 13 Jun 2010 15:42:11 +0000 Subject: Add USB device driver git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2743 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/arch/arm/src/lpc17xx/Make.defs | 6 +- nuttx/arch/arm/src/lpc17xx/lpc17_usb.h | 177 +- nuttx/arch/arm/src/lpc17xx/lpc17_usbdev.c | 3319 +++++++++++++++++++++++++++++ nuttx/configs/nucleus2g/src/up_leds.c | 18 + 4 files changed, 3505 insertions(+), 15 deletions(-) create mode 100755 nuttx/arch/arm/src/lpc17xx/lpc17_usbdev.c diff --git a/nuttx/arch/arm/src/lpc17xx/Make.defs b/nuttx/arch/arm/src/lpc17xx/Make.defs index 5b5c04f61..21d7b4703 100755 --- a/nuttx/arch/arm/src/lpc17xx/Make.defs +++ b/nuttx/arch/arm/src/lpc17xx/Make.defs @@ -65,6 +65,10 @@ ifeq ($(CONFIG_DEBUG),y) CHIP_CSRCS += lpc17_gpiodbg.c endif +ifeq ($(CONFIG_USBDEV),y) +CHIP_CSRCS += lpc17_usbdev.c +endif + ifeq ($(CONFIG_LPC17_DMA),y) -CHIP_CSRCS += lpc17_dmac.c +CHIP_CSRCS += lpc17_gpdma.c endif diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_usb.h b/nuttx/arch/arm/src/lpc17xx/lpc17_usb.h index f50acbe00..f9d9752e5 100755 --- a/nuttx/arch/arm/src/lpc17xx/lpc17_usb.h +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_usb.h @@ -359,7 +359,6 @@ #define USBDEV_CMDDATA_SHIFT (0) /* Bits 0-7: Command read data */ #define USBDEV_CMDDATA_MASK (0xff << USBDEV_CMDDATA_SHIFT) /* Bits 8-31: Reserved */ - /* USB transfer registers: * * USB Receive Data (Bits 0-31: Received data) @@ -369,7 +368,7 @@ #define USBDEV_RXPLEN_SHIFT (0) /* Bits 0-9: Bytes remaining to be read */ #define USBDEV_RXPLEN_MASK (0x3ff << USBDEV_RXPLEN_SHIFT) -#define USBDEV_RXPLEN_DV (1 << 10) /* Bit 10: DV Data valid*/ +#define USBDEV_RXPLEN_DV (1 << 10) /* Bit 10: DV Data valid */ #define USBDEV_RXPLEN_PKTRDY (1 << 11) /* Bit 11: Packet ready for reading */ /* Bits 12-31: Reserved */ /* USB Transmit Data (Bits 0-31: Transmit data) */ @@ -383,8 +382,8 @@ #define USBDEV_CTRL_RDEN (1 << 0) /* Bit 0: Read mode control */ #define USBDEV_CTRL_WREN (1 << 1) /* Bit 1: Write mode control */ -#define USBDEV_CTRL_LOGENDPOINT_SHIFT (2) /* Bits 2-5: Logical Endpoint number */ -#define USBDEV_CTRL_LOGENDPOINT_MASK (15 << USBDEV_CTRL_LOGENDPOINT_SHIFT) +#define USBDEV_CTRL_LOGEP_SHIFT (2) /* Bits 2-5: Logical Endpoint number */ +#define USBDEV_CTRL_LOGEP_MASK (15 << USBDEV_CTRL_LOGEP_SHIFT) /* Bits 6-31: Reserved */ /* Endpoint interrupt registers: * @@ -435,15 +434,15 @@ #define USBDEV_PHYEP(n) (1 << (n)) #define USBDEV_PHYEP0 (1 << 0) -#define USBDEV_PHYEP0 (1 << 1) -#define USBDEV_PHYEP0 (1 << 2) -#define USBDEV_PHYEP0 (1 << 3) -#define USBDEV_PHYEP0 (1 << 4) -#define USBDEV_PHYEP0 (1 << 5) -#define USBDEV_PHYEP0 (1 << 6) -#define USBDEV_PHYEP0 (1 << 7) -#define USBDEV_PHYEP0 (1 << 8) -#define USBDEV_PHYEP0 (1 << 9) +#define USBDEV_PHYEP1 (1 << 1) +#define USBDEV_PHYEP2 (1 << 2) +#define USBDEV_PHYEP3 (1 << 3) +#define USBDEV_PHYEP4 (1 << 4) +#define USBDEV_PHYEP5 (1 << 5) +#define USBDEV_PHYEP6 (1 << 6) +#define USBDEV_PHYEP7 (1 << 7) +#define USBDEV_PHYEP8 (1 << 8) +#define USBDEV_PHYEP9 (1 << 9) #define USBDEV_PHYEP10 (1 << 10) #define USBDEV_PHYEP11 (1 << 11) #define USBDEV_PHYEP12 (1 << 12) @@ -472,7 +471,6 @@ #define USBDEV_EPIND_SHIFT (0) /* Bits 0-4: Physical endpoint number (0-31) */ #define USBDEV_EPIND_MASK (31 << USBDEV_EPIND_SHIFT) /* Bits 5-31: Reserved */ - /* USB MaxPacketSize */ #define USBDEV_MAXPSIZE_SHIFT (0) /* Bits 0-9: Maximum packet size value */ @@ -579,6 +577,157 @@ #define USBDEV_CLK_AHBCLK (1 << 4) /* Bit 4: AHB clock */ /* Bits 5-31: Reserved */ +/* Endpoints *************************************************************************/ + +#define LPC17_EP0_OUT 0 +#define LPC17_EP0_IN 1 +#define LPC17_CTRLEP_OUT LPC17_EP0_OUT +#define LPC17_CTRLEP_IN LPC17_EP0_IN +#define LPC17_EP1_OUT 2 +#define LPC17_EP1_IN 3 +#define LPC17_EP2_OUT 4 +#define LPC17_EP2_IN 5 +#define LPC17_EP3_OUT 6 +#define LPC17_EP3_IN 7 +#define LPC17_EP4_OUT 8 +#define LPC17_EP4_IN 9 +#define LPC17_EP5_OUT 10 +#define LPC17_EP5_IN 11 +#define LPC17_EP6_OUT 12 +#define LPC17_EP6_IN 13 +#define LPC17_EP7_OUT 14 +#define LPC17_EP7_IN 15 +#define LPC17_EP8_OUT 16 +#define LPC17_EP8_IN 17 +#define LPC17_EP9_OUT 18 +#define LPC17_EP9_IN 19 +#define LPC17_EP10_OUT 20 +#define LPC17_EP10_IN 21 +#define LPC17_EP11_OUT 22 +#define LPC17_EP11_IN 23 +#define LPC17_EP12_OUT 24 +#define LPC17_EP12_IN 25 +#define LPC17_EP13_OUT 26 +#define LPC17_EP13_IN 27 +#define LPC17_EP14_OUT 28 +#define LPC17_EP14_IN 29 +#define LPC17_EP15_OUT 30 +#define LPC17_EP15_IN 31 +#define LPC17_NUMEPS 32 + +/* Commands *************************************************************************/ + +/* USB Command Code Register -- Command phase values */ + +#define CMD_USBDEV_CMDWR (0x00000500) +#define CMD_USBDEV_DATAWR (0x00000100) +#define CMD_USBDEV_DATARD (0x00000200) + +/* Device Commands */ + +#define CMD_USBDEV_SETADDRESS (0x00d0) +#define CMD_USBDEV_CONFIG (0x00d8) +#define CMD_USBDEV_SETMODE (0x00f3) +#define CMD_USBDEV_READFRAMENO (0x00f5) +#define CMD_USBDEV_READTESTREG (0x00fd) +#define CMD_USBDEV_SETSTATUS (0x01fe) +#define CMD_USBDEV_GETSTATUS (0x00fe) +#define CMD_USBDEV_GETERRORCODE (0x00ff) +#define CMD_USBDEV_READERRORSTATUS (0x00fb) + +/* Endpoint Commands */ + +#define CMD_USBDEV_EPSELECT (0x0000) +#define CMD_USBDEV_EPSELECTCLEAR (0x0040) +#define CMD_USBDEV_EPSETSTATUS (0x0140) +#define CMD_USBDEV_EPCLRBUFFER (0x00f2) +#define CMD_USBDEV_EPVALIDATEBUFFER (0x00fa) + +/* Command/response bit definitions ********************************************/ +/* SETADDRESS (0xd0) command definitions */ + +#define CMD_USBDEV_SETADDRESS_MASK (0x7f) /* Bits 0-6: Device address */ +#define CMD_USBDEV_SETADDRESS_DEVEN (1 << 7) /* Bit 7: Device enable */ + +/* SETSTATUS (0xfe) and GETSTATUS (0xfe) response: */ + +#define CMD_STATUS_CONNECT (1 << 0) /* Bit 0: Connected */ +#define CMD_STATUS_CONNCHG (1 << 1) /* Bit 1: Connect change */ +#define CMD_STATUS_SUSPEND (1 << 2) /* Bit 2: Suspend */ +#define CMD_STATUS_SUSPCHG (1 << 3) /* Bit 3: Suspend change */ +#define CMD_STATUS_RESET (1 << 4) /* Bit 4: Bus reset bit */ + +/* EPSELECT (0x00) endpoint status response */ + +#define CMD_EPSELECT_FE (1 << 0) /* Bit 0: IN empty or OUT full */ +#define CMD_EPSELECT_ST (1 << 1) /* Bit 1: Endpoint is stalled */ +#define CMD_EPSELECT_STP (1 << 2) /* Bit 2: Last packet was setup */ +#define CMD_EPSELECT_PO (1 << 3) /* Bit 3: Previous packet was overwritten */ +#define CMD_EPSELECT_EPN (1 << 4) /* Bit 4: NAK sent */ +#define CMD_EPSELECT_B1FULL (1 << 5) /* Bit 5: Buffer 1 full */ +#define CMD_EPSELECT_B2FULL (1 << 6) /* Bit 6: Buffer 2 full */ + /* Bit 7: Reserved */ +/* EPSETSTATUS (0x40) command */ + +#define CMD_SETSTAUS_ST (1 << 0) /* Bit 0: Stalled endpoint bit */ + /* Bits 1-4: Reserved */ +#define CMD_SETSTAUS_DA (1 << 5) /* Bit 5: Disabled endpoint bit */ +#define CMD_SETSTAUS_RFMO (1 << 6) /* Bit 6: Rate feedback mode */ +#define CMD_SETSTAUS_CNDST (1 << 7) /* Bit 7: Conditional stall bit */ + +/* EPCLRBUFFER (0xf2) response */ + +#define CMD_USBDEV_CLRBUFFER_PO (0x00000001) + +/* DMA ******************************************************************************/ +/* The DMA descriptor */ + +#define USB_DMADESC_NEXTDDPTR 0 /* Offset 0: Next USB descriptor in RAM */ +#define USB_DMADESC_CONFIG 1 /* Offset 1: DMA configuration info. */ +#define USB_DMADESC_STARTADDR 2 /* Offset 2: DMA start address */ +#define USB_DMADESC_STATUS 3 /* Offset 3: DMA status info (read only) */ +#define USB_DMADESC_ISOCSIZEADDR 4 /* Offset 4: Isoc. packet size address */ + +/* Bit settings for CONFIG (offset 1 )*/ + +#define USB_DMADESC_MODE_SHIFT (0) /* Bits 0-1: DMA mode */ +#define USB_DMADESC_MODE_MASK (3 << USB_DMADESC_MODE_SHIFT) +# define USB_DMADESC_MODENORMAL (0 << USB_DMADESC_MODE_SHIFT) /* Mode normal */ +# define USB_DMADESC_MODEATLE (1 << USB_DMADESC_MODE_SHIFT) /* ATLE normal */ +#define USB_DMADESC_NEXTDDVALID (1 << 2) /* Bit 2: Next descriptor valid */ + /* Bit 3: Reserved */ +#define USB_DMADESC_ISCOEP (1 << 4) /* Bit 4: ISOC endpoint */ +#define USB_DMADESC_PKTSIZE_SHIFT (5) /* Bits 5-15: Max packet size */ +#define USB_DMADESC_PKTSIZE_MASK (0x7ff << USB_DMADESC_PKTSIZE_SHIFT) +#define USB_DMADESC_BUFLEN_SHIFT (16) /* Bits 16-31: DMA buffer length */ +#define USB_DMADESC_BUFLEN_MASK (0xffff << USB_DMADESC_BUFLEN_SHIFT + +/* Bit settings for STATUS (offset 3). All must be initialized to zero. */ + +#define USB_DMADESC_STATUS_SHIFT (1) /* Bits 1-4: DMA status */ +#define USB_DMADESC_STATUS_MASK (15 << USB_DMADESC_STATUS_SHIFT) +# define USB_DMADESC_NOTSERVICED (0 << USB_DMADESC_STATUS_SHIFT) +# define USB_DMADESC_BEINGSERVICED (1 << USB_DMADESC_STATUS_SHIFT) +# define USB_DMADESC_NORMALCOMPLETION (2 << USB_DMADESC_STATUS_SHIFT) +# define USB_DMADESC_DATAUNDERRUN (3 << USB_DMADESC_STATUS_SHIFT) +# define USB_DMADESC_DATAOVERRUN (8 << USB_DMADESC_STATUS_SHIFT) +# define USB_DMADESC_SYSTEMERROR (9 << USB_DMADESC_STATUS_SHIFT) +#define USB_DMADESC_PKTVALID (1 << 5) /* Bit 5: Packet valid */ +#define USB_DMADESC_LSBEXTRACTED (1 << 6) /* Bit 6: LS byte extracted */ +#define USB_DMADESC_MSBEXTRACTED (1 << 7) /* Bit 7: MS byte extracted */ +#define USB_DMADESC_MSGLENPOS_SHIFT (8) /* Bits 8-13: Message length position */ +#define USB_DMADESC_MSGLENPOS_MASK (0x3f << USB_DMADESC_MSGLENPOS_SHIFT) +#define USB_DMADESC_DMACOUNT_SHIFT (16) /* Bits 16-31: DMA count */ +#define USB_DMADESC_DMACOUNT_MASK (0xffff << USB_DMADESC_DMACOUNT_SHIFT) + +/* DMA packet size format */ + +#define USB_DMAPKTSIZE_PKTLEN_SHIFT (0) /* Bits 0-15: Packet length */ +#define USB_DMAPKTSIZE_PKTLEN_MASK (0xffff << USB_DMAPKTSIZE_PKTLEN_SHIFT) +#define USB_DMAPKTSIZE_PKTVALID (1 << 16) /* Bit 16: Packet valid */ +#define USB_DMAPKTSIZE_FRAMENO_SHIFT (17) /* Bit 17-31: Frame number */ +#define USB_DMAPKTSIZE_FRAMENO_MASK (0x7fff << USB_DMAPKTSIZE_FRAMENO_SHIFT) + /************************************************************************************ * Public Types ************************************************************************************/ diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_usbdev.c b/nuttx/arch/arm/src/lpc17xx/lpc17_usbdev.c new file mode 100755 index 000000000..cf77f07a4 --- /dev/null +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_usbdev.c @@ -0,0 +1,3319 @@ +/******************************************************************************* + * arch/arm/src/lpc17xx/lpc17_usbdev.c + * + * Copyright (C) 2010 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +/******************************************************************************* + * Included Files + *******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "chip.h" +#include "up_arch.h" +#include "up_internal.h" + +#include "lpc17_usb.h" +#include "lpc17_syscon.h" + +/******************************************************************************* + * Definitions + *******************************************************************************/ + +/* Configuration ***************************************************************/ + +#ifndef CONFIG_USBDEV_EP0_MAXSIZE +# define CONFIG_USBDEV_EP0_MAXSIZE 64 +#endif + +#ifndef CONFIG_USBDEV_MAXPOWER +# define CONFIG_USBDEV_MAXPOWER 100 /* mA */ +#endif + +#define USB_SLOW_INT USBDEV_INT_EPSLOW +#define USB_DEVSTATUS_INT USBDEV_INT_DEVSTAT + +#ifdef CONFIG_LPC17_USBDEV_EPFAST_INTERRUPT +# define USB_FAST_INT USBDEV_INT_EPFAST +#else +# define USB_FAST_INT 0 +#endif + +/* Extremely detailed register debug that you would normally never want + * enabled. + */ + +#undef CONFIG_LPC17_USBDEV_REGDEBUG + +/* Enable reading SOF from interrupt handler vs. simply reading on demand. Probably + * a bad idea... Unless there is some issue with sampling the SOF from hardware + * asynchronously. + */ + +#ifdef CONFIG_LPC17_USBDEV_FRAME_INTERRUPT +# define USB_FRAME_INT USBDEV_INT_FRAME +#else +# define USB_FRAME_INT 0 +#endif + +#ifdef CONFIG_DEBUG +# define USB_ERROR_INT USBDEV_INT_EPRINT +#else +# define USB_ERROR_INT 0 +#endif + +/* Number of DMA descriptors */ + +#ifdef CONFIG_LPC17_USBDEV_DMA +# error DMA SUPPORT NOT YET FULLY IMPLEMENTED +# ifndef CONFIG_LPC17_USBDEV_NDMADESCRIPTORS +# define CONFIG_LPC17_USBDEV_NDMADESCRIPTORS 8 +# elif CONFIG_LPC17_USBDEV_NDMADESCRIPTORS > 30 +# define CONFIG_LPC17_USBDEV_NDMADESCRIPTORS 30 +# endif +#endif + +/* Debug ***********************************************************************/ + +/* Trace error codes */ + +#define LPC17_TRACEERR_ALLOCFAIL 0x0001 +#define LPC17_TRACEERR_BADCLEARFEATURE 0x0002 +#define LPC17_TRACEERR_BADDEVGETSTATUS 0x0003 +#define LPC17_TRACEERR_BADEPNO 0x0004 +#define LPC17_TRACEERR_BADEPGETSTATUS 0x0005 +#define LPC17_TRACEERR_BADEPTYPE 0x0006 +#define LPC17_TRACEERR_BADGETCONFIG 0x0007 +#define LPC17_TRACEERR_BADGETSETDESC 0x0008 +#define LPC17_TRACEERR_BADGETSTATUS 0x0009 +#define LPC17_TRACEERR_BADSETADDRESS 0x000a +#define LPC17_TRACEERR_BADSETCONFIG 0x000b +#define LPC17_TRACEERR_BADSETFEATURE 0x000c +#define LPC17_TRACEERR_BINDFAILED 0x000d +#define LPC17_TRACEERR_DISPATCHSTALL 0x000e +#define LPC17_TRACEERR_DMABUSY 0x000f +#define LPC17_TRACEERR_DRIVER 0x0010 +#define LPC17_TRACEERR_DRIVERREGISTERED 0x0011 +#define LPC17_TRACEERR_EP0INSTALLED 0x0012 +#define LPC17_TRACEERR_EP0OUTSTALLED 0x0013 +#define LPC17_TRACEERR_EP0SETUPSTALLED 0x0014 +#define LPC17_TRACEERR_EPINNULLPACKET 0x0015 +#define LPC17_TRACEERR_EPOUTNULLPACKET 0x0016 +#define LPC17_TRACEERR_EPREAD 0x0017 +#define LPC17_TRACEERR_INVALIDCMD 0x0018 +#define LPC17_TRACEERR_INVALIDCTRLREQ 0x0019 +#define LPC17_TRACEERR_INVALIDPARMS 0x001a +#define LPC17_TRACEERR_IRQREGISTRATION 0x001b +#define LPC17_TRACEERR_NODMADESC 0x001c +#define LPC17_TRACEERR_NOEP 0x001d +#define LPC17_TRACEERR_NOTCONFIGURED 0x001e +#define LPC17_TRACEERR_REQABORTED 0x001f + +/* Trace interrupt codes */ + +#define LPC17_TRACEINTID_USB 0x0001 +#define LPC17_TRACEINTID_CLEARFEATURE 0x0002 +#define LPC17_TRACEINTID_CONNECTCHG 0x0003 +#define LPC17_TRACEINTID_CONNECTED 0x0004 +#define LPC17_TRACEINTID_DEVGETSTATUS 0x0005 +#define LPC17_TRACEINTID_DEVRESET 0x0006 +#define LPC17_TRACEINTID_DEVSTAT 0x0007 +#define LPC17_TRACEINTID_DISCONNECTED 0x0008 +#define LPC17_TRACEINTID_DISPATCH 0x0009 +#define LPC17_TRACEINTID_EP0IN 0x000a +#define LPC17_TRACEINTID_EP0OUT 0x000b +#define LPC17_TRACEINTID_EP0SETUP 0x000c +#define LPC17_TRACEINTID_EPDMA 0x000d +#define LPC17_TRACEINTID_EPFAST 0x000e +#define LPC17_TRACEINTID_EPGETSTATUS 0x000f +#define LPC17_TRACEINTID_EPIN 0x0010 +#define LPC17_TRACEINTID_EPINQEMPTY 0x0011 +#define LPC17_TRACEINTID_EP0INSETADDRESS 0x0012 +#define LPC17_TRACEINTID_EPOUT 0x0013 +#define LPC17_TRACEINTID_EPOUTQEMPTY 0x0014 +#define LPC17_TRACEINTID_EP0SETUPSETADDRESS 0x0015 +#define LPC17_TRACEINTID_EPRINT 0x0016 +#define LPC17_TRACEINTID_EPSLOW 0x0017 +#define LPC17_TRACEINTID_FRAME 0x0018 +#define LPC17_TRACEINTID_GETCONFIG 0x0019 +#define LPC17_TRACEINTID_GETSETDESC 0x001a +#define LPC17_TRACEINTID_GETSETIF 0x001b +#define LPC17_TRACEINTID_GETSTATUS 0x001c +#define LPC17_TRACEINTID_IFGETSTATUS 0x001d +#define LPC17_TRACEINTID_SETCONFIG 0x001e +#define LPC17_TRACEINTID_SETFEATURE 0x001f +#define LPC17_TRACEINTID_SUSPENDCHG 0x0020 +#define LPC17_TRACEINTID_SYNCHFRAME 0x0021 + +/* Hardware interface **********************************************************/ + +/* Macros for testing the device status response */ + +#define DEVSTATUS_CONNECT(s) (((s)&CMD_STATUS_CONNECT)!=0) +#define DEVSTATUS_CONNCHG(s) (((s)&CMD_STATUS_CONNCHG)!=0) +#define DEVSTATUS_SUSPEND(s) (((s)&CMD_STATUS_SUSPEND)!=0) +#define DEVSTATUS_SUSPCHG(s) (((s)&CMD_STATUS_SUSPCHG)!=0) +#define DEVSTATUS_RESET(s) (((s)&CMD_STATUS_RESET)!=0) + +/* If this bit is set in the lpc17_epread response, it means that the + * recevied packet was overwritten by a later setup packet (ep0 only). + */ + +#define LPC17_READOVERRUN_BIT (0x80000000) +#define LPC17_READOVERRUN(s) (((s) & LPC17_READOVERRUN_BIT) != 0) + +/* USB RAM ******************************************************************** + * + * UBS_UDCA is is list of 32 pointers to DMA desciptors located at the + * beginning of USB RAM. Each pointer points to a DMA descriptor with + * assocated DMA buffer. + */ + +#define USB_UDCA (uint32_t*)LPC17_USBDEV_RAMBASE) +#define USB_USCASIZE (LPC17_NPHYSENDPOINTS*sizeof(uint32_t)) + +/* Each descriptor must be aligned to a 128 address boundary */ + +#define USB_DDALIGNDOWN(a) ((a)&~0x7f) +#define USB_DDALIGNUP(a) USB_DDALIGNDOWN((a)+0x7f) + +#define USB_DDSIZE USB_DDALIGNDOWN((LPC17_USBDEV_RAMSIZE-USB_USCASIZE)/CONFIG_LPC17_USBDEV_NDMADESCRIPTORS) +#define USB_DDESC ((struct lpc17_dmadesc_s*)(LPC17_USBDEV_RAMBASE+USB_USCASIZE)) + +#ifdef CONFIG_USBDEV_ISOCHRONOUS +# define USB_DDESCSIZE (5*sizeof(uint32_t)) +#else +# define USB_DDESCSIZE (4*sizeof(uint32_t)) +#endif + +/* Endpoints ******************************************************************/ + +/* Number of endpoints */ + +#define LPC17_NLOGENDPOINTS (16) /* ep0-15 */ +#define LPC17_NPHYSENDPOINTS (32) /* x2 for IN and OUT */ + +/* Odd physical endpoint numbers are IN; even are out */ + +#define LPC17_EPPHYIN(epphy) (((epphy)&1)!=0) +#define LPC17_EPPHYOUT(epphy) (((epphy)&1)==0) + +#define LPC17_EPPHYIN2LOG(epphy) (((uint8_t)(epphy)>>1)|USB_DIR_IN) +#define LPC17_EPPHYOUT2LOG(epphy) (((uint8_t)(epphy)>>1)|USB_DIR_OUT) + +/* Each endpoint has somewhat different characteristics */ + +#define LPC17_EPALLSET (0xffffffff) /* All endpoints */ +#define LPC17_EPOUTSET (0x55555555) /* Even phy endpoint numbers are OUT EPs */ +#define LPC17_EPINSET (0xaaaaaaaa) /* Odd endpoint numbers are IN EPs */ +#define LPC17_EPCTRLSET (0x00000003) /* EP0 IN/OUT are control endpoints */ +#define LPC17_EPINTRSET (0x0c30c30c) /* Interrupt endpoints */ +#define LPC17_EPBULKSET (0xf0c30c30) /* Bulk endpoints */ +#define LPC17_EPISOCSET (0x030c30c0) /* Isochronous endpoints */ +#define LPC17_EPDBLBUFFER (0xf3cf3cf0) /* Double buffered endpoints */ + +#define LPC17_EP0MAXPACKET (64) /* EP0 max packet size (1-64) */ +#define LPC17_BULKMAXPACKET (64) /* Bulk endpoint max packet (8/16/32/64) */ +#define LPC17_INTRMAXPACKET (64) /* Interrupt endpoint max packet (1 to 64) */ +#define LPC17_ISOCMAXPACKET (512) /* Acutally 1..1023 */ + +/* EP0 status. EP0 transfers occur in a number of different contexts. A + * simple state machine is required to handle the various transfer complete + * interrupt responses. The following values are the various states: + */ + /*** INTERRUPT CAUSE ***/ +#define LPC17_EP0REQUEST (0) /* Normal request handling */ +#define LPC17_EP0STATUSIN (1) /* Status sent */ +#define LPC17_EP0STATUSOUT (2) /* Status received */ +#define LPC17_EP0SHORTWRITE (3) /* Short data sent with no request */ +#define LPC17_EP0SHORTWRSENT (4) /* Short data write complete */ +#define LPC17_EP0SETADDRESS (5) /* Set address received */ +#define LPC17_EP0WRITEREQUEST (6) /* EP0 write request sent */ + +/* Request queue operations ****************************************************/ + +#define lpc17_rqempty(ep) ((ep)->head == NULL) +#define lpc17_rqpeek(ep) ((ep)->head) + +/******************************************************************************* + * Private Types + *******************************************************************************/ + +/* A container for a request so that the request make be retained in a list */ + +struct lpc17_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct lpc17_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct lpc17_ep_s +{ + /* Common endpoint fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_ep_s + * to struct lpc17_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* LPC17xx-specific fields */ + + struct lpc17_usbdev_s *dev; /* Reference to private driver data */ + struct lpc17_req_s *head; /* Request list for this endpoint */ + struct lpc17_req_s *tail; + uint8_t epphy; /* Physical EP address */ + uint8_t stalled:1; /* 1: Endpoint is stalled */ + uint8_t halted:1; /* 1: Endpoint feature halted */ + uint8_t txbusy:1; /* 1: TX endpoint FIFO full */ + uint8_t txnullpkt:1; /* Null packet needed at end of transfer */ +}; + +/* This represents a DMA descriptor */ + +#ifdef CONFIG_LPC17_USBDEV_DMA +struct lpc17_dmadesc_s +{ + uint32_t nextdesc; /* Address of the next DMA descripto in RAM */ + uint32_t config; /* Misc. bit encoded configuration information */ + uint32_t start; /* DMA start address */ + uint32_t status; /* Misc. bit encoded status inforamation */ +#ifdef CONFIG_USBDEV_ISOCHRONOUS + uint32_t size; /* Isochronous packet size address */ +#endif + uint8_t buffer[USB_DDSIZE-USB_DDESCSIZE]; +}; +#endif + +/* This structure retains the state of the USB device controller */ + +struct lpc17_usbdev_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_s + * to structlpc17_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* LPC17xx-specific fields */ + + uint8_t devstatus; /* Last response to device status command */ + uint8_t ep0state; /* State of certain EP0 operations */ + uint8_t paddr; /* Address assigned by SETADDRESS */ + uint8_t stalled:1; /* 1: Protocol stalled */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint8_t paddrset:1; /* 1: Peripheral addr has been set */ + uint8_t attached:1; /* 1: Host attached */ + uint8_t rxpending:1; /* 1: RX pending */ + uint32_t softprio; /* Bitset of high priority interrupts */ + uint32_t epavail; /* Bitset of available endpoints */ +#ifdef CONFIG_LPC17_USBDEV_FRAME_INTERRUPT + uint32_t sof; /* Last start-of-frame */ +#endif + + /* Allocated DMA descriptor */ + +#ifdef CONFIG_LPC17_USBDEV_DMA + struct lpc17_dmadesc_s *dmadesc; +#endif + + /* The endpoint list */ + + struct lpc17_ep_s eplist[LPC17_NPHYSENDPOINTS]; +}; + +/******************************************************************************* + * Private Function Prototypes + *******************************************************************************/ + +/* Register operations ********************************************************/ + +#if defined(CONFIG_LPC17_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) +static uint32_t lpc17_getreg(uint32_t addr); +static void lpc17_putreg(uint32_t val, uint32_t addr); +#else +# define lpc17_getreg(addr) getreg32(addr) +# define lpc17_putreg(val,addr) putreg32(val,addr) +#endif + +/* Command operations **********************************************************/ + +static uint32_t lpc17_usbcmd(uint16_t cmd, uint8_t data); + +/* Request queue operations ****************************************************/ + +static FAR struct lpc17_req_s *lpc17_rqdequeue(FAR struct lpc17_ep_s *privep); +static void lpc17_rqenqueue(FAR struct lpc17_ep_s *privep, + FAR struct lpc17_req_s *req); + +/* Low level data transfers and request operations *****************************/ + +static void lpc17_epwrite(uint8_t epphy, const uint8_t *data, uint32_t nbytes); +static int lpc17_epread(uint8_t epphy, uint8_t *data, uint32_t nbytes); +static inline void lpc17_abortrequest(struct lpc17_ep_s *privep, + struct lpc17_req_s *privreq, int16_t result); +static void lpc17_reqcomplete(struct lpc17_ep_s *privep, int16_t result); +static int lpc17_wrrequest(struct lpc17_ep_s *privep); +static int lpc17_rdrequest(struct lpc17_ep_s *privep); +static void lpc17_cancelrequests(struct lpc17_ep_s *privep); + +/* Interrupt handling **********************************************************/ + +static struct lpc17_ep_s *lpc17_epfindbyaddr(struct lpc17_usbdev_s *priv, + uint16_t eplog); +static void lpc17_eprealize(struct lpc17_ep_s *privep, bool prio, + uint32_t packetsize); +static uint8_t lpc17_epclrinterrupt(uint8_t epphy); +static inline void lpc17_ep0configure(struct lpc17_usbdev_s *priv); +#ifdef CONFIG_LPC17_USBDEV_DMA +static inline void lpc17_dmareset(uint32_t enable); +#endif +static void lpc17_usbreset(struct lpc17_usbdev_s *priv); +static void lpc17_dispatchrequest(struct lpc17_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl); +static inline void lpc17_ep0setup(struct lpc17_usbdev_s *priv); +static inline void lpc17_ep0dataoutinterrupt(struct lpc17_usbdev_s *priv); +static inline void lpc17_ep0dataininterrupt(struct lpc17_usbdev_s *priv); +static int lpc17_usbinterrupt(int irq, FAR void *context); + +#ifdef CONFIG_LPC17_USBDEV_DMA +static int lpc17_dmasetup(struct lpc17_usbdev_s *priv, uint8_t epphy, + uint32_t epmaxsize, uint32_t nbytes, uint32_t *isocpacket, + bool isochronous); +static void lpc17_dmarestart(uint8_t epphy, uint32_t descndx); +static void lpc17_dmadisable(uint8_t epphy); +#endif /* CONFIG_LPC17_USBDEV_DMA */ + +/* Endpoint operations *********************************************************/ + +static int lpc17_epconfigure(FAR struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int lpc17_epdisable(FAR struct usbdev_ep_s *ep); +static FAR struct usbdev_req_s *lpc17_epallocreq(FAR struct usbdev_ep_s *ep); +static void lpc17_epfreereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *); +#ifdef CONFIG_LPC17_USBDEV_DMA +static FAR void *lpc17_epallocbuffer(FAR struct usbdev_ep_s *ep, + uint16_t nbytes); +static void lpc17_epfreebuffer(FAR struct usbdev_ep_s *ep, void *buf); +#endif +static int lpc17_epsubmit(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int lpc17_epcancel(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int lpc17_epstall(FAR struct usbdev_ep_s *ep, bool resume); + +/* USB device controller operations ********************************************/ + +static FAR struct usbdev_ep_s *lpc17_allocep(FAR struct usbdev_s *dev, + uint8_t epno, bool in, uint8_t eptype); +static void lpc17_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep); +static int lpc17_getframe(struct usbdev_s *dev); +static int lpc17_wakeup(struct usbdev_s *dev); +static int lpc17_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int lpc17_pullup(struct usbdev_s *dev, bool enable); + +/******************************************************************************* + * Private Data + *******************************************************************************/ + +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct lpc17_usbdev_s g_usbdev; + +static const struct usbdev_epops_s g_epops = +{ + .configure = lpc17_epconfigure, + .disable = lpc17_epdisable, + .allocreq = lpc17_epallocreq, + .freereq = lpc17_epfreereq, +#ifdef CONFIG_LPC17_USBDEV_DMA + .allocbuffer = lpc17_epallocbuffer, + .freebuffer = lpc17_epfreebuffer, +#endif + .submit = lpc17_epsubmit, + .cancel = lpc17_epcancel, + .stall = lpc17_epstall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = lpc17_allocep, + .freeep = lpc17_freeep, + .getframe = lpc17_getframe, + .wakeup = lpc17_wakeup, + .selfpowered = lpc17_selfpowered, + .pullup = lpc17_pullup, +}; + +/******************************************************************************* + * Public Data + *******************************************************************************/ + +/******************************************************************************* + * Private Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: lpc17_getreg + * + * Description: + * Get the contents of an LPC17xx register + * + *******************************************************************************/ + +#if defined(CONFIG_LPC17_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) +static uint32_t lpc17_getreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same registe last time? Are + * we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + lldbg("...\n"); + } + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + lldbg("[repeats %d more times]\n", count-3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + lldbg("%08x->%08x\n", addr, val); + return val; +} +#endif + +/******************************************************************************* + * Name: lpc17_putreg + * + * Description: + * Set the contents of an LPC17xx register to a value + * + *******************************************************************************/ + +#if defined(CONFIG_LPC17_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) +static void lpc17_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + lldbg("%08x<-%08x\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/******************************************************************************* + * Name: lpc17_usbcmd + * + * Description: + * Transmit commands to the USB engine + * + *******************************************************************************/ + +static uint32_t lpc17_usbcmd(uint16_t cmd, uint8_t data) +{ + irqstate_t flags; + uint32_t tmp = 0; + + /* Disable interrupt and clear CDFULL and CCEMPTY interrupt status */ + + flags = irqsave(); + lpc17_putreg(USBDEV_INT_CDFULL|USBDEV_INT_CCEMPTY, LPC17_USBDEV_INTCLR); + + /* Load command + WR in command code register */ + + lpc17_putreg(((cmd & 0xff) << 16) + CMD_USBDEV_CMDWR, LPC17_USBDEV_CMDCODE); + + /* Wait until the command register is empty (CCEMPTY != 0, command is accepted) */ + + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_CCEMPTY) == 0); + + /* Clear command register empty (CCEMPTY) interrupt */ + + lpc17_putreg(USBDEV_INT_CCEMPTY, LPC17_USBDEV_INTCLR); + + /* Determine next phase of the command */ + + switch (cmd) + { + /* Write operations (1 byte of data) */ + + case CMD_USBDEV_SETADDRESS: + case CMD_USBDEV_CONFIG: + case CMD_USBDEV_SETMODE: + case CMD_USBDEV_SETSTATUS: + { + /* Send data + WR and wait for CCEMPTY */ + + lpc17_putreg((data << 16) + CMD_USBDEV_DATAWR, LPC17_USBDEV_CMDCODE); + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_CCEMPTY) == 0); + } + break; + + /* 16 bit read operations */ + + case CMD_USBDEV_READFRAMENO: + case CMD_USBDEV_READTESTREG: + { + /* Send command code + RD and wait for CDFULL */ + + lpc17_putreg((cmd << 16) + CMD_USBDEV_DATARD, LPC17_USBDEV_CMDCODE); + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_CDFULL) == 0); + + /* Clear CDFULL and read LS data */ + + lpc17_putreg(USBDEV_INT_CDFULL, LPC17_USBDEV_INTCLR); + tmp = lpc17_getreg(LPC17_USBDEV_CMDDATA); + + /* Send command code + RD and wait for CDFULL */ + + lpc17_putreg((cmd << 16) + CMD_USBDEV_DATARD, LPC17_USBDEV_CMDCODE); + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_CDFULL) == 0); + + /* Read MS data */ + + tmp |= lpc17_getreg(LPC17_USBDEV_CMDDATA) << 8; + } + break; + + /* 8-bit read operations */ + + case CMD_USBDEV_GETSTATUS: + case CMD_USBDEV_GETERRORCODE: + case CMD_USBDEV_READERRORSTATUS: + case CMD_USBDEV_EPCLRBUFFER: + { + /* Send command code + RD and wait for CDFULL */ + + lpc17_putreg((cmd << 16) + CMD_USBDEV_DATARD, LPC17_USBDEV_CMDCODE); + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_CDFULL) == 0); + + /* Read data */ + + tmp = lpc17_getreg(LPC17_USBDEV_CMDDATA); + } + break; + + /* No data transfer */ + + case CMD_USBDEV_EPVALIDATEBUFFER: + break; + + default: + switch (cmd & 0x1e0) + { + case CMD_USBDEV_EPSELECT: + case CMD_USBDEV_EPSELECTCLEAR: + { + /* Send command code + RD and wait for CDFULL */ + + lpc17_putreg((cmd << 16) + CMD_USBDEV_DATARD, LPC17_USBDEV_CMDCODE); + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_CDFULL) == 0); + + /* Read data */ + + tmp = lpc17_getreg(LPC17_USBDEV_CMDDATA); + } + break; + + case CMD_USBDEV_EPSETSTATUS: + { + /* Send data + RD and wait for CCEMPTY */ + + lpc17_putreg((data << 16) + CMD_USBDEV_DATAWR, LPC17_USBDEV_CMDCODE); + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_CCEMPTY) == 0); + } + break; + + default: + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDCMD), 0); + break; + } + break; + } + + /* Restore the interrupt flags */ + + irqrestore(flags); + return tmp; +} + +/******************************************************************************* + * Name: lpc17_rqdequeue + * + * Description: + * Remove a request from an endpoint request queue + * + *******************************************************************************/ + +static FAR struct lpc17_req_s *lpc17_rqdequeue(FAR struct lpc17_ep_s *privep) +{ + FAR struct lpc17_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/******************************************************************************* + * Name: lpc17_rqenqueue + * + * Description: + * Add a request from an endpoint request queue + * + *******************************************************************************/ + +static void lpc17_rqenqueue(FAR struct lpc17_ep_s *privep, + FAR struct lpc17_req_s *req) +{ + req->flink = NULL; + if (!privep->head) + { + privep->head = req; + privep->tail = req; + } + else + { + privep->tail->flink = req; + privep->tail = req; + } +} + +/******************************************************************************* + * Name: lpc17_epwrite + * + * Description: + * Endpoint write (IN) + * + *******************************************************************************/ + +static void lpc17_epwrite(uint8_t epphy, const uint8_t *data, uint32_t nbytes) +{ + uint32_t value; + bool aligned = (((uint32_t)data & 3) == 0); + + /* Set the write enable bit for this physical EP address. Bits 2-5 are + * the logical endpoint number (0-15) + */ + + lpc17_putreg(((epphy << 1) & USBDEV_CTRL_LOGEP_MASK) | USBDEV_CTRL_WREN, + LPC17_USBDEV_CTRL); + + /* Set the transmit packet length (nbytes must be less than 2048) */ + + lpc17_putreg(nbytes, LPC17_USBDEV_TXPLEN); + + /* Transfer the packet data */ + + do + { + /* Zero length packets are a special case */ + + if (nbytes) + { + if (aligned) + { + value = *(uint32_t*)data; + } + else + { + value = (uint32_t)data[0] | ((uint32_t)data[1] << 8) | + ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); + } + + lpc17_putreg(value, LPC17_USBDEV_TXDATA); + data += 4; + } + else + { + /* Zero length packet */ + + lpc17_putreg(0, LPC17_USBDEV_TXDATA); + } + } + while ((lpc17_getreg(LPC17_USBDEV_CTRL) & USBDEV_CTRL_WREN) != 0); + + /* Done */ + + lpc17_putreg(0, LPC17_USBDEV_CTRL); + (void)lpc17_usbcmd(CMD_USBDEV_EPSELECT | epphy, 0); + (void)lpc17_usbcmd(CMD_USBDEV_EPVALIDATEBUFFER, 0); +} + +/******************************************************************************* + * Name: lpc17_epread + * + * Description: + * Endpoint read (OUT) + * + *******************************************************************************/ + +static int lpc17_epread(uint8_t epphy, uint8_t *data, uint32_t nbytes) +{ + uint32_t pktlen; + uint32_t result; + uint32_t value; + uint8_t aligned = 0; + + /* If data is NULL, then we are being asked to read but discard the data. + * For most cases, the resulting buffer will be aligned and we will be + * able to do faster 32-bit transfers. + */ + + if (data) + { + if (((uint32_t)data & 3) == 0) + { + aligned = 1; + } + else + { + aligned = 2; + } + } + + /* Set the read enable bit for this physical EP address. Bits 2-5 are + * the logical endpoint number (0-15). + */ + + lpc17_putreg(((epphy << 1) & USBDEV_CTRL_LOGEP_MASK) | USBDEV_CTRL_RDEN, + LPC17_USBDEV_CTRL); + + /* Wait for packet buffer ready for reading */ + + while ((lpc17_getreg(LPC17_USBDEV_RXPLEN) & USBDEV_RXPLEN_PKTRDY) == 0); + + /* Get the number of bytes of data to be read */ + + pktlen = lpc17_getreg(LPC17_USBDEV_RXPLEN) & USBDEV_RXPLEN_MASK; + + /* Read data from input buffer while read data is valid (DV) */ + + while ((lpc17_getreg(LPC17_USBDEV_RXPLEN) & USBDEV_RXPLEN_DV) != 0) + { + value = lpc17_getreg(LPC17_USBDEV_RXDATA); + if (aligned == 1) + { + *(uint32_t*)data = value; + data += 4; + } + else if (aligned == 2) + { + *data++ = (uint8_t)value; + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 24); + } + } + + /* Done */ + + lpc17_putreg(0, LPC17_USBDEV_CTRL); + (void)lpc17_usbcmd(CMD_USBDEV_EPSELECT | epphy, 0); + result = lpc17_usbcmd(CMD_USBDEV_EPCLRBUFFER, 0); + + /* The packet overrun bit in the clear buffer response is applicable only + * on EP0 transfers. If set it means that the recevied packet was overwritten + * by a later setup packet. + */ + + if (epphy == LPC17_EP0_OUT && (result & CMD_USBDEV_CLRBUFFER_PO) != 0) + { + /* Pass this information in bit 31 */ + + pktlen |= LPC17_READOVERRUN_BIT; + } + return pktlen; +} + +/******************************************************************************* + * Name: lpc17_abortrequest + * + * Description: + * Discard a request + * + *******************************************************************************/ + +static inline void lpc17_abortrequest(struct lpc17_ep_s *privep, + struct lpc17_req_s *privreq, + int16_t result) +{ + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_REQABORTED), (uint16_t)privep->epphy); + + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->req.callback(&privep->ep, &privreq->req); +} + +/******************************************************************************* + * Name: lpc17_reqcomplete + * + * Description: + * Handle termination of the request at the head of the endpoint request queue. + * + *******************************************************************************/ + +static void lpc17_reqcomplete(struct lpc17_ep_s *privep, int16_t result) +{ + struct lpc17_req_s *privreq; + int stalled = privep->stalled; + irqstate_t flags; + + /* Remove the completed request at the head of the endpoint request list */ + + flags = irqsave(); + privreq = lpc17_rqdequeue(privep); + irqrestore(flags); + + if (privreq) + { + /* If endpoint 0, temporarily reflect the state of protocol stalled + * in the callback. + */ + + if (privep->epphy == LPC17_EP0_IN) + { + privep->stalled = privep->dev->stalled; + } + + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->flink = NULL; + privreq->req.callback(&privep->ep, &privreq->req); + + /* Restore the stalled indication */ + + privep->stalled = stalled; + } +} + +/******************************************************************************* + * Name: lpc17_wrrequest + * + * Description: + * Send from the next queued write request + * + *******************************************************************************/ + +static int lpc17_wrrequest(struct lpc17_ep_s *privep) +{ + struct lpc17_req_s *privreq; + uint8_t *buf; + int nbytes; + int bytesleft; + + /* Check the request from the head of the endpoint request queue */ + + privreq = lpc17_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPINQEMPTY), 0); + return OK; + } + + ullvdbg("epphy=%d req=%p: len=%d xfrd=%d nullpkt=%d\n", + privep->epphy, privreq, privreq->req.len, privreq->req.xfrd, privep->txnullpkt); + + /* Ignore any attempt to send a zero length packet on anything but EP0IN */ + + if (privreq->req.len == 0) + { + if (privep->epphy == LPC17_EP0_IN) + { + lpc17_epwrite(LPC17_EP0_IN, NULL, 0); + } + else + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_EPINNULLPACKET), 0); + } + + /* In any event, the request is complete */ + + lpc17_reqcomplete(privep, OK); + return OK; + } + + /* Otherwise send the data in the packet (in the DMA on case, we + * may be resuming transfer already in progress. + */ +#warning REVISIT... If the EP supports double buffering, then we can do better + + /* Get the number of bytes left to be sent in the packet */ + + bytesleft = privreq->req.len - privreq->req.xfrd; + + /* Send the next packet if (1) there are more bytes to be sent, or + * (2) the last packet sent was exactly maxpacketsize (bytesleft == 0) + */ + + usbtrace(TRACE_WRITE(privep->epphy), privreq->req.xfrd); + if (bytesleft > 0 || privep->txnullpkt) + { + /* Indicate that there is data in the TX FIFO. This will be cleared + * when the EPIN interrupt is received + */ + + privep->txbusy = 1; + + /* Try to send maxpacketsize -- unless we don't have that many + * bytes to send. + */ + + privep->txnullpkt = 0; + if (bytesleft > privep->ep.maxpacket) + { + nbytes = privep->ep.maxpacket; + } + else + { + nbytes = bytesleft; + if ((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0) + { + privep->txnullpkt = (bytesleft == privep->ep.maxpacket); + } + } + + /* Send the largest number of bytes that we can in this packet */ + + buf = privreq->req.buf + privreq->req.xfrd; + lpc17_epwrite(privep->epphy, buf, nbytes); + + /* Update for the next time through the loop */ + + privreq->req.xfrd += nbytes; + } + + /* If all of the bytes were sent (including any final null packet) + * then we are finished with the transfer + */ + + if (privreq->req.xfrd >= privreq->req.len && !privep->txnullpkt) + { + usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); + privep->txnullpkt = 0; + lpc17_reqcomplete(privep, OK); + } + + return OK; +} + +/******************************************************************************* + * Name: lpc17_rdrequest + * + * Description: + * Receive to the next queued read request + * + *******************************************************************************/ + +static int lpc17_rdrequest(struct lpc17_ep_s *privep) +{ + struct lpc17_req_s *privreq; + uint8_t *buf; + int nbytesread; + + /* Check the request from the head of the endpoint request queue */ + + privreq = lpc17_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPOUTQEMPTY), 0); + return OK; + } + + ullvdbg("len=%d xfrd=%d nullpkt=%d\n", + privreq->req.len, privreq->req.xfrd, privep->txnullpkt); + + /* Ignore any attempt to receive a zero length packet */ + + if (privreq->req.len == 0) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_EPOUTNULLPACKET), 0); + lpc17_reqcomplete(privep, OK); + return OK; + } + + usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd); + + /* Receive the next packet */ + + buf = privreq->req.buf + privreq->req.xfrd; + nbytesread = lpc17_epread(privep->epphy, buf, privep->ep.maxpacket); + if (nbytesread < 0) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_EPREAD), nbytesread); + return ERROR; + } + + /* If the receive buffer is full or if the last packet was not full + * then we are finished with the transfer. + */ + + privreq->req.xfrd += nbytesread; + if (privreq->req.xfrd >= privreq->req.len || nbytesread < privep->ep.maxpacket) + { + usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); + lpc17_reqcomplete(privep, OK); + } + + return OK; +} + +/******************************************************************************* + * Name: lpc17_cancelrequests + * + * Description: + * Cancel all pending requests for an endpoint + * + *******************************************************************************/ + +static void lpc17_cancelrequests(struct lpc17_ep_s *privep) +{ + while (!lpc17_rqempty(privep)) + { + usbtrace(TRACE_COMPLETE(privep->epphy), + (lpc17_rqpeek(privep))->req.xfrd); + lpc17_reqcomplete(privep, -ESHUTDOWN); + } +} + +/******************************************************************************* + * Name: lpc17_epfindbyaddr + * + * Description: + * Find the physical endpoint structure corresponding to a logic endpoint + * address + * + *******************************************************************************/ + +static struct lpc17_ep_s *lpc17_epfindbyaddr(struct lpc17_usbdev_s *priv, + uint16_t eplog) +{ + struct lpc17_ep_s *privep; + int i; + + /* Endpoint zero is a special case */ + + if (USB_EPNO(eplog) == 0) + { + return &priv->eplist[0]; + } + + /* Handle the remaining */ + + for (i = 1; i < LPC17_NPHYSENDPOINTS; i++) + { + privep = &priv->eplist[i]; + + /* Same logical endpoint number? (includes direction bit) */ + + if (eplog == privep->ep.eplog) + { + /* Return endpoint found */ + + return privep; + } + } + + /* Return endpoint not found */ + + return NULL; +} + +/******************************************************************************* + * Name: lpc17_eprealize + * + * Description: + * Enable or disable an endpoint + * + *******************************************************************************/ + +static void lpc17_eprealize(struct lpc17_ep_s *privep, bool prio, uint32_t packetsize) +{ + struct lpc17_usbdev_s *priv = privep->dev; + uint32_t mask; + uint32_t reg; + + /* Initialize endpoint software priority */ + + mask = 1 << privep->epphy; + if (prio) + { + priv->softprio = priv->softprio | mask; + } + else + { + priv->softprio = priv->softprio & ~mask; + } + + /* Clear realize interrupt bit */ + + lpc17_putreg(USBDEV_INT_EPRLZED, LPC17_USBDEV_INTCLR); + + /* Realize the endpoint */ + + reg = lpc17_getreg(LPC17_USBDEV_REEP); + reg |= (1 << privep->epphy); + lpc17_putreg(reg, LPC17_USBDEV_REEP); + + /* Set endpoint maximum packet size */ + + lpc17_putreg(privep->epphy, LPC17_USBDEV_EPIND); + lpc17_putreg(packetsize, LPC17_USBDEV_MAXPSIZE); + + /* Wait for Realize complete */ + + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_EPRLZED) == 0); + + /* Clear realize interrupt bit */ + + lpc17_putreg(USBDEV_INT_EPRLZED,LPC17_USBDEV_INTCLR); +} + +/******************************************************************************* + * Name: lpc17_epclrinterrupt + * + * Description: + * Clear the EP interrupt flag and return the current EP status + * + *******************************************************************************/ + +static uint8_t lpc17_epclrinterrupt(uint8_t epphy) +{ + /* Clear the endpoint interrupt */ + + lpc17_putreg(1 << epphy, LPC17_USBDEV_EPINTCLR); + + /* Wait for data in the command data register */ + + while ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INT_CDFULL) == 0); + + /* Return the value of the command data register */ + + return lpc17_getreg(LPC17_USBDEV_CMDDATA); +} + +/******************************************************************************* + * Name: lpc17_ep0configure + * + * Description: + * Configure endpoint 0 + * + *******************************************************************************/ + +static inline void lpc17_ep0configure(struct lpc17_usbdev_s *priv) +{ + uint32_t inten; + + /* EndPoint 0 initialization */ + + lpc17_eprealize(&priv->eplist[LPC17_CTRLEP_OUT], 0, CONFIG_USBDEV_EP0_MAXSIZE); + lpc17_eprealize(&priv->eplist[LPC17_CTRLEP_IN], 1, CONFIG_USBDEV_EP0_MAXSIZE); + + /* Enable EP0 interrupts (not DMA) */ + + inten = lpc17_getreg(LPC17_USBDEV_EPINTEN); + inten |= 3; /* EP0 Rx and Tx */ + lpc17_putreg(inten, LPC17_USBDEV_EPINTEN); +} + +/******************************************************************************* + * Name: lpc17_dmareset + * + * Description: Reset USB DMA + * + *******************************************************************************/ + +#ifdef CONFIG_LPC17_USBDEV_DMA +static inline void lpc17_dmareset(uint32_t enable) +{ + int i; + + /* Disable All DMA interrupts */ + + lpc17_putreg(0, LPC17_USBDEV_DMAINTEN); + + /* DMA Disable */ + + lpc17_putreg(0xffffffff, LPC17_USBDEV_EPDMADIS); + + /* DMA Request clear */ + + putreq32(0xffffffff, LPC17_USBDEV_DMARCLR); + + /* End of Transfer Interrupt Clear */ + + putreq32(0xffffffff, LPC17_USBDEV_EOTINTCLR); + + /* New DD Request Interrupt Clear */ + + putreq32(0xffffffff, LPC17_USBDEV_NDDRINTCLR); + + /* System Error Interrupt Clear */ + + putreq32(0xffffffff, LPC17_USBDEV_SYSERRINTCLR); + + /* Nullify all pointers in the UDCA */ + + for (i = 0; i < LPC17_NPHYSENDPOINTS; ++i) + { + USB_UDCA[i] = NULL; + } + + /* Set USB UDCA Head register */ + + lpc17_putreg((uint32_t)USB_UDCA, LPC17_USBDEV_UDCAH); + + /* Invalidate all DMA descriptors */ + + for (i = 0; i < CONFIG_LPC17_USBDEV_NDMADESCRIPTORS; ++i) + { + memset(&USB_DDESC[i], 0, USB_DDESCSIZE); + } + + /* Enable DMA interrupts */ + + lpc17_putreg(enable, LPC17_USBDEV_DMAINTEN); +} +#endif + +/******************************************************************************* + * Name: lpc17_usbreset + * + * Description: + * Reset Usb engine + * + *******************************************************************************/ + +static void lpc17_usbreset(struct lpc17_usbdev_s *priv) +{ + /* Disable all endpoint interrupts */ + + lpc17_putreg(0, LPC17_USBDEV_EPINTEN); + + /* Frame is Hp interrupt */ + + lpc17_putreg(1, LPC17_USBDEV_INTPRI); + + /* Clear all pending interrupts */ + + lpc17_putreg(0xffffffff, LPC17_USBDEV_EPINTCLR); + lpc17_putreg(0xffffffff, LPC17_USBDEV_INTCLR); + + /* Periperhal address is needed */ + + priv->paddrset = 0; + + /* Endpoints not yet configured */ + + lpc17_usbcmd(CMD_USBDEV_CONFIG, 0); + + /* EndPoint 0 initialization */ + + lpc17_ep0configure(priv); + +#ifdef CONFIG_LPC17_USBDEV_DMA + /* Enable End_of_Transfer_Interrupt and System_Error_Interrupt USB DMA + * interrupts + */ + + lpc17_dmareset(CONFIG_LPC17_USBDEV_DMAINT_MASK); + +#endif + + /* Enable Device interrupts */ + + lpc17_putreg(USB_SLOW_INT|USB_DEVSTATUS_INT|USB_FAST_INT|USB_FRAME_INT|USB_ERROR_INT, + LPC17_USBDEV_INTEN); +} + +/******************************************************************************* + * Name: lpc17_dispatchrequest + * + * Description: + * Provide unhandled setup actions to the class driver. This is logically part + * of the USB interrupt handler. + * + *******************************************************************************/ + +static void lpc17_dispatchrequest(struct lpc17_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl) +{ + int ret; + + usbtrace(TRACE_INTDECODE(LPC17_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) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_DISPATCHSTALL), 0); + priv->stalled = 1; + } + } +} + +/******************************************************************************* + * Name: lpc17_ep0setup + * + * Description: + * USB Ctrl EP Setup Event. This is logically part of the USB interrupt + * handler. This event occurs when a setup packet is receive on EP0 OUT. + * + *******************************************************************************/ + +static inline void lpc17_ep0setup(struct lpc17_usbdev_s *priv) +{ + struct lpc17_ep_s *ep0 = &priv->eplist[LPC17_EP0_OUT]; + struct lpc17_ep_s *privep; + struct lpc17_req_s *privreq = lpc17_rqpeek(ep0); + struct usb_ctrlreq_s ctrl; + uint16_t value; + uint16_t index; + uint16_t len; + uint8_t response[2]; + int ret; + + /* Starting a control request? */ + + if (priv->usbdev.speed == USB_SPEED_UNKNOWN) + { + priv->usbdev.speed = USB_SPEED_FULL; + lpc17_usbcmd(CMD_USBDEV_CONFIG, 1); + } + + /* Terminate any pending requests */ + + while (!lpc17_rqempty(ep0)) + { + int16_t result = OK; + if (privreq->req.xfrd != privreq->req.len) + { + result = -EPROTO; + } + + usbtrace(TRACE_COMPLETE(ep0->epphy), privreq->req.xfrd); + lpc17_reqcomplete(ep0, result); + } + + /* Assume NOT stalled */ + + ep0->stalled = 0; + priv->stalled = 0; + + /* Read EP0 data */ + + ret = lpc17_epread(LPC17_EP0_OUT, (uint8_t*)&ctrl, USB_SIZEOF_CTRLREQ); + if (ret <= 0) + { + return; + } + + /* And extract the little-endian 16-bit values to host order */ + + value = GETUINT16(ctrl.value); + index = GETUINT16(ctrl.index); + len = GETUINT16(ctrl.len); + + ullvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", + ctrl.type, ctrl.req, value, index, len); + + /* Dispatch any non-standard requests */ + + if ((ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + lpc17_dispatchrequest(priv, &ctrl); + return; + } + + /* Handle standard request. Pick off the things of interest to the + * USB device controller driver; pass what is left to the class driver + */ + + switch (ctrl.req) + { + case USB_REQ_GETSTATUS: + { + /* type: device-to-host; recipient = device, interface, endpoint + * value: 0 + * index: zero interface endpoint + * len: 2; data = status + */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_GETSTATUS), 0); + if (!priv->paddrset || len != 2 || + (ctrl.type & USB_REQ_DIR_IN) == 0 || value != 0) + { + priv->stalled = 1; + } + else + { + switch (ctrl.type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPGETSTATUS), 0); + privep = lpc17_epfindbyaddr(priv, index); + if (!privep) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADEPGETSTATUS), 0); + priv->stalled = 1; + } + else + { + if ((lpc17_usbcmd(CMD_USBDEV_EPSELECT|privep->epphy, 0) & CMD_EPSELECT_ST) != 0) + { + response[0] = 1; /* Stalled */ + } + else + { + response[0] = 0; /* Not stalled */ + } + response[1] = 0; + lpc17_epwrite(LPC17_EP0_IN, response, 2); + priv->ep0state = LPC17_EP0SHORTWRITE; + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index == 0) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_DEVGETSTATUS), 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + + response[0] = (priv->selfpowered << USB_FEATURE_SELFPOWERED) | + (1 << USB_FEATURE_REMOTEWAKEUP); + response[1] = 0; + lpc17_epwrite(LPC17_EP0_IN, response, 2); + priv->ep0state = LPC17_EP0SHORTWRITE; + } + else + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADDEVGETSTATUS), 0); + priv->stalled = 1; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_IFGETSTATUS), 0); + response[0] = 0; + response[1] = 0; + lpc17_epwrite(LPC17_EP0_IN, response, 2); + priv->ep0state = LPC17_EP0SHORTWRITE; + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADGETSTATUS), 0); + priv->stalled = 1; + } + break; + } + } + } + break; + + case USB_REQ_CLEARFEATURE: + { + /* type: host-to-device; recipient = device, interface or endpoint + * value: feature selector + * index: zero interface endpoint; + * len: zero, data = none + */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_CLEARFEATURE), 0); + if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) + { + lpc17_dispatchrequest(priv, &ctrl); + } + else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 && + (privep = lpc17_epfindbyaddr(priv, index)) != NULL) + { + privep->halted = 0; + ret = lpc17_epstall(&privep->ep, true); + lpc17_epwrite(LPC17_EP0_IN, NULL, 0); + priv->ep0state = LPC17_EP0STATUSIN; + } + else + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADCLEARFEATURE), 0); + priv->stalled = 1; + } + } + break; + + case USB_REQ_SETFEATURE: + { + /* type: host-to-device; recipient = device, interface, endpoint + * value: feature selector + * index: zero interface endpoint; + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_SETFEATURE), 0); + if (((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) && + value == USB_FEATURE_TESTMODE) + { + ullvdbg("test mode: %d\n", index); + } + else if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) + { + lpc17_dispatchrequest(priv, &ctrl); + } + else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 && + (privep = lpc17_epfindbyaddr(priv, index)) != NULL) + { + privep->halted = 1; + lpc17_epwrite(LPC17_EP0_IN, NULL, 0); + priv->ep0state = LPC17_EP0STATUSIN; + } + else + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADSETFEATURE), 0); + priv->stalled = 1; + } + } + break; + + case USB_REQ_SETADDRESS: + { + /* type: host-to-device; recipient = device + * value: device address + * index: 0 + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EP0SETUPSETADDRESS), value); + if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + index == 0 && len == 0 && value < 128) + { + /* Save the address. We cannot actually change to the next address until + * the completion of the status phase. + */ + + priv->paddr = ctrl.value[0]; + + /* Note that if we send the SETADDRESS command twice, that will force the + * address change. Otherwise, the hardware will automatically set the + * address at the end of the status phase. + */ + + lpc17_usbcmd(CMD_USBDEV_SETADDRESS, CMD_USBDEV_SETADDRESS_DEVEN | priv->paddr); + + /* Send a NULL packet. The status phase completes when the null packet has + * been sent successfully. + */ + + lpc17_epwrite(LPC17_EP0_IN, NULL, 0); + priv->ep0state = LPC17_EP0SETADDRESS; + } + else + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADSETADDRESS), 0); + priv->stalled = 1; + } + } + break; + + case USB_REQ_GETDESCRIPTOR: + /* type: device-to-host; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + case USB_REQ_SETDESCRIPTOR: + /* type: host-to-device; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_GETSETDESC), 0); + if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) + { + lpc17_dispatchrequest(priv, &ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADGETSETDESC), 0); + priv->stalled = 1; + } + } + break; + + case USB_REQ_GETCONFIGURATION: + /* type: device-to-host; recipient = device + * value: 0; + * index: 0; + * len: 1; data = configuration value + */ + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_GETCONFIG), 0); + if (priv->paddrset && (ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + value == 0 && index == 0 && len == 1) + { + lpc17_dispatchrequest(priv, &ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADGETCONFIG), 0); + priv->stalled = 1; + } + } + break; + + case USB_REQ_SETCONFIGURATION: + /* type: host-to-device; recipient = device + * value: configuration value + * index: 0; + * len: 0; data = none + */ + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_SETCONFIG), 0); + if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + index == 0 && len == 0) + { + lpc17_dispatchrequest(priv, &ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADSETCONFIG), 0); + priv->stalled = 1; + } + } + break; + + case USB_REQ_GETINTERFACE: + /* type: device-to-host; recipient = interface + * value: 0 + * index: interface; + * len: 1; data = alt interface + */ + case USB_REQ_SETINTERFACE: + /* type: host-to-device; recipient = interface + * value: alternate setting + * index: interface; + * len: 0; data = none + */ + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_GETSETIF), 0); + lpc17_dispatchrequest(priv, &ctrl); + } + break; + + case USB_REQ_SYNCHFRAME: + /* type: device-to-host; recipient = endpoint + * value: 0 + * index: endpoint; + * len: 2; data = frame number + */ + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDCTRLREQ), 0); + priv->stalled = 1; + } + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_EP0SETUPSTALLED), priv->ep0state); + lpc17_epstall(&ep0->ep, false); + lpc17_epstall(&ep0->ep, false); + } +} + +/******************************************************************************* + * Name: lpc17_ep0dataoutinterrupt + * + * Description: + * USB Ctrl EP Data OUT Event. This is logically part of the USB interrupt + * handler. Each non-isochronous OUT endpoint gives an interrupt when they + * receive a packet without error. + * + *******************************************************************************/ + +static inline void lpc17_ep0dataoutinterrupt(struct lpc17_usbdev_s *priv) +{ + struct lpc17_ep_s *ep0; + uint32_t pktlen; + + /* Copy new setup packet into setup buffer */ + + switch (priv->ep0state) + { + case LPC17_EP0SHORTWRITE: + { + priv->ep0state = LPC17_EP0STATUSOUT; + pktlen = lpc17_epread(LPC17_EP0_OUT, NULL, CONFIG_USBDEV_EP0_MAXSIZE); + if (LPC17_READOVERRUN(pktlen)) + { + lpc17_ep0setup(priv); + } + } + break; + + case LPC17_EP0SHORTWRSENT: + { + priv->ep0state = LPC17_EP0REQUEST; + pktlen = lpc17_epread(LPC17_EP0_OUT, NULL, CONFIG_USBDEV_EP0_MAXSIZE); + if (LPC17_READOVERRUN(pktlen)) + { + lpc17_ep0setup(priv); + } + } + break; + + case LPC17_EP0REQUEST: + { + /* Process the next request action (if any) */ + + lpc17_rdrequest(&priv->eplist[LPC17_EP0_OUT]); + } + break; + + default: + priv->stalled = 1; + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_EP0OUTSTALLED), priv->ep0state); + ep0 = &priv->eplist[LPC17_EP0_OUT]; + lpc17_epstall(&ep0->ep, false); + lpc17_epstall(&ep0->ep, false); + } + return; +} + +/******************************************************************************* + * Name: lpc17_ep0dataininterrupt + * + * Description: + * USB Ctrl EP Data IN Event. This is logically part of the USB interrupt + * handler. All non-isochronous IN endpoints give this interrupt when a + * packet is successfully transmitted (OR a NAK handshake is sent on the bus + * provided that the interrupt on NAK feature is enabled). + * + *******************************************************************************/ + +static inline void lpc17_ep0dataininterrupt(struct lpc17_usbdev_s *priv) +{ + struct lpc17_ep_s *ep0; + + switch (priv->ep0state) + { + case LPC17_EP0STATUSOUT: + case LPC17_EP0STATUSIN: + priv->ep0state = LPC17_EP0REQUEST; + break; + + case LPC17_EP0SHORTWRITE: + priv->ep0state = LPC17_EP0SHORTWRSENT; + break; + + case LPC17_EP0SETADDRESS: + { + /* If the address was set to a non-zero value, then thiscompletes the + * default phase, and begins the address phase (still not fully configured) + */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EP0INSETADDRESS), (uint16_t)priv->paddr); + lpc17_usbcmd(CMD_USBDEV_CONFIG, 0); + if (priv->paddr) + { + priv->paddrset = 1; + priv->ep0state = LPC17_EP0REQUEST; + } + } + break; + + case LPC17_EP0REQUEST: + { + /* Process the next request action (if any) */ + + ep0 = &priv->eplist[LPC17_EP0_IN]; + ep0->txbusy = 0; + lpc17_wrrequest(ep0); + } + break; + + default: + priv->stalled = 1; + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_EP0INSTALLED), priv->ep0state); + ep0 = &priv->eplist[LPC17_EP0_IN]; + lpc17_epstall(&ep0->ep, false); + lpc17_epstall(&ep0->ep, false); + } +} + +/******************************************************************************* + * Name: lpc17_usbinterrupt + * + * Description: + * USB interrupt handler + * + *******************************************************************************/ + +static int lpc17_usbinterrupt(int irq, FAR void *context) +{ + struct lpc17_usbdev_s *priv = &g_usbdev; + struct lpc17_ep_s *privep ; + + uint32_t devintstatus; /* Sampled state of the device interrupt status register */ + uint32_t epintstatus; /* Sampled state of the endpoint interrupt status register */ +#ifdef CONFIG_LPC17_USBDEV_DMA + uint32_t dmaintstatus; /* Sampled state of dma interrupt status register */ +#endif + uint32_t softprio; /* Current priority interrupt bitset */ + uint32_t pending; /* Pending subset of priority interrupt bitset */ + uint8_t epphy; /* Physical endpoint number being processed */ + int i; + + usbtrace(TRACE_INTENTRY(LPC17_TRACEINTID_USB), 0); + + /* Read the device interrupt status register */ + + devintstatus = lpc17_getreg(LPC17_USBDEV_INTST); + +#ifdef CONFIG_LPC17_USBDEV_DMA + /* Check for low priority and high priority (non-DMA) interrupts */ + + if ((lpc17_getreg(LPC17_USBDEV_INTST) & (USBDEV_INTST_REQLP|USBDEV_INTST_REQHP)) != 0) + { +#endif +#ifdef CONFIG_LPC17_USBDEV_EPFAST_INTERRUPT + /* Fast EP interrupt */ + + if ((devintstatus & USBDEV_INT_EPFAST) != 0) + { + /* Clear Fast EP interrupt */ + + lpc17_putreg(USBDEV_INT_EPFAST, LPC17_USBDEV_INTCLR); + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPFAST), 0); + + /* Do what? */ + } + +#endif + +#if CONFIG_DEBUG + /* USB engine error interrupt */ + + if ((devintstatus & USBDEV_INT_EPRINT)) + { + uint8_t errcode; + + /* Clear the error interrupt */ + + lpc17_putreg(USBDEV_INT_EPRINT, LPC17_USBDEV_INTCLR); + + /* And show what error occurred */ + + errcode = (uint8_t)lpc17_usbcmd(CMD_USBDEV_READERRORSTATUS, 0) & 0x0f; + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPRINT), (uint16_t)errcode); + } +#endif + +#ifdef CONFIG_LPC17_USBDEV_FRAME_INTERRUPT + /* Frame interrupt */ + + if ((devintstatus & USBDEV_INT_FRAME) != 0) + { + /* Clear the frame interrupt */ + + lpc17_putreg(USBDEV_INT_FRAME, LPC17_USBDEV_INTCLR); + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_FRAME), 0); + + /* Then read the start of frame value */ + + priv->sof = (uint16_t)lpc17_usbcmd(CMD_USBDEV_READFRAMENO, 0); + } +#endif + + /* Device Status interrupt */ + + if ((devintstatus & USBDEV_INT_DEVSTAT) != 0) + { + /* Clear Device status interrupt */ + + lpc17_putreg(USBDEV_INT_DEVSTAT, LPC17_USBDEV_INTCLR); + + /* Get device status */ + + g_usbdev.devstatus = (uint8_t)lpc17_usbcmd(CMD_USBDEV_GETSTATUS, 0); + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_DEVSTAT), (uint16_t)g_usbdev.devstatus); + + /* Device connection status */ + + if (DEVSTATUS_CONNCHG(g_usbdev.devstatus)) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_CONNECTCHG), + (uint16_t)g_usbdev.devstatus); + if (DEVSTATUS_CONNECT(g_usbdev.devstatus)) + { + /* Host is connected */ + + if (!priv->attached) + { + /* We have a transition from unattached to attached */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_CONNECTED), + (uint16_t)g_usbdev.devstatus); + priv->usbdev.speed = USB_SPEED_UNKNOWN; + lpc17_usbcmd(CMD_USBDEV_CONFIG, 0); + priv->attached = 1; + } + } + + /* Otherwise the host is not attached */ + + else if (priv->attached) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_DISCONNECTED), + (uint16_t)g_usbdev.devstatus); + priv->usbdev.speed = USB_SPEED_UNKNOWN; + lpc17_usbcmd(CMD_USBDEV_CONFIG, 0); + priv->attached = 0; + priv->paddrset = 0; + } + } + + /* Device suspend status */ + + if (DEVSTATUS_SUSPCHG(g_usbdev.devstatus)) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_SUSPENDCHG), + (uint16_t)g_usbdev.devstatus); + } + + /* Device reset */ + + if (DEVSTATUS_RESET(g_usbdev.devstatus)) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_DEVRESET), + (uint16_t)g_usbdev.devstatus); + lpc17_usbreset(priv); + } + } + + /* Slow EP interrupt */ + + if ((devintstatus & USBDEV_INT_EPSLOW) != 0) + { + /* Clear Slow EP interrupt */ + + lpc17_putreg(USBDEV_INT_EPSLOW, LPC17_USBDEV_INTCLR); + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPSLOW), 0); + + do + { + /* Read the endpoint interrupt status register */ + + epintstatus = lpc17_getreg(LPC17_USBDEV_EPINTST); + + /* Loop twice: Process software high priority interrupts + * on the first pass and low priority interrupts on the + * second. + */ + + softprio = priv->softprio; + for (i = 0; i < 2; i++, softprio = ~softprio) + { + /* On the first time through the loop, pending will be + * the bitset of high priority pending interrupts; on the + * second time throught it will be the bitset of low + * priority interrupts. + */ + + pending = epintstatus & softprio; + + /* EP0 OUT interrupt indicated by bit0 == 1 */ + + if ((pending & 1) != 0) + { + /* Clear the endpoint interrupt */ + + uint32_t result = lpc17_epclrinterrupt(LPC17_CTRLEP_OUT); + if (result & CMD_EPSELECT_STP) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EP0SETUP), (uint16_t)result); + lpc17_ep0setup(priv); + } + else + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EP0OUT), priv->ep0state); + lpc17_ep0dataoutinterrupt(priv); + } + break; + } + + /* EP0 IN interrupt indicated by bit1 == 1 */ + + if ((pending & 2) != 0) + { + /* Clear the endpoint interrupt */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EP0IN), priv->ep0state); + (void)lpc17_epclrinterrupt(LPC17_CTRLEP_IN); + lpc17_ep0dataininterrupt(priv); + } + pending >>= 2; + + /* All other endpoints EP 1-31 */ + + for (epphy = 2; pending; epphy++, pending >>= 1) + { + /* Is the endpoint interrupt pending? */ + + if ((pending & 1) != 0) + { + /* Yes.. clear the endpoint interrupt */ + + (void)lpc17_epclrinterrupt(epphy); + + /* Get the endpoint sructure corresponding to the physical + * endpoint number. + */ + + privep = &priv->eplist[epphy]; + + /* Check for complete on IN or OUT endpoint. Odd physical + * endpoint addresses are IN endpoints. + */ + + if ((epphy & 1) != 0) + { + /* IN: device-to-host */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPOUT), (uint16_t)epphy); + if (priv->usbdev.speed == USB_SPEED_UNKNOWN) + { + priv->usbdev.speed = USB_SPEED_FULL; + lpc17_usbcmd(CMD_USBDEV_CONFIG, 1); + } + + /* Write host data from the current write request (if any) */ + + privep->txbusy = 0; + lpc17_wrrequest(privep); + } + else + { + /* OUT: host-to-device */ + + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPIN), (uint16_t)epphy); + + /* Read host data into the current read request */ + + if (!lpc17_rqempty(privep)) + { + lpc17_rdrequest(privep); + } + else + { + ullvdbg("Pending data on OUT endpoint\n"); + priv->rxpending = 1; + } + } + } + } + } + } + while (epintstatus); + } +#ifdef CONFIG_LPC17_USBDEV_DMA + } + + /* Check for DMA interrupts */ + + if ((lpc17_getreg(LPC17_USBDEV_INTST) & USBDEV_INTST_REQDMA) != 0) + { + /* First Software High priority and then low priority */ + + uint32_t tmp; + + /* Collect the DMA interrupt sources */ + + dmaintstatus = 0; + tmp = lpc17_getreg(LPC17_USBDEV_EOTINTST); + if (lpc17_getreg(LPC17_USBDEV_DMAINTEN) & 1) + { + dmaintstatus |= tmp; + } + lpc17_putreg(tmp, LPC17_USBDEV_EOTINTCLR); + + tmp = lpc17_getreg(LPC17_USBDEV_NDDRINTST); + if (lpc17_getreg(LPC17_USBDEV_DMAINTEN) & 2) + { + dmaintstatus |= tmp; + } + lpc17_putreg(tmp, LPC17_USBDEV_NDDRINTCLR); + + tmp = lpc17_getreg(LPC17_USBDEV_SYSERRINTST); + if (lpc17_getreg(LPC17_USBDEV_DMAINTEN) & 4) + { + dmaintstatus |= tmp; + } + lpc17_putreg(tmp, LPC17_USBDEV_SYSERRINTCLR); + + /* Loop twice: Process software high priority interrupts on the + * first pass and low priority interrupts on the second. + */ + + softprio = priv->softprio; + for (i = 0; i < 2; i++, softprio = ~softprio) + { + /* On the first time through the loop, pending will be + * the bitset of high priority pending interrupts; on the + * second time throught it will be the bitset of low + * priority interrupts. Note that EP0 IN and OUT are + * omitted. + */ + + pending = (dmaintstatus & softprio) >> 2; + for (epphy = 2; pending; epphy++, pending >>= 1) + { + if ((pending & 1) != 0) + { + usbtrace(TRACE_INTDECODE(LPC17_TRACEINTID_EPDMA), (uint16_t)epphy); +#warning DO WHAT? + } + } + } + } +#endif + usbtrace(TRACE_INTEXIT(LPC17_TRACEINTID_USB), 0); + return OK; +} + +/******************************************************************************* + * Name: lpc17_dmasetup + * + * Description: + * Setup for DMA Transfer + * + *******************************************************************************/ + +#ifdef CONFIG_LPC17_USBDEV_DMA +static int lpc17_dmasetup(struct lpc17_usbdev_s *priv, uint8_t epphy, + uint32_t epmaxsize, uint32_t nbytes, uint32_t *isocpacket, + bool isochronous); +{ + struct lpc17_dmadesc_s *dmadesc = priv; + uint32_t reg; + +#ifdef CONFIG_DEBUG + if (!priv || epphy < 2) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Check if a DMA descriptor has been assigned. If not, than that indicates + * that we will have to do parallel I/O + */ + + if (!dmadesc) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_NODMADESC), 0); + return -EBUSY; + } + + /* Verify that the DMA descriptor is available */ + + if ((dmadesc->status & USB_DMADESC_STATUS_MASK) == USB_DMADESC_BEINGSERVICED) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_DMABUSY), 0); + return -EBUSY; /* Shouldn't happen */ + } + + /* Init DMA Descriptor */ + + dmadesc->nexdesc = 0; + dmadesc->config = USB_DMADESC_MODENORMAL | + ((epmaxsize << USB_DMADESC_PKTSIZE_SHIFT) & USB_DMADESC_PKTSIZE_MASK) | + ((nbytes << USB_DMADESC_BUFLEN_SHIFT) & USB_DMADESC_BUFLEN_MASK); + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + if (isochronous) + { + dmadesc->config |= USB_DMADESC_ISCOEP; + } +#endif + + dmadesc->start = (uint32_t)&dmadesc->buffer; + dmadesc->status = 0; + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + dmadesc->size = (uint32_t)packet; +#endif + + /* Enable DMA tranfer for this endpoint */ + + putreq32(1 << epphy, LPC17_USBDEV_EPDMAEN); + + /* Check state of IN/OUT Ep buffer */ + + reg = lpc17_usbcmd(CMD_USBDEV_EPSELECT | epphy, 0); + + if ((LPC17_EPPHYIN(epphy) && (reg & 0x60) == 0) || + (LPC17_EPPHYOUT(epphy) && (reg & 0x60) == 0x60)) + { + /* DMA should be "being serviced" */ + + if ((dmadesc->status & USB_DMADESC_STATUS_MASK) != USB_DMADESC_BEINGSERVICED)) + { + /* Re-trigger the DMA Transfer */ + + putreq32(1 << epphy, LPC17_USBDEV_DMARCLR); + putreq32(1 << epphy, LPC17_USBDEV_EPDMAEN); + } + } + return OK; +} +#endif /* CONFIG_LPC17_USBDEV_DMA */ + +/******************************************************************************* + * Name: lpc17_dmarestart + * + * Description: + * Restart DMA Transfer + * + *******************************************************************************/ + +#ifdef CONFIG_LPC17_USBDEV_DMA +static void lpc17_dmarestart(uint8_t epphy, uint32_t descndx) +{ + uint32_t reg; + + /* Clear DMA descriptor status */ + + USB_DmaDesc[descndx].status = 0; + + /* Enable DMA transfer on the endpoint */ + + lpc17_putreg(1 << epph, LPC17_USBDEV_EPDMAEN); + + /* Check the state of IN/OUT EP buffer */ + + uint32_t reg = lpc17_usbcmd(CMD_USBDEV_EPSELECT | epphy, 0); + if ((LPC17_EPPHYIN(epphy) && (reg & 0x60) == 0) || + (LPC17_EPPHYIN(epphy) && (reg & 0x60) == 0x60)) + { + /* Re-trigger the DMA Transfer */ + + putreq32(1 << epphy, LPC17_USBDEV_DMARCLR); + putreq32(1 << epphy, LPC17_USBDEV_EPDMAEN); + } +} +#endif /* CONFIG_LPC17_USBDEV_DMA */ + +/******************************************************************************* + * Name: lpc17_dmadisable + * + * Description: + * Disable DMA transfer for the EP + * + *******************************************************************************/ + +#ifdef CONFIG_LPC17_USBDEV_DMA +static void lpc17_dmadisable(uint8_t epphy) +{ + EPDMADIS = 1 << epphy; +} +#endif /* CONFIG_LPC17_USBDEV_DMA */ + +/******************************************************************************* + * Endpoint operations + *******************************************************************************/ + +/******************************************************************************* + * Name: lpc17_epconfigure + * + * Description: + * Configure endpoint, making it usable + * + * Input Parameters: + * ep - the struct usbdev_ep_s instance obtained from allocep() + * desc - A struct usb_epdesc_s instance describing the endpoint + * last - true if this this last endpoint to be configured. Some hardware + * needs to take special action when all of the endpoints have been + * configured. + * + *******************************************************************************/ + +static int lpc17_epconfigure(FAR struct usbdev_ep_s *ep, + FAR const struct usb_epdesc_s *desc, + bool last) +{ + FAR struct lpc17_ep_s *privep = (FAR struct lpc17_ep_s *)ep; + uint32_t inten; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + DEBUGASSERT(desc->addr == ep->eplog); + + /* Realize the endpoint */ + + lpc17_eprealize(privep, 1, GETUINT16(desc->mxpacketsize)); + + /* Enable and reset EP -- twice */ + + lpc17_usbcmd(CMD_USBDEV_EPSETSTATUS | privep->epphy, 0); + lpc17_usbcmd(CMD_USBDEV_EPSETSTATUS | privep->epphy, 0); + +#ifdef CONFIG_LPC17_USBDEV_DMA + /* Enable DMA Ep interrupt (WO) */ + + lpc17_putreg(1 << privep->epphy, LPC17_USBDEV_EPDMAEN); +#else + /* Enable Ep interrupt (R/W) */ + + inten = lpc17_getreg(LPC17_USBDEV_EPINTEN); + inten |= (1 << privep->epphy); + lpc17_putreg(inten, LPC17_USBDEV_EPINTEN); +#endif + + /* If all of the endpoints have been configured, then tell the USB controller + * to enabled normal activity on all realized endpoints. + */ + + if (last) + { + lpc17_usbcmd(CMD_USBDEV_CONFIG, 1); + } + return OK; +} + +/******************************************************************************* + * Name: lpc17_epdisable + * + * Description: + * The endpoint will no longer be used + * + *******************************************************************************/ + +static int lpc17_epdisable(FAR struct usbdev_ep_s *ep) +{ + FAR struct lpc17_ep_s *privep = (FAR struct lpc17_ep_s *)ep; + irqstate_t flags; + uint32_t mask = (1 << privep->epphy); + uint32_t reg; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + usbtrace(TRACE_EPDISABLE, privep->epphy); + + /* Cancel any ongoing activity */ + + flags = irqsave(); + lpc17_cancelrequests(privep); + + /* Disable endpoint and interrupt */ + + reg = lpc17_getreg(LPC17_USBDEV_REEP); + reg &= ~mask; + lpc17_putreg(reg, LPC17_USBDEV_REEP); + + lpc17_putreg(mask, LPC17_USBDEV_EPDMADIS); + + reg = lpc17_getreg(LPC17_USBDEV_EPINTEN); + reg &= ~mask; + lpc17_putreg(reg, LPC17_USBDEV_EPINTEN); + + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: lpc17_epallocreq + * + * Description: + * Allocate an I/O request + * + *******************************************************************************/ + +static FAR struct usbdev_req_s *lpc17_epallocreq(FAR struct usbdev_ep_s *ep) +{ + FAR struct lpc17_req_s *privreq; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + usbtrace(TRACE_EPALLOCREQ, ((FAR struct lpc17_ep_s *)ep)->epphy); + + privreq = (FAR struct lpc17_req_s *)malloc(sizeof(struct lpc17_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct lpc17_req_s)); + return &privreq->req; +} + +/******************************************************************************* + * Name: lpc17_epfreereq + * + * Description: + * Free an I/O request + * + *******************************************************************************/ + +static void lpc17_epfreereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct lpc17_req_s *privreq = (FAR struct lpc17_req_s *)req; + +#ifdef CONFIG_DEBUG + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + usbtrace(TRACE_EPFREEREQ, ((FAR struct lpc17_ep_s *)ep)->epphy); + + free(privreq); +} + +/******************************************************************************* + * Name: lpc17_epallocbuffer + * + * Description: + * Allocate an I/O buffer + * + *******************************************************************************/ + +#ifdef CONFIG_LPC17_USBDEV_DMA +static FAR void *lpc17_epallocbuffer(FAR struct usbdev_ep_s *ep, uint16_t nbytes) +{ + FAR struct lpc17_ep_s *privep = (FAR struct lpc17_ep_s *)ep; + int descndx; + + usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); + + /* Find a free DMA description */ + +#error "LOGIC INCOMPLETE" + + /* Set UDCA to the allocated DMA descriptor for this endpoint */ + + USB_UDCA[privep->epphy] = &USB_DDESC[descndx]; + return &USB_DDESC[descndx] +} +#endif + +/******************************************************************************* + * Name: lpc17_epfreebuffer + * + * Description: + * Free an I/O buffer + * + *******************************************************************************/ + +#ifdef CONFIG_LPC17_USBDEV_DMA +static void lpc17_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf) +{ + FAR struct lpc17_ep_s *privep = (FAR struct lpc17_ep_s *)ep; + + usbtrace(TRACE_EPFREEBUFFER, privep->epphy); + + /* Indicate that there is no DMA descriptor associated with this endpoint */ + + USB_UDCA[privep->epphy] = NULL; + + /* Mark the DMA descriptor as free for re-allocation */ + +#error "LOGIC INCOMPLETE" +} +#endif + +/******************************************************************************* + * Name: lpc17_epsubmit + * + * Description: + * Submit an I/O request to the endpoint + * + *******************************************************************************/ + +static int lpc17_epsubmit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct lpc17_req_s *privreq = (FAR struct lpc17_req_s *)req; + FAR struct lpc17_ep_s *privep = (FAR struct lpc17_ep_s *)ep; + FAR struct lpc17_usbdev_s *priv; + irqstate_t flags; + int ret = OK; + +#ifdef CONFIG_DEBUG + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + ullvdbg("req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf, ep); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPSUBMIT, privep->epphy); + priv = privep->dev; + + if (!priv->driver || priv->usbdev.speed == USB_SPEED_UNKNOWN) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_NOTCONFIGURED), priv->usbdev.speed); + return -ESHUTDOWN; + } + + /* Handle the request from the class driver */ + + req->result = -EINPROGRESS; + req->xfrd = 0; + flags = irqsave(); + + /* If we are stalled, then drop all requests on the floor */ + + if (privep->stalled) + { + lpc17_abortrequest(privep, privreq, -EBUSY); + ret = -EBUSY; + } + + /* Handle IN (device-to-host) requests */ + + else if (LPC17_EPPHYIN(privep->epphy)) + { + /* Add the new request to the request queue for the IN endpoint */ + + lpc17_rqenqueue(privep, privreq); + usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); + + /* If the IN endpoint FIFO is available, then transfer the data now */ + + if (privep->txbusy == 0) + { + ret = lpc17_wrrequest(privep); + } + } + + /* Handle OUT (host-to-device) requests */ + + else + { + /* Add the new request to the request queue for the OUT endpoint */ + + privep->txnullpkt = 0; + lpc17_rqenqueue(privep, privreq); + usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len); + + /* This there a incoming data pending the availability of a request? */ + + if (priv->rxpending) + { + ret = lpc17_rdrequest(privep); + priv->rxpending = 0; + } + } + + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: lpc17_epcancel + * + * Description: + * Cancel an I/O request previously sent to an endpoint + * + *******************************************************************************/ + +static int lpc17_epcancel(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct lpc17_ep_s *privep = (FAR struct lpc17_ep_s *)ep; + FAR struct lpc17_usbdev_s *priv; + irqstate_t flags; + +#ifdef CONFIG_DEBUG + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + usbtrace(TRACE_EPCANCEL, privep->epphy); + priv = privep->dev; + + flags = irqsave(); + lpc17_cancelrequests(privep); + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: lpc17_epstall + * + * Description: + * Stall or resume and endpoint + * + *******************************************************************************/ + +static int lpc17_epstall(FAR struct usbdev_ep_s *ep, bool resume) +{ + FAR struct lpc17_ep_s *privep = (FAR struct lpc17_ep_s *)ep; + irqstate_t flags; + + /* STALL or RESUME the endpoint */ + + flags = irqsave(); + usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy); + lpc17_usbcmd(CMD_USBDEV_EPSETSTATUS | privep->epphy, (resume ? 0 : CMD_SETSTAUS_ST)); + + /* If the endpoint of was resumed, then restart any queue write requests */ + + if (resume) + { + (void)lpc17_wrrequest(privep); + } + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Device operations + *******************************************************************************/ + +/******************************************************************************* + * Name: lpc17_allocep + * + * Description: + * Allocate an endpoint matching the parameters. + * + * Input Parameters: + * eplog - 7-bit logical endpoint number (direction bit ignored). Zero means + * that any endpoint matching the other requirements will suffice. The + * assigned endpoint can be found in the eplog field. + * in - true: IN (device-to-host) endpoint requested + * eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK, + * USB_EP_ATTR_XFER_INT} + * + *******************************************************************************/ + +static FAR struct usbdev_ep_s *lpc17_allocep(FAR struct usbdev_s *dev, uint8_t eplog, + bool in, uint8_t eptype) +{ + FAR struct lpc17_usbdev_s *priv = (FAR struct lpc17_usbdev_s *)dev; + uint32_t epset = LPC17_EPALLSET & ~LPC17_EPCTRLSET; + irqstate_t flags; + int epndx = 0; + + usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog); + + /* Ignore any direction bits in the logical address */ + + eplog = USB_EPNO(eplog); + + /* A logical address of 0 means that any endpoint will do */ + + if (eplog > 0) + { + /* Otherwise, we will return the endpoint structure only for the requested + * 'logical' endpoint. All of the other checks will still be performed. + * + * First, verify that the logical endpoint is in the range supported by + * by the hardware. + */ + + if (eplog >= LPC17_NLOGENDPOINTS) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADEPNO), (uint16_t)eplog); + return NULL; + } + + /* Convert the logical address to a physical OUT endpoint address and + * remove all of the candidate endpoints from the bitset except for the + * the IN/OUT pair for this logical address. + */ + + epset &= 3 << (eplog << 1); + } + + /* Get the subset matching the requested direction */ + + if (in) + { + epset &= LPC17_EPINSET; + } + else + { + epset &= LPC17_EPOUTSET; + } + + /* Get the subset matching the requested type */ + + switch (eptype) + { + case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ + epset &= LPC17_EPINTRSET; + break; + + case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ + epset &= LPC17_EPBULKSET; + break; + + case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ + epset &= LPC17_EPISOCSET; + break; + + case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */ + default: + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BADEPTYPE), (uint16_t)eptype); + return NULL; + } + + /* Is the resulting endpoint supported by the LPC17xx? */ + + if (epset) + { + /* Yes.. now see if any of the request endpoints are available */ + + flags = irqsave(); + epset &= priv->epavail; + if (epset) + { + /* Select the lowest bit in the set of matching, available endpoints */ + + for (epndx = 2; epndx < LPC17_NPHYSENDPOINTS; epndx++) + { + uint32_t bit = 1 << epndx; + if ((epset & bit) != 0) + { + /* Mark the IN/OUT endpoint no longer available */ + + priv->epavail &= ~(3 << (bit & ~1)); + irqrestore(flags); + + /* And return the pointer to the standard endpoint structure */ + + return &priv->eplist[epndx].ep; + } + } + /* Shouldn't get here */ + } + irqrestore(flags); + } + + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_NOEP), (uint16_t)eplog); + return NULL; +} + +/******************************************************************************* + * Name: lpc17_freeep + * + * Description: + * Free the previously allocated endpoint + * + *******************************************************************************/ + +static void lpc17_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep) +{ + FAR struct lpc17_usbdev_s *priv = (FAR struct lpc17_usbdev_s *)dev; + FAR struct lpc17_ep_s *privep = (FAR struct lpc17_ep_s *)ep; + irqstate_t flags; + + usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy); + + if (priv && privep) + { + /* Mark the endpoint as available */ + + flags = irqsave(); + priv->epavail |= (1 << privep->epphy); + irqrestore(flags); + } +} + +/******************************************************************************* + * Name: lpc17_getframe + * + * Description: + * Returns the current frame number + * + *******************************************************************************/ + +static int lpc17_getframe(struct usbdev_s *dev) +{ +#ifdef CONFIG_LPC17_USBDEV_FRAME_INTERRUPT + FAR struct lpc17_usbdev_s *priv = (FAR struct lpc17_usbdev_s *)dev; + + /* Return last valid value of SOF read by the interrupt handler */ + + usbtrace(TRACE_DEVGETFRAME, (uint16_t)priv->sof); + return priv->sof; +#else + /* Return the last frame number detected by the hardware */ + + usbtrace(TRACE_DEVGETFRAME, 0); + return (int)lpc17_usbcmd(CMD_USBDEV_READFRAMENO, 0); +#endif +} + +/******************************************************************************* + * Name: lpc17_wakeup + * + * Description: + * Tries to wake up the host connected to this device + * + *******************************************************************************/ + +static int lpc17_wakeup(struct usbdev_s *dev) +{ + uint8_t arg = CMD_STATUS_SUSPEND; + irqstate_t flags; + + usbtrace(TRACE_DEVWAKEUP, (uint16_t)g_usbdev.devstatus); + + flags = irqsave(); + if (DEVSTATUS_CONNECT(g_usbdev.devstatus)) + { + arg |= CMD_STATUS_CONNECT; + } + + lpc17_usbcmd(CMD_USBDEV_SETSTATUS, arg); + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: lpc17_selfpowered + * + * Description: + * Sets/clears the device selfpowered feature + * + *******************************************************************************/ + +static int lpc17_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + FAR struct lpc17_usbdev_s *priv = (FAR struct lpc17_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG + if (!dev) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/******************************************************************************* + * Name: lpc17_pullup + * + * Description: + * Software-controlled connect to/disconnect from USB host + * + *******************************************************************************/ + +static int lpc17_pullup(struct usbdev_s *dev, bool enable) +{ + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + /* The CMD_STATUS_CONNECT bit in the CMD_USBDEV_SETSTATUS command + * controls the LPC17xx SoftConnect_N output pin that is used for SoftConnect. + */ + + lpc17_usbcmd(CMD_USBDEV_SETSTATUS, (enable ? CMD_STATUS_CONNECT : 0)); + return OK; +} + +/******************************************************************************* + * Public Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: up_usbinitialize + * + * Description: + * Initialize USB hardware. + * + * Assumptions: + * - This function is called very early in the initialization sequence + * - PLL and GIO pin initialization is not performed here but should been in + * the low-level boot logic: PLL1 must be configured for operation at 48MHz + * and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect + * LED. + * + *******************************************************************************/ + +void up_usbinitialize(void) +{ + struct lpc17_usbdev_s *priv = &g_usbdev; + uint32_t reg; + int i; + + usbtrace(TRACE_DEVINIT, 0); + + /* Disable USB interrupts */ + + lpc17_putreg(0, LPC17_USBDEV_INTST); + + /* Initialize the device state structure */ + + memset(priv, 0, sizeof(struct lpc17_usbdev_s)); + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->eplist[LPC17_EP0_IN].ep; + priv->epavail = LPC17_EPALLSET; + + /* Initialize the endpoint list */ + + for (i = 0; i < LPC17_NPHYSENDPOINTS; i++) + { + uint32_t bit = 1 << i; + + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the physical endpoint number (which is just the index to the + * endpoint). + */ + priv->eplist[i].ep.ops = &g_epops; + priv->eplist[i].dev = priv; + + /* The index, i, is the physical endpoint address; Map this + * to a logical endpoint address usable by the class driver. + */ + + priv->eplist[i].epphy = i; + if (LPC17_EPPHYIN(i)) + { + priv->eplist[i].ep.eplog = LPC17_EPPHYIN2LOG(i); + } + else + { + priv->eplist[i].ep.eplog = LPC17_EPPHYOUT2LOG(i); + } + + /* The maximum packet size may depend on the type of endpoint */ + + if ((LPC17_EPCTRLSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = LPC17_EP0MAXPACKET; + } + else if ((LPC17_EPINTRSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = LPC17_INTRMAXPACKET; + } + else if ((LPC17_EPBULKSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = LPC17_BULKMAXPACKET; + } + else /* if ((LPC17_EPISOCSET & bit) != 0) */ + { + priv->eplist[i].ep.maxpacket = LPC17_ISOCMAXPACKET; + } + } + + /* Turn on USB power and clocking */ + + reg = lpc17_getreg(LPC17_PCON_PCONP); + reg |= LPC17_PCONP_PCUSB; + lpc17_putreg(reg, LPC17_PCON_PCONP); + + /* Attach USB controller interrupt handler */ + + if (irq_attach(LPC17_IRQ_USB, lpc17_usbinterrupt) != 0) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_IRQREGISTRATION), + (uint16_t)LPC17_IRQ_USB); + goto errout; + } + + /* Enable USB inerrupts at the controller -- but do not disable + * the ARM interrupt until the device is bound to the class + * driver + */ + + lpc17_putreg(USBDEV_INTST_ENUSBINTS, LPC17_USBDEV_INTST); + + /* Disconnect device */ + + lpc17_pullup(&priv->usbdev, false); + + /* Enable EP0 for OUT (host-to-device) */ + + lpc17_usbcmd(CMD_USBDEV_SETADDRESS, CMD_USBDEV_SETADDRESS_DEVEN|0); + lpc17_usbcmd(CMD_USBDEV_SETADDRESS, CMD_USBDEV_SETADDRESS_DEVEN|0); + + /* Reset/Re-initialize the USB hardware */ + + lpc17_usbreset(priv); + + /* Init Device state structure */ + + priv->devstatus = lpc17_usbcmd(CMD_USBDEV_GETSTATUS, 0); + return; + +errout: + up_usbuninitialize(); +} + +/******************************************************************************* + * Name: up_usbuninitialize + *******************************************************************************/ + +void up_usbuninitialize(void) +{ + struct lpc17_usbdev_s *priv = &g_usbdev; + uint32_t reg; + irqstate_t flags; + + usbtrace(TRACE_DEVUNINIT, 0); + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(priv->driver); + } + + /* Disconnect device */ + + flags = irqsave(); + lpc17_pullup(&priv->usbdev, false); + priv->usbdev.speed = USB_SPEED_UNKNOWN; + lpc17_usbcmd(CMD_USBDEV_CONFIG, 0); + + /* Disable and detach IRQs */ + + up_disable_irq(LPC17_IRQ_USB); + irq_detach(LPC17_IRQ_USB); + + /* Turn off USB power and clocking */ + + reg = lpc17_getreg(LPC17_PCON_PCONP); + reg &= ~LPC17_PCONP_PCUSB; + lpc17_putreg(reg, LPC17_PCON_PCONP); + irqrestore(flags); +} + +/******************************************************************************* + * Name: usbdev_register + * + * Description: + * Register a USB device class driver. The class driver's bind() method will be + * called to bind it to a USB device driver. + * + *******************************************************************************/ + +int usbdev_register(struct usbdevclass_driver_s *driver) +{ + int ret; + + usbtrace(TRACE_DEVREGISTER, 0); + +#ifdef CONFIG_DEBUG + if (!driver || !driver->ops->bind || !driver->ops->unbind || + !driver->ops->disconnect || !driver->ops->setup) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + g_usbdev.driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &g_usbdev.usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_BINDFAILED), (uint16_t)-ret); + g_usbdev.driver = NULL; + } + else + { + /* Enable USB controller interrupts */ + + up_enable_irq(LPC17_IRQ_USB); + } + return ret; +} + +/******************************************************************************* + * Name: usbdev_unregister + * + * Description: + * Un-register usbdev class driver.If the USB device is connected to a USB host, + * it will first disconnect(). The driver is also requested to unbind() and clean + * up any device state, before this procedure finally returns. + * + *******************************************************************************/ + +int usbdev_unregister(struct usbdevclass_driver_s *driver) +{ + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG + if (driver != g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(LPC17_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &g_usbdev.usbdev); + + /* Disable USB controller interrupts */ + + up_disable_irq(LPC17_IRQ_USB); + + /* Unhook the driver */ + + g_usbdev.driver = NULL; + return OK; +} diff --git a/nuttx/configs/nucleus2g/src/up_leds.c b/nuttx/configs/nucleus2g/src/up_leds.c index 6ac41ff41..a3babda36 100755 --- a/nuttx/configs/nucleus2g/src/up_leds.c +++ b/nuttx/configs/nucleus2g/src/up_leds.c @@ -89,6 +89,24 @@ * Private Data ****************************************************************************/ +/* The Nucleus2G has 3 LEDs... two on the Babel CAN board and a "heartbeat" LED." + * The LEDs on the Babel CAN board are capabl of OFF/GREEN/RED/AMBER status. + * In normal usage, the two LEDs on the Babel CAN board would show CAN status, but if + * CONFIG_ARCH_LEDS is defined, these LEDs will be controlled as follows for NuttX + * debug functionality (where NC means "No Change"). + * + * LED1 LED2 HEARTBEAT + * +------- ------ ----------------------- + * LED_STARTED | OFF OFF OFF + * LED_HEAPALLOCATE | GREEN OFF OFF + * LED_IRQSENABLED | OFF GREEN OFF + * LED_STACKCREATED | GREEN GREEN OFF + * LED_INIRQ | NC NC ON + * LED_SIGNAL | NC RED NC + * LED_ASSERTION | RED NC NC + * LED_PANIC | RED RED NC (1Hz flashing) + */ + static const uint8_t g_led1on[8] = { LED_OFF, LED_GREEN, LED_OFF, LED_GREEN, -- cgit v1.2.3