/*******************************************************************************
 * arch/arm/src/lpc214x/lpc214x_usbdev.c
 *
 *   Copyright (C) 2008 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *******************************************************************************/

/*******************************************************************************
 * Included Files
 *******************************************************************************/

#include <nuttx/config.h>
#include <sys/types.h>

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/arch.h>
#include <nuttx/usb.h>
#include <nuttx/usbdev.h>
#include <nuttx/usbdev_trace.h>

#include <arch/irq.h>
#include <arch/board/board.h>

#include "up_arch.h"
#include "up_internal.h"
#include "lpc214x_usbdev.h"
#include "lpc214x_pll.h"
#include "lpc214x_power.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_DEVINT_EPSLOW
#define USB_DEVSTATUS_INT USBDEV_DEVINT_DEVSTAT

#ifdef CONFIG_LPC214X_USBDEV_EPFAST_INTERRUPT
#  define USB_FAST_INT USBDEV_DEVINT_EPFAST
#else
#  define USB_FAST_INT 0
#endif

/* Extremely detailed register debug that you would normally never want
 * enabled.
 */

#undef CONFIG_LPC214X_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_LPC214X_USBDEV_FRAME_INTERRUPT
#  define USB_FRAME_INT USBDEV_DEVINT_FRAME
#else
#  define USB_FRAME_INT 0
#endif

#ifdef CONFIG_DEBUG
#  define USB_ERROR_INT USBDEV_DEVINT_EPRINT
#else
#  define USB_ERROR_INT 0
#endif

/* Number of DMA descriptors */

#ifdef CONFIG_LPC214X_USBDEV_DMA
# error DMA SUPPORT NOT YET FULLY IMPLEMENTED
#  ifndef CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS
#    define CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS 8
#  elif CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS > 30
#    define CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS 30
#  endif
#endif

/* Debug ***********************************************************************/

/* Trace error codes */

#define LPC214X_TRACEERR_ALLOCFAIL            0x0001
#define LPC214X_TRACEERR_BADCLEARFEATURE      0x0002
#define LPC214X_TRACEERR_BADDEVGETSTATUS      0x0003
#define LPC214X_TRACEERR_BADEPNO              0x0004
#define LPC214X_TRACEERR_BADEPGETSTATUS       0x0005
#define LPC214X_TRACEERR_BADEPTYPE            0x0006
#define LPC214X_TRACEERR_BADGETCONFIG         0x0007
#define LPC214X_TRACEERR_BADGETSETDESC        0x0008
#define LPC214X_TRACEERR_BADGETSTATUS         0x0009
#define LPC214X_TRACEERR_BADSETADDRESS        0x000a
#define LPC214X_TRACEERR_BADSETCONFIG         0x000b
#define LPC214X_TRACEERR_BADSETFEATURE        0x000c
#define LPC214X_TRACEERR_BINDFAILED           0x000d
#define LPC214X_TRACEERR_DISPATCHSTALL        0x000e
#define LPC214X_TRACEERR_DMABUSY              0x000f
#define LPC214X_TRACEERR_DRIVER               0x0010
#define LPC214X_TRACEERR_DRIVERREGISTERED     0x0011
#define LPC214X_TRACEERR_EP0INSTALLED         0x0012
#define LPC214X_TRACEERR_EP0OUTSTALLED        0x0013
#define LPC214X_TRACEERR_EP0SETUPSTALLED      0x0014
#define LPC214X_TRACEERR_EPINNULLPACKET       0x0015
#define LPC214X_TRACEERR_EPOUTNULLPACKET      0x0016
#define LPC214X_TRACEERR_EPREAD               0x0017
#define LPC214X_TRACEERR_INVALIDCMD           0x0018
#define LPC214X_TRACEERR_INVALIDCTRLREQ       0x0019
#define LPC214X_TRACEERR_INVALIDPARMS         0x001a
#define LPC214X_TRACEERR_IRQREGISTRATION      0x001b
#define LPC214X_TRACEERR_NODMADESC            0x001c
#define LPC214X_TRACEERR_NOEP                 0x001d
#define LPC214X_TRACEERR_NOTCONFIGURED        0x001e
#define LPC214X_TRACEERR_REQABORTED           0x001f

/* Trace interrupt codes */

#define LPC214X_TRACEINTID_USB                0x0001
#define LPC214X_TRACEINTID_CLEARFEATURE       0x0002
#define LPC214X_TRACEINTID_CONNECTCHG         0x0003
#define LPC214X_TRACEINTID_CONNECTED          0x0004
#define LPC214X_TRACEINTID_DEVGETSTATUS       0x0005
#define LPC214X_TRACEINTID_DEVRESET           0x0006
#define LPC214X_TRACEINTID_DEVSTAT            0x0007
#define LPC214X_TRACEINTID_DISCONNECTED       0x0008
#define LPC214X_TRACEINTID_DISPATCH           0x0009
#define LPC214X_TRACEINTID_EP0IN              0x000a
#define LPC214X_TRACEINTID_EP0OUT             0x000b
#define LPC214X_TRACEINTID_EP0SETUP           0x000c
#define LPC214X_TRACEINTID_EPDMA              0x000d
#define LPC214X_TRACEINTID_EPFAST             0x000e
#define LPC214X_TRACEINTID_EPGETSTATUS        0x000f
#define LPC214X_TRACEINTID_EPIN               0x0010
#define LPC214X_TRACEINTID_EPINQEMPTY         0x0011
#define LPC214X_TRACEINTID_EP0INSETADDRESS    0x0012
#define LPC214X_TRACEINTID_EPOUT              0x0013
#define LPC214X_TRACEINTID_EPOUTQEMPTY        0x0014
#define LPC214X_TRACEINTID_EP0SETUPSETADDRESS 0x0015
#define LPC214X_TRACEINTID_EPRINT             0x0016
#define LPC214X_TRACEINTID_EPSLOW             0x0017
#define LPC214X_TRACEINTID_FRAME              0x0018
#define LPC214X_TRACEINTID_GETCONFIG          0x0019
#define LPC214X_TRACEINTID_GETSETDESC         0x001a
#define LPC214X_TRACEINTID_GETSETIF           0x001b
#define LPC214X_TRACEINTID_GETSTATUS          0x001c
#define LPC214X_TRACEINTID_IFGETSTATUS        0x001d
#define LPC214X_TRACEINTID_SETCONFIG          0x001e
#define LPC214X_TRACEINTID_SETFEATURE         0x001f
#define LPC214X_TRACEINTID_SUSPENDCHG         0x0020
#define LPC214X_TRACEINTID_SYNCHFRAME         0x0021

/* Hardware interface **********************************************************/

/* Macros for testing the device status response */

#define DEVSTATUS_CONNECT(s)    (((s)&USBDEV_DEVSTATUS_CONNECT)!=0)
#define DEVSTATUS_CONNCHG(s)    (((s)&USBDEV_DEVSTATUS_CONNCHG)!=0)
#define DEVSTATUS_SUSPEND(s)    (((s)&USBDEV_DEVSTATUS_SUSPEND)!=0)
#define DEVSTATUS_SUSPCHG(s)    (((s)&USBDEV_DEVSTATUS_SUSPCHG)!=0)
#define DEVSTATUS_RESET(s)      (((s)&USBDEV_DEVSTATUS_RESET)!=0)

/* If this bit is set in the lpc214x_epread response, it means that the
 * recevied packet was overwritten by a later setup packet (ep0 only).
 */

#define LPC214X_READOVERRUN_BIT (0x80000000)
#define LPC214X_READOVERRUN(s)  (((s) & LPC214X_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*)LPC214X_USBDEV_RAMBASE)
#define USB_USCASIZE       (LPC214X_NPHYSENDPOINTS*sizeof(uint32))

/* 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((LPC214X_USBDEV_RAMSIZE-USB_USCASIZE)/CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS)
#define USB_DDESC  ((struct lpc214x_dmadesc_s*)(LPC214X_USBDEV_RAMBASE+USB_USCASIZE))

#ifdef CONFIG_USBDEV_ISOCHRONOUS
#  define USB_DDESCSIZE (5*sizeof(uint32))
#else
#  define USB_DDESCSIZE (4*sizeof(uint32))
#endif

/* Endpoints ******************************************************************/

/* Number of endpoints */

#define LPC214X_NLOGENDPOINTS        (16)          /* ep0-15 */
#define LPC214X_NPHYSENDPOINTS       (32)          /* x2 for IN and OUT */

/* Odd physical endpoint numbers are IN; even are out */

#define LPC214X_EPPHYIN(epphy)       (((epphy)&1)!=0)
#define LPC214X_EPPHYOUT(epphy)      (((epphy)&1)==0)

#define LPC214X_EPPHYIN2LOG(epphy)   (((ubyte)(epphy)>>1)|USB_DIR_IN)
#define LPC214X_EPPHYOUT2LOG(epphy)  (((ubyte)(epphy)>>1)|USB_DIR_OUT)

/* Each endpoint has somewhat different characteristics */

#define LPC214X_EPALLSET             (0xffffffff)  /* All endpoints */
#define LPC214X_EPOUTSET             (0x55555555)  /* Even phy endpoint numbers are OUT EPs */
#define LPC214X_EPINSET              (0xaaaaaaaa)  /* Odd endpoint numbers are IN EPs */
#define LPC214X_EPCTRLSET            (0x00000003)  /* EP0 IN/OUT are control endpoints */
#define LPC214X_EPINTRSET            (0x0c30c30c)  /* Interrupt endpoints */
#define LPC214X_EPBULKSET            (0xf0c30c30)  /* Bulk endpoints */
#define LPC214X_EPISOCSET            (0x030c30c0)  /* Isochronous endpoints */
#define LPC214X_EPDBLBUFFER          (0xf3cf3cf0)  /* Double buffered endpoints */

#define LPC214X_EP0MAXPACKET         (64)          /* EP0 max packet size (1-64) */
#define LPC214X_BULKMAXPACKET        (64)          /* Bulk endpoint max packet (8/16/32/64) */
#define LPC214X_INTRMAXPACKET        (64)          /* Interrupt endpoint max packet (1 to 64) */
#define LPC214X_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 LPC214X_EP0REQUEST           (0)           /* Normal request handling */
#define LPC214X_EP0STATUSIN          (1)           /* Status sent */
#define LPC214X_EP0STATUSOUT         (2)           /* Status received */
#define LPC214X_EP0SHORTWRITE        (3)           /* Short data sent with no request */
#define LPC214X_EP0SHORTWRSENT       (4)           /* Short data write complete */
#define LPC214X_EP0SETADDRESS        (5)           /* Set address received */
#define LPC214X_EP0WRITEREQUEST      (6)           /* EP0 write request sent */

/* Request queue operations ****************************************************/

#define lpc214x_rqempty(ep)          ((ep)->head == NULL)
#define lpc214x_rqpeek(ep)           ((ep)->head)

/*******************************************************************************
 * Private Types
 *******************************************************************************/

/* A container for a request so that the request make be retained in a list */

struct lpc214x_req_s
{
  struct usbdev_req_s    req;           /* Standard USB request */
  struct lpc214x_req_s  *flink;         /* Supports a singly linked list */
};

/* This is the internal representation of an endpoint */

struct lpc214x_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 lpc214x_ep_s.
   */

  struct usbdev_ep_s      ep;            /* Standard endpoint structure */

  /* LPC214X-specific fields */

  struct lpc214x_usbdev_s *dev;          /* Reference to private driver data */
  struct lpc214x_req_s    *head;         /* Request list for this endpoint */
  struct lpc214x_req_s    *tail;
  ubyte                   epphy;         /* Physical EP address */
  ubyte                   stalled:1;     /* 1: Endpoint is stalled */
  ubyte                   halted:1;      /* 1: Endpoint feature halted */
  ubyte                   txbusy:1;      /* 1: TX endpoint FIFO full */
  ubyte                   txnullpkt:1;   /* Null packet needed at end of transfer */
};

/* This represents a DMA descriptor */

#ifdef CONFIG_LPC214X_USBDEV_DMA
struct lpc214x_dmadesc_s
{
  uint32                  nextdesc;      /* Address of the next DMA descripto in RAM */
  uint32                  config;        /* Misc. bit encoded configuration information */
  uint32                  start;         /* DMA start address */
  uint32                  status;        /* Misc. bit encoded status inforamation */
#ifdef CONFIG_USBDEV_ISOCHRONOUS
  uint32                  size;          /* Isochronous packet size address */
#endif
  ubyte                   buffer[USB_DDSIZE-USB_DDESCSIZE];
};
#endif

/* This structure retains the state of the USB device controller */

struct lpc214x_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 structlpc214x_usbdev_s.
   */

  struct usbdev_s         usbdev;

  /* The bound device class driver */

  struct usbdevclass_driver_s *driver;

  /* LPC214X-specific fields */

  ubyte                   devstatus;     /* Last response to device status command */
  ubyte                   ep0state;      /* State of certain EP0 operations */
  ubyte                   paddr;         /* Address assigned by SETADDRESS */
  ubyte                   stalled:1;     /* 1: Protocol stalled */
  ubyte                   selfpowered:1; /* 1: Device is self powered */
  ubyte                   paddrset:1;    /* 1: Peripheral addr has been set */
  ubyte                   attached:1;    /* 1: Host attached */
  ubyte                   rxpending:1;   /* 1: RX pending */
  uint32                  softprio;      /* Bitset of high priority interrupts */
  uint32                  epavail;       /* Bitset of available endpoints */
#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT
  uint32                  sof;           /* Last start-of-frame */
#endif

  /* Allocated DMA descriptor */

#ifdef CONFIG_LPC214X_USBDEV_DMA
  struct lpc214x_dmadesc_s *dmadesc;
#endif

  /* The endpoint list */

  struct lpc214x_ep_s     eplist[LPC214X_NPHYSENDPOINTS];
};

/*******************************************************************************
 * Private Function Prototypes
 *******************************************************************************/

/* Register operations ********************************************************/

#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32 lpc214x_getreg(uint32 addr);
static void lpc214x_putreg(uint32 val, uint32 addr);
#else
# define lpc214x_getreg(addr)     getreg32(addr)
# define lpc214x_putreg(val,addr) putreg32(val,addr)
#endif

/* Command operations **********************************************************/

static uint32 lpc214x_usbcmd(uint16 cmd, ubyte data);

/* Request queue operations ****************************************************/

static FAR struct lpc214x_req_s *lpc214x_rqdequeue(FAR struct lpc214x_ep_s *privep);
static void lpc214x_rqenqueue(FAR struct lpc214x_ep_s *privep,
              FAR struct lpc214x_req_s *req);

/* Low level data transfers and request operations *****************************/

static void lpc214x_epwrite(ubyte epphy, const ubyte *data, uint32 nbytes);
static int  lpc214x_epread(ubyte epphy, ubyte *data, uint32 nbytes);
static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep,
              struct lpc214x_req_s *privreq, sint16 result);
static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 result);
static int  lpc214x_wrrequest(struct lpc214x_ep_s *privep);
static int  lpc214x_rdrequest(struct lpc214x_ep_s *privep);
static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep);

/* Interrupt handling **********************************************************/

static struct lpc214x_ep_s *lpc214x_epfindbyaddr(struct lpc214x_usbdev_s *priv,
              uint16 eplog);
static void lpc214x_eprealize(struct lpc214x_ep_s *privep, boolean prio,
              uint32 packetsize);
static ubyte lpc214x_epclrinterrupt(ubyte epphy);
static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv);
#ifdef CONFIG_LPC214X_USBDEV_DMA
static inline void lpc214x_dmareset(uint32 enable);
#endif
static void lpc214x_usbreset(struct lpc214x_usbdev_s *priv);
static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv,
              const struct usb_ctrlreq_s *ctrl);
static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv);
static inline void lpc214x_ep0dataoutinterrupt(struct lpc214x_usbdev_s *priv);
static inline void lpc214x_ep0dataininterrupt(struct lpc214x_usbdev_s *priv);
static int lpc214x_usbinterrupt(int irq, FAR void *context);

#ifdef CONFIG_LPC214X_USBDEV_DMA
static int  lpc214x_dmasetup(struct lpc214x_usbdev_s *priv, ubyte epphy,
              uint32 epmaxsize, uint32 nbytes, uint32 *isocpacket,
              boolean isochronous);
static void lpc214x_dmarestart(ubyte epphy, uint32 descndx);
static void lpc214x_dmadisable(ubyte epphy);
#endif /* CONFIG_LPC214X_USBDEV_DMA */

/* Endpoint operations *********************************************************/

static int  lpc214x_epconfigure(FAR struct usbdev_ep_s *ep,
              const struct usb_epdesc_s *desc, boolean last);
static int  lpc214x_epdisable(FAR struct usbdev_ep_s *ep);
static FAR struct usbdev_req_s *lpc214x_epallocreq(FAR struct usbdev_ep_s *ep);
static void lpc214x_epfreereq(FAR struct usbdev_ep_s *ep,
              FAR struct usbdev_req_s *);
#ifdef CONFIG_LPC214X_USBDEV_DMA
static FAR void *lpc214x_epallocbuffer(FAR struct usbdev_ep_s *ep,
              uint16 nbytes);
static void lpc214x_epfreebuffer(FAR struct usbdev_ep_s *ep, void *buf);
#endif
static int  lpc214x_epsubmit(FAR struct usbdev_ep_s *ep,
              struct usbdev_req_s *req);
static int  lpc214x_epcancel(FAR struct usbdev_ep_s *ep,
              struct usbdev_req_s *req);
static int  lpc214x_epstall(FAR struct usbdev_ep_s *ep, boolean resume);

/* USB device controller operations ********************************************/

static FAR struct usbdev_ep_s *lcp214x_allocep(FAR struct usbdev_s *dev,
              ubyte epno, boolean in, ubyte eptype);
static void lpc214x_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep);
static int  lpc214x_getframe(struct usbdev_s *dev);
static int  lpc214x_wakeup(struct usbdev_s *dev);
static int  lpc214x_selfpowered(struct usbdev_s *dev, boolean selfpowered);
static int  lpc214x_pullup(struct usbdev_s *dev, boolean 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 lpc214x_usbdev_s g_usbdev;

static const struct usbdev_epops_s g_epops =
{
  .configure   = lpc214x_epconfigure,
  .disable     = lpc214x_epdisable,
  .allocreq    = lpc214x_epallocreq,
  .freereq     = lpc214x_epfreereq,
#ifdef CONFIG_LPC214X_USBDEV_DMA
  .allocbuffer = lpc214x_epallocbuffer,
  .freebuffer  = lpc214x_epfreebuffer,
#endif
  .submit      = lpc214x_epsubmit,
  .cancel      = lpc214x_epcancel,
  .stall       = lpc214x_epstall,
};

static const struct usbdev_ops_s g_devops =
{
  .allocep     = lcp214x_allocep,
  .freeep      = lpc214x_freeep,
  .getframe    = lpc214x_getframe,
  .wakeup      = lpc214x_wakeup,
  .selfpowered = lpc214x_selfpowered,
  .pullup      = lpc214x_pullup,
};

/*******************************************************************************
 * Public Data
 *******************************************************************************/

/*******************************************************************************
 * Private Functions
 *******************************************************************************/

/*******************************************************************************
 * Name: lpc214x_getreg
 *
 * Description:
 *   Get the contents of an LPC214x register
 *
 *******************************************************************************/

#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32 lpc214x_getreg(uint32 addr)
{
  static uint32 prevaddr = 0;
  static uint32 preval = 0;
  static uint32 count = 0;

  /* Read the value from the register */

  uint32 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: lpc214x_putreg
 *
 * Description:
 *   Set the contents of an LPC214x register to a value
 *
 *******************************************************************************/

#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void lpc214x_putreg(uint32 val, uint32 addr)
{
  /* Show the register value being written */

  lldbg("%08x<-%08x\n", addr, val);

  /* Write the value */

  putreg32(val, addr);
}
#endif

/*******************************************************************************
 * Name: lpc214x_usbcmd
 *
 * Description:
 *   Transmit commands to the USB engine
 *
 *******************************************************************************/

static uint32 lpc214x_usbcmd(uint16 cmd, ubyte data)
{
  irqstate_t flags;
  uint32 tmp = 0;

  /* Disable interrupt and clear CDFULL and CCEMPTY interrupt status */

  flags = irqsave();
  lpc214x_putreg(USBDEV_DEVINT_CDFULL|USBDEV_DEVINT_CCEMTY, LPC214X_USBDEV_DEVINTCLR);

  /* Load command + WR in command code register */

  lpc214x_putreg(((cmd & 0xff) << 16) + CMD_USB_CMDWR, LPC214X_USBDEV_CMDCODE);

  /* Wait until the command register is empty (CCEMPTY != 0, command is accepted) */

  while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CCEMTY) == 0);

  /* Clear command register empty (CCEMPTY) interrupt */

  lpc214x_putreg(USBDEV_DEVINT_CCEMTY, LPC214X_USBDEV_DEVINTCLR);

  /* Determine next phase of the command */

  switch (cmd)
    {
      /* Write operations (1 byte of data) */

    case CMD_USB_DEV_SETADDRESS:
    case CMD_USB_DEV_CONFIG:
    case CMD_USB_DEV_SETMODE:
    case CMD_USB_DEV_SETSTATUS:
      {
        /* Send data + WR and wait for CCEMPTY */

        lpc214x_putreg((data << 16) + CMD_USB_DATAWR, LPC214X_USBDEV_CMDCODE);
        while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CCEMTY) == 0);
      }
      break;

      /* 16 bit read operations */

    case CMD_USB_DEV_READFRAMENO:
    case CMD_USB_DEV_READTESTREG:
      {
        /* Send command code + RD and wait for CDFULL */

        lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, LPC214X_USBDEV_CMDCODE);
        while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

        /* Clear CDFULL and read LS data */

        lpc214x_putreg(USBDEV_DEVINT_CDFULL, LPC214X_USBDEV_DEVINTCLR);
        tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA);

        /* Send command code + RD and wait for CDFULL */

        lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, LPC214X_USBDEV_CMDCODE);
        while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

        /* Read MS data */

        tmp |= lpc214x_getreg(LPC214X_USBDEV_CMDDATA) << 8;
      }
      break;

     /* 8-bit read operations */

    case CMD_USB_DEV_GETSTATUS:
    case CMD_USB_DEV_GETERRORCODE:
    case CMD_USB_DEV_READERRORSTATUS:
    case CMD_USB_EP_CLRBUFFER:
      {
        /* Send command code + RD and wait for CDFULL */

        lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, LPC214X_USBDEV_CMDCODE);
        while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

        /* Read data */

        tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA);
      }
      break;

      /* No data transfer */

    case CMD_USB_EP_VALIDATEBUFFER:
      break;

    default:
      switch (cmd & 0x1e0)
        {
        case CMD_USB_EP_SELECT:
        case CMD_USB_EP_SELECTCLEAR:
          {
            /* Send command code + RD and wait for CDFULL */

            lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, LPC214X_USBDEV_CMDCODE);
            while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

            /* Read data */

            tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA);
          }
          break;

        case CMD_USB_EP_SETSTATUS:
          {
            /* Send data + RD and wait for CCEMPTY */

            lpc214x_putreg((data << 16) + CMD_USB_DATAWR, LPC214X_USBDEV_CMDCODE);
            while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CCEMTY) == 0);
          }
          break;

        default:
          usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDCMD), 0);
          break;
        }
      break;
    }

  /* Restore the interrupt flags */

  irqrestore(flags);
  return tmp;
}

/*******************************************************************************
 * Name: lpc214x_rqdequeue
 *
 * Description:
 *   Remove a request from an endpoint request queue
 *
 *******************************************************************************/

static FAR struct lpc214x_req_s *lpc214x_rqdequeue(FAR struct lpc214x_ep_s *privep)
{
  FAR struct lpc214x_req_s *ret = privep->head;

  if (ret)
    {
      privep->head = ret->flink;
      if (!privep->head)
        {
          privep->tail = NULL;
        }

      ret->flink = NULL;
    }

  return ret;
}

/*******************************************************************************
 * Name: lpc214x_rqenqueue
 *
 * Description:
 *   Add a request from an endpoint request queue
 *
 *******************************************************************************/

static void lpc214x_rqenqueue(FAR struct lpc214x_ep_s *privep,
                              FAR struct lpc214x_req_s *req)
{
  req->flink = NULL;
  if (!privep->head)
    {
      privep->head = req;
      privep->tail = req;
    }
  else
    {
      privep->tail->flink = req;
      privep->tail        = req;
    }
}

/*******************************************************************************
 * Name: lpc214x_epwrite
 *
 * Description:
 *   Endpoint write (IN)
 *
 *******************************************************************************/

static void lpc214x_epwrite(ubyte epphy, const ubyte *data, uint32 nbytes)
{
  uint32 value;
  boolean aligned = (((uint32)data & 3) == 0);

  /* Set the write enable bit for this physical EP address. Bits 2-5 are
   * the logical endpoint number (0-15)
   */

  lpc214x_putreg(((epphy << 1) & LPC214X_USBCTRL_EPMASK) | LPC214X_USBCTRL_WREN,
                 LPC214X_USBDEV_CTRL);

  /* Set the transmit packet length (nbytes must be less than 2048) */

  lpc214x_putreg(nbytes, LPC214X_USBDEV_TXPLEN);

  /* Transfer the packet data */

  do
    {
      /* Zero length packets are a special case */

      if (nbytes)
        {
          if (aligned)
            {
              value = *(uint32*)data;
            }
          else
            {
              value =  (uint32)data[0]        | ((uint32)data[1] << 8) |
                      ((uint32)data[2] << 16) | ((uint32)data[3] << 24);
            }

          lpc214x_putreg(value, LPC214X_USBDEV_TXDATA);
          data += 4;
        }
      else
        {
          /* Zero length packet */

          lpc214x_putreg(0, LPC214X_USBDEV_TXDATA);
        }
    }
  while ((lpc214x_getreg(LPC214X_USBDEV_CTRL) & LPC214X_USBCTRL_WREN) != 0);

  /* Done */

  lpc214x_putreg(0, LPC214X_USBDEV_CTRL);
  (void)lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0);
  (void)lpc214x_usbcmd(CMD_USB_EP_VALIDATEBUFFER, 0);
}

/*******************************************************************************
 * Name: lpc214x_epread
 *
 * Description:
 *   Endpoint read (OUT)
 *
 *******************************************************************************/

static int lpc214x_epread(ubyte epphy, ubyte *data, uint32 nbytes)
{
  uint32 pktlen;
  uint32 result;
  uint32 value;
  ubyte  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)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).
   */

  lpc214x_putreg(((epphy << 1) & LPC214X_USBCTRL_EPMASK) | LPC214X_USBCTRL_RDEN,
                 LPC214X_USBDEV_CTRL);

  /* Wait for packet buffer ready for reading */

  while ((lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & USBDEV_RXPLEN_PKTRDY) == 0);

  /* Get the number of bytes of data to be read */

  pktlen = lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & USBDEV_RXPLEN_PKTLENGTH;

  /* Read data from input buffer while read data is valid (DV) */

  while ((lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & USBDEV_RXPLEN_DV) != 0)
    {
      value = lpc214x_getreg(LPC214X_USBDEV_RXDATA);
      if (aligned == 1)
        {
          *(uint32*)data = value;
          data += 4;
        }
      else if (aligned == 2)
        {
          *data++ = (ubyte)value;
          *data++ = (ubyte)(value >> 8);
          *data++ = (ubyte)(value >> 16);
          *data++ = (ubyte)(value >> 24);
        }
    }

  /* Done */

  lpc214x_putreg(0, LPC214X_USBDEV_CTRL);
  (void)lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0);
  result = lpc214x_usbcmd(CMD_USB_EP_CLRBUFFER, 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 == LPC214X_EP0_OUT && (result & CMD_USB_CLRBUFFER_PO) != 0)
    {
      /* Pass this information in bit 31 */

      pktlen |= LPC214X_READOVERRUN_BIT;
    }
  return pktlen;
}

/*******************************************************************************
 * Name: lpc214x_abortrequest
 *
 * Description:
 *   Discard a request
 *
 *******************************************************************************/

static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep,
                                        struct lpc214x_req_s *privreq,
                                        sint16 result)
{
  usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_REQABORTED), (uint16)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: lpc214x_reqcomplete
 *
 * Description:
 *   Handle termination of the request at the head of the endpoint request queue.
 *
 *******************************************************************************/

static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 result)
{
  struct lpc214x_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 = lpc214x_rqdequeue(privep);
  irqrestore(flags);

  if (privreq)
    {
      /* If endpoint 0, temporarily reflect the state of protocol stalled
       * in the callback.
       */

      if (privep->epphy == LPC214X_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: lpc214x_wrrequest
 *
 * Description:
 *   Send from the next queued write request
 *
 *******************************************************************************/

static int lpc214x_wrrequest(struct lpc214x_ep_s *privep)
{
  struct lpc214x_req_s *privreq;
  ubyte *buf;
  int nbytes;
  int bytesleft;

  /* Check the request from the head of the endpoint request queue */

  privreq = lpc214x_rqpeek(privep);
  if (!privreq)
    {
      usbtrace(TRACE_INTDECODE(LPC214X_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 == LPC214X_EP0_IN)
        {
          lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0);
        }
      else
        {
          usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EPINNULLPACKET), 0);
        }

      /* In any event, the request is complete */

      lpc214x_reqcomplete(privep, OK);
      return OK;
    }

  /* 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;
      lpc214x_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;
      lpc214x_reqcomplete(privep, OK);
    }

  return OK;
}

/*******************************************************************************
 * Name: lpc214x_rdrequest
 *
 * Description:
 *   Receive to the next queued read request
 *
 *******************************************************************************/

static int lpc214x_rdrequest(struct lpc214x_ep_s *privep)
{
  struct lpc214x_req_s *privreq;
  ubyte *buf;
  int nbytesread;

  /* Check the request from the head of the endpoint request queue */

  privreq = lpc214x_rqpeek(privep);
  if (!privreq)
    {
      usbtrace(TRACE_INTDECODE(LPC214X_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(LPC214X_TRACEERR_EPOUTNULLPACKET), 0);
      lpc214x_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 = lpc214x_epread(privep->epphy, buf, privep->ep.maxpacket);
  if (nbytesread < 0)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_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);
      lpc214x_reqcomplete(privep, OK);
    }

  return OK;
}

/*******************************************************************************
 * Name: lpc214x_cancelrequests
 *
 * Description:
 *   Cancel all pending requests for an endpoint
 *
 *******************************************************************************/

static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep)
{
  while (!lpc214x_rqempty(privep))
    {
      usbtrace(TRACE_COMPLETE(privep->epphy),
               (lpc214x_rqpeek(privep))->req.xfrd);
      lpc214x_reqcomplete(privep, -ESHUTDOWN);
    }
}

/*******************************************************************************
 * Name: lpc214x_epfindbyaddr
 *
 * Description:
 *   Find the physical endpoint structure corresponding to a logic endpoint
 *   address
 *
 *******************************************************************************/

static struct lpc214x_ep_s *lpc214x_epfindbyaddr(struct lpc214x_usbdev_s *priv,
              uint16 eplog)
{
  struct lpc214x_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 < LPC214X_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: lpc214x_eprealize
 *
 * Description:
 *   Enable or disable an endpoint
 *
 *******************************************************************************/

static void lpc214x_eprealize(struct lpc214x_ep_s *privep, boolean prio, uint32 packetsize)
{
  struct lpc214x_usbdev_s *priv = privep->dev;
  uint32 mask;
  uint32 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 */

  lpc214x_putreg(USBDEV_DEVINT_EPRLZED, LPC214X_USBDEV_DEVINTCLR);

  /* Realize the endpoint */

  reg  = lpc214x_getreg(LPC214X_USBDEV_REEP);
  reg |= (1 << privep->epphy);
  lpc214x_putreg(reg, LPC214X_USBDEV_REEP);

  /* Set endpoint maximum packet size */

  lpc214x_putreg(privep->epphy, LPC214X_USBDEV_EPIND);
  lpc214x_putreg(packetsize, LPC214X_USBDEV_MAXPSIZE);

  /* Wait for Realize complete */

  while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_EPRLZED) == 0);

  /* Clear realize interrupt bit */

  lpc214x_putreg(USBDEV_DEVINT_EPRLZED,LPC214X_USBDEV_DEVINTCLR);
}

/*******************************************************************************
 * Name: lpc214x_epclrinterrupt
 *
 * Description:
 *   Clear the EP interrupt flag and return the current EP status
 *
 *******************************************************************************/

static ubyte lpc214x_epclrinterrupt(ubyte epphy)
{
  /* Clear the endpoint interrupt */

  lpc214x_putreg(1 << epphy, LPC214X_USBDEV_EPINTCLR);

  /* Wait for data in the command data register */

  while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & USBDEV_DEVINT_CDFULL) == 0);

  /* Return the value of the command data register */

  return lpc214x_getreg(LPC214X_USBDEV_CMDDATA);
}

/*******************************************************************************
 * Name: lpc214x_ep0configure
 *
 * Description:
 *   Configure endpoint 0
 *
 *******************************************************************************/

static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv)
{
  uint32 inten;

  /* EndPoint 0 initialization */

  lpc214x_eprealize(&priv->eplist[LPC214X_CTRLEP_OUT], 0, CONFIG_USBDEV_EP0_MAXSIZE);
  lpc214x_eprealize(&priv->eplist[LPC214X_CTRLEP_IN], 1, CONFIG_USBDEV_EP0_MAXSIZE);

  /* Enable EP0 interrupts (not DMA) */

  inten = lpc214x_getreg(LPC214X_USBDEV_EPINTEN);
  inten |= 3; /* EP0 Rx and Tx */
  lpc214x_putreg(inten, LPC214X_USBDEV_EPINTEN);
}

/*******************************************************************************
 * Name: lpc214x_dmareset
 *
 * Description: Reset USB DMA
 *
 *******************************************************************************/

#ifdef CONFIG_LPC214X_USBDEV_DMA
static inline void lpc214x_dmareset(uint32 enable)
{
  int i;

  /* Disable All DMA interrupts */

  lpc214x_putreg(0, LPC214X_USBDEV_DMAINTEN);

  /* DMA Disable */

  lpc214x_putreg(0xffffffff, LPC214X_USBDEV_EPDMADIS);

  /* DMA Request clear */

  putreq32(0xffffffff, LPC214X_USBDEV_DMARCLR);

  /* End of Transfer Interrupt Clear */

  putreq32(0xffffffff, LPC214X_USBDEV_EOTINTCLR);

  /* New DD Request Interrupt Clear */

  putreq32(0xffffffff, LPC214X_USBDEV_NDDRINTCLR);

  /* System Error Interrupt Clear */

  putreq32(0xffffffff, LPC214X_USBDEV_SYSERRINTCLR);

  /* Nullify all pointers in the UDCA */

  for (i = 0; i < LPC214X_NPHYSENDPOINTS; ++i)
    {
      USB_UDCA[i] = NULL;
    }

  /* Set USB UDCA Head register */

  lpc214x_putreg((uint32)USB_UDCA, LPC214X_USBDEV_UDCAH);

  /* Invalidate all DMA descriptors */

  for (i = 0; i < CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS; ++i)
    {
      memset(&USB_DDESC[i], 0, USB_DDESCSIZE);
    }

  /* Enable DMA interrupts */

  lpc214x_putreg(enable, LPC214X_USBDEV_DMAINTEN);
}
#endif

/*******************************************************************************
 * Name: lpc214x_usbreset
 *
 * Description:
 *   Reset Usb engine
 *
 *******************************************************************************/

static void lpc214x_usbreset(struct lpc214x_usbdev_s *priv)
{
  /* Disable all endpoint interrupts */

  lpc214x_putreg(0, LPC214X_USBDEV_EPINTEN);

  /* Frame is Hp interrupt */

  lpc214x_putreg(1, LPC214X_USBDEV_DEVINTPRI);

  /* Clear all pending interrupts */

  lpc214x_putreg(0xffffffff, LPC214X_USBDEV_EPINTCLR);
  lpc214x_putreg(0xffffffff, LPC214X_USBDEV_DEVINTCLR);

  /* Periperhal address is needed */

  priv->paddrset = 0;

  /* Endpoints not yet configured */

  lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);

  /* EndPoint 0 initialization */

  lpc214x_ep0configure(priv);

#ifdef CONFIG_LPC214X_USBDEV_DMA
  /* Enable End_of_Transfer_Interrupt and System_Error_Interrupt USB DMA
   * interrupts
   */

  lpc214x_dmareset(CONFIG_LPC214X_USBDEV_DMAINTMASK);

#endif

  /* Enable Device interrupts */

  lpc214x_putreg(USB_SLOW_INT|USB_DEVSTATUS_INT|USB_FAST_INT|USB_FRAME_INT|USB_ERROR_INT,
                 LPC214X_USBDEV_DEVINTEN);
}

/*******************************************************************************
 * Name: lpc214x_dispatchrequest
 *
 * Description:
 *   Provide unhandled setup actions to the class driver. This is logically part
 *   of the USB interrupt handler.
 *
 *******************************************************************************/

static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv,
                                    const struct usb_ctrlreq_s *ctrl)
{
  int ret;

  usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DISPATCH), 0);
  if (priv && priv->driver)
    {
      /* Forward to the control request to the class driver implementation */

      ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl);
      if (ret < 0)
        {
          /* Stall on failure */

          usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DISPATCHSTALL), 0);
          priv->stalled = 1;
        }
    }
}

/*******************************************************************************
 * Name: lpc214x_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 lpc214x_ep0setup(struct lpc214x_usbdev_s *priv)
{
  struct lpc214x_ep_s *ep0 = &priv->eplist[LPC214X_EP0_OUT];
  struct lpc214x_ep_s *privep;
  struct lpc214x_req_s *privreq = lpc214x_rqpeek(ep0);
  struct usb_ctrlreq_s ctrl;
  uint16 value;
  uint16 index;
  uint16 len;
  ubyte  response[2];
  int    ret;

  /* Starting a control request? */

  if (priv->usbdev.speed == USB_SPEED_UNKNOWN)
    {
      priv->usbdev.speed = USB_SPEED_FULL;
      lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 1);
    }

  /* Terminate any pending requests */

  while (!lpc214x_rqempty(ep0))
    {
      sint16 result = OK;
      if (privreq->req.xfrd != privreq->req.len)
        {
          result = -EPROTO;
        }

      usbtrace(TRACE_COMPLETE(ep0->epphy), privreq->req.xfrd);
      lpc214x_reqcomplete(ep0, result);
    }

  /* Assume NOT stalled */

  ep0->stalled  = 0;
  priv->stalled = 0;

  /* Read EP0 data */

  ret = lpc214x_epread(LPC214X_EP0_OUT, (ubyte*)&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)
    {
      lpc214x_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(LPC214X_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(LPC214X_TRACEINTID_EPGETSTATUS), 0);
                  privep = lpc214x_epfindbyaddr(priv, index);
                  if (!privep)
                    {
                      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADEPGETSTATUS), 0);
                      priv->stalled = 1;
                    }
                  else
                    {
                       if ((lpc214x_usbcmd(CMD_USB_EP_SELECT|privep->epphy, 0) & CMD_USB_EPSELECT_ST) != 0)
                         {
                           response[0] = 1; /* Stalled */
                         }
                       else
                         {
                           response[0] = 0; /* Not stalled */
                         }
                      response[1] = 0;
                      lpc214x_epwrite(LPC214X_EP0_IN, response, 2);
                      priv->ep0state = LPC214X_EP0SHORTWRITE;
                    }
                }
                break;

              case USB_REQ_RECIPIENT_DEVICE:
                {
                  if (index == 0)
                    {
                      usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVGETSTATUS), 0);

                      /* Features:  Remote Wakeup=YES; selfpowered=? */

                      response[0] = (priv->selfpowered << USB_FEATURE_SELFPOWERED) |
                                    (1 << USB_FEATURE_REMOTEWAKEUP);
                      response[1] = 0;
                      lpc214x_epwrite(LPC214X_EP0_IN, response, 2);
                      priv->ep0state = LPC214X_EP0SHORTWRITE;
                    }
                  else
                    {
                      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADDEVGETSTATUS), 0);
                      priv->stalled = 1;
                    }
                }
                break;

              case USB_REQ_RECIPIENT_INTERFACE:
                {
                  usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_IFGETSTATUS), 0);
                  response[0] = 0;
                  response[1] = 0;
                  lpc214x_epwrite(LPC214X_EP0_IN, response, 2);
                  priv->ep0state = LPC214X_EP0SHORTWRITE;
                }
                break;

              default:
                {
                  usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_TRACEINTID_CLEARFEATURE), 0);
        if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT)
          {
            lpc214x_dispatchrequest(priv, &ctrl);
          }
        else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
                 (privep = lpc214x_epfindbyaddr(priv, index)) != NULL)
          {
            privep->halted = 0;
            ret = lpc214x_epstall(&privep->ep, TRUE);
            lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0);
            priv->ep0state = LPC214X_EP0STATUSIN;
          }
        else
          {
            usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_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)
          {
           lpc214x_dispatchrequest(priv, &ctrl);
          }
        else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
                 (privep = lpc214x_epfindbyaddr(priv, index)) != NULL)
          {
            privep->halted = 1;
            lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0);
            priv->ep0state = LPC214X_EP0STATUSIN;
          }
        else
          {
            usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_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.
             */

            lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, CMD_USB_SETADDRESS_DEVEN | priv->paddr);

            /* Send a NULL packet. The status phase completes when the null packet has
             * been sent successfully.
             */

            lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0);
            priv->ep0state = LPC214X_EP0SETADDRESS;
          }
        else
          {
            usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_TRACEINTID_GETSETDESC), 0);
        if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE)
          {
            lpc214x_dispatchrequest(priv, &ctrl);
          }
        else
          {
            usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_TRACEINTID_GETCONFIG), 0);
        if (priv->paddrset && (ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
            value == 0 && index == 0 && len == 1)
          {
            lpc214x_dispatchrequest(priv, &ctrl);
          }
        else
          {
            usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_TRACEINTID_SETCONFIG), 0);
        if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
            index == 0 && len == 0)
          {
            lpc214x_dispatchrequest(priv, &ctrl);
          }
        else
          {
            usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_TRACEINTID_GETSETIF), 0);
        lpc214x_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(LPC214X_TRACEINTID_SYNCHFRAME), 0);
      }
      break;

    default:
      {
        usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDCTRLREQ), 0);
        priv->stalled = 1;
      }
      break;
    }

  if (priv->stalled)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0SETUPSTALLED), priv->ep0state);
      lpc214x_epstall(&ep0->ep, FALSE);
      lpc214x_epstall(&ep0->ep, FALSE);
    }
}

/*******************************************************************************
 * Name: lpc214x_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 lpc214x_ep0dataoutinterrupt(struct lpc214x_usbdev_s *priv)
{
  struct lpc214x_ep_s *ep0;
  uint32 pktlen;

  /* Copy new setup packet into setup buffer */

  switch (priv->ep0state)
    {
    case LPC214X_EP0SHORTWRITE:
      {
        priv->ep0state = LPC214X_EP0STATUSOUT;
        pktlen = lpc214x_epread(LPC214X_EP0_OUT, NULL, CONFIG_USBDEV_EP0_MAXSIZE);
        if (LPC214X_READOVERRUN(pktlen))
          {
            lpc214x_ep0setup(priv);
          }
      }
      break;

    case LPC214X_EP0SHORTWRSENT:
      {
        priv->ep0state = LPC214X_EP0REQUEST;
        pktlen = lpc214x_epread(LPC214X_EP0_OUT, NULL, CONFIG_USBDEV_EP0_MAXSIZE);
        if (LPC214X_READOVERRUN(pktlen))
          {
            lpc214x_ep0setup(priv);
          }
      }
      break;

    case LPC214X_EP0REQUEST:
      {
        /* Process the next request action (if any) */

        lpc214x_rdrequest(&priv->eplist[LPC214X_EP0_OUT]);
      }
      break;

    default:
      priv->stalled = 1;
      break;
    }

  if (priv->stalled)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0OUTSTALLED), priv->ep0state);
      ep0 = &priv->eplist[LPC214X_EP0_OUT];
      lpc214x_epstall(&ep0->ep, FALSE);
      lpc214x_epstall(&ep0->ep, FALSE);
    }
  return;
}

/*******************************************************************************
 * Name: lpc214x_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 lpc214x_ep0dataininterrupt(struct lpc214x_usbdev_s *priv)
{
  struct lpc214x_ep_s *ep0;

  switch (priv->ep0state)
    {
    case LPC214X_EP0STATUSOUT:
    case LPC214X_EP0STATUSIN:
      priv->ep0state = LPC214X_EP0REQUEST;
      break;

    case LPC214X_EP0SHORTWRITE:
      priv->ep0state = LPC214X_EP0SHORTWRSENT;
      break;

    case LPC214X_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(LPC214X_TRACEINTID_EP0INSETADDRESS), (uint16)priv->paddr);
        lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);
        if (priv->paddr)
          {
            priv->paddrset = 1;
            priv->ep0state = LPC214X_EP0REQUEST;
          }
      }
      break;

    case LPC214X_EP0REQUEST:
      {
        /* Process the next request action (if any) */

        ep0 = &priv->eplist[LPC214X_EP0_IN];
        ep0->txbusy = 0;
        lpc214x_wrrequest(ep0);
      }
      break;

    default:
      priv->stalled = 1;
      break;
    }

  if (priv->stalled)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0INSTALLED), priv->ep0state);
      ep0 = &priv->eplist[LPC214X_EP0_IN];
      lpc214x_epstall(&ep0->ep, FALSE);
      lpc214x_epstall(&ep0->ep, FALSE);
    }
}

/*******************************************************************************
 * Name: lpc214x_usbinterrupt
 *
 * Description:
 *   USB interrupt handler
 *
 *******************************************************************************/

static int lpc214x_usbinterrupt(int irq, FAR void *context)
{
  struct lpc214x_usbdev_s *priv = &g_usbdev;
  struct lpc214x_ep_s *privep ;

  uint32 devintstatus;  /* Sampled state of the device interrupt status register */
  uint32 epintstatus;   /* Sampled state of the endpoint interrupt status register */
#ifdef CONFIG_LPC214X_USBDEV_DMA
  uint32 dmaintstatus;  /* Sampled state of dma interrupt status register */
#endif
  uint32 softprio;      /* Current priority interrupt bitset */
  uint32 pending;       /* Pending subset of priority interrupt bitset */
  ubyte  epphy;         /* Physical endpoint number being processed */
  int    i;

  usbtrace(TRACE_INTENTRY(LPC214X_TRACEINTID_USB), 0);

  /* Read the device interrupt status register */

  devintstatus = lpc214x_getreg(LPC214X_USBDEV_DEVINTST);

#ifdef CONFIG_LPC214X_USBDEV_DMA
  /* Check for low priority and high priority (non-DMA) interrupts */

  if ((lpc214x_getreg(LPC214X_USBDEV_INTST) & (USBDEV_INTST_REQLP|USBDEV_INTST_REQHP)) != 0)
    {
#endif
#ifdef CONFIG_LPC214X_USBDEV_EPFAST_INTERRUPT
      /* Fast EP interrupt */

      if ((devintstatus & USBDEV_DEVINT_EPFAST) != 0)
        {
          /* Clear Fast EP interrupt */

         lpc214x_putreg(USBDEV_DEVINT_EPFAST, LPC214X_USBDEV_DEVINTCLR);
         usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPFAST), 0);

         /* Do what? */
        }

#endif

#if CONFIG_DEBUG
      /* USB engine error interrupt */

      if ((devintstatus & USBDEV_DEVINT_EPRINT))
        {
          ubyte errcode;

          /* Clear the error interrupt */

          lpc214x_putreg(USBDEV_DEVINT_EPRINT, LPC214X_USBDEV_DEVINTCLR);

          /* And show what error occurred */

          errcode  = (ubyte)lpc214x_usbcmd(CMD_USB_DEV_READERRORSTATUS, 0) & 0x0f;
          usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPRINT), (uint16)errcode);
        }
#endif

#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT
      /* Frame interrupt */

      if ((devintstatus & USBDEV_DEVINT_FRAME) != 0)
        {
          /* Clear the frame interrupt */

          lpc214x_putreg(USBDEV_DEVINT_FRAME, LPC214X_USBDEV_DEVINTCLR);
          usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_FRAME), 0);

          /* Then read the start of frame value */

          priv->sof = (uint16)lpc214x_usbcmd(CMD_USB_DEV_READFRAMENO, 0);
        }
#endif

      /* Device Status interrupt */

      if ((devintstatus & USBDEV_DEVINT_DEVSTAT) != 0)
        {
          /* Clear Device status interrupt */

          lpc214x_putreg(USBDEV_DEVINT_DEVSTAT, LPC214X_USBDEV_DEVINTCLR);

          /* Get device status */

          g_usbdev.devstatus = (ubyte)lpc214x_usbcmd(CMD_USB_DEV_GETSTATUS, 0);
          usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVSTAT), (uint16)g_usbdev.devstatus);

          /* Device connection status */

          if (DEVSTATUS_CONNCHG(g_usbdev.devstatus))
            {
              usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_CONNECTCHG),
                       (uint16)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(LPC214X_TRACEINTID_CONNECTED),
                                (uint16)g_usbdev.devstatus);
                       priv->usbdev.speed = USB_SPEED_UNKNOWN;
                       lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);
                       priv->attached     = 1;
                    }
                 }

               /* Otherwise the host is not attached */

               else if (priv->attached)
                 {
                   usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DISCONNECTED),
                            (uint16)g_usbdev.devstatus);
                   priv->usbdev.speed = USB_SPEED_UNKNOWN;
                   lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);
                   priv->attached = 0;
                   priv->paddrset = 0;
                 }
            }

          /* Device suspend status */

          if (DEVSTATUS_SUSPCHG(g_usbdev.devstatus))
            {
              usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SUSPENDCHG),
                       (uint16)g_usbdev.devstatus);
            }

          /* Device reset */

          if (DEVSTATUS_RESET(g_usbdev.devstatus))
            {
              usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVRESET),
                       (uint16)g_usbdev.devstatus);
              lpc214x_usbreset(priv);
            }
        }

      /* Slow EP interrupt */

      if ((devintstatus & USBDEV_DEVINT_EPSLOW) != 0)
        {
          /* Clear Slow EP interrupt */

          lpc214x_putreg(USBDEV_DEVINT_EPSLOW, LPC214X_USBDEV_DEVINTCLR);
          usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPSLOW), 0);

          do
            {
              /* Read the endpoint interrupt status register */

              epintstatus = lpc214x_getreg(LPC214X_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 result = lpc214x_epclrinterrupt(LPC214X_CTRLEP_OUT);
                      if (result & USBDEV_EPSETUPPACKET)
                        {
                          usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0SETUP), (uint16)result);
                          lpc214x_ep0setup(priv);
                        }
                      else
                        {
                          usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0OUT), priv->ep0state);
                          lpc214x_ep0dataoutinterrupt(priv);
                        }
                      break;
                    }

                  /* EP0 IN interrupt indicated by bit1 == 1 */

                  if ((pending & 2) != 0)
                    {
                      /* Clear the endpoint interrupt */

                      usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0IN), priv->ep0state);
                      (void)lpc214x_epclrinterrupt(LPC214X_CTRLEP_IN);
                      lpc214x_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)lpc214x_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(LPC214X_TRACEINTID_EPOUT), (uint16)epphy);
                              if (priv->usbdev.speed == USB_SPEED_UNKNOWN)
                                {
                                  priv->usbdev.speed = USB_SPEED_FULL;
                                  lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 1);
                                }

                              /* Write host data from the current write request (if any) */

                              privep->txbusy = 0;
                              lpc214x_wrrequest(privep);
                           }
                          else
                            {
                              /* OUT: host-to-device */

                              usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPIN), (uint16)epphy);

                              /* Read host data into the current read request */

                             if (!lpc214x_rqempty(privep))
                                 {
                                  lpc214x_rdrequest(privep);
                                }
                              else
                                {
                                  ullvdbg("Pending data on OUT endpoint\n");
                                  priv->rxpending = 1;
                                }
                            }
                        }
                    }
                }
            }
          while (epintstatus);
        }
#ifdef CONFIG_LPC214X_USBDEV_DMA
    }

  /* Check for DMA interrupts */

  if ((lpc214x_getreg(LPC214X_USBDEV_INTST) & USBDEV_INTST_REQDMA) != 0)
    {
      /* First Software High priority and then low priority */

      uint32 tmp;

      /* Collect the DMA interrupt sources */

      dmaintstatus = 0;
      tmp = lpc214x_getreg(LPC214X_USBDEV_EOTINTST);
      if (lpc214x_getreg(LPC214X_USBDEV_DMAINTEN) & 1)
        {
          dmaintstatus |= tmp;
        }
      lpc214x_putreg(tmp, LPC214X_USBDEV_EOTINTCLR);

      tmp = lpc214x_getreg(LPC214X_USBDEV_NDDRINTST);
      if (lpc214x_getreg(LPC214X_USBDEV_DMAINTEN) & 2)
        {
          dmaintstatus |= tmp;
        }
      lpc214x_putreg(tmp, LPC214X_USBDEV_NDDRINTCLR);

      tmp = lpc214x_getreg(LPC214X_USBDEV_SYSERRINTST);
      if (lpc214x_getreg(LPC214X_USBDEV_DMAINTEN) & 4)
        {
          dmaintstatus |= tmp;
        }
      lpc214x_putreg(tmp, LPC214X_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(LPC214X_TRACEINTID_EPDMA), (uint16)epphy);
#warning DO WHAT?
                }
            }
        }
    }
#endif
  usbtrace(TRACE_INTEXIT(LPC214X_TRACEINTID_USB), 0);
  return OK;
}

/*******************************************************************************
 * Name: lpc214x_dmasetup
 *
 * Description:
 *   Setup for DMA Transfer
 *
 *******************************************************************************/

#ifdef CONFIG_LPC214X_USBDEV_DMA
static int lpc214x_dmasetup(struct lpc214x_usbdev_s *priv, ubyte epphy,
                            uint32 epmaxsize, uint32 nbytes, uint32 *isocpacket,
                            boolean isochronous);
{
  struct lpc214x_dmadesc_s *dmadesc = priv;
  uint32 reg;

#ifdef CONFIG_DEBUG
  if (!priv || epphy < 2)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_TRACEERR_NODMADESC), 0);
      return -EBUSY;
    }

  /* Verify that the DMA descriptor is available */

  if ((dmadesc->status & USB_DMADESC_STATUSMASK) == USB_DMADESC_BEINGSERVICED)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DMABUSY), 0);
      return -EBUSY; /* Shouldn't happen */
    }

  /* Init DMA Descriptor */

  dmadesc->nexdesc = 0;
  dmadesc->config  = USB_DMADESC_MODENORMAL |
                     ((epmaxsize << USB_DMADESC_PKTSIZESHIFT) & USB_DMADESC_PKTSIZEMASK) |
                     ((nbytes << USB_DMADESC_BULENSHIFT) & USB_DMADESC_BUFLENMASK);

#ifdef CONFIG_USBDEV_ISOCHRONOUS
  if (isochronous)
    {
      dmadesc->config |= USB_DMADESC_ISCOEP;
    }
#endif

  dmadesc->start = (uint32)&dmadesc->buffer;
  dmadesc->status = 0;

#ifdef CONFIG_USBDEV_ISOCHRONOUS
  dmadesc->size = (uint32)packet;
#endif

  /* Enable DMA tranfer for this endpoint */

  putreq32(1 << epphy, LPC214X_USBDEV_EPDMAEN);

  /* Check state of IN/OUT Ep buffer */

  reg = lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0);

  if ((LPC214X_EPPHYIN(epphy) &&  (reg & 0x60) == 0) ||
      (LPC214X_EPPHYOUT(epphy) &&  (reg & 0x60) == 0x60))
    {
      /* DMA should be "being serviced" */

      if ((dmadesc->status & USB_DMADESC_STATUSMASK) != USB_DMADESC_BEINGSERVICED))
        {
          /* Re-trigger the DMA Transfer */

          putreq21(1 << epphy, LPC214X_USBDEV_DMARCLR);
          putreq32(1 << epphy, LPC214X_USBDEV_EPDMAEN);
        }
    }
  return OK;
}
#endif /* CONFIG_LPC214X_USBDEV_DMA */

/*******************************************************************************
 * Name: lpc214x_dmarestart
 *
 * Description:
 *   Restart DMA Transfer
 *
 *******************************************************************************/

#ifdef CONFIG_LPC214X_USBDEV_DMA
static void lpc214x_dmarestart(ubyte epphy, uint32 descndx)
{
  uint32 reg;

  /* Clear DMA descriptor status */

  USB_DmaDesc[descndx].status = 0;

  /* Enable DMA transfer on the endpoint */

  lpc214x_putreg(1 << epph, LPC214X_USBDEV_EPDMAEN);

  /* Check the state of IN/OUT EP buffer */

  uint32 reg = lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0);
  if ((LPC214X_EPPHYIN(epphy) &&  (reg & 0x60) == 0) ||
      (LPC214X_EPPHYIN(epphy) &&  (reg & 0x60) == 0x60))
    {
      /* Re-trigger the DMA Transfer */

      putreq21(1 << epphy, LPC214X_USBDEV_DMARCLR);
      putreq32(1 << epphy, LPC214X_USBDEV_EPDMAEN);
    }
}
#endif /* CONFIG_LPC214X_USBDEV_DMA */

/*******************************************************************************
 * Name: lpc214x_dmadisable
 *
 * Description:
 *   Disable DMA transfer for the EP
 *
 *******************************************************************************/

#ifdef CONFIG_LPC214X_USBDEV_DMA
static void lpc214x_dmadisable(ubyte epphy)
{
  EPDMADIS = 1 << epphy;
}
#endif /* CONFIG_LPC214X_USBDEV_DMA */

/*******************************************************************************
 * Endpoint operations
 *******************************************************************************/

/*******************************************************************************
 * Name: lpc214x_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 lpc214x_epconfigure(FAR struct usbdev_ep_s *ep,
                               FAR const struct usb_epdesc_s *desc,
                               boolean last)
{
  FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
  uint32 inten;

  usbtrace(TRACE_EPCONFIGURE, privep->epphy);
  DEBUGASSERT(desc->addr == ep->eplog);

  /* Realize the endpoint */

  lpc214x_eprealize(privep, 1, GETUINT16(desc->mxpacketsize));

  /* Enable and reset EP -- twice */

  lpc214x_usbcmd(CMD_USB_EP_SETSTATUS | privep->epphy, 0);
  lpc214x_usbcmd(CMD_USB_EP_SETSTATUS | privep->epphy, 0);

#ifdef CONFIG_LPC214X_USBDEV_DMA
  /* Enable DMA Ep interrupt (WO) */

   lpc214x_putreg(1 << privep->epphy, LPC214X_USBDEV_EPDMAEN);
#else
  /* Enable Ep interrupt (R/W) */

  inten = lpc214x_getreg(LPC214X_USBDEV_EPINTEN);
  inten |= (1 << privep->epphy);
  lpc214x_putreg(inten, LPC214X_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)
    {
      lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 1);
    }
   return OK;
}

/*******************************************************************************
 * Name: lpc214x_epdisable
 *
 * Description:
 *   The endpoint will no longer be used
 *
 *******************************************************************************/

static int lpc214x_epdisable(FAR struct usbdev_ep_s *ep)
{
  FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
  irqstate_t flags;
  uint32 mask = (1 << privep->epphy);
  uint32 reg;

#ifdef CONFIG_DEBUG
  if (!ep)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif
  usbtrace(TRACE_EPDISABLE, privep->epphy);

  /* Cancel any ongoing activity */

  flags = irqsave();
  lpc214x_cancelrequests(privep);

  /* Disable endpoint and interrupt */

  reg  = lpc214x_getreg(LPC214X_USBDEV_REEP);
  reg &= ~mask;
  lpc214x_putreg(reg, LPC214X_USBDEV_REEP);

  lpc214x_putreg(mask, LPC214X_USBDEV_EPDMADIS);

  reg  = lpc214x_getreg(LPC214X_USBDEV_EPINTEN);
  reg &= ~mask;
  lpc214x_putreg(reg, LPC214X_USBDEV_EPINTEN);

  irqrestore(flags);
  return OK;
}

/*******************************************************************************
 * Name: lpc214x_epallocreq
 *
 * Description:
 *   Allocate an I/O request
 *
 *******************************************************************************/

static FAR struct usbdev_req_s *lpc214x_epallocreq(FAR struct usbdev_ep_s *ep)
{
  FAR struct lpc214x_req_s *privreq;

#ifdef CONFIG_DEBUG
  if (!ep)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return NULL;
    }
#endif
  usbtrace(TRACE_EPALLOCREQ, ((FAR struct lpc214x_ep_s *)ep)->epphy);

  privreq = (FAR struct lpc214x_req_s *)malloc(sizeof(struct lpc214x_req_s));
  if (!privreq)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_ALLOCFAIL), 0);
      return NULL;
    }

  memset(privreq, 0, sizeof(struct lpc214x_req_s));
  return &privreq->req;
}

/*******************************************************************************
 * Name: lpc214x_epfreereq
 *
 * Description:
 *   Free an I/O request
 *
 *******************************************************************************/

static void lpc214x_epfreereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
  FAR struct lpc214x_req_s *privreq = (FAR struct lpc214x_req_s *)req;

#ifdef CONFIG_DEBUG
  if (!ep || !req)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return;
    }
#endif
  usbtrace(TRACE_EPFREEREQ, ((FAR struct lpc214x_ep_s *)ep)->epphy);

  free(privreq);
}

/*******************************************************************************
 * Name: lpc214x_epallocbuffer
 *
 * Description:
 *   Allocate an I/O buffer
 *
 *******************************************************************************/

#ifdef CONFIG_LPC214X_USBDEV_DMA
static FAR void *lpc214x_epallocbuffer(FAR struct usbdev_ep_s *ep, uint16 nbytes)
{
  FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_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: lpc214x_epfreebuffer
 *
 * Description:
 *   Free an I/O buffer
 *
 *******************************************************************************/

#ifdef CONFIG_LPC214X_USBDEV_DMA
static void lpc214x_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf)
{
  FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_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: lpc214x_epsubmit
 *
 * Description:
 *   Submit an I/O request to the endpoint
 *
 *******************************************************************************/

static int lpc214x_epsubmit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
  FAR struct lpc214x_req_s *privreq = (FAR struct lpc214x_req_s *)req;
  FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
  FAR struct lpc214x_usbdev_s *priv;
  irqstate_t flags;
  int ret = OK;

#ifdef CONFIG_DEBUG
  if (!req || !req->callback || !req->buf || !ep)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_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)
    {
      lpc214x_abortrequest(privep, privreq, -EBUSY);
      ret = -EBUSY;
    }

  /* Handle IN (device-to-host) requests */

  else if (LPC214X_EPPHYIN(privep->epphy))
    {
      /* Add the new request to the request queue for the IN endpoint */

      lpc214x_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 = lpc214x_wrrequest(privep);
        }
    }

  /* Handle OUT (host-to-device) requests */

  else
    {
      /* Add the new request to the request queue for the OUT endpoint */

      privep->txnullpkt = 0;
      lpc214x_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 = lpc214x_rdrequest(privep);
          priv->rxpending = 0;
        }
    }

  irqrestore(flags);
  return ret;
}

/*******************************************************************************
 * Name: lpc214x_epcancel
 *
 * Description:
 *   Cancel an I/O request previously sent to an endpoint
 *
 *******************************************************************************/

static int lpc214x_epcancel(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
  FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
  FAR struct lpc214x_usbdev_s *priv;
  irqstate_t flags;

#ifdef CONFIG_DEBUG
  if (!ep || !req)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif
  usbtrace(TRACE_EPCANCEL, privep->epphy);
  priv = privep->dev;

  flags = irqsave();
  lpc214x_cancelrequests(privep);
  irqrestore(flags);
  return OK;
}

/*******************************************************************************
 * Name: lpc214x_epstall
 *
 * Description:
 *   Stall or resume and endpoint
 *
 *******************************************************************************/

static int lpc214x_epstall(FAR struct usbdev_ep_s *ep, boolean resume)
{
  FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
  irqstate_t flags;

  /* STALL or RESUME the endpoint */

  flags = irqsave();
  usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy);
  lpc214x_usbcmd(CMD_USB_EP_SETSTATUS | privep->epphy, (resume ? 0 : USBDEV_EPSTALL));

  /* If the endpoint of was resumed, then restart any queue write requests */

  if (resume)
    {
      (void)lpc214x_wrrequest(privep);
    }
  irqrestore(flags);
  return OK;
}

/*******************************************************************************
 * Device operations
 *******************************************************************************/

/*******************************************************************************
 * Name: lcp214x_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 *lcp214x_allocep(FAR struct usbdev_s *dev, ubyte eplog,
                                               boolean in, ubyte eptype)
{
  FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;
  uint32 epset = LPC214X_EPALLSET & ~LPC214X_EPCTRLSET;
  irqstate_t flags;
  int epndx = 0;

  usbtrace(TRACE_DEVALLOCEP, (uint16)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 >= LPC214X_NLOGENDPOINTS)
        {
          usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADEPNO), (uint16)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 &= LPC214X_EPINSET;
    }
  else
    {
      epset &= LPC214X_EPOUTSET;
    }

  /* Get the subset matching the requested type */

  switch (eptype)
    {
    case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */
      epset &= LPC214X_EPINTRSET;
      break;

    case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */
      epset &= LPC214X_EPBULKSET;
      break;

    case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */
      epset &= LPC214X_EPISOCSET;
      break;

    case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */
    default:
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADEPTYPE), (uint16)eptype);
      return NULL;
    }

  /* Is the resulting endpoint supported by the LPC214x? */

  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 < LPC214X_NPHYSENDPOINTS; epndx++)
            {
              uint32 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(LPC214X_TRACEERR_NOEP), (uint16)eplog);
  return NULL;
}

/*******************************************************************************
 * Name: lpc214x_freeep
 *
 * Description:
 *   Free the previously allocated endpoint
 *
 *******************************************************************************/

static void lpc214x_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep)
{
  FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;
  FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
  irqstate_t flags;

  usbtrace(TRACE_DEVFREEEP, (uint16)privep->epphy);

  if (priv && privep)
    {
      /* Mark the endpoint as available */

      flags = irqsave();
      priv->epavail |= (1 << privep->epphy);
      irqrestore(flags);
    }
}

/*******************************************************************************
 * Name: lpc214x_getframe
 *
 * Description:
 *   Returns the current frame number
 *
 *******************************************************************************/

static int lpc214x_getframe(struct usbdev_s *dev)
{
#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT
  FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;

  /* Return last valid value of SOF read by the interrupt handler */

  usbtrace(TRACE_DEVGETFRAME, (uint16)priv->sof);
  return priv->sof;
#else
  /* Return the last frame number detected by the hardware */

  usbtrace(TRACE_DEVGETFRAME, 0);
  return (int)lpc214x_usbcmd(CMD_USB_DEV_READFRAMENO, 0);
#endif
}

/*******************************************************************************
 * Name: lpc214x_wakeup
 *
 * Description:
 *   Tries to wake up the host connected to this device
 *
 *******************************************************************************/

static int lpc214x_wakeup(struct usbdev_s *dev)
{
  ubyte arg = USBDEV_DEVSTATUS_SUSPEND;
  irqstate_t flags;

  usbtrace(TRACE_DEVWAKEUP, (uint16)g_usbdev.devstatus);

  flags = irqsave();
  if (DEVSTATUS_CONNECT(g_usbdev.devstatus))
    {
      arg |= USBDEV_DEVSTATUS_CONNECT;
    }

  lpc214x_usbcmd(CMD_USB_DEV_SETSTATUS, arg);
  irqrestore(flags);
  return OK;
}

/*******************************************************************************
 * Name: lpc214x_selfpowered
 *
 * Description:
 *   Sets/clears the device selfpowered feature 
 *
 *******************************************************************************/

static int lpc214x_selfpowered(struct usbdev_s *dev, boolean selfpowered)
{
  FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;

  usbtrace(TRACE_DEVSELFPOWERED, (uint16)selfpowered);

#ifdef CONFIG_DEBUG
  if (!dev)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return -ENODEV;
    }
#endif

  priv->selfpowered = selfpowered;
  return OK;
}

/*******************************************************************************
 * Name: lpc214x_pullup
 *
 * Description:
 *   Software-controlled connect to/disconnect from USB host
 *
 *******************************************************************************/

static int lpc214x_pullup(struct usbdev_s *dev, boolean enable)
{
  usbtrace(TRACE_DEVPULLUP, (uint16)enable);

  /* The USBDEV_DEVSTATUS_CONNECT bit in the CMD_USB_DEV_SETSTATUS command
   * controls the LPC214x SoftConnect_N output pin that is used for SoftConnect.
   */

  lpc214x_usbcmd(CMD_USB_DEV_SETSTATUS, (enable ? USBDEV_DEVSTATUS_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 lpc214x_usbdev_s *priv = &g_usbdev;
  uint32 reg;
  int i;

  usbtrace(TRACE_DEVINIT, 0);

  /* Disable USB interrupts */

  lpc214x_putreg(0, LPC214X_USBDEV_INTST);

  /* Initialize the device state structure */

  memset(priv, 0, sizeof(struct lpc214x_usbdev_s));
  priv->usbdev.ops = &g_devops;
  priv->usbdev.ep0 = &priv->eplist[LPC214X_EP0_IN].ep;
  priv->epavail    = LPC214X_EPALLSET;

  /* Initialize the endpoint list */

  for (i = 0; i < LPC214X_NPHYSENDPOINTS; i++)
    {
      uint32 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 (LPC214X_EPPHYIN(i))
        {
          priv->eplist[i].ep.eplog = LPC214X_EPPHYIN2LOG(i);
        }
      else
        {
          priv->eplist[i].ep.eplog = LPC214X_EPPHYOUT2LOG(i);
        }

      /* The maximum packet size may depend on the type of endpoint */

      if ((LPC214X_EPCTRLSET & bit) != 0)
        {
          priv->eplist[i].ep.maxpacket = LPC214X_EP0MAXPACKET;
        }
      else if ((LPC214X_EPINTRSET & bit) != 0)
        {
          priv->eplist[i].ep.maxpacket = LPC214X_INTRMAXPACKET;
        }
      else if ((LPC214X_EPBULKSET & bit) != 0)
        {
          priv->eplist[i].ep.maxpacket = LPC214X_BULKMAXPACKET;
        }
      else /* if ((LPC214X_EPISOCSET & bit) != 0) */
        {
          priv->eplist[i].ep.maxpacket = LPC214X_ISOCMAXPACKET;
        }
    }

  /* Turn on USB power and clocking */

  reg = lpc214x_getreg(LPC214X_PCON_PCONP);
  reg |= LPC214X_PCONP_PCUSB;
  lpc214x_putreg(reg, LPC214X_PCON_PCONP);

  /* Attach USB controller interrupt handler */

  if (irq_attach(LPC214X_USB_IRQ, lpc214x_usbinterrupt) != 0)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_IRQREGISTRATION),
               (uint16)LPC214X_USB_IRQ);
      goto errout;
    }

  /* Enable USB inerrupts at the controller -- but do not disable
   * the ARM interrupt until the device is bound to the class
   * driver
   */

  lpc214x_putreg(USBDEV_INTST_ENUSBINTS, LPC214X_USBDEV_INTST);

  /* Disconnect device */

  lpc214x_pullup(&priv->usbdev, FALSE);

  /* Enable EP0 for OUT (host-to-device) */

  lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, CMD_USB_SETADDRESS_DEVEN|0);
  lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, CMD_USB_SETADDRESS_DEVEN|0);

  /* Reset/Re-initialize the USB hardware */

  lpc214x_usbreset(priv);

  /* Init Device state structure */

  priv->devstatus = lpc214x_usbcmd(CMD_USB_DEV_GETSTATUS, 0);
  return;

errout:
  up_usbuninitialize();
}

/*******************************************************************************
 * Name: up_usbuninitialize
 *******************************************************************************/

void up_usbuninitialize(void)
{
  struct lpc214x_usbdev_s *priv = &g_usbdev;
  uint32 reg;
  irqstate_t flags;

  usbtrace(TRACE_DEVUNINIT, 0);

  if (priv->driver)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DRIVERREGISTERED), 0);
      usbdev_unregister(priv->driver);
    }

  /* Disconnect device */

  flags = irqsave();
  lpc214x_pullup(&priv->usbdev, FALSE);
  priv->usbdev.speed = USB_SPEED_UNKNOWN;
  lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);

  /* Disable and detach IRQs */

  up_disable_irq(LPC214X_USB_IRQ);
  irq_detach(LPC214X_USB_IRQ);

  /* Turn off USB power and clocking */

  reg = lpc214x_getreg(LPC214X_PCON_PCONP);
  reg &= ~LPC214X_PCONP_PCUSB;
  lpc214x_putreg(reg, LPC214X_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(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }

  if (g_usbdev.driver)
    {
      usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_TRACEERR_BINDFAILED), (uint16)-ret);
      g_usbdev.driver = NULL;
    }
  else
    {
      /* Enable USB controller interrupts */

      up_enable_irq(LPC214X_USB_IRQ);
    }
  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(LPC214X_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  /* Unbind the class driver */

  CLASS_UNBIND(driver, &g_usbdev.usbdev);

  /* Disable USB controller interrupts */

  up_disable_irq(LPC214X_USB_IRQ);

  /* Unhook the driver */

  g_usbdev.driver = NULL;
  return OK;
}
