From 042da0c84ccdc86ff3085f2a8f18fe33fa7b65c7 Mon Sep 17 00:00:00 2001 From: patacongo Date: Tue, 11 Jan 2011 01:41:06 +0000 Subject: Improve endpoint management git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3240 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/ChangeLog | 3 +- nuttx/Documentation/NuttX.html | 71 +--- nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h | 8 +- nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c | 244 +++++++------- nuttx/configs/nucleus2g/README.txt | 4 +- nuttx/configs/olimex-lpc1766stk/README.txt | 4 +- nuttx/drivers/usbhost/usbhost_storage.c | 511 ++++++++++++++++------------- nuttx/include/nuttx/usb/usbhost.h | 90 ++++- 8 files changed, 520 insertions(+), 415 deletions(-) diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index d6a03b732..1d781751f 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -1405,7 +1405,7 @@ * include/nuttx/spi.h -- the SPI_SETBITS macro was calling the setmode method. This is a very important bug-fix in some usages. -5.16 2011-xx-xx Gregory Nutt +5.16 2011-01-10 Gregory Nutt * include/nuttx/usb -- Created new directory. Moved all usb-related header files to this new directory. Created a skeleton for a new USB host header @@ -1418,3 +1418,4 @@ * drivers/usbhost -- Add a USB host class driver for the (Bulk-Only) USB Mass Storage Class. +5.17 2011-xx-xx Gregory Nutt diff --git a/nuttx/Documentation/NuttX.html b/nuttx/Documentation/NuttX.html index 34bdedec5..011f69b63 100644 --- a/nuttx/Documentation/NuttX.html +++ b/nuttx/Documentation/NuttX.html @@ -8,7 +8,7 @@

NuttX RTOS

-

Last Updated: January 9, 2011

+

Last Updated: January 10, 2011

@@ -1969,50 +1969,18 @@ Other memory:
    -5.15 2010-12-12 Gregory Nutt <spudmonkey@racsa.co.cr>
    -
    -    * net/uip/uip_tcpaddsend.c and net/send.c -- Another place where the TCP sequence
    -      number problem "fixed" in 5.14 might occur.
    -    * net/send.c -- Check if the destination IP address is in the ARP table.  If
    -      not, then don't consider the packet sent.  It won't be, an ARP packet will go
    -      out instead.  This improves behavior, for example, on the first GET request
    -      from a browser.
    -    * arch/arm/src/lpc17xx/lpc17_emacram.h and lpc17_allocateheap.c -- The Ethernet
    -      logic was using all of AHB SRAM Bank0 for Ethernet packet buffers (16Kb).  An
    -      option was added to limit the amount of SRAM used for packet buffering and to
    -      re-use any extra Bank0 memory for heap.  configs/olimex-lpc1766stk/nettest
    -      now uses only 8Kb at the beginning of Bank0; the 8Kb at the end of Bank0 is
    -      included in the heap
    -    * arch/arm/src/lpc17xx/lpc17_ssp.c -- Fix compilation errors when SSP1 is
    -      selected.
    -    * configs/olimex-lpc1766stk/nsh -- Enable network and SD/MMC card support in
    -      NSH.  Networking and telnetd interface as well as SPI-based microSD are
    -      now functional.
    -    * examples/nsh/nsh_netinit.c -- Fix NSH bug.  If CONFIG_NET is selected, but
    -      CONFIG_EXAMPLES_NSH_TELNETD is not selected, then the network is never
    -      initialized and bad things happen if you try to ping.
    -    * drivers/lcd -- Add header files for the Phillips PCF8833 LCD controller and
    -      for the Epson S1D15G10 LCD controller.  A driver for the Nokia 6100 LCD is
    -      coming.
    -    * include/nuttx/spi.h and almost all other SPI files -- Added an optional
    -      cmddata() method to the SPI interface.  Some devices require an additional
    -      out-of-band bit to specify if the next word sent to the device is a command
    -      or data. This is typical, for example, in "9-bit" displays where the 9th bit
    -      is the CMD/DATA bit. The cmddata method provides selection of command or data.
    -    * drivers/lcd/p14201.c -- Now uses the cmddata() method of the SPI interface.
    -    * arch/arm/src/lpc17xx/lpc17_usbdev.c -- LPC17xx USB driver now appears to
    -      to be fully functional.  examples/usbstorage configuration verified (the
    -      examples/usbserial configuration is untested).
    -    * drivers/usbdev/usbserial.c and usbstorage.c -- All USB class drivers need
    -      to call DEV_CONNECT() when they are ready to be enumerated.  That is,
    -      (1) initially when bound to the USB driver, and (2) after a USB reset.
    -    * drivers/lcd/nokia6100.c -- A driver for the Nokia 6100 LCD.  This driver
    -      has not be verified as of the initial check-in.
    -    * configs/olimex-lpc1766stk/nx -- A NX graphics configuration for the Olimex
    -      LPC1766-STK board using the Nokia 6100 LCD driver.  This configuration has
    -      not been verified as of the initial check-in.
    -    * include/nuttx/spi.h -- the SPI_SETBITS macro was calling the setmode method.
    -      This is a very important bug-fix in some usages.
    +nuttx-5.16 2011-01-10 Gregory Nutt <spudmonkey@racsa.co.cr>
    +
    +    * include/nuttx/usb -- Created new directory.  Moved all usb-related header
    +      files to this new directory.  Created a skeleton for a new USB host header
    +      file
    +    * drivers/usbhost -- Add USB host "registry" where connect devices can be
    +      matched with the correct USB class driver.
    +    * arc/arc/src/lpc17xx/lpc17_usbhost.c -- Add a simple USB host driver for
    +      the NXP lpc17xx.
    +    * drivers/usbhost -- Add generic USB device enumeration logic.
    +    * drivers/usbhost -- Add a USB host class driver for the (Bulk-Only) USB
    +      Mass Storage Class.
     
     pascal-2.0 2009-12-21 Gregory Nutt <spudmonkey@racsa.co.cr>
     
    @@ -2042,18 +2010,7 @@ buildroot-1.8 2009-12-21 <spudmonkey@racsa.co.cr>
     
     
     
      -nuttx-5.16 2011-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr>
      -
      -    * include/nuttx/usb -- Created new directory.  Moved all usb-related header
      -      files to this new directory.  Created a skeleton for a new USB host header
      -      file
      -    * drivers/usbhost -- Add USB host "registry" where connect devices can be
      -      matched with the correct USB class driver.
      -    * arc/arc/src/lpc17xx/lpc17_usbhost.c -- Add a simple USB host driver for
      -      the NXP lpc17xx.
      -    * drivers/usbhost -- Add generic USB device enumeration logic.
      -    * drivers/usbhost -- Add a USB host class driver for the (Bulk-Only) USB
      -      Mass Storage Class.
      +nuttx-5.17 2011-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr>
       
       pascal-2.1 2010-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr>
       
      diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h b/nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h
      index d2fc11248..0e9f81ecb 100755
      --- a/nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h
      +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h
      @@ -214,10 +214,10 @@
       
       /* Finally, use the remainder of the allocated OHCI for IO buffers */
       
      -#define LPC17_IOBUFFERS     ((LPC17_OHCIRAM_END - LPC17_IOFREE_BASE) / CONFIG_USBHOST_IOBUFSIZE)
      -
      -#if LPC17_IOBUFFERS < 1
      -#  warning "No IO buffers allocated"
      +#if CONFIG_USBHOST_IOBUFSIZE > 0
      +#  define LPC17_IOBUFFERS   ((LPC17_OHCIRAM_END - LPC17_IOFREE_BASE) / CONFIG_USBHOST_IOBUFSIZE)
      +#else
      +#  define LPC17_IOBUFFERS 0
       #endif
       
       /************************************************************************************
      diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c b/nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c
      index 950a0ffe7..917ecd2e5 100755
      --- a/nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c
      +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c
      @@ -71,15 +71,13 @@
        * Definitions
        *******************************************************************************/
       
      -/* I think it is the case that all I/O buffers must lie in AHB SRAM because of
      - * the OHCI DMA.  But this definition has here so that I can experiment later
      - * to see if this really required.
      +/* All I/O buffers must lie in AHB SRAM because of the OHCI DMA. It might be
      + * okay if no I/O buffers are used *IF* the application can guarantee that all
      + * end-user I/O buffers reside in AHB SRAM.
        */
       
      -#define CONFIG_UBHOST_AHBIOBUFFERS 1
      -
      -#if defined(CONFIG_UBHOST_AHBIOBUFFERS) && LPC17_IOBUFFERS < 1
      -#  error "No IO buffers allocated"
      +#if LPC17_IOBUFFERS < 1
      +#  warning "No IO buffers allocated"
       #endif
       
       /* Frame Interval / Periodic Start */
      @@ -206,11 +204,9 @@ static void lpc17_putle16(uint8_t *dest, uint16_t val);
       
       /* Descriptor helper functions *************************************************/
       
      -static struct ohci_ed_s *lpc17_edalloc(struct lpc17_usbhost_s *priv);
      -static void lpc17_edfree(struct lpc17_usbhost_s *priv, struct ohci_ed_s *ed);
       static uint8_t *lpc17_tdalloc(struct lpc17_usbhost_s *priv);
       static void lpc17_tdfree(struct lpc17_usbhost_s *priv, uint8_t *buffer);
      -#ifdef CONFIG_UBHOST_AHBIOBUFFERS
      +#if LPC17_IOBUFFERS > 0
       static uint8_t *lpc17_ioalloc(struct lpc17_usbhost_s *priv);
       static void lpc17_iofree(struct lpc17_usbhost_s *priv, uint8_t *buffer);
       #endif
      @@ -230,6 +226,9 @@ static int lpc17_wait(FAR struct usbhost_driver_s *drvr, bool connected);
       static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr);
       static int lpc17_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr,
                                     uint16_t maxpacketsize);
      +static int lpc17_epalloc(FAR struct usbhost_driver_s *drvr,
      +                         const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep);
      +static int lpc17_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep);
       static int lpc17_alloc(FAR struct usbhost_driver_s *drvr,
                              FAR uint8_t **buffer, FAR size_t *maxlen);
       static int lpc17_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer);
      @@ -239,8 +238,7 @@ static int lpc17_ctrlin(FAR struct usbhost_driver_s *drvr,
       static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
                                FAR const struct usb_ctrlreq_s *req,
                                FAR const uint8_t *buffer);
      -static int lpc17_transfer(FAR struct usbhost_driver_s *drvr,
      -                          FAR struct usbhost_epdesc_s *ed,
      +static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
                                 FAR uint8_t *buffer, size_t buflen);
       static void lpc17_disconnect(FAR struct usbhost_driver_s *drvr);
         
      @@ -266,6 +264,8 @@ static struct lpc17_usbhost_s g_usbhost =
             .wait         = lpc17_wait,
             .enumerate    = lpc17_enumerate,
             .ep0configure = lpc17_ep0configure,
      +      .epalloc      = lpc17_epalloc,
      +      .epfree       = lpc17_epfree,
             .alloc        = lpc17_alloc,
             .free         = lpc17_free,
             .ctrlin       = lpc17_ctrlin,
      @@ -280,7 +280,7 @@ static struct lpc17_usbhost_s g_usbhost =
       
       static struct lpc17_edlist_s  *g_edfree;
       static struct lpc17_buflist_s *g_tdfree;
      -#ifdef CONFIG_UBHOST_AHBIOBUFFERS
      +#if LPC17_IOBUFFERS > 0
       static struct lpc17_buflist_s *g_iofree;
       #endif
       
      @@ -464,39 +464,6 @@ static void lpc17_putle16(uint8_t *dest, uint16_t val)
         dest[1] = val >> 8;
       }
       
      -/*******************************************************************************
      - * Name: lpc17_edalloc
      - *
      - * Description:
      - *   Allocate an ED from the free list
      - *
      - *******************************************************************************/
      -
      -static struct ohci_ed_s *lpc17_edalloc(struct lpc17_usbhost_s *priv)
      -{
      -  struct ohci_ed_s *ret = (struct ohci_ed_s *)g_edfree;
      -  if (ret)
      -    {
      -      g_edfree = ((struct lpc17_edlist_s*)ret)->flink;
      -    }
      -  return ret;
      -}
      -
      -/*******************************************************************************
      - * Name: lpc17_edfree
      - *
      - * Description:
      - *   Return an ED to the free list
      - *
      - *******************************************************************************/
      -
      -static void lpc17_edfree(struct lpc17_usbhost_s *priv, struct ohci_ed_s *ed)
      -{
      -  struct lpc17_edlist_s *edfree = (struct lpc17_edlist_s *)ed;
      -  edfree->flink                 = g_edfree;
      -  g_edfree                      = edfree;
      -}
      -
       /*******************************************************************************
        * Name: lpc17_tdalloc
        *
      @@ -550,7 +517,7 @@ static void lpc17_tdfree(struct lpc17_usbhost_s *priv, uint8_t *buffer)
        *
        *******************************************************************************/
       
      -#ifdef CONFIG_UBHOST_AHBIOBUFFERS
      +#if LPC17_IOBUFFERS > 0
       static uint8_t *lpc17_ioalloc(struct lpc17_usbhost_s *priv)
       {
         uint8_t *ret = (uint8_t *)g_iofree;
      @@ -570,7 +537,7 @@ static uint8_t *lpc17_ioalloc(struct lpc17_usbhost_s *priv)
        *
        *******************************************************************************/
       
      -#ifdef CONFIG_UBHOST_AHBIOBUFFERS
      +#if LPC17_IOBUFFERS > 0
       static void lpc17_iofree(struct lpc17_usbhost_s *priv, uint8_t *buffer)
       {
         struct lpc17_buflist_s *iofree = (struct lpc17_buflist_s *)buffer;
      @@ -1067,6 +1034,104 @@ static int lpc17_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcadd
         return OK;
       }
       
      +/************************************************************************************
      + * Name: lpc17_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 desciptor.
      + *
      + * Returned Values:
      + *   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 lpc17_epalloc(FAR struct usbhost_driver_s *drvr,
      +                         const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep)
      +{
      +  struct ohci_ed_s *ed;
      +  int ret = -ENOMEM;
      +
      +  DEBUGASSERT(epdesc && ep);
      +
      +  /* Take the next ED from the beginning of the free list */
      +
      +  ed = (struct ohci_ed_s *)g_edfree;
      +  if (ed)
      +    {
      +      /* Remove the ED from the freelist */
      +
      +      g_edfree = ((struct lpc17_edlist_s*)ed)->flink;
      +
      +      /* Configure the endpoint descriptor. */
      + 
      +      lpc17_edinit(ed);
      +      ed->ctrl = (uint32_t)(epdesc->funcaddr)     << ED_CONTROL_FA_SHIFT | 
      +                 (uint32_t)(epdesc->addr)         << ED_CONTROL_EN_SHIFT |
      +                 (uint32_t)(epdesc->mxpacketsize) << ED_CONTROL_MPS_SHIFT;
      +
      +      /* Get the direction of the endpoint */
      +
      +      if (epdesc->in != 0)
      +        {
      +          ed->ctrl |= ED_CONTROL_D_IN;
      +        }
      +      else
      +        {
      +          ed->ctrl |= ED_CONTROL_D_OUT;
      +        }
      +
      +      /* Return an opaque reference to the ED */
      +
      +      *ep      = (usbhost_ep_t)ed;
      +      ret      = OK;
      +    }
      +  return ret;
      +}
      +
      +/************************************************************************************
      + * Name: lpc17_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 endpint to be freed.
      + *
      + * Returned Values:
      + *   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 lpc17_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
      +{
      +  struct lpc17_edlist_s *ed = (struct lpc17_edlist_s *)ep;
      +
      +  DEBUGASSERT(ed);
      +
      +  /* Put the ED back into the free list */
      +
      +  ed->flink = g_edfree;
      +  g_edfree  = ed;
      +  return OK;
      +}
      +
       /*******************************************************************************
        * Name: lpc17_alloc
        *
      @@ -1251,7 +1316,7 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
        * Input Parameters:
        *   drvr - The USB host driver instance obtained as a parameter from the call to
        *      the class create() method.
      - *   ed - The IN or OUT endpoint descriptor for the device endpoint on which to
      + *   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
      @@ -1268,26 +1333,32 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr,
        *
        *******************************************************************************/
       
      -static int lpc17_transfer(FAR struct usbhost_driver_s *drvr,
      -                          FAR struct usbhost_epdesc_s *ep,
      +static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
                                 FAR uint8_t *buffer, size_t buflen)
       {
         struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
      -  struct ohci_ed_s *ed = NULL;
      +  struct ohci_ed_s *ed = (struct ohci_ed_s *)ep;
         uint32_t dirpid;
         uint32_t regval;
      -#ifdef CONFIG_UBHOST_AHBIOBUFFERS
      +#if LPC17_IOBUFFERS > 0
         uint8_t *origbuf = NULL;
       #endif
      +  bool in;
         int ret;
       
      -  DEBUGASSERT(drvr && ep && buffer && buflen > 0);
      +  DEBUGASSERT(priv && ed && buffer && buflen > 0);
      +
      +  in = (ed->ctrl  & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN;
         uvdbg("EP%d %s toggle:%d maxpacket:%d buflen:%d\n",
      -        ep->addr, ep->in ? "IN" : "OUT", ep->toggle, ep->mxpacketsize, buflen);
      +        (ed->ctrl  & ED_CONTROL_EN_MASK)  >> ED_CONTROL_EN_SHIFT, 
      +        in ? "IN" : "OUT",
      +        (ed->headp & ED_HEADP_C)          != 0 ? 1 : 0,
      +        (ed->ctrl  & ED_CONTROL_MPS_MASK) >> ED_CONTROL_MPS_SHIFT, 
      +        buflen);
       
         /* Allocate an IO buffer if the user buffer does not lie in AHB SRAM */
       
      -#ifdef CONFIG_UBHOST_AHBIOBUFFERS
      +#if LPC17_IOBUFFERS > 0
         if ((uintptr_t)buffer < LPC17_SRAM_BANK0 ||
             (uintptr_t)buffer >= (LPC17_SRAM_BANK0 + LPC17_BANK0_SIZE + LPC17_BANK1_SIZE))
           {
      @@ -1318,7 +1389,7 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr,
              * way around this copy.
              */
       
      -      if (ep->in == 0)
      +      if (!in)
               {
                 memcpy(buffer, origbuf, buflen);
               }
      @@ -1336,45 +1407,17 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr,
             goto errout;
           }
       
      -  /* Allocate an ED */
      -
      -  ed = lpc17_edalloc(priv);
      -  if (!ed)
      -    {
      -      udbg("ED allocation failed\n");
      -      ret = -ENOMEM;
      -      goto errout;
      -    }
      -
      -  /* Format the endpoint descriptor.  This could be a lot simpler if
      -   * the OHCI ED structure were exposed outside of the driver.
      -   */
      - 
      -  lpc17_edinit(ed);
      -  ed->ctrl = (uint32_t)(ep->funcaddr)     << ED_CONTROL_FA_SHIFT | 
      -             (uint32_t)(ep->addr)         << ED_CONTROL_EN_SHIFT |
      -             (uint32_t)(ep->mxpacketsize) << ED_CONTROL_MPS_SHIFT;
      -
         /* Get the direction of the endpoint */
       
      -  if (ep->in != 0)
      +  if (in)
           {
      -      ed->ctrl |= ED_CONTROL_D_IN;
             dirpid    = GTD_STATUS_DP_IN;
           }
         else
           {
      -      ed->ctrl |= ED_CONTROL_D_OUT;
             dirpid    = GTD_STATUS_DP_OUT;
           }
       
      -  /* Set/restore the toggle carry bit */
      -
      -  if (ep->toggle)
      -    {
      -      ed->headp = ED_HEADP_C;
      -    }
      -
         /* Then enqueue the transfer */
       
         priv->tdstatus = TD_CC_NOERROR;
      @@ -1420,24 +1463,10 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr,
             ret = -EIO;
           }
       
      -  /* Save the toggle carry bit.  This bit is updated each time that an
      -   * ED is retired.  This could be a lot simpler if the OHCI ED structure
      -   * were exposed outside of the driver.
      -   */
      -
      -  if ((ed->headp & ED_HEADP_C) != 0)
      -    {
      -      ep->toggle = 1;
      -    }
      -  else
      -    {
      -      ep->toggle = 0;
      -    }
      -
       errout:
         /* Free any temporary IO buffers */
       
      -#ifdef CONFIG_UBHOST_AHBIOBUFFERS
      +#if LPC17_IOBUFFERS > 0
         if (buffer && origbuf)
           {
             /* If this is an IN transaction, get the user data from the AHB
      @@ -1446,7 +1475,7 @@ errout:
              * way around this copy.
              */
       
      -      if (ep->in != 0 && ret == OK)
      +      if (in && ret == OK)
               {
                 memcpy(origbuf, buffer, buflen);
               }
      @@ -1457,13 +1486,6 @@ errout:
           }
       #endif
       
      -  /* Free the endpoint descriptor */
      -
      -  if (ed)
      -    {
      -      lpc17_edfree(priv, ed);
      -    }
      -
         return ret;
       }
       
      @@ -1644,7 +1666,7 @@ FAR struct usbhost_driver_s *usbhost_initialize(int controller)
           {
             /* Put the ED in a free list */
       
      -      lpc17_edfree(priv, &EDFREE[i]);
      +      lpc17_epfree(&priv->drvr, (usbhost_ep_t)&EDFREE[i]);
           }
       
         /* Initialize user-configurable TD buffers */
      @@ -1658,7 +1680,7 @@ FAR struct usbhost_driver_s *usbhost_initialize(int controller)
             buffer += CONFIG_USBHOST_TDBUFSIZE;
           }
       
      -#ifdef CONFIG_UBHOST_AHBIOBUFFERS
      +#if LPC17_IOBUFFERS > 0
         /* Initialize user-configurable IO buffers */
       
         buffer = IOFREE;
      diff --git a/nuttx/configs/nucleus2g/README.txt b/nuttx/configs/nucleus2g/README.txt
      index 21b04a08f..4d88654c5 100755
      --- a/nuttx/configs/nucleus2g/README.txt
      +++ b/nuttx/configs/nucleus2g/README.txt
      @@ -443,7 +443,9 @@ Nucleus 2G Configuration Options
           CONFIG_USBHOST_TDBUFSIZE
             Size of one transfer descriptor buffer
           CONFIG_USBHOST_IOBUFSIZE
      -      Size of one end-user I/O buffer
      +      Size of one end-user I/O buffer.  This can be zero if the
      +      application can guarantee that all end-user I/O buffers
      +      reside in AHB SRAM.
       
       USB Host Configuration
       ^^^^^^^^^^^^^^^^^^^^^^
      diff --git a/nuttx/configs/olimex-lpc1766stk/README.txt b/nuttx/configs/olimex-lpc1766stk/README.txt
      index cd7a1ca49..eae9685ea 100755
      --- a/nuttx/configs/olimex-lpc1766stk/README.txt
      +++ b/nuttx/configs/olimex-lpc1766stk/README.txt
      @@ -689,7 +689,9 @@ Olimex LPC1766-STK Configuration Options
           CONFIG_USBHOST_TDBUFSIZE
             Size of one transfer descriptor buffer
           CONFIG_USBHOST_IOBUFSIZE
      -      Size of one end-user I/O buffer
      +      Size of one end-user I/O buffer.  This can be zero if the
      +      application can guarantee that all end-user I/O buffers
      +      reside in AHB SRAM.
       
       USB Host Configuration
       ^^^^^^^^^^^^^^^^^^^^^^
      diff --git a/nuttx/drivers/usbhost/usbhost_storage.c b/nuttx/drivers/usbhost/usbhost_storage.c
      index c13e32bb8..e0f4dc4ad 100644
      --- a/nuttx/drivers/usbhost/usbhost_storage.c
      +++ b/nuttx/drivers/usbhost/usbhost_storage.c
      @@ -129,8 +129,8 @@ struct usbhost_state_s
         struct work_s           work;         /* For interacting with the worker thread */
         FAR uint8_t            *tdbuffer;     /* The allocated transfer descriptor buffer */
         size_t                  tdbuflen;     /* Size of the allocated transfer buffer */
      -  struct usbhost_epdesc_s bulkin;       /* Bulk IN endpoint */
      -  struct usbhost_epdesc_s bulkout;      /* Bulk OUT endpoint */
      +  usbhost_ep_t            bulkin;       /* Bulk IN endpoint */
      +  usbhost_ep_t            bulkout;      /* Bulk OUT endpoint */
       };
       
       /****************************************************************************
      @@ -186,8 +186,10 @@ static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv);
       /* Worker thread actions */
       
       static void usbhost_destroy(FAR void *arg);
      -static void usbhost_initvolume(FAR void *arg);
      -static void usbhost_work(FAR struct usbhost_state_s *priv, worker_t worker);
      +static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
      +                                  FAR const uint8_t *configdesc, int desclen,
      +                                  uint8_t funcaddr);
      +static inline int usbhost_initvolume(FAR struct usbhost_state_s *priv);
       
       /* (Little Endian) Data helpers */
       
      @@ -690,13 +692,13 @@ static inline int usbhost_testunitready(FAR struct usbhost_state_s *priv)
         /* Construct and send the CBW */
        
         usbhost_testunitreadycbw(cbw);
      -  result = DRVR_TRANSFER(priv->drvr, &priv->bulkout,
      +  result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
                               (uint8_t*)cbw, USBSTRG_CBW_SIZEOF);
         if (result == OK)
           {
             /* Receive the CSW */
       
      -      result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +      result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                    priv->tdbuffer, USBSTRG_CSW_SIZEOF);
             if (result == OK)
               {
      @@ -723,19 +725,19 @@ static inline int usbhost_requestsense(FAR struct usbhost_state_s *priv)
         /* Construct and send the CBW */
        
         usbhost_requestsensecbw(cbw);
      -  result = DRVR_TRANSFER(priv->drvr, &priv->bulkout,
      +  result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
                               (uint8_t*)cbw, USBSTRG_CBW_SIZEOF);
         if (result == OK)
           {
             /* Receive the sense data response */
       
      -      result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +      result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                    priv->tdbuffer, SCSIRESP_FIXEDSENSEDATA_SIZEOF);
             if (result == OK)
               {
                 /* Receive the CSW */
       
      -          result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +          result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                        priv->tdbuffer, USBSTRG_CSW_SIZEOF);
                 if (result == OK)
                   {
      @@ -765,13 +767,13 @@ static inline int usbhost_readcapacity(FAR struct usbhost_state_s *priv)
         /* Construct and send the CBW */
        
         usbhost_readcapacitycbw(cbw);
      -  result = DRVR_TRANSFER(priv->drvr, &priv->bulkout,
      +  result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
                               (uint8_t*)cbw, USBSTRG_CBW_SIZEOF);
         if (result == OK)
           {
             /* Receive the read capacity CBW IN response */
       
      -      result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +      result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                    priv->tdbuffer, SCSIRESP_READCAPACITY10_SIZEOF);
             if (result == OK)
               {
      @@ -783,7 +785,7 @@ static inline int usbhost_readcapacity(FAR struct usbhost_state_s *priv)
       
                 /* Receive the CSW */
       
      -          result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +          result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                        priv->tdbuffer, USBSTRG_CSW_SIZEOF);
                 if (result == OK)
                   {
      @@ -813,13 +815,13 @@ static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv)
         /* Construct and send the CBW */
        
         usbhost_inquirycbw(cbw);
      -  result = DRVR_TRANSFER(priv->drvr, &priv->bulkout,
      +  result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
                                (uint8_t*)cbw, USBSTRG_CBW_SIZEOF);
         if (result == OK)
           {
             /* Receive the CBW IN response */
       
      -      result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +      result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                    priv->tdbuffer, SCSIRESP_INQUIRY_SIZEOF);
             if (result == OK)
               {
      @@ -829,7 +831,7 @@ static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv)
       
                 /* Receive the CSW */
       
      -          result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +          result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                        priv->tdbuffer, USBSTRG_CSW_SIZEOF);
                 if (result == OK)
                   {
      @@ -874,6 +876,18 @@ static void usbhost_destroy(FAR void *arg)
       
         usbhost_freedevno(priv);
       
      +  /* Free the bulk endpoints */
      +
      +  if (priv->bulkout)
      +    {
      +      DRVR_EPFREE(priv->drvr, priv->bulkout);
      +    }
      +
      +  if (priv->bulkin)
      +    {
      +      DRVR_EPFREE(priv->drvr, priv->bulkin);
      +    }
      +
         /* Free any transfer buffers */
       
         usbhost_tdfree(priv);
      @@ -895,6 +909,198 @@ static void usbhost_destroy(FAR void *arg)
         usbhost_freeclass(priv);
       }
       
      +/****************************************************************************
      + * Name: usbhost_cfgdesc
      + *
      + * Description:
      + *   This function implements the connect() method of struct
      + *   usbhost_class_s.  This method is a callback into the class
      + *   implementation.  It is used to provide the device's configuration
      + *   descriptor to the class so that the class may initialize properly
      + *
      + * Input Parameters:
      + *   priv - The USB host class instance.
      + *   configdesc - A pointer to a uint8_t buffer container the configuration descripor.
      + *   desclen - The length in bytes of the configuration descriptor.
      + *   funcaddr - The USB address of the function containing the endpoint that EP0
      + *     controls
      + *
      + * Returned Values:
      + *   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 inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv,
      +                                  FAR const uint8_t *configdesc, int desclen,
      +                                  uint8_t funcaddr)
      +{
      +  FAR struct usb_cfgdesc_s *cfgdesc;
      +  FAR struct usb_desc_s *desc;
      +  FAR struct usbhost_epdesc_s bindesc;
      +  FAR struct usbhost_epdesc_s boutdesc;
      +  int remaining;
      +  uint8_t found = 0;
      +  int ret;
      +
      +  DEBUGASSERT(priv != NULL && 
      +              configdesc != NULL &&
      +              desclen >= sizeof(struct usb_cfgdesc_s));
      +  
      +  /* Verify that we were passed a configuration descriptor */
      +
      +  cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
      +  if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
      +    {
      +      return -EINVAL;
      +    }
      +
      +  /* Get the total length of the configuration descriptor (little endian).
      +   * It might be a good check to get the number of interfaces here too.
      +  */
      +
      +  remaining = (int)usbhost_getle16(cfgdesc->totallen);
      +
      +  /* Skip to the next entry descriptor */
      +
      +  configdesc += cfgdesc->len;
      +  remaining  -= cfgdesc->len;
      +
      +  /* Loop where there are more dscriptors to examine */
      +
      +  while (remaining >= sizeof(struct usb_desc_s))
      +    {
      +      /* What is the next descriptor? */
      +
      +      desc = (FAR struct usb_desc_s *)configdesc;
      +      switch (desc->type)
      +        {
      +        /* Interface descriptor. We really should get the number of endpoints
      +         * from this descriptor too.
      +         */
      +
      +        case USB_DESC_TYPE_INTERFACE:
      +          {
      +            DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);
      +            if ((found & USBHOST_IFFOUND) != 0)
      +              {
      +                /* Oops.. more than one interface.  We don't know what to do with this. */
      +
      +                return -ENOSYS;
      +              }
      +            found |= USBHOST_IFFOUND;
      +          }
      +          break;
      +
      +        /* Endpoint descriptor.  We expect two bulk endpoints, an IN and an OUT */
      +        case USB_DESC_TYPE_ENDPOINT:
      +          {
      +            FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc;
      +            DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);
      +
      +            /* Check for a bulk endpoint.  We only support the bulk-only
      +             * protocol so I suppose anything else should really be an error.
      +             */
      +
      +            if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_BULK)
      +              {
      +                /* Yes.. it is a bulk endpoint.  IN or OUT? */
      +
      +                if (USB_ISEPOUT(epdesc->addr))
      +                  {
      +                    /* It is an OUT bulk endpoint.  There should be only one bulk OUT endpoint. */
      +
      +                    if ((found & USBHOST_BOUTFOUND) != 0)
      +                      {
      +                        /* Oops.. more than one interface.  We don't know what to do with this. */
      +
      +                        return -EINVAL;
      +                      }
      +                    found |= USBHOST_BOUTFOUND;
      +
      +                    /* Save the bulk OUT endpoint information */
      +
      +                    boutdesc.addr         = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
      +                    boutdesc.in           = false;
      +                    boutdesc.funcaddr     = funcaddr;
      +                    boutdesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
      +                    uvdbg("Bulk OUT EP addr:%d mxpacketsize:%d\n",
      +                          boutdesc.addr, boutdesc.mxpacketsize);
      +                  }
      +                else
      +                  {
      +                    /* It is an IN bulk endpoint.  There should be only one bulk IN endpoint. */
      +
      +                    if ((found & USBHOST_BINFOUND) != 0)
      +                      {
      +                        /* Oops.. more than one interface.  We don't know what to do with this. */
      +
      +                        return -EINVAL;
      +                      }
      +                    found |= USBHOST_BINFOUND;
      +
      +                    /* Save the bulk IN endpoint information */
      +                    
      +                    bindesc.addr        = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
      +                    bindesc.in           = 1;
      +                    bindesc.funcaddr     = funcaddr;
      +                    bindesc.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
      +                    uvdbg("Bulk IN EP addr:%d mxpacketsize:%d\n",
      +                          bindesc.addr, bindesc.mxpacketsize);
      +                  }
      +              }
      +          }
      +          break;
      +
      +        /* Other descriptors are just ignored for now */
      +
      +        default:
      +          break;
      +        }
      +
      +      /* Increment the address of the next descriptor */
      + 
      +      configdesc += desc->len;
      +      remaining  -= desc->len;
      +    }
      +
      +  /* Sanity checking... did we find all of things that we need? Hmmm..  I wonder..
      +   * can we work read-only or write-only if only one bulk endpoint found?
      +   */
      +    
      +  if (found != USBHOST_ALLFOUND)
      +    {
      +      ulldbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n",
      +             (found & USBHOST_IFFOUND) != 0  ? "YES" : "NO",
      +             (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO",
      +             (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO");
      +      return -EINVAL;
      +    }
      +
      +  /* We are good... Allocate the endpoints */
      +
      +  ret = DRVR_EPALLOC(priv->drvr, &boutdesc, &priv->bulkout);
      +  if (ret != OK)
      +    {
      +      udbg("ERROR: Failed to allocated Bulk OUT endpoint\n");
      +      return ret;
      +    }
      +
      +  ret = DRVR_EPALLOC(priv->drvr, &bindesc, &priv->bulkin);
      +  if (ret != OK)
      +    {
      +      udbg("ERROR: Failed to allocated Bulk IN endpoint\n");
      +      (void)DRVR_EPFREE(priv->drvr, priv->bulkout);
      +      return ret;
      +    }
      +
      +  ullvdbg("Endpoints allocated\n");
      +  return OK;
      +}
      +
       /****************************************************************************
        * Name: usbhost_initvolume
        *
      @@ -908,29 +1114,28 @@ static void usbhost_destroy(FAR void *arg)
        *   connect() was called from an interrupt handler, on the worker thread.
        *
        * Input Parameters:
      - *   arg - A reference to the class instance.
      + *   priv - A reference to the class instance.
        *
        * Returned Values:
        *   None
        *
        ****************************************************************************/
       
      -static void usbhost_initvolume(FAR void *arg)
      +static inline int usbhost_initvolume(FAR struct usbhost_state_s *priv)
       {
      -  FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg;
         FAR struct usbstrg_csw_s *csw;
         unsigned int retries;
      -  int result = OK;
      +  int ret = OK;
       
         DEBUGASSERT(priv != NULL);
       
         /* Set aside a transfer buffer for exclusive use by the mass storage driver */
       
      -  result = usbhost_tdalloc(priv);
      -  if (result != OK)
      +  ret = usbhost_tdalloc(priv);
      +  if (ret != OK)
           {
             udbg("ERROR: Failed to allocate transfer buffer\n");
      -      return;
      +      return ret;
           }
       
         /* Increment the reference count.  This will prevent usbhost_destroy() from
      @@ -943,18 +1148,18 @@ static void usbhost_initvolume(FAR void *arg)
         /* Request the maximum logical unit number */
       
         uvdbg("Get max LUN\n");
      -  result = usbhost_maxlunreq(priv);
      +  ret = usbhost_maxlunreq(priv);
       
         /* Wait for the unit to be ready */
       
      -  for (retries = 0; retries < USBHOST_MAX_RETRIES && result == OK; retries++)
      +  for (retries = 0; retries < USBHOST_MAX_RETRIES && ret == OK; retries++)
           {
             uvdbg("Test unit ready, retries=%d\n", retries);
       
             /* Send TESTUNITREADY to see the unit is ready */
             
      -      result = usbhost_testunitready(priv);
      -      if (result == OK)
      +      ret = usbhost_testunitready(priv);
      +      if (ret == OK)
               {
                 /* Is the unit is ready */
       
      @@ -972,7 +1177,7 @@ static void usbhost_initvolume(FAR void *arg)
                  */
       
                 uvdbg("Request sense\n");
      -          result = usbhost_requestsense(priv);
      +          ret = usbhost_requestsense(priv);
               }
           }
       
      @@ -981,16 +1186,16 @@ static void usbhost_initvolume(FAR void *arg)
         if (retries >= USBHOST_MAX_RETRIES)
           {
             udbg("ERROR: Timeout!\n");
      -      result = -ETIMEDOUT;
      +      ret = -ETIMEDOUT;
           }
       
      -  if (result == OK)
      +  if (ret == OK)
           {
             /* Get the capacity of the volume */
       
             uvdbg("Read capacity\n");
      -      result = usbhost_readcapacity(priv);
      -      if (result == OK)
      +      ret = usbhost_readcapacity(priv);
      +      if (ret == OK)
               {
                 /* Check the CSW for errors */
       
      @@ -998,20 +1203,20 @@ static void usbhost_initvolume(FAR void *arg)
                 if (csw->status != 0)
                   {
                     udbg("ERROR: CSW status error: %d\n", csw->status);
      -              result = -ENODEV;
      +              ret = -ENODEV;
                   }
               }
           }
       
         /* Get information about the volume */
       
      -  if (result == OK)
      +  if (ret == OK)
           {
             /* Inquiry */
       
             uvdbg("Inquiry\n");
      -      result = usbhost_inquiry(priv);
      -      if (result == OK)
      +      ret = usbhost_inquiry(priv);
      +      if (ret == OK)
               {
                 /* Check the CSW for errors */
       
      @@ -1019,20 +1224,20 @@ static void usbhost_initvolume(FAR void *arg)
                 if (csw->status != 0)
                   {
                     udbg("ERROR: CSW status error: %d\n", csw->status);
      -              result = -ENODEV;
      +              ret = -ENODEV;
                   }
               }
           }
       
         /* Register the block driver */
       
      -  if (result == OK)
      +  if (ret == OK)
           {
             char devname[DEV_NAMELEN];
       
             uvdbg("Register block driver\n");
             usbhost_mkdevname(priv, devname);
      -      result = register_blockdriver(devname, &g_bops, 0, priv);
      +      ret = register_blockdriver(devname, &g_bops, 0, priv);
           }
       
         /* Check if we successfully initialized. We now have to be concerned
      @@ -1040,7 +1245,7 @@ static void usbhost_initvolume(FAR void *arg)
          * driver has been registerd.
          */
       
      -  if (result == OK)
      +  if (ret == OK)
           {
             usbhost_takesem(&priv->exclsem);
             DEBUGASSERT(priv->crefs >= 2);
      @@ -1057,7 +1262,7 @@ static void usbhost_initvolume(FAR void *arg)
                  * destroyed when usb_destroy is called.
                  */
         
      -          result = -ENODEV;
      +          ret = -ENODEV;
               }
             else
               {
      @@ -1071,50 +1276,13 @@ static void usbhost_initvolume(FAR void *arg)
       
         /* Disconnect on any errors detected during volume initialization */
       
      -  if (result != OK)
      +  if (ret != OK)
           {
      -      udbg("ERROR! Aborting: %d\n", result);
      +      udbg("ERROR! Aborting: %d\n", ret);
             usbhost_destroy(priv);
           }
      -}
       
      -/****************************************************************************
      - * Name: usbhost_work
      - *
      - * Description:
      - *   Perform work, depending on context:  If we are executing from an
      - *   interrupt handler, then defer the work to the worker thread.  Otherwise,
      - *   just execute the work now.
      - *
      - * Input Parameters:
      - *   priv - A reference to the class instance.
      - *   worker - A reference to the worker function to be executed
      - *
      - * Returned Values:
      - *   None
      - *
      - ****************************************************************************/
      -
      -static void usbhost_work(FAR struct usbhost_state_s *priv, worker_t worker)
      -{
      -  /* Are we in an interrupt handler? */
      -
      -  if (up_interrupt_context())
      -    {
      -      /* Yes.. do the work on the worker thread.  Higher level logic should
      -       * prevent us from over-running the work structure.
      -       */
      -
      -      uvdbg("Queuing work: worker %p->%p\n", priv->work.worker, worker);
      -      DEBUGASSERT(priv->work.worker == NULL);
      -      (void)work_queue(&priv->work, worker, priv, 0);
      -    }
      -  else
      -    {
      -      /* No.. do the work now */
      -
      -      worker(priv);
      -    }
      +  return ret;
       }
       
       /****************************************************************************
      @@ -1474,9 +1642,7 @@ static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *d
        *   returned indicating the nature of the failure
        *
        * Assumptions:
      - *   This function is probably called on the same thread that called the driver
      - *   enumerate() method.  However, this function may also be called from an
      - *   interrupt handler.
      + *   This function will *not* be called from an interrupt handler.
        *
        ****************************************************************************/
       
      @@ -1485,151 +1651,31 @@ static int usbhost_connect(FAR struct usbhost_class_s *class,
                                  uint8_t funcaddr)
       {
         FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class;
      -  FAR struct usb_cfgdesc_s *cfgdesc;
      -  FAR struct usb_desc_s *desc;
      -  int remaining;
      -  uint8_t found = 0;
      +  int ret;
       
         DEBUGASSERT(priv != NULL && 
                     configdesc != NULL &&
                     desclen >= sizeof(struct usb_cfgdesc_s));
      -  
      -  /* Verify that we were passed a configuration descriptor */
       
      -  cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc;
      -  if (cfgdesc->type != USB_DESC_TYPE_CONFIG)
      +  /* Parse the configuration descriptor to get the bulk I/O endpoints */
      +
      +  ret = usbhost_cfgdesc(priv, configdesc, desclen, funcaddr);
      +  if (ret != OK)
           {
      -      return -EINVAL;
      +      udbg("usbhost_cfgdesc() failed: %d\n", ret);
           }
      -
      -  /* Get the total length of the configuration descriptor (little endian).
      -   * It might be a good check to get the number of interfaces here too.
      -  */
      -
      -  remaining = (int)usbhost_getle16(cfgdesc->totallen);
      -
      -  /* Skip to the next entry descriptor */
      -
      -  configdesc += cfgdesc->len;
      -  remaining  -= cfgdesc->len;
      -
      -  /* Loop where there are more dscriptors to examine */
      -
      -  while (remaining >= sizeof(struct usb_desc_s))
      +  else
           {
      -      /* What is the next descriptor? */
      +      /* Now configure the LUNs and register the block driver(s) */
       
      -      desc = (FAR struct usb_desc_s *)configdesc;
      -      switch (desc->type)
      +      ret = usbhost_initvolume(priv);
      +      if (ret != OK)
               {
      -        /* Interface descriptor. We really should get the number of endpoints
      -         * from this descriptor too.
      -         */
      -
      -        case USB_DESC_TYPE_INTERFACE:
      -          {
      -            DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC);
      -            if ((found & USBHOST_IFFOUND) != 0)
      -              {
      -                /* Oops.. more than one interface.  We don't know what to do with this. */
      -
      -                return -ENOSYS;
      -              }
      -            found |= USBHOST_IFFOUND;
      -          }
      -          break;
      -
      -        /* Endpoint descriptor.  We expect two bulk endpoints, an IN and an OUT */
      -        case USB_DESC_TYPE_ENDPOINT:
      -          {
      -            FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc;
      -            DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC);
      -
      -            /* Check for a bulk endpoint.  We only support the bulk-only
      -             * protocol so I suppose anything else should really be an error.
      -             */
      -
      -            if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_BULK)
      -              {
      -                /* Yes.. it is a bulk endpoint.  IN or OUT? */
      -
      -                if (USB_ISEPOUT(epdesc->addr))
      -                  {
      -                    /* It is an OUT bulk endpoint.  There should be only one bulk OUT endpoint. */
      -
      -                    if ((found & USBHOST_BOUTFOUND) != 0)
      -                      {
      -                        /* Oops.. more than one interface.  We don't know what to do with this. */
      -
      -                        return -EINVAL;
      -                      }
      -                    found |= USBHOST_BOUTFOUND;
      -
      -                    /* Save the bulk OUT endpoint information */
      -
      -                    priv->bulkout.addr         = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
      -                    priv->bulkout.in           = 0;
      -                    priv->bulkout.funcaddr     = funcaddr;
      -                    priv->bulkout.mxpacketsize = usbhost_getle16(epdesc->mxpacketsize);
      -                    uvdbg("Bulk OUT EP addr:%d mxpacketsize:%d\n",
      -                          priv->bulkout.addr, priv->bulkout.mxpacketsize);
      -                  }
      -                else
      -                  {
      -                    /* It is an IN bulk endpoint.  There should be only one bulk IN endpoint. */
      -
      -                    if ((found & USBHOST_BINFOUND) != 0)
      -                      {
      -                        /* Oops.. more than one interface.  We don't know what to do with this. */
      -
      -                        return -EINVAL;
      -                      }
      -                    found |= USBHOST_BINFOUND;
      -
      -                    /* Save the bulk IN endpoint information */
      -                    
      -                    priv->bulkin.addr          = epdesc->addr & USB_EP_ADDR_NUMBER_MASK;
      -                    priv->bulkin.in            = 1;
      -                    priv->bulkin.funcaddr      = funcaddr;
      -                    priv->bulkin.mxpacketsize  = usbhost_getle16(epdesc->mxpacketsize);
      -                    uvdbg("Bulk IN EP addr:%d mxpacketsize:%d\n",
      -                          priv->bulkin.addr, priv->bulkin.mxpacketsize);
      -                  }
      -              }
      -          }
      -          break;
      -
      -        /* Other descriptors are just ignored for now */
      -
      -        default:
      -          break;
      +          udbg("usbhost_initvolume() failed: %d\n", ret);
               }
      -
      -      /* Increment the address of the next descriptor */
      - 
      -      configdesc += desc->len;
      -      remaining  -= desc->len;
      -    }
      -
      -  /* Sanity checking... did we find all of things that we need? Hmmm..  I wonder..
      -   * can we work read-only or write-only if only one bulk endpoint found?
      -   */
      -    
      -  if (found != USBHOST_ALLFOUND)
      -    {
      -      ulldbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n",
      -             (found & USBHOST_IFFOUND) != 0  ? "YES" : "NO",
      -             (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO",
      -             (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO");
      -      return -EINVAL;
           }
      -
      -  ullvdbg("Mass Storage device connected\n");
      -
      -  /* Now configure the LUNs and register the block driver(s) */
      -
      -  usbhost_work(priv, usbhost_initvolume);
      -  return OK;
      + 
      +  return ret;
       }
       
       /****************************************************************************
      @@ -1677,10 +1723,27 @@ static int usbhost_disconnected(struct usbhost_class_s *class)
         ullvdbg("crefs: %d\n", priv->crefs);
         if (priv->crefs == 1)
           {
      -      /* Destroy the class instance */
      +      /* Destroy the class instance.  If we are executing from an interrupt
      +       * handler, then defer the destruction to the worker thread.
      +       * Otherwise, destroy the instance now.
      +       */
      +
      +      if (up_interrupt_context())
      +        {
      +          /* Destroy the instance on the worker thread. */
       
      -      usbhost_work(priv, usbhost_destroy);
      +          uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy);
      +          DEBUGASSERT(priv->work.worker == NULL);
      +          (void)work_queue(&priv->work, usbhost_destroy, priv, 0);
      +       }
      +      else
      +        {
      +          /* Do the work now */
      +
      +          usbhost_destroy(priv);
      +        }
           }
      +
         irqrestore(flags);  
         return OK;
       }
      @@ -1845,19 +1908,19 @@ static ssize_t usbhost_read(FAR struct inode *inode, unsigned char *buffer,
                 /* Construct and send the CBW */
        
                 usbhost_readcbw(startsector, priv->blocksize, nsectors, cbw);
      -          result = DRVR_TRANSFER(priv->drvr, &priv->bulkout,
      +          result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
                                        (uint8_t*)cbw, USBSTRG_CBW_SIZEOF);
                 if (result == OK)
                   {
                     /* Receive the user data */
       
      -              result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +              result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                            buffer, priv->blocksize * nsectors);
                     if (result == OK)
                       {
                         /* Receive the CSW */
       
      -                  result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +                  result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                                priv->tdbuffer, USBSTRG_CSW_SIZEOF);
                         if (result == OK)
                           {
      @@ -1937,19 +2000,19 @@ static ssize_t usbhost_write(FAR struct inode *inode, const unsigned char *buffe
                 /* Construct and send the CBW */
        
                 usbhost_writecbw(startsector, priv->blocksize, nsectors, cbw);
      -          result = DRVR_TRANSFER(priv->drvr, &priv->bulkout,
      +          result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
                                        (uint8_t*)cbw, USBSTRG_CBW_SIZEOF);
                 if (result == OK)
                   {
                     /* Send the user data */
       
      -              result = DRVR_TRANSFER(priv->drvr, &priv->bulkout,
      +              result = DRVR_TRANSFER(priv->drvr, priv->bulkout,
                                            (uint8_t*)buffer, priv->blocksize * nsectors);
                     if (result == OK)
                       {
                         /* Receive the CSW */
       
      -                  result = DRVR_TRANSFER(priv->drvr, &priv->bulkin,
      +                  result = DRVR_TRANSFER(priv->drvr, priv->bulkin,
                                                priv->tdbuffer, USBSTRG_CSW_SIZEOF);
                         if (result == OK)
                           {
      diff --git a/nuttx/include/nuttx/usb/usbhost.h b/nuttx/include/nuttx/usb/usbhost.h
      index a83048f68..e69f1814a 100644
      --- a/nuttx/include/nuttx/usb/usbhost.h
      +++ b/nuttx/include/nuttx/usb/usbhost.h
      @@ -139,7 +139,7 @@
        *   returned indicating the nature of the failure
        *
        * Assumptions:
      - *   This function may be called from an interrupt handler.
      + *   This function will *not* be called from an interrupt handler.
        *
        ************************************************************************************/
       
      @@ -225,6 +225,52 @@
       
       #define DRVR_EP0CONFIGURE(drvr,funcaddr,mps) ((drvr)->ep0configure(drvr,funcaddr,mps))
       
      +/************************************************************************************
      + * Name: DRVR_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 desciptor.
      + *
      + * Returned Values:
      + *   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.
      + *
      + ************************************************************************************/
      +
      +#define DRVR_EPALLOC(drvr,epdesc,ep) ((drvr)->epalloc(drvr,epdesc,ep))
      +
      +/************************************************************************************
      + * Name: DRVR_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 endpint to be freed.
      + *
      + * Returned Values:
      + *   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.
      + *
      + ************************************************************************************/
      +
      +#define DRVR_EPFREE(drvr,ep) ((drvr)->epfree(drvr,ep))
      +
       /************************************************************************************
        * Name: DRVR_ALLOC
        *
      @@ -441,11 +487,30 @@ struct usbhost_class_s
         int (*disconnected)(FAR struct usbhost_class_s *class);
       };
       
      +/* This structure describes one endpoint.  It is used as an input to the
      + * allocep() method.
      + */
      +
      +struct usbhost_epdesc_s
      +{
      +  uint8_t  addr;         /* Endpoint address */
      +  bool     in;           /* Direction: true->IN */
      +  uint8_t  funcaddr;     /* USB address of function containing endpoint */
      +  uint16_t mxpacketsize; /* Max packetsize */
      +};
      +
      +/* This type represents one endpoint configured by the allocep() method.
      + * The actual form is know only internally to the USB host controller
      + * (for example, for an OHCI driver, this would probably be a pointer
      + * to an endpoint descriptor).
      + */
      +
      +typedef FAR void *usbhost_ep_t;
      +
       /* struct usbhost_driver_s provides access to the USB host driver from the
        * USB host class implementation.
        */
       
      -struct usbhost_epdesc_s;
       struct usbhost_driver_s
       {
         /* Wait for a device to connect or disconnect. */
      @@ -472,6 +537,12 @@ struct usbhost_driver_s
         int (*ep0configure)(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr,
                             uint16_t maxpacketsize);
       
      +  /* Allocate and configure an endpoint. */
      +
      +  int (*epalloc)(FAR struct usbhost_driver_s *drvr,
      +                const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep);
      +  int (*epfree)(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep);
      +
         /* Some hardware supports special memory in which transfer descriptors can
          * be accessed more efficiently.  The following methods provide a mechanism
          * to allocate and free the transfer descriptor memory.  If the underlying
      @@ -508,8 +579,7 @@ struct usbhost_driver_s
          * transfer has completed.
          */
       
      -  int (*transfer)(FAR struct usbhost_driver_s *drvr,
      -                  FAR struct usbhost_epdesc_s *ep,
      +  int (*transfer)(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
                         FAR uint8_t *buffer, size_t buflen);
       
         /* Called by the class when an error occurs and driver has been disconnected.
      @@ -521,18 +591,6 @@ struct usbhost_driver_s
         void (*disconnect)(FAR struct usbhost_driver_s *drvr);
       };
       
      -/* This structure describes one endpoint */
      -
      -struct usbhost_epdesc_s
      -{
      -  uint8_t  addr     : 4; /* Endpoint address */
      -  uint8_t  pad      : 3;
      -  uint8_t  in       : 1; /* Direction: 1->IN */
      -  uint8_t  funcaddr : 7; /* USB address of function containing endpoint */
      -  uint8_t  toggle   : 1; /* Last toggle (modified by the driver) */
      -  uint16_t mxpacketsize; /* Max packetsize */
      -};
      -
       /************************************************************************************
        * Public Data
        ************************************************************************************/
      -- 
      cgit v1.2.3