On Tue, Jul 20, 2010 at 04:49:25PM +0800, Andiry Xu wrote: > >From 02b3e01af9fef97d113f30de96c07f6a4c24a895 Mon Sep 17 00:00:00 2001 > From: Andiry Xu <andiry.xu@xxxxxxx> > Date: Thu, 15 Jul 2010 16:14:09 +0800 > Subject: [PATCH 06/10] xHCI: Missed Service Error Event process > > This patch adds mechanism to process Missed Service Error Event. > Sometimes the xHC is unable to process the isoc TDs in time, it will > generate Missed Service Error Event. In this case some TDs on the ring are > not processed and missed. When encounter a Missed Servce Error Event, set > the skip flag of the ep, and process the missed TDs until reach the next > processed TD, then clear the skip flag. > > Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx> Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> > --- > drivers/usb/host/xhci-mem.c | 1 + > drivers/usb/host/xhci-ring.c | 158 ++++++++++++++++++++++++++++------------- > drivers/usb/host/xhci.h | 8 ++ > 3 files changed, 117 insertions(+), 50 deletions(-) > > diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c > index 8cc824b..5763dd3 100644 > --- a/drivers/usb/host/xhci-mem.c > +++ b/drivers/usb/host/xhci-mem.c > @@ -1123,6 +1123,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, > virt_dev->num_rings_cached--; > xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring); > } > + virt_dev->eps[ep_index].skip = false; > ep_ring = virt_dev->eps[ep_index].new_ring; > ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state; > > diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c > index 5bb12fe..4c35010 100644 > --- a/drivers/usb/host/xhci-ring.c > +++ b/drivers/usb/host/xhci-ring.c > @@ -1675,6 +1675,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, > "still with TDs queued?\n", > TRB_TO_SLOT_ID(event->flags), ep_index); > goto cleanup; > + case COMP_MISSED_INT: > + /* > + * When encounter missed service error, one or more isoc tds > + * may be missed by xHC. > + * Set skip flag of the ep_ring; Complete the missed tds as > + * short transfer when process the ep_ring next time. > + */ > + ep->skip = true; > + xhci_dbg(xhci, "Miss service interval error, set skip flag\n"); > + goto cleanup; > default: > if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { > status = 0; > @@ -1685,60 +1695,108 @@ static int handle_tx_event(struct xhci_hcd *xhci, > goto cleanup; > } > > - /* This TRB should be in the TD at the head of this ring's TD list */ > - if (list_empty(&ep_ring->td_list)) { > - xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", > - TRB_TO_SLOT_ID(event->flags), ep_index); > - xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", > - (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); > - xhci_print_trb_offsets(xhci, (union xhci_trb *) event); > - goto cleanup; > - } > - td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); > - > - /* Is this a TRB in the currently executing TD? */ > - event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, > - td->last_trb, event_dma); > - if (!event_seg) { > - /* HC is busted, give up! */ > - xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); > - return -ESHUTDOWN; > - } > - event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; > + do { > + /* This TRB should be in the TD at the head of this ring's > + * TD list. > + */ > + if (list_empty(&ep_ring->td_list)) { > + xhci_warn(xhci, "WARN Event TRB for slot %d ep %d " > + "with no TDs queued?\n", > + TRB_TO_SLOT_ID(event->flags), ep_index); > + xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", > + (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); > + xhci_print_trb_offsets(xhci, (union xhci_trb *) event); > + if (ep->skip) { > + ep->skip = false; > + xhci_dbg(xhci, "td_list is empty while skip " > + "flag set. Clear skip flag.\n"); > + } > + ret = 0; > + goto cleanup; > + } > > - /* Now update the urb's actual_length and give back to the core */ > - /* Was this a control transfer? */ > - if (usb_endpoint_xfer_control(&td->urb->ep->desc)) > - ret = process_ctrl_td(xhci, td, event_trb, event, ep, > - &status); > - else > - ret = process_bulk_intr_td(xhci, td, event_trb, event, ep, > - &status); > + td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); > + /* Is this a TRB in the currently executing TD? */ > + event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, > + td->last_trb, event_dma); > + if (event_seg && ep->skip) { > + xhci_dbg(xhci, "Found td. Clear skip flag.\n"); > + ep->skip = false; > + } > + if (!event_seg && > + (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) { > + /* HC is busted, give up! */ > + xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not " > + "part of current TD\n"); > + return -ESHUTDOWN; > + } > > -cleanup: > - inc_deq(xhci, xhci->event_ring, true); > - xhci_set_hc_event_deq(xhci); > + if (event_seg) { > + event_trb = &event_seg->trbs[(event_dma - > + event_seg->dma) / sizeof(*event_trb)]; > + /* > + * No-op TRB should not trigger interrupts. > + * If event_trb is a no-op TRB, it means the > + * corresponding TD has been cancelled. Just ignore > + * the TD. > + */ > + if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK) > + == TRB_TYPE(TRB_TR_NOOP)) { > + xhci_dbg(xhci, "event_trb is a no-op TRB. " > + "Skip it\n"); > + goto cleanup; > + } > + } > > - /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */ > - if (ret) { > - urb = td->urb; > - /* Leave the TD around for the reset endpoint function to use > - * (but only if it's not a control endpoint, since we already > - * queued the Set TR dequeue pointer command for stalled > - * control endpoints). > + /* Now update the urb's actual_length and give back to > + * the core > */ > - if (usb_endpoint_xfer_control(&urb->ep->desc) || > - (trb_comp_code != COMP_STALL && > - trb_comp_code != COMP_BABBLE)) > - kfree(td); > - > - usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); > - xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n", > - urb, urb->actual_length, status); > - spin_unlock(&xhci->lock); > - usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status); > - spin_lock(&xhci->lock); > - } > + if (usb_endpoint_xfer_control(&td->urb->ep->desc)) > + ret = process_ctrl_td(xhci, td, event_trb, event, ep, > + &status); > + else > + ret = process_bulk_intr_td(xhci, td, event_trb, event, > + ep, &status); > + > +cleanup: > + /* > + * Do not update event ring dequeue pointer if ep->skip is set. > + * Will roll back to continue process missed tds. > + */ > + if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { > + inc_deq(xhci, xhci->event_ring, true); > + xhci_set_hc_event_deq(xhci); > + } > + > + if (ret) { > + urb = td->urb; > + /* Leave the TD around for the reset endpoint function > + * to use(but only if it's not a control endpoint, > + * since we already queued the Set TR dequeue pointer > + * command for stalled control endpoints). > + */ > + if (usb_endpoint_xfer_control(&urb->ep->desc) || > + (trb_comp_code != COMP_STALL && > + trb_comp_code != COMP_BABBLE)) > + kfree(td); > + > + usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); > + xhci_dbg(xhci, "Giveback URB %p, len = %d, " > + "status = %d\n", > + urb, urb->actual_length, status); > + spin_unlock(&xhci->lock); > + usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status); > + spin_lock(&xhci->lock); > + } > + > + /* > + * If ep->skip is set, it means there are missed tds on the > + * endpoint ring need to take care of. > + * Process them as short transfer until reach the td pointed by > + * the event. > + */ > + } while (ep->skip && trb_comp_code != COMP_MISSED_INT); > + > return 0; > } > > diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h > index 6c7e343..01da844 100644 > --- a/drivers/usb/host/xhci.h > +++ b/drivers/usb/host/xhci.h > @@ -720,6 +720,14 @@ struct xhci_virt_ep { > struct timer_list stop_cmd_timer; > int stop_cmds_pending; > struct xhci_hcd *xhci; > + /* > + * Sometimes the xHC can not process isochronous endpoint ring quickly > + * enough, and it will miss some isoc tds on the ring and generate > + * a Missed Service Error Event. > + * Set skip flag when receive a Missed Service Error Event and > + * process the missed tds on the endpoint ring. > + */ > + bool skip; > }; > > struct xhci_virt_device { > -- > 1.7.0.4 > > > > -- > 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 -- 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