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

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

#include <nuttx/config.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>

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

#include <avr/io.h>

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

#include "chip.h"
#include "up_arch.h"
#include "up_internal.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/* Configuration ***************************************************************/
/* PLL Settings are based on F_CPU frequency which is defined in the board.h file */

#if (BOARD_CPU_CLOCK == 8000000)
#  define USB_PLL_PSC                     ((1 << PLLP1) | (1 << PLLP0))
#elif (BOARD_CPU_CLOCK == 16000000)
#  if defined(__AVR_AT90USB647__)
#    define USB_PLL_PSC                   ((1 << PLLP2) | (1 << PLLP1))
#  else
#    define USB_PLL_PSC                   ((1 << PLLP2) | (1 << PLLP0))
#  endif
#else
#  error "Unsuppored CPU clock"
#endif

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

/* Trace error codes */

#define AVR_TRACEERR_ALLOCFAIL            0x0001
#define AVR_TRACEERR_BADCLREPFEATURE      0x0002
#define AVR_TRACEERR_BADCLRDEVFEATURE     0x0003
#define AVR_TRACEERR_BADDEVGETSTATUS      0x0004
#define AVR_TRACEERR_BADEPNO              0x0005
#define AVR_TRACEERR_BADEPGETSTATUS       0x0006
#define AVR_TRACEERR_BADGETCONFIG         0x0007
#define AVR_TRACEERR_BADGETSETDESC        0x0008
#define AVR_TRACEERR_BADGETSTATUS         0x0009
#define AVR_TRACEERR_BADSETADDRESS        0x000a
#define AVR_TRACEERR_BADSETCONFIG         0x000b
#define AVR_TRACEERR_BADSETEPFEATURE      0x000c
#define AVR_TRACEERR_BADSETDEVFEATURE     0x000d
#define AVR_TRACEERR_BINDFAILED           0x000e
#define AVR_TRACEERR_DRIVER               0x000f
#define AVR_TRACEERR_DISPATCHSTALL        0x0010
#define AVR_TRACEERR_DRIVERREGISTERED     0x0011
#define AVR_TRACEERR_EPNULLPACKET         0x0012
#define AVR_TRACEERR_XFERTYPE             0x0013
#define AVR_TRACEERR_PKTSIZE              0x0014
#define AVR_TRACEERR_EPCFGBAD             0x0015
#define AVR_TRACEERR_EP0CFGBAD            0x0016
#define AVR_TRACEERR_EP0SETUPSTALLED      0x0017
#define AVR_TRACEERR_EP0RXOUTI            0x0018
#define AVR_TRACEERR_EP0FIFOFULL          0x0019
#define AVR_TRACEERR_EP0FIFONOTREADY      0x001a
#define AVR_TRACEERR_INFIFO               0x001b
#define AVR_TRACEERR_INVALIDCTRLREQ       0x001c
#define AVR_TRACEERR_INVALIDPARMS         0x001d
#define AVR_TRACEERR_IRQREGISTRATION      0x001e
#define AVR_TRACEERR_NOEP                 0x001f
#define AVR_TRACEERR_NOTCONFIGURED        0x0020

/* Trace interrupt codes */

#define AVR_TRACEINTID_GENINT             0x0001
#define AVR_TRACEINTID_EPINT              0x0002
#define AVR_TRACEINTID_VBUS               0x0003
#define AVR_TRACEINTID_SUSPEND            0x0004
#define AVR_TRACEINTID_WAKEUP             0x0005
#define AVR_TRACEINTID_EOR                0x0006
#define AVR_TRACEINTID_CLEARFEATURE       0x0007
#define AVR_TRACEINTID_DEVGETSTATUS       0x0008
#define AVR_TRACEINTID_DISPATCH           0x0009
#define AVR_TRACEINTID_EP0SETUP           0x000a
#define AVR_TRACEINTID_EPGETSTATUS        0x000b
#define AVR_TRACEINTID_EPIN               0x000c
#define AVR_TRACEINTID_EPOUT              0x000d
#define AVR_TRACEINTID_EP0SETUPSETADDRESS 0x000e
#define AVR_TRACEINTID_GETCONFIG          0x000f
#define AVR_TRACEINTID_GETSETDESC         0x0010
#define AVR_TRACEINTID_GETSETIF           0x0011
#define AVR_TRACEINTID_GETSTATUS          0x0012
#define AVR_TRACEINTID_IFGETSTATUS        0x0013
#define AVR_TRACEINTID_SETCONFIG          0x0014
#define AVR_TRACEINTID_SETFEATURE         0x0015
#define AVR_TRACEINTID_SYNCHFRAME         0x0016

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

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

/* Number of endpoints */

#define AVR_NENDPOINTS                    (7) /* ep0-6 */

/* Endpoint 0 is special... */

#define AVR_EP0		                      (0)
#define AVR_CTRLEP_SIZE                   (8)

/* Bit encoded ep0-6 */

#define AVR_ALL_EPS                       (0x7f)

/* Endpoint configuration definitions */

#define AVR_EPTYPE_CTRL                   (0 << EPTYPE0)
#define AVR_EPTYPE_ISOC                   (1 << EPTYPE0)
#define AVR_EPTYPE_BULK                   (2 << EPTYPE0)
#define AVR_EPTYPE_INTR                   (3 << EPTYPE0)

#define AVR_DIR_OUT                       (0 << EPDIR)
#define AVR_DIR_IN                        (1 << EPDIR)

#define AVR_SINGLE_BANK                   (0 << EPBK0)
#define AVR_DOUBLE_BANK                   (1 << EPBK0)

#define AVR_EPSIZE_8                      (0 << EPSIZE0)
#define AVR_EPSIZE_16                     (1 << EPSIZE0)
#define AVR_EPSIZE_32                     (2 << EPSIZE0)
#define AVR_EPSIZE_64                     (3 << EPSIZE0)
#define AVR_EPSIZE_128                    (4 << EPSIZE0)
#define AVR_EPSIZE_256                    (5 << EPSIZE0)

/* General endpoint defintions */

#define AVR_EP0                           (0)
#define AVR_NENDPOINTS                    (7)
#define AVR_EPNO_MASK                     (3)

#define AVR_TIMEOUT_LONG                  (100)
#define AVR_TIMEOUT_SHORT                 (32)
#define AVR_TIMEOUT_NONE                  (0)

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

#define avr_rqempty(ep)                   ((ep)->head == NULL)
#define avr_rqpeek(ep)                    ((ep)->head)

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

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

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

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

struct avr_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 avr_ep_s. */

  struct usbdev_ep_s ep;      /* Standard endpoint structure */

  /* AVR-specific fields */

  struct avr_req_s *head;     /* Request list for this endpoint */
  struct avr_req_s *tail;
  struct avr_req_s *pending;  /* Pending IN request */
  uint8_t stalled:1;          /* 1: Endpoint is stalled */
  uint8_t epin:1;             /* 1: IN endpoint */
};

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

struct avr_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
   * struct avr_usbdev_s. */

  struct usbdev_s usbdev;

  /* The bound device class driver */

  struct usbdevclass_driver_s *driver;

  /* AVR-specific fields */

  uint8_t  ep0buf[64];        /* buffer for EP0 short transfers */
  uint8_t  paddr;             /* Address assigned by SETADDRESS */
  uint8_t  epavail;           /* Bitset of available (unconfigured) endpoints */
  uint8_t  epinset;           /* The set of all configured IN endpoints */
  uint8_t  epoutset;          /* The set of all configured OUT endpoints */
  uint8_t  stalled:1;         /* 1: Protocol stalled */
  uint8_t  selfpowered:1;     /* 1: Device is self powered */
  uint8_t  paddrset:1;        /* 1: Peripheral addr has been set */
  uint8_t  attached:1;        /* 1: Host attached */
#ifdef CONFIG_USBDEV_SELFPOWERED
  uint8_t  wkupen:1;          /* 1: Wake-up enabled */
#endif
  volatile bool connected;    /* Device is connected */

  /* The endpoint list */

  struct avr_ep_s eplist[AVR_NENDPOINTS];
};

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

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

static FAR struct avr_req_s *avr_rqdequeue(FAR struct avr_ep_s *privep);
static inline void avr_rqenqueue(FAR struct avr_ep_s *privep,
                                 FAR struct avr_req_s *req);

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

static void avr_txready(void);
static int avr_fifoready(int timeout);
static void avr_ep0send(FAR const uint8_t *buffer, uint16_t buflen);
static inline int avr_epNsend(FAR struct avr_ep_s *privep,
                              FAR struct avr_req_s *privreq);
static inline int avr_epNrecv(FAR struct avr_ep_s *privep,
                              FAR struct usbdev_req_s *req);
static int avr_epINqueue(FAR struct avr_ep_s *privep);
static int avr_epOUTqueue(FAR struct avr_ep_s *privep);
static void avr_reqcomplete(FAR struct avr_ep_s *privep, FAR struct avr_req_s *privreq,
                            int result);
static void avr_cancelrequests(FAR struct avr_ep_s *privep, int status);
static void avr_cancelall(int status);

/* Endpoint interrupt handling *************************************************/

static struct avr_ep_s *avr_epfindbyaddr(uint8_t epno);
static void avr_dispatchrequest(FAR const struct usb_ctrlreq_s *ctrl);
static int avr_ep0configure(void);
static void avr_setaddress(uint8_t address);
static void avr_ep0setup(void);
static int avr_epinterrupt(int irq, FAR void *context);

/* General interrupt handling **************************************************/

static void avr_epreset(FAR struct avr_ep_s *privep, int status);
static void avr_usbreset(void);
static void avr_genvbus(void);
static inline void avr_gensuspend(void);
static void avr_genwakeup(void);
static inline void avr_geneor(void);
static int avr_geninterrupt(int irq, FAR void *context);

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

static int avr_epconfigure(FAR struct usbdev_ep_s *ep,
                           const struct usb_epdesc_s *desc, bool last);
static int avr_epdisable(FAR struct usbdev_ep_s *ep);
static FAR struct usbdev_req_s *avr_epallocreq(FAR struct usbdev_ep_s *ep);
static void avr_epfreereq(FAR struct usbdev_ep_s *ep,
                          FAR struct usbdev_req_s *);
#ifdef CONFIG_USBDEV_DMA
static void *avr_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes);
static void avr_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf);
#endif
static int avr_epsubmit(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req);
static int avr_epcancel(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req);
static int avr_epstall(FAR struct usbdev_ep_s *ep, bool resume);

static FAR struct usbdev_ep_s *avr_allocep(FAR struct usbdev_s *dev,
                                           uint8_t epno, bool in,
                                           uint8_t eptype);
static void avr_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep);
static int avr_getframe(struct usbdev_s *dev);
static int avr_wakeup(struct usbdev_s *dev);
static int avr_selfpowered(struct usbdev_s *dev, bool selfpowered);
static int avr_pullup(struct usbdev_s *dev, bool enable);

/****************************************************************************
 * Private Data
 ****************************************************************************/

/* Since there is only a single USB interface, all status information can be
 * be simply retained in a single global instance.
 */

static struct avr_usbdev_s g_usbdev;

static const struct usbdev_epops_s g_epops =
{
  .configure   = avr_epconfigure,
  .disable     = avr_epdisable,
  .allocreq    = avr_epallocreq,
  .freereq     = avr_epfreereq,
#ifdef CONFIG_USBDEV_DMA
  .allocbuffer = avr_epallocbuffer,
  .freebuffer  = avr_epfreebuffer,
#endif
  .submit      = avr_epsubmit,
  .cancel      = avr_epcancel,
  .stall       = avr_epstall,
};

static const struct usbdev_ops_s g_devops =
{
  .allocep     = avr_allocep,
  .freeep      = avr_freeep,
  .getframe    = avr_getframe,
  .wakeup      = avr_wakeup,
  .selfpowered = avr_selfpowered,
  .pullup      = avr_pullup,
};

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

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

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

static FAR struct avr_req_s *avr_rqdequeue(FAR struct avr_ep_s *privep)
{
  FAR struct avr_req_s *ret = privep->head;

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

      ret->flink = NULL;
    }

  return ret;
}

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

static inline void avr_rqenqueue(FAR struct avr_ep_s *privep,
                                 FAR struct avr_req_s *req)
{
  req->flink = NULL;
  if (!privep->head)
    {
      privep->head = req;
      privep->tail = req;
    }
  else
    {
      privep->tail->flink = req;
      privep->tail = req;
    }
}

/****************************************************************************
 * Name: avr_txready
 *
 * Description:
 *   Wait for the selected endpoint to be ready for an IN (TX) transfer
 *
 ****************************************************************************/

static void avr_txready(void)
{
  int retries = 10000;
  while (((UEINTX & (1 << TXINI))  == 0) && retries-- > 0);
}

/****************************************************************************
 * Name: avr_fifoready
 *
 * Description:
 *   Wait for the selected endpoint FIFO to be ready
 *
 ****************************************************************************/

static int avr_fifoready(int timeout)
{
  UDINT &= ~(1 << SOFI);

  for (; ; )
    {
      /* Check if the FIFO is ready by testing RWAL (read/write allowed).  The
       * meaning of this bigtdepends on the direction of the endpoint: For an
       * OUT endpoint, the RWAL bit is set if the firmware can read data from
       * the bank, and cleared by hardware when the bank is empty; For an IN
       * endpoint, the RWAL bit is set if the firmware can write data to the
       * bank, and cleared by hardware when the bank is full.
       */

      if ((UEINTX & (1 << RWAL)) != 0)
        {
          return OK;
        }

      /* Check if we are still connected and not stalled */

      if (!(g_usbdev.connected))
        {
          return -ENODEV;
        }
      else if ((UECONX & (1 << STALLRQ)) != 0)
        {
          return -EAGAIN;
        }

      /* Timeing is driven by the start of frame (SOF) interrupt which we
       * assume here to be at a one millisecond rate. */

      if ((UDINT & (1 << SOFI)) != 0)
        {
          /* Clear the SOF interrupt decrement the count of elapsed
           * milliseconds */

          UDINT &= ~(1 << SOFI);

          if ((timeout--) > 0)
            {
              /* The timeout has elapsed... return a failure */

              return -ETIME;
            }
        }
    }
}

/****************************************************************************
 * Name: avr_ep0send
 *
 * Description:
 *   Schedule a short TX transfer for Endpoint 0
 *
 * Assumptions:
 * - Endpoint 0 is already selected.
 *
 ****************************************************************************/

static void avr_ep0send(FAR const uint8_t *buffer, uint16_t buflen)
{
  FAR const uint8_t *ptr = buffer;
  uint8_t regval;

  /* Loop while there are more bytes to send and RXOUTI is clear.  RXOUTI is
   * set when a new OUT data is received
   */

  while (buflen)
    {
      /* Verify that RXOUTI is clear.  RXOUTI is set when a new OUT data is
       * received.  In this case, we have not option but to abort the transfer.
       */

      regval = UEINTX;
      if ((regval & (1 << RXOUTI)) != 0)
        {
           usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0RXOUTI), regval);
           return;
        }

      /* Okay... wait for the selected endpoint to be ready for an TX transfer */

      avr_txready();

      /* Now send as many bytes as possible */

      while (buflen > 0)
        {
          /* Break out of the loop if the FIFO is full */

          if (UEBCX == AVR_CTRLEP_SIZE)
            {
              /* Clearing FIFOCON frees the current bank and switches to the
               * following bank.  TXINI must be cleared to acknowledge the
               * interrupt.
               */

              usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFOFULL), regval);

              /* TXINI must always be cleared BEFORE clearing FIFOCON */

              regval = UEINTX;
              regval &= ~(1 << TXINI);
              UEINTX = regval;
              regval &= ~(1 << FIFOCON);
              UEINTX = regval;
              break;
            }

          /* Not full, transfer another byte */

          UEDATX = *ptr++;
          buflen--;
        }

      /* Clearing FIFOCON frees the current bank and switches to the following
       * bank.  TXINI must be cleared to acknowledge the interrupt. TXINI must
       * always be cleared BEFORE clearing FIFOCON.
       */

      regval = UEINTX;
      regval &= ~(1 << TXINI);
      UEINTX = regval;
      regval &= ~(1 << FIFOCON);
      UEINTX = regval;
    }
}

/****************************************************************************
 * Name: avr_epNsend
 *
 * Description:
 *   Perform a TX transfer for Endpoint N
 *
 ****************************************************************************/

static inline int avr_epNsend(FAR struct avr_ep_s *privep,
                              FAR struct avr_req_s *privreq)
{
  FAR struct usbdev_req_s *req;
  FAR const uint8_t *buffer;
  uint16_t buflen;
  uint16_t len;
  uint16_t pktmask;
  uint8_t  ret;
  uint8_t  more;
  uint8_t  regval;
  bool     zlp;

  /* Check if endpoint is ready for read/write operations */

  DEBUGASSERT((UEINTX & (1 << RWAL)) != 0);

  /* Setup pointers and counts for this transfer */

  req     = &privreq->req;
  buffer  = &req->buf[req->xfrd];
  buflen  = req->len - req->xfrd;
  zlp     = ((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0);
  pktmask = privep->ep.maxpacket - 1;

  /* Select the endpoint */

  UENUM   = privep->ep.eplog;

  /* This function should not be called if we are not ready to write! */

  ret = avr_fifoready(AVR_TIMEOUT_LONG);
  if (ret != OK)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFOFULL), regval);
      return -EAGAIN;
    }

  /* Send the USB data.  The outer loop handles for each packet of data
   * (including zero-length packets)
   */

  do
    {
      /* Then loop, putting each outgoing byte into the transmit FIFO until
       * either (1) all of the data has been sent (len == buflen) or
       * (2) the transmit FIFO is full
       */

      len = 0;
      while (len < buflen && (UEINTX & (1 << RWAL)) != 0)
        {
          /* Add another byte to the transmit FIFO */

          UEDATX = *buffer++;
          len++;
        }

      /* We now have one complete packet in the transmit FIFO(or maybe two
       * packets if dual buffering is enabled).
       *
       * Clear any pending TXINI interrupts
       */

      UEINT &= ~(1 <<  privep->ep.eplog);

      /* Clear TXINI and send what is in the transmit FIFO (could be a zero
       * length packet). TXINI must always be cleared BEFORE clearing FIFOCON.
       */

      regval = UEINTX;
      regval &= ~(1 << TXINI);
      UEINTX = regval;
      regval &= ~(1 << FIFOCON);
      UEINTX = regval;

      /* Adjust the remaining number of bytes to transfer. */

      req->xfrd += len;
      buffer    += len;
      buflen    -= len;

      usbtrace(TRACE_WRITE(privep->ep.eplog), privreq->req.xfrd);

      /* Check if we need to send a zero length packet (ZLP); We need to send
       * a ZLP if the last packet sent was exactly equal to the packet length
       * AND if the endpoint is configuration to send ZLPs. However, in dual
       * buffer mode, we may have actually just sent two packets so the actual
       * check is for a non-zero, transfer of a multiple of the packet
       */

      if (buflen > 0)
        {
          /* There is more data to be sent */

          more = true;
        }
      else if (zlp)
        {
          /* All of the data has been sent.  A ZLP might be needed if the last
           * transfer was an exact multiple of the packet size.
           */

          if (len && (len & pktmask) == 0)
            {
              /* The last packet was not a ZLP and was an example multiple of
               * the packet size.  A ZLP is needed.
               */

              more = true;
            }
          else
            {
              /* The last packet was a ZLP or was a partial packet.  We are
               * finished with this request.
               */

              more = false;
            }
        }
      else
        {
          /* No more data to be sent and a ZLP is not needed */

          more = false;
        }

      /* RWAL will be de-asserted when there is no more space in the transmit
       * FIFO.  We care only if we have more data (or a zero-length-packet) to
       * send. Try a short inline wait to see if the FIFO becomes write ready.
       * This saves handling an interrupt most of the time (really depends on
       * how fast the host takes the data from the transmit FIFO).
       */

      if (more && (ret = avr_fifoready(AVR_TIMEOUT_SHORT)))
        {
          /* If the endpoint simply did not become ready within the timeout,
           * then handle the remainder of the transfer asynchronously in the
           * TXINI interrupt handler. */

          if (ret == -ETIME)
            {
              /* Enable the endpoint IN interrupt ISR. */

              UEIENX |= (1 << TXINE);
            }

          /* A fatal error occurred while waiting for the IN FIFO to become
           * available.
           */

          usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INFIFO), regval);
          return ret;
        }
    }
  while (more);
  return OK;
}

/****************************************************************************
 * Name: avr_epNrecv
 *
 * Description:
 *   Perform an RX transfer for Endpoint N
 *
 ****************************************************************************/

static inline int avr_epNrecv(FAR struct avr_ep_s *privep,
                              FAR struct usbdev_req_s *req)
{
  FAR uint8_t *buffer;
  uint8_t regval;
  int ret;

  /* Setup pointers and counts for this transfer */

  buffer    = req->buf;
  req->xfrd = 0;

  /* This function should not be called if we are not ready to read! */

  ret = avr_fifoready(AVR_TIMEOUT_LONG);
  if (ret != OK)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFONOTREADY), ret);
      return ret;
    }

  /* Loop until the requested number of bytes have been read */

  while (req->xfrd < req->len)
    {
      /* RWAL will be de-asserted when everything has been read from the
       * receive FIFO */

      if (((UEINTX & (1 << RWAL)) == 0))
        {
          /* The FIFO is empty.. Acknowledge receipt of the packet. RXOUTI must
           * always be cleared BEFORE clearing FIFOCON.
           */

          regval = UEINTX;
          regval &= ~(1 << RXOUTI);
          UEINTX = regval;
          regval &= ~(1 << FIFOCON);
          UEINTX = regval;

          /* Return success */

          usbtrace(TRACE_READ(privep->ep.eplog), req->xfrd);
          return OK;
        }
      else
        {
          /* Receive the next byte */

          *buffer++ = UEDATX;

          /* Increment the number of bytes received and try again */

          req->xfrd++;
        }
    }

  /* We get here if the request buffer is full.  There could be more bytes
   * pending in the FIFO?
   *
   * Finalize the OUT stream transfer.  RXOUTI must always be cleared BEFORE
   * clearing FIFOCON.
   */

  regval = UEINTX;
  regval &= ~(1 << RXOUTI);
  UEINTX = regval;
  regval &= ~(1 << FIFOCON);
  UEINTX = regval;

  usbtrace(TRACE_READ(privep->ep.eplog), req->xfrd);
  return OK;
}

/****************************************************************************
 * Name: avr_epINqueue
 *
 * Description:
 *   This is part of the IN endpoint interrupt handling logic.  It is called
 *   from interrupt handling logic for an endpoint when the TXIN endpoint
 *   interrupt occurs.  Thus function is also called from the requeust enqueuing
 *   logic BUT with interrupts disabled.
 *
 ****************************************************************************/

static int avr_epINqueue(FAR struct avr_ep_s *privep)
{
  FAR struct avr_req_s *privreq;
  int ret = OK;

  usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EPIN), 0);

  /* First, check if there is already pending IN transfer */

  if (privep->pending)
    {
      /* Yes.. use this request to continue the transfer */

      privreq         = privep->pending;
    }
  else
    {
      /* No.. remove the next request from the queue of IN requests */

      privreq         =  avr_rqdequeue(privep);
      privep->pending = privreq;
    }

  /* Is there an IN request */

  if (privreq)
    {
      /* Yes.. perform the IN transfer */

      ret = avr_epNsend(privep, privreq);

      /* The return value of -ETIME means that the transfer was not
       * finished within this interrupt.  We just need to exit with the
       * pending transfer in place.
       */

      if (ret == OK || ret != -ETIME)
        {
          /* The transfer has completed, perhaps with an error.  Return the request
           * to the class driver.
           */

          usbtrace(TRACE_COMPLETE(privep->ep.eplog), privreq->req.xfrd);
          privep->pending = NULL;
          avr_reqcomplete(privep, privreq, ret);
        }
    }
  return ret;
}

/****************************************************************************
 * Name: avr_epOUTqueue
 *
 * Description:
 *   This is part of the OUT endpointeinterrupt handling logic.  It is called
 *   from interrupt handling logic for an endpoint when the RXOUT endpoint
 *   interrupt occurs.
 *
 ****************************************************************************/

static int avr_epOUTqueue(FAR struct avr_ep_s *privep)
{
  FAR struct avr_req_s *privreq;
  int ret = OK;

  usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EPOUT), 0);

  /* Remove the next request from the queue of OUT requests */

  privreq =  avr_rqdequeue(privep);

  /* Is there an OUT request */

  if (privreq)
    {
      /* Yes.. perform the OUT transfer */

      ret = avr_epNrecv(privep, &privreq->req);

      /* The transfer has completed, perhaps with an error.  Return the request
       * to the class driver.
       */

      usbtrace(TRACE_COMPLETE(privep->ep.eplog), privreq->req.xfrd);
      avr_reqcomplete(privep, privreq, ret);
    }
  return ret;
}

/****************************************************************************
 * Name: avr_reqcomplete
 *
 * Description:
 *   Handle termination of the request at the head of the endpoint request queue.
 *
 ****************************************************************************/

static void avr_reqcomplete(FAR struct avr_ep_s *privep, FAR struct avr_req_s *privreq,
                            int result)
{
  /* If endpoint 0, temporarily reflect the state of protocol stalled in the
   * callback. */

  bool stalled = privep->stalled;
  if (privep->ep.eplog == AVR_EP0)
    {
      privep->stalled = g_usbdev.stalled;
    }

  /* Save the result in the request structure */

  privreq->req.result = result;

  /* Callback to the request completion handler */

  privreq->req.callback(&privep->ep, &privreq->req);

  /* Restore the stalled indication */

  privep->stalled = stalled;
}

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

static void avr_cancelrequests(FAR struct avr_ep_s *privep, int status)
{
  /* Is there a pending, active IN transfer? */

  if (privep->pending)
    {
      /* Remove the pending request */

      FAR struct avr_req_s *privreq = privep->pending;
      privep->pending = NULL;

      /* Make sure that the endpoint IN interrupt is disabled.  */

      UENUM = privep->ep.eplog;
      UEIENX &= ~(1 << TXINE);

      /* Complete the request with the provided status */

      usbtrace(TRACE_COMPLETE(privep->ep.eplog), privreq->req.xfrd);
      avr_reqcomplete(privep, privreq, status);
    }

  /* Then complete any queue requests.  None of these should be active. */

  while (!avr_rqempty(privep))
    {
      usbtrace(TRACE_COMPLETE(privep->ep.eplog), (avr_rqpeek(privep))->req.xfrd);
      avr_reqcomplete(privep, avr_rqdequeue(privep), status);
    }
}

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

static void avr_cancelall(int status)
{
  struct avr_ep_s *privep;
  int i;

  for (i = 1; i < AVR_NENDPOINTS; i++)
    {
      privep = &g_usbdev.eplist[i];
      if (privep)
        {
          avr_cancelrequests(privep, status);
        }
    }
}

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

static struct avr_ep_s *avr_epfindbyaddr(uint8_t epno)
{
  struct avr_ep_s *privep;
  int i;

  /* Endpoint zero is a special case */

  if (epno == AVR_EP0)
    {
      return &g_usbdev.eplist[0];
    }

  /* Handle the remaining */

  for (i = 1; i < AVR_NENDPOINTS; i++)
    {
      privep = &g_usbdev.eplist[i];

      /* Same logical endpoint number? (includes direction bit) */

      if (epno == privep->ep.eplog)
        {
          /* Return endpoint found */

          return privep;
        }
    }

  /* Return endpoint not found */

  return NULL;
}

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

static void avr_dispatchrequest(FAR const struct usb_ctrlreq_s *ctrl)
{
  int ret = -EIO;

  usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_DISPATCH), 0);
  if (g_usbdev.driver)
    {
      /* Forward to the control request to the class driver implementation */

      ret = CLASS_SETUP(g_usbdev.driver, &g_usbdev.usbdev, ctrl, NULL, 0);
    }

  if (ret < 0)
    {
      /* Stall on failure */

      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_DISPATCHSTALL), 0);
      g_usbdev.stalled = true;
    }
}

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

static int avr_ep0configure(void)
{
  FAR struct avr_ep_s *privep = &g_usbdev.eplist[AVR_EP0];
  uint8_t regval;

  /* Configure endpoint 0 */

  UENUM   = AVR_EP0;
  UECONX |= (1 << EPEN);
  UECFG1X = 0;
  UECFG0X = AVR_EPTYPE_CTRL;
  UECFG1X =  (1 << ALLOC) | AVR_EPSIZE_8;

  /* Check for configuration failure */

  regval = UESTA0X;
  if ((regval &  (1 << CFGOK)) == 0)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0CFGBAD), regval);
      return -EINVAL;
    }

  /* Initialize the endpoint data structure.  Mark EP0 as an IN endpoint so
   * that the submit() logic will know that any enqueue packets are to be
   * sent.
   */

  memset(privep, 0, sizeof(struct avr_ep_s));
  privep->ep.ops       = &g_epops;
  privep->ep.maxpacket = AVR_CTRLEP_SIZE;
  privep->epin         = 1;

  /* Enable OUT interrupts */

  UEIENX |= (1 << RXOUTE);
  return OK;
}

/****************************************************************************
 * Name: avr_epreset
 *
 * Description:
 *   Reset the specified endpoint
 *
 * Input Parameters:
 *   epno - The endpoint to be reset
 *
 ****************************************************************************/

static void avr_epreset(FAR struct avr_ep_s *privep, int status)
{
  uint8_t epno = privep->ep.eplog;

  /* Reset the endpoint hardware */

  UEINT   &= ~(1 << epno);
  UENUM    = epno;
  UEIENX   = 0;
  UEINTX   = 0;
  UECFG1X &= ~(1 << ALLOC);
  UECONX  &= ~(1 << EPEN);

  /* Cancel all requests */

  avr_cancelrequests(privep, status);

  /* Reset endpoint status */

  privep->stalled = false;
}

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

static void avr_usbreset(void)
{
  uint8_t epno;
  uint8_t regval;

  /* Disable all interrupts */

  USBCON &= ~((1 << VBUSTE) | (1 << IDTE));
  UDIEN   = 0;

  /* Clear all pending interrupts */

  USBINT  = 0;
  UDINT   = 0;

  /* Reset selected state variables */

  g_usbdev.connected = false;
#ifdef CONFIG_USBDEV_SELFPOWERED
  g_usbdev.wkupen    = false;
#endif

  /* Reset endpoints */

  for (epno = 0; epno < AVR_NENDPOINTS; epno++)
    {
      struct avr_ep_s *privep = &g_usbdev.eplist[epno];
      avr_epreset(privep, -ESHUTDOWN);
    }

  /* Tell the class driver that we are disconnected. The class driver should
   * then accept any new configurations. */

  if (g_usbdev.driver)
    {
      CLASS_DISCONNECT(g_usbdev.driver, &g_usbdev.usbdev);
    }

  /* Enable the PLL */

  PLLCSR  =  USB_PLL_PSC;
  PLLCSR |= (1 << PLLE);
  while ((PLLCSR & (1 << PLOCK)) == 0);

  /* Reset the USB interface */

  regval = USBCON;
  USBCON = (regval & ~(1 << USBE));
  USBCON = (regval | (1 << USBE));

#ifndef CONFIG_USB_DISABLE_PADREGULATOR
  /* Enable the USB pad regulator */

  UHWCON |= (1 << UVREGE);
#endif

  /* Enable USB clock inputs */

  USBCON &= ~(1 << FRZCLK);

  /* Select low or high speed */

#ifdef CONFIG_USB_LOWSPEED
  UDCON |= (1 << LSM);
#else
  UDCON &= ~(1 << LSM);
#endif

  /* Set USB address to 0 */

  avr_setaddress(0);

  /* EndPoint 0 initialization */

  (void)avr_ep0configure();

  /* Enable VBUS interrupts */

  USBCON |= (1 << VBUSTE);

  /* Attach the device to the USB bus. This announces the device's presence to
   * any attached USB host, starting the enumeration process. If no host is
   * present, attaching the device will allow for enumeration once a host is
   * connected to the device.
   */

  UDCON &= ~(1 << DETACH);

  /* Enable Suspend and reset interrupts */

  UDIEN |=  ((1 << SUSPE) | (1 << EORSTE));
}

/****************************************************************************
 * Name: avr_usbshutdown
 *
 * Description:
 *   Shutdown the USB interface and put the hardare in a known state
 *
 ****************************************************************************/

void avr_usbshutdown(void)
{
  /* Inform the class driver of the disconnection */

  if (g_usbdev.driver)
    {
      CLASS_DISCONNECT(g_usbdev.driver, &g_usbdev.usbdev);
    }

  /* Detach the device from the USB bus, terminating communications */

  UDCON |= (1 << DETACH);

  /* Disable all interrupts */

  USBCON &= ~((1 << VBUSTE) | (1 << IDTE));
  UDIEN = 0;

  /* Clear all pending interrupts */

  USBINT = 0;
  UDINT  = 0;

  g_usbdev.connected = false;
#ifdef CONFIG_USBDEV_REMOTEWAKEUP
  g_usbdev.wkupen    = false;
#endif

  /* Disable the USB interface */

  USBCON &= ~(1 << USBE);

  /* Shut down the USB PLL */

  PLLCSR = 0;

  /* Turn off the VBUS pad */

  USBCON &= ~(1 << OTGPADE);
}

/****************************************************************************
 * Name: avr_setaddress
 *
 * Description:
 *   Set the devices USB address
 *
 ****************************************************************************/

static inline void avr_setaddress(uint8_t address)
{
  uint8_t regval;

  g_usbdev.paddr    = address;
  g_usbdev.paddrset = (address != 0);

  UEINTX &= ~(1 << RXSTPI);

  /* TXINI must always be cleared BEFORE clearing FIFOCON. */

  regval = UEINTX;
  regval &= ~(1 << TXINI);
  UEINTX = regval;
  regval &= ~(1 << FIFOCON);
  UEINTX = regval;
  regval  = UEINTX;

  avr_txready();
  UDADDR  = ((1 << ADDEN) | address);
}

/****************************************************************************
 * Name: avr_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 avr_ep0setup(void)
{
  struct avr_ep_s *privep;
  struct usb_ctrlreq_s ctrl;
  uint8_t *ptr;
  uint16_t value;
  uint16_t index;
  uint16_t len;
  uint8_t  i;

  usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EP0SETUP), 0);

  /* Terminate any pending requests */

  avr_cancelrequests(&g_usbdev.eplist[AVR_EP0], -EPROTO);
  avr_cancelrequests(&g_usbdev.eplist[AVR_EP0], -EPROTO);

  /* Assume NOT stalled */

  g_usbdev.eplist[AVR_EP0].stalled = false;
  g_usbdev.eplist[AVR_EP0].stalled = false;
  g_usbdev.stalled = false;

  /* Read EP0 setup data -- Read the setup data from the hardware. */

  ptr = (uint8_t *)&ctrl;
  for (i = 0; i < USB_SIZEOF_CTRLREQ; i++)
    {
      *ptr++ = UEDATX;
    }

  /* 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)
    {
      avr_dispatchrequest(&ctrl);
    }
  else
    {
      /* 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(AVR_TRACEINTID_GETSTATUS), 0);
            if (!g_usbdev.paddrset || len != 2 ||
                (ctrl.type & USB_REQ_DIR_IN) == 0 || value != 0)
              {
                g_usbdev.stalled = true;
              }
            else
              {
                switch (ctrl.type & USB_REQ_RECIPIENT_MASK)
                  {
                  case USB_REQ_RECIPIENT_ENDPOINT:
                    {
                      usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EPGETSTATUS), 0);
                      privep = avr_epfindbyaddr(index);
                      if (!privep)
                        {
                          usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADEPGETSTATUS), 0);
                          g_usbdev.stalled = true;
                        }
                      else
                        {
                          /* Return endpoint stalled status */

                          if (privep->stalled)
                            {
                              g_usbdev.ep0buf[0] = (1 << USB_FEATURE_ENDPOINTHALT); /* Stalled */
                            }
                          else
                            {
                              g_usbdev.ep0buf[0] = 0; /* Not stalled */
                            }
                          g_usbdev.ep0buf[1] = 0;

                          /* And send the response */

                          avr_ep0send(g_usbdev.ep0buf, 2);
                        }
                    }
                    break;

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

                          /* Features: Remote Wakeup=YES; selfpowered=? */
                          /* Return self-powered status */

#ifdef CONFIG_USBDEV_SELFPOWERED
                          g_usbdev.ep0buf[0] = (1 << USB_FEATURE_SELFPOWERED);
#else
                          g_usbdev.ep0buf[0] = 0;
#endif
                          /* Return remote wake-up enabled status */

#ifdef CONFIG_USBDEV_REMOTEWAKEUP
                          if (g_usbdev.wkupen)
                            {
                              status |= (1 << USB_FEATURE_REMOTEWAKEUP);
                            }
#endif
                          g_usbdev.ep0buf[1] = 0;

                          /* And send the response */

                          avr_ep0send(g_usbdev.ep0buf, 2);
                        }
                      else
                        {
                          usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADDEVGETSTATUS), 0);
                          g_usbdev.stalled = true;
                        }
                    }
                    break;

                  case USB_REQ_RECIPIENT_INTERFACE:
                    {
                      usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_IFGETSTATUS), 0);
                      g_usbdev.ep0buf[0] = 0;
                      g_usbdev.ep0buf[1] = 0;

                      avr_ep0send(g_usbdev.ep0buf, 2);
                    }
                    break;

                  default:
                    {
                      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADGETSTATUS), 0);
                      g_usbdev.stalled = true;
                    }
                    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(AVR_TRACEINTID_CLEARFEATURE), 0);
            switch (ctrl.type & USB_REQ_RECIPIENT_MASK)
              {
              case USB_REQ_RECIPIENT_ENDPOINT:
                if (g_usbdev.paddrset != 0 &&
                    value == USB_FEATURE_ENDPOINTHALT &&
                    len == 0 &&
                    (privep = avr_epfindbyaddr(index)) != NULL)
                  {
                    avr_epstall(&privep->ep, false);
                  }
                else
                  {
                    usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADCLREPFEATURE), value);
                    g_usbdev.stalled = true;
                  }
                break;

              case USB_REQ_RECIPIENT_DEVICE:
#ifdef CONFIG_USBDEV_SELFPOWERED
                if (g_usbdev.paddrset != 0 &&
                    value == USB_FEATURE_REMOTEWAKEUP &&
                    len == 0)
                  {
                    g_usbdev.wkupen = 0;
                  }
                else
                  {
                    usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADCLRDEVFEATURE), value);
                    g_usbdev.stalled = true;
                  }
                break;
#endif

              default:
                 avr_dispatchrequest(&ctrl);
                 break;
              }
          }
          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(AVR_TRACEINTID_SETFEATURE), 0);
            switch (ctrl.type & USB_REQ_RECIPIENT_MASK)
              {
              case USB_REQ_RECIPIENT_ENDPOINT:
                if (g_usbdev.paddrset != 0 &&
                    value == USB_FEATURE_ENDPOINTHALT &&
                    len == 0 &&
                    (privep = avr_epfindbyaddr(index)) != NULL)
                  {
                    avr_epstall(&privep->ep, true);
                  }
                else
                  {
                    usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETEPFEATURE), value);
                    g_usbdev.stalled = true;
                  }
                break;

              case USB_REQ_RECIPIENT_DEVICE:
#ifdef CONFIG_USBDEV_SELFPOWERED
                if (value == USB_FEATURE_TESTMODE)
                  {
                    ullvdbg("test mode: %d\n", index);
                  }
                else if (value == USB_FEATURE_REMOTEWAKEUP)
                  {
                     g_usbdev.wkupen = 1;
                  }
                else
                  {
                    usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETDEVFEATURE), value);
                    g_usbdev.stalled = true;
                  }
                break;
#endif

              default:
                 avr_dispatchrequest(&ctrl);
                 break;
              }
          }
          break;

        case USB_REQ_SETADDRESS:
          {
            /* type: host-to-device; recipient = device value: device address
             * index: 0 len: 0; data = none
             */

            usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EP0SETUPSETADDRESS), value);
            if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
                 index == 0 &&
                 len == 0 &&
                 value < 128)
              {
                /* Save the address */

                avr_setaddress(ctrl.value[0]);
              }
            else
              {
                usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETADDRESS), 0);
                g_usbdev.stalled = true;
              }
          }
          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(AVR_TRACEINTID_GETSETDESC), 0);
            if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE)
              {
                avr_dispatchrequest(&ctrl);
              }
            else
              {
                usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADGETSETDESC), 0);
                g_usbdev.stalled = true;
              }
          }
          break;

        case USB_REQ_GETCONFIGURATION:
          {
            /* type: device-to-host; recipient = device value: 0; index: 0; len:
             * 1; data = configuration value
             */

            usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_GETCONFIG), 0);
            if (g_usbdev.paddrset &&
                (ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
                value == 0 &&
                index == 0 &&
                len == 1)
              {
                avr_dispatchrequest(&ctrl);
              }
            else
              {
                usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADGETCONFIG), 0);
                g_usbdev.stalled = true;
              }
          }
          break;

        case USB_REQ_SETCONFIGURATION:
          {
            /* type: host-to-device; recipient = device value: configuration
             * value index: 0; len: 0; data = none
             */

            usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_SETCONFIG), 0);
            if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
                index == 0 &&
                len == 0)
              {
                avr_dispatchrequest(&ctrl);
              }
            else
              {
                usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETCONFIG), 0);
                g_usbdev.stalled = true;
              }
          }
          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(AVR_TRACEINTID_GETSETIF), 0);
            avr_dispatchrequest(&ctrl);
          }
          break;

        case USB_REQ_SYNCHFRAME:
          {
            /* type: device-to-host; recipient = endpoint value: 0 index:
             * endpoint; len: 2; data = frame number
             */

            usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_SYNCHFRAME), 0);
          }
          break;

        default:
          {
            usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDCTRLREQ), 0);
            g_usbdev.stalled = true;
          }
          break;
        }
    }

  if (g_usbdev.stalled)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0SETUPSTALLED), 0);
      avr_epstall(&g_usbdev.eplist[AVR_EP0].ep, false);
      avr_epstall(&g_usbdev.eplist[AVR_EP0].ep, false);
    }

  if ((UEINTX & (1 << RXSTPI)) != 0)
    {
      UECONX |= (1 << STALLRQ);
      UEINTX &= ~(1 << RXSTPI);
    }
}

/****************************************************************************
 * Name: avr_ep0interrupt
 *
 * Description:
 *   USB endpoint/pipe IN interrupt handler
 *
 ****************************************************************************/

static inline void avr_ep0interrupt(void)
{
  /* Check if the control endpoint endpoint is pending */

  if ((UEINT & (1 << AVR_EP0)) != 0)
    {
      /* Clear the endpoint interrupt */

      UEINT &= ~(1 << AVR_EP0);

      /* Select the control endpoint */

      UENUM = AVR_EP0;

      /* Check if the control endpoint has received a setup packet */

      if ((UEINTX & (1 << RXSTPI)) != 0)
        {
          /* It has... process the control packet */

          avr_ep0setup();
        }

      /* Handshake the endpoint setup interrupt */

      UEINTX &= ~(1 << RXSTPI);
    }
}

/****************************************************************************
 * Name: avr_epNinterrupt
 *
 * Description:
 *   USB endpoint/pipe IN interrupt handler
 *
 ****************************************************************************/

static inline void avr_epNinterrupt(void)
{
  struct avr_ep_s *privep;
  uint8_t ueint = UEINT & (g_usbdev.epoutset | g_usbdev.epinset);
  uint8_t epno;
  uint8_t mask;

  /* Check if any endpoint interrupt is pending */

  for (epno = 1, mask = 2; epno < AVR_NENDPOINTS && ueint != 0; epno++, mask <<= 1)
    {
      /* Is there an interrupt pending on this endpoint? */

      if ((ueint & mask) != 0)
        {
          ueint &= ~mask;

          /* Select the endpoint */

          UENUM = epno;
          privep = &g_usbdev.eplist[epno];

          /* Is this an IN or an OUT interrupt? */

          if (privep->epin)
            {
              /* Clear the endpoint IN interrupt flag (TXINI) */

              UEINTX &= ~(1 << TXINI);

              /* Are IN endpoint interrupts enabled? */

              if ((UEIENX & (1 << TXINE)) != 0)
                {
                  /* Clear the endpoint interrupt */

                  UEINT &= ~(1 << epno);

                  /* Handle the IN request queue */

                  (void)avr_epINqueue(privep);
                }
            }
          else
            {
              /* Is is an OUT endpoint interrupt.  Are OUT endpoint
               * interrupts enabled?
               */

              if ((UEIENX & (1 << RXOUTE)) != 0)
                {
                  /* Clear the endpoint interrupt */

                  UEINT &= ~(1 << epno);

                  /* Handle the OUT request queue */

                  (void)avr_epOUTqueue(privep);
                }
            }
        }
    }
}

/****************************************************************************
 * Name: avr_epinterrupt
 *
 * Description:
 *   USB endpoint/pipe interrupt handler
 *
 ****************************************************************************/

static int avr_epinterrupt(int irq, FAR void *context)
{
  usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_EPINT), irq);

  /* Handle control endpoint interrupts */

  avr_ep0interrupt();

  /* Handle opther endpoint interrupts (N=1,2,..6 */

  avr_epNinterrupt();

  usbtrace(TRACE_INTEXIT(AVR_TRACEINTID_EPINT), irq);
  return OK;
}

/****************************************************************************
 * Name: avr_genvbus
 *
 * Description:
 *   A change in VBUS has been detected.  Check if the device has been
 *   connected to or disconnected from a host.
 *
 ****************************************************************************/

static void avr_genvbus(void)
{
  bool vbus;

  usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_VBUS), USBSTA);

  /* How has the VSUS signal changed? */

  vbus = ((USBSTA & (1 << VBUS)) != 0);
  if (!g_usbdev.connected && vbus)
    {
      /* We were not connected, but now we are */

      avr_usbreset();
      g_usbdev.connected = true;
    }
  else if (g_usbdev.connected && !vbus)
    {
      /* We were connected, but now we are not */
      /* Cancel all pending and queue requests */

      avr_cancelall(-ENODEV);

      /* Detach the device from the USB bus, terminating communications */

      UDCON |= (1 << DETACH);

      /* Disable the clock inputs (the ”Resume Detection” is still active).
       * This reduces the power consumption. Clear to enable the clock inputs. */

      USBCON |= (1 << FRZCLK);

      /* Shut down the USB PLL */

      PLLCSR = 0;

      /* Disable the USB pad regulator */

      UHWCON &= ~(1 << UVREGE);
      g_usbdev.connected = false;
    }
}

/****************************************************************************
 * Name: avr_gensuspend
 *
 * Description:
 *   The USB controller has been put in suspend mode.
 *
 ****************************************************************************/

static inline void avr_gensuspend(void)
{
  usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_SUSPEND), UDIEN);

  /* Notify the class driver of the suspend event */

  if (g_usbdev.driver)
    {
      CLASS_SUSPEND(g_usbdev.driver, &g_usbdev.usbdev);
    }

  /* Disable suspend event interrupts; enable wakeup event interrupt */

  UDIEN &= ~(1 << SUSPE);
  UDIEN |= (1 << WAKEUPE);

  /* Disable the clock inputs to reduce power consumption. (wakeup
   * detection is still active).
   */

  USBCON |= (1 << FRZCLK);

  /* And shut down the USB PLL */

  PLLCSR = 0;
}

/****************************************************************************
 * Name: avr_genwakeup
 *
 * Description:
 *  Resume from suspend mode.
 *
 ****************************************************************************/

static void avr_genwakeup(void)
{
  usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_WAKEUP), UDIEN);

  /* Re-enable the PLL */

  PLLCSR  =  USB_PLL_PSC;
  PLLCSR |= (1 << PLLE);
  while ((PLLCSR & (1 << PLOCK)) == 0);

  /* Re-enable USB clock inputs */

  USBCON &= ~(1 << FRZCLK);
  UDIEN  &= ~(1 << WAKEUPE);
  UDIEN  |=  (1 << SUSPE);

  /* Notify the class driver of the resume event */

  if (g_usbdev.driver)
    {
      CLASS_RESUME(g_usbdev.driver, &g_usbdev.usbdev);
    }
}

/****************************************************************************
 * Name: avr_geneor
 *
 * Description:
 *  Handle an end-of-reset interrupt
 *
 ****************************************************************************/

static inline void avr_geneor(void)
{
  uint8_t epno;

  usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_EOR), UDIEN);

  UDIEN &= ~(1 << SUSPE);
  UDIEN |= (1 << WAKEUPE);

  /* Reset all endpoints and reconfigure endpoint 0 */

  UEINT = 0;
  for (epno = 0; epno < AVR_NENDPOINTS; epno++)
    {
      struct avr_ep_s *privep = &g_usbdev.eplist[epno];
      avr_epreset(privep, -EAGAIN);
    }

  usbtrace(TRACE_EPCONFIGURE, AVR_EP0);

  /* Configure endpoint 0 */

  (void)avr_ep0configure();

  /* Reset endpoint status */

  g_usbdev.stalled = false;

  /* Enable the endpoint SETUP interrupt ISR for the control endpoint */

  UEIENX |= (1 << RXSTPE);
}

/****************************************************************************
 * Name: avr_geninterrupt
 *
 * Description:
 *   USB general interrupt handler
 *
 ****************************************************************************/

static int avr_geninterrupt(int irq, FAR void *context)
{
  usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_GENINT), irq);

  /* Check for a change in VBUS state detected */

  if ((USBINT & (1 << VBUSTI)) != 0 && (USBCON & (1 << VBUSTE)) != 0)
    {
      USBINT &= ~(1 << VBUSTI);
      avr_genvbus();
    }

  /* Check for a suspend event */

  if ((UDINT & (1 << SUSPI)) != 0 && (UDIEN & (1 << SUSPE)) != 0)
    {
      UDINT &= ~(1 << SUSPI);
      avr_gensuspend();
    }

  /* Check for a wake-up event */

  if ((UDINT & (1 << WAKEUPI)) != 0 && (UDIEN & (1 << WAKEUPE)) != 0)
    {
      UDINT &= ~(1 << WAKEUPI);
      avr_genwakeup();
    }

  /* Check for an end-of-reset, speed identification interrupt */

  if ((UDINT & (1 << EORSTI)) != 0 && (UDIEN & (1 << EORSTE)) != 0)
    {
      UDINT &= ~(1 << EORSTI);
      avr_geneor();
    }

  usbtrace(TRACE_INTEXIT(AVR_TRACEINTID_GENINT), irq);
  return OK;
}

/****************************************************************************
 * Name: avr_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 avr_epconfigure(FAR struct usbdev_ep_s *ep,
                           FAR const struct usb_epdesc_s *desc, bool last)
{
  FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep;
  uint16_t maxpacket = GETUINT16(desc->mxpacketsize);
  uint8_t uecfg0x;
  uint8_t uecfg1x;
  uint8_t ueienx = 0;
  uint8_t regval;

  usbtrace(TRACE_EPCONFIGURE, ep->eplog);
  DEBUGASSERT(ep->eplog != 0 && desc->addr == ep->eplog);

  /* Configure the endpoint */

  uecfg0x = 0;
  uecfg1x = (1 << ALLOC);

  /* Handle the endpoint type */

  switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK)
    {
    case USB_EP_ATTR_XFER_CONTROL:
      uecfg0x |= AVR_EPTYPE_CTRL;
      break;

    case USB_EP_ATTR_XFER_ISOC:
      uecfg0x |= AVR_EPTYPE_ISOC;
      break;

    case USB_EP_ATTR_XFER_BULK:
      uecfg0x |= AVR_EPTYPE_BULK;
      break;

    case USB_EP_ATTR_XFER_INT:
      uecfg0x |= AVR_EPTYPE_INTR;
      break;

    default:
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_XFERTYPE), desc->attr);
      return -EINVAL;
    }

  /* Handle the endpoint direction */

  if (USB_ISEPIN(desc->addr))
    {
      DEBUGASSERT(privep->epin != 0);
      uecfg0x |= AVR_DIR_IN;
      ueienx   = (1 << RXOUTE);
    }
  else
    {
      DEBUGASSERT(privep->epin == 0);
    }

  /* Handle banking (this needs to be revisited... Always double bank?) */

  uecfg1x |= AVR_DOUBLE_BANK;

  /* Handle the maximum packet size */

  switch (maxpacket)
    {
      case 8:
        uecfg1x |= AVR_EPSIZE_8;
        break;
      case 16:
        uecfg1x |= AVR_EPSIZE_16;
        break;
      case 32:
        uecfg1x |= AVR_EPSIZE_32;
        break;
      case 64:
        uecfg1x |= AVR_EPSIZE_64;
        break;
      case 128:
        uecfg1x |= AVR_EPSIZE_128;
        break;
      case 256:
        if (ep->eplog == 1)
          {
            uecfg1x |= AVR_EPSIZE_8;
            break;
          }
      default:
        usbtrace(TRACE_DEVERROR(AVR_TRACEERR_PKTSIZE), maxpacket);
        return -EINVAL;
    }

  /* Instantiate the configuration */

  UENUM   = ep->eplog;
  UECONX |= (1 << EPEN);
  UECFG1X = 0;
  UECFG0X = uecfg0x;
  UECFG1X = uecfg1x;

  /* Check for configuration failure */

  regval = UESTA0X;
  if ((regval &  (1 << CFGOK)) == 0)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EPCFGBAD), regval);
      return -EINVAL;
    }

  /* Save the new max packet size and reset endpoint status */

  privep->ep.maxpacket = maxpacket;
  privep->stalled      = 0;

  /* Enable interrupts as appropriate for this endpoint */

  UEIENX |= uecfg1x;
  return OK;
}

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

static int avr_epdisable(FAR struct usbdev_ep_s *ep)
{
  FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep;
  irqstate_t flags;

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

  flags = irqsave();

  /* Disable the endpoint */

  avr_epreset(privep, -ESHUTDOWN);
  g_usbdev.stalled = true;

  irqrestore(flags);
  return OK;
}

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

static FAR struct usbdev_req_s *avr_epallocreq(FAR struct usbdev_ep_s *ep)
{
  FAR struct avr_req_s *privreq;

#ifdef CONFIG_DEBUG
  if (!ep)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0);
      return NULL;
    }
#endif
  usbtrace(TRACE_EPALLOCREQ, ((FAR struct avr_ep_s *)ep)->ep.eplog);

  privreq = (FAR struct avr_req_s *)kmm_malloc(sizeof(struct avr_req_s));
  if (!privreq)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_ALLOCFAIL), 0);
      return NULL;
    }

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

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

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

#ifdef CONFIG_DEBUG
  if (!ep || !req)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0);
      return;
    }
#endif

  usbtrace(TRACE_EPFREEREQ, ((FAR struct avr_ep_s *)ep)->ep.eplog);
  kmm_free(privreq);
}

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

#ifdef CONFIG_USBDEV_DMA
static void *avr_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes)
{
  usbtrace(TRACE_EPALLOCBUFFER, privep->ep.eplog);

#ifdef CONFIG_USBDEV_DMAMEMORY
  return usbdev_dma_alloc(bytes);
#else
  return kmm_malloc(bytes);
#endif
}
#endif

/****************************************************************************
 * Name: avr_epfreebuffer
 *
 * Description:
 *   Free an I/O buffer
 *
 ****************************************************************************/

#ifdef CONFIG_USBDEV_DMA
static void avr_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf)
{
  usbtrace(TRACE_EPFREEBUFFER, privep->ep.eplog);

#ifdef CONFIG_USBDEV_DMAMEMORY
  usbdev_dma_free(buf);
#else
  kmm_free(buf);
#endif
}
#endif

/****************************************************************************
 * Name: avr_epsubmit
 *
 * Description:
 *   Submit an I/O request to the endpoint
 *
 ****************************************************************************/

static int avr_epsubmit(FAR struct usbdev_ep_s *ep,
                        FAR struct usbdev_req_s *req)
{
  FAR struct avr_req_s *privreq = (FAR struct avr_req_s *)req;
  FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep;
  irqstate_t flags;
  int ret = OK;

#ifdef CONFIG_DEBUG
  if (!req || !req->callback || !req->buf || !ep)
    {
      usbtrace(TRACE_DEVERROR(AVR_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->ep.eplog);

  if (!g_usbdev.driver || g_usbdev.usbdev.speed == USB_SPEED_UNKNOWN)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_NOTCONFIGURED), g_usbdev.usbdev.speed);
      return -ESHUTDOWN;
    }

  /* Handle the request from the class driver */

  req->result = -EINPROGRESS;
  req->xfrd   = 0;

  /* Disable Interrupts */

  flags = irqsave();

  /* If we are stalled, then drop all requests on the floor */

  if (g_usbdev.stalled)
    {
      ret = -EBUSY;
    }

  /* Ignore any attempt to enqueue a zero length packet */

  else if (privreq->req.len == 0)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EPNULLPACKET), 0);
      ret = -EINVAL;
    }
  else
    {
      /* Add the new request to the request queue for the endpoint */

      avr_rqenqueue(privep, privreq);

      /* Some special operations have to be performed for IN requests.  For
       * these, we may have to initiate the next transfer.
       */

      if (privep->epin)
        {
          /* It is an IN transfer */

          usbtrace(TRACE_INREQQUEUED(privep->ep.eplog), privreq->req.len);

          /* Is there an IN transfer in progress (waiting for the FIFO)? If
           * not and if the FIFO is available now, then start the next
           * IN transfer.
           */

          if (!privep->pending && avr_fifoready(AVR_TIMEOUT_NONE) == OK)
            {
              /* No, then start the next IN transfer */

              ret = avr_epINqueue(privep);
            }
        }
      else
        {
          /* It is an OUT transfer */

          usbtrace(TRACE_OUTREQQUEUED(privep->ep.eplog), privreq->req.len);

          /* If there is something avaible in the fifo now, then go get it */

          if (avr_fifoready(AVR_TIMEOUT_NONE) == OK);
            {
              ret = avr_epOUTqueue(privep);
            }
        }
    }

  irqrestore(flags);
  return ret;
}

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

static int avr_epcancel(FAR struct usbdev_ep_s *ep,
                        FAR struct usbdev_req_s *req)
{
  FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep;
  irqstate_t flags;

#ifdef CONFIG_DEBUG
  if (!ep || !req)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  usbtrace(TRACE_EPCANCEL, privep->ep.eplog);

  /* FIXME: if the request is the first, then we need to flush the EP otherwise
   * just remove it from the list but ... all other implementations cancel all
   * requests ... */

  flags = irqsave();
  avr_cancelrequests(privep, -ESHUTDOWN);
  irqrestore(flags);
  return OK;
}

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

static int avr_epstall(FAR struct usbdev_ep_s *ep, bool resume)
{
  irqstate_t flags;

  /* STALL or RESUME the endpoint */

  flags = irqsave();
  if (resume)
    {
      /* Clear stall and reset the data toggle */

      UECONX       |= (1 << STALLRQC);
      UERST         = (1 << ep->eplog);
      UERST         = 0;
      UECONX       |= (1 << RSTDT);
      g_usbdev.stalled = false;
    }
  else
    {
      UECONX       |= (1 << STALLRQ);
      g_usbdev.stalled = true;
    }
  irqrestore(flags);
  return OK;
}

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

/****************************************************************************
 * Name: avr_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 *avr_allocep(FAR struct usbdev_s *dev,
                                           uint8_t epno, bool in,
                                           uint8_t eptype)
{
  FAR struct avr_ep_s *privep;
  irqstate_t flags;
  uint8_t epset = g_usbdev.epavail;
  uint8_t epmask;
  uint8_t epndx = 0;

  usbtrace(TRACE_DEVALLOCEP, epno);

  /* Ignore any direction bits in the logical address */

  epno = USB_EPNO(epno);

  /* A logical address of 0 means that any endpoint will do */

  if (epno > 0)
    {
      /* Otherwise, we will return the endpoint structure only for the
       * requested 'logical' endpoint.
       */

#ifdef CONFIG_DEBUG
      if (epno >= AVR_NENDPOINTS)
        {
          usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADEPNO), (uint16_t)epno);
          return NULL;
        }
#endif

      /* 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 &= (1 << epno);
    }

  /* Are any endpoints available? */

  if (epset)
    {
      /* Yes.. now see if any of the request endpoints are available */

      flags = irqsave();

      /* Select the lowest bit in the set of matching, available endpoints */

      for (epndx = 1; epndx < AVR_NENDPOINTS; epndx++)
        {
          epmask = 1 << epndx;
          if ((epset & epmask) != 0)
            {
              /* Initialize the endpoint structure */

              privep           = &g_usbdev.eplist[epndx];
              memset(privep, 0, sizeof(struct avr_ep_s));

              privep->ep.ops       = &g_epops;
              privep->ep.eplog     = epndx;
              privep->ep.maxpacket = (epndx == 1) ? 256 : 64;

              /* Mark the IN/OUT endpoint no longer available */

              g_usbdev.epavail &= ~epmask;
              if (in)
                {
                  g_usbdev.epinset |= epmask;
                  privep->epin      = 1;
                }
              else
                {
                  g_usbdev.epoutset |= epmask;
                  privep->epin       = 0;
                }

              /* And return the pointer to the standard endpoint structure */

              irqrestore(flags);
              return &privep->ep;
            }
        }

      /* Shouldn't get here */

      irqrestore(flags);
    }

  usbtrace(TRACE_DEVERROR(AVR_TRACEERR_NOEP), (uint16_t) epno);
  return NULL;
}

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

static void avr_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep)
{
  FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep;
  irqstate_t flags;
  uint8_t epmask;

  usbtrace(TRACE_DEVFREEEP, (uint16_t) privep->ep.eplog);

  /* Mark the endpoint as available */

  flags = irqsave();
  epmask = (1 << privep->ep.eplog);
  g_usbdev.epavail  |= epmask;
  g_usbdev.epinset  &= ~epmask;
  g_usbdev.epoutset &= ~epmask;
  irqrestore(flags);
}

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

static int avr_getframe(struct usbdev_s *dev)
{
  /* Return the last frame number detected by the hardware */

  usbtrace(TRACE_DEVGETFRAME, 0);
  return (int)UDFNUMH << 8 | (int)UDFNUML;
}

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

static int avr_wakeup(struct usbdev_s *dev)
{
  irqstate_t flags;

  usbtrace(TRACE_DEVWAKEUP, 0);

  flags = irqsave();
  avr_genwakeup();
  irqrestore(flags);
  return OK;
}

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

static int avr_selfpowered(struct usbdev_s *dev, bool selfpowered)
{
  usbtrace(TRACE_DEVSELFPOWERED, (uint16_t) selfpowered);

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

  g_usbdev.selfpowered = selfpowered;
  return OK;
}

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

static int avr_pullup(struct usbdev_s *dev, bool enable)
{
  usbtrace(TRACE_DEVPULLUP, (uint16_t) enable);
  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)
{
  usbtrace(TRACE_DEVINIT, 0);

  /* Initialize the device state structure */

  memset(&g_usbdev, 0, sizeof(struct avr_usbdev_s));
  g_usbdev.usbdev.ops = &g_devops;
  g_usbdev.usbdev.ep0 = &g_usbdev.eplist[AVR_EP0].ep;
  g_usbdev.epavail    = AVR_ALL_EPS & ~(1 << AVR_EP0);

  /* Attach USB controller general interrupt handler */

  if (irq_attach(AT90USB_IRQ_USBGEN, avr_geninterrupt) != 0)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_IRQREGISTRATION), AT90USB_IRQ_USBGEN);
      goto errout;
    }

  /* Attach USB controller endpoint/pipe interrupt handler */

  if (irq_attach(AT90USB_IRQ_USBEP, avr_epinterrupt) != 0)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_IRQREGISTRATION), AT90USB_IRQ_USBEP);
      goto errout;
    }

  /* Shutdown the USB interface to put it in a known initial state */

  avr_usbshutdown();

  /* Select USB device mode */

  UHWCON |= (1 << UIMOD);

  /* Reset the interface to force re-enumeration (the reset operation
   * enables interrupts.
   */

  avr_usbreset();

  /* Set the VBUS pad */

  USBCON |= (1 << OTGPADE);

  /* Disconnect device */

  avr_pullup(&g_usbdev.usbdev, false);
  return;

errout:
  up_usbuninitialize();
}

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

void up_usbuninitialize(void)
{
  irqstate_t flags;

  usbtrace(TRACE_DEVUNINIT, 0);

  if (g_usbdev.driver)
    {
      usbtrace(TRACE_DEVERROR(AVR_TRACEERR_DRIVERREGISTERED), 0);
      usbdev_unregister(g_usbdev.driver);
    }

  /* Disconnect device */

  flags = irqsave();
  avr_pullup(&g_usbdev.usbdev, false);
  g_usbdev.usbdev.speed = USB_SPEED_UNKNOWN;

  /* Detach IRQs */

  irq_detach(AT90USB_IRQ_USBGEN);
  irq_detach(AT90USB_IRQ_USBEP);

  /* Shutdown the USB controller hardware */

  avr_usbshutdown();
  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(AVR_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }

  if (g_usbdev.driver)
    {
      usbtrace(TRACE_DEVERROR(AVR_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(AVR_TRACEERR_BINDFAILED), (uint16_t) - ret);
      g_usbdev.driver = NULL;
    }
  else
    {
      /* FIXME: nothing seems to call DEV_CONNECT(), but we need to set the RS
       * bit to enable the controller.  It kind of makes sense to do this
       * after the class has bound to us... GEN: This bug is really in the
       * class driver.  It should make the soft connect when it is ready to be
       * enumerated.  I have added that logic to the class drivers but left
       * this logic here. */

      avr_pullup(&g_usbdev.usbdev, true);
    }
  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(AVR_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  /* Unbind the class driver */

  CLASS_UNBIND(driver, &g_usbdev.usbdev);

  /* Unhook the driver */

  g_usbdev.driver = NULL;
  return OK;
}

/****************************************************************************
 * Name: avr_pollvbus
 *
 * Description:
 *   Sample VBUS to see if there are changes in our connection status.  There
 *   is actually an interrupt to signal this case so it should not be necessary
 *   to poll our connection status.  However, on certain "noisy" systems, VBUS
 *   may bounce and provide inaccurate information in the interrupt handler
 *   (especially if a relay is used to switch VBUS!).  This poll is, then,
 *   simply a failsafe to assure that VBUS connection events are never missed.
 *
 ****************************************************************************/

#ifdef CONFIG_USB_NOISYVBUS
void avr_pollvbus(void)
{
  irqstate_t flags;

  flags = irqsave();
  avr_genvbus();
  irqrestore(flags);
}
#endif