/****************************************************************************
 * arch/mips/src/pic32mx/pic32mx_usbdev.c
 *
 *   Copyright (C) 2011-2014 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * References:
 *   This file derives from the STM32 USB device driver with modifications
 *   based on additional information from:
 *
 *   - "USB On-The-Go (OTG)", DS61126E, Microchip Technology Inc., 2009
 *   - Sample code provided with the Sure Electronics PIC32 board
 *     (which seems to have derived from Microchip PICDEM PIC18 code).
 *
 * 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/wdog.h>
#include <nuttx/kmalloc.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>

#include <arch/irq.h>

#include "up_arch.h"
#include "pic32mx-internal.h"
#include "pic32mx-usbotg.h"

#if defined(CONFIG_USBDEV) && defined(CONFIG_PIC32MX_USBDEV)

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

#ifndef CONFIG_USBDEV_EP0_MAXSIZE
#  define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif

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

#ifndef CONFIG_DEBUG
#  undef CONFIG_PIC32MX_USBDEV_REGDEBUG
#  undef CONFIG_PIC32MX_USBDEV_BDTDEBUG
#endif

/* Disable this logic because it is buggy.  It works most of the time but
 * has some lurking issues that keep this higher performance solution from
 * being usable.
 */

#undef CONFIG_USBDEV_NOREADAHEAD      /* Makes no difference */

#undef CONFIG_USBDEV_NOWRITEAHEAD
#define CONFIG_USBDEV_NOWRITEAHEAD 1  /* Fixes some problems with IN transfers */

/* Interrupts ***************************************************************/
/* Initial interrupt sets */

#ifdef CONFIG_USB_SOFINTS
#  define USB_SOF_INTERRUPT USB_INT_SOF
#else
#  define USB_SOF_INTERRUPT 0
#endif

#define ERROR_INTERRUPTS  (USB_EINT_PID | USB_EINT_CRC5 | USB_EINT_EOF | \
                           USB_EINT_CRC16 | USB_EINT_DFN8 | USB_EINT_BTO | \
                           USB_EINT_BTS)
#define NORMAL_INTERRUPTS (USB_INT_URST | USB_INT_UERR | USB_SOF_INTERRUPT | \
                           USB_INT_TRN | USB_INT_IDLE | USB_INT_STALL)

/* Endpoints ****************************************************************/
/* Endpoint identifiers. The PIC32MX supports up to 16 mono-directional or 8
 * bidirectional endpoints.  However, when you take into account PMA buffer
 * usage (see below) and the fact that EP0 is bidirectional, then there is
 * a functional limitation of EP0 + 5 mono-directional endpoints = 6.  We'll
 * define PIC32MX_NENDPOINTS to be 8, however, because that is how many
 * endpoint register sets there are.
 */

#define PIC32MX_NENDPOINTS    (16)
#define EP0                   (0)

#define PIC32MX_ENDP_BIT(ep)  (1 << (ep))
#define PIC32MX_ENDP_ALLSET   0xffff

/* BDT Table Indexing.  The BDT is addressed in the hardware as follows:
 *
 *   Bits 9-31:  These come the BDT address bits written into the BDTP3,
 *      BDTP2, and BDTP1 registers
 *   Bits 5-8:   The endpoint number
 *   Bit 4:      Direction:
 *               1 = Transmit: SETUP/OUT for host, IN for function
 *               0 = Receive: IN for host, SETUP/OUT for function
 *   Bit 3:      PPBI, the ping point buffer index bit (0=EVEN, 1=ODD)
 *   Bits 0-2:   Supports 8-byte BDT entries
 */

#define EP0_OUT_EVEN          (0)
#define EP0_OUT_ODD           (1)
#define EP0_IN_EVEN           (2)
#define EP0_IN_ODD            (3)
#define EP_OUT_EVEN(ep)       ((int)(ep) << 2)
#define EP_OUT_ODD(ep)        (((int)(ep) << 2) + 1)
#define EP_IN_EVEN(ep)        (((int)(ep) << 2) + 2)
#define EP_IN_ODD(ep)         (((int)(ep) << 2) + 3)

#define EP(ep,dir,pp)         (((int)(ep) << 2) + ((int)(dir) << 1) + (int)(pp))
#define EP_DIR_OUT            0
#define EP_DIR_IN             1
#define EP_PP_EVEN            0
#define EP_PP_ODD             1

/* Packet sizes.  We use a fixed 64 max packet size for all endpoint types */

#define PIC32MX_MAXPACKET_SHIFT (6)
#define PIC32MX_MAXPACKET_SIZE  (1 << (PIC32MX_MAXPACKET_SHIFT))

#define PIC32MX_EP0MAXPACKET  PIC32MX_MAXPACKET_SIZE

/* Endpoint register initialization parameters */

#define PIC32MX_EP_CONTROL    (USB_EP_EPHSHK|USB_EP_EPTXEN|USB_EP_EPRXEN)
#define PIC32MX_EP_BULKIN     (USB_EP_EPTXEN|USB_EP_EPCONDIS|USB_EP_EPHSHK)
#define PIC32MX_EP_BULKOUT    (USB_EP_EPRXEN|USB_EP_EPCONDIS|USB_EP_EPHSHK)
#define PIC32MX_EP_INTIN      (USB_EP_EPTXEN|USB_EP_EPCONDIS|USB_EP_EPHSHK)
#define PIC32MX_EP_INTOUT     (USB_EP_EPRXEN|USB_EP_EPCONDIS|USB_EP_EPHSHK)
#define PIC32MX_EP_ISOCIN     (USB_EP_EPTXEN|USB_EP_EPCONDIS)
#define PIC32MX_EP_ISOCOUT    (USB_EP_EPRXEN|USB_EP_EPCONDIS)

/* USB-related masks */

#define REQRECIPIENT_MASK     (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)

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

#define pic32mx_rqempty(q)    ((q)->head == NULL)
#define pic32mx_rqhead(q)     ((q)->head)
#define pic32mx_rqtail(q)     ((q)->tail)

#define RESTART_DELAY         (150 * CLOCKS_PER_SEC / 1000)

/* USB trace ****************************************************************/
/* Trace error codes */

#define PIC32MX_TRACEERR_ALLOCFAIL            0x0001
#define PIC32MX_TRACEERR_BADCLEARFEATURE      0x0002
#define PIC32MX_TRACEERR_BADDEVGETSTATUS      0x0003
#define PIC32MX_TRACEERR_BADEPGETSTATUS       0x0004
#define PIC32MX_TRACEERR_BADEPNO              0x0005
#define PIC32MX_TRACEERR_BADEPTYPE            0x0006
#define PIC32MX_TRACEERR_BADGETCONFIG         0x0007
#define PIC32MX_TRACEERR_BADGETSETDESC        0x0008
#define PIC32MX_TRACEERR_BADGETSTATUS         0x0009
#define PIC32MX_TRACEERR_BADSETADDRESS        0x000a
#define PIC32MX_TRACEERR_BADSETCONFIG         0x000b
#define PIC32MX_TRACEERR_BADSETFEATURE        0x000c
#define PIC32MX_TRACEERR_BINDFAILED           0x000d
#define PIC32MX_TRACEERR_DISPATCHSTALL        0x000e
#define PIC32MX_TRACEERR_DRIVER               0x000f
#define PIC32MX_TRACEERR_DRIVERREGISTERED     0x0010
#define PIC32MX_TRACEERR_EP0SETUPSTALLED      0x0011
#define PIC32MX_TRACEERR_EPDISABLED           0x0012
#define PIC32MX_TRACEERR_EPOUTNULLPACKET      0x0013
#define PIC32MX_TRACEERR_EPRESERVE            0x0014
#define PIC32MX_TRACEERR_INVALIDCTRLREQ       0x0015
#define PIC32MX_TRACEERR_INVALIDPARMS         0x0016
#define PIC32MX_TRACEERR_IRQREGISTRATION      0x0017
#define PIC32MX_TRACEERR_NOTCONFIGURED        0x0018
#define PIC32MX_TRACEERR_REQABORTED           0x0019
#define PIC32MX_TRACEERR_INVALIDSTATE         0x001a

/* Trace interrupt codes */

#define PIC32MX_TRACEINTID_CLEARFEATURE       0x0001
#define PIC32MX_TRACEINTID_DEVGETSTATUS       0x0002
#define PIC32MX_TRACEINTID_DISPATCH           0x0003
#define PIC32MX_TRACEINTID_EP0IN              0x0004
#define PIC32MX_TRACEINTID_EP0INDONE          0x0005
#define PIC32MX_TRACEINTID_EP0OUTDONE         0x0006
#define PIC32MX_TRACEINTID_EP0SETUPDONE       0x0007
#define PIC32MX_TRACEINTID_EP0SETUPSETADDRESS 0x0008
#define PIC32MX_TRACEINTID_EP0ADDRESSSET      0x0009
#define PIC32MX_TRACEINTID_EPGETSTATUS        0x000a
#define PIC32MX_TRACEINTID_EPINDONE           0x000b
#define PIC32MX_TRACEINTID_EPINQEMPTY         0x000c
#define PIC32MX_TRACEINTID_EPOUTDONE          0x000d
#define PIC32MX_TRACEINTID_EPOUTQEMPTY        0x000e
#define PIC32MX_TRACEINTID_SOF                0x000f
#define PIC32MX_TRACEINTID_GETCONFIG          0x0010
#define PIC32MX_TRACEINTID_GETSETDESC         0x0011
#define PIC32MX_TRACEINTID_GETSETIF           0x0012
#define PIC32MX_TRACEINTID_GETSTATUS          0x0013
#define PIC32MX_TRACEINTID_IFGETSTATUS        0x0014
#define PIC32MX_TRACEINTID_TRNC               0x0015
#define PIC32MX_TRACEINTID_TRNCS              0x0016
#define PIC32MX_TRACEINTID_INTERRUPT          0x0017
#define PIC32MX_TRACEINTID_NOSTDREQ           0x0018
#define PIC32MX_TRACEINTID_RESET              0x0019
#define PIC32MX_TRACEINTID_SETCONFIG          0x001a
#define PIC32MX_TRACEINTID_SETFEATURE         0x001b
#define PIC32MX_TRACEINTID_IDLE               0x001c
#define PIC32MX_TRACEINTID_SYNCHFRAME         0x001d
#define PIC32MX_TRACEINTID_WKUP               0x001e
#define PIC32MX_TRACEINTID_T1MSEC             0x001f
#define PIC32MX_TRACEINTID_OTGID              0x0020
#define PIC32MX_TRACEINTID_STALL              0x0021
#define PIC32MX_TRACEINTID_UERR               0x0022
#define PIC32MX_TRACEINTID_SUSPENDED          0x0023
#define PIC32MX_TRACEINTID_WAITRESET          0x0024

/* Misc Helper Macros *******************************************************/

#define PHYS_ADDR(va) ((uint32_t)(va) & 0x1fffffff)
#define VIRT_ADDR(pa) (KSEG1_BASE | (uint32_t)(pa))

/* Ever-present MIN and MAX macros */

#ifndef MIN
#  define MIN(a,b) (a < b ? a : b)
#endif

#ifndef MAX
#  define MAX(a,b) (a > b ? a : b)
#endif

/* Byte ordering in host-based values */

#ifdef CONFIG_ENDIAN_BIG
#  define LSB 1
#  define MSB 0
#else
#  define LSB 0
#  define MSB 1
#endif

/* Debug ********************************************************************/
/* CONFIG_PIC32MX_USBDEV_REGDEBUG enables dumping of all low-level register
 * access and BDT accesses.  Normally, this generates so much debug output
 * that USB may not even be functional.
 */

#ifdef CONFIG_PIC32MX_USBDEV_REGDEBUG

#  undef CONFIG_PIC32MX_USBDEV_BDTDEBUG
#  define CONFIG_PIC32MX_USBDEV_BDTDEBUG 1

#  define regdbg lldbg
#  ifdef CONFIG_DEBUG_VERBOSE
#    define regvdbg lldbg
#  else
#    define regvdbg(x...)
#  endif

#else

#  define pic32mx_getreg(addr)      getreg16(addr)
#  define pic32mx_putreg(val,addr)  putreg16(val,addr)
#  define regdbg(x...)
#  define regvdbg(x...)

#endif

/* CONFIG_PIC32MX_USBDEV_BDTDEBUG dumps most BDT settings */

#ifdef CONFIG_PIC32MX_USBDEV_BDTDEBUG

#  define bdtdbg lldbg
#  ifdef CONFIG_DEBUG_VERBOSE
#    define bdtvdbg lldbg
#  else
#    define bdtvdbg(x...)
#  endif

#else

#  define bdtdbg(x...)
#  define bdtvdbg(x...)

#endif

/****************************************************************************
 * Private Type Definitions
 ****************************************************************************/

/* Overvall device state */

enum pic32mx_devstate_e
{
  DEVSTATE_DETACHED = 0,  /* Not connected to a host */
  DEVSTATE_ATTACHED,      /* Connected to a host */
  DEVSTATE_POWERED,       /* Powered */
  DEVSTATE_DEFAULT,       /* Default state */
  DEVSTATE_ADDRPENDING,   /* Waiting for an address */
  DEVSTATE_ADDRESS,       /* Address received */
  DEVSTATE_CONFIGURED,    /* Configuration received */
};

/* The various states of the control pipe */

enum pic32mx_ctrlstate_e
{
  CTRLSTATE_WAITSETUP = 0,  /* No request in progress, waiting for setup */
  CTRLSTATE_RDREQUEST,      /* Read request (OUT) in progress */
  CTRLSTATE_WRREQUEST,      /* Write request (IN) in progress */
  CTRLSTATE_STALL,          /* EP0 stall requested */
  CTRLSTATE_STALLED         /* EP0 is stalled */
};

union wb_u
{
  uint16_t w;
  uint8_t  b[2];
};

/* A container for a request so that the request make be retained in a
 * singly-linked list.
 */

struct pic32mx_req_s
{
  struct usbdev_req_s   req;                 /* Standard USB request */
#ifdef CONFIG_USBDEV_NOWRITEAHEAD
  uint16_t              inflight[1];         /* The number of bytes "in-flight" */
#else
  uint16_t              inflight[2];         /* The number of bytes "in-flight" */
#endif
  struct pic32mx_req_s *flink;               /* Supports a singly linked list */
};

/* This structure represents the 'head' of a singly linked list of requests */

struct pic32mx_queue_s
{
  struct pic32mx_req_s    *head;             /* Head of the request queue */
  struct pic32mx_req_s    *tail;             /* Tail of the request queue */
};

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

struct pic32mx_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 pic32mx_ep_s.
   */

  struct usbdev_ep_s       ep;               /* Standard endpoint structure */

  /* PIC32MX-specific fields */

  struct pic32mx_usbdev_s *dev;              /* Reference to private driver data */
  struct pic32mx_queue_s   pend;             /* List of pending (inactive) requests for this endpoint */
  struct pic32mx_queue_s   active;           /* List of active requests for this endpoint */
  uint8_t                  stalled:1;        /* true: Endpoint is stalled */
  uint8_t                  halted:1;         /* true: Endpoint feature halted */
  uint8_t                  txnullpkt:1;      /* Null packet needed at end of TX transfer */
  uint8_t                  txdata1:1;        /* Data0/1 of next TX transfer */
  uint8_t                  rxdata1:1;        /* Data0/1 of next RX transfer */
  volatile struct usbotg_bdtentry_s *bdtin;  /* BDT entry for the IN transaction */
  volatile struct usbotg_bdtentry_s *bdtout; /* BDT entry for the OUT transaction */
};

struct pic32mx_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 structpic32mx_usbdev_s.
   */

  struct usbdev_s usbdev;

  /* The bound device class driver */

  struct usbdevclass_driver_s *driver;

  /* PIC32MX-specific fields */

  struct usb_ctrlreq_s     ctrl;          /* Last EP0 request */
  uint8_t                  devstate;      /* Driver state (see enum pic32mx_devstate_e) */
  uint8_t                  ctrlstate;     /* Control EP state (see enum pic32mx_ctrlstate_e) */
  uint8_t                  selfpowered:1; /* 1: Device is self powered */
  uint8_t                  rwakeup:1;     /* 1: Device supports remote wakeup */
  uint8_t                  attached:1;    /* Device is attached to the host */
  uint8_t                  ep0done:1;     /* EP0 OUT already prepared */
  uint8_t                  rxbusy:1;      /* EP0 OUT data transfer in progress */
  uint16_t                 epavail;       /* Bitset of available endpoints */
  uint16_t                 epstalled;     /* Bitset of stalled endpoints */
  WDOG_ID                  wdog;          /* Supports the restart delay */

  /* The endpoint list */

  struct pic32mx_ep_s      eplist[PIC32MX_NENDPOINTS];
};

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

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

#ifdef CONFIG_PIC32MX_USBDEV_REGDEBUG
static uint16_t pic32mx_getreg(uint32_t addr);
static void pic32mx_putreg(uint16_t val, uint32_t addr);
#endif

/* Suspend/Resume Helpers ***************************************************/

static void   pic32mx_suspend(struct pic32mx_usbdev_s *priv);
static void   pic32mx_resume(struct pic32mx_usbdev_s *priv);

/* Request Queue Management *************************************************/

static struct pic32mx_req_s *pic32mx_remfirst(struct pic32mx_queue_s *queue);
static struct pic32mx_req_s *pic32mx_remlast(struct pic32mx_queue_s *queue);
static void   pic32mx_addlast(struct pic32mx_queue_s *queue,
                struct pic32mx_req_s *req);
static void   pic32mx_addfirst(struct pic32mx_queue_s *queue,
                 struct pic32mx_req_s *req);

/* Request Helpers **********************************************************/

static void   pic32mx_reqreturn(struct pic32mx_ep_s *privep,
                struct pic32mx_req_s *privreq, int16_t result);
static void   pic32mx_reqcomplete(struct pic32mx_ep_s *privep,
                int16_t result);
static void   pic32mx_epwrite(struct pic32mx_ep_s *privep,
                volatile struct usbotg_bdtentry_s *bdt,
                const uint8_t *src, uint32_t nbytes);
static void   pic32mx_wrcomplete(struct pic32mx_usbdev_s *priv,
                struct pic32mx_ep_s *privep);
static void   pic32mx_rqrestart(int argc, uint32_t arg1, ...);
static void   pic32mx_delayedrestart(struct pic32mx_usbdev_s *priv,
                uint8_t epno);
static void   pic32mx_rqstop(struct pic32mx_ep_s *privep);
static int    pic32mx_wrstart(struct pic32mx_usbdev_s *priv,
                struct pic32mx_ep_s *privep);
static int    pic32mx_wrrequest(struct pic32mx_usbdev_s *priv,
                struct pic32mx_ep_s *privep);
static int    pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv,
                struct pic32mx_ep_s *privep);
static int    pic32mx_ep0rdsetup(struct pic32mx_usbdev_s *priv,
                uint8_t *dest, int readlen);
static int    pic32mx_rdsetup(struct pic32mx_ep_s *privep, uint8_t *dest,
                int readlen);
static int    pic32mx_rdrequest(struct pic32mx_usbdev_s *priv,
                struct pic32mx_ep_s *privep);
static void   pic32mx_cancelrequests(struct pic32mx_ep_s *privep,
                int16_t result);

/* Interrupt level processing ***********************************************/

static void   pic32mx_dispatchrequest(struct pic32mx_usbdev_s *priv);
static void   pic32mx_ep0stall(struct pic32mx_usbdev_s *priv);
static void   pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno,
                uint16_t ustat);
static void   pic32mx_ep0nextsetup(struct pic32mx_usbdev_s *priv);
static void   pic32mx_ep0rdcomplete(struct pic32mx_usbdev_s *priv);
static void   pic32mx_ep0setup(struct pic32mx_usbdev_s *priv);
static void   pic32mx_ep0outcomplete(struct pic32mx_usbdev_s *priv);
static void   pic32mx_ep0incomplete(struct pic32mx_usbdev_s *priv);
static void   pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv,
                uint16_t ustat);
static int    pic32mx_interrupt(int irq, void *context);

/* Endpoint helpers *********************************************************/

static inline struct pic32mx_ep_s *
              pic32mx_epreserve(struct pic32mx_usbdev_s *priv, uint8_t epset);
static inline void
              pic32mx_epunreserve(struct pic32mx_usbdev_s *priv,
                struct pic32mx_ep_s *privep);
static inline bool
              pic32mx_epreserved(struct pic32mx_usbdev_s *priv, int epno);
static void  pic32mx_ep0configure(struct pic32mx_usbdev_s *priv);

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

static int    pic32mx_epconfigure(struct usbdev_ep_s *ep,
                const struct usb_epdesc_s *desc, bool last);
static int    pic32mx_epdisable(struct usbdev_ep_s *ep);
static struct usbdev_req_s *
              pic32mx_epallocreq(struct usbdev_ep_s *ep);
static void   pic32mx_epfreereq(struct usbdev_ep_s *ep,
                struct usbdev_req_s *);
static int    pic32mx_epsubmit(struct usbdev_ep_s *ep,
                struct usbdev_req_s *req);
static int    pic32mx_epcancel(struct usbdev_ep_s *ep,
                struct usbdev_req_s *req);
static int    pic32mx_epbdtstall(struct usbdev_ep_s *ep, bool resume,
                bool epin);
static int    pic32mx_epstall(struct usbdev_ep_s *ep, bool resume);

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

static struct usbdev_ep_s *
              pic32mx_allocep(struct usbdev_s *dev, uint8_t epno, bool in,
                uint8_t eptype);
static void   pic32mx_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep);
static int    pic32mx_getframe(struct usbdev_s *dev);
static int    pic32mx_wakeup(struct usbdev_s *dev);
static int    pic32mx_selfpowered(struct usbdev_s *dev, bool selfpowered);

/* Initialization/Reset *****************************************************/

static void   pic32mx_reset(struct pic32mx_usbdev_s *priv);
static void   pic32mx_attach(struct pic32mx_usbdev_s *priv);
static void   pic32mx_detach(struct pic32mx_usbdev_s *priv);
static void   pic32mx_swreset(struct pic32mx_usbdev_s *priv);
static void   pic32mx_hwreset(struct pic32mx_usbdev_s *priv);
static void   pic32mx_stateinit(struct pic32mx_usbdev_s *priv);
static void   pic32mx_hwshutdown(struct pic32mx_usbdev_s *priv);

/****************************************************************************
 * 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 pic32mx_usbdev_s g_usbdev;

static const struct usbdev_epops_s g_epops =
{
  .configure   = pic32mx_epconfigure,
  .disable     = pic32mx_epdisable,
  .allocreq    = pic32mx_epallocreq,
  .freereq     = pic32mx_epfreereq,
  .submit      = pic32mx_epsubmit,
  .cancel      = pic32mx_epcancel,
  .stall       = pic32mx_epstall,
};

static const struct usbdev_ops_s g_devops =
{
  .allocep     = pic32mx_allocep,
  .freeep      = pic32mx_freeep,
  .getframe    = pic32mx_getframe,
  .wakeup      = pic32mx_wakeup,
  .selfpowered = pic32mx_selfpowered,
  .pullup      = pic32mx_usbpullup,
};

/* Buffer Descriptor Table.  Four BDT entries per
 *
 * The BDT is addressed in the hardware as follows:
 *
 *   Bits 9-31:  These come the BDT address bits written into the BDTP3, BDTP2
 *      and BDTP1 registers
 *   Bits 5-8:   The endpoint number
 *   Bit 4:      Direction (0=IN/Tx, 1 = OUT/Rx)
 *   Bit 3:      PPBI, the ping point buffer index bit.
 *   Bits 0-2:   Supports 8-byte BDT entries
 */

static volatile struct usbotg_bdtentry_s g_bdt[4*PIC32MX_NENDPOINTS]
  __attribute__ ((aligned(512)));

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

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

/****************************************************************************
 * Register Operations
 ****************************************************************************/
/****************************************************************************
 * Name: pic32mx_getreg
 ****************************************************************************/

#ifdef CONFIG_PIC32MX_USBDEV_REGDEBUG
static uint16_t pic32mx_getreg(uint32_t addr)
{
  static uint32_t prevaddr = 0;
  static uint16_t preval = 0;
  static uint32_t count = 0;

  /* Read the value from the register */

  uint16_t val = getreg16(addr);

  /* Is this the same value that we read from the same register 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->%04x\n", addr, val);
  return val;
}
#endif

/****************************************************************************
 * Name: pic32mx_putreg
 ****************************************************************************/

#ifdef CONFIG_PIC32MX_USBDEV_REGDEBUG
static void pic32mx_putreg(uint16_t val, uint32_t addr)
{
  /* Show the register value being written */

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

  /* Write the value */

  putreg16(val, addr);
}
#endif

/****************************************************************************
 * Request Helpers
 ****************************************************************************/
/****************************************************************************
 * Name: pic32mx_remfirst
 ****************************************************************************/

static struct pic32mx_req_s *pic32mx_remfirst(struct pic32mx_queue_s *queue)
{
  struct pic32mx_req_s *ret = queue->head;

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

      ret->flink = NULL;
    }

  return ret;
}


/****************************************************************************
 * Name: pic32mx_remlast
 ****************************************************************************/

static struct pic32mx_req_s *pic32mx_remlast(struct pic32mx_queue_s *queue)
{
  struct pic32mx_req_s *prev;
  struct pic32mx_req_s *ret = queue->tail;

  ret = queue->tail;
  if (ret)
    {
      if (queue->head == queue->tail)
        {
          queue->head = NULL;
          queue->tail = NULL;
        }
      else
        {
          for (prev = queue->head;
               prev && prev->flink != ret;
               prev = prev->flink);

          if (prev)
            {
              prev->flink = NULL;
              queue->tail = prev;
            }
        }

      ret->flink = NULL;
    }

  return ret;
}

/****************************************************************************
 * Name: pic32mx_addlast
 ****************************************************************************/

static void pic32mx_addlast(struct pic32mx_queue_s *queue, struct pic32mx_req_s *req)
{
  req->flink = NULL;
  if (!queue->head)
    {
      queue->head = req;
      queue->tail = req;
    }
  else
    {
      queue->tail->flink = req;
      queue->tail        = req;
    }
}

/****************************************************************************
 * Name: pic32mx_addfirst
 ****************************************************************************/

static void pic32mx_addfirst(struct pic32mx_queue_s *queue, struct pic32mx_req_s *req)
{
  req->flink = queue->head;
  if (!queue->head)
    {
      queue->tail = req;
    }
  queue->head = req;
}

/****************************************************************************
 * Name: pic32mx_reqreturn
 ****************************************************************************/

static void pic32mx_reqreturn(struct pic32mx_ep_s *privep,
                              struct pic32mx_req_s *privreq, int16_t result)
{
  /* If endpoint 0, temporarily reflect the state of protocol stalled
   * in the callback.
   */

  bool stalled = privep->stalled;
  if (USB_EPNO(privep->ep.eplog) == EP0)
    {
      privep->stalled = (privep->dev->ctrlstate == CTRLSTATE_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: pic32mx_reqcomplete
 ****************************************************************************/

static void pic32mx_reqcomplete(struct pic32mx_ep_s *privep, int16_t result)
{
  struct pic32mx_req_s *privreq;
  irqstate_t flags;

  /* Remove the completed request at the head of the endpoint's active
   * request list.
   */

  flags = irqsave();
  privreq = pic32mx_remfirst(&privep->active);
  irqrestore(flags);

  if (privreq)
    {
      /* Return the request to the class driver */

      pic32mx_reqreturn(privep, privreq, result);
    }
}

/****************************************************************************
 * Name: pic32mx_epwrite
 ****************************************************************************/

static void pic32mx_epwrite(struct pic32mx_ep_s *privep,
                            volatile struct usbotg_bdtentry_s *bdt,
                            const uint8_t *src, uint32_t nbytes)
{
  uint32_t status;

  usbtrace(TRACE_WRITE(USB_EPNO(privep->ep.eplog)), nbytes);

  /* Clear all bits in the status (assuring that we own the BDT) */

  bdt->status = 0;

  /* Get the correct data toggle (as well as other BDT bits) */

  if (privep->txdata1)
    {
      status          = (USB_BDT_UOWN | USB_BDT_DATA1 | USB_BDT_DTS);
      privep->txdata1 = 0;
    }
  else
    {
      status          = (USB_BDT_UOWN | USB_BDT_DATA0 | USB_BDT_DTS);
      privep->txdata1 = 1;
    }

  /* Set the data pointer and data length */

  bdt->addr = (uint8_t *)PHYS_ADDR(src);
  status   |= (nbytes << USB_BDT_BYTECOUNT_SHIFT) | USB_BDT_DTS;

  /* And, finally, give the BDT to the USB */

  bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n",
         USB_EPNO(privep->ep.eplog), bdt, status, bdt->addr);

  bdt->status = status;
}

/****************************************************************************
 * Name: pic32mx_wrcomplete
 ****************************************************************************/

static void pic32mx_wrcomplete(struct pic32mx_usbdev_s *priv,
                               struct pic32mx_ep_s *privep)
{
  volatile struct usbotg_bdtentry_s *bdtin;
  struct pic32mx_req_s *privreq;
  int bytesleft;
  int epno;

  /* Check the request at the head of the endpoint's active request queue.
   * Since we got here from a write completion event, the active request queue
   * should not be empty.
   */

  privreq = pic32mx_rqhead(&privep->active);
  DEBUGASSERT(privreq != NULL);

  /* An outgoing IN packet has completed.  bdtin should point to the BDT
   * that just completed.
   */

  bdtin = privep->bdtin;
  epno   = USB_EPNO(privep->ep.eplog);

#ifdef CONFIG_USBDEV_NOWRITEAHEAD
  ullvdbg("EP%d: len=%d xfrd=%d inflight=%d\n",
          epno, privreq->req.len, privreq->req.xfrd, privreq->inflight[0]);
#else
  ullvdbg("EP%d: len=%d xfrd=%d inflight={%d, %d}\n",
          epno, privreq->req.len, privreq->req.xfrd,
          privreq->inflight[0], privreq->inflight[1]);
#endif
  bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n",
         epno, bdtin, bdtin->status, bdtin->addr);

  /* We should own the BDT that just completed. But NULLify the entire BDT IN.
   * Why?  So that we can tell later that the BDT available.  No, it is not
   * sufficient to look at the UOWN bit.  If UOWN==0, then the transfer has
   * been completed BUT it may not yet have been processed.  But a completely
   * NULLified BDT is a sure indication
   */

  DEBUGASSERT((bdtin->status & USB_BDT_UOWN) == USB_BDT_COWN);
  bdtin->status = 0;
  bdtin->addr   = 0;

  /* Toggle bdtin to the other BDT.  Is the current bdtin the EVEN bdt? */

  privep->bdtin = &g_bdt[EP_IN_EVEN(epno)];
  if (bdtin == privep->bdtin)
    {
      /* Yes.. Then the other BDT is the ODD BDT */

      privep->bdtin++;
    }

  /* Update the number of bytes transferred. */

  privreq->req.xfrd   += privreq->inflight[0];
#ifdef CONFIG_USBDEV_NOWRITEAHEAD
  privreq->inflight[0] = 0;
#else
  privreq->inflight[0] = privreq->inflight[1];
  privreq->inflight[1] = 0;
#endif
  bytesleft            = privreq->req.len - privreq->req.xfrd;

  /* If all of the bytes were sent (bytesleft == 0) and no NULL packet is
   * needed (!txnullpkt), then we are finished with the transfer
   */

  if (bytesleft == 0 && !privep->txnullpkt)
    {
      /* The transfer is complete.  Give the completed request back to
       * the class driver.
       */

      usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
      pic32mx_reqcomplete(privep, OK);

      /* Special case writes to endpoint zero.  If there is no transfer in
       * progress, then we need to configure to received the next SETUP packet.
       */

      if (USB_EPNO(privep->ep.eplog) == 0)
        {
          priv->ctrlstate = CTRLSTATE_WAITSETUP;
        }
    }
}

/****************************************************************************
 * Name: pic32mx_rqrestart
 ****************************************************************************/

static void pic32mx_rqrestart(int argc, uint32_t arg1, ...)
{
  struct pic32mx_usbdev_s *priv;
  struct pic32mx_ep_s *privep;
  struct pic32mx_req_s *privreq;
  uint16_t epstalled;
  uint16_t mask;
  int epno;

  /* Recover the pointer to the driver structure */

  priv = (struct pic32mx_usbdev_s *)((uintptr_t)arg1);
  DEBUGASSERT(priv != NULL);

  /* Sample and clear the set of endpoints that have recovered from a stall */

  epstalled = priv->epstalled;
  priv->epstalled = 0;

  /* Loop, checking each bit in the epstalled bit set */

  for (epno = 0; epstalled && epno < PIC32MX_NENDPOINTS; epno++)
    {
      /* Has this encpoint recovered from a stall? */

      mask = (1 << epno);
      if ((epstalled & mask) != 0)
        {
          /* Yes, this endpoint needs to be restarteed */

          epstalled      &= ~mask;
          privep          = &priv->eplist[epno];

          /* Reset some endpoint state variables */

          privep->stalled   = false;
          privep->txnullpkt = false;

          /* Check the request at the head of the endpoint's pending request queue */

          privreq = pic32mx_rqhead(&privep->pend);
          if (privreq)
            {
              /* Restart transmission after we have recovered from a stall */

              privreq->req.xfrd    = 0;
              privreq->inflight[0] = 0;
#ifndef CONFIG_USBDEV_NOWRITEAHEAD
              privreq->inflight[1] = 0;
#endif
              (void)pic32mx_wrrequest(priv, privep);
            }
        }
    }
}

/****************************************************************************
 * Name: pic32mx_delayedrestart
 ****************************************************************************/

static void pic32mx_delayedrestart(struct pic32mx_usbdev_s *priv, uint8_t epno)
{
  /* Add endpoint to the set of endpoints that need to be restarted */

  priv->epstalled |= (1 << epno);

  /* And start (or re-start) the watchdog timer */

  wd_start(priv->wdog, RESTART_DELAY, pic32mx_rqrestart, 1, (uint32_t)priv);
}

/****************************************************************************
 * Name: pic32mx_rqstop
 ****************************************************************************/

static void pic32mx_rqstop(struct pic32mx_ep_s *privep)
{
  struct pic32mx_req_s *privreq;

  /* Move all of the active requests back to the pending request queue */

  while ((privreq = pic32mx_remlast(&privep->active)))
    {
      /* Move the request back to the head of the pending list */

      pic32mx_addfirst(&privep->pend, privreq);
    }
}

/****************************************************************************
 * Name: pic32mx_wrstart
 ****************************************************************************/

static int pic32mx_wrstart(struct pic32mx_usbdev_s *priv,
                           struct pic32mx_ep_s *privep)
{
  volatile struct usbotg_bdtentry_s *bdt;
  struct pic32mx_req_s *privreq;
  uint8_t *buf;
  uint8_t epno;
  int nbytes;
  int bytesleft;
  int xfrd;
  int index;

  /* We get here when either (1) an IN endpoint completion interrupt occurs,
   * or (2) a new write request is reqeived from the class.
   */

  /* Get the endpoint number that we are servicing */

  epno = USB_EPNO(privep->ep.eplog);

  /* Decide which BDT to use.  bdtin points to the "current" BDT.  That is,
   * the one that either (1) available for next transfer, or (2) the one
   * that is currently busy with the current transfer.  If the current
   * BDT is busy, we have the option of setting up the other BDT in advance
   * in order to improve data transfer performance.
   */

  bdt   = privep->bdtin;
  index = 0;

  if (bdt->status || bdt->addr)
    {
#ifdef CONFIG_USBDEV_NOWRITEAHEAD
      /* The current BDT is not available and write ahead is disabled.  There
       * is nothing we can do now.  Return -EBUSY to indicate this condition.
       */

      return -EBUSY;
#else
      /* The current BDT is not available, check the other BDT */

      volatile struct usbotg_bdtentry_s *otherbdt;
      otherbdt = &g_bdt[EP(epno, EP_DIR_IN, EP_PP_EVEN)];
      if (otherbdt == bdt)
        {
          otherbdt++;
        }

      /* Is it available? */

      if (otherbdt->status || otherbdt->addr)
        {
          /* No, neither are available.  We cannot perform the transfer now.
           * Return -EBUSY to indicate this condition.
           */

          return -EBUSY;
        }

      /* Yes... use the other BDT */

      bdt   = otherbdt;
      index = 1;
#endif
    }

  /* A BDT is available.  Which request should we be operating on?  The last
   * incomplete, active request would be at the tail of the active list.
   */

  privreq = pic32mx_rqtail(&privep->active);

  /* This request would be NULL if there is no incomplete, active request. */

  if (privreq)
    {
      /* Get the number of bytes left to be transferred in the request */

      xfrd      = privreq->req.xfrd;
      bytesleft = privreq->req.len - xfrd;

      /* Even if the request is incomplete, transfer of all the requested
       * bytes may already been started.  NOTE: inflight[1] should be zero
       * because we know that there is a BDT available.
       */

#ifndef CONFIG_USBDEV_NOWRITEAHEAD
      DEBUGASSERT(privreq->inflight[1] == 0);
#endif
      /* Has the transfer been initiated for all of the bytes? */

      if (bytesleft > privreq->inflight[0])
        {
          /* No.. we have more work to do with this request */

          xfrd      += privreq->inflight[0];
          bytesleft -=  privreq->inflight[0];
        }

      /* Do we need to send a null packet after this packet? */

      else if (privep->txnullpkt)
        {
          /* Yes... set up for the NULL packet transfer */

          xfrd      = privreq->req.len;
          bytesleft = 0;
        }
      else
        {
          /* No.. We are finished with this request.  We need to get the
           * next request from the head of the pending request list.
           */

          privreq = NULL;
        }
    }

  /* If privreq is NULL here then either (1) there is no active request, or
   * (2) the (only) active request is fully queued.  In either case, we need
   * to get the next request from the head of the pending request list.
   */

  if (!privreq)
    {
      /* Remove the next request from the head of the pending request list */

      privreq = pic32mx_remfirst(&privep->pend);
      if (!privreq)
        {
          /* The pending request list is empty. There are no queued TX
           * requests to be sent.
           */

          usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPINQEMPTY), epno);

          /* Return -ENODATA to indicate that there are no further requests
           * to be processed.
           */

          return -ENODATA;
        }

      /* Add this request to the tail of the active request list */

      pic32mx_addlast(&privep->active, privreq);

      /* Set up the first transfer for this request */

      xfrd      = 0;
      bytesleft = privreq->req.len;
    }

  ullvdbg("epno=%d req=%p: len=%d xfrd=%d index=%d nullpkt=%d\n",
          epno, privreq, privreq->req.len, xfrd, index, privep->txnullpkt);

  /* Get the number of bytes left to be sent in the packet */

  nbytes = bytesleft;
  if (nbytes > 0 || privep->txnullpkt)
    {
      /* Either send the maxpacketsize or all of the remaining data in
       * the request.
       */

      privep->txnullpkt = 0;
      if (nbytes >= privep->ep.maxpacket)
        {
          nbytes =  privep->ep.maxpacket;

          /* Handle the case where this packet is exactly the
           * maxpacketsize.  Do we need to send a zero-length packet
           * in this case?
           */

          if (bytesleft ==  privep->ep.maxpacket &&
             (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
            {
              privep->txnullpkt = 1;
            }
        }
    }

  /* Send the packet (might be a null packet with nbytes == 0) */

  buf = privreq->req.buf + xfrd;

  /* Setup the writes to the endpoints */

  pic32mx_epwrite(privep, bdt, buf, nbytes);

  /* Special case endpoint 0 state information.  The write request is in
   * progress.
   */

  if (epno == 0)
    {
      priv->ctrlstate = CTRLSTATE_WRREQUEST;
    }

  /* Update for the next data IN interrupt */

  privreq->inflight[index] = nbytes;
  return OK;
}

/****************************************************************************
 * Name: pic32mx_wrrequest
 ****************************************************************************/

static int pic32mx_wrrequest(struct pic32mx_usbdev_s *priv, struct pic32mx_ep_s *privep)
{
  int ret;

  /* Always try to start two transfers in order to take advantage of the
   * PIC32MX's ping pong buffering.
   */

  ret = pic32mx_wrstart(priv, privep);
#ifndef CONFIG_USBDEV_NOWRITEAHEAD
  if (ret == OK)
    {
      /* Note:  We need to return the error condition only if nothing was
       * queued
       */

      (void)pic32mx_wrstart(priv, privep);
    }
#else
  UNUSED(ret);
#endif

  /* We return OK to indicate that a write request is still in progress */

  return pic32mx_rqhead(&privep->active) == NULL ? -ENODATA : OK;
}

/****************************************************************************
 * Name: pic32mx_rdcomplete
 ****************************************************************************/

static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv,
                              struct pic32mx_ep_s *privep)
{
  volatile struct usbotg_bdtentry_s *bdtout;
  struct pic32mx_req_s *privreq;
  int epno;

  /* Check the request at the head of the endpoint's active request queue */

  privreq = pic32mx_rqhead(&privep->active);
  if (!privreq)
    {
      /* There is no active packet waiting to receive any data. Then why are
       * we here?
       */

      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTQEMPTY),
               USB_EPNO(privep->ep.eplog));
      return -EINVAL;
    }

  /* bdtout should point to the BDT that just completed */

  bdtout = privep->bdtout;
  epno   = USB_EPNO(privep->ep.eplog);

  ullvdbg("EP%d: len=%d xfrd=%d\n",
          epno, privreq->req.len, privreq->req.xfrd);
  bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n",
         epno, bdtout, bdtout->status, bdtout->addr);

  /* We should own the BDT that just completed */

  DEBUGASSERT((bdtout->status & USB_BDT_UOWN) == USB_BDT_COWN);

  /* Get the length of the data received from the BDT. */

  privreq->req.xfrd = (bdtout->status & USB_BDT_BYTECOUNT_MASK) >> USB_BDT_BYTECOUNT_SHIFT;

  /* Complete the transfer and return the request to the class driver. */

  usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
  pic32mx_reqcomplete(privep, OK);

  /* Nullify the BDT entry that just completed.  Why?  So that we can tell later
   * that the BDT has been processed.  No, it is not sufficient to look at the
   * UOWN bit.  If UOWN==0, then the transfer has been completed BUT it may not
   * yet have been processed.
   */

  bdtout->status = 0;
  bdtout->addr   = 0;

  /* Toggle bdtout to the other BDT.  Is the current bdtout the EVEN bdt? */

  privep->bdtout = &g_bdt[EP_OUT_EVEN(epno)];
  if (bdtout == privep->bdtout)
    {
      /* Yes.. Then the other BDT is the ODD BDT */

      privep->bdtout++;
    }

  /* Set up the next read operation */

  return pic32mx_rdrequest(priv, privep);
}

/****************************************************************************
 * Name: pic32mx_ep0rdsetup
 ****************************************************************************/

static int pic32mx_ep0rdsetup(struct pic32mx_usbdev_s *priv, uint8_t *dest,
                              int readlen)
{
  volatile struct usbotg_bdtentry_s *bdtout;
  volatile struct usbotg_bdtentry_s *otherbdt;
  struct pic32mx_ep_s *privep;
  uint32_t status;

  /* bdtout refers to the next ping-pong BDT to use. */

  privep = &priv->eplist[EP0];
  bdtout = privep->bdtout;

  /* Get the other BDT.  Check if the current BDT the EVEN BDT? */

  otherbdt = &g_bdt[EP_OUT_EVEN(EP0)];
  if (bdtout == otherbdt)
    {
      /* Yes.. then the other BDT is the ODD BDT. */

      otherbdt++;
    }

  /* If there is no RX transfer in progress, then the other BDT is setup
   * to receive the next setup packet.  There is a race condition here!
   * Stop any setup packet.
   */

  if (!priv->rxbusy)
    {
      /* Nullify all BDT OUT entries.  Why?  So that we can tell later
       * that the BDT available.  No, it is not sufficient to look at the
       * UOWN bit.  If UOWN==0, then the transfer has been completed BUT
       * it may not yet have been processed.  But a completely NULLified
       * BDT is a sure indication
       */

      bdtout->status   = 0;
      bdtout->addr     = 0;
      otherbdt->status = 0;
      otherbdt->addr   = 0;

      /* Reset the other BDT to zero... this will cause any attempted use
       * of the other BDT to be NAKed.  Set the first DATA0/1 value to 1.
       */

      privep->rxdata1  = 1;
    }

  /* Otherwise, there are RX transfers in progress.  bdtout may be
   * unavailable now.  In that case, we are free to setup the other BDT
   * in order to improve performance.  NOTE: That we check if the
   * entire BDT has been NULLified.  That is the only sure indication
   * that the BDT is available (see above).
   */

  if (bdtout->status || bdtout->addr)
    {
#ifdef CONFIG_USBDEV_NOREADAHEAD
      /* We will not try to read ahead */

      return -EBUSY;
#else
      /* bdtout is not available.  Is the other BDT available? */

      if (otherbdt->status || otherbdt->addr)
        {
          /* Neither are available... we cannot accept the request now */

          return -EBUSY;
        }

      /* Use the other BDT */

      bdtout = otherbdt;
#endif
    }

  usbtrace(TRACE_READ(EP0), readlen);

  /* Get the correct data toggle (as well as other BDT bits) */

  if (privep->rxdata1)
    {
      status          = (USB_BDT_UOWN | USB_BDT_DATA1 | USB_BDT_DTS);
      privep->rxdata1 = 0;
    }
  else
    {
      status          = (USB_BDT_UOWN | USB_BDT_DATA0 | USB_BDT_DTS);
      privep->rxdata1 = 1;
    }

   /* Set the data pointer, data length, and enable the endpoint */

  bdtout->addr  = (uint8_t *)PHYS_ADDR(dest);
  status       |= ((uint32_t)readlen << USB_BDT_BYTECOUNT_SHIFT);

  /* Then give the BDT to the USB */

  bdtdbg("EP0 BDT OUT [%p] {%08x, %08x}\n", bdtout, status, bdtout->addr);
  bdtout->status = status;

  priv->ctrlstate = CTRLSTATE_RDREQUEST;
  priv->rxbusy    = 1;
  return OK;
}

/****************************************************************************
 * Name: pic32mx_rdsetup
 ****************************************************************************/

static int pic32mx_rdsetup(struct pic32mx_ep_s *privep, uint8_t *dest, int readlen)
{
  volatile struct usbotg_bdtentry_s *bdtout;
  uint32_t status;
  int epno;

  /* Select a BDT.  Check both the even and the ODD BDT and use the first one
   * that we own.
   */

  epno = USB_EPNO(privep->ep.eplog);

  /* bdtout refers to the next ping-pong BDT to use.  However, bdtout may be
   * unavailable now.  But, in that case, we are free to setup the other BDT
   * in order to improve performance.
   *
   * Note that we NULLify the BDT OUT entries.  This is so that we can tell
   * that the BDT readlly available.  No, it is not sufficient to look at the
   * UOWN bit.  If UOWN==0, then the transfer has been completed BUT it may
   * not yet have been processed.  But a completely NULLified BDT is a sure
   * indication
   */

  bdtout = privep->bdtout;
  if (bdtout->status || bdtout->addr)
    {
#ifdef CONFIG_USBDEV_NOREADAHEAD
      /* We will not try to read-ahead */

      return -EBUSY;
#else
      volatile struct usbotg_bdtentry_s *otherbdt;

      /* Is the current BDT the EVEN BDT? */

      otherbdt = &g_bdt[EP_OUT_EVEN(epno)];
      if (bdtout == otherbdt)
        {
          /* Yes.. select the ODD BDT */

          otherbdt++;
        }

      /* Is the other BDT available? */

      if (otherbdt->status || otherbdt->addr)
        {
          /* Neither are available... we cannot accept the request now */

          return -EBUSY;
        }

      /* Use the other BDT */

      bdtout = otherbdt;
#endif
    }

  usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), readlen);

  /* Clear status bits (making sure that UOWN is cleared before doing anything
   * else).
   */

  bdtout->status = 0;

  /* Set the data pointer, data length, and enable the endpoint */

  bdtout->addr = (uint8_t *)PHYS_ADDR(dest);

  /* Get the correct data toggle. */

  if (privep->rxdata1)
    {
      status          = (USB_BDT_UOWN | USB_BDT_DATA1 | USB_BDT_DTS);
      privep->rxdata1 = 0;
    }
  else
    {
      status          = (USB_BDT_UOWN | USB_BDT_DATA0 | USB_BDT_DTS);
      privep->rxdata1 = 1;
    }

  /* Set the data length (preserving the data toggle). */

  status |= ((uint32_t)readlen << USB_BDT_BYTECOUNT_SHIFT);

  /* Then give the BDT to the USB */

  bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n",  epno, bdtout, status, bdtout->addr);

  bdtout->status = status;
  return OK;
}

/****************************************************************************
 * Name: pic32mx_rdrequest
 ****************************************************************************/

static int pic32mx_rdrequest(struct pic32mx_usbdev_s *priv,
                             struct pic32mx_ep_s *privep)
{
  struct pic32mx_req_s *privreq;
  int readlen;
  int ret;

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

  privreq = pic32mx_rqhead(&privep->pend);
  if (!privreq)
    {
      /* There is no packet to receive any data. */

      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTQEMPTY),
               USB_EPNO(privep->ep.eplog));

      /* Special case reads from to endpoint zero.  If there is no transfer in
       * progress, then we need to configure to received the next SETUP packet.
       */

      if (USB_EPNO(privep->ep.eplog) == 0 &&
          priv->ctrlstate == CTRLSTATE_RDREQUEST)
        {
          priv->ctrlstate = CTRLSTATE_WAITSETUP;
          priv->rxbusy    = 0;
        }

      return OK;
    }

  ullvdbg("EP%d: len=%d\n", USB_EPNO(privep->ep.eplog), privreq->req.len);

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

  if (privreq->req.len == 0)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_EPOUTNULLPACKET), 0);
      pic32mx_reqcomplete(privep, OK);
      return OK;
    }

  /* Limit the size of the transfer to either the buffer size or the max
   * packet size of the endpoint.
   */

  readlen = MIN(privreq->req.len, privep->ep.maxpacket);

  /* Handle EP0 in a few special ways */

  if (USB_EPNO(privep->ep.eplog) == EP0)
    {
      ret = pic32mx_ep0rdsetup(priv, privreq->req.buf, readlen);
    }
  else
    {
      ret = pic32mx_rdsetup(privep, privreq->req.buf, readlen);
    }

  /* If the read request was successfully setup, then move the request from
   * the head of the pending request queue to the tail of the active request
   * queue.
   */

  if (ret == OK)
    {
      privreq = pic32mx_remfirst(&privep->pend);
      DEBUGASSERT(privreq != NULL);
      pic32mx_addlast(&privep->active, privreq);
    }

  return ret;
}

/****************************************************************************
 * Name: pic32mx_cancelrequests
 ****************************************************************************/

static void pic32mx_cancelrequests(struct pic32mx_ep_s *privep, int16_t result)
{
  struct pic32mx_req_s *privreq;

  while ((privreq = pic32mx_remfirst(&privep->active)))
    {
      usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
      pic32mx_reqreturn(privep, privreq, result);
    }

  while ((privreq = pic32mx_remfirst(&privep->pend)))
    {
      usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
      pic32mx_reqreturn(privep, privreq, result);
    }
}

/****************************************************************************
 * Interrupt Level Processing
 ****************************************************************************/
/****************************************************************************
 * Name: pic32mx_dispatchrequest
 ****************************************************************************/

static void pic32mx_dispatchrequest(struct pic32mx_usbdev_s *priv)
{
  int ret;

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

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

          usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_DISPATCHSTALL), 0);
          priv->ctrlstate = CTRLSTATE_STALL;
        }
    }
}

/****************************************************************************
 * Name: pic32mx_ep0stall
 ****************************************************************************/

static void pic32mx_ep0stall(struct pic32mx_usbdev_s *priv)
{
  uint16_t regval;

  /* Check if EP0 is stalled */

  regval = pic32mx_getreg(PIC32MX_USB_EP0);
  if ((regval & USB_EP_EPSTALL) != 0)
    {
      /* If so, clear the EP0 stall status */

      regval &= ~USB_EP_EPSTALL;
      pic32mx_putreg(regval, PIC32MX_USB_EP0);
    }
}

/****************************************************************************
 * Name: pic32mx_eptransfer
 ****************************************************************************/

static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno,
                               uint16_t ustat)
{
  struct pic32mx_ep_s *privep;
  int ret;

  /* Decode and service non control endpoints interrupt */

  privep = &priv->eplist[epno];

  /* Check if the last transaction was an EP0 OUT transaction */

  if ((ustat & USB_STAT_DIR) == USB_STAT_DIR_OUT)
    {
      /* OUT: host-to-device */

      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTDONE), ustat);

      /* Handle read requests. Call pic32mx_rdcomplete() to complete the OUT
       * transfer and setup the next out transfer.
       */

      ret = pic32mx_rdcomplete(priv, privep);
#ifdef CONFIG_USBDEV_NOREADAHEAD
      if (ret == OK)
        {
          /* If that succeeds, then try to set up another OUT transfer. */

          (void)pic32mx_rdrequest(priv, privep);
        }
#else
      UNUSED(ret);
#endif
    }
  else
    {
      /* IN: device-to-host */

      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPINDONE), ustat);

      /* An outgoing IN packet has completed.  Update the number of bytes transferred
       * and check for completion of the transfer.
       */

      pic32mx_wrcomplete(priv, privep);

      /* Handle additional queued write requests */

      (void)pic32mx_wrrequest(priv, privep);
    }
}

/****************************************************************************
 * Name: pic32mx_ep0nextsetup
 *
 * Description:
 *   This function is called (1) after sucessful completion of an EP0 Setup
 *   command, or (2) after receipt of the OUT complete event (for simple
 *   transfers).  It simply sets up the single BDT to accept the next
 *   SETUP commend.
 *
 ****************************************************************************/

static void pic32mx_ep0nextsetup(struct pic32mx_usbdev_s *priv)
{
  volatile struct usbotg_bdtentry_s *bdt = priv->eplist[EP0].bdtout;
  uint32_t bytecount;

  /* This operation should be performed no more than once per OUT transaction.
   * priv->ep0done is set to zero at the beginning of processing of each EP0
   * transfer.  It is set the first time that this function runs after the EP0
   * transfer.
   */

  if (!priv->ep0done)
    {
      bytecount     = (USB_SIZEOF_CTRLREQ << USB_BDT_BYTECOUNT_SHIFT);
      bdt->addr     = (uint8_t *)PHYS_ADDR(&priv->ctrl);
      bdt->status   = (USB_BDT_UOWN | bytecount);
      priv->ep0done = 1;
    }
}

/****************************************************************************
 * Name: pic32mx_ep0rdcomplete
 *
 * Description:
 *   This function is called after a sequence of read sequence.  In this
 *   context, only one BDT is used.  Both BDTs must be prepared to receive
 *   SETUP packets.
 *
 ****************************************************************************/

static void pic32mx_ep0rdcomplete(struct pic32mx_usbdev_s *priv)
{
  volatile struct usbotg_bdtentry_s *bdt;
  struct pic32mx_ep_s *ep0;
  uint32_t physaddr;
  uint32_t bytecount;

  /* This operation should be performed no more than once per OUT transaction.
   * priv->ep0done is set to zero at the beginning of processing of each EP0
   * transfer.  It is set the first time that this function runs after the EP0
   * transfer.
   */

  if (!priv->ep0done)
    {
      bytecount     = (USB_SIZEOF_CTRLREQ << USB_BDT_BYTECOUNT_SHIFT);
      physaddr      = PHYS_ADDR(&priv->ctrl);

      bdt           = &g_bdt[EP0_OUT_EVEN];
      bdt->addr     = (uint8_t *)physaddr;
      bdt->status   = (USB_BDT_UOWN | bytecount);

      bdt           = &g_bdt[EP0_OUT_ODD];
      bdt->addr     = (uint8_t *)physaddr;
      bdt->status   = (USB_BDT_UOWN | bytecount);

      priv->ep0done = 1;

      /* Data toggling is not used on SETUP transfers.  And IN transfer
       * resulting from a SETUP command should begin with DATA1.
       */

      ep0           = &priv->eplist[EP0];
      ep0->rxdata1  = 0;
      ep0->txdata1  = 1;
    }
}

/****************************************************************************
 * Name: pic32mx_ep0setup
 ****************************************************************************/

static void pic32mx_ep0setup(struct pic32mx_usbdev_s *priv)
{
  volatile struct usbotg_bdtentry_s *bdt;
  struct pic32mx_ep_s  *ep0;
  struct pic32mx_ep_s  *privep;
  union wb_u            value;
  union wb_u            index;
  union wb_u            len;
  union wb_u            response;
  uint16_t              regval;
  bool                  dispatched = false;
  uint8_t               epno;
  int                   nbytes = 0; /* Assume zero-length packet */
  int                   ret;

  /* Cancel any pending requests. */

  ep0 = &priv->eplist[EP0];
  pic32mx_cancelrequests(ep0, -EPROTO);

  /* Assume NOT stalled; no TX in progress; no RX overrun.  Data 0/1 toggling
   * is not used on SETUP packets, but any following EP0 IN transfer should
   * beginning with DATA1.
   */

  ep0->stalled   = false;
  ep0->rxdata1   = 0;
  ep0->txdata1   = 1;

  /* Initialize for the SETUP */

  priv->ctrlstate = CTRLSTATE_WAITSETUP;

  /* And extract the little-endian 16-bit values to host order */

  value.w = GETUINT16(priv->ctrl.value);
  index.w = GETUINT16(priv->ctrl.index);
  len.w   = GETUINT16(priv->ctrl.len);

  ullvdbg("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n",
          priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w);

  /* Dispatch any non-standard requests */

  if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_NOSTDREQ), priv->ctrl.type);

      /* Let the class implementation handle all non-standar requests */

      pic32mx_dispatchrequest(priv);
      dispatched = true;
      goto resume_packet_processing;  /* Sorry about the goto */
    }

  /* Handle standard request.  Pick off the things of interest to the
   * USB device controller driver; pass what is left to the class driver
   */

  switch (priv->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(PIC32MX_TRACEINTID_GETSTATUS), priv->ctrl.type);
        if (len.w != 2 || (priv->ctrl.type & USB_REQ_DIR_IN) == 0 ||
            index.b[MSB] != 0 || value.w != 0)
          {
            usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADEPGETSTATUS), 0);
            priv->ctrlstate = CTRLSTATE_STALL;
          }
        else
          {
            switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK)
              {
               case USB_REQ_RECIPIENT_ENDPOINT:
                {
                  epno = USB_EPNO(index.b[LSB]);
                  usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPGETSTATUS), epno);
                  if (epno >= PIC32MX_NENDPOINTS)
                    {
                      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADEPGETSTATUS), epno);
                      priv->ctrlstate = CTRLSTATE_STALL;
                    }
                  else
                    {
                      privep            = &priv->eplist[epno];
                      response.w        = 0; /* Not stalled */
                      nbytes            = 2; /* Response size: 2 bytes */

                      if (USB_ISEPIN(index.b[LSB]))
                        {
                          /* IN endpoint */

                          bdt = privep->bdtin;
                        }
                      else
                        {
                          /* OUT endpoint */

                          bdt = privep->bdtout;
                        }

                      /* BSTALL set if stalled */

                      if ((bdt->status & USB_BDT_BSTALL) != 0)
                        {
                          response.b[LSB] = 1; /* Stalled, set bit 0 */
                        }
                    }
                }
                break;

              case USB_REQ_RECIPIENT_DEVICE:
                {
                 if (index.w == 0)
                    {
                      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_DEVGETSTATUS), 0);

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

                      response.w      = 0;
                      response.b[LSB] = (priv->selfpowered << USB_FEATURE_SELFPOWERED) |
                                        (priv->rwakeup << USB_FEATURE_REMOTEWAKEUP);
                      nbytes          = 2; /* Response size: 2 bytes */
                    }
                  else
                    {
                      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADDEVGETSTATUS), 0);
                      priv->ctrlstate = CTRLSTATE_STALL;
                    }
                }
                break;

              case USB_REQ_RECIPIENT_INTERFACE:
                {
                  usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_IFGETSTATUS), 0);
                  response.w        = 0;
                  nbytes            = 2; /* Response size: 2 bytes */
                }
                break;

              default:
                {
                  usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADGETSTATUS), 0);
                  priv->ctrlstate = CTRLSTATE_STALL;
                }
                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(PIC32MX_TRACEINTID_CLEARFEATURE), priv->ctrl.type);
        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE)
          {
            /* Disable B device from performing HNP */

#ifdef CONFIG_USBOTG
            if (value.w == USBOTG_FEATURE_B_HNP_ENABLE)
              {
                /* Disable HNP */
#warning Missing Logic
              }

            /* Disable A device HNP support */

            else if (value.w == USBOTG_FEATURE_A_HNP_SUPPORT)
              {
                /* Disable HNP support */
#warning Missing Logic
              }

            /* Disable alternate HNP support */

            else if (value.w == USBOTG_FEATURE_A_ALT_HNP_SUPPORT)
              {
                /* Disable alternate HNP */
#warning Missing Logic
              }
            else
#endif
            /* Disable remote wakeup */

            if (value.w == USB_FEATURE_REMOTEWAKEUP)
              {
                priv->rwakeup     = 0;
              }
            else
              {
                /* Let the class implementation handle all other device features */

                pic32mx_dispatchrequest(priv);
                dispatched = true;
              }
          }
        else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_ENDPOINT)
          {
            epno = USB_EPNO(index.b[LSB]);
            if (epno > 0 && epno < PIC32MX_NENDPOINTS && index.b[MSB] == 0 &&
                value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
              {
                privep            = &priv->eplist[epno];
                privep->halted    = false;
                ret               = pic32mx_epstall(&privep->ep, true);
                UNUSED(ret);
              }
            else
              {
                usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADCLEARFEATURE), 0);
                priv->ctrlstate = CTRLSTATE_STALL;
              }
          }
        else
          {
            /* Let the class implementation handle all other recipients. */

            pic32mx_dispatchrequest(priv);
            dispatched = true;
          }
      }
      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(PIC32MX_TRACEINTID_SETFEATURE), priv->ctrl.type);

        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE)
          {
            /* Enable B device to perform HNP */

#ifdef CONFIG_USBOTG
            if (value.w == USBOTG_FEATURE_B_HNP_ENABLE)
              {
                /* Enable HNP */
#warning "Missing logic"
              }

            /* Enable A device HNP supports */

            else if (value.w == USBOTG_FEATURE_A_HNP_SUPPORT)
              {
                /* Enable HNP support */
#warning "Missing logic"
              }

            /* Another port on the A device supports HNP */

            else if (value.w == USBOTG_FEATURE_A_ALT_HNP_SUPPORT)
              {
                /* Enable alternate HNP */
#warning "Missing logic"
              }
            else
#endif

            if (value.w == USB_FEATURE_REMOTEWAKEUP)
              {
                priv->rwakeup     = 0;
              }
            else if (value.w == USB_FEATURE_TESTMODE)
              {
                /* Special case recipient=device test mode */

                ullvdbg("test mode: %d\n", index.w);
              }
            else
              {
                /* Let the class implementation handle all other device features */

                pic32mx_dispatchrequest(priv);
                dispatched = true;
              }
          }
        else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_ENDPOINT)
          {
            /* Handler recipient=endpoint */

            epno = USB_EPNO(index.b[LSB]);
            if (epno < PIC32MX_NENDPOINTS && index.b[MSB] == 0 &&
                value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
              {
                privep            = &priv->eplist[epno];
                privep->halted    = true;
                ret               = pic32mx_epstall(&privep->ep, false);
                UNUSED(ret);
              }
            else
              {
                usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADSETFEATURE), 0);
                priv->ctrlstate = CTRLSTATE_STALL;
              }
          }
        else
          {
            /* The class driver handles all recipients except recipient=endpoint */

            pic32mx_dispatchrequest(priv);
            dispatched = true;
          }
      }
      break;

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

        usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0SETUPSETADDRESS), value.w);
        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_DEVICE ||
            index.w != 0 || len.w != 0 || value.w > 127)
          {
            usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADSETADDRESS), 0);
            priv->ctrlstate = CTRLSTATE_STALL;
          }
        else
          {
            /* Note that setting of the device address will be deferred.  A zero-length
             * packet will be sent and the device address will be set when the zero-
             * length packet transfer completes.
             */

            priv->devstate = DEVSTATE_ADDRPENDING;
          }
      }
      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(PIC32MX_TRACEINTID_GETSETDESC), priv->ctrl.type);
        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE)
          {
            /* The request seems valid... let the class implementation handle it */

            pic32mx_dispatchrequest(priv);
            dispatched = true;
          }
        else
          {
            usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADGETSETDESC), 0);
            priv->ctrlstate = CTRLSTATE_STALL;
          }
      }
      break;

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

      {
        usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_GETCONFIG), priv->ctrl.type);
        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
            value.w == 0 && index.w == 0 && len.w == 1)
          {
            /* The request seems valid... let the class implementation handle it */

            pic32mx_dispatchrequest(priv);
            dispatched = true;
          }
        else
          {
            usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADGETCONFIG), 0);
            priv->ctrlstate = CTRLSTATE_STALL;
          }
      }
      break;

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

      {
        usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_SETCONFIG), priv->ctrl.type);
        if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
            index.w == 0 && len.w == 0)
          {
             /* The request seems valid... let the class implementation handle it */

             pic32mx_dispatchrequest(priv);
             dispatched = true;
          }
        else
          {
            usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADSETCONFIG), 0);
            priv->ctrlstate = CTRLSTATE_STALL;
          }
      }
      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
       */

      {
        /* Let the class implementation handle the request */

        usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_GETSETIF), priv->ctrl.type);
        pic32mx_dispatchrequest(priv);
        dispatched = true;
      }
      break;

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

      {
        usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_SYNCHFRAME), 0);
      }
      break;

    default:
      {
        usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDCTRLREQ), priv->ctrl.req);
        priv->ctrlstate = CTRLSTATE_STALL;
      }
      break;
    }

  /* PKTDIS bit is set when a Setup Transaction is received. Clear to resume
   * packet processing.
   */

resume_packet_processing:
  regval = pic32mx_getreg(PIC32MX_USB_CON);
  regval &= ~USB_CON_PKTDIS;
  pic32mx_putreg(regval, PIC32MX_USB_CON);

  /* At this point, the request has been handled and there are three possible
   * outcomes:
   *
   * 1. The setup request was successfully handled above and a response packet
   *    must be sent (may be a zero length packet).
   * 2. The request was successfully handled by the class implementation.  In
   *    case, the EP0 IN response has already been queued and the local variable
   *    'dispatched' will be set to true and ctrlstate != CTRLSTATE_STALL;
   * 3. An error was detected in either the above logic or by the class implementation
   *    logic.  In either case, priv->state will be set CTRLSTATE_STALL
   *    to indicate this case.
   *
   * NOTE: Non-standard requests are a special case.  They are handled by the
   * class implementation and this function returned early above, skipping this
   * logic altogether.
   */

  if (!dispatched && (priv->ctrlstate != CTRLSTATE_STALL))
    {
      /* The SETUP command was not dispatched to the class driver and the SETUP
       * command did not cause a stall. We will respond.  First, restrict the
       * data length to the length requested in the setup packet
       */

      if (nbytes > len.w)
        {
          nbytes = len.w;
        }

      /* Send the EP0 SETUP response (might be a zero-length packet) */

      pic32mx_epwrite(ep0, ep0->bdtin, response.b, nbytes);
      priv->ctrlstate = CTRLSTATE_WAITSETUP;
    }

  /* Did we stall?  This might have occurred from the above logic OR the stall
   * condition may have been set less obviously in pic32mx_dispatchrequest().
   * In either case, we handle the stall condition the same.
   *
   * However, bad things happen if we try to stall a SETUP packet.  So lets
   * not.  If we wait a bit, things will recover.  Hmmm.. If we completed
   * the data phase (perhaps by sending a NULL packet), then I think we
   * could stall the endpoint and perhaps speed things up a bit???.
   */

   /* Set up the BDT to accept the next setup commend. */

   pic32mx_ep0nextsetup(priv);
   priv->ctrlstate = CTRLSTATE_WAITSETUP;
}

/****************************************************************************
 * Name: pic32mx_ep0incomplete
 ****************************************************************************/

static void pic32mx_ep0incomplete(struct pic32mx_usbdev_s *priv)
{
  struct pic32mx_ep_s *ep0 = &priv->eplist[EP0];
  volatile struct usbotg_bdtentry_s *bdtlast;
  int ret;

  /* Get the last BDT and make sure that we own it. */

  bdtlast = ep0->bdtin;

  /* Make sure that we own the last BDT. */

  bdtlast->status = 0;
  bdtlast->addr   = 0;

  /* Are we processing the completion of one packet of an outgoing request
   * from the class driver?
   */

  if (priv->ctrlstate == CTRLSTATE_WRREQUEST)
    {
      /* An outgoing EP0 transfer has completed.  Update the byte count and
       * check for the completion of the transfer.
       *
       * NOTE: pic32mx_wrcomplete() will toggle bdtin to the other buffer so
       * we do not need to that for this case.
       */

      pic32mx_wrcomplete(priv, &priv->eplist[EP0]);

      /* Handle the next queue IN transfer.  If there are no further queued
       * IN transfers, pic32mx_wrrequest will return -ENODATA and that is the
       * only expected error return value in this context.
       */

      ret = pic32mx_wrrequest(priv, &priv->eplist[EP0]);
      if (ret < 0)
        {
          DEBUGASSERT(ret == -ENODATA);

          /* If there is nothing to be sent, then we need to configure to
           * receive the next SETUP packet.
           */

          priv->ctrlstate = CTRLSTATE_WAITSETUP;
        }
    }

  /* No.. Are we processing the completion of a status response? */

  else if (priv->ctrlstate == CTRLSTATE_WAITSETUP)
    {
      /* Get the next IN BDT */

      if (bdtlast == &g_bdt[EP0_IN_EVEN])
        {
          ep0->bdtin = &g_bdt[EP0_IN_ODD];
        }
      else
        {
          DEBUGASSERT(bdtlast == &g_bdt[EP0_IN_ODD]);
          ep0->bdtin = &g_bdt[EP0_IN_EVEN];
       }

      /* Look at the saved SETUP command.  Was it a SET ADDRESS request?
       * If so, then now is the time to set the address.
       */

      if (priv->devstate == DEVSTATE_ADDRPENDING)
        {
          uint16_t addr = GETUINT16(priv->ctrl.value);
          usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0ADDRESSSET), addr);

          /* This should be the equivalent state */

          DEBUGASSERT(priv->ctrl.req == USB_REQ_SETADDRESS &&
                     (priv->ctrl.type & REQRECIPIENT_MASK) ==
                     (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_DEVICE));

          /* Set (or clear) the address */

          pic32mx_putreg(addr, PIC32MX_USB_ADDR);
          if (addr > 0)
            {
              priv->devstate = DEVSTATE_ADDRESS;
            }
          else
            {
              priv->devstate = DEVSTATE_DEFAULT;
            }
        }
    }

  /* No other state is expected in this context */

  else
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDSTATE), priv->ctrlstate);
      priv->ctrlstate = CTRLSTATE_STALL;
    }
}

/****************************************************************************
 * Name: pic32mx_ep0outcomplete
 ****************************************************************************/

static void pic32mx_ep0outcomplete(struct pic32mx_usbdev_s *priv)
{
  struct pic32mx_ep_s *ep0 = &priv->eplist[EP0];

  switch (priv->ctrlstate)
    {
      /* Read request in progress */

      case CTRLSTATE_RDREQUEST:

        /* Process the next read request for EP0 */

        pic32mx_rdcomplete(priv, ep0);

        /* Was this the end of the OUT transfer? */

        if (priv->ctrlstate == CTRLSTATE_WAITSETUP)
          {
            /* Prepare EP0 OUT for the next SETUP transaction. */

            pic32mx_ep0rdcomplete(priv);
          }
        break;

      /* No transfer in progress, waiting for SETUP */

      case CTRLSTATE_WAITSETUP:
        {
          /* In this case the last OUT transaction must have been a status
           * stage of a CTRLSTATE_WRREQUEST: Prepare EP0 OUT for the next SETUP
           * transaction.
           */

           pic32mx_ep0nextsetup(priv);
        }
        break;

      /* Unexpected state OR host aborted the OUT transfer before it completed,
       * STALL the endpoint in either case
       */

      default:
        {
          usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDSTATE), priv->ctrlstate);
          priv->ctrlstate = CTRLSTATE_STALL;
        }
        break;
    }
}

/****************************************************************************
 * Name: pic32mx_ep0transfer
 ****************************************************************************/

static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t ustat)
{
  volatile struct usbotg_bdtentry_s *bdt;

  /* The following information is available in the status register :
   *
   * ENDPT - The 4 bit endpoint number that cause the interrupt.
   * DIR   - The direction of the endpoint.
   * PPBI  - The ping-pong buffer used in the transaction.
   */

  priv->ep0done = 0;

  /* Check if the last transaction was an EP0 OUT transaction */

  if ((ustat & USB_STAT_DIR) == USB_STAT_DIR_OUT)
    {
      int index;

      /* It was an EP0 OUT transaction.  Get the index to the BDT. */

      index = ((ustat & USB_STAT_PPBI) == 0 ? EP0_OUT_EVEN : EP0_OUT_ODD);
      bdt   = &g_bdt[index];
      priv->eplist[0].bdtout = bdt;

      bdtdbg("EP0 BDT OUT [%p] {%08x, %08x}\n", bdt, bdt->status, bdt->addr);

      /* Check the current EP0 OUT buffer contains a SETUP packet */

      if (((bdt->status & USB_BDT_PID_MASK) >> USB_BDT_PID_SHIFT) == USB_PID_SETUP_TOKEN)
        {
          /* Check if the SETUP transaction data went into the priv->ctrl
           * buffer. If not, then we will need to copy it.
           */

          if (bdt->addr != (uint8_t *)PHYS_ADDR(&priv->ctrl))
            {
              void *src = (void *)VIRT_ADDR(bdt->addr);
              void *dest = &priv->ctrl;

              memcpy(dest, src, USB_SIZEOF_CTRLREQ);
              bdt->addr = (uint8_t *)PHYS_ADDR(&priv->ctrl);
            }

          /* Handle the control OUT transfer */

          usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0SETUPDONE), bdt->status);
          pic32mx_ep0setup(priv);
        }
      else
        {
          /* Handle the data OUT transfer */

          usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0OUTDONE), ustat);
          pic32mx_ep0outcomplete(priv);
        }
    }

  /* No.. it was an EP0 IN transfer */

  else /* if ((status & USB_STAT_DIR) == USB_STAT_DIR_IN) */
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0INDONE), ustat);

      /* Handle the IN transfer complete */

      pic32mx_ep0incomplete(priv);
    }

  /* Check for a request to stall EP0 */

  if (priv->ctrlstate == CTRLSTATE_STALL)
    {
      /* Stall EP0 */

      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_EP0SETUPSTALLED), priv->ctrlstate);
      (void)pic32mx_epstall(&priv->eplist[EP0].ep, false);
    }
}

/****************************************************************************
 * Name: pic32mx_interrupt
 ****************************************************************************/

static int pic32mx_interrupt(int irq, void *context)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct pic32mx_usbdev_s *priv = &g_usbdev;
  uint16_t usbir;
  uint16_t otgir;
  uint16_t regval;
  int i;

  /* Get the set of pending USB and OTG interrupts interrupts */

  usbir = pic32mx_getreg(PIC32MX_USB_IR) & pic32mx_getreg(PIC32MX_USB_IE);
  otgir = pic32mx_getreg(PIC32MX_USBOTG_IR) & pic32mx_getreg(PIC32MX_USBOTG_IE);
  usbtrace(TRACE_INTENTRY(PIC32MX_TRACEINTID_INTERRUPT), usbir | otgir);

#ifdef CONFIG_USBOTG
  /* Session Request Protocol (SRP) Time Out Check */

  /* if USB OTG SRP is ready */
#  warning "Missing logic"
    {
      /* Check if the 1 millisecond timer has expired */

      if (otgir & OTG_INT_T1MSEC) != 0)
        {
          usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_T1MSEC), otgir);

          /* Check for the USB OTG SRP timeout */
#  warning "Missing logic"
            {
              /* Handle OTG events of the SRP timeout has expired */
#  warning "Missing logic"
            }

            /* Clear Interrupt 1 msec timer Flag */

            pic32mx_putreg(USBOTG_INT_T1MSEC, PIC32MX_USBOTG_IR);
        }
    }
#endif

  /* Handle events while we are in the attached state */

  if (priv->devstate == DEVSTATE_ATTACHED)
    {
      /* Clear all USB interrupts */

      pic32mx_putreg(USB_INT_ALL, PIC32MX_USB_IR);

      /* Make sure that the USE reset and IDLE detect interrupts are enabled */

      regval = pic32mx_getreg(PIC32MX_USB_IE);
      regval |= (USB_INT_URST | USB_INT_IDLE);
      pic32mx_putreg(regval, PIC32MX_USB_IE);

      /* Now were are in the powered state */

      priv->devstate = DEVSTATE_POWERED;
    }

#ifdef  CONFIG_USBOTG
  /* Check if the ID Pin Changed State */

  if (otgir & USBOTG_INT_ID) != 0)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_OTGID), otgir);

      /* Re-detect and re-initialize */
#warning "Missing logic"

      pic32mx_putreg(USBOTG_INT_ID, PIC32MX_USBOTG_IR);
    }
#endif

  /* Service the USB Activity Interrupt */

  if ((otgir & USBOTG_INT_ACTV) != 0)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_WKUP), otgir);

      /* Wake-up from susepnd mode */

      pic32mx_putreg(USBOTG_INT_ACTV, PIC32MX_USBOTG_IR);
      pic32mx_resume(priv);
    }

  /*  It is pointless to continue servicing if the device is in suspend mode. */

  if ((pic32mx_getreg(PIC32MX_USB_PWRC) & USB_PWRC_USUSPEND) != 0)
    {
      /* Just clear the interrupt and return */

      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_SUSPENDED), pic32mx_getreg(PIC32MX_USB_PWRC));
      goto interrupt_exit;
    }

  /* Service USB Bus Reset Interrupt. When bus reset is received during
   * suspend, ACTVIF will be set first, once the UCONbits.SUSPND is clear,
   * then the URSTIF bit will be asserted. This is why URSTIF is checked
   * after ACTVIF.  The USB reset flag is masked when the USB state is in
   * DEVSTATE_DETACHED or DEVSTATE_ATTACHED, and therefore cannot cause a
   * USB reset event during these two states.
   */

  if ((usbir & USB_INT_URST) != 0)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_RESET), usbir);

      /* Reset interrupt received. Restore our initial state.  NOTE:  the
       * hardware automatically resets the USB address, so we really just
       * need reset any existing configuration/transfer states.
       */

      pic32mx_reset(priv);
      priv->devstate = DEVSTATE_DEFAULT;

#ifdef CONFIG_USBOTG
        /* Disable and deactivate HNP */
#warning Missing Logic
#endif
      /* Acknowlege the reset interrupt */

      pic32mx_putreg(USB_INT_URST, PIC32MX_USB_IR);
      goto interrupt_exit;
    }

  /* Service IDLE interrupts */

  if ((usbir & USB_INT_IDLE) != 0)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_IDLE), usbir);

#ifdef  CONFIG_USBOTG
      /* If Suspended, Try to switch to Host */
#warning "Missing logic"
#else
      pic32mx_suspend(priv);

#endif
      pic32mx_putreg(USB_INT_IDLE, PIC32MX_USB_IR);
    }

  /* Service SOF interrupts */

#ifdef CONFIG_USB_SOFINTS
  if ((usbir & USB_INT_SOF) != 0)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_SOF), 0);

      /* I am not sure why you would ever enable SOF interrupts */

      pic32mx_putreg(USB_INT_SOF, PIC32MX_USB_IR);
    }
#endif

   /* Service stall interrupts */

   if ((usbir & USB_INT_STALL) != 0)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_STALL), usbir);

      pic32mx_ep0stall(priv);

      /* Clear the pending STALL interrupt */

      pic32mx_putreg(USB_INT_STALL, PIC32MX_USB_IR);
    }

  /* Service error interrupts */

  if ((usbir & USB_INT_UERR) != 0)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_UERR), usbir);
      ulldbg("Error: EIR=%04x\n", pic32mx_getreg(PIC32MX_USB_EIR));

      /* Clear all pending USB error interrupts */

      pic32mx_putreg(USB_EINT_ALL, PIC32MX_USB_EIR);
    }

  /* There is no point in continuing if the host has not sent a bus reset.
   * Once bus reset is received, the device transitions into the DEFAULT
   * state and is ready for communication.
   */

  if (priv->devstate < DEVSTATE_DEFAULT)
    {
      /* Just clear the interrupt and return */

      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_WAITRESET), priv->devstate);
      goto interrupt_exit;
    }

  /*  Service USB Transaction Complete Interrupt */

  if ((usbir & USB_INT_TRN) != 0)
    {
      usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_TRNC), usbir);

      /* Drain the USAT FIFO entries.  If the USB FIFO ever gets full, USB
       * bandwidth utilization can be compromised, and the device won't be
       * able to receive SETUP packets.
       */

      for (i = 0; i < 4; i++)
        {
          uint8_t epno;

          /* Check the pending interrupt register.  Is token processing complete. */

          if ((pic32mx_getreg(PIC32MX_USB_IR) & USB_INT_TRN) != 0)
            {
              regval = pic32mx_getreg(PIC32MX_USB_STAT);
              pic32mx_putreg(USB_INT_TRN, PIC32MX_USB_IR);

              usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_TRNCS), regval);

              /* Handle the endpoint transfer complete event. */

              epno = (regval & USB_STAT_ENDPT_MASK) >> USB_STAT_ENDPT_SHIFT;
              if (epno == 0)
                {
                   pic32mx_ep0transfer(priv, regval);
                }
              else
                {
                  pic32mx_eptransfer(priv, epno, regval);
                }
            }
          else
            {
               /* USTAT FIFO must be empty. */

               break;
            }
        }
    }

  /* Clear the pending USB interrupt.  Goto is used in the above to assure
   * that all interrupt exists pass through this logic.
   */

interrupt_exit:
  up_clrpend_irq(PIC32MX_IRQSRC_USB);
  usbtrace(TRACE_INTEXIT(PIC32MX_TRACEINTID_INTERRUPT), usbir | otgir);
  return OK;
}

/****************************************************************************
 * Suspend/Resume Helpers
 ****************************************************************************/
/****************************************************************************
 * Name: pic32mx_suspend
 ****************************************************************************/

static void pic32mx_suspend(struct pic32mx_usbdev_s *priv)
{
  uint16_t regval;

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

  if (priv->driver)
    {
      CLASS_SUSPEND(priv->driver, &priv->usbdev);
    }

  /* Enable the ACTV interrupt.
   *
   * NOTE: Do not clear UIRbits.ACTVIF here! Reason: ACTVIF is only
   * generated once an IDLEIF has been generated. This is a 1:1 ratio
   * interrupt generation. For every IDLEIF, there will be only one ACTVIF
   * regardless of the number of subsequent bus transitions.  If the ACTIF
   * is cleared here, a problem could occur. The driver services IDLEIF
   * first because ACTIVIE=0. If this routine clears the only ACTIVIF,
   * then it can never get out of the suspend mode.
   */

  regval  = pic32mx_getreg(PIC32MX_USBOTG_IE);
  regval |= USBOTG_INT_ACTV;
  pic32mx_putreg(regval, PIC32MX_USBOTG_IE);

  /* Disable further IDLE interrupts.  Once is enough. */

  regval  = pic32mx_getreg(PIC32MX_USB_IE);
  regval &= ~USB_INT_IDLE;
  pic32mx_putreg(regval, PIC32MX_USB_IE);

  /* Invoke a callback into board-specific logic.  The board-specific logic
   * may enter into sleep or idle modes or switch to a slower clock, etc.
   */

  pic32mx_usbsuspend((struct usbdev_s *)priv, false);
}

/****************************************************************************
 * Name: pic32mx_resume
 ****************************************************************************/

static void pic32mx_resume(struct pic32mx_usbdev_s *priv)
{
  irqstate_t flags;
  uint16_t regval;

  flags = irqsave();

  /* Start RESUME signaling */

  regval = pic32mx_getreg(PIC32MX_USB_CON);
  regval |= USB_CON_RESUME;
  pic32mx_putreg(regval, PIC32MX_USB_CON);

  /* Keep the RESUME line set for 1-13 ms */

  up_mdelay(10);

  regval &= ~USB_CON_RESUME;
  pic32mx_putreg(regval, PIC32MX_USB_CON);

  /* This function is called when the USB activity interrupt occurs.
   * If using clock switching, this is the place to call out to
   * logic to restore the original MCU core clock frequency.
   */

  pic32mx_usbsuspend((struct usbdev_s *)priv, true);

  /* Disable further activity interrupts */

  regval  = pic32mx_getreg(PIC32MX_USBOTG_IE);
  regval &= ~USBOTG_INT_ACTV;
  pic32mx_putreg(regval, PIC32MX_USBOTG_IE);

  /* The ACTVIF bit cannot be cleared immediately after the USB module wakes
   * up from Suspend or while the USB module is suspended. A few clock cycles
   * are required to synchronize the internal hardware state machine before
   * the ACTIVIF bit can be cleared by firmware. Clearing the ACTVIF bit
   * before the internal hardware is synchronized may not have an effect on
   * the value of ACTVIF. Additionally, if the USB module uses the clock from
   * the 96 MHz PLL source, then after clearing the SUSPND bit, the USB
   * module may not be immediately operational while waiting for the 96 MHz
   * PLL to lock.
   */

  pic32mx_putreg(USB_INT_IDLE, PIC32MX_USBOTG_IR);

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

  if (priv->driver)
    {
      CLASS_RESUME(priv->driver, &priv->usbdev);
    }

  irqrestore(flags);
}

/****************************************************************************
 * Endpoint Helpers
 ****************************************************************************/
/****************************************************************************
 * Name: pic32mx_epreserve
 ****************************************************************************/

static inline struct pic32mx_ep_s *
pic32mx_epreserve(struct pic32mx_usbdev_s *priv, uint8_t epset)
{
  struct pic32mx_ep_s *privep = NULL;
  irqstate_t flags;
  int epndx = 0;

  flags = irqsave();
  epset &= priv->epavail;
  if (epset)
    {
      /* Select the lowest bit in the set of matching, available endpoints
       * (skipping EP0)
       */

      for (epndx = 1; epndx < PIC32MX_NENDPOINTS; epndx++)
        {
          uint8_t bit = PIC32MX_ENDP_BIT(epndx);
          if ((epset & bit) != 0)
            {
              /* Mark the endpoint no longer available */

              priv->epavail &= ~bit;

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

              privep = &priv->eplist[epndx];
              break;
            }
        }
    }

  irqrestore(flags);
  return privep;
}

/****************************************************************************
 * Name: pic32mx_epunreserve
 ****************************************************************************/

static inline void
pic32mx_epunreserve(struct pic32mx_usbdev_s *priv, struct pic32mx_ep_s *privep)
{
  irqstate_t flags = irqsave();
  priv->epavail   |= PIC32MX_ENDP_BIT(USB_EPNO(privep->ep.eplog));
  irqrestore(flags);
}

/****************************************************************************
 * Name: pic32mx_epreserved
 ****************************************************************************/

static inline bool
pic32mx_epreserved(struct pic32mx_usbdev_s *priv, int epno)
{
  return ((priv->epavail & PIC32MX_ENDP_BIT(epno)) == 0);
}

/****************************************************************************
 * Name: pic32mx_ep0configure
 ****************************************************************************/

static void pic32mx_ep0configure(struct pic32mx_usbdev_s *priv)
{
  volatile struct usbotg_bdtentry_s *bdt;
  struct pic32mx_ep_s *ep0;
  uint32_t bytecount;

  /* Enable the EP0 endpoint */

  pic32mx_putreg(PIC32MX_EP_CONTROL, PIC32MX_USB_EP0);

  /* Configure the OUT BDTs.  We assume that the ping-poing buffer index has
   * just been reset and we expect to receive on the EVEN BDT first.  Data
   * toggle synchronization is not needed for SETUP packets.
   */

  ep0         = &priv->eplist[EP0];
  bytecount   = (USB_SIZEOF_CTRLREQ << USB_BDT_BYTECOUNT_SHIFT);

  bdt         = &g_bdt[EP0_OUT_EVEN];
  bdt->addr   = (uint8_t *)PHYS_ADDR(&priv->ctrl);
  bdt->status = (USB_BDT_UOWN | bytecount);
  ep0->bdtout = bdt;

  bdt++;
  bdt->status = (USB_BDT_UOWN | bytecount);
  bdt->addr   = (uint8_t *)PHYS_ADDR(&priv->ctrl);

  /* Configure the IN BDTs. */

  bdt         = &g_bdt[EP0_IN_EVEN];
  bdt->status = 0;
  bdt->addr   = 0;
  ep0->bdtin  = bdt;

  bdt++;
  bdt->status = 0;
  bdt->addr   = 0;

  /* Data toggling is not used on SETUP transfers.  And IN transfer resulting
   * from a SETUP command should begin with DATA1.
   */

  ep0->rxdata1 = 0;
  ep0->txdata1 = 1;
}

/****************************************************************************
 * Endpoint operations
 ****************************************************************************/
/****************************************************************************
 * Name: pic32mx_epconfigure
 ****************************************************************************/

static int pic32mx_epconfigure(struct usbdev_ep_s *ep,
                               const struct usb_epdesc_s *desc,
                               bool last)
{
  struct pic32mx_ep_s *privep = (struct pic32mx_ep_s *)ep;
  volatile struct usbotg_bdtentry_s *bdt;
  uint16_t maxpacket;
  uint16_t regval;
  uint8_t  epno;
  bool     epin;
  bool     bidi;
  int      index;

#ifdef CONFIG_DEBUG
  if (!ep || !desc)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      ulldbg("ERROR: ep=%p desc=%p\n");
      return -EINVAL;
    }
#endif

  /* Get the unadorned endpoint address */

  epno = USB_EPNO(desc->addr);
  epin = USB_ISEPIN(desc->addr);

  usbtrace(TRACE_EPCONFIGURE, (uint16_t)epno);
  DEBUGASSERT(epno == USB_EPNO(ep->eplog));

  /* Set the requested type */

  switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK)
   {
    case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */
      regval = epin ? PIC32MX_EP_INTIN : PIC32MX_EP_INTOUT;
      break;

    case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */
      regval = epin ? PIC32MX_EP_BULKIN : PIC32MX_EP_BULKOUT;
      break;

    case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */
      regval = epin ? PIC32MX_EP_ISOCIN : PIC32MX_EP_ISOCOUT;
      break;

    case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint */
      regval = PIC32MX_EP_CONTROL;
      bidi   = true;
      break;

    default:
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADEPTYPE), (uint16_t)desc->type);
      return -EINVAL;
    }

  /* Enable the endpoint */

  pic32mx_putreg(regval, PIC32MX_USB_EP(epno));

  /* Setup up buffer descriptor table (BDT) entry/ies for this endpoint */

  if (epin || bidi)
    {
      /* Get the pointer to BDT entry */

      index         =  EP(epno, EP_DIR_IN, EP_PP_EVEN);
      bdt           = &g_bdt[index];
      privep->bdtin = bdt;

      /* Mark that we own the entry */

      bdt->status = 0;
      bdt->addr   = 0;

      bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr);

      /* Now do the same for the other buffer. */

      bdt++;
      bdt->status = 0;
      bdt->addr   = 0;

      bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr);
    }

  if (!epin || bidi)
    {
      index          =  EP(epno, EP_DIR_OUT, EP_PP_EVEN);
      bdt            = &g_bdt[index];
      privep->bdtout = bdt;

      /* Mark that we own the entry */

      bdt->status = 0;
      bdt->addr   = 0;

      bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr);

      /* Now do the same for the other buffer. */

      bdt++;
      bdt->status = 0;
      bdt->addr   = 0;

      bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr);
    }

  /* Get the maxpacket size of the endpoint. */

  maxpacket = GETUINT16(desc->mxpacketsize);
  DEBUGASSERT(maxpacket <= PIC32MX_MAXPACKET_SIZE);
  ep->maxpacket = maxpacket;

  /* Set the full, logic EP number (that includes direction encoded in bit 7) */

  if (epin)
    {
      ep->eplog = USB_EPIN(epno);
    }
  else
    {
      ep->eplog = USB_EPOUT(epno);
    }

  return OK;
}

/****************************************************************************
 * Name: pic32mx_epdisable
 ****************************************************************************/

static int pic32mx_epdisable(struct usbdev_ep_s *ep)
{
  struct pic32mx_ep_s *privep;
  volatile uint32_t *ptr;
  int epno;
  int i;
  irqstate_t flags;

#ifdef CONFIG_DEBUG
  if (!ep)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      ulldbg("ERROR: ep=%p\n", ep);
      return -EINVAL;
    }
#endif

  privep = (struct pic32mx_ep_s *)ep;
  epno   = USB_EPNO(ep->eplog);
  usbtrace(TRACE_EPDISABLE, epno);

  /* Cancel any ongoing activity */

  flags = irqsave();
  pic32mx_cancelrequests(privep, -ESHUTDOWN);

  /* Disable the endpoint */

  pic32mx_putreg(0, PIC32MX_USB_EP(epno));

  /* Reset the BDTs for the endpoint.  Four BDT entries per endpoint; Two
   * 32-bit words per BDT.
   */

  ptr = (uint32_t *)&g_bdt[EP(epno, EP_DIR_OUT, EP_PP_EVEN)];
  for (i = 0; i < USB_BDT_WORD_SIZE * USB_NBDTS_PER_EP; i++)
    {
      *ptr++ = 0;
    }

  irqrestore(flags);
  return OK;
}

/****************************************************************************
 * Name: pic32mx_epallocreq
 ****************************************************************************/

static struct usbdev_req_s *pic32mx_epallocreq(struct usbdev_ep_s *ep)
{
  struct pic32mx_req_s *privreq;

#ifdef CONFIG_DEBUG
  if (!ep)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      return NULL;
    }
#endif
  usbtrace(TRACE_EPALLOCREQ, USB_EPNO(ep->eplog));

  privreq = (struct pic32mx_req_s *)kmm_malloc(sizeof(struct pic32mx_req_s));
  if (!privreq)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_ALLOCFAIL), 0);
      return NULL;
    }

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

/****************************************************************************
 * Name: pic32mx_epfreereq
 ****************************************************************************/

static void pic32mx_epfreereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
  struct pic32mx_req_s *privreq = (struct pic32mx_req_s *)req;

#ifdef CONFIG_DEBUG
  if (!ep || !req)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      return;
    }
#endif
  usbtrace(TRACE_EPFREEREQ, USB_EPNO(ep->eplog));

  kmm_free(privreq);
}

/****************************************************************************
 * Name: pic32mx_epsubmit
 ****************************************************************************/

static int pic32mx_epsubmit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
  struct pic32mx_req_s *privreq = (struct pic32mx_req_s *)req;
  struct pic32mx_ep_s *privep = (struct pic32mx_ep_s *)ep;
  struct pic32mx_usbdev_s *priv;
  irqstate_t flags;
  uint8_t epno;
  int ret = OK;

#ifdef CONFIG_DEBUG
  if (!req || !req->callback || !req->buf || !ep)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      ulldbg("ERROR: req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf, ep);
      return -EINVAL;
    }
#endif

  usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog));
  priv = privep->dev;

#ifdef CONFIG_DEBUG
  if (!priv->driver)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_NOTCONFIGURED), priv->usbdev.speed);
      ulldbg("ERROR: driver=%p\n", priv->driver);
      return -ESHUTDOWN;
    }
#endif

  /* Handle the request from the class driver */

  epno                 = USB_EPNO(ep->eplog);
  req->result          = -EINPROGRESS;
  req->xfrd            = 0;
  privreq->inflight[0] = 0;
#ifndef CONFIG_USBDEV_NOWRITEAHEAD
  privreq->inflight[1] = 0;
#endif
  flags                = irqsave();

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

  pic32mx_addlast(&privep->pend, privreq);

  /* Handle IN (device-to-host) requests.  NOTE:  If the class device is
   * using the bi-directional EP0, then we assume that they intend the EP0
   * IN functionality.
   */

  if (USB_ISEPIN(ep->eplog) || epno == EP0)
    {
      usbtrace(TRACE_INREQQUEUED(epno), req->len);

      /* If the endpoint is not stalled and an IN endpoint BDT is available,
       * then transfer the data now.
       */

      if (!privep->stalled)
        {
          (void)pic32mx_wrrequest(priv, privep);
        }
    }

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

  else
    {
      usbtrace(TRACE_OUTREQQUEUED(epno), req->len);

      /* Set up the read operation (unless the endpoint is stalled).  Because
       * the PIC32MX supports ping-pong* buffering.  There may be two pending
       * read requests.  The following call will attempt to setup a read
       * using this request for this endpoint.  It is not harmful if this
       * fails.
       */

      if (!privep->stalled)
        {
          (void)pic32mx_rdrequest(priv, privep);
        }
    }

  irqrestore(flags);
  return ret;
}

/****************************************************************************
 * Name: pic32mx_epcancel
 ****************************************************************************/

static int pic32mx_epcancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
  struct pic32mx_ep_s *privep = (struct pic32mx_ep_s *)ep;
  irqstate_t flags;

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

  usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog));

  flags = irqsave();
  pic32mx_cancelrequests(privep, -EAGAIN);
  irqrestore(flags);
  return OK;
}

/****************************************************************************
 * Name: pic32mx_epbdtstall
 ****************************************************************************/

static int pic32mx_epbdtstall(struct usbdev_ep_s *ep, bool resume, bool epin)
{
  struct pic32mx_ep_s *privep;
  struct pic32mx_usbdev_s *priv;
  volatile struct usbotg_bdtentry_s *bdt;
  volatile struct usbotg_bdtentry_s *otherbdt;
  uint32_t regaddr;
  uint16_t regval;
  uint8_t epno;

  /* Recover pointers */

  privep = (struct pic32mx_ep_s *)ep;
  priv   = (struct pic32mx_usbdev_s *)privep->dev;
  epno   = USB_EPNO(ep->eplog);

  /* Check for an IN endpoint */

  if (epin)
    {
      /* Get a pointer to the current IN BDT */

      bdt = privep->bdtin;

      /* Get the other BDT */

      otherbdt = &g_bdt[EP(epno, EP_DIR_IN, EP_PP_EVEN)];
      if (otherbdt == bdt)
        {
          otherbdt++;
        }

      /* Reset the data toggle */

      privep->txdata1 = false;
    }

  /* Otherwise it is an an OUT endpoint. */

  else
    {
      /* Get a pointer to the current OUT BDT */

      bdt = privep->bdtout;

      /* Get a pointer to the other BDT */

      otherbdt = &g_bdt[EP(epno, EP_DIR_OUT, EP_PP_EVEN)];
      if (otherbdt == bdt)
        {
          otherbdt++;
        }

      /* Reset the data toggle */

      privep->rxdata1 = false;
    }

  /* Handle the resume condition */

  if (resume)
    {
      /* Resuming a stalled endpoint */

      usbtrace(TRACE_EPRESUME, epno);

      /* Point to the appropriate EP register */

      regaddr = PIC32MX_USB_EP(epno);

      /* Clear the STALL bit in the UEP register */

      regval  = pic32mx_getreg(regaddr);
      regval &= ~USB_EP_EPSTALL;
      pic32mx_putreg(regval, regaddr);

      /* Check for the EP0 OUT endpoint.  This is a special case because we
       * need to set it up to receive the next setup packet (Hmmm... what
       * if there are queued outgoing reponses.  We need to revisit this.)
       */

      if (epno == 0 && !epin)
        {
          uint32_t bytecount = (USB_SIZEOF_CTRLREQ << USB_BDT_BYTECOUNT_SHIFT);
          uint32_t physaddr  = PHYS_ADDR(&priv->ctrl);

          /* Configure the other BDT to receive a SETUP command. */

          otherbdt->addr     = (uint8_t *)physaddr;
          otherbdt->status   = (USB_BDT_UOWN | bytecount);

          /* Configure the current BDT to receive a SETUP command. */

          bdt->addr          = (uint8_t *)physaddr;
          bdt->status        = (USB_BDT_UOWN | bytecount);

          bdtdbg("EP0 BDT IN [%p] {%08x, %08x}\n",
                 bdt, bdt->status, bdt->addr);
          bdtdbg("EP0 BDT IN [%p] {%08x, %08x}\n",
                 otherbdt, otherbdt->status, otherbdt->addr);
        }
      else
        {
          /* Return the other BDT to the CPU. */

          otherbdt->addr   = 0;
          otherbdt->status = 0;

          /* Return the current BDT to the CPU. */

          bdt->addr        = 0;
          bdt->status      = 0;

          bdtdbg("EP%d BDT %s [%p] {%08x, %08x}\n",
                 epno, epin ? "IN" : "OUT", bdt, bdt->status, bdt->addr);
          bdtdbg("EP%d BDT %s [%p] {%08x, %08x}\n",
                 epno, epin ? "IN" : "OUT", otherbdt, otherbdt->status, otherbdt->addr);

          /* Restart any queued requests (after a delay so that we can be assured
           * that the hardware has recovered from the stall -- I don't know of any
           * other way to assure this.).
           */

          pic32mx_delayedrestart(priv, epno);
        }
    }

  /* Handle the stall condition */

  else
    {
      usbtrace(TRACE_EPSTALL, epno);
      privep->stalled  = true;

      /* Stall the other BDT. */

      otherbdt->status = (USB_BDT_UOWN | USB_BDT_BSTALL);
      otherbdt->addr   = 0;

      /* Stall the current BDT. */

      bdt->status      = (USB_BDT_UOWN | USB_BDT_BSTALL);
      bdt->addr        = 0;

      /* Stop any queued requests.  Hmmm.. is there a race condition here? */

      pic32mx_rqstop(privep);

      bdtdbg("EP%d BDT %s [%p] {%08x, %08x}\n",
             epno, epin ? "IN" : "OUT", bdt, bdt->status, bdt->addr);
      bdtdbg("EP%d BDT %s [%p] {%08x, %08x}\n",
             epno, epin ? "IN" : "OUT", otherbdt, otherbdt->status, otherbdt->addr);
    }

  return OK;
}

/****************************************************************************
 * Name: pic32mx_epstall
 ****************************************************************************/

static int pic32mx_epstall(struct usbdev_ep_s *ep, bool resume)
{
  struct pic32mx_ep_s *privep;
  irqstate_t flags;
  int ret;

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

  /* Recover pointers */

  privep = (struct pic32mx_ep_s *)ep;

  /* STALL or RESUME the endpoint */

  flags = irqsave();

  /* Special case EP0.  When we stall EP0 we have to stall both the IN and
   * OUT BDTs.
   */

  if (USB_EPNO(ep->eplog) == 0)
    {
      ret = pic32mx_epbdtstall(ep, resume, true);
      if (ret == OK)
        {
          ret = pic32mx_epbdtstall(ep, resume, false);
        }

      /* Set the EP0 control state appropriately */

      privep->dev->ctrlstate = resume ? CTRLSTATE_WAITSETUP : CTRLSTATE_STALLED;
    }

  /* Otherwise, select the BDT for the endpoint direction */

  else
    {
      /* It is a unidirectional endpoint */

      ret = pic32mx_epbdtstall(ep, resume, USB_ISEPIN(ep->eplog));
    }

  irqrestore(flags);
  return ret;
}

/****************************************************************************
 * Device Controller Operations
 ****************************************************************************/
/****************************************************************************
 * Name: pic32mx_allocep
 ****************************************************************************/

static struct usbdev_ep_s *pic32mx_allocep(struct usbdev_s *dev, uint8_t epno,
                                           bool epin, uint8_t eptype)
{
  struct pic32mx_usbdev_s *priv = (struct pic32mx_usbdev_s *)dev;
  struct pic32mx_ep_s *privep = NULL;
  uint16_t epset = PIC32MX_ENDP_ALLSET;

  usbtrace(TRACE_DEVALLOCEP, (uint16_t)epno);
#ifdef CONFIG_DEBUG
  if (!dev)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      return NULL;
    }
#endif

  /* 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.  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 (epno >= PIC32MX_NENDPOINTS)
        {
          usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADEPNO), (uint16_t)epno);
          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 = PIC32MX_ENDP_BIT(epno);
    }

  /* Check if the selected endpoint number is available */

  privep = pic32mx_epreserve(priv, epset);
  if (!privep)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_EPRESERVE), (uint16_t)epset);
      return NULL;
    }

  return &privep->ep;
}

/****************************************************************************
 * Name: pic32mx_freeep
 ****************************************************************************/

static void pic32mx_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep)
{
  struct pic32mx_usbdev_s *priv;
  struct pic32mx_ep_s *privep;

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

  priv   = (struct pic32mx_usbdev_s *)dev;
  privep = (struct pic32mx_ep_s *)ep;
  usbtrace(TRACE_DEVFREEEP, (uint16_t)USB_EPNO(ep->eplog));
  DEBUGASSERT(priv && privep);

  /* Disable the endpoint */

  (void)pic32mx_epdisable(ep);

  /* Mark the endpoint as available */

  pic32mx_epunreserve(priv, privep);
}

/****************************************************************************
 * Name: pic32mx_getframe
 ****************************************************************************/

static int pic32mx_getframe(struct usbdev_s *dev)
{
  uint16_t frml;
  uint16_t frmh;
  uint16_t tmp;

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

  /* Return the last frame number detected by the hardware.  Thr FRMH/L
   * registers are updated with the current frame number whenever a SOF
   * TOKEN is received.
   */

  do
    {
      /* Loop until we can be sure that there was no wrap from the FRML
       * to the FRMH register.
       */

      frmh = pic32mx_getreg(PIC32MX_USB_FRMH) & USB_FRMH_MASK;
      frml = pic32mx_getreg(PIC32MX_USB_FRML) & USB_FRML_MASK;
      tmp  = pic32mx_getreg(PIC32MX_USB_FRMH) & USB_FRMH_MASK;
    }
  while (frmh != tmp);

  /* Combine to for the full 11-bit value */

  tmp = (frmh) << 8 | frml;
  usbtrace(TRACE_DEVGETFRAME, tmp);
  return tmp;
}

/****************************************************************************
 * Name: pic32mx_wakeup
 ****************************************************************************/

static int pic32mx_wakeup(struct usbdev_s *dev)
{
  struct pic32mx_usbdev_s *priv = (struct pic32mx_usbdev_s *)dev;

  usbtrace(TRACE_DEVWAKEUP, 0);
#ifdef CONFIG_DEBUG
  if (!dev)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  /* Resume normal operation. */

  pic32mx_resume(priv);
  return OK;
}

/****************************************************************************
 * Name: pic32mx_selfpowered
 ****************************************************************************/

static int pic32mx_selfpowered(struct usbdev_s *dev, bool selfpowered)
{
  struct pic32mx_usbdev_s *priv = (struct pic32mx_usbdev_s *)dev;

  usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered);

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

  priv->selfpowered = selfpowered;
  return OK;
}

/****************************************************************************
 * Initialization/Reset
 ****************************************************************************/
/****************************************************************************
 * Name: pic32mx_reset
 *
 * Description:
 *   Reset the software and hardware states.  If the USB controller has been
 *   attached to a host, then connect to the bus as well.  At the end of
 *   this reset, the hardware should be in the full up, ready-to-run state.
 *
 ****************************************************************************/

static void pic32mx_reset(struct pic32mx_usbdev_s *priv)
{
  /* Reset the software configuration */

  pic32mx_swreset(priv);

  /* Re-configure the USB controller in its initial, unconnected state */

  pic32mx_hwreset(priv);

  /* pic32mx_attach() was called, then the attach flag will be set and we
   * should also attach to the USB bus.
   */

  if (priv->attached)
    {
      /* usbdev_attach() has already been called.. attach to the bus
       * now
       */

      pic32mx_attach(priv);
    }
}

/****************************************************************************
 * Name: pic32mx_attach
 ****************************************************************************/

static void pic32mx_attach(struct pic32mx_usbdev_s *priv)
{
  uint16_t regval;

  /* Check if we are in the detached state */

  if (priv->devstate == DEVSTATE_DETACHED)
    {
      /* Disable USB interrupts at the interrupt controller */

      up_disable_irq(PIC32MX_IRQSRC_USB);

      /* Initialize registers to known states. */

      pic32mx_putreg(0, PIC32MX_USB_CON);

      /* Configure things like: pull ups, full/low-speed mode,
       * set the ping pong mode, and set internal transceiver
       */

      pic32mx_putreg(0, PIC32MX_USB_CNFG1);

      /* Enable interrupts at the USB controller */

      pic32mx_putreg(ERROR_INTERRUPTS, PIC32MX_USB_EIE);
      pic32mx_putreg(NORMAL_INTERRUPTS, PIC32MX_USB_IE);

      /* Configure EP0 */

      pic32mx_ep0configure(priv);

      /* Flush any pending transactions */

      while ((pic32mx_getreg(PIC32MX_USB_IR) & USB_INT_TRN) != 0)
        {
          pic32mx_putreg(USB_INT_TRN, PIC32MX_USB_IR);
        }

      /* Make sure packet processing is enabled */

      regval = pic32mx_getreg(PIC32MX_USB_CON);
      regval &= ~USB_CON_PKTDIS;
      pic32mx_putreg(regval, PIC32MX_USB_CON);

      /* Enable the USB module and attach to bus */

      do
        {
          regval = pic32mx_getreg(PIC32MX_USB_CON);
          if ((regval & USB_CON_USBEN) == 0)
            {
              pic32mx_putreg(regval | USB_CON_USBEN, PIC32MX_USB_CON);
            }
        }
      while ((regval & USB_CON_USBEN) == 0);

      /* Enable OTG */

#ifdef CONFIG_USBOTG
      regval  = pic32mx_getreg(PIC32MX_USBOTG_CON);
      regval |= (USBOTG_CON_DPPULUP | USBOTG_CON_OTGEN);
      pic32mx_putreg(regval, PIC32MX_USBOTG_CON);
#endif

      /* Transition to the attached state */

      priv->devstate     = DEVSTATE_ATTACHED;
      priv->usbdev.speed = USB_SPEED_FULL;

      /* Clear all pending USB interrupts */

      pic32mx_putreg(USB_EINT_ALL, PIC32MX_USB_EIR);
      pic32mx_putreg(USB_INT_ALL, PIC32MX_USB_IR);

      /* Enable USB interrupts at the interrupt controller */

      up_enable_irq(PIC32MX_IRQSRC_USB);

      /* Enable pull-up to connect the device.  The host should enumerate us
       * some time after this
       */

      pic32mx_usbpullup(&priv->usbdev, true);
    }
}

/****************************************************************************
 * Name: pic32mx_detach
 ****************************************************************************/

static void pic32mx_detach(struct pic32mx_usbdev_s *priv)
{
  /* Disable USB interrupts at the interrupt controller */

  up_disable_irq(PIC32MX_IRQSRC_USB);

  /* Disable the USB controller and detach from the bus. */

  pic32mx_putreg(0, PIC32MX_USB_CON);

  /* Mask all USB interrupts */

  pic32mx_putreg(0, PIC32MX_USB_IE);

  /* We are now in the detached state  */

  priv->attached = 0;
  priv->devstate = DEVSTATE_DETACHED;

#ifdef CONFIG_USBOTG
  /* Disable the D+ Pullup */

  regval  = pic32mx_getreg(PIC32MX_USBOTG_CON);
  regval &= ~USBOTG_CON_DPPULUP;
  pic32mx_putreg(regval, PIC32MX_USBOTG_CON);

  /* Disable and deactivate HNP */
#warning Missing Logic

  /* Check if the ID Pin Changed State */

  if ((pic32mx_getreg(PIC32MX_USBOTG_IR) & pic32mx_getreg(PIC32MX_USBOTG_IE) & USBOTG_INT_ID) != 0)
    {
      /* Re-detect & Initialize */
#warning "Missing logic"

      /* Clear ID Interrupt Flag */

      pic32mx_putreg(USBOTG_INT_ID, PIC32MX_USBOTG_IR);
    }
#endif
}

/****************************************************************************
 * Name: pic32mx_swreset
 ****************************************************************************/

static void pic32mx_swreset(struct pic32mx_usbdev_s *priv)
{
  int epno;

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

  if (priv->driver)
    {
      CLASS_DISCONNECT(priv->driver, &priv->usbdev);
    }

  /* Flush and reset endpoint states (except EP0) */

  for (epno = 1; epno < PIC32MX_NENDPOINTS; epno++)
    {
      struct pic32mx_ep_s *privep = &priv->eplist[epno];

      /* Cancel any queued requests.  Since they are canceled
       * with status -ESHUTDOWN, then will not be requeued
       * until the configuration is reset.  NOTE:  This should
       * not be necessary... the CLASS_DISCONNECT above should
       * result in the class implementation calling pic32mx_epdisable
       * for each of its configured endpoints.
       */

      pic32mx_cancelrequests(privep, -EAGAIN);

      /* Reset endpoint status */

      privep->stalled   = false;
      privep->halted    = false;
      privep->txnullpkt = false;
    }

  /* Reset to the default address */

  pic32mx_putreg(0, PIC32MX_USB_ADDR);

  /* Unconfigure each endpoint by clearing the endpoint control registers
   * (except EP0)
   */

  for (epno = 1; epno < PIC32MX_NENDPOINTS; epno++)
    {
      pic32mx_putreg(0, PIC32MX_USB_EP(epno));
    }

  /* Reset the control state */

  priv->ctrlstate = CTRLSTATE_WAITSETUP;
  priv->rxbusy    = 0;
}

/****************************************************************************
 * Name: pic32mx_hwreset
 *
 * Description:
 *   Reset the hardware and leave it in a known, unready state.
 *
 ****************************************************************************/

static void pic32mx_hwreset(struct pic32mx_usbdev_s *priv)
{
  uint32_t physaddr;
  uint16_t regval;

  /* Power down the USB module.  This will reset all USB registers. */

  regval = pic32mx_getreg(PIC32MX_USB_PWRC);
  regval &= ~USB_PWRC_USBPWR;
  pic32mx_putreg(regval, PIC32MX_USB_PWRC);

  /* Clear all of the buffer descriptor table (BDT) entries */

  memset((void *)g_bdt, 0, sizeof(g_bdt));

  /* Power up the USB module */

  regval |= USB_PWRC_USBPWR;
  pic32mx_putreg(regval, PIC32MX_USB_PWRC);

  /* Set the address of the buffer descriptor table (BDT)
   *
   * BDTP1: Bit 1-7: Bits 9-15 of the BDT base address
   * BDTP2: Bit 0-7: Bits 16-23 of the BDT base address
   * BDTP3: Bit 0-7: Bits 24-31 of the BDT base address
   */

  physaddr = PHYS_ADDR(g_bdt);
  pic32mx_putreg((uint16_t)((physaddr >> 24) & USB_BDTP3_MASK), PIC32MX_USB_BDTP3);
  pic32mx_putreg((uint16_t)((physaddr >> 16) & USB_BDTP2_MASK), PIC32MX_USB_BDTP2);
  pic32mx_putreg((uint16_t)((physaddr >>  8) & USB_BDTP1_MASK), PIC32MX_USB_BDTP1);

  /* Assert reset request to all of the Ping Pong buffer pointers.  This
   * will reset all Even/Odd buffer pointers to the EVEN BD banks.
   */

  regval  = pic32mx_getreg(PIC32MX_USB_CON);
  regval |= USB_CON_PPBRST;
  pic32mx_putreg(regval, PIC32MX_USB_CON);

  /* Bring the ping pong buffer pointers out of reset */

  regval &= ~USB_CON_PPBRST;
  pic32mx_putreg(regval, PIC32MX_USB_CON);

  /* Indicate that we are now in the detached state  */

  priv->devstate = DEVSTATE_DETACHED;
}

/****************************************************************************
 * Name: pic32mx_stateinit
 ****************************************************************************/

static void pic32mx_stateinit(struct pic32mx_usbdev_s *priv)
{
  int epno;

  /* Disconnect the device / disable the pull-up.  We don't want the
   * host to enumerate us until the class driver is registered.
   */

  pic32mx_usbpullup(&priv->usbdev, false);

  /* Initialize the device state structure.  NOTE: many fields
   * have the initial value of zero and, hence, are not explicitly
   * initialized here.
   */

  memset(priv, 0, sizeof(struct pic32mx_usbdev_s));
  priv->usbdev.ops   = &g_devops;
  priv->usbdev.ep0   = &priv->eplist[EP0].ep;
  priv->epavail      = PIC32MX_ENDP_ALLSET & ~PIC32MX_ENDP_BIT(EP0);
  priv->rwakeup      = 1;

  /* Initialize the endpoint list */

  for (epno = 0; epno < PIC32MX_NENDPOINTS; epno++)
    {
      struct pic32mx_ep_s *privep = &priv->eplist[epno];

      /* 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.
       */

      privep->ep.ops           = &g_epops;
      privep->dev              = priv;
      privep->ep.eplog         = epno;

      /* We will use a fixed maxpacket size for all endpoints (perhaps
       * ISOC endpoints could have larger maxpacket???).  A smaller
       * packet size can be selected when the endpoint is configured.
       */

      privep->ep.maxpacket     = PIC32MX_MAXPACKET_SIZE;
    }

  /* Select a smaller endpoint size for EP0 */

#if PIC32MX_EP0MAXPACKET < PIC32MX_MAXPACKET_SIZE
  priv->eplist[EP0].ep.maxpacket = PIC32MX_EP0MAXPACKET;
#endif
}

/****************************************************************************
 * Name: pic32mx_hwshutdown
 ****************************************************************************/

static void pic32mx_hwshutdown(struct pic32mx_usbdev_s *priv)
{
  uint16_t regval;

  /* Put the hardware and driver in its initial, unconnected state */

  pic32mx_swreset(priv);
  pic32mx_hwreset(priv);
  priv->usbdev.speed = USB_SPEED_UNKNOWN;

  /* Disable all interrupts and force the USB controller into reset */

  pic32mx_putreg(0, PIC32MX_USB_EIE);
  pic32mx_putreg(0, PIC32MX_USB_IE);

  /* Clear any pending interrupts */

  pic32mx_putreg(USB_EINT_ALL, PIC32MX_USB_EIR);
  pic32mx_putreg(USB_INT_ALL, PIC32MX_USB_IR);

  /* Disconnect the device / disable the pull-up */

  pic32mx_usbpullup(&priv->usbdev, false);

  /* Power down the USB controller */

  regval  = pic32mx_getreg(PIC32MX_USB_PWRC);
  regval &= ~USB_PWRC_USBPWR;
  pic32mx_putreg(regval, PIC32MX_USB_PWRC);
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/
/****************************************************************************
 * Name: up_usbinitialize
 *
 * Description:
 *   Initialize the USB driver
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

void up_usbinitialize(void)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct pic32mx_usbdev_s *priv = &g_usbdev;

  usbtrace(TRACE_DEVINIT, 0);

  /* Initialize the driver state structure */

  pic32mx_stateinit(priv);

  /* Then perform a few one-time initialization operstions.  First, initialize
   * the watchdog timer that is used to perform a delayed queue restart
   * after recovering from a stall.
   */

  priv->epstalled = 0;
  priv->wdog      = wd_create();

  /* Attach USB controller interrupt handler.  The hardware will not be
   * initialized and interrupts will not be enabled until the class device
   * driver is bound.  Getting the IRQs here only makes sure that we have
   * them when we need them later.
   */

  if (irq_attach(PIC32MX_IRQ_USB, pic32mx_interrupt) != 0)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_IRQREGISTRATION),
               (uint16_t)PIC32MX_IRQ_USB);
      up_usbuninitialize();
    }
}

/****************************************************************************
 * Name: up_usbuninitialize
 * Description:
 *   Initialize the USB driver
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

void up_usbuninitialize(void)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct pic32mx_usbdev_s *priv = &g_usbdev;
  irqstate_t flags;

  flags = irqsave();
  usbtrace(TRACE_DEVUNINIT, 0);

  /* Disable and detach the USB IRQs */

  up_disable_irq(PIC32MX_IRQSRC_USB);
  irq_detach(PIC32MX_IRQ_USB);

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

  /* Put the hardware in an inactive state */

  pic32mx_hwshutdown(priv);
  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)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct pic32mx_usbdev_s *priv = &g_usbdev;
  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(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }

  if (priv->driver)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_DRIVER), 0);
      return -EBUSY;
    }
#endif

  /* First hook up the driver */

  priv->driver = driver;

  /* Then bind the class driver */

  ret = CLASS_BIND(driver, &priv->usbdev);
  if (ret)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BINDFAILED), (uint16_t)-ret);
      priv->driver = NULL;
    }

  /* The class driver has been successfully bound. */

  else
    {
      /* Setup the USB controller in it initial ready-to-run state (might
       * be connected or unconnected, depending on usbdev_attach() has
       * been called).
       */

      DEBUGASSERT(priv->devstate == DEVSTATE_DETACHED);
      pic32mx_reset(priv);
   }
  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)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct pic32mx_usbdev_s *priv = &g_usbdev;
  irqstate_t flags;

  usbtrace(TRACE_DEVUNREGISTER, 0);

#ifdef CONFIG_DEBUG
  if (driver != priv->driver)
    {
      usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  /* Reset the hardware and cancel all requests.  All requests must be
   * canceled while the class driver is still bound.  This will put
   * the hardware back into its initial, unconnected state.
   */

  flags = irqsave();
  pic32mx_swreset(priv);
  pic32mx_hwreset(priv);

  /* Unbind the class driver */

  CLASS_UNBIND(driver, &priv->usbdev);

  /* Disable USB controller interrupts (but keep them attached) */

  up_disable_irq(PIC32MX_IRQSRC_USB);

  /* Put the hardware in an inactive state.  Then bring the hardware back up
   * in the reset state (this is probably not necessary, the pic32mx_hwreset()
   * call above was probably sufficient).
   */

  pic32mx_hwshutdown(priv);
  pic32mx_stateinit(priv);

  /* Unhook the driver */

  priv->driver = NULL;
  irqrestore(flags);
  return OK;
}

/****************************************************************************
 * Name: pic32mx_usbattach and pic32mx_usbdetach
 *
 * Description:
 *   The USB stack must be notified when the device is attached or detached
 *   by calling one of these functions.
 *
 ****************************************************************************/

void pic32mx_usbattach(void)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct pic32mx_usbdev_s *priv = &g_usbdev;

  /* Mark that we are attached */

  priv->attached = 1;

  /* This API may be called asynchronously from other initialization
   * interfaces.  In particular, we may not want to attach the bus yet...
   * that should only be done when the class driver is attached.  Has
   * the class driver been attached?
   */

  if (priv->driver)
    {
      /* Yes.. then attach to the bus */

      pic32mx_attach(priv);
    }
}

void pic32mx_usbdetach(void)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct pic32mx_usbdev_s *priv = &g_usbdev;

  /* Detach from the bus */

  pic32mx_detach(priv);
}

#endif /* CONFIG_USBDEV && CONFIG_PIC32MX_USB */
