diff --git a/arch/arm/src/lpc31xx/lpc31_ehci.c b/arch/arm/src/lpc31xx/lpc31_ehci.c
index fda9405939d0a3db113d57141b082aca1ab9c968..9729fa5c4d740506cc3b72250720f47912e5d30a 100644
--- a/arch/arm/src/lpc31xx/lpc31_ehci.c
+++ b/arch/arm/src/lpc31xx/lpc31_ehci.c
@@ -475,13 +475,14 @@ static struct lpc31_qtd_s *lpc31_qtd_setupphase(struct lpc31_epinfo_s *epinfo,
 static struct lpc31_qtd_s *lpc31_qtd_dataphase(struct lpc31_epinfo_s *epinfo,
          void *buffer, int buflen, uint32_t tokenbits);
 static struct lpc31_qtd_s *lpc31_qtd_statusphase(uint32_t tokenbits);
-static ssize_t lpc31_async_transfer(struct lpc31_rhport_s *rhport,
+static ssize_t lpc31_async_setup(struct lpc31_rhport_s *rhport,
          struct lpc31_epinfo_s *epinfo, const struct usb_ctrlreq_s *req,
          uint8_t *buffer, size_t buflen);
 #ifndef CONFIG_USBHOST_INT_DISABLE
-static ssize_t lpc31_intr_transfer(struct lpc31_rhport_s *rhport,
+static int lpc31_intr_setup(struct lpc31_rhport_s *rhport,
          struct lpc31_epinfo_s *epinfo, uint8_t *buffer, size_t buflen);
 #endif
+static ssize_t lpc31_transfer_wait(struct lpc31_epinfo_s *epinfo);
 
 /* Interrupt Handling **********************************************************/
 
@@ -1995,7 +1996,7 @@ static struct lpc31_qtd_s *lpc31_qtd_statusphase(uint32_t tokenbits)
 }
 
 /*******************************************************************************
- * Name: lpc31_async_transfer
+ * Name: lpc31_async_setup
  *
  * Description:
  *   Process a IN or OUT request on any asynchronous endpoint (bulk or control).
@@ -2006,23 +2007,18 @@ static struct lpc31_qtd_s *lpc31_qtd_statusphase(uint32_t tokenbits)
  *   This is a blocking function; it will not return until the control transfer
  *   has completed.
  *
- * Assumption:  The caller holds the EHCI exclsem.  The caller must be aware
- *   that the EHCI exclsem will released while waiting for the transfer to
- *   complete, but will be re-aquired when before returning.  The state of
- *   EHCI resources could be very different upon return.
+ * Assumption:  The caller holds the EHCI exclsem.
  *
  * Returned value:
- *   On success, this function returns the number of bytes actually transferred.
- *   For control transfers, this size includes the size of the control request
- *   plus the size of the data (which could be short); For bulk transfers, this
- *   will be the number of data bytes transfers (which could be short).
+ *   Zero (OK) is returned on success; a negated errno value is return on
+ *   any failure.
  *
  *******************************************************************************/
 
-static ssize_t lpc31_async_transfer(struct lpc31_rhport_s *rhport,
-                                  struct lpc31_epinfo_s *epinfo,
-                                  const struct usb_ctrlreq_s *req,
-                                  uint8_t *buffer, size_t buflen)
+static int lpc31_async_setup(struct lpc31_rhport_s *rhport,
+                             struct lpc31_epinfo_s *epinfo,
+                             const struct usb_ctrlreq_s *req,
+                             uint8_t *buffer, size_t buflen)
 {
   struct lpc31_qh_s *qh;
   struct lpc31_qtd_s *qtd;
@@ -2051,23 +2047,13 @@ static ssize_t lpc31_async_transfer(struct lpc31_rhport_s *rhport,
 
   DEBUGASSERT(req || (buffer && buflen > 0));
 
-  /* Set the request for the IOC event well BEFORE enabling the transfer. */
-
-  ret = lpc31_ioc_setup(rhport, epinfo);
-  if (ret != OK)
-    {
-      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
-      return ret;
-    }
-
   /* Create and initialize a Queue Head (QH) structure for this transfer */
 
   qh = lpc31_qh_create(rhport, epinfo);
   if (qh == NULL)
     {
       usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0);
-      ret = -ENOMEM;
-      goto errout_with_iocwait;
+      return -ENOMEM;
     }
 
   /* Initialize the QH link and get the next data toggle (not used for SETUP
@@ -2264,72 +2250,17 @@ static ssize_t lpc31_async_transfer(struct lpc31_rhport_s *rhport,
 
   regval |= EHCI_USBCMD_ASEN;
   lpc31_putreg(regval, &HCOR->usbcmd);
-
-  /* Release the EHCI semaphore while we wait.  Other threads need the
-   * opportunity to access the EHCI resources while we wait.
-   *
-   * REVISIT:  Is this safe?  NO.  This is a bug and needs rethinking.
-   * We need to lock all of the port-resources (not EHCI common) until
-   * the transfer is complete.  But we can't use the common EHCI exclsem
-   * or we will deadlock while waiting (because the working thread that
-   * wakes this thread up needs the exclsem).
-   */
-#warning REVISIT
-  lpc31_givesem(&g_ehci.exclsem);
-
-  /* Wait for the IOC completion event */
-
-  ret = lpc31_ioc_wait(epinfo);
-
-  /* Re-aquire the EHCI semaphore.  The caller expects to be holding
-   * this upon return.
-   */
-
-  lpc31_takesem(&g_ehci.exclsem);
-
-#if 0 /* Does not seem to be needed */
-  /* Was there a data buffer?  Was this an OUT transfer? */
-
-  if (buffer != NULL && buflen > 0 && !dirin)
-    {
-      /* We have received data from the host -- unless there was an error.
-       * in any event, we will invalidate the data buffer so that we will
-       * reload any new data freshly DMAed into the user buffer.
-       *
-       * NOTE: This might be un-necessary.  We cleaned and invalidated the
-       * D-Cache prior to starting the DMA so the D-Cache should still be
-       * invalid in this memory region.
-       */
-
-      cp15_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
-    }
-#endif
-
-  /* Did lpc31_ioc_wait() report an error? */
-
-  if (ret < 0)
-    {
-      usbhost_trace1(EHCI_TRACE1_TRANSFER_FAILED, -ret);
-      goto errout_with_iocwait;
-    }
-
-  /* Transfer completed successfully.  Return the number of bytes
-   * transferred.
-   */
-
-  return epinfo->xfrd;
+  return OK;
 
   /* Clean-up after an error */
 
 errout_with_qh:
   lpc31_qh_discard(qh);
-errout_with_iocwait:
-  epinfo->iocwait = false;
-  return (ssize_t)ret;
+  return ret;
 }
 
 /*******************************************************************************
- * Name: lpc31_intr_transfer
+ * Name: lpc31_intr_setup
  *
  * Description:
  *   Process a IN or OUT request on any interrupt endpoint by inserting a qTD
@@ -2368,23 +2299,18 @@ errout_with_iocwait:
  *     frame list), followed by shorter poll rates, with queue heads with a
  *     poll rate of one, on the very end."
  *
- * Assumption:  The caller holds the EHCI exclsem.  The caller must be aware
- *   that the EHCI exclsem will released while waiting for the transfer to
- *   complete, but will be re-aquired when before returning.  The state of
- *   EHCI resources could be very different upon return.
+ * Assumption:  The caller holds the EHCI exclsem.
  *
  * Returned value:
- *   On success, this function returns the number of bytes actually transferred.
- *   For control transfers, this size includes the size of the control request
- *   plus the size of the data (which could be short); For bulk transfers, this
- *   will be the number of data bytes transfers (which could be short).
+ *   Zero (OK) is returned on success; a negated errno value is return on
+ *   any failure.
  *
  *******************************************************************************/
 
 #ifndef CONFIG_USBHOST_INT_DISABLE
-static ssize_t lpc31_intr_transfer(struct lpc31_rhport_s *rhport,
-                                 struct lpc31_epinfo_s *epinfo,
-                                 uint8_t *buffer, size_t buflen)
+static int lpc31_intr_setup(struct lpc31_rhport_s *rhport,
+                            struct lpc31_epinfo_s *epinfo,
+                            uint8_t *buffer, size_t buflen)
 {
   struct lpc31_qh_s *qh;
   struct lpc31_qtd_s *qtd;
@@ -2404,27 +2330,17 @@ static ssize_t lpc31_intr_transfer(struct lpc31_rhport_s *rhport,
 
   DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
 
-  /* Set the request for the IOC event well BEFORE enabling the transfer. */
-
-  ret = lpc31_ioc_setup(rhport, epinfo);
-  if (ret != OK)
-    {
-      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
-      return ret;
-    }
-
   /* Create and initialize a Queue Head (QH) structure for this transfer */
 
   qh = lpc31_qh_create(rhport, epinfo);
   if (qh == NULL)
     {
       usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0);
-      ret = -ENOMEM;
-      goto errout_with_iocwait;
+      return -ENOMEM;
     }
 
-  /* Extra TOKEN bits include the data toggle, the data PID, and and indication to interrupt at the end of this
-   * transfer.
+  /* Extra TOKEN bits include the data toggle, the data PID, and an
+   * indication to interrupt at the end of this transfer.
    */
 
   tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
@@ -2470,6 +2386,38 @@ static ssize_t lpc31_intr_transfer(struct lpc31_rhport_s *rhport,
 
   regval |= EHCI_USBCMD_PSEN;
   lpc31_putreg(regval, &HCOR->usbcmd);
+  return OK;
+
+  /* Clean-up after an error */
+
+errout_with_qh:
+  lpc31_qh_discard(qh);
+  return (ssize_t)ret;
+}
+#endif /* CONFIG_USBHOST_INT_DISABLE */
+
+/*******************************************************************************
+ * Name: lpc31_transfer_wait
+ *
+ * Description:
+ *   Wait for an IN or OUT transfer to complete.
+ *
+ * Assumption:  The caller holds the EHCI exclsem.  The caller must be aware
+ *   that the EHCI exclsem will released while waiting for the transfer to
+ *   complete, but will be re-acquired when before returning.  The state of
+ *   EHCI resources could be very different upon return.
+ *
+ * Returned value:
+ *   On success, this function returns the number of bytes actually transferred.
+ *   For control transfers, this size includes the size of the control request
+ *   plus the size of the data (which could be short); For bulk transfers, this
+ *   will be the number of data bytes transfers (which could be short).
+ *
+ *******************************************************************************/
+
+static ssize_t lpc31_transfer_wait(struct lpc31_epinfo_s *epinfo)
+{
+  int ret;
 
   /* Release the EHCI semaphore while we wait.  Other threads need the
    * opportunity to access the EHCI resources while we wait.
@@ -2487,33 +2435,49 @@ static ssize_t lpc31_intr_transfer(struct lpc31_rhport_s *rhport,
 
   ret = lpc31_ioc_wait(epinfo);
 
-  /* Re-aquire the EHCI semaphore.  The caller expects to be holding
+  /* Wait for the IOC completion event */
+
+  ret = lpc31_ioc_wait(epinfo);
+
+  /* Re-acquire the EHCI semaphore.  The caller expects to be holding
    * this upon return.
    */
 
   lpc31_takesem(&g_ehci.exclsem);
 
+#if 0 /* Does not seem to be needed */
+  /* Was there a data buffer?  Was this an OUT transfer? */
+
+  if (buffer != NULL && buflen > 0 && !dirin)
+    {
+      /* We have received data from the host -- unless there was an error.
+       * in any event, we will invalidate the data buffer so that we will
+       * reload any new data freshly DMAed into the user buffer.
+       *
+       * NOTE: This might be un-necessary.  We cleaned and invalidated the
+       * D-Cache prior to starting the DMA so the D-Cache should still be
+       * invalid in this memory region.
+       */
+
+      cp15_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
+    }
+#endif
+
   /* Did lpc31_ioc_wait() report an error? */
 
   if (ret < 0)
     {
       usbhost_trace1(EHCI_TRACE1_TRANSFER_FAILED, -ret);
-      goto errout_with_iocwait;
+      epinfo->iocwait = false;
+      return (ssize_t)ret;
     }
 
-  /* Transfer completed successfully.  Return the number of bytes transferred */
+  /* Transfer completed successfully.  Return the number of bytes
+   * transferred.
+   */
 
   return epinfo->xfrd;
-
-  /* Clean-up after an error */
-
-errout_with_qh:
-  lpc31_qh_discard(qh);
-errout_with_iocwait:
-  epinfo->iocwait = false;
-  return (ssize_t)ret;
 }
-#endif
 
 /*******************************************************************************
  * EHCI Interrupt Handling
@@ -3906,6 +3870,7 @@ static int lpc31_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
   struct lpc31_epinfo_s *ep0info = (struct lpc31_epinfo_s *)ep0;
   uint16_t len;
   ssize_t nbytes;
+  int ret;
 
   DEBUGASSERT(rhport != NULL && ep0info != NULL && req != NULL);
 
@@ -3925,11 +3890,35 @@ static int lpc31_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
 
   lpc31_takesem(&g_ehci.exclsem);
 
-  /* Now perform the transfer */
+  /* Set the request for the IOC event well BEFORE initiating the transfer. */
+
+  ret = lpc31_ioc_setup(rhport, ep0info);
+  if (ret != OK)
+    {
+      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
+      goto errout_with_sem;
+    }
+
+  /* Now initiate the transfer */
+
+  ret = lpc31_async_setup(rhport, ep0info, req, buffer, len);
+  if (ret < 0)
+    {
+      udbg("ERROR: lpc31_async_setup failed: %d\n", ret);
+      goto errout_with_iocwait;
+    }
+
+  /* And wait for the transfer to complete */
 
-  nbytes = lpc31_async_transfer(rhport, ep0info, req, buffer, len);
+  nbytes = lpc31_transfer_wait(ep0info);
   lpc31_givesem(&g_ehci.exclsem);
-  return nbytes >=0 ? OK : (int)nbytes;
+  return nbytes >= 0 ? OK : (int)nbytes;
+
+errout_with_iocwait:
+  ep0info->iocwait = false;
+errout_with_sem:
+  lpc31_givesem(&g_ehci.exclsem);
+  return ret;
 }
 
 static int lpc31_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
@@ -3986,6 +3975,7 @@ static int lpc31_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
   struct lpc31_rhport_s *rhport = (struct lpc31_rhport_s *)drvr;
   struct lpc31_epinfo_s *epinfo = (struct lpc31_epinfo_s *)ep;
   ssize_t nbytes;
+  int ret;
 
   DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
 
@@ -3993,17 +3983,26 @@ static int lpc31_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
 
   lpc31_takesem(&g_ehci.exclsem);
 
-  /* Perform the transfer */
+  /* Set the request for the IOC event well BEFORE initiating the transfer. */
+
+  ret = lpc31_ioc_setup(rhport, epinfo);
+  if (ret != OK)
+    {
+      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
+      goto errout_with_sem;
+    }
+
+  /* Initiate the transfer */
 
   switch (epinfo->xfrtype)
     {
       case USB_EP_ATTR_XFER_BULK:
-        nbytes = lpc31_async_transfer(rhport, epinfo, NULL, buffer, buflen);
+        ret = lpc31_async_setup(rhport, epinfo, NULL, buffer, buflen);
         break;
 
 #ifndef CONFIG_USBHOST_INT_DISABLE
       case USB_EP_ATTR_XFER_INT:
-        nbytes = lpc31_intr_transfer(rhport, epinfo, buffer, buflen);
+        ret = lpc31_intr_setup(rhport, epinfo, buffer, buflen);
         break;
 #endif
 
@@ -4014,12 +4013,28 @@ static int lpc31_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
       case USB_EP_ATTR_XFER_CONTROL:
       default:
         usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype);
-        nbytes = -ENOSYS;
+        ret = -ENOSYS;
         break;
     }
 
+  /* Check for errors in the setup of the transfer */
+
+  if (ret < 0)
+    {
+      goto errout_with_iocwait;
+    }
+
+  /* Then wait for the transfer to complete */
+
+  nbytes = lpc31_transfer_wait(epinfo);
   lpc31_givesem(&g_ehci.exclsem);
-  return nbytes >=0 ? OK : (int)nbytes;
+  return nbytes >= 0 ? OK : (int)nbytes;
+
+errout_with_iocwait:
+  epinfo->iocwait = false;
+errout_with_sem:
+  lpc31_givesem(&g_ehci.exclsem);
+  return ret;
 }
 
 /*******************************************************************************
diff --git a/arch/arm/src/sama5/sam_ehci.c b/arch/arm/src/sama5/sam_ehci.c
index 11d4feb4c0cdf4a508d3956c6775c24d8cc13317..845769f77be1b2ba57c5c941c7eaca40b9fc2481 100644
--- a/arch/arm/src/sama5/sam_ehci.c
+++ b/arch/arm/src/sama5/sam_ehci.c
@@ -352,13 +352,14 @@ static struct sam_qtd_s *sam_qtd_setupphase(struct sam_epinfo_s *epinfo,
 static struct sam_qtd_s *sam_qtd_dataphase(struct sam_epinfo_s *epinfo,
          void *buffer, int buflen, uint32_t tokenbits);
 static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits);
-static ssize_t sam_async_transfer(struct sam_rhport_s *rhport,
+static int sam_async_setup(struct sam_rhport_s *rhport,
          struct sam_epinfo_s *epinfo, const struct usb_ctrlreq_s *req,
          uint8_t *buffer, size_t buflen);
 #ifndef CONFIG_USBHOST_INT_DISABLE
-static ssize_t sam_intr_transfer(struct sam_rhport_s *rhport,
+static int sam_intr_setup(struct sam_rhport_s *rhport,
          struct sam_epinfo_s *epinfo, uint8_t *buffer, size_t buflen);
 #endif
+static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo);
 
 /* Interrupt Handling **********************************************************/
 
@@ -1814,7 +1815,7 @@ static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits)
 }
 
 /*******************************************************************************
- * Name: sam_async_transfer
+ * Name: sam_async_setup
  *
  * Description:
  *   Process a IN or OUT request on any asynchronous endpoint (bulk or control).
@@ -1825,23 +1826,18 @@ static struct sam_qtd_s *sam_qtd_statusphase(uint32_t tokenbits)
  *   This is a blocking function; it will not return until the control transfer
  *   has completed.
  *
- * Assumption:  The caller holds the EHCI exclsem.  The caller must be aware
- *   that the EHCI exclsem will released while waiting for the transfer to
- *   complete, but will be re-aquired when before returning.  The state of
- *   EHCI resources could be very different upon return.
+ * Assumption:  The caller holds the EHCI exclsem.
  *
  * Returned value:
- *   On success, this function returns the number of bytes actually transferred.
- *   For control transfers, this size includes the size of the control request
- *   plus the size of the data (which could be short); For bulk transfers, this
- *   will be the number of data bytes transfers (which could be short).
+ *   Zero (OK) is returned on success; a negated errno value is return on
+ *   any failure.
  *
  *******************************************************************************/
 
-static ssize_t sam_async_transfer(struct sam_rhport_s *rhport,
-                                  struct sam_epinfo_s *epinfo,
-                                  const struct usb_ctrlreq_s *req,
-                                  uint8_t *buffer, size_t buflen)
+static int sam_async_setup(struct sam_rhport_s *rhport,
+                           struct sam_epinfo_s *epinfo,
+                           const struct usb_ctrlreq_s *req,
+                           uint8_t *buffer, size_t buflen)
 {
   struct sam_qh_s *qh;
   struct sam_qtd_s *qtd;
@@ -1870,23 +1866,13 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport,
 
   DEBUGASSERT(req || (buffer && buflen > 0));
 
-  /* Set the request for the IOC event well BEFORE enabling the transfer. */
-
-  ret = sam_ioc_setup(rhport, epinfo);
-  if (ret != OK)
-    {
-      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
-      return ret;
-    }
-
   /* Create and initialize a Queue Head (QH) structure for this transfer */
 
   qh = sam_qh_create(rhport, epinfo);
   if (qh == NULL)
     {
       usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0);
-      ret = -ENOMEM;
-      goto errout_with_iocwait;
+      return -ENOMEM;
     }
 
   /* Initialize the QH link and get the next data toggle (not used for SETUP
@@ -2083,72 +2069,17 @@ static ssize_t sam_async_transfer(struct sam_rhport_s *rhport,
 
   regval |= EHCI_USBCMD_ASEN;
   sam_putreg(regval, &HCOR->usbcmd);
-
-  /* Release the EHCI semaphore while we wait.  Other threads need the
-   * opportunity to access the EHCI resources while we wait.
-   *
-   * REVISIT:  Is this safe?  NO.  This is a bug and needs rethinking.
-   * We need to lock all of the port-resources (not EHCI common) until
-   * the transfer is complete.  But we can't use the common EHCI exclsem
-   * or we will deadlock while waiting (because the working thread that
-   * wakes this thread up needs the exclsem).
-   */
-#warning REVISIT
-  sam_givesem(&g_ehci.exclsem);
-
-  /* Wait for the IOC completion event */
-
-  ret = sam_ioc_wait(epinfo);
-
-  /* Re-aquire the EHCI semaphore.  The caller expects to be holding
-   * this upon return.
-   */
-
-  sam_takesem(&g_ehci.exclsem);
-
-#if 0 /* Does not seem to be needed */
-  /* Was there a data buffer?  Was this an OUT transfer? */
-
-  if (buffer != NULL && buflen > 0 && !dirin)
-    {
-      /* We have received data from the host -- unless there was an error.
-       * in any event, we will invalidate the data buffer so that we will
-       * reload any new data freshly DMAed into the user buffer.
-       *
-       * NOTE: This might be un-necessary.  We cleaned and invalidated the
-       * D-Cache prior to starting the DMA so the D-Cache should still be
-       * invalid in this memory region.
-       */
-
-      arch_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
-    }
-#endif
-
-  /* Did sam_ioc_wait() report an error? */
-
-  if (ret < 0)
-    {
-      usbhost_trace1(EHCI_TRACE1_TRANSFER_FAILED, -ret);
-      goto errout_with_iocwait;
-    }
-
-  /* Transfer completed successfully.  Return the number of bytes
-   * transferred.
-   */
-
-  return epinfo->xfrd;
+  return OK;
 
   /* Clean-up after an error */
 
 errout_with_qh:
   sam_qh_discard(qh);
-errout_with_iocwait:
-  epinfo->iocwait = false;
-  return (ssize_t)ret;
+  return ret;
 }
 
 /*******************************************************************************
- * Name: sam_intr_transfer
+ * Name: sam_intr_setup
  *
  * Description:
  *   Process a IN or OUT request on any interrupt endpoint by inserting a qTD
@@ -2187,23 +2118,18 @@ errout_with_iocwait:
  *     frame list), followed by shorter poll rates, with queue heads with a
  *     poll rate of one, on the very end."
  *
- * Assumption:  The caller holds the EHCI exclsem.  The caller must be aware
- *   that the EHCI exclsem will released while waiting for the transfer to
- *   complete, but will be re-aquired when before returning.  The state of
- *   EHCI resources could be very different upon return.
+ * Assumption:  The caller holds the EHCI exclsem.
  *
  * Returned value:
- *   On success, this function returns the number of bytes actually transferred.
- *   For control transfers, this size includes the size of the control request
- *   plus the size of the data (which could be short); For bulk transfers, this
- *   will be the number of data bytes transfers (which could be short).
+ *   Zero (OK) is returned on success; a negated errno value is return on
+ *   any failure.
  *
  *******************************************************************************/
 
 #ifndef CONFIG_USBHOST_INT_DISABLE
-static ssize_t sam_intr_transfer(struct sam_rhport_s *rhport,
-                                 struct sam_epinfo_s *epinfo,
-                                 uint8_t *buffer, size_t buflen)
+static int sam_intr_setup(struct sam_rhport_s *rhport,
+                          struct sam_epinfo_s *epinfo,
+                          uint8_t *buffer, size_t buflen)
 {
   struct sam_qh_s *qh;
   struct sam_qtd_s *qtd;
@@ -2223,27 +2149,17 @@ static ssize_t sam_intr_transfer(struct sam_rhport_s *rhport,
 
   DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
 
-  /* Set the request for the IOC event well BEFORE enabling the transfer. */
-
-  ret = sam_ioc_setup(rhport, epinfo);
-  if (ret != OK)
-    {
-      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
-      return ret;
-    }
-
   /* Create and initialize a Queue Head (QH) structure for this transfer */
 
   qh = sam_qh_create(rhport, epinfo);
   if (qh == NULL)
     {
       usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0);
-      ret = -ENOMEM;
-      goto errout_with_iocwait;
+      return -ENOMEM;
     }
 
-  /* Extra TOKEN bits include the data toggle, the data PID, and and indication to interrupt at the end of this
-   * transfer.
+  /* Extra TOKEN bits include the data toggle, the data PID, and an
+   * indication to interrupt at the end of this transfer.
    */
 
   tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT;
@@ -2289,6 +2205,38 @@ static ssize_t sam_intr_transfer(struct sam_rhport_s *rhport,
 
   regval |= EHCI_USBCMD_PSEN;
   sam_putreg(regval, &HCOR->usbcmd);
+  return OK;
+
+  /* Clean-up after an error */
+
+errout_with_qh:
+  sam_qh_discard(qh);
+  return ret;
+}
+#endif /* CONFIG_USBHOST_INT_DISABLE */
+
+/*******************************************************************************
+ * Name: sam_transfer_wait
+ *
+ * Description:
+ *   Wait for an IN or OUT transfer to complete.
+ *
+ * Assumption:  The caller holds the EHCI exclsem.  The caller must be aware
+ *   that the EHCI exclsem will released while waiting for the transfer to
+ *   complete, but will be re-acquired when before returning.  The state of
+ *   EHCI resources could be very different upon return.
+ *
+ * Returned value:
+ *   On success, this function returns the number of bytes actually transferred.
+ *   For control transfers, this size includes the size of the control request
+ *   plus the size of the data (which could be short); For bulk transfers, this
+ *   will be the number of data bytes transfers (which could be short).
+ *
+ *******************************************************************************/
+
+static ssize_t sam_transfer_wait(struct sam_epinfo_s *epinfo)
+{
+  int ret;
 
   /* Release the EHCI semaphore while we wait.  Other threads need the
    * opportunity to access the EHCI resources while we wait.
@@ -2306,33 +2254,49 @@ static ssize_t sam_intr_transfer(struct sam_rhport_s *rhport,
 
   ret = sam_ioc_wait(epinfo);
 
-  /* Re-aquire the EHCI semaphore.  The caller expects to be holding
+  /* Wait for the IOC completion event */
+
+  ret = sam_ioc_wait(epinfo);
+
+  /* Re-acquire the EHCI semaphore.  The caller expects to be holding
    * this upon return.
    */
 
   sam_takesem(&g_ehci.exclsem);
 
+#if 0 /* Does not seem to be needed */
+  /* Was there a data buffer?  Was this an OUT transfer? */
+
+  if (buffer != NULL && buflen > 0 && !dirin)
+    {
+      /* We have received data from the host -- unless there was an error.
+       * in any event, we will invalidate the data buffer so that we will
+       * reload any new data freshly DMAed into the user buffer.
+       *
+       * NOTE: This might be un-necessary.  We cleaned and invalidated the
+       * D-Cache prior to starting the DMA so the D-Cache should still be
+       * invalid in this memory region.
+       */
+
+      cp15_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
+    }
+#endif
+
   /* Did sam_ioc_wait() report an error? */
 
   if (ret < 0)
     {
       usbhost_trace1(EHCI_TRACE1_TRANSFER_FAILED, -ret);
-      goto errout_with_iocwait;
+      epinfo->iocwait = false;
+      return (ssize_t)ret;
     }
 
-  /* Transfer completed successfully.  Return the number of bytes transferred */
+  /* Transfer completed successfully.  Return the number of bytes
+   * transferred.
+   */
 
   return epinfo->xfrd;
-
-  /* Clean-up after an error */
-
-errout_with_qh:
-  sam_qh_discard(qh);
-errout_with_iocwait:
-  epinfo->iocwait = false;
-  return (ssize_t)ret;
 }
-#endif
 
 /*******************************************************************************
  * EHCI Interrupt Handling
@@ -3035,7 +2999,7 @@ static int sam_wait(FAR struct usbhost_connection_s *conn,
 
       for (rhpndx = 0; rhpndx < SAM_EHCI_NRHPORT; rhpndx++)
         {
-          struct lpc31_rhport_s *rhport;
+          struct sam_rhport_s *rhport;
           struct usbhost_hubport_s *connport;
 
           /* Has the connection state changed on the RH port? */
@@ -3732,6 +3696,7 @@ static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
   struct sam_epinfo_s *ep0info = (struct sam_epinfo_s *)ep0;
   uint16_t len;
   ssize_t nbytes;
+  int ret;
 
   DEBUGASSERT(rhport != NULL && ep0info != NULL && req != NULL);
 
@@ -3751,11 +3716,35 @@ static int sam_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
 
   sam_takesem(&g_ehci.exclsem);
 
-  /* Now perform the transfer */
+  /* Set the request for the IOC event well BEFORE initiating the transfer. */
+
+  ret = sam_ioc_setup(rhport, ep0info);
+  if (ret != OK)
+    {
+      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
+      goto errout_with_sem;
+    }
+
+  /* Now initiate the transfer */
+
+  ret = sam_async_setup(rhport, ep0info, req, buffer, len);
+  if (ret < 0)
+    {
+      udbg("ERROR: sam_async_setup failed: %d\n", ret);
+      goto errout_with_iocwait;
+    }
+
+  /* And wait for the transfer to complete */
+
+  nbytes = sam_transfer_wait(ep0info);
+  sam_givesem(&g_ehci.exclsem);
+  return nbytes >= 0 ? OK : (int)nbytes;
 
-  nbytes = sam_async_transfer(rhport, ep0info, req, buffer, len);
+errout_with_iocwait:
+  ep0info->iocwait = false;
+errout_with_sem:
   sam_givesem(&g_ehci.exclsem);
-  return nbytes >=0 ? OK : (int)nbytes;
+  return ret;
 }
 
 static int sam_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
@@ -3812,6 +3801,7 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
   struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
   struct sam_epinfo_s *epinfo = (struct sam_epinfo_s *)ep;
   ssize_t nbytes;
+  int ret;
 
   DEBUGASSERT(rhport && epinfo && buffer && buflen > 0);
 
@@ -3819,17 +3809,26 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
 
   sam_takesem(&g_ehci.exclsem);
 
-  /* Perform the transfer */
+  /* Set the request for the IOC event well BEFORE initiating the transfer. */
+
+  ret = sam_ioc_setup(rhport, epinfo);
+  if (ret != OK)
+    {
+      usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret);
+      goto errout_with_sem;
+    }
+
+  /* Initiate the transfer */
 
   switch (epinfo->xfrtype)
     {
       case USB_EP_ATTR_XFER_BULK:
-        nbytes = sam_async_transfer(rhport, epinfo, NULL, buffer, buflen);
+        ret = sam_async_setup(rhport, epinfo, NULL, buffer, buflen);
         break;
 
 #ifndef CONFIG_USBHOST_INT_DISABLE
       case USB_EP_ATTR_XFER_INT:
-        nbytes = sam_intr_transfer(rhport, epinfo, buffer, buflen);
+        ret = sam_intr_setup(rhport, epinfo, buffer, buflen);
         break;
 #endif
 
@@ -3840,12 +3839,28 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
       case USB_EP_ATTR_XFER_CONTROL:
       default:
         usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype);
-        nbytes = -ENOSYS;
+        ret = -ENOSYS;
         break;
     }
 
+  /* Check for errors in the setup of the transfer */
+
+  if (ret < 0)
+    {
+      goto errout_with_iocwait;
+    }
+
+  /* Then wait for the transfer to complete */
+
+  nbytes = sam_transfer_wait(epinfo);
   sam_givesem(&g_ehci.exclsem);
-  return nbytes >=0 ? OK : (int)nbytes;
+  return nbytes >= 0 ? OK : (int)nbytes;
+
+errout_with_iocwait:
+  epinfo->iocwait = false;
+errout_with_sem:
+  sam_givesem(&g_ehci.exclsem);
+  return ret;
 }
 
 /*******************************************************************************