Hi Andiry and Alex, I've noticed (although I don't have time to confirm again) that the NEC/Rensas host controller seems to return these transaction errors whenever there is a link TRB in the middle of the control transfer TD. Have you also noticed this? If so, we should probably let Rensas know, as it might indicate a hardware bug. Or it could mean we're doing something odd in the xHCI driver. Sarah Sharp On Thu, May 05, 2011 at 06:14:07PM +0800, Andiry Xu wrote: > From: Alex He <alex.he@xxxxxxx> > > xHCI 1.0 spec 4.6.8.1 describes Soft Retry mechanism. > A Soft Retry may effectively be used to recover from a USB Transaction Error > that was due to a temporary error condition. Oftern the delay introduced > between software detecting the error and attempting a Soft Retry is enough > to let the temporary condition clear and allow a successful transfer. > > Signed-off-by: Alex He <alex.he@xxxxxxx> > Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx> > --- > drivers/usb/host/xhci-ring.c | 70 ++++++++++++++++++++++++++++++++++++++++++ > drivers/usb/host/xhci.h | 6 +++ > 2 files changed, 76 insertions(+), 0 deletions(-) > > diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c > index e07162d..d0b8c85 100644 > --- a/drivers/usb/host/xhci-ring.c > +++ b/drivers/usb/host/xhci-ring.c > @@ -1645,6 +1645,18 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, > if (!xhci_requires_manual_halt_cleanup(xhci, > ep_ctx, trb_comp_code)) > break; > + if (trb_comp_code == COMP_TX_ERR && > + xhci->hci_version == 0x100) { > + if (ep->soft_retries++ < SOFT_RETRY) { > + xhci_soft_retry_ep(xhci, slot_id, ep_index, > + ep_ring->stream_id, td, > + event_trb); > + return 0; > + } else { > + ep->soft_retries = 0; > + } > + } > + > xhci_dbg(xhci, "TRB error code %u, " > "halted endpoint index = %u\n", > trb_comp_code, ep_index); > @@ -1818,10 +1830,21 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, > union xhci_trb *event_trb, struct xhci_transfer_event *event, > struct xhci_virt_ep *ep, int *status) > { > + struct xhci_virt_device *xdev; > + unsigned int slot_id; > + struct xhci_slot_ctx *slot_ctx; > + int ep_index; > struct xhci_ring *ep_ring; > union xhci_trb *cur_trb; > struct xhci_segment *cur_seg; > u32 trb_comp_code; > + u32 tt_hub_slot_id; > + > + ep_index = TRB_TO_EP_ID(event->flags) - 1; > + slot_id = TRB_TO_SLOT_ID(event->flags); > + xdev = xhci->devs[slot_id]; > + slot_ctx = xhci_get_slot_ctx(xhci, xdev->out_ctx); > + tt_hub_slot_id = slot_ctx->tt_info && 0xff; > > ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); > trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); > @@ -1852,6 +1875,25 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, > else > *status = 0; > break; > + case COMP_TX_ERR: > + if (xhci->hci_version == 0x100) { > + if (usb_endpoint_xfer_bulk(&td->urb->ep->desc) && > + (ep->soft_retries++ < SOFT_RETRY)) { > + xhci_soft_retry_ep(xhci, slot_id, ep_index, > + ep_ring->stream_id, td, event_trb); > + return 0; > + > + } > + if (usb_endpoint_xfer_int(&td->urb->ep->desc) && > + tt_hub_slot_id == 0 && > + (ep->soft_retries++ < SOFT_RETRY)) { > + xhci_soft_retry_ep(xhci, slot_id, ep_index, > + ep_ring->stream_id, td, event_trb); > + return 0; > + } > + ep->soft_retries = 0; > + } > + break; > default: > /* Others already handled above */ > break; > @@ -3565,3 +3607,31 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id, > return queue_command(xhci, 0, 0, 0, trb_slot_id | trb_ep_index | type, > false); > } > + > +/* > + * A Soft Retry may effectively be used to recver from a USB Transaction Error > + * that was due to a temporary error codition. Details in the Chapter 4.6.8.1 > + * NOTE: > + * 1. can't be called for isoc ep > + * 2. can't be called for interrupt ep behind a TT > + */ > +void xhci_soft_retry_ep(struct xhci_hcd *xhci, int slot_id, > + unsigned int ep_index, unsigned int stream_id, > + struct xhci_td *td, union xhci_trb *event_trb) > +{ > + u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); > + u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); > + u32 type = TRB_TYPE(TRB_RESET_EP); > + u32 tsp_flag = 1 << 9; > + struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; > + > + ep->ep_state |= EP_HALTED; > + ep->stopped_td = td; > + ep->stopped_trb = event_trb; > + ep->stopped_stream = stream_id; > + > + queue_command(xhci, 0, 0, 0, > + trb_slot_id | trb_ep_index | type | tsp_flag, false); > + > + xhci_ring_cmd_db(xhci); > +} > diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h > index 33a49d5..dd45929 100644 > --- a/drivers/usb/host/xhci.h > +++ b/drivers/usb/host/xhci.h > @@ -767,6 +767,9 @@ struct xhci_virt_ep { > * process the missed tds on the endpoint ring. > */ > bool skip; > + /* Soft Retries to prevent an infinite loop */ > + unsigned int soft_retries; > +#define SOFT_RETRY 2 > }; > > struct xhci_virt_device { > @@ -1540,6 +1543,9 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci, > unsigned int slot_id, unsigned int ep_index, > struct xhci_dequeue_state *deq_state); > void xhci_stop_endpoint_command_watchdog(unsigned long arg); > +void xhci_soft_retry_ep(struct xhci_hcd *xhci, int slot_id, > + unsigned int ep_index, unsigned int stream_id, > + struct xhci_td *td, union xhci_trb *event_trb); > void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, > unsigned int ep_index, unsigned int stream_id); > > -- > 1.7.1 > > > -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html