/****************************************************************************
 * drivers/usbhost/usbhost_max3421e.c
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/* References:
 *   "MAX3421E USB Peripheral/Host Controller with SPI Interface",
 *      19-3953, Rev 4, Maxim Integrated, July 2013 (Datasheet).
 *   "MAX3421E Programming Guide", Maxim Integrated, December 2006
 *      (Application Note).
 */

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

#include <nuttx/config.h>

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

#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/clock.h>
#include <nuttx/signal.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/wqueue.h>
#include <nuttx/spi/spi.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbhost.h>
#include <nuttx/usb/usbhost_devaddr.h>
#include <nuttx/usb/max3421e.h>
#include <nuttx/usb/usbhost_trace.h>

#include <nuttx/irq.h>

#ifdef CONFIG_USBHOST_MAX3421E

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

/* Configuration ************************************************************/

/* MAX3421E USB Host Driver Support
 *
 * Pre-requisites
 *
 *  CONFIG_USBHOST - Enable general USB host support
 *  CONFIG_USBHOST_MAX3421E - Enable the MAX3421E USB host support
 *  CONFIG_SCHED_LPWORK - Low priority work queue support is required.
 *
 * Options:
 *
 *   CONFIG_MAX3421E_DESCSIZE - Maximum size of a descriptor.  Default: 128
 *   CONFIG_MAX3421E_USBHOST_REGDEBUG - Enable very low-level register access
 *     debug.  Depends on CONFIG_DEBUG_USB_INFO.
 *   CONFIG_MAX3421E_USBHOST_PKTDUMP - Dump all incoming and outgoing USB
 *     packets. Depends on CONFIG_DEBUG_USB_INFO.
 */

/* Maximum size of a descriptor */

#ifndef CONFIG_MAX3421E_DESCSIZE
#  define CONFIG_MAX3421E_DESCSIZE 128
#endif

/* Low-priority work queue support is required.  The high priority work
 * queue is not used because this driver requires SPI access and may
 * block or wait for a variety of reasons.
 */

#ifndef CONFIG_SCHED_LPWORK
#  warning Low priority work thread support is necessary (CONFIG_SCHED_LPWORK)
#endif

/* Register/packet debug depends on CONFIG_DEBUG_FEATURES */

#ifndef CONFIG_DEBUG_USB_INFO
#  undef CONFIG_MAX3421E_USBHOST_REGDEBUG
#  undef CONFIG_MAX3421E_USBHOST_PKTDUMP
#endif

/* Delays *******************************************************************/

#define MAX3421E_READY_DELAY         200000      /* In loop counts */
#define MAX3421E_FLUSH_DELAY         200000      /* In loop counts */
#define MAX3421E_SETUP_DELAY         SEC2TICK(5) /* 5 seconds in system ticks */
#define MAX3421E_DATANAK_DELAY       SEC2TICK(5) /* 5 seconds in system ticks */
#define MAX3421E_RETRY_COUNT         5           /* Number of tries before giving up */

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

#define TR_FMT1 false
#define TR_FMT2 true

#define TRENTRY(id,fmt1,string) {string}

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

/* The following enumeration represents the various states of the USB host
 * state machine (for debug purposes only)
 */

enum max3421e_smstate_e
{
  SMSTATE_DETACHED = 0,  /* Not attached to a device */
  SMSTATE_ATTACHED,      /* Attached to a device */
  SMSTATE_ENUM,          /* Attached, enumerating */
  SMSTATE_CLASS_BOUND,   /* Enumeration complete, class bound */
};

/* When a transfer completes and the HXFRDN interrupt is received, this
 * informs the HXFRDN interrupt handle of the type of transfer that just
 * completed.
 */

enum mx3421e_hxfrdn_e
{
  HXFRDN_SETUP = 0,      /* A setup transfer just completed */
  HXFRDN_SNDZLP,         /* A zero length IN transfer just completed */
  HXFRDN_SNDFIFO,        /* A normal IN transfer using SNDFIFO just completed */
  HXFRDN_RCVFIFO,        /* A normal OUT transfer using RCVFIFO just completed */
};

/* This structure retains the state of one host channel.  NOTE: Since there
 * is only one channel operation active at a time, some of the fields in
 * in the structure could be moved in struct max3421e_ubhost_s to achieve
 * some memory savings.
 */

struct max3421e_chan_s
{
  bool              inuse;     /* True: This channel is "in use" */
  bool              in;        /* True: IN endpoint */
  uint8_t           chidx;     /* Channel index (0-3) */
  uint8_t           epno;      /* Device endpoint number (0-127) */
  uint8_t           eptype;    /* See MAX3421E_EPTYPE_* definitions */
  uint8_t           funcaddr;  /* Device function address */
  uint8_t           speed;     /* Device speed */
  uint8_t           interval;  /* Interrupt/isochronous EP polling interval */
  uint8_t           maxpacket; /* Max packet size (8 or 64) */
  uint8_t           toggles;   /* Saved data toggles from the HCTL register */
};

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

struct max3421e_usbhost_s
{
  /* Common device fields.  This must be the first thing defined in the
   * structure so that it is possible to simply cast from struct
   * usbhost_driver_s to struct max3421e_usbhost_s.
   */

  struct usbhost_driver_s drvr;

  /* This is the interface to the max3421e lower-half driver */

  FAR const struct max3421e_lowerhalf_s *lower;

  /* This is the hub port description understood by class drivers */

  struct usbhost_roothubport_s rhport;

  /* Overall driver status */

  bool              connected; /* Connected to device */
  bool              change;    /* Connection change */
  bool              pscwait;   /* True: Thread is waiting for a port event */
#ifdef CONFIG_DEBUG_ASSERTIONS
  bool              locked;    /* The SPI bus is locked */
#endif
  uint8_t           smstate;   /* The state of the USB host state machine */
  uint8_t           irqset;    /* Set of enabled interrupts */
  uint8_t           xfrtype;   /* See enum mx3421e_hxfrdn_e */
  uint8_t           inflight;  /* Number of Tx bytes "in-flight" (<= 128) */
  uint8_t           result;    /* The result of the transfer */
  uint16_t          buflen;    /* Buffer length (at start of transfer) */
  uint16_t          xfrd;      /* Bytes transferred (at end of transfer) */
  rmutex_t          lock;      /* Support mutually exclusive access */
  sem_t             pscsem;    /* Semaphore to wait for a port event */
  sem_t             waitsem;   /* Channel wait semaphore */
  FAR uint8_t      *buffer;    /* Transfer buffer pointer */
#ifdef CONFIG_USBHOST_ASYNCH
  usbhost_asynch_t  callback;  /* Transfer complete callback */
  FAR void         *arg;       /* Argument that accompanies the callback */
#endif
  struct work_s     irqwork;   /* Used to process interrupts */

#ifdef CONFIG_USBHOST_HUB
  /* Used to pass external hub port events */

  FAR struct usbhost_hubport_s *hport;
#endif

  struct usbhost_devaddr_s devgen;  /* Address generation data */

  /* The channel waiting for the next event (there will only be one in
   * this design)
   */

  FAR struct max3421e_chan_s *waiter;

  /* The state of each host channel, one for each device endpoint. */

  struct max3421e_chan_s chan[MAX3421E_NHOST_CHANNELS];
};

/* This is the MAX3421E connection structure */

struct max3421e_connection_s
{
  /* Common device fields.  This must be the first thing defined in the
   * structure so that it is possible to simply cast from struct
   * usbhost_connection_s to struct usbhost_connection_s.
   */

  struct usbhost_connection_s conn;

  /* Pointer to the associated state structure */

  FAR struct max3421e_usbhost_s *priv;
};

/* Supports allocation of both structures simultaneously */

struct usbhost_alloc_s
{
  struct max3421e_usbhost_s    priv;
  struct max3421e_connection_s conn;
};

/* Tracing support */

#ifdef HAVE_USBHOST_TRACE
struct max3421e_usbhost_trace_s
{
#if 0
  uint16_t id;
  bool fmt2;
#endif
  FAR const char *string;
};

enum usbhost_trace1codes_e
{
  __TRACE1_BASEVALUE = 0,           /* This will force the first value to be 1 */

  MAX3421E_TRACE1_ALLOC_FAIL,
#ifdef CONFIG_USBHOST_ASYNCH
  MAX3421E_TRACE1_ASYNCHSETUP_FAIL1,
  MAX3421E_TRACE1_ASYNCHSETUP_FAIL2,
#endif
  MAX3421E_TRACE1_BAD_JKSTATE,
  MAX3421E_TRACE1_BADREVISION,
  MAX3421E_TRACE1_CHANALLOC_FAIL,
  MAX3421E_TRACE1_CHANWAIT_FAIL,
  MAX3421E_TRACE1_DEVDISCONN1,
  MAX3421E_TRACE1_DEVDISCONN2,
  MAX3421E_TRACE1_DEVDISCONN3,
  MAX3421E_TRACE1_DEVDISCONN4,
  MAX3421E_TRACE1_DEVDISCONN5,
  MAX3421E_TRACE1_DEVDISCONN6,
  MAX3421E_TRACE1_DEVDISCONN7,
  MAX3421E_TRACE1_DEVDISCONN8,
  MAX3421E_TRACE1_ENUMERATE_FAIL,
  MAX3421E_TRACE1_INSETUP_FAIL1,
#ifdef CONFIG_USBHOST_ASYNCH
  MAX3421E_TRACE1_INSETUP_FAIL2,
  MAX3421E_TRACE1_INSETUP_FAIL3,
#endif
  MAX3421E_TRACE1_IRQATTACH_FAIL,
  MAX3421E_TRACE1_OUTSETUP_FAIL1,
#ifdef CONFIG_USBHOST_ASYNCH
  MAX3421E_TRACE1_OUTSETUP_FAIL2,
  MAX3421E_TRACE1_OUTSETUP_FAIL3,
#endif
  MAX3421E_TRACE1_RECVDATA_FAIL,
  MAX3421E_TRACE1_RECVSTATUS_FAIL,
  MAX3421E_TRACE1_SENDDATA_FAIL,
  MAX3421E_TRACE1_SENDSETUP_FAIL1,
  MAX3421E_TRACE1_SENDSETUP_FAIL2,
  MAX3421E_TRACE1_SENDSTATUS_FAIL,
  MAX3421E_TRACE1_TRANSFER_FAILED1,
  MAX3421E_TRACE1_TRANSFER_FAILED2,
  MAX3421E_TRACE1_TRANSFER_FAILED3,

#ifdef HAVE_USBHOST_TRACE_VERBOSE
  MAX3421E_VTRACE1_CANCEL,
  MAX3421E_VTRACE1_CONNECTED1,
  MAX3421E_VTRACE1_CONNECTED2,
  MAX3421E_VTRACE1_CONNECTED3,
  MAX3421E_VTRACE1_DISCONNECTED1,
  MAX3421E_VTRACE1_DISCONNECTED2,
  MAX3421E_VTRACE1_ENUMERATE,
#ifdef CONFIG_USBHOST_HUB
  MAX3421E_VTRACE1_HUB_CONNECTED,
#endif
  MAX3421E_VTRACE1_INITIALIZED,
#ifdef CONFIG_USBHOST_ASYNCH
  MAX3421E_VTRACE1_TRANSFER_COMPLETE,
#endif

#endif

  __TRACE1_NSTRINGS,                 /* Separates the format 1 from the format 2 strings */

#ifdef HAVE_USBHOST_TRACE_VERBOSE
#ifdef CONFIG_USBHOST_ASYNCH
  MAX3421E_VTRACE2_ASYNCH,
#endif
  MAX3421E_VTRACE2_BULKIN,
  MAX3421E_VTRACE2_BULKOUT,
  MAX3421E_VTRACE2_CHANWAKEUP_IN,
  MAX3421E_VTRACE2_CHANWAKEUP_OUT,
  MAX3421E_VTRACE2_CTRLIN,
  MAX3421E_VTRACE2_CTRLOUT,
#ifdef CONFIG_USBHOST_HUB
  MAX3421E_VTRACE2_HUB_CONNECTED,
#endif
  MAX3421E_VTRACE2_INTRIN,
  MAX3421E_VTRACE2_INTROUT,
  MAX3421E_VTRACE2_ISOCIN,
  MAX3421E_VTRACE2_ISOCOUT,
  MAX3421E_VTRACE2_RECVSTATUS,
  MAX3421E_VTRACE2_SENDSTATUS,
  MAX3421E_VTRACE2_STARTTRANSFER1,
  MAX3421E_VTRACE2_STARTTRANSFER2,
  MAX3421E_VTRACE2_TRANSFER,
#ifdef CONFIG_USBHOST_ASYNCH
  MAX3421E_VTRACE2_XFRCOMPLETE,
#endif
#endif
  __TRACE2_NSTRINGS                  /* Total number of enumeration values */
};

#  define TRACE1_FIRST     ((int)__TRACE1_BASEVALUE + 1)
#  define TRACE1_INDEX(id) ((int)(id) - TRACE1_FIRST)
#  define TRACE1_NSTRINGS  TRACE1_INDEX(__TRACE1_NSTRINGS)

#  define TRACE2_FIRST     ((int)__TRACE1_NSTRINGS + 1)
#  define TRACE2_INDEX(id) ((int)(id) - TRACE2_FIRST)
#  define TRACE2_NSTRINGS  TRACE2_INDEX(__TRACE2_NSTRINGS)

#endif

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

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

static void max3421e_lock(FAR struct max3421e_usbhost_s *priv);
static void max3421e_unlock(FAR struct max3421e_usbhost_s *priv);

#ifdef CONFIG_MAX3421E_USBHOST_REGDEBUG
static void max3421e_printreg(uint8_t addr, uint8_t val, bool iswrite);
static void max3421e_checkreg(uint8_t addr, uint8_t val, bool iswrite)
#endif

static inline uint8_t max3421e_fmtcmd(FAR struct max3421e_usbhost_s *priv,
              uint8_t addr, uint8_t dir);
static uint32_t max3421e_getreg(FAR struct max3421e_usbhost_s *priv,
              uint8_t addr);
static void max3421e_putreg(FAR struct max3421e_usbhost_s *priv,
              uint8_t addr, uint8_t value);

static inline void max3421e_modifyreg(FAR struct max3421e_usbhost_s *priv,
              uint8_t addr, uint8_t clrbits, uint8_t setbits);

static void max3421e_recvblock(FAR struct max3421e_usbhost_s *priv,
              uint8_t addr, FAR void *buffer, size_t buflen);
static void max3421e_sndblock(FAR struct max3421e_usbhost_s *priv,
              uint8_t addr, FAR const void *buffer, size_t buflen);

#ifdef CONFIG_MAX3421E_USBHOST_PKTDUMP
#  define max3421e_pktdump(m,b,n) lib_dumpbuffer(m,b,n)
#else
#  define max3421e_pktdump(m,b,n)
#endif

/* Byte stream access helper functions **************************************/

static inline uint16_t max3421e_getle16(const uint8_t *val);

/* Channel management *******************************************************/

static int max3421e_chan_alloc(FAR struct max3421e_usbhost_s *priv);
static inline void max3421e_chan_free(FAR struct max3421e_usbhost_s *priv,
              int chidx);
static int max3421e_chan_waitsetup(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
#ifdef CONFIG_USBHOST_ASYNCH
static int max3421e_chan_asynchsetup(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan, usbhost_asynch_t callback,
              FAR void *arg);
#endif
static int max3421e_chan_wait(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static void max3421e_chan_wakeup(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan, int result);

/* Control/data transfer logic **********************************************/

static inline void max3421e_save_toggles(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static inline void max3421e_restore_toggles(
              FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static int  max3421e_transfer_status(FAR struct max3421e_usbhost_s *priv);
static void max3421e_transfer_terminate(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan, int result);

/* OUT transfers */

static void max3421e_put_sndfifo(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static void max3421e_send_continue(FAR struct max3421e_usbhost_s *priv);
static void max3421e_send_start(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static ssize_t max3421e_out_transfer(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan, FAR uint8_t *buffer,
              size_t buflen);
#ifdef CONFIG_USBHOST_ASYNCH
static void max3421e_out_next(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static int  max3421e_out_asynch(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan, FAR uint8_t *buffer,
              size_t buflen, usbhost_asynch_t callback, FAR void *arg);
#endif

/* Control transfers */

static int  max3421e_ctrl_sendsetup(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan,
              FAR const struct usb_ctrlreq_s *req);
static int  max3421e_ctrl_senddata(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan,
              FAR uint8_t *buffer, unsigned int buflen);
static int  max3421e_ctrl_sendstatus(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static int  max3421e_ctrl_recvstatus(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static int  max3421e_in_setup(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static int  max3421e_out_setup(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);

/* IN transfers */

static uint8_t max3421e_get_rcvfifo(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static void max3421e_recv_restart(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static void max3421e_recv_continue(FAR struct max3421e_usbhost_s *priv);
static void max3421e_recv_start(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static ssize_t max3421e_in_transfer(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan, FAR uint8_t *buffer,
              size_t buflen);
#ifdef CONFIG_USBHOST_ASYNCH
static void max3421e_in_next(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan);
static int  max3421e_in_asynch(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_chan_s *chan, FAR uint8_t *buffer,
              size_t buflen, usbhost_asynch_t callback, FAR void *arg);
#endif

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

static void max3421e_connect_event(FAR struct max3421e_usbhost_s *priv);
static void max3421e_disconnect_event(FAR struct max3421e_usbhost_s *priv);
static int  max3421e_connected(FAR struct max3421e_usbhost_s *priv);
static void max3421e_disconnected(FAR struct max3421e_usbhost_s *priv);
static void max3421e_irqwork(FAR void *arg);
static int  max3421e_interrupt(int irq, FAR void *context, FAR void *arg);

/* Interrupt controls */

static inline void max3421e_int_enable(FAR struct max3421e_usbhost_s *priv,
              uint8_t irqset);
static inline void max3421e_int_disable(FAR struct max3421e_usbhost_s *priv,
              uint8_t irqset);
static inline uint8_t max3421e_int_status(
              FAR struct max3421e_usbhost_s *priv);
static inline void max3421e_int_clear(FAR struct max3421e_usbhost_s *priv,
              uint8_t irqset);
static void max3421e_int_wait(FAR struct max3421e_usbhost_s *priv,
              uint8_t irqset, unsigned int usec);

/* USB host controller operations *******************************************/

static int  max3421e_wait(FAR struct usbhost_connection_s *conn,
              FAR struct usbhost_hubport_s **hport);
static int  max3421e_getspeed(FAR struct max3421e_usbhost_s *priv,
              FAR struct usbhost_connection_s *conn,
              FAR struct usbhost_hubport_s *hport);
static int  max3421e_enumerate(FAR struct usbhost_connection_s *conn,
              FAR struct usbhost_hubport_s *hport);

static int  max3421e_ep0configure(FAR struct usbhost_driver_s *drvr,
              usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed,
              uint16_t maxpacketsize);
static int  max3421e_epalloc(FAR struct usbhost_driver_s *drvr,
              FAR const struct usbhost_epdesc_s *epdesc,
              FAR usbhost_ep_t *ep);
static int  max3421e_epfree(FAR struct usbhost_driver_s *drvr,
              usbhost_ep_t ep);
static int  max3421e_alloc(FAR struct usbhost_driver_s *drvr,
              FAR uint8_t **buffer, FAR size_t *maxlen);
static int  max3421e_free(FAR struct usbhost_driver_s *drvr,
              FAR uint8_t *buffer);
static int  max3421e_ioalloc(FAR struct usbhost_driver_s *drvr,
              FAR uint8_t **buffer, size_t buflen);
static int  max3421e_iofree(FAR struct usbhost_driver_s *drvr,
              FAR uint8_t *buffer);
static int  max3421e_ctrlin(FAR struct usbhost_driver_s *drvr,
              usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req,
              FAR uint8_t *buffer);
static int  max3421e_ctrlout(FAR struct usbhost_driver_s *drvr,
              usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req,
              FAR const uint8_t *buffer);
static ssize_t max3421e_transfer(FAR struct usbhost_driver_s *drvr,
              usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen);
#ifdef CONFIG_USBHOST_ASYNCH
static int  max3421e_asynch(FAR struct usbhost_driver_s *drvr,
              usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen,
              usbhost_asynch_t callback, FAR void *arg);
#endif
static int  max3421e_cancel(FAR struct usbhost_driver_s *drvr,
                            usbhost_ep_t ep);
#ifdef CONFIG_USBHOST_HUB
static int  max3421e_connect(FAR struct max3421e_usbhost_s *priv,
              FAR struct usbhost_hubport_s *hport, bool connected);
#endif
static void max3421e_disconnect(FAR struct usbhost_driver_s *drvr,
              FAR struct usbhost_hubport_s *hport);

/* Initialization ***********************************************************/

static void max3421e_busreset(FAR struct max3421e_usbhost_s *priv);
static int  max3421e_startsof(FAR struct max3421e_usbhost_s *priv);

static inline int max3421e_sw_initialize(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_connection_s *conn,
              FAR const struct max3421e_lowerhalf_s *lower);
static inline int max3421e_hw_initialize(
              FAR struct max3421e_usbhost_s *priv);

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

#ifdef HAVE_USBHOST_TRACE
/* Trace/debug format strings. */

static const struct max3421e_usbhost_trace_s g_trace1[TRACE1_NSTRINGS] =
{
  TRENTRY(MAX3421E_TRACE1_ALLOC_FAIL, TR_FMT1,
          "INIT: Failed to allocate state structure: %u\n"),
#ifdef CONFIG_USBHOST_ASYNCH
  TRENTRY(MAX3421E_TRACE1_ASYNCHSETUP_FAIL1, TR_FMT1,
          "OUT: Asynch setup failed: %u\n"),
  TRENTRY(MAX3421E_TRACE1_ASYNCHSETUP_FAIL2, TR_FMT1,
          "IN: Asynch setup failed: %u\n"),
#endif
  TRENTRY(MAX3421E_TRACE1_BAD_JKSTATE, TR_FMT1,
          "CONNECT: Bad JK state: %02x\n"),
  TRENTRY(MAX3421E_TRACE1_BADREVISION, TR_FMT1,
          "INIT: Bad revision number:  %02x\n"),
  TRENTRY(MAX3421E_TRACE1_CHANALLOC_FAIL, TR_FMT1,
          "EPALLOC: Channel allocation failed: %u\n"),
  TRENTRY(MAX3421E_TRACE1_CHANWAIT_FAIL, TR_FMT1,
          "OUT: Channel wait failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_DEVDISCONN1, TR_FMT1,
          "OUT: Disconnected during wait: %u\n"),
  TRENTRY(MAX3421E_TRACE1_DEVDISCONN2, TR_FMT1,
          "CTRL: Disconnected during SETUP phase: %u\n"),
  TRENTRY(MAX3421E_TRACE1_DEVDISCONN3, TR_FMT1,
          "CTRL OUT: Disconnected during DATA phase: %u\n"),
  TRENTRY(MAX3421E_TRACE1_DEVDISCONN4, TR_FMT1,
          "CTRL IN: Disconnected during DATA phase: %u"),
  TRENTRY(MAX3421E_TRACE1_DEVDISCONN5, TR_FMT1,
          "IN: Disconnected during wait: %u\n"),
  TRENTRY(MAX3421E_TRACE1_DEVDISCONN6, TR_FMT1,
          "CONNECT: Device disconnect #1: %u\n"),
  TRENTRY(MAX3421E_TRACE1_DEVDISCONN7, TR_FMT1,
          "CONNECT: Device disconnect #2: %u\n"),
  TRENTRY(MAX3421E_TRACE1_DEVDISCONN8, TR_FMT1,
          "CONNECT: Device disconnect #3: %u\n"),
  TRENTRY(MAX3421E_TRACE1_ENUMERATE_FAIL, TR_FMT1,
          "CONNECT: Enumeration failed: %u\n"),
  TRENTRY(MAX3421E_TRACE1_INSETUP_FAIL1, TR_FMT1,
          "CTRL IN: SETUP phase failure: %u\n"),
#ifdef CONFIG_USBHOST_ASYNCH
  TRENTRY(MAX3421E_TRACE1_INSETUP_FAIL2, TR_FMT1,
          "CTRL IN: Asynch SETUP phase failure #1: %u\n"),
  TRENTRY(MAX3421E_TRACE1_INSETUP_FAIL3, TR_FMT1,
          "CTRL IN: Asynch SETUP phase failure #2: %u\n"),
#endif
  TRENTRY(MAX3421E_TRACE1_IRQATTACH_FAIL, TR_FMT1,
          "INIT: Failed to attach interrupt: %u\n"),
  TRENTRY(MAX3421E_TRACE1_OUTSETUP_FAIL1, TR_FMT1,
          "CTRL OUT: SETUP phase failure: %u\n"),
#ifdef CONFIG_USBHOST_ASYNCH
  TRENTRY(MAX3421E_TRACE1_OUTSETUP_FAIL2, TR_FMT1,
          "CTRL OUT: Asynch SETUP phase failure #1: %u\n"),
  TRENTRY(MAX3421E_TRACE1_OUTSETUP_FAIL3, TR_FMT1,
          "CTRL OUT: Asynch SETUP phase failure #2: %u\n"),
#endif
  TRENTRY(MAX3421E_TRACE1_RECVDATA_FAIL, TR_FMT1,
          "CTRL IN: Data phase failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_RECVSTATUS_FAIL, TR_FMT1,
          "CTRL OUT: Status phase failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_SENDDATA_FAIL, TR_FMT1,
          "CTRL OUT: Data phase failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_SENDSETUP_FAIL1, TR_FMT1,
          "CTRL OUT: SETUP phase failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_SENDSETUP_FAIL2, TR_FMT1,
          "CTRL IN: SETUP phase failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_SENDSTATUS_FAIL, TR_FMT1,
          "CTRL IN: Status phase failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_TRANSFER_FAILED1, TR_FMT1,
          "OUT: Transfer wait returned failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_TRANSFER_FAILED2, TR_FMT1,
          "CTRL: SETUP wait returned failure: %u\n"),
  TRENTRY(MAX3421E_TRACE1_TRANSFER_FAILED3, TR_FMT1,
          "IN: Transfer wait returned failure: %u\n"),

#ifdef HAVE_USBHOST_TRACE_VERBOSE
  TRENTRY(MAX3421E_VTRACE1_CANCEL, TR_FMT1,
          "Transfer canceled: EP%u\n"),
  TRENTRY(MAX3421E_VTRACE1_CONNECTED1, TR_FMT1,
          "CONNECT: Connection event: %u\n"),
  TRENTRY(MAX3421E_VTRACE1_CONNECTED2, TR_FMT1,
          "CONNECT: Connection change detected: %u\n"),
  TRENTRY(MAX3421E_VTRACE1_CONNECTED3, TR_FMT1,
          "CONNECT: Connected: %u\n"),
  TRENTRY(MAX3421E_VTRACE1_DISCONNECTED1, TR_FMT1,
          "CONNECT: Disconnected: %u\n"),
  TRENTRY(MAX3421E_VTRACE1_DISCONNECTED2, TR_FMT1,
          "CONNECT: Disconnect detected: %u\n"),
  TRENTRY(MAX3421E_VTRACE1_ENUMERATE, TR_FMT1,
          "ENUMERATE: Start: %u\n"),
#ifdef CONFIG_USBHOST_HUB
  TRENTRY(MAX3421E_VTRACE1_HUB_CONNECTED, TR_FMT1,
          "CONNECT: Hub connected: %u\n"),
#endif
  TRENTRY(MAX3421E_VTRACE1_INITIALIZED, TR_FMT1,
          "INIT: Hardware initialized: %u\n"),
#ifdef CONFIG_USBHOST_ASYNCH
  TRENTRY(MAX3421E_VTRACE1_TRANSFER_COMPLETE, TR_FMT1,
          "OUT: Asynch transfer complete: %u\n"),
#endif
#endif
};

static const struct max3421e_usbhost_trace_s g_trace2[TRACE2_NSTRINGS] =
{
#ifdef HAVE_USBHOST_TRACE_VERBOSE
#ifdef CONFIG_USBHOST_ASYNCH
  TRENTRY(MAX3421E_VTRACE2_ASYNCH, TR_FMT2,
          "ASYNCH: Transfer started: EP%u len=%u\n"),
#endif
  TRENTRY(MAX3421E_VTRACE2_BULKIN, TR_FMT2,
          "BULK IN:  SETUP: Chan%u len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_BULKOUT, TR_FMT2,
          "BULK OUT:  SETUP: Chan%u len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_CHANWAKEUP_IN, TR_FMT2,
          "IN: Channel wakeup: Chan%, result=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_CHANWAKEUP_OUT, TR_FMT2,
          "OUT: Channel wakeup: Chan%u result=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_CTRLIN, TR_FMT2,
          "CTRL IN: Start: type=%u req=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_CTRLOUT, TR_FMT2,
          "CTRL OUT: Start: type=%u req=%u\n"),
#ifdef CONFIG_USBHOST_HUB
  TRENTRY(MAX3421E_VTRACE2_HUB_CONNECTED, TR_FMT2,
          "CONNECT: Hub connected: port=%u, connected=%u\n"),
#endif
  TRENTRY(MAX3421E_VTRACE2_INTRIN, TR_FMT2,
          "INTR IN: SETUP: Chan%u len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_INTROUT, TR_FMT2,
          "INTR OUT: SETUP: Chan%u len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_ISOCIN, TR_FMT2,
          "ISOC IN: SETUP: Chan%u len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_ISOCOUT, TR_FMT2,
          "ISOC OUT: SETUP: Chan%u len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_RECVSTATUS, TR_FMT2,
          "CTRL OUT: Receive status: Chan% len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_SENDSTATUS, TR_FMT2,
          "CTRL IN: Send status: Chan% len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_STARTTRANSFER1, TR_FMT2,
          "OUT: Send start: Chan% len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_STARTTRANSFER2, TR_FMT2,
          "IN: Receive start: Chan% len=%u\n"),
  TRENTRY(MAX3421E_VTRACE2_TRANSFER, TR_FMT2,
          "Transfer start: EP%u len=%u\n"),
#ifdef CONFIG_USBHOST_ASYNCH
  TRENTRY(MAX3421E_VTRACE2_XFRCOMPLETE, TR_FMT2,
          "ASYNCH: Transfer complete: EP%u len=%u\n"),
#endif
#endif
};
#endif

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

/****************************************************************************
 * Name: max3421e_lock
 *
 * Description:
 *   Lock and configure the SPI bus.
 *
 ****************************************************************************/

static void max3421e_lock(FAR struct max3421e_usbhost_s *priv)
{
  FAR const struct max3421e_lowerhalf_s *lower = priv->lower;
  FAR struct spi_dev_s *spi;

  DEBUGASSERT(lower != NULL && lower->spi != NULL && !priv->locked);
  spi = lower->spi;

  SPI_LOCK(spi, true);
  SPI_SETMODE(spi, lower->mode);
  SPI_SETBITS(spi, 8);
  SPI_HWFEATURES(spi, 0);
  SPI_SETFREQUENCY(spi, lower->frequency);

#ifdef CONFIG_DEBUG_ASSERTIONS
  /* Mark the SPI bus as locked (for debug only) */

  priv->locked = true;
#endif
}

/****************************************************************************
 * Name: max3421e_unlock
 *
 * Description:
 *   Unlock the SPI bus.
 *
 ****************************************************************************/

static void max3421e_unlock(FAR struct max3421e_usbhost_s *priv)
{
  FAR const struct max3421e_lowerhalf_s *lower = priv->lower;

  DEBUGASSERT(lower != NULL && lower->spi != NULL && priv->locked);
  SPI_LOCK(lower->spi, false);

#ifdef CONFIG_DEBUG_ASSERTIONS
  /* Mark the SPI bus as unlocked (for debug only) */

  priv->locked = false;
#endif
}

/****************************************************************************
 * Name: max3421e_printreg
 *
 * Description:
 *   Print the contents of an MAX3421Exx register operation
 *
 ****************************************************************************/

#ifdef CONFIG_MAX3421E_USBHOST_REGDEBUG
static void max3421e_printreg(uint8_t addr, uint8_t val, bool iswrite)
{
  uinfo("%02x%s%02x\n", addr, iswrite ? "<-" : "->", val);
}
#endif

/****************************************************************************
 * Name: max3421e_checkreg
 *
 * Description:
 *   Get the contents of an MAX3421E register
 *
 ****************************************************************************/

#ifdef CONFIG_MAX3421E_USBHOST_REGDEBUG
static void max3421e_checkreg(uint8_t addr, uint8_t val, bool iswrite)
{
  static uint8_t prevaddr = 0;
  static uint8_t preval = 0;
  static unsigned int count = 0;
  static bool prevwrite = false;

  /* Is this the same value that we read from/wrote to the same register
   * last time?  Are we polling the register?  If so, suppress the output.
   */

  if (addr == prevaddr && val == preval && prevwrite == iswrite)
    {
      /* Yes.. Just increment the count */

      count++;
    }
  else
    {
      /* No this is a new address or value or operation. Were there any
       * duplicate accesses before this one?
       */

      if (count > 0)
        {
          /* Yes.. Just one? */

          if (count == 1)
            {
              /* Yes.. Just one */

              max3421e_printreg(prevaddr, preval, prevwrite);
            }
          else
            {
              /* No.. More than one. */

              uinfo("[repeats %d more times]\n", count);
            }
        }

      /* Save the new address, value, count, and operation for next time */

      prevaddr  = addr;
      preval    = val;
      count     = 0;
      prevwrite = iswrite;

      /* Show the new register access */

      max3421e_printreg(addr, val, iswrite);
    }
}
#endif

/****************************************************************************
 * Name: max3421e_fmtcmd
 *
 * Description:
 *   Format a command
 *
 *   The command byte contains the register address, a direction bit, and an
 *   ACKSTAT bit:
 *
 *     Bits 3-7:  Command
 *     Bit 2:     Unused
 *     Bit 1:     Direction (read = 0, write = 1)
 *     Bit 0:     ACKSTAT
 *
 *   The ACKSTAT bit is ignored in host mode.
 *
 ****************************************************************************/

static inline uint8_t max3421e_fmtcmd(FAR struct max3421e_usbhost_s *priv,
                                      uint8_t addr, uint8_t dir)
{
  return addr | dir | MAX3421E_ACKSTAT_FALSE;
}

/****************************************************************************
 * Name: max3421e_getreg
 *
 * Description:
 *   Get the contents of an MAX3421E register
 *
 * Assumption:
 *   SPI bus is locked
 *
 ****************************************************************************/

static uint32_t max3421e_getreg(FAR struct max3421e_usbhost_s *priv,
                                uint8_t addr)
{
  FAR const struct max3421e_lowerhalf_s *lower = priv->lower;
  FAR struct spi_dev_s *spi;
  uint8_t cmd;
  uint8_t value;

  DEBUGASSERT(lower != NULL && lower->spi != NULL && priv->locked);
  spi = lower->spi;

  /* Select the MAX4321E */

  SPI_SELECT(spi, SPIDEV_USBHOST(lower->devid), true);

  /* Send the read command byte */

  cmd = max3421e_fmtcmd(priv, addr, MAX3421E_DIR_READ);
  SPI_SEND(spi, cmd);

  /* Read the value of the register */

  value = SPI_SEND(spi, 0xff);

  /* De-select the MAX4321E */

  SPI_SELECT(spi, SPIDEV_USBHOST(lower->devid), false);

#ifdef CONFIG_MAX3421E_USBHOST_REGDEBUG
  /* Check if we need to print this value */

  max3421e_checkreg(addr, value, false);
#endif

  return value;
}

/****************************************************************************
 * Name: max3421e_putreg
 *
 * Description:
 *   Set the contents of an MAX3421E register to a value
 *
 * Assumption:
 *   SPI bus is locked
 *
 ****************************************************************************/

static void max3421e_putreg(FAR struct max3421e_usbhost_s *priv,
                            uint8_t addr, uint8_t value)
{
  FAR const struct max3421e_lowerhalf_s *lower = priv->lower;
  FAR struct spi_dev_s *spi;
  uint8_t cmd;

  DEBUGASSERT(lower != NULL && lower->spi != NULL && priv->locked);
  spi = lower->spi;

#ifdef CONFIG_MAX3421E_USBHOST_REGDEBUG
  /* Check if we need to print this value */

  max3421e_checkreg(addr, val, true);
#endif

  /* Select the MAX4321E */

  SPI_SELECT(spi, SPIDEV_USBHOST(lower->devid), true);

  /* Send the write command byte */

  cmd = max3421e_fmtcmd(priv, addr, MAX3421E_DIR_WRITE);
  SPI_SEND(spi, cmd);

  /* Send the new value for the register */

  SPI_SEND(spi, value);

  /* De-select the MAX4321E */

  SPI_SELECT(spi, SPIDEV_USBHOST(lower->devid), false);
}

/****************************************************************************
 * Name: max3421e_modifyreg
 *
 * Description:
 *   Modify selected bits of an MAX3421E register.
 *
 * Assumption:
 *   SPI bus is locked
 *
 ****************************************************************************/

static inline void max3421e_modifyreg(FAR struct max3421e_usbhost_s *priv,
                                      uint8_t addr, uint8_t clrbits,
                                      uint8_t setbits)
{
  uint8_t value;

  value  = max3421e_getreg(priv, addr);
  value &= ~clrbits;
  value |= setbits;
  max3421e_putreg(priv, addr, value);
}

/****************************************************************************
 * Name: max3421e_recvblock
 *
 * Description:
 *   Receive a block of data from the MAX341E.
 *
 * Assumption:
 *   SPI bus is locked
 *
 ****************************************************************************/

static void max3421e_recvblock(FAR struct max3421e_usbhost_s *priv,
                               uint8_t addr, FAR void *buffer, size_t buflen)
{
  FAR const struct max3421e_lowerhalf_s *lower = priv->lower;
  FAR struct spi_dev_s *spi;
  uint8_t cmd;

  DEBUGASSERT(lower != NULL && lower->spi != NULL && priv->locked);
  spi = lower->spi;

  /* Select the MAX4321E */

  SPI_SELECT(spi, SPIDEV_USBHOST(lower->devid), true);

  /* Send the read command byte */

  cmd = max3421e_fmtcmd(priv, addr, MAX3421E_DIR_READ);
  SPI_SEND(spi, cmd);

  /* Read the block of values from the register(s) */

  SPI_RECVBLOCK(spi, buffer, buflen);

  /* De-select the MAX4321E */

  SPI_SELECT(spi, SPIDEV_USBHOST(lower->devid), false);

#ifdef CONFIG_MAX3421E_USBHOST_REGDEBUG
  /* Dump the block of data received */

  lib_dumpbuffer("Received:", buffer, buflen);
#endif
}

/****************************************************************************
 * Name: max3421e_sndblock
 *
 * Description:
 *   Send a block of data to the MAX341E.
 *
 * Assumption:
 *   SPI bus is locked
 *
 ****************************************************************************/

static void max3421e_sndblock(FAR struct max3421e_usbhost_s *priv,
                              uint8_t addr, FAR const void *buffer,
                              size_t buflen)
{
  FAR const struct max3421e_lowerhalf_s *lower = priv->lower;
  FAR struct spi_dev_s *spi;
  uint8_t cmd;

  DEBUGASSERT(lower != NULL && lower->spi != NULL && priv->locked);
  spi = lower->spi;

#ifdef CONFIG_MAX3421E_USBHOST_REGDEBUG
  /* Dump the block of data to be sent */

  lib_dumpbuffer("Sending:", buffer, buflen);
#endif

  /* Select the MAX4321E */

  SPI_SELECT(spi, SPIDEV_USBHOST(lower->devid), true);

  /* Send the wrte command byte */

  cmd = max3421e_fmtcmd(priv, addr, MAX3421E_DIR_WRITE);
  SPI_SEND(spi, cmd);

  /* Send the new value for the register */

  SPI_SNDBLOCK(spi, buffer, buflen);

  /* De-select the MAX4321E */

  SPI_SELECT(spi, SPIDEV_USBHOST(lower->devid), false);
}

/****************************************************************************
 * Name: max3421e_getle16
 *
 * Description:
 *   Get a (possibly unaligned) 16-bit little endian value.
 *
 ****************************************************************************/

static inline uint16_t max3421e_getle16(const uint8_t *val)
{
  return (uint16_t)val[1] << 8 | (uint16_t)val[0];
}

/****************************************************************************
 * Name: max3421e_chan_alloc
 *
 * Description:
 *   Allocate a channel.
 *
 ****************************************************************************/

static int max3421e_chan_alloc(FAR struct max3421e_usbhost_s *priv)
{
  int chidx;

  /* Search the table of channels */

  for (chidx = 0; chidx < MAX3421E_NHOST_CHANNELS; chidx++)
    {
      /* Is this channel available? */

      if (!priv->chan[chidx].inuse)
        {
          /* Yes... make it "in use" and return the index */

          priv->chan[chidx].inuse = true;
          return chidx;
        }
    }

  /* All of the channels are "in-use" */

  return -EBUSY;
}

/****************************************************************************
 * Name: max3421e_chan_free
 *
 * Description:
 *   Free a previoiusly allocated channel.
 *
 ****************************************************************************/

static void max3421e_chan_free(FAR struct max3421e_usbhost_s *priv,
                               int chidx)
{
  DEBUGASSERT((unsigned)chidx < MAX3421E_NHOST_CHANNELS);

  /* Halt the channel */
#warning Missing logic

  /* Mark the channel available */

  priv->chan[chidx].inuse = false;
}

/****************************************************************************
 * Name: max3421e_chan_waitsetup
 *
 * Description:
 *   Set the request for the transfer complete event well BEFORE enabling
 *   the transfer (as soon as we are absolutely committed to the transfer).
 *   We do this to minimize race conditions.  This logic would have to be
 *   expanded if we want to have more than one packet in flight at a time!
 *
 * Assumptions:
 *   Called from a normal thread context BEFORE the transfer has been
 *   started.
 *
 ****************************************************************************/

static int max3421e_chan_waitsetup(FAR struct max3421e_usbhost_s *priv,
                                   FAR struct max3421e_chan_s *chan)
{
  irqstate_t flags = enter_critical_section();
  int        ret   = -ENODEV;

  DEBUGASSERT(priv != NULL && chan != NULL && priv->waiter == NULL);

  /* Is the device still connected? */

  if (priv->connected)
    {
      /* Yes.. then set waiter to indicate that we expect to be informed
       * when either (1) the device is disconnected, or (2) the transfer
       * completed.
       */

      priv->waiter   = chan;
#ifdef CONFIG_USBHOST_ASYNCH
      priv->callback = NULL;
      priv->arg      = NULL;
#endif
      ret            = OK;
    }

  leave_critical_section(flags);
  return ret;
}

/****************************************************************************
 * Name: max3421e_chan_asynchsetup
 *
 * Description:
 *   Set the request for the transfer complete event well BEFORE enabling the
 *   transfer (as soon as we are absolutely committed to transfer).  We do
 *   this to minimize race conditions.  This logic would have to be expanded
 *   if we want to have more than one packet in flight at a time!
 *
 * Assumptions:
 *   Might be called from the level of an interrupt handler
 *
 ****************************************************************************/

#ifdef CONFIG_USBHOST_ASYNCH
static int max3421e_chan_asynchsetup(FAR struct max3421e_usbhost_s *priv,
                                  FAR struct max3421e_chan_s *chan,
                                  usbhost_asynch_t callback, FAR void *arg)
{
  irqstate_t flags = enter_critical_section();
  int        ret   = -ENODEV;

  DEBUGASSERT(priv != NULL && chan != NULL);

  /* Is the device still connected? */

  if (priv->connected)
    {
      priv->waiter   = NULL;      /* No waiter */
      priv->callback = callback;
      priv->arg      = arg;
      ret            = OK;
    }

  leave_critical_section(flags);
  return ret;
}
#endif

/****************************************************************************
 * Name: max3421e_chan_wait
 *
 * Description:
 *   Wait for a transfer on a channel to complete.
 *
 * Assumptions:
 *   Called from a normal thread context
 *
 ****************************************************************************/

static int max3421e_chan_wait(FAR struct max3421e_usbhost_s *priv,
                           FAR struct max3421e_chan_s *chan)
{
  irqstate_t flags;
  int ret;

  /* Disable interrupts so that the following operations will be atomic.  On
   * the host global interrupt needs to be disabled.  However, here we
   * disable all interrupts to exploit that fact that interrupts will be re-
   * enabled while we wait.
   */

  flags = enter_critical_section();

  /* Loop, testing for an end of transfer condition.  The channel 'result'
   * was set to EBUSY and 'waiter' was set to the channel expecting the
   * response before the transfer was started; 'waiter' will be nullified
   * and 'result' will be set appropriately when the transfer is completed.
   */

  do
    {
      /* Wait for the transfer to complete.  NOTE the transfer may already
       * completed before we get here or the transfer may complete while we
       * wait here.
       */

      ret = nxsem_wait_uninterruptible(&priv->waitsem);
      if (ret < 0)
        {
          leave_critical_section(flags);
          return ret;
        }
    }
  while (priv->waiter != NULL);

  /* The transfer is complete re-enable interrupts and return the result */

  ret = -(int)priv->result;
  leave_critical_section(flags);
  return ret;
}

/****************************************************************************
 * Name: max3421e_chan_wakeup
 *
 * Description:
 *   A channel transfer has completed... wakeup any threads waiting for the
 *   transfer to complete.
 *
 * Assumptions:
 *   This function is called from the transfer complete interrupt handler for
 *   the channel.  Interrupts are disabled.
 *
 ****************************************************************************/

static void max3421e_chan_wakeup(FAR struct max3421e_usbhost_s *priv,
                                 FAR struct max3421e_chan_s *chan,
                                 int result)
{
  /* Save the result of the operation */

  DEBUGASSERT(priv->result != EBUSY);
  priv->result = result;

  /* Is there a thread waiting for this transfer to complete? */

  if (priv->waiter != NULL)
    {
#ifdef CONFIG_USBHOST_ASYNCH
      /* Yes.. there should not also be a callback scheduled */

      DEBUGASSERT(priv->callback == NULL);
#endif
      /* Wake'em up! */

      usbhost_vtrace2(chan->in ? MAX3421E_VTRACE2_CHANWAKEUP_IN :
                                 MAX3421E_VTRACE2_CHANWAKEUP_OUT,
                      chan->chidx, priv->result);

      nxsem_post(&priv->waitsem);
      priv->waiter = NULL;
    }

#ifdef CONFIG_USBHOST_ASYNCH
  /* No.. is an asynchronous callback expected when the transfer completes? */

  else if (priv->callback)
    {
      /* Handle continuation of IN/OUT pipes */

      if (chan->in)
        {
          max3421e_in_next(priv, chan);
        }
      else
        {
          max3421e_out_next(priv, chan);
        }
    }
#endif
}

/****************************************************************************
 * Name: max3421e_save_toggles and max3421e_restore_toggles
 *
 * Description:
 *   Save and restore data toggles from/to the HCTL register.  The MAX3421E
 *   will automatically update the toggles for consecutive transfers to the
 *   same endpoint; We need to use these only when we change endpoints.
 *
 ****************************************************************************/

static inline void
  max3421e_save_toggles(FAR struct max3421e_usbhost_s *priv,
                       FAR struct max3421e_chan_s *chan)
{
  chan->toggles = max3421e_getreg(priv, MAX3421E_USBHOST_HCTL);
}

static inline void
  max3421e_restore_toggles(FAR struct max3421e_usbhost_s *priv,
                           FAR struct max3421e_chan_s *chan)
{
  max3421e_modifyreg(priv, MAX3421E_USBHOST_HCTL,
                     USBHOST_HCTL_TOGGLES_MASK,
                     chan->toggles & USBHOST_HCTL_TOGGLES_MASK);
}

/****************************************************************************
 * Name: max3421e_transfer_status
 *
 * Description:
 *   Get the end-of-transfer status from HRSLT register.
 *
 *   REVISIT:   Currently NAKs are treated as errors.  A NAK on the first
 *   packet can probably be treated that way.  But not NAKs after the
 *   transfer is in progress.  We should also need to reset the peripheral
 *   in that case.  Better to try and retry here within the driver.
 *
 * Returned value:
 *   OK     - Transfer successful
 *  -EAGAIN - If devices NAKs the transfer.
 *  -EPERM  - If the endpoint stalls
 *  -BUSY   - The transfer is not complete
 *  -EIO    - Other, undecoded error
 *
 * Assumptions:
 *   The SPI bus is locked.
 *
 ****************************************************************************/

static int max3421e_transfer_status(FAR struct max3421e_usbhost_s *priv)
{
  uint8_t regval;
  int ret;

  /* Get the result of the transfer from the HRSLT register */

  regval = max3421e_getreg(priv, MAX3421E_USBHOST_HRSL);

  /* Make the error result to something that the world knows about */

  switch (regval & USBHOST_HRSL_HRSLT_MASK)
    {
      case USBHOST_HRSL_HRSLT_SUCCESS:
        ret = OK;
        break;

      case USBHOST_HRSL_HRSLT_BUSY:
        ret = -EBUSY;
        break;

      case USBHOST_HRSL_HRSLT_NAK:
        ret = -EAGAIN;
        break;

      case USBHOST_HRSL_HRSLT_STALL:
        ret = -EPERM;
        break;

      default:
        ret = -EIO;
        break;
    }

  return ret;
}

/****************************************************************************
 * Name: max3421e_transfer_terminate
 *
 * Description:
 *   Terminate a IN or OUT transfer due to an error (or because a zero-
 *   length OUT transfer occurred).
 *
 * Returned value:
 *   OK     - Transfer successful
 *  -EAGAIN - If devices NAKs the transfer.
 *  -EPERM  - If the endpoint stalls
 *  -BUSY   - The transfer is not complete
 *  -EIO    - Other, undecoded error
 *
 * Assumptions:
 *   The SPI bus is locked.
 *
 ****************************************************************************/

static void max3421e_transfer_terminate(FAR struct max3421e_usbhost_s *priv,
                                        FAR struct max3421e_chan_s *chan,
                                        int result)
{
  /* Disable further SNDBAV, RCVDAV or HXFRDN interrupts */

  max3421e_int_disable(priv, USBHOST_HIRQ_SNDBAVIRQ |
                             USBHOST_HIRQ_RCVDAVIRQ |
                             USBHOST_HIRQ_HXFRDNIRQ);

  /* Save the endpoint toggle settings.
   *
   * REVISIT:  The MAX4321E sends fixed DATA0 and DATA1 PID tokens for the
   * various stages of a CONTROL transfer, regardless of the setting of
   * the internal data toggle.
   */

  max3421e_save_toggles(priv, chan);

  /* Wake up any waiters for the end of transfer event */

  DEBUGASSERT(priv->waiter != NULL);
  max3421e_chan_wakeup(priv, chan, -result);
}

/****************************************************************************
 * Name: max3421e_put_sndfifo
 *
 * Description:
 *   Copy data from the user-provided buffer into the SNDFIFO.
 *
 * Assumptions:
 *   The SPI bus is locked.
 *   There is data to be remaining to be sent.
 *   The caller has already set up for the wait event.
 *
 ****************************************************************************/

static void max3421e_put_sndfifo(FAR struct max3421e_usbhost_s *priv,
                                 FAR struct max3421e_chan_s *chan)
{
  uint16_t committed;
  uint16_t wrsize;
  uint8_t maxpacket;
  int i;

  DEBUGASSERT(priv != NULL && chan != NULL);

  /* The SNDFIFO is double buffered.  We may load up to MAX3421E_SNDFIFO_SIZE
   * into one buffer.  After loading the SNDFIFO buffer, the write the SNDBC
   * (Send Byte Count) register with the number of bytes loaded.  The
   * MAX3421E will clear SNDBAVIRQ (Send Buffer Available IRQ) and commit
   * the FIFO to USB transmission.
   *
   * If the other buffer is available when SNDBC is written, the MAX3421E
   * will clear SNDBAVIRQ then immediately set it to indicate
   * availability of the second buffer.
   *
   * The CPU should load the SNDFIFO only when a SNDBAVIRQ = 1.
   */

  maxpacket = chan->maxpacket;
  DEBUGASSERT(maxpacket <= MAX3421E_SNDFIFO_SIZE);

  committed = priv->xfrd + (uint16_t)priv->inflight;
  DEBUGASSERT(committed < priv->buflen);

  for (i = 0;
       i < 2 && committed < priv->buflen &&
       (max3421e_int_status(priv) & USBHOST_HIRQ_SNDBAVIRQ) != 0;
       i++)
    {
      /* Get the size of the biggest thing that we can put in the current
       * SNDFIFO buffer.
       */

      wrsize  = priv->buflen - committed;
      if (wrsize > maxpacket)
        {
          wrsize = maxpacket;
        }

      /* Write packet into the SNDFIFO. */

      max3421e_sndblock(priv, MAX3421E_USBHOST_SNDFIFO,
                        priv->buffer + committed, wrsize);

      /* Write the byte count to the SNDBC register */

      max3421e_putreg(priv, MAX3421E_USBHOST_SNDBC, wrsize);

      /* Send the OUT token */

      max3421e_putreg(priv, MAX3421E_USBHOST_HXFR,
                      USBHOST_HXFR_TOKEN_OUT | chan->epno);

      /* Increment the count of bytes "in-flight" in the SNDFIFO */

      priv->inflight += wrsize;
      committed       = priv->xfrd + wrsize;
    }

  /* Enable the SNDFIFO interrupt to handle the completion/continuation
   * of transfer.  Enable the HXFRDNIRQ to catch NAKs and transfer errors.
   */

  priv->xfrtype = HXFRDN_SNDFIFO;
  max3421e_int_enable(priv, USBHOST_HIRQ_SNDBAVIRQ | USBHOST_HIRQ_HXFRDNIRQ);
}

/****************************************************************************
 * Name: max3421e_send_continue
 *
 * Description:
 *   Continue the send operation started by max3421e_send_start().  If
 *   max3421e_put_sndfifo() was unable to load the entire outgoing buffer
 *   into the SNDFIFO, it enabled SNDBAV interrupt.
 *
 *   When the SNDBAV interrupt occurred, max3421e_irqwork() disabled that
 *   interrupt and called this function in order to continue that long send
 *   operations.
 *
 * Assumptions:
 *   The SPI bus is locked.
 *   The SNDBAV interrupt has been disabled.
 *
 ****************************************************************************/

static void max3421e_send_continue(FAR struct max3421e_usbhost_s *priv)
{
  FAR struct max3421e_chan_s *chan;
  int result;
  uint8_t xfrd;

  DEBUGASSERT(priv != NULL && priv->waiter != NULL);
  chan = priv->waiter;

  /* Check the result of a transfer */

  result = max3421e_transfer_status(priv);
  if (result < 0)
    {
      /* Terminate the transfer on any error. */

      max3421e_transfer_terminate(priv, chan, result);
      return;
    }

  /* Update the number of bytes transferred.  We have to be a little clever
   * here:  We do not keep track of the number of bytes sent in each of the
   * SNDFIFO buffers, rather only the outstanding number of buffered
   * transferred, 'inflight'.  However, we know that all transfers will be
   * the max packet size (other than the last one perhaps).
   */

  /* If the number inflight is strictly greater than the maxpacket size, then
   * we can infer that the transfer that just completed was maxpacket size.
   */

  if (priv->inflight > chan->maxpacket)
    {
      xfrd = chan->maxpacket;
    }

  /* If the number inflight is less than chan->maxpacket, then that must have
   * been the last packet of the transfer.
   */

  else if (priv->inflight < chan->maxpacket)
    {
      xfrd = priv->inflight;
      DEBUGASSERT((priv->xfrd + xfrd) == priv->buflen);
    }

  /* If the number inflight is exactly the maxpacket size and the transfer
   * is not yet finished, then the the transfer size must have been max
   * packet size.
   */

  else if ((priv->xfrd + chan->maxpacket) >= priv->buflen)
    {
      xfrd = priv->buflen - priv->xfrd;
      DEBUGASSERT((priv->xfrd + xfrd) == priv->buflen);
    }

  /* Otherwise, the transfer must have been the max packet size */

  else
    {
      xfrd = chan->maxpacket;
    }

  priv->xfrd     += xfrd;
  priv->inflight -= xfrd;

  /* Check for the end of transfer */

  if (priv->xfrd >= priv->buflen)
    {
      /* Successful end-of-transfer */

      max3421e_transfer_terminate(priv, chan, OK);
    }
  else
    {
      /* No.. there are more bytes to be sent */

      max3421e_put_sndfifo(priv, chan);
    }
}

/****************************************************************************
 * Name: max3421e_send_start
 *
 * Description:
 *   Start at transfer on the selected IN or OUT channel.
 *
 ****************************************************************************/

static void max3421e_send_start(FAR struct max3421e_usbhost_s *priv,
                                FAR struct max3421e_chan_s *chan)
{
  max3421e_pktdump("Sending", priv->buffer, priv->buflen);

  /* Set up the initial state of the transfer */

  usbhost_vtrace2(MAX3421E_VTRACE2_STARTTRANSFER1,
                  chan->chidx, priv->buflen);

  priv->result   = EBUSY;
  priv->inflight = 0;
  priv->xfrd     = 0;

  /* Make sure the peripheral address is correct */

  max3421e_putreg(priv, MAX3421E_USBHOST_PERADDR, chan->funcaddr);

  /* Checkout for zero length packet */

  if (priv->buflen > 0)
    {
      /* No.. we need to copy the outgoing data into SNDFIFO. */

      max3421e_restore_toggles(priv, chan);

      /* Then load the data into the SNDFIFO and start the transfer */

      max3421e_put_sndfifo(priv, chan);
    }
  else
    {
      /* Write the zero byte count to the SNDBC register */

      max3421e_putreg(priv, MAX3421E_USBHOST_SNDBC, 0);

      /* Send the OUT token */

      max3421e_putreg(priv, MAX3421E_USBHOST_HXFR,
                      USBHOST_HXFR_TOKEN_OUT | chan->epno);

      /* Enable the HXFRDNIRQ to catch completion of the ZLP. */

      priv->xfrtype = HXFRDN_SNDZLP;
      max3421e_int_enable(priv, USBHOST_HIRQ_HXFRDNIRQ);
    }
}

/****************************************************************************
 * Name: max3421e_out_transfer
 *
 * Description:
 *   Transfer the 'buflen' bytes in 'buffer' through an OUT channel.
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the TRANSFER
 *   interface and must manage the SPI lock itself.  The lock, for example,
 *   must be relinquished before waiting.
 *
 ****************************************************************************/

static ssize_t max3421e_out_transfer(FAR struct max3421e_usbhost_s *priv,
                                     FAR struct max3421e_chan_s *chan,
                                     FAR uint8_t *buffer, size_t buflen)
{
  clock_t start;
  clock_t elapsed;
  size_t xfrlen;
  ssize_t xfrd;
  int ret;

  /* Loop until the transfer completes (i.e., buflen is decremented to zero)
   * or a fatal error occurs (any error other than a simple NAK)
   */

  start = clock_systime_ticks();
  xfrd  = 0;

  while (buflen > 0)
    {
      /* Transfer one packet at a time.  The hardware is capable of queueing
       * multiple OUT packets, but I just haven't figured out how to handle
       * the case where a single OUT packet in the group is NAKed.
       */

      xfrlen       = MIN(chan->maxpacket, buflen);
      priv->buffer = buffer;
      priv->buflen = xfrlen;
      priv->xfrd   = 0;

      /* Set up for the wait BEFORE starting the transfer */

      ret = max3421e_chan_waitsetup(priv, chan);
      if (ret < 0)
        {
          usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN1, 0);
          return (ssize_t)ret;
        }

      /* Set up for the transfer based on the direction and the endpoint */

      max3421e_lock(priv);
      ret = max3421e_out_setup(priv, chan);
      max3421e_unlock(priv);

      if (ret < 0)
        {
          usbhost_trace1(MAX3421E_TRACE1_OUTSETUP_FAIL1, -ret);
          return (ssize_t)ret;
        }

      /* Wait for the transfer to complete and get the result */

      ret = max3421e_chan_wait(priv, chan);

      /* Handle transfer failures */

      if (ret < 0)
        {
          usbhost_trace1(MAX3421E_TRACE1_TRANSFER_FAILED1, ret);

          /* Check for a special case:  If (1) the transfer was NAKed and (2)
           * no SNDFIFO empty or Rx FIFO not-empty event occurred, then we
           * should be able to just flush the Rx and SNDFIFOs and try again.
           * We can detect this latter case because then the transfer buffer
           * pointer and buffer size will be unaltered.
           */

          elapsed = clock_systime_ticks() - start;
          if (ret != -EAGAIN ||                     /* Not a NAK condition OR */
              elapsed >= MAX3421E_DATANAK_DELAY ||  /* Timeout has elapsed OR */
              priv->xfrd > 0)                       /* Data has been partially transferred */
            {
              /* Break out and return the error */

              usbhost_trace1(MAX3421E_TRACE1_CHANWAIT_FAIL, -ret);
              return (ssize_t)ret;
            }

          /* Get the device a little time to catch up.  Then retry the
           * transfer using the same buffer pointer and length.
           */

          nxsig_usleep(20 * 1000);
        }
      else
        {
          /* Successfully transferred. Update the buffer pointer/length */

          buffer += xfrlen;
          buflen -= xfrlen;
          xfrd   += priv->xfrd;
        }
    }

  return xfrd;
}

/****************************************************************************
 * Name: max3421e_out_next
 *
 * Description:
 *   Initiate the next of a sequence of asynchronous transfers.
 *
 * Assumptions:
 *   This function is always called from an interrupt handler
 *
 ****************************************************************************/

#ifdef CONFIG_USBHOST_ASYNCH
static void max3421e_out_next(FAR struct max3421e_usbhost_s *priv,
                           FAR struct max3421e_chan_s *chan)
{
  usbhost_asynch_t callback;
  FAR void *arg;
  ssize_t nbytes;
  int result;
  int ret;

  /* Is the full transfer complete? Did the last chunk transfer OK? */

  result = -(int)priv->result;
  if (priv->xfrd < priv->buflen && result == OK)
    {
      /* Yes.. Set up for the next transfer based on the direction and the
       * endpoint type
       */

      ret = max3421e_out_setup(priv, chan);
      if (ret >= 0)
        {
          return;
        }

      usbhost_trace1(MAX3421E_TRACE1_OUTSETUP_FAIL2, -ret);
      result = ret;
    }

  /* The transfer is complete, with or without an error */

  usbhost_vtrace1(MAX3421E_VTRACE1_TRANSFER_COMPLETE, result);

  /* Extract the callback information */

  callback       = priv->callback;
  arg            = priv->arg;
  nbytes         = priv->xfrd;

  priv->callback = NULL;
  priv->arg      = NULL;
  priv->xfrd     = 0;

  /* Then perform the callback */

  if (result < 0)
    {
      nbytes = (ssize_t)result;
    }

  callback(arg, nbytes);
}
#endif

/****************************************************************************
 * Name: max3421e_out_asynch
 *
 * Description:
 *   Initiate the first of a sequence of asynchronous transfers.
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the ASYNCH
 *   interface and must manage the SPI lock itself.  The lock, for example,
 *   must be relinquished before waiting.
 *
 ****************************************************************************/

#ifdef CONFIG_USBHOST_ASYNCH
static int max3421e_out_asynch(FAR struct max3421e_usbhost_s *priv,
                               FAR struct max3421e_chan_s *chan,
                               FAR uint8_t *buffer, size_t buflen,
                               usbhost_asynch_t callback, FAR void *arg)
{
  int ret;

  /* Set up for the transfer BEFORE starting the first transfer */

  priv->buffer = buffer;
  priv->buflen = buflen;
  priv->xfrd   = 0;

  ret = max3421e_chan_asynchsetup(priv, chan, callback, arg);
  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_ASYNCHSETUP_FAIL1, -ret);
      return ret;
    }

  /* Set up for the transfer based on the direction and the endpoint type */

  max3421e_lock(priv);
  ret = max3421e_out_setup(priv, chan);
  max3421e_unlock(priv);

  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_OUTSETUP_FAIL3, -ret);
    }

  /* And return with the transfer pending */

  return ret;
}
#endif

/****************************************************************************
 * Name: max3421e_ctrl_sendsetup
 *
 * Description:
 *   Send an IN/OUT SETUP packet.
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the CTRLIN and
 *   CTRLOUT interfaces and must manage the SPI lock itself.  The lock, for
 *   example, must be relinquished before waiting.
 *
 ****************************************************************************/

static int max3421e_ctrl_sendsetup(FAR struct max3421e_usbhost_s *priv,
                                   FAR struct max3421e_chan_s *chan,
                                   FAR const struct usb_ctrlreq_s *req)
{
  clock_t start;
  clock_t elapsed;
  int ret;

  /* Loop while the device reports NAK (and a timeout is not exceeded */

  start = clock_systime_ticks();
  do
    {
      /* Send the  SETUP packet */

      priv->buffer   = (FAR uint8_t *)req;
      priv->buflen   = USB_SIZEOF_CTRLREQ;
      priv->inflight = 0;
      priv->xfrd     = 0;
      priv->result   = EBUSY;

      /* Set up for the wait BEFORE starting the transfer */

      ret = max3421e_chan_waitsetup(priv, chan);
      if (ret < 0)
        {
          usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN2, 0);
          return ret;
        }

      /* Make sure the peripheral address is correct */

      max3421e_lock(priv);
      max3421e_putreg(priv, MAX3421E_USBHOST_PERADDR, chan->funcaddr);

      /* Write packet into the SUDFIFO. */

      max3421e_sndblock(priv, MAX3421E_USBHOST_SUDFIFO, priv->buffer,
                        priv->buflen);

      /* Send the SETUP token (always EP0) */

      max3421e_putreg(priv, MAX3421E_USBHOST_HXFR,
                      USBHOST_HXFR_TOKEN_SETUP);

      /* Increment the count of bytes "in-flight" in the SNDFIFO */

      priv->inflight = priv->buflen;

      /* The MAX3421E waits 18 bit times for the device to respond or time
       * out then terminates the transfer by asserting the HXFRDNIRQ and
       * updating the HSRLT bits.  NOTE:  The USB spec says that a
       * peripheral must always ACK a SETUP packet.
       */

      priv->xfrtype = HXFRDN_SETUP;
      max3421e_int_enable(priv, USBHOST_HIRQ_HXFRDNIRQ);

      /* Wait for the transfer to complete */

      max3421e_unlock(priv);
      ret = max3421e_chan_wait(priv, chan);

      /* Return on success and for all failures other than EAGAIN.  EAGAIN
       * means that the device NAKed the SETUP command and that we should
       * try a few more times.  NOTE:  The USB spec says that a peripheral
       * must always ACK a SETUP packet.
       */

      if (ret != -EAGAIN)
        {
          /* Output some debug information if the transfer failed */

          if (ret < 0)
            {
              usbhost_trace1(MAX3421E_TRACE1_TRANSFER_FAILED2, ret);
            }

          /* Return the result in any event */

          return ret;
        }

      /* Get the elapsed time (in frames) */

      elapsed = clock_systime_ticks() - start;
    }
  while (elapsed < MAX3421E_SETUP_DELAY);

  return -ETIMEDOUT;
}

/****************************************************************************
 * Name: max3421e_ctrl_senddata
 *
 * Description:
 *   Send data in the data phase of an OUT control transfer.  Or send status
 *   in the status phase of an IN control transfer
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the CTRLOUT
 *   interface and must manage the SPI lock itself.  The lock, for example,
 *   must be relinquished before waiting.
 *
 ****************************************************************************/

static int max3421e_ctrl_senddata(FAR struct max3421e_usbhost_s *priv,
                                  FAR struct max3421e_chan_s *chan,
                                  FAR uint8_t *buffer, unsigned int buflen)
{
  int ret;

  /* Save buffer information */

  chan->in     = false;
  priv->xfrd   = 0;
  priv->buffer = buffer;
  priv->buflen = buflen;

  /* The MAX4321E sends fixed DATA0 and DATA1 PID tokens for the various
   * stages of a CONTROL transfer, regardless of the setting of the internal
   * data toggle.
   */

  /* Set up for the wait BEFORE starting the transfer */

  ret = max3421e_chan_waitsetup(priv, chan);
  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN3, 0);
      return ret;
    }

  /* Start the transfer */

  max3421e_lock(priv);
  max3421e_send_start(priv, chan);
  max3421e_unlock(priv);

  /* Wait for the transfer to complete and return the result */

  return max3421e_chan_wait(priv, chan);
}

/****************************************************************************
 * Name: max3421e_ctrl_recvdata
 *
 * Description:
 *   Receive data in the data phase of an IN control transfer.  Or receive
 *   status in the status phase of an OUT control transfer
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the CTRLIN
 *   interface and must manage the SPI lock itself.  The lock, for example,
 *   must be relinquished before waiting.
 *
 ****************************************************************************/

static int max3421e_ctrl_recvdata(FAR struct max3421e_usbhost_s *priv,
                                  FAR struct max3421e_chan_s *chan,
                                  FAR uint8_t *buffer, unsigned int buflen)
{
  int ret;

  /* Save buffer information */

  chan->in     = true;
  priv->buffer = buffer;
  priv->buflen = buflen;
  priv->xfrd   = 0;

  /* The MAX4321E sends fixed DATA0 and DATA1 PID tokens for the various
   * stages of a CONTROL transfer, regardless of the setting of the internal
   * data toggle.
   */

  /* Set up for the wait BEFORE starting the transfer */

  ret = max3421e_chan_waitsetup(priv, chan);
  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN4, 0);
      return ret;
    }

  /* Start the transfer */

  max3421e_lock(priv);
  max3421e_recv_start(priv, chan);
  max3421e_unlock(priv);

  /* Wait for the transfer to complete and return the result */

  return max3421e_chan_wait(priv, chan);
}

/****************************************************************************
 * Name: max3421e_ctrl_sendstatus
 *
 * Description:
 *   Send status to complete the status phase of a CTRLIN transfer.
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the CTRLIN
 *   interface and must manage the SPI lock itself.  The lock, for example,
 *   must be relinquished before waiting.
 *
 ****************************************************************************/

static int max3421e_ctrl_sendstatus(FAR struct max3421e_usbhost_s *priv,
                                    FAR struct max3421e_chan_s *chan)
{
  /* Set up the initial state of the transfer */

  usbhost_vtrace2(MAX3421E_VTRACE2_SENDSTATUS, chan->chidx, priv->buflen);

  priv->result   = EBUSY;
  priv->inflight = 0;
  priv->buflen   = 0;
  priv->xfrd     = 0;

  /* Make sure the peripheral address is correct */

  max3421e_lock(priv);
  max3421e_putreg(priv, MAX3421E_USBHOST_PERADDR, chan->funcaddr);

  /* Write the zero byte count to the SNDBC register */

  max3421e_putreg(priv, MAX3421E_USBHOST_SNDBC, 0);

  /* Send the HS-OUT token */

  max3421e_putreg(priv, MAX3421E_USBHOST_HXFR,
                  USBHOST_HXFR_TOKEN_OUTHS | chan->epno);

  /* Enable the HXFRDNIRQ to catch completion of the ZLP. */

  priv->xfrtype = HXFRDN_SNDZLP;
  max3421e_int_enable(priv, USBHOST_HIRQ_HXFRDNIRQ);
  max3421e_unlock(priv);

  /* Wait for the transfer to complete and return the result */

  return max3421e_chan_wait(priv, chan);
}

/****************************************************************************
 * Name: max3421e_ctrl_recvstatus
 *
 * Description:
 *   Receive status to complete the status phase of a CTRLOUT transfer.
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the CTRLOUT
 *   interface and must manage the SPI lock itself.  The lock, for example,
 *   must be relinquished before waiting.
 *
 ****************************************************************************/

static int max3421e_ctrl_recvstatus(FAR struct max3421e_usbhost_s *priv,
                                    FAR struct max3421e_chan_s *chan)
{
  max3421e_pktdump("Sending", priv->buffer, priv->buflen);

  /* Set up the initial state of the transfer */

  usbhost_vtrace2(MAX3421E_VTRACE2_RECVSTATUS, chan->chidx, priv->buflen);

  priv->result   = EBUSY;
  priv->inflight = 0;
  priv->buflen   = 0;
  priv->xfrd     = 0;

  /* Make sure the peripheral address is correct */

  max3421e_lock(priv);
  max3421e_putreg(priv, MAX3421E_USBHOST_PERADDR, chan->funcaddr);

  /* Send the HS-IN token. */

  max3421e_putreg(priv, MAX3421E_USBHOST_HXFR,
                  USBHOST_HXFR_TOKEN_INHS | chan->epno);

  /* Enable the RCVFIFO interrupt to handle the completion/continuation
   * of transfer.  Enable the HXFRDNIRQ to catch NAKs and transfer
   * errors.
   */

  priv->xfrtype = HXFRDN_RCVFIFO;
  max3421e_int_enable(priv, USBHOST_HIRQ_RCVDAVIRQ | USBHOST_HIRQ_HXFRDNIRQ);
  max3421e_unlock(priv);

  /* Wait for the transfer to complete and return the result */

  return max3421e_chan_wait(priv, chan);
}

/****************************************************************************
 * Name: max3421e_in_setup
 *
 * Description:
 *   Initiate an IN transfer on an bulk, interrupt, or isochronous pipe.
 *
 ****************************************************************************/

static int max3421e_in_setup(FAR struct max3421e_usbhost_s *priv,
                             FAR struct max3421e_chan_s *chan)
{
  /* Set up for the transfer based on the direction and the endpoint type */

  switch (chan->eptype)
    {
      default:
      case USB_EP_ATTR_XFER_CONTROL: /* Control */
        {
          /* This kind of transfer on control endpoints other than EP0 are
           * not currently supported
           */

          return -ENOSYS;
        }

      case USB_EP_ATTR_XFER_ISOC: /* Isochronous */
        {
          /* Set up the IN DATA0 PID */

          usbhost_vtrace2(MAX3421E_VTRACE2_ISOCIN,
                          chan->chidx, priv->buflen);

          chan->toggles = USBHOST_HCTL_RCVTOG0 | USBHOST_HCTL_SNDTOG0;
        }
        break;

      case USB_EP_ATTR_XFER_BULK: /* Bulk */
        {
          usbhost_vtrace2(MAX3421E_VTRACE2_BULKIN,
                          chan->chidx, priv->buflen);
        }
        break;

      case USB_EP_ATTR_XFER_INT: /* Interrupt */
        {
          usbhost_vtrace2(MAX3421E_VTRACE2_INTRIN,
                          chan->chidx, priv->buflen);
        }
        break;
    }

  /* Start the transfer. */

  max3421e_recv_start(priv, chan);
  return OK;
}

/****************************************************************************
 * Name: max3421e_out_setup
 *
 * Description:
 *   Initiate an OUT transfer on an bulk, interrupt, or isochronous pipe.
 *
 ****************************************************************************/

static int max3421e_out_setup(FAR struct max3421e_usbhost_s *priv,
                              FAR struct max3421e_chan_s *chan)
{
  /* Set up for the transfer based on the direction and the endpoint type */

  switch (chan->eptype)
    {
      default:
      case USB_EP_ATTR_XFER_CONTROL: /* Control */
        {
          /* This kind of transfer on control endpoints other than EP0 are
           * not currently supported
           */

          return -ENOSYS;
        }

      case USB_EP_ATTR_XFER_ISOC: /* Isochronous */
        {
          /* Set up the IN DATA0 PID */

          usbhost_vtrace2(MAX3421E_VTRACE2_ISOCOUT,
                          chan->chidx, priv->buflen);

          chan->toggles = USBHOST_HCTL_RCVTOG0 | USBHOST_HCTL_SNDTOG0;
        }
        break;

      case USB_EP_ATTR_XFER_BULK: /* Bulk */
        {
          usbhost_vtrace2(MAX3421E_VTRACE2_BULKOUT,
                          chan->chidx, priv->buflen);
        }
        break;

      case USB_EP_ATTR_XFER_INT: /* Interrupt */
        {
          usbhost_vtrace2(MAX3421E_VTRACE2_INTROUT,
                          chan->chidx, priv->buflen);
        }
        break;
    }

  /* Start the transfer */

  max3421e_send_start(priv, chan);
  return OK;
}

/****************************************************************************
 * Name: max3421e_get_rcvfifo
 *
 * Description:
 *   Copy data from the RCVFIFO to the user-provided buffer.
 *
 *   The RCVFIFO is double buffered.  If another packet is available in the
 *   other buffer, the MAX3421E will immediately re-assert RCVDAVIRQ and we
 *   will catch that by interrupt handling logic.
 *
 * Assumptions:
 *   The SPI bus is locked.
 *
 ****************************************************************************/

static uint8_t max3421e_get_rcvfifo(FAR struct max3421e_usbhost_s *priv,
                                    FAR struct max3421e_chan_s *chan)
{
  uint16_t remaining;
  uint8_t navail;
  uint8_t nrcvd;

  /* Get the number of bytes available in the RCVFIFO */

  navail = max3421e_getreg(priv, MAX3421E_USBHOST_RCVBC);

  /* Get the number that will fit into the user-provided buffer */

  remaining = priv->buflen - priv->xfrd;
  if (navail > remaining)
    {
      nrcvd = remaining;
    }
  else
    {
      nrcvd = navail;
    }

  /* Read the received data into the user-buffer. */

  max3421e_recvblock(priv, MAX3421E_USBHOST_RCVFIFO,
                     priv->buffer + priv->xfrd, nrcvd);

  /* Update the number of bytes transferred */

  priv->xfrd += nrcvd;

  /* Discard any byte remaining in the RCVFIFO */

  /* REVISIT:  Is this necessary?  Or the MAX3421E automatically discard any
   * unread data?
   */

#if 0
  if (nrcvd < navail)
    {
      max3421e_discard()
    }
#endif

  return nrcvd;
}

/****************************************************************************
 * Name: max3421e_recv_restart
 *
 * Description:
 *   Start/Re-start the transfer on the selected IN or OUT channel.
 *
 ****************************************************************************/

static void max3421e_recv_restart(FAR struct max3421e_usbhost_s *priv,
                                  FAR struct max3421e_chan_s *chan)
{
  /* Send the IN token. */

  max3421e_putreg(priv, MAX3421E_USBHOST_HXFR,
                  USBHOST_HXFR_TOKEN_IN | chan->epno);

  /* Enable the RCVFIFO interrupt to handle the completion/continuation
   * of transfer.  Enable the HXFRDNIRQ to catch NAKs and transfer
   * errors.
   */

  priv->xfrtype = HXFRDN_RCVFIFO;
  max3421e_int_enable(priv, USBHOST_HIRQ_RCVDAVIRQ | USBHOST_HIRQ_HXFRDNIRQ);
}

/****************************************************************************
 * Name: max3421e_recv_continue
 *
 * Description:
 *   Continue the receive operation started by max3421e_recv_start().  This
 *   function is called from the interrupt handler worker when an interrupt
 *   indicates that new, incoming data is available in the RCVFIFO (RCVDAV)
 *
 *   When the RCBBAV interrupt occurred, max3421e_irqwork() disabled that
 *   interrupt and called this function in order to handle the receipt of
 *   data
 *
 * Assumptions:
 *   The SPI bus is locked.
 *   The RCVDAV interrupt has been disabled.
 *
 ****************************************************************************/

static void max3421e_recv_continue(FAR struct max3421e_usbhost_s *priv)
{
  FAR struct max3421e_chan_s *chan;
  uint8_t nrcvd;
  int result;

  DEBUGASSERT(priv != NULL && priv->waiter != NULL);
  chan = priv->waiter;

  /* Check the result of a transfer */

  result = max3421e_transfer_status(priv);
  if (result < 0)
    {
      /* Terminate the transfer on any error. */

      max3421e_transfer_terminate(priv, chan, result);
      return;
    }

  /* Transfer the data from the RCVFIFO to the user buffer */

  nrcvd = max3421e_get_rcvfifo(priv, chan);

  /* A partial or zero-length packet is an indication that the transfer
   * completed early.  Terminate the transfer in those cases OR if all
   * of the requested data has been received
   */

  if (nrcvd < chan->maxpacket || priv->xfrd >= priv->buflen)
    {
      max3421e_transfer_terminate(priv, chan, OK);
    }

  /* If not all of the data has been received, then setup to receive
   * another packet.
   */

  else
    {
      max3421e_recv_restart(priv, chan);
    }
}

/****************************************************************************
 * Name: max3421e_recv_start
 *
 * Description:
 *   Start at transfer on the selected IN or OUT channel.
 *
 * Assumptions:
 *   The caller has the SPI locked.
 *
 ****************************************************************************/

static void max3421e_recv_start(FAR struct max3421e_usbhost_s *priv,
                                FAR struct max3421e_chan_s *chan)
{
  max3421e_pktdump("Sending", priv->buffer, priv->buflen);

  /* Set up the initial state of the transfer */

  usbhost_vtrace2(MAX3421E_VTRACE2_STARTTRANSFER2,
                  chan->chidx, priv->buflen);

  priv->result   = EBUSY;
  priv->inflight = 0;
  priv->xfrd     = 0;

  /* Make sure the peripheral address is correct */

  max3421e_putreg(priv, MAX3421E_USBHOST_PERADDR, chan->funcaddr);

  /* Start the transfer. */

  max3421e_recv_restart(priv, chan);
}

/****************************************************************************
 * Name: max3421e_in_transfer
 *
 * Description:
 *   Transfer 'buflen' bytes into 'buffer' from an IN channel.
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the TRANSFER
 *   interface and must manage the SPI lock itself.  The lock, for example,
 *   must be relinquished before waiting.
 *
 ****************************************************************************/

static ssize_t max3421e_in_transfer(FAR struct max3421e_usbhost_s *priv,
                                    FAR struct max3421e_chan_s *chan,
                                    FAR uint8_t *buffer, size_t buflen)
{
  clock_t start;
  ssize_t xfrd;
  int ret;

  /* Loop until the transfer completes (i.e., buflen is decremented to zero)
   * or a fatal error occurs any error other than a simple NAK.  NAK would
   * simply indicate the end of the transfer (short-transfer).
   */

  priv->buffer = buffer;
  priv->buflen = buflen;
  priv->xfrd   = 0;
  xfrd         = 0;

  start = clock_systime_ticks();
  while (priv->xfrd < priv->buflen)
    {
      /* Set up for the wait BEFORE starting the transfer */

      ret = max3421e_chan_waitsetup(priv, chan);
      if (ret < 0)
        {
          usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN5, 0);
          return (ssize_t)ret;
        }

      /* Set up for the transfer based on the direction and the endpoint */

      max3421e_lock(priv);
      ret = max3421e_in_setup(priv, chan);
      max3421e_unlock(priv);

      if (ret < 0)
        {
          usbhost_trace1(MAX3421E_TRACE1_INSETUP_FAIL1, -ret);
          return (ssize_t)ret;
        }

      /* Wait for the transfer to complete and get the result */

      ret = max3421e_chan_wait(priv, chan);

      /* EAGAIN indicates that the device NAKed the transfer. */

      if (ret < 0)
        {
          /* The transfer failed.  If we received a NAK, return all data
           * buffered so far (if any).
           */

          if (ret == -EAGAIN)
            {
              /* Was data buffered prior to the NAK? */

              if (xfrd > 0)
                {
                  /* Yes, return the amount of data received.
                   *
                   * REVISIT: This behavior is clearly correct for CDC/ACM
                   * bulk transfers and HID interrupt transfers.  But I am
                   * not so certain for MSC bulk transfers which, I think,
                   * could have NAKed packets in the middle of a transfer.
                   */

                  return xfrd;
                }
              else
                {
                  useconds_t delay;

                  /* Get the elapsed time.  Has the timeout elapsed?
                   * if not then try again.
                   */

                  clock_t elapsed = clock_systime_ticks() - start;
                  if (elapsed >= MAX3421E_DATANAK_DELAY)
                    {
                      /* Timeout out... break out returning the NAK as
                       * as a failure.
                       */

                      return (ssize_t)ret;
                    }

                  /* Wait a bit before retrying after a NAK. */

                  if (chan->eptype == USB_EP_ATTR_XFER_INT)
                    {
                      /* For interrupt (and isochronous) endpoints, the
                       * polling rate is determined by the bInterval field
                       * of the endpoint descriptor (in units of frames
                       * which we treat as milliseconds here).
                       */

                      if (chan->interval > 0)
                        {
                          /* Convert the delay to units of microseconds */

                          delay = (useconds_t)chan->interval * 1000;
                        }
                      else
                        {
                          /* Out of range! For interrupt endpoints, the valid
                           * range is 1-255 frames.  Assume one frame.
                           */

                          delay = 1000;
                        }
                    }
                  else
                    {
                      /* For Isochronous endpoints, bInterval must be 1.
                       * Bulk endpoints do not have a polling interval.
                       * Rather, the should wait until data is received.
                       *
                       * REVISIT:  For bulk endpoints this 1 msec delay is
                       * only intended to give the CPU a break from the bulk
                       * EP tight polling loop.  But are there performance
                       * issues?
                       */

                      delay = 1000;
                    }

                  /* Wait for the next polling interval.  For interrupt and
                   * isochronous endpoints, this is necessary to assure the
                   * polling interval.  It is used in other cases only to
                   * prevent the polling from consuming too much CPU
                   * bandwidth.
                   *
                   * Small delays could require more resolution than is
                   * provided by the system timer.  For example, if the
                   * system timer resolution is 10MS, then nxsig_usleep(1000)
                   * will actually request a delay 20MS (due to both
                   * quantization and rounding).
                   *
                   * REVISIT: So which is better?  To ignore tiny delays and
                   * hog the system bandwidth?  Or to wait for an excessive
                   * amount and destroy system throughput?
                   */

                  if (delay > CONFIG_USEC_PER_TICK)
                    {
                      nxsig_usleep(delay - CONFIG_USEC_PER_TICK);
                    }
                }
            }
          else
            {
              /* Some unexpected, fatal error occurred. */

              usbhost_trace1(MAX3421E_TRACE1_TRANSFER_FAILED3, -ret);

              /* Break out and return the error */

              return (ssize_t)ret;
            }
        }
      else
        {
          /* Successfully received another chunk of data... add that to the
           * running total.  Then continue reading until we read 'buflen'
           * bytes of data or until the devices NAKs (implying a short
           * packet).
           */

          xfrd += priv->xfrd;
        }
    }

  return xfrd;
}

/****************************************************************************
 * Name: max3421e_in_next
 *
 * Description:
 *   Initiate the next of a sequence of asynchronous transfers.
 *
 * Assumptions:
 *   This function is always called from an interrupt handler
 *
 ****************************************************************************/

#ifdef CONFIG_USBHOST_ASYNCH
static void max3421e_in_next(FAR struct max3421e_usbhost_s *priv,
                             FAR struct max3421e_chan_s *chan)
{
  usbhost_asynch_t callback;
  FAR void *arg;
  ssize_t nbytes;
  int result;
  int ret;

  /* Is the full transfer complete? Did the last chunk transfer OK? */

  result = -(int)priv->result;
  if (priv->xfrd < priv->buflen && result == OK)
    {
      /* Yes.. Set up for the next transfer based on the direction and the
       * endpoint type
       */

      ret = max3421e_in_setup(priv, chan);
      if (ret >= 0)
        {
          return;
        }

      usbhost_trace1(MAX3421E_TRACE1_INSETUP_FAIL2, -ret);
      result = ret;
    }

  /* The transfer is complete, with or without an error */

  usbhost_vtrace2(MAX3421E_VTRACE2_XFRCOMPLETE,
                  (unsigned int)ep, buflen);

  /* Extract the callback information */

  callback       = priv->callback;
  arg            = priv->arg;
  nbytes         = priv->xfrd;

  priv->callback = NULL;
  priv->arg      = NULL;
  priv->xfrd     = 0;

  /* Then perform the callback */

  if (result < 0)
    {
      nbytes = (ssize_t)result;
    }

  callback(arg, nbytes);
}
#endif

/****************************************************************************
 * Name: max3421e_in_asynch
 *
 * Description:
 *   Initiate the first of a sequence of asynchronous transfers.
 *
 * Assumptions:
 *   The SPI is not locked.  This function is called only from the ASYNCH
 *   interface and must manage the SPI lock itself.  The lock, for example,
 *   must be relinquished before waiting.
 *
 ****************************************************************************/

#ifdef CONFIG_USBHOST_ASYNCH
static int max3421e_in_asynch(FAR struct max3421e_usbhost_s *priv,
                              FAR struct max3421e_chan_s *chan,
                              FAR uint8_t *buffer, size_t buflen,
                              usbhost_asynch_t callback, FAR void *arg)
{
  int ret;

  /* Set up for the transfer BEFORE starting the first transfer */

  priv->buffer = buffer;
  priv->buflen = buflen;
  priv->xfrd   = 0;

  ret = max3421e_chan_asynchsetup(priv, chan, callback, arg);
  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_ASYNCHSETUP_FAIL2, -ret);
      return ret;
    }

  /* Set up for the transfer based on the direction and the endpoint type */

  max3421e_lock(priv);
  ret = max3421e_in_setup(priv, chan);
  max3421e_unlock(priv);

  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_INSETUP_FAIL3, -ret);
    }

  /* And return with the transfer pending */

  return ret;
}
#endif

/****************************************************************************
 * Name: max3421e_connect_event
 *
 * Description:
 *   Handle a connection event.
 *
 ****************************************************************************/

static void max3421e_connect_event(FAR struct max3421e_usbhost_s *priv)
{
  /* Were we previously disconnected? */

  if (!priv->connected)
    {
      /* Yes.. then now we are connected */

      usbhost_vtrace1(MAX3421E_VTRACE1_CONNECTED1, 0);
      priv->connected = true;
      priv->change    = true;
      DEBUGASSERT(priv->smstate == SMSTATE_DETACHED);

      /* Notify any waiters */

      priv->smstate = SMSTATE_ATTACHED;
      if (priv->pscwait)
        {
          nxsem_post(&priv->pscsem);
          priv->pscwait = false;
        }
    }
}

/****************************************************************************
 * Name: max3421e_disconnect_event
 *
 * Description:
 *   Handle a disconnection event.
 *
 ****************************************************************************/

static void max3421e_disconnect_event(FAR struct max3421e_usbhost_s *priv)
{
  /* Were we previously connected? */

  if (priv->connected)
    {
      /* Yes.. then we no longer connected */

      usbhost_vtrace1(MAX3421E_VTRACE1_DISCONNECTED1, 0);

      /* Are we bound to a class driver? */

      if (priv->rhport.hport.devclass)
        {
          /* Yes.. Disconnect the class driver */

          CLASS_DISCONNECTED(priv->rhport.hport.devclass);
          priv->rhport.hport.devclass = NULL;
        }

      /* Re-Initialize Host for new Enumeration */

      priv->smstate   = SMSTATE_DETACHED;
      priv->connected = false;
      priv->change    = true;

      priv->rhport.hport.speed = USB_SPEED_FULL;
      priv->rhport.hport.funcaddr = 0;

      /* Notify any waiters that there is a change in the connection state */

      if (priv->pscwait)
        {
          nxsem_post(&priv->pscsem);
          priv->pscwait = false;
        }
    }
}

/****************************************************************************
 * Name: max3421e_connected
 *
 * Description:
 *   USB host port interrupt handler
 *
 ****************************************************************************/

static int max3421e_connected(FAR struct max3421e_usbhost_s *priv)
{
  int ret;

  /* Stop SOF generation and reset the bus */

  max3421e_busreset(priv);
  nxsig_sleep(1);

  /* Check for low- or full-speed and restart SOF generation. */

  ret = max3421e_startsof(priv);
  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN7, -ret);
      return ret;
    }

  usbhost_vtrace1(MAX3421E_VTRACE1_CONNECTED3, 0);

  /* Were we previously disconnected? */

  max3421e_connect_event(priv);
  return OK;
}

/****************************************************************************
 * Name: max3421e_disconnected
 *
 * Description:
 *   USB disconnect detected interrupt handler
 *
 ****************************************************************************/

static void max3421e_disconnected(FAR struct max3421e_usbhost_s *priv)
{
  usbhost_vtrace1(MAX3421E_VTRACE1_DISCONNECTED2, 0);

  /* Disable the SOF generator */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_MODE, USBHOST_MODE_SOFKAENAB, 0);

  /* Handle the disconnection event */

  max3421e_disconnect_event(priv);
}

/****************************************************************************
 * Name: max3421e_irqwork
 *
 * Description:
 *   MAX3421E interrupt worker.  Perform MAX3421E interrupt processing on the
 *   high priority work queue thread.  Interrupts were disabled by the
 *   interrupt handler when the interrupt was received.  This worker must
 *   re-enable MAX3421E interrupts when interrupt processing is complete.
 *
 ****************************************************************************/

static void max3421e_irqwork(FAR void *arg)
{
  FAR struct max3421e_usbhost_s *priv;
  FAR const struct max3421e_lowerhalf_s *lower;
  uint8_t pending;
  int ret;

  priv  = (FAR struct max3421e_usbhost_s *)arg;
  DEBUGASSERT(priv != NULL && priv->lower != NULL);
  lower = (FAR const struct max3421e_lowerhalf_s *)priv->lower;

  /* Get exclusive access to the SPI bus */

  max3421e_lock(priv);

  /* Loop while there are pending interrupts to process.  This loop may save
   * a little interrupt handling overhead.
   */

  for (; ; )
    {
      /* Get the unmasked bits in the GINT status */

      pending = max3421e_int_status(priv);
      priv->lower->acknowledge(lower);

      /* Break out of the loop when there are no pending interrupts. */

      if (pending == 0)
        {
          break;
        }

      /* Possibilities:
       *
       *   HXFRDNIRQ    - Host Transfer Done Interrupt
       *   FRAMEIRQ     - Frame Generator Interrupt
       *   CONNIRQ      - Peripheral Connect/Disconnect Interrupt
       *   SUSDNIRQ     - Suspend operation Done
       *   SNDBAVIRQ    - SNDFIFO is available
       *   RCVDAVIRQ    - RCVFIFO data available
       *   RSMREQIRQ    - Remote Wakeup Interrupt
       *   BUSEVENTIRQ  - Bus Reset or Bus Resume Interrupt
       *
       * Only CONNIRQ handled here.
       */

      /* HXFRDNIRQ:  Host transfer done interrupt */

      if ((pending & USBHOST_HIRQ_HXFRDNIRQ) != 0 &&
          (pending & USBHOST_HIRQ_SNDBAVIRQ) == 0 &&
          (pending & USBHOST_HIRQ_RCVDAVIRQ) == 0)
        {
          int result;

          /* Disable further SNDBAV (or HXFRDN) interrupts */

          max3421e_int_disable(priv, USBHOST_HIRQ_HXFRDNIRQ);

          /* Clear the pending HXFRDN interrupt */

          max3421e_int_clear(priv, USBHOST_HIRQ_HXFRDNIRQ);

          /* Check transfer status and terminate the transfer if any error
           * occurred.
           */

          result = max3421e_transfer_status(priv);
          if (result < 0 || priv->xfrtype == HXFRDN_SETUP ||
              priv->xfrtype == HXFRDN_SNDZLP)
            {
              FAR struct max3421e_chan_s *chan = priv->waiter;
              DEBUGASSERT(chan != NULL);

              max3421e_transfer_terminate(priv, chan, result);
            }
        }

      /* CONNIRQ: Has a peripheral been connected or disconnected */

      if ((pending & USBHOST_HIRQ_CONNIRQ) != 0)
        {
          /* Clear the pending CONNIRQ interrupt */

          max3421e_int_clear(priv, USBHOST_HIRQ_CONNIRQ);

          /* Check if a peripheral device has been connected */

          ret = max3421e_connected(priv);
          if (ret < 0)
            {
              /* No.. then a device must have been disconnected. */

              max3421e_disconnected(priv);
            }
        }

      /* SNDBAV: The SNDFIFO is available */

      else if ((pending & USBHOST_HIRQ_SNDBAVIRQ) != 0)
        {
          /* Disable further SNDBAV (or HXFRDN) interrupts */

          max3421e_int_disable(priv, USBHOST_HIRQ_SNDBAVIRQ |
                                     USBHOST_HIRQ_HXFRDNIRQ);

          /* Clear the pending SNDBAV interrupt */

          max3421e_int_clear(priv, USBHOST_HIRQ_SNDBAVIRQ);

          /* Finish long transfer, possibly re-enabling the SNDBAV
           * interrupt (see max3421e_send_start)
           */

          max3421e_send_continue(priv);
        }

      /* RCVDAVIRQ: RCVFIFO data available */

      else if ((pending & USBHOST_HIRQ_RCVDAVIRQ) != 0)
        {
          /* Disable further RCVDAV (or HXFRDN) interrupts */

          max3421e_int_disable(priv, USBHOST_HIRQ_RCVDAVIRQ |
                                     USBHOST_HIRQ_HXFRDNIRQ);

          /* Clear the pending RCVDAV interrupt.  The RCVFIFO is double
           * buffered.  If another packet is available in the other buffer,
           * the MAX3421E will immediately re-assert RCVDAVIRQ and we
           * will catch that the next time through this loop.
           */

          max3421e_int_clear(priv, USBHOST_HIRQ_RCVDAVIRQ);

          /* Handle the receipt of data */

          max3421e_recv_continue(priv);
        }
    }

  /* Re-enable interrupts */

  lower->enable(lower, true);
  max3421e_unlock(priv);
}

/****************************************************************************
 * Name: max3421e_interrupt
 *
 * Description:
 *   MAX3421E interrupt handler.  This interrupt handler simply defers
 *   interrupt processing to the high priority work queue thread.  This is
 *   necessary because we cannot perform interrupt/DMA driven SPI accesses
 *   from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_interrupt(int irq, FAR void *context, FAR void *arg)
{
  FAR struct max3421e_usbhost_s *priv;
  FAR const struct max3421e_lowerhalf_s *lower;

  priv = (FAR struct max3421e_usbhost_s *)arg;
  DEBUGASSERT(priv != NULL && priv->lower != NULL);
  lower = (FAR const struct max3421e_lowerhalf_s *)priv->lower;

  /* Disable further interrupts until work associated with this interrupt
   * has been processed.
   */

  lower->enable(lower, false);

  /* And defer interrupt processing to the high priority work queue thread */

  work_queue(LPWORK, &priv->irqwork, max3421e_irqwork, priv, 0);
  return OK;
}

/****************************************************************************
 * Name: max3421e_int_enable, max3421e_int_disable, max3421e_int_status, and
 *       max3421e_int_wait
 *
 * Description:
 *   Respectively enable, disable, or get status of the USB host interrupt
 *   (HIRQ) and a mask of enabled interrupts.
 *
 * Input Parameters:
 *   priv - Private state data
 *   irqset - IRQ bits to be set (max3421e_int_status only)
 *
 * Returned Value:
 *   The current unmasks interrupt status  (max3421e_int_status only)
 *
 ****************************************************************************/

/* Enable a set of interrupts */

static inline void max3421e_int_enable(FAR struct max3421e_usbhost_s *priv,
                                       uint8_t irqset)
{
  priv->irqset |= irqset;
  max3421e_putreg(priv, MAX3421E_USBHOST_HIEN, priv->irqset);
}

/* Disable a set of interrupts */

static inline void max3421e_int_disable(FAR struct max3421e_usbhost_s *priv,
                                        uint8_t irqset)
{
  priv->irqset &= ~irqset;
  max3421e_putreg(priv, MAX3421E_USBHOST_HIEN, priv->irqset);
}

/* Get the set of pending interrupts */

static inline uint8_t
  max3421e_int_status(FAR struct max3421e_usbhost_s *priv)
{
  return max3421e_getreg(priv, MAX3421E_USBHOST_HIRQ) & priv->irqset;
}

/* Clear a set of pending interrupts */

static inline void max3421e_int_clear(FAR struct max3421e_usbhost_s *priv,
                                      uint8_t irqset)
{
  max3421e_putreg(priv, MAX3421E_USBHOST_HIRQ, irqset);
}

/* Wait until any interrupt from a set of interrupts occurs */

static void max3421e_int_wait(FAR struct max3421e_usbhost_s *priv,
                              uint8_t irqset, unsigned int usec)
{
  uint8_t regval;

  do
    {
      regval  =  max3421e_getreg(priv, MAX3421E_USBHOST_HIRQ);
      regval &= irqset;

      if (regval == 0 && usec > 0)
        {
          nxsig_usleep(usec);
        }
    }
  while (regval == 0);
}

/****************************************************************************
 * Name: max3421e_wait
 *
 * Description:
 *   Wait for a device to be connected or disconnected to/from a hub port.
 *
 * Input Parameters:
 *   conn  - The USB host connection instance obtained as a parameter from
 *           the call to the USB driver initialization logic.
 *   hport - The location to return the hub port descriptor that detected the
 *           connection related event.
 *
 * Returned Value:
 *   Zero (OK) is returned on success when a device is connected or
 *   disconnected. This function will not return until either (1) a device is
 *   connected or disconnect to/from any hub port or until (2) some failure
 *   occurs.  On a failure, a negated errno value is returned indicating the
 *   nature of the failure
 *
 * Assumptions:
 *   - Called from a single thread so no mutual exclusion is required.
 *   - Never called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_wait(FAR struct usbhost_connection_s *conn,
                         FAR struct usbhost_hubport_s **hport)
{
  FAR struct max3421e_connection_s *maxconn;
  FAR struct max3421e_usbhost_s *priv;
  struct usbhost_hubport_s *connport;
  int ret;

  maxconn = (FAR struct max3421e_connection_s *)conn;
  DEBUGASSERT(maxconn != NULL && maxconn->priv != NULL);
  priv = maxconn->priv;

  /* Loop until a change in connection state is detected */

  for (; ; )
    {
      /* We must have exclusive access to USB host hardware and structures */

      ret = nxrmutex_lock(&priv->lock);
      if (ret < 0)
        {
          return ret;
        }

      /* Is there a change in the connection state of the single root hub
       * port?
       */

      if (priv->change)
        {
          connport = &priv->rhport.hport;

          /* Yes. Remember the new state */

          connport->connected = priv->connected;
          priv->change = false;

          /* And return the root hub port */

          *hport = connport;

          usbhost_vtrace1(MAX3421E_VTRACE1_CONNECTED2, connport->connected);

          nxrmutex_unlock(&priv->lock);
          return OK;
        }

#ifdef CONFIG_USBHOST_HUB
      /* Is a device connected to an external hub? */

      if (priv->hport)
        {
          /* Yes.. return the external hub port */

          connport = (struct usbhost_hubport_s *)priv->hport;
          priv->hport = NULL;

          *hport = connport;

          usbhost_vtrace1(MAX3421E_VTRACE1_HUB_CONNECTED,
                          connport->connected);

          nxrmutex_unlock(&priv->lock);
          return OK;
        }
#endif

      /* Wait for the next connection event */

      priv->pscwait = true;
      nxrmutex_unlock(&priv->lock);
      ret = nxsem_wait_uninterruptible(&priv->pscsem);
      if (ret < 0)
        {
          return ret;
        }
    }
}

/****************************************************************************
 * Name: max3421e_getspeed
 *
 * Description:
 *   Get the speed of the connected device.
 *
 * Input Parameters:
 *   priv - Driver private state structure
 *   conn - The USB host connection instance obtained as a parameter from
 *      the call to the USB driver initialization logic.
 *   hport - The descriptor of the hub port that has the newly connected
 *      device.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   The caller has the SPI bus locked.
 *
 ****************************************************************************/

static int max3421e_getspeed(FAR struct max3421e_usbhost_s *priv,
                             FAR struct usbhost_connection_s *conn,
                             FAR struct usbhost_hubport_s *hport)
{
  int ret;

  DEBUGASSERT(conn != NULL && hport != NULL && hport->port == 0);

  /* Are we connected to a device?  The caller should have called the wait()
   * method first to be assured that a device is connected.
   */

  if (!priv->connected)
    {
      /* No, return an error */

      usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN6, 0);
      return -ENODEV;
    }

  DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED);

  /* USB 2.0 spec says at least 50ms delay before port reset.  We wait
   * 100ms.
   */

  nxsig_usleep(100 * 1000);

  /* Make sure we are still connected */

  if (!priv->connected)
    {
      /* No, return an error */

      usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN6, 0);
      nxrmutex_unlock(&priv->lock);
      return -ENODEV;
    }

  /* Stop SOF generation and reset the host port */

  max3421e_busreset(priv);
  nxsig_sleep(1);

  /* Get the current device speed */

  ret = max3421e_startsof(priv);
  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN8, -ret);
    }

  return ret;
}

/****************************************************************************
 * Name: max3421e_enumerate
 *
 * Description:
 *   Enumerate the connected device.  As part of this enumeration process,
 *   the driver will (1) get the device's configuration descriptor, (2)
 *   extract the class ID info from the configuration descriptor, (3) call
 *   usbhost_findclass() to find the class that supports this device, (4)
 *   call the create() method on the struct usbhost_registry_s interface
 *   to get a class instance, and finally (5) call the connect() method
 *   of the struct usbhost_class_s interface.  After that, the class is in
 *   charge of the sequence of operations.
 *
 * Input Parameters:
 *   conn - The USB host connection instance obtained as a parameter from
 *      the call to the USB driver initialization logic.
 *   hport - The descriptor of the hub port that has the newly connected
 *      device.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   This function will *not* be called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_enumerate(FAR struct usbhost_connection_s *conn,
                              FAR struct usbhost_hubport_s *hport)
{
  FAR struct max3421e_connection_s *maxconn;
  FAR struct max3421e_usbhost_s *priv;
  int ret;

  maxconn = (FAR struct max3421e_connection_s *)conn;
  DEBUGASSERT(maxconn != NULL && maxconn->priv != NULL);
  priv = maxconn->priv;

  /* We must have exclusive access to the USB host hardware and structures */

  ret = nxrmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* If this is a connection on the root hub, then we need to go to
   * little more effort to get the device speed.  If it is a connection
   * on an external hub, then we already have that information.
   */

  max3421e_lock(priv);

#warning REVISIT:  Isn't this already done?

#ifdef CONFIG_USBHOST_HUB
  if (ROOTHUB(hport))
#endif
    {
      ret = max3421e_getspeed(priv, conn, hport);
      if (ret < 0)
        {
          nxrmutex_unlock(&priv->lock);
          return ret;
        }
    }

  /* Set enumeration data toggles */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_HCTL,
                     USBHOST_HCTL_TOGGLES_MASK,
                     USBHOST_HCTL_RCVTOG1 | USBHOST_HCTL_SNDTOG1);
  max3421e_unlock(priv);

  /* Then let the common usbhost_enumerate do the real enumeration. */

  usbhost_vtrace1(MAX3421E_VTRACE1_ENUMERATE, 0);
  priv->smstate = SMSTATE_ENUM;

  ret = usbhost_enumerate(hport, &hport->devclass);

  /* The enumeration may fail either because of some HCD interfaces failure
   * or because the device class is not supported.  In either case, we just
   * need to perform the disconnection operation and make ready for a new
   * enumeration.
   */

  if (ret < 0)
    {
      /* Return to the disconnected state */

      usbhost_trace1(MAX3421E_TRACE1_ENUMERATE_FAIL, -ret);
      max3421e_disconnect_event(priv);
    }

  /* Set post-enumeration data toggles (assuming success) */

  max3421e_lock(priv);
  max3421e_modifyreg(priv, MAX3421E_USBHOST_HCTL,
                     USBHOST_HCTL_TOGGLES_MASK,
                     USBHOST_HCTL_RCVTOG0 | USBHOST_HCTL_SNDTOG0);
  max3421e_unlock(priv);

  nxrmutex_unlock(&priv->lock);
  return ret;
}

/****************************************************************************
 * Name: max3421e_ep0configure
 *
 * Description:
 *   Configure endpoint 0.  This method is normally used internally by the
 *   enumerate() method but is made available at the interface to support an
 *   external implementation of the enumeration logic.
 *
 * Input Parameters:
 *   drvr          - The USB host driver instance obtained as a parameter
 *                   from the call to the class create() method.
 *   ep0           - The (opaque) EP0 endpoint instance
 *   funcaddr      - The USB address of the function containing the endpoint
 *                   that EP0 controls
 *   speed         - The speed of the port USB_SPEED_LOW or _FULL
 *   maxpacketsize - The maximum number of bytes that can be sent to or
 *                   received from the endpoint in a single data packet
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   This function will *not* be called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_ep0configure(FAR struct usbhost_driver_s *drvr,
                                 usbhost_ep_t ep0,
                                 uint8_t funcaddr, uint8_t speed,
                                 uint16_t maxpacketsize)
{
  FAR struct max3421e_usbhost_s *priv =
    (FAR struct max3421e_usbhost_s *)drvr;
  FAR struct max3421e_chan_s *chan;
  int ret;

  DEBUGASSERT(drvr != NULL && funcaddr < 128 && maxpacketsize <= 64);

  /* We must have exclusive access to the USB host hardware and structures */

  ret = nxrmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* Configure the EP0 channel */

  chan            = &priv->chan[(intptr_t)ep0];
  chan->funcaddr  = funcaddr;
  chan->speed     = speed;
  chan->maxpacket = maxpacketsize;
  chan->toggles   = USBHOST_HCTL_RCVTOG0 | USBHOST_HCTL_SNDTOG0;

  nxrmutex_unlock(&priv->lock);
  return OK;
}

/****************************************************************************
 * Name: max3421e_epalloc
 *
 * Description:
 *   Allocate and configure one endpoint.
 *
 * Input Parameters:
 *   drvr   - The USB host driver instance obtained as a parameter from the
 *            call to the class create() method.
 *   epdesc - Describes the endpoint to be allocated.
 *   ep     - A memory location provided by the caller in which to receive
 *            the allocated endpoint descriptor.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   This function will *not* be called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_epalloc(FAR struct usbhost_driver_s *drvr,
                            FAR const struct usbhost_epdesc_s *epdesc,
                            FAR usbhost_ep_t *ep)
{
  FAR struct max3421e_usbhost_s *priv =
    (FAR struct max3421e_usbhost_s *)drvr;
  struct usbhost_hubport_s *hport;
  FAR struct max3421e_chan_s *chan;
  int chidx;
  int ret;

  /* Sanity check.  NOTE that this method should only be called if a device
   * is connected (because we need a valid low speed indication).
   */

  DEBUGASSERT(drvr != 0 && epdesc != NULL && ep != NULL);
  hport = epdesc->hport;
  DEBUGASSERT(hport != NULL);

  /* We must have exclusive access to the USB host hardware and structures */

  ret = nxrmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* Allocate a host channel for the endpoint */

  chidx = max3421e_chan_alloc(priv);
  if (chidx < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_CHANALLOC_FAIL, -chidx);
      nxrmutex_unlock(&priv->lock);
      return chidx;
    }

  /* Decode the endpoint descriptor to initialize the channel data
   * structures.  Note:  Here we depend on the fact that the endpoint point
   * type is encoded in the same way in the endpoint descriptor as it is in
   * the OTG HS hardware.
   */

  chan            = &priv->chan[chidx];
  chan->epno      = epdesc->addr & USB_EPNO_MASK;
  chan->in        = epdesc->in;
  chan->eptype    = epdesc->xfrtype;
  chan->funcaddr  = hport->funcaddr;
  chan->speed     = hport->speed;
  chan->interval  = epdesc->interval;
  chan->maxpacket = epdesc->mxpacketsize;
  chan->toggles   = USBHOST_HCTL_RCVTOG0 | USBHOST_HCTL_SNDTOG0;

  /* Return the endpoint number as the endpoint "handle" */

  *ep = (usbhost_ep_t)chidx;
  nxrmutex_unlock(&priv->lock);
  return OK;
}

/****************************************************************************
 * Name: max3421e_epfree
 *
 * Description:
 *   Free and endpoint previously allocated by DRVR_EPALLOC.
 *
 * Input Parameters:
 *   drvr - The USB host driver instance obtained as a parameter from the
 *          call to the class create() method.
 *   ep   - The endpoint to be freed.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   This function will *not* be called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_epfree(FAR struct usbhost_driver_s *drvr,
                           usbhost_ep_t ep)
{
  FAR struct max3421e_usbhost_s *priv =
    (FAR struct max3421e_usbhost_s *)drvr;
  int ret;

  DEBUGASSERT(priv);

  /* We must have exclusive access to the USB host hardware and structures */

  ret = nxrmutex_lock(&priv->lock);
  if (ret >= 0)
    {
      /* Halt the channel and mark the channel available */

      max3421e_chan_free(priv, (intptr_t)ep);
      nxrmutex_unlock(&priv->lock);
    }

  return ret;
}

/****************************************************************************
 * Name: max3421e_alloc
 *
 * Description:
 *   Some hardware supports special memory in which request and descriptor
 *   data can be accessed more efficiently.  This method provides a
 *   mechanism to allocate the request/descriptor memory.  If the underlying
 *   hardware does not support such "special" memory, this functions may
 *   simply map to kmm_malloc.
 *
 *   This interface was optimized under a particular assumption.  It was
 *   assumed that the driver maintains a pool of small, pre-allocated
 *   buffers for descriptor traffic.  NOTE that size is not an input, but
 *   an output:  The size of the pre-allocated buffer is returned.
 *
 * Input Parameters:
 *   drvr   - The USB host driver instance obtained as a parameter from the
 *            call to the class create() method.
 *   buffer - The address of a memory location provided by the caller in
 *            which to return the allocated buffer memory address.
 *   maxlen - The address of a memory location provided by the caller in
 *            which to return the maximum size of the allocated buffer
 *             memory.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   - Called from a single thread so no mutual exclusion is required.
 *   - Never called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_alloc(FAR struct usbhost_driver_s *drvr,
                          FAR uint8_t **buffer, FAR size_t *maxlen)
{
  FAR uint8_t *alloc;

  DEBUGASSERT(drvr && buffer && maxlen);

  /* There is no special memory requirement for the MAX3421E. */

  alloc = kmm_malloc(CONFIG_MAX3421E_DESCSIZE);
  if (!alloc)
    {
      return -ENOMEM;
    }

  /* Return the allocated address and size of the descriptor buffer */

  *buffer = alloc;
  *maxlen = CONFIG_MAX3421E_DESCSIZE;
  return OK;
}

/****************************************************************************
 * Name: max3421e_free
 *
 * Description:
 *   Some hardware supports special memory in which request and descriptor
 *   data can be accessed more efficiently.  This method provides a
 *   mechanism to free that request/descriptor memory.  If the underlying
 *   hardware does not support such "special" memory, this functions may
 *   simply map to kmm_free().
 *
 * Input Parameters:
 *   drvr   - The USB host driver instance obtained as a parameter from the
 *            call to the class create() method.
 *   buffer - The address of the allocated buffer memory to be freed.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   - Never called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_free(FAR struct usbhost_driver_s *drvr,
                         FAR uint8_t *buffer)
{
  /* There is no special memory requirement */

  DEBUGASSERT(drvr && buffer);
  kmm_free(buffer);
  return OK;
}

/****************************************************************************
 * Name: max3421e_ioalloc
 *
 * Description:
 *   Some hardware supports special memory in which larger IO buffers can
 *   be accessed more efficiently.  This method provides a mechanism to
 *   allocate the request/descriptor memory.  If the underlying hardware
 *   does not support such "special" memory, this functions may simply map
 *   to kmm_malloc.
 *
 *   This interface differs from DRVR_ALLOC in that the buffers are
 *   variable-sized.
 *
 * Input Parameters:
 *   drvr   - The USB host driver instance obtained as a parameter from the
 *            call to the class create() method.
 *   buffer - The address of a memory location provided by the caller in
 *            which to return the allocated buffer memory address.
 *   buflen - The size of the buffer required.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   This function will *not* be called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_ioalloc(FAR struct usbhost_driver_s *drvr,
                            FAR uint8_t **buffer, size_t buflen)
{
  FAR uint8_t *alloc;

  DEBUGASSERT(drvr && buffer && buflen > 0);

  /* There is no special memory requirement */

  alloc = kmm_malloc(buflen);
  if (!alloc)
    {
      return -ENOMEM;
    }

  /* Return the allocated buffer */

  *buffer = alloc;
  return OK;
}

/****************************************************************************
 * Name: max3421e_iofree
 *
 * Description:
 *   Some hardware supports special memory in which IO data can  be accessed
 *   more efficiently.  This method provides a mechanism to free that IO
 *   buffer memory.  If the underlying hardware does not support such
 *   "special" memory, this functions may simply map to kmm_free().
 *
 * Input Parameters:
 *   drvr   - The USB host driver instance obtained as a parameter from the
 *            call to the class create() method.
 *   buffer - The address of the allocated buffer memory to be freed.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   This function will *not* be called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_iofree(FAR struct usbhost_driver_s *drvr,
                           FAR uint8_t *buffer)
{
  /* There is no special memory requirement */

  DEBUGASSERT(drvr && buffer);
  kmm_free(buffer);
  return OK;
}

/****************************************************************************
 * Name: max3421e_ctrlin and max3421e_ctrlout
 *
 * Description:
 *   Process a IN or OUT request on the control endpoint.  These methods
 *   will enqueue the request and wait for it to complete.  Only one
 *   transfer may be queued; Neither these methods nor the transfer() method
 *   can be called again until the control transfer functions returns.
 *
 *   These are blocking methods; these functions will not return until the
 *   control transfer has completed.
 *
 * Input Parameters:
 *   drvr   - The USB host driver instance obtained as a parameter from
 *            the call to the class create() method.
 *   ep0    - The control endpoint to send/receive the control request.
 *   req    - Describes the request to be sent.  This request must lie in
 *            memory created by DRVR_ALLOC.
 *   buffer - A buffer used for sending the request and for returning any
 *            responses.  This buffer must be large enough to hold the
 *            length value in the request description. buffer must have been
 *            allocated using DRVR_ALLOC.
 *
 *   NOTE: On an IN transaction, req and buffer may refer to the same
 *   allocated memory.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   - Called from a single thread so no mutual exclusion is required.
 *   - Never called from an interrupt handler.
 *
 ****************************************************************************/

static int max3421e_ctrlin(FAR struct usbhost_driver_s *drvr,
                           usbhost_ep_t ep0,
                           FAR const struct usb_ctrlreq_s *req,
                           FAR uint8_t *buffer)
{
  FAR struct max3421e_usbhost_s *priv;
  FAR struct max3421e_chan_s *chan;
  uint16_t buflen;
  clock_t start;
  clock_t elapsed;
  int retries;
  int ret;

  DEBUGASSERT(drvr != NULL && req != NULL);
  priv = (FAR struct max3421e_usbhost_s *)drvr;

  DEBUGASSERT((intptr_t)ep0 >= 0 && (intptr_t)ep0 < MAX3421E_NHOST_CHANNELS);
  chan = &priv->chan[(intptr_t)ep0];

  usbhost_vtrace2(MAX3421E_VTRACE2_CTRLIN, req->type, req->req);
  uinfo("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n",
        req->type, req->req, req->value[1], req->value[0],
        req->index[1], req->index[0], req->len[1], req->len[0]);

  /* Extract values from the request */

  buflen = max3421e_getle16(req->len);

  /* We must have exclusive access to the USB host hardware and structures */

  ret = nxrmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* Loop, retrying until the retry time expires */

  for (retries = 0; retries < MAX3421E_RETRY_COUNT; retries++)
    {
      /* Send the SETUP request */

      ret = max3421e_ctrl_sendsetup(priv, chan, req);
      if (ret < 0)
        {
          usbhost_trace1(MAX3421E_TRACE1_SENDSETUP_FAIL2, -ret);
          continue;
        }

      /* Get the start time.  Loop again until the timeout expires */

      start = clock_systime_ticks();
      do
        {
          /* Handle the IN data phase (if any) */

          /* The MAX4321E sends fixed DATA0 and DATA1 PID tokens for the
           * various stages of a CONTROL transfer, regardless of the
           * setting of the internal data toggle.
           */

          if (buflen > 0)
            {
              ret = max3421e_ctrl_recvdata(priv, chan, buffer, buflen);
              if (ret < 0)
                {
                  usbhost_trace1(MAX3421E_TRACE1_RECVDATA_FAIL, -ret);
                }
            }

          /* Handle the status OUT phase */

          if (ret >= OK)
            {
              ret = max3421e_ctrl_sendstatus(priv, chan);
              if (ret >= OK)
                {
                  /* All success transactions exit here */

                  nxrmutex_unlock(&priv->lock);
                  return OK;
                }

              usbhost_trace1(MAX3421E_TRACE1_SENDSTATUS_FAIL, -ret);
            }

          /* Get the elapsed time (in frames) */

          elapsed = clock_systime_ticks() - start;
        }
      while (elapsed < MAX3421E_DATANAK_DELAY);
    }

  /* All failures exit here after all retries and timeouts are exhausted */

  nxrmutex_unlock(&priv->lock);
  return -ETIMEDOUT;
}

static int max3421e_ctrlout(FAR struct usbhost_driver_s *drvr,
                            usbhost_ep_t ep0,
                            FAR const struct usb_ctrlreq_s *req,
                            FAR const uint8_t *buffer)
{
  FAR struct max3421e_usbhost_s *priv =
    (FAR struct max3421e_usbhost_s *)drvr;
  FAR struct max3421e_chan_s *chan;
  uint16_t buflen;
  clock_t start;
  clock_t elapsed;
  int retries;
  int ret;

  DEBUGASSERT(drvr != NULL && req != NULL);
  priv = (FAR struct max3421e_usbhost_s *)drvr;

  DEBUGASSERT((intptr_t)ep0 >= 0 && (intptr_t)ep0 < MAX3421E_NHOST_CHANNELS);
  chan = &priv->chan[(intptr_t)ep0];

  usbhost_vtrace2(MAX3421E_VTRACE2_CTRLOUT, req->type, req->req);
  uinfo("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n",
        req->type, req->req, req->value[1], req->value[0],
        req->index[1], req->index[0], req->len[1], req->len[0]);

  /* Extract values from the request */

  buflen = max3421e_getle16(req->len);

  /* We must have exclusive access to the USB host hardware and structures */

  ret = nxrmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* Loop, retrying until the retry time expires */

  for (retries = 0; retries < MAX3421E_RETRY_COUNT; retries++)
    {
      /* Send the SETUP request */

      ret = max3421e_ctrl_sendsetup(priv, chan, req);
      if (ret < 0)
        {
          usbhost_trace1(MAX3421E_TRACE1_SENDSETUP_FAIL1, -ret);
          continue;
        }

      /* Get the start time.  Loop again until the timeout expires */

      start = clock_systime_ticks();
      do
        {
          /* Handle the data OUT phase (if any) */

          /* The MAX4321E sends fixed DATA0 and DATA1 PID tokens for the
           * various stages of a CONTROL transfer, regardless of the setting
           * of the internal data toggle.
           */

          if (buflen > 0)
            {
              /* Start DATA out transfer (only one DATA packet) */

              ret = max3421e_ctrl_senddata(priv, chan, NULL, 0);
              if (ret < 0)
                {
                  usbhost_trace1(MAX3421E_TRACE1_SENDDATA_FAIL, -ret);
                }
            }

          /* Handle the status IN phase */

          if (ret >= OK)
            {
              ret = max3421e_ctrl_recvstatus(priv, chan);
              if (ret >= OK)
                {
                  /* All success transactions exit here */

                  nxrmutex_unlock(&priv->lock);
                  return OK;
                }

              usbhost_trace1(MAX3421E_TRACE1_RECVSTATUS_FAIL, -ret);
            }

          /* Get the elapsed time (in frames) */

          elapsed = clock_systime_ticks() - start;
        }
      while (elapsed < MAX3421E_DATANAK_DELAY);
    }

  /* All failures exit here after all retries and timeouts are exhausted */

  nxrmutex_unlock(&priv->lock);
  return -ETIMEDOUT;
}

/****************************************************************************
 * Name: max3421e_transfer
 *
 * Description:
 *   Process a request to handle a transfer descriptor.  This method will
 *   enqueue the transfer request, blocking until the transfer completes.
 *   Only one transfer may be  queued; Neither this method nor the ctrlin or
 *   ctrlout methods can be called again until this function returns.
 *
 *   This is a blocking method; this functions will not return until the
 *   transfer has completed.
 *
 * Input Parameters:
 *   drvr   - The USB host driver instance obtained as a parameter from the
 *            call to the class create() method.
 *   ep     - The IN or OUT endpoint descriptor for the device endpoint on
 *            which to perform the transfer.
 *   buffer - A buffer containing the data to be sent (OUT endpoint) or
 *            received (IN endpoint).  buffer must have been allocated
 *            using DRVR_ALLOC
 *   buflen - The length of the data to be sent or received.
 *
 * Returned Value:
 *   On success, a non-negative value is returned that indicates the number
 *   of bytes successfully transferred.  On a failure, a negated errno value
 *   is returned that indicates the nature of the failure:
 *
 *     EAGAIN - If devices NAKs the transfer (or NYET or other error where
 *              it may be appropriate to restart the entire transaction).
 *     EPERM  - If the endpoint stalls
 *     EIO    - On a TX or data toggle error
 *     EPIPE  - Overrun errors
 *
 * Assumptions:
 *   - Called from a single thread so no mutual exclusion is required.
 *   - Never called from an interrupt handler.
 *
 ****************************************************************************/

static ssize_t max3421e_transfer(FAR struct usbhost_driver_s *drvr,
                                 usbhost_ep_t ep, FAR uint8_t *buffer,
                                 size_t buflen)
{
  FAR struct max3421e_usbhost_s *priv =
    (FAR struct max3421e_usbhost_s *)drvr;
  FAR struct max3421e_chan_s *chan;
  ssize_t nbytes;
  int ret;

  DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
  DEBUGASSERT((intptr_t)ep >= 0 && (intptr_t)ep < MAX3421E_NHOST_CHANNELS);
  chan = &priv->chan[(intptr_t)ep];

  usbhost_vtrace2(MAX3421E_VTRACE2_TRANSFER, (unsigned int)ep, buflen);

  /* We must have exclusive access to the USB host hardware and structures */

  ret = nxrmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* Handle IN and OUT transfer differently */

  if (chan->in)
    {
      nbytes = max3421e_in_transfer(priv, chan, buffer, buflen);
    }
  else
    {
      nbytes = max3421e_out_transfer(priv, chan, buffer, buflen);
    }

  nxrmutex_unlock(&priv->lock);
  return nbytes;
}

/****************************************************************************
 * Name: max3421e_asynch
 *
 * Description:
 *   Process a request to handle a transfer descriptor.  This method will
 *   enqueue the transfer request and return immediately.  When the transfer
 *   completes, the callback will be invoked with the provided transfer.
 *   This method is useful for receiving interrupt transfers which may come
 *   infrequently.
 *
 *   Only one transfer may be queued; Neither this method nor the ctrlin or
 *   ctrlout methods can be called again until the transfer completes.
 *
 * Input Parameters:
 *   drvr     - The USB host driver instance obtained as a parameter from
 *              the call to the class create() method.
 *   ep       - The IN or OUT endpoint descriptor for the device endpoint
 *              on which to perform the transfer.
 *   buffer   - A buffer containing the data to be sent (OUT endpoint) or
 *              received (IN endpoint).  buffer must have been allocated
 *              using DRVR_ALLOC
 *   buflen   - The length of the data to be sent or received.
 *   callback - This function will be called when the transfer completes.
 *   arg      - The arbitrary parameter that will be passed to the callback
 *              function when the transfer completes.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure
 *
 * Assumptions:
 *   - Called from a single thread so no mutual exclusion is required.
 *   - Never called from an interrupt handler.
 *
 ****************************************************************************/

#ifdef CONFIG_USBHOST_ASYNCH
static int max3421e_asynch(FAR struct usbhost_driver_s *drvr,
                           usbhost_ep_t ep,
                           FAR uint8_t *buffer, size_t buflen,
                           usbhost_asynch_t callback, FAR void *arg)
{
  FAR struct max3421e_usbhost_s *priv =
    (FAR struct max3421e_usbhost_s *)drvr;
  FAR struct max3421e_chan_s *chan;
  int ret;

  DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
  DEBUGASSERT((intptr_t)ep >= 0 && ep < MAX3421E_NHOST_CHANNELS);
  chan = &priv->chan[(intptr_t)ep];

  usbhost_vtrace2(MAX3421E_VTRACE2_ASYNCH, (unsigned int)ep, buflen);

  /* We must have exclusive access to the USB host hardware and structures */

  ret = nxrmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* Handle IN and OUT transfer slightly differently */

  if (chan->in)
    {
      ret = max3421e_in_asynch(priv, chan, buffer, buflen, callback, arg);
    }
  else
    {
      ret = max3421e_out_asynch(priv, chan, buffer, buflen, callback, arg);
    }

  nxrmutex_unlock(&priv->lock);
  return ret;
}
#endif /* CONFIG_USBHOST_ASYNCH */

/****************************************************************************
 * Name: max3421e_cancel
 *
 * Description:
 *   Cancel a pending transfer on an endpoint.  Canceled synchronous or
 *   asynchronous transfer will complete normally with the error -ESHUTDOWN.
 *
 * Input Parameters:
 *   drvr - The USB host driver instance obtained as a parameter from the
 *          call to the class create() method.
 *   ep   - The IN or OUT endpoint descriptor for the device endpoint on
 *          which an asynchronous transfer should be transferred.
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure.
 *
 ****************************************************************************/

static int max3421e_cancel(FAR struct usbhost_driver_s *drvr,
                           usbhost_ep_t ep)
{
  FAR struct max3421e_usbhost_s *priv =
    (FAR struct max3421e_usbhost_s *)drvr;
  FAR struct max3421e_chan_s *chan;
  irqstate_t flags;

  DEBUGASSERT(priv != NULL);
  DEBUGASSERT((intptr_t)ep >= 0 && (intptr_t)ep < MAX3421E_NHOST_CHANNELS);
  chan = &priv->chan[(intptr_t)ep];

  usbhost_vtrace1(MAX3421E_VTRACE1_CANCEL, (intptr_t)ep);

  /* We need to disable interrupts to avoid race conditions with the
   * asynchronous completion of the transfer being canceled.
   */

  flags = enter_critical_section();

  /* Halt the channel */
#warning Missing logic
  UNUSED(chan);  /* For now */

  priv->result = -ESHUTDOWN;

  /* Is there a thread waiting for this transfer to complete? */

  if (priv->waiter != NULL)
    {
#ifdef CONFIG_USBHOST_ASYNCH
      /* Yes.. there should not also be a callback scheduled */

      DEBUGASSERT(priv->callback == NULL);
#endif

      /* Wake'em up! */

      nxsem_post(&priv->waitsem);
      priv->waiter = NULL;
    }

#ifdef CONFIG_USBHOST_ASYNCH
  /* No.. is an asynchronous callback expected when the transfer
   * completes?
   */

  else if (priv->callback)
    {
      usbhost_asynch_t callback;
      FAR void *arg;

      /* Extract the callback information */

      callback       = priv->callback;
      arg            = priv->arg;

      priv->callback = NULL;
      priv->arg      = NULL;
      priv->xfrd     = 0;

      /* Then perform the callback */

      callback(arg, -ESHUTDOWN);
    }
#endif

  leave_critical_section(flags);
  return OK;
}

/****************************************************************************
 * Name: max3421e_connect
 *
 * Description:
 *   New connections may be detected by an attached hub.  This method is the
 *   mechanism that is used by the hub class to introduce a new connection
 *   and port description to the system.
 *
 * Input Parameters:
 *   drvr      - The USB host driver instance obtained as a parameter from
 *               the call to the class create() method.
 *   hport     - The descriptor of the hub port that detected the connection
 *               related event
 *   connected - True: device connected; false: device disconnected
 *
 * Returned Value:
 *   On success, zero (OK) is returned. On a failure, a negated errno value
 *   is returned indicating the nature of the failure.
 *
 ****************************************************************************/

#ifdef CONFIG_USBHOST_HUB
static int max3421e_connect(FAR struct usbhost_driver_s *drvr,
                            FAR struct usbhost_hubport_s *hport,
                            bool connected)
{
  FAR struct max3421e_usbhost_s *priv =
    (FAR struct max3421e_usbhost_s *)drvr;
  irqstate_t flags;

  DEBUGASSERT(priv != NULL && hport != NULL);

  /* Set the connected/disconnected flag */

  hport->connected = connected;
  usbhost_vtrace2(MAX3421E_VTRACE2_HUB_CONNECTED, hport->port, connected);

  /* Report the connection event */

  flags = enter_critical_section();
  priv->hport = hport;
  if (priv->pscwait)
    {
      priv->pscwait = false;
      nxsem_post(&priv->pscsem);
    }

  leave_critical_section(flags);
  return OK;
}
#endif

/****************************************************************************
 * Name: max3421e_disconnect
 *
 * Description:
 *   Called by the class when an error occurs and driver has been
 *   disconnected.  The USB host driver should discard the handle to the
 *   class instance (it is stale) and not attempt any further interaction
 *   with the class driver instance (until a new instance is received from
 *   the create() method).  The driver should not called the class'
 *   disconnected() method.
 *
 * Input Parameters:
 *   drvr  - The USB host driver instance obtained as a parameter from the
 *           call to the class create() method.
 *   hport - The port from which the device is being disconnected.  Might be
 *           a port on a hub.
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *   - Only a single class bound to a single device is supported.
 *   - Never called from an interrupt handler.
 *
 ****************************************************************************/

static void max3421e_disconnect(FAR struct usbhost_driver_s *drvr,
                                FAR struct usbhost_hubport_s *hport)
{
  DEBUGASSERT(hport != NULL);
  hport->devclass = NULL;
}

/****************************************************************************
 * Initialization
 ****************************************************************************/

/****************************************************************************
 * Name: max3421e_busreset
 *
 * Description:
 *   Reset the USB host port.
 *
 * Input Parameters:
 *   priv -- USB host driver private data structure.
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void max3421e_busreset(FAR struct max3421e_usbhost_s *priv)
{
  /* Disable the SOF generator */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_MODE, USBHOST_MODE_SOFKAENAB, 0);

  /* Clear any pending bus event */

  max3421e_int_clear(priv, USBHOST_HIRQ_BUSEVENTIRQ);

  /* Perform the reset */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_HCTL, 0, USBHOST_HCTL_BUSRST);
  max3421e_int_wait(priv, USBHOST_HIRQ_BUSEVENTIRQ, 250);
}

/****************************************************************************
 * Name: max3421e_startsof
 *
 * Description:
 *   Called after bus reset.  Determine bus speed and restart SOFs.
 *
 * Input Parameters:
 *   priv -- USB host driver private data structure.
 *
 * Returned Value:
 *   OK if successfully connect; -ENODEV if not connected.
 *
 ****************************************************************************/

static int max3421e_startsof(FAR struct max3421e_usbhost_s *priv)
{
  uint8_t clrbits;
  uint8_t setbits;
  uint8_t regval;
  bool lowspeed;

  /* Check if we are already in low- or full-speed mode */

  regval = max3421e_getreg(priv, MAX3421E_USBHOST_MODE);
  lowspeed = ((regval & USBHOST_MODE_SPEED) != 0);

  /* Enable SAMPLEBUS */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_HCTL, 0,
                     USBHOST_HCTL_BUSSAMPLE);

  while ((max3421e_getreg(priv, MAX3421E_USBHOST_HCTL) &
          USBHOST_HCTL_BUSSAMPLE) == 0)
    {
      nxsig_usleep(5);
    }

  /* Check for low- or full-speed and start SOF (actually already started
   * by max3421e_busreset).
   */

  clrbits = 0;
  setbits = USBHOST_MODE_HOST | USBHOST_MODE_SOFKAENAB |
            USBHOST_MODE_DMPULLD | USBHOST_MODE_DPPULLDN;

  regval = max3421e_getreg(priv, MAX3421E_USBHOST_HRSL);
  switch (regval & (USBHOST_HRSL_KSTATUS | USBHOST_HRSL_JSTATUS))
    {
      default:
      case (USBHOST_HRSL_KSTATUS | USBHOST_HRSL_JSTATUS):

        /* Invalid state */

        usbhost_trace1(MAX3421E_TRACE1_BAD_JKSTATE, regval);

        /* Fall through */

      case 0:

        /* 0:  Not connected */

        return -ENODEV;

      case USBHOST_HRSL_KSTATUS:

        /* J=0, K=1: low-speed in full-speed (or vice versa) */

        if (lowspeed)
          {
            /* Full speed in low speed */

            clrbits |= USBHOST_MODE_SPEED;
          }
        else
          {
            /* Low speed in full speed */

            setbits |= USBHOST_MODE_SPEED;
          }
        break;

      case USBHOST_HRSL_JSTATUS:

        /* J=1,K=0: full-speed in full-speed (or vice versa) */

        if (lowspeed)
          {
            /* Low speed in low speed */

            setbits |= USBHOST_MODE_SPEED;
          }
        else
          {
            /* Full speed in full speed */

            clrbits |= USBHOST_MODE_SPEED;
          }

        break;
    }

  /* Restart the SOF generator */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_MODE, clrbits, setbits);

  /* Wait for the first SOF received and 20ms has passed */

  max3421e_int_wait(priv, USBHOST_HIRQ_FRAMEIRQ, 0);
  nxsig_usleep(20 * 1000);
  return OK;
}

/****************************************************************************
 * Name: max3421e_sw_initialize
 *
 * Description:
 *   One-time setup of the host driver state structure.
 *
 * Input Parameters:
 *   priv  -- USB host driver private data structure.
 *   conn  -- Custom USB host connection structure.
 *   lower -- The lower half driver instance.
 *
 * Returned Value:
 *   None.
 *
 ****************************************************************************/

static inline int max3421e_sw_initialize(FAR struct max3421e_usbhost_s *priv,
              FAR struct max3421e_connection_s *conn,
              FAR const struct max3421e_lowerhalf_s *lower)
{
  FAR struct usbhost_driver_s *drvr;
  FAR struct usbhost_hubport_s *hport;
  int ret;
  int i;

  /* Initialize the device operations */

  drvr                 = &priv->drvr;
  drvr->ep0configure   = max3421e_ep0configure;
  drvr->epalloc        = max3421e_epalloc;
  drvr->epfree         = max3421e_epfree;
  drvr->alloc          = max3421e_alloc;
  drvr->free           = max3421e_free;
  drvr->ioalloc        = max3421e_ioalloc;
  drvr->iofree         = max3421e_iofree;
  drvr->ctrlin         = max3421e_ctrlin;
  drvr->ctrlout        = max3421e_ctrlout;
  drvr->transfer       = max3421e_transfer;
#ifdef CONFIG_USBHOST_ASYNCH
  drvr->asynch         = max3421e_asynch;
#endif
  drvr->cancel         = max3421e_cancel;
#ifdef CONFIG_USBHOST_HUB
  drvr->connect        = max3421e_connect;
#endif
  drvr->disconnect     = max3421e_disconnect;

  /* Initialize the public port representation */

  hport                = &priv->rhport.hport;
  hport->drvr          = drvr;
#ifdef CONFIG_USBHOST_HUB
  hport->parent        = NULL;
#endif
  hport->ep0           = 0;
  hport->speed         = USB_SPEED_FULL;

  /* Initialize function address generation logic */

  usbhost_devaddr_initialize(&priv->devgen);
  priv->rhport.pdevgen = &priv->devgen;

  /* Initialize semaphores */

  nxsem_init(&priv->pscsem,  0, 0);
  nxsem_init(&priv->waitsem,  0, 0);

  /* Initialize lock */

  nxrmutex_init(&priv->lock);

  /* Initialize the driver state data */

  priv->lower     = lower;
  priv->smstate   = SMSTATE_DETACHED;
  priv->connected = false;
  priv->irqset    = 0;
  priv->change    = false;

  /* Put all of the channels back in their initial, allocated state */

  memset(priv->chan, 0,
         MAX3421E_NHOST_CHANNELS * sizeof(struct max3421e_chan_s));

  /* Initialize each channel */

  for (i = 0; i < MAX3421E_NHOST_CHANNELS; i++)
    {
      FAR struct max3421e_chan_s *chan = &priv->chan[i];

      chan->chidx = i;
    }

  /* Initialize the connection structure */

  conn->conn.wait      = max3421e_wait;
  conn->conn.enumerate = max3421e_enumerate;
  conn->priv           = priv;

  /* Attach USB host controller interrupt handler */

  ret = lower->attach(lower, max3421e_interrupt, priv);
  if (ret < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_IRQATTACH_FAIL, 0);
      return ret;
    }

  return OK;
}

/****************************************************************************
 * Name: max3421e_hw_initialize
 *
 * Description:
 *   One-time setup of the host controller hardware for normal operations.
 *
 * Input Parameters:
 *   priv -- USB host driver private data structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno value on failure.
 *
 ****************************************************************************/

static inline int max3421e_hw_initialize(FAR struct max3421e_usbhost_s *priv)
{
  uint8_t revision;
  uint8_t regval;
  int ret;

  /* Get exclusive access to the SPI bus */

  max3421e_lock(priv);

  /* Configure full duplex SPI, level or edge-active, rising- or falling
   * edge interrupt.
   *
   * NOTE:  Initially, the MAX3421E operations in half-duplex mode.  MISO is
   * tristated and there is no status response to commands.  Writes are not
   * effected:  The MISO pin continues to be high impedance and the master
   * continues to drive MOSI.
   *
   * For reads, however, after the 8-bit command, the max3421e starts driving
   * the MOSI pin.  The  master must turn off its driver to the MOSI pin to
   * avoid contention.
   */

  regval  = priv->lower->intconfig;
  regval &= (USBHOST_PINCTL_INTLEVEL | USBHOST_PINCTL_POSINT);
  regval |= USBHOST_PINCTL_FDUPSPI;
  max3421e_putreg(priv, MAX3421E_USBHOST_PINCTL, regval);

  /* Reset the MAX3421E by toggling the CHIPRES bit in the USBCTRL register.
   *
   * NOTE: The bits that control the SPI interface are not changed by
   * CHIPRES: FDUPSPI, INTLEVEL, and POSINT.
   */

  max3421e_putreg(priv, MAX3421E_USBHOST_USBCTL, USBHOST_USBCTL_CHIPRES);
  max3421e_putreg(priv, MAX3421E_USBHOST_USBCTL, 0);

  /* Wait for the oscillator to become stable */

  while ((max3421e_getreg(priv, MAX3421E_USBHOST_USBIRQ) &
                          USBHOST_USBIRQ_OSCOKIRQ) == 0)
    {
    }

  /* Disable interrupts, clear pending interrupts, and reset the interrupt
   * state.
   */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_CPUCTL, USBHOST_CPUCTL_IE, 0);
  max3421e_putreg(priv, MAX3421E_USBHOST_HIEN, 0);
  max3421e_int_clear(priv, 0xff);
  priv->irqset  = 0;

  /* Configure as full-speed USB host */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_MODE,
                     USBHOST_MODE_SPEED | USBHOST_MODE_SOFKAENAB,
                     USBHOST_MODE_HOST | USBHOST_MODE_DMPULLD |
                     USBHOST_MODE_DPPULLDN);

  /* Clear and enable the connection detected (CONDIRQ) interrupt */

  max3421e_int_clear(priv, USBHOST_HIRQ_CONNIRQ);
  max3421e_int_enable(priv, USBHOST_HIRQ_CONNIRQ);

  /* Enable MAX3412E interrupts */

  max3421e_modifyreg(priv, MAX3421E_USBHOST_CPUCTL, 0, USBHOST_CPUCTL_IE);

  usbhost_vtrace1(MAX3421E_VTRACE1_INITIALIZED, 0);

  revision = max3421e_getreg(priv, MAX3421E_USBHOST_REVISION);
  if (revision != USBHOST_REVISION)
    {
      usbhost_trace1(MAX3421E_TRACE1_BADREVISION, revision);
      max3421e_unlock(priv);
      return -ENODEV;
    }

  /* Perform a bus reset to reconnect after a power down */

  ret = max3421e_connected(priv);
  if (ret < 0)
    {
      /* Nothing connected. */

      max3421e_disconnected(priv);
    }

  max3421e_unlock(priv);
  return OK;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: max3421e_usbhost_initialize
 *
 * Description:
 *   Initialize MAX3421E as USB host controller.
 *
 * Input Parameters:
 *   lower - The interface to the lower half driver
 *
 * Returned Value:
 *   And instance of the USB host interface.  The controlling task should
 *   use this interface to (1) call the wait() method to wait for a device
 *   to be connected, and (2) call the enumerate() method to bind the device
 *   to a class driver.
 *
 * Assumptions:
 * - This function should called in the initialization sequence in order
 *   to initialize the USB device functionality.
 * - Class drivers should be initialized prior to calling this function.
 *   Otherwise, there is a race condition if the device is already connected.
 *
 ****************************************************************************/

FAR struct usbhost_connection_s *
max3421e_usbhost_initialize(FAR const struct max3421e_lowerhalf_s *lower)
{
  FAR struct usbhost_alloc_s *alloc;
  FAR struct max3421e_usbhost_s *priv;
  FAR struct max3421e_connection_s *conn;
  int ret;

  DEBUGASSERT(lower != NULL && lower->spi != NULL && lower->attach != NULL &&
              lower->attach != NULL && lower->acknowledge != NULL &&
              lower->power != NULL);

  /* Allocate and instance of the MAX4321E state structure */

  alloc = (FAR struct usbhost_alloc_s *)
    kmm_zalloc(sizeof(struct usbhost_alloc_s));

  if (alloc < 0)
    {
      usbhost_trace1(MAX3421E_TRACE1_ALLOC_FAIL, 0);
      return NULL;
    }

  priv = &alloc->priv;
  conn = &alloc->conn;

  /* Initialize the state of the host driver */

  ret = max3421e_sw_initialize(priv, conn, lower);
  if (ret < 0)
    {
      goto errout_with_alloc;
    }

  /* Initialize the MAX3421E, putting it into full operational state. */

  ret = max3421e_hw_initialize(priv);
  if (ret < 0)
    {
      goto errout_with_alloc;
    }

  /* Drive Vbus +5V (the smoke test). */

  lower->power(lower, true);

  /* Enable host interrupts */

  lower->enable(lower, true);
  return &conn->conn;

errout_with_alloc:
  kmm_free(alloc);
  return NULL;
}

/****************************************************************************
 * Name: usbhost_trformat1 and usbhost_trformat2
 *
 * Description:
 *   This interface must be provided by platform specific logic that knows
 *   the HCDs encoding of USB trace data.
 *
 *   Given an 9-bit index, return a format string suitable for use with,
 *   say, printf.  The returned format is expected to handle two unsigned
 *   integer values.
 *
 ****************************************************************************/

#ifdef HAVE_USBHOST_TRACE
FAR const char *usbhost_trformat1(uint16_t id)
{
  int ndx = TRACE1_INDEX(id);

  if (ndx < TRACE1_NSTRINGS)
    {
      return g_trace1[ndx].string;
    }

  return NULL;
}

FAR const char *usbhost_trformat2(uint16_t id)
{
  int ndx = TRACE2_INDEX(id);

  if (ndx < TRACE2_NSTRINGS)
    {
      return g_trace2[ndx].string;
    }

  return NULL;
}
#endif

#endif /* CONFIG_USBHOST_MAX3421E */