LPC17 USB host: Fix some OHCI done head handling when a tranfer is cancelled
This commit is contained in:
parent
6591a36950
commit
87b065253d
@ -1786,87 +1786,107 @@ static int lpc17_usbinterrupt(int irq, void *context)
|
|||||||
* cleared in the interrupt status register.
|
* cleared in the interrupt status register.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
td = (struct lpc17_gtd_s *)HCCA->donehead;
|
td = (struct lpc17_gtd_s *)(HCCA->donehead & HCCA_DONEHEAD_MASK);
|
||||||
HCCA->donehead = 0;
|
HCCA->donehead = 0;
|
||||||
|
|
||||||
/* Process each TD in the write done list */
|
/* Process each TD in the write done list */
|
||||||
|
|
||||||
for (; td; td = next)
|
for (; td; td = next)
|
||||||
{
|
{
|
||||||
|
/* REVISIT: I have encountered bad TDs in the done list linked
|
||||||
|
* after at least one good TD. This is some consequence of how
|
||||||
|
* transfers are being cancelled. But for now, I have only
|
||||||
|
* this work-around.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((uintptr_t)td < LPC17_TDFREE_BASE ||
|
||||||
|
(uintptr_t)td >= (LPC17_TDFREE_BASE + LPC17_TD_SIZE*CONFIG_USBHOST_NTDS))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the ED in which this TD was enqueued */
|
/* Get the ED in which this TD was enqueued */
|
||||||
|
|
||||||
ed = td->ed;
|
ed = td->ed;
|
||||||
DEBUGASSERT(ed != NULL && ed->xfrinfo != NULL);
|
DEBUGASSERT(ed != NULL);
|
||||||
|
|
||||||
|
/* If there is a transfer in progress, then the xfrinfo pointer will be
|
||||||
|
* non-NULL. But it appears that a NULL pointer may be received with a
|
||||||
|
* spurious interrupt such as may occur after a transfer is cancelled.
|
||||||
|
*/
|
||||||
|
|
||||||
xfrinfo = ed->xfrinfo;
|
xfrinfo = ed->xfrinfo;
|
||||||
|
if (xfrinfo)
|
||||||
/* Save the condition code from the (single) TD status/control
|
|
||||||
* word.
|
|
||||||
*/
|
|
||||||
|
|
||||||
xfrinfo->tdstatus = (td->hw.ctrl & GTD_STATUS_CC_MASK) >> GTD_STATUS_CC_SHIFT;
|
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_USB
|
|
||||||
if (xfrinfo->tdstatus != TD_CC_NOERROR)
|
|
||||||
{
|
{
|
||||||
/* The transfer failed for some reason... dump some diagnostic info. */
|
/* Save the condition code from the (single) TD status/control
|
||||||
|
* word.
|
||||||
ulldbg("ERROR: ED xfrtype:%d TD CTRL:%08x/CC:%d RHPORTST1:%08x\n",
|
|
||||||
ed->xfrtype, td->hw.ctrl, xfrinfo->tdstatus,
|
|
||||||
lpc17_getreg(LPC17_USBHOST_RHPORTST1));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Determine the number of bytes actually transfer by
|
|
||||||
* subtracting the buffer start address from the CBP. A value
|
|
||||||
* of zero means that all bytes were transferred.
|
|
||||||
*/
|
|
||||||
|
|
||||||
tmp = (uintptr_t)td->hw.cbp;
|
|
||||||
if (tmp == 0)
|
|
||||||
{
|
|
||||||
/* Set the (fake) CBP to the end of the buffer + 1 */
|
|
||||||
|
|
||||||
tmp = xfrinfo->buflen;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DEBUGASSERT(tmp >= (uintptr_t)xfrinfo->buffer);
|
|
||||||
|
|
||||||
/* Determine the size of the transfer by subtracting the
|
|
||||||
* current buffer pointer (CBP) from the initial buffer
|
|
||||||
* pointer (on packet receipt only).
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
tmp -= (uintptr_t)xfrinfo->buffer;
|
xfrinfo->tdstatus = (td->hw.ctrl & GTD_STATUS_CC_MASK) >> GTD_STATUS_CC_SHIFT;
|
||||||
DEBUGASSERT(tmp < UINT16_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
xfrinfo->xfrd = (uint16_t)tmp;
|
#ifdef CONFIG_DEBUG_USB
|
||||||
|
if (xfrinfo->tdstatus != TD_CC_NOERROR)
|
||||||
|
{
|
||||||
|
/* The transfer failed for some reason... dump some diagnostic info. */
|
||||||
|
|
||||||
/* Return the TD to the free list */
|
ulldbg("ERROR: ED xfrtype:%d TD CTRL:%08x/CC:%d RHPORTST1:%08x\n",
|
||||||
|
ed->xfrtype, td->hw.ctrl, xfrinfo->tdstatus,
|
||||||
|
lpc17_getreg(LPC17_USBHOST_RHPORTST1));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
next = (struct lpc17_gtd_s *)td->hw.nexttd;
|
/* Determine the number of bytes actually transfer by
|
||||||
lpc17_tdfree(td);
|
* subtracting the buffer start address from the CBP. A
|
||||||
|
* value of zero means that all bytes were transferred.
|
||||||
|
*/
|
||||||
|
|
||||||
if (xfrinfo->wdhwait)
|
tmp = (uintptr_t)td->hw.cbp;
|
||||||
{
|
if (tmp == 0)
|
||||||
/* Wake up the thread waiting for the WDH event */
|
{
|
||||||
|
/* Set the (fake) CBP to the end of the buffer + 1 */
|
||||||
|
|
||||||
lpc17_givesem(&ed->wdhsem);
|
tmp = xfrinfo->buflen;
|
||||||
xfrinfo->wdhwait = false;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
DEBUGASSERT(tmp >= (uintptr_t)xfrinfo->buffer);
|
||||||
|
|
||||||
|
/* Determine the size of the transfer by subtracting
|
||||||
|
* the current buffer pointer (CBP) from the initial
|
||||||
|
* buffer pointer (on packet receipt only).
|
||||||
|
*/
|
||||||
|
|
||||||
|
tmp -= (uintptr_t)xfrinfo->buffer;
|
||||||
|
DEBUGASSERT(tmp < UINT16_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
xfrinfo->xfrd = (uint16_t)tmp;
|
||||||
|
|
||||||
|
/* Return the TD to the free list */
|
||||||
|
|
||||||
|
next = (struct lpc17_gtd_s *)td->hw.nexttd;
|
||||||
|
lpc17_tdfree(td);
|
||||||
|
|
||||||
|
if (xfrinfo->wdhwait)
|
||||||
|
{
|
||||||
|
/* Wake up the thread waiting for the WDH event */
|
||||||
|
|
||||||
|
lpc17_givesem(&ed->wdhsem);
|
||||||
|
xfrinfo->wdhwait = false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_USBHOST_ASYNCH
|
#ifdef CONFIG_USBHOST_ASYNCH
|
||||||
/* Perform any pending callbacks for the case of asynchronous
|
/* Perform any pending callbacks for the case of
|
||||||
* transfers.
|
* asynchronous transfers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
else if (xfrinfo->callback)
|
else if (xfrinfo->callback)
|
||||||
{
|
{
|
||||||
DEBUGASSERT(xfrinfo->wdhwait == false);
|
DEBUGASSERT(xfrinfo->wdhwait == false);
|
||||||
lpc17_asynch_completion(priv, ed);
|
lpc17_asynch_completion(priv, ed);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2113,7 +2113,7 @@ static void sam_wdh_bottomhalf(void)
|
|||||||
|
|
||||||
/* Now read the done head. */
|
/* Now read the done head. */
|
||||||
|
|
||||||
td = (struct sam_gtd_s *)sam_virtramaddr(g_hcca.donehead);
|
td = (struct sam_gtd_s *)sam_virtramaddr(g_hcca.donehead & HCCA_DONEHEAD_MASK);
|
||||||
g_hcca.donehead = 0;
|
g_hcca.donehead = 0;
|
||||||
|
|
||||||
/* Process each TD in the write done list */
|
/* Process each TD in the write done list */
|
||||||
|
@ -367,11 +367,17 @@
|
|||||||
/* HccaDoneHead: When the HC reaches the end of a frame and its deferred
|
/* HccaDoneHead: When the HC reaches the end of a frame and its deferred
|
||||||
* interrupt register is 0, it writes the current value of its HcDoneHead to
|
* interrupt register is 0, it writes the current value of its HcDoneHead to
|
||||||
* this location and generates an interrupt.
|
* this location and generates an interrupt.
|
||||||
|
*
|
||||||
|
* The LSB of HCCADoneHead may be set to 1 to indicate that an unmasked
|
||||||
|
* HcInterruptStatus was set when HccaDoneHead was written.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define HCCA_DONEHEAD_OFFSET (0x84)
|
#define HCCA_DONEHEAD_OFFSET (0x84)
|
||||||
#define HCCA_DONEHEAD_BSIZE (4)
|
#define HCCA_DONEHEAD_BSIZE (4)
|
||||||
|
|
||||||
|
#define HCCA_DONEHEAD_MASK 0xfffffffe
|
||||||
|
#define HCCA_DONEHEAD_INTSTA (1 << 0)
|
||||||
|
|
||||||
/* 0x88: 116 bytes reserved */
|
/* 0x88: 116 bytes reserved */
|
||||||
|
|
||||||
#define HCCA_RESERVED_OFFSET (0x88)
|
#define HCCA_RESERVED_OFFSET (0x88)
|
||||||
|
Loading…
Reference in New Issue
Block a user