ECHI HCDs: Separate logic used in synchronous transfers so that it can be used in forthcoming asynchronous transfer

This commit is contained in:
Gregory Nutt 2015-04-27 07:38:20 -06:00
parent 8f5b62add6
commit 30ff783af0
2 changed files with 287 additions and 257 deletions

View File

@ -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. */
nbytes = lpc31_async_transfer(rhport, ep0info, req, buffer, len);
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_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;
}
/*******************************************************************************

View File

@ -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. */
nbytes = sam_async_transfer(rhport, ep0info, req, buffer, len);
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;
return nbytes >= 0 ? OK : (int)nbytes;
errout_with_iocwait:
ep0info->iocwait = false;
errout_with_sem:
sam_givesem(&g_ehci.exclsem);
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;
}
/*******************************************************************************