OHCI HCDs will not support cancellation of syncrhonous transfers

This commit is contained in:
Gregory Nutt 2015-05-11 10:36:17 -06:00
parent 0536435ff8
commit 102554adfe
2 changed files with 144 additions and 44 deletions

View File

@ -1568,7 +1568,7 @@ static int lpc17_ctrltd(struct lpc17_usbhost_s *priv, struct lpc17_ed_s *ed,
}
else
{
uvdbg("Bad TD completion status: %d\n", xfrinfo->tdstatus);
udbg("ERROR: Bad TD completion status: %d\n", xfrinfo->tdstatus);
ret = xfrinfo->tdstatus == TD_CC_STALL ? -EPERM : -EIO;
}
}
@ -2957,10 +2957,26 @@ static ssize_t lpc17_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
}
else
{
/* Return an I/O error */
/* Map the bad completion status to something that a class driver
* might understand.
*/
udbg("ERROR: Bad TD completion status: %d\n", xfrinfo->tdstatus);
nbytes = -EIO;
switch (xfrinfo->tdstatus)
{
case TD_CC_STALL:
nbytes = -EPERM;
break;
case TD_CC_USER:
nbytes = -ESHUTDOWN;
break;
default:
nbytes = -EIO;
break;
}
}
errout_with_wdhwait:
@ -3031,10 +3047,26 @@ static void lpc17_asynch_completion(struct lpc17_usbhost_s *priv,
}
else
{
/* Provide an I/O error indication */
/* Map the bad completion status to something that a class driver
* might understand.
*/
udbg("ERROR: Bad TD completion status: %d\n", xfrinfo->tdstatus);
nbytes = -EIO;
switch (xfrinfo->tdstatus)
{
case TD_CC_STALL:
nbytes = -EPERM;
break;
case TD_CC_USER:
nbytes = -ESHUTDOWN;
break;
default:
nbytes = -EIO;
break;
}
}
#if LPC17_IOBUFFERS > 0
@ -3209,14 +3241,14 @@ errout_with_sem:
#ifdef CONFIG_USBHOST_ASYNCH
static int lpc17_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
{
struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr;
struct lpc17_ed_s *ed = (struct lpc17_ed_s *)ep;
struct lpc17_gtd_s *td;
struct lpc17_gtd_s *next;
struct lpc17_xfrinfo_s *xfrinfo;
irqstate_t flags;
int ret = OK;
DEBUGASSERT(ed != NULL);
DEBUGASSERT(priv != NULL && ed != NULL);
/* These first steps must be atomic as possible */
@ -3227,40 +3259,66 @@ static int lpc17_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
xfrinfo = ed->xfrinfo;
if (xfrinfo)
{
/* It would be an usage error to use the interface to try to cancel a
* synchronous transfer (wdhwait == true).
/* It might be possible for no transfer to be in progress (callback == NULL
* and wdhwait == false)
*/
DEBUGASSERT(xfrinfo->wdhwait == false);
/* We really need some kind of atomic test and set to do this right */
td = (struct lpc17_gtd_s *)(ed->hw.headp & ED_HEADP_ADDR_MASK);
ed->hw.headp = LPC17_TDTAIL_ADDR;
ed->xfrinfo = NULL;
/* Free all transfer descriptors that were connected to the ED */
DEBUGASSERT(td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR);
while (td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR)
if (xfrinfo->callback || xfrinfo->wdhwait)
{
next = (struct lpc17_gtd_s *)td->hw.nexttd;
lpc17_tdfree(td);
td = next;
/* We really need some kind of atomic test and set to do this right */
td = (struct lpc17_gtd_s *)(ed->hw.headp & ED_HEADP_ADDR_MASK);
ed->hw.headp = LPC17_TDTAIL_ADDR;
ed->xfrinfo = NULL;
/* Free all transfer descriptors that were connected to the ED */
DEBUGASSERT(td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR);
while (td != (struct lpc17_gtd_s *)LPC17_TDTAIL_ADDR)
{
next = (struct lpc17_gtd_s *)td->hw.nexttd;
lpc17_tdfree(td);
td = next;
}
xfrinfo->tdstatus = TD_CC_USER;
/* If there is a thread waiting for the transfer to complete, then
* wake up the thread.
*/
if (xfrinfo->wdhwait)
{
/* Wake up the waiting thread */
lpc17_givesem(&ed->wdhsem);
xfrinfo->wdhwait = false;
/* And free the transfer structure */
lpc17_free_xfrinfo(xfrinfo);
ed->xfrinfo = NULL;
}
else
{
/* Otherwise, perform the callback and free the transfer structure */
lpc17_asynch_completion(priv, ed);
}
}
else
{
/* Just free the transfer structure */
ret = xfrinfo->wdhwait ? -EINVAL : OK;
/* Free the transfer structure */
lpc17_free_xfrinfo(xfrinfo);
ed->xfrinfo = NULL;
lpc17_free_xfrinfo(xfrinfo);
ed->xfrinfo = NULL;
}
}
/* Determine the return value */
irqrestore(flags);
return ret;
return OK;
}
#endif /* CONFIG_USBHOST_ASYNCH */

View File

@ -1914,7 +1914,7 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, struct sam_eplist_s *eplist,
{
usbhost_trace2(OHCI_TRACE2_BADTDSTATUS, RHPORT(rhport),
edctrl->tdstatus);
ret = -EIO;
ret = edctrl->tdstatus == TD_CC_STALL ? -EPERM : -EIO;
}
}
@ -3389,7 +3389,20 @@ static ssize_t sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
/* A transfer error occurred */
usbhost_trace2(OHCI_TRACE2_BADTDSTATUS, RHPORT(rhport), ed->tdstatus);
ret = ed->tdstatus == TD_CC_STALL ? -EPERM : -EIO;
switch (ed->tdstatus)
{
case TD_CC_STALL:
ret = -EPERM;
break;
case TD_CC_USER:
ret = -ESHUTDOWN;
break;
default:
ret = -EIO;
break;
}
errout:
/* Make sure that there is no outstanding request on this endpoint */
@ -3455,8 +3468,26 @@ static void sam_asynch_completion(struct sam_eplist_s *eplist)
}
else
{
/* Map the bad completion status to something that a class driver
* might understand.
*/
usbhost_trace1(OHCI_TRACE1_BADTDSTATUS, ed->tdstatus);
nbytes = (ed->tdstatus == TD_CC_STALL) ? -EPERM : -EIO;
switch (ed->tdstatus)
{
case TD_CC_STALL:
nbytes = -EPERM;
break;
case TD_CC_USER:
nbytes = -ESHUTDOWN;
break;
default:
nbytes = -EIO;
break;
}
}
/* Extract the callback information before freeing the buffer */
@ -3605,18 +3636,11 @@ static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
flags = irqsave();
/* It might be possible for no transfer to be in progress (callback == NULL),
* but it would be an usage error to use the interface to try to cancel a
* synchronous transfer (wdhwait == true).
/* It might be possible for no transfer to be in progress (callback == NULL
* and wdhwait == false)
*/
DEBUGASSERT(eplist->wdhwait == false);
if (eplist->wdhwait)
{
return -EINVAL;
}
if (eplist->callback)
if (eplist->callback || eplist->wdhwait)
{
/* We really need some kind of atomic test and set to do this right */
@ -3636,6 +3660,24 @@ static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
sam_tdfree(td);
td = next;
}
ed->tdstatus = TD_CC_USER;
/* If there is a thread waiting for the transfer to complete, then
* wake up the thread.
*/
if (eplist->wdhwait)
{
sam_givesem(&eplist->wdhsem);
eplist->wdhwait = false;
}
else
{
/* Otherwise, perform the callback */
sam_asynch_completion(eplist);
}
}
/* Reset any pending activity indications */