>From 9060de6f21a4e2d7ea108040a9d86ca04d3909ee Mon Sep 17 00:00:00 2001 From: Libin Yang <libin.yang@xxxxxxx> Date: Fri, 26 Feb 2010 11:01:17 +0800 Subject: [PATCH 5/7] xHCI: isoc interrupt and error handling This patch implements the interrupt and error handling for isoc transfer. Signed-off-by: Libin Yang <libin.yang@xxxxxxx> --- drivers/usb/host/xhci-ring.c | 130 ++++++++++++++++++++++++++++++++++++++++- 1 files changed, 126 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 281284a..548cf2e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1223,6 +1223,17 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n"); status = -ENOSR; break; + case COMP_UNDERRUN: + xhci_warn(xhci, "WARN: underrun event on endpoint\n"); + case COMP_OVERRUN: + xhci_warn(xhci, "WARN: overrun event on endpoint\n"); + case COMP_BW_OVER: + xhci_warn(xhci, "WARN: bandwidth overrun event on endpoint\n"); + case COMP_MISSED_INT: + xhci_warn(xhci, "WARN: missed service event on endpoint\n"); + case COMP_BUFF_OVER: + xhci_warn(xhci, "WARN: buffer overrun event on endpoint\n"); + goto handle_isoc; default: if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { status = 0; @@ -1311,6 +1322,118 @@ static int handle_tx_event(struct xhci_hcd *xhci, } } } + } else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { + int skip_td; + int idx; + int len; + union xhci_trb *cur_trb; + struct xhci_segment *cur_seg; +handle_isoc: + skip_td = 0; + urb = td->urb; + urb_priv = urb->hcpriv; + idx = urb_priv->td_cnt; + + /* handle completion code */ + switch (trb_comp_code) { + case COMP_SUCCESS: + urb->iso_frame_desc[idx].status = 0; + xhci_dbg(xhci, "Successful isoc " + "transfer!\n"); + break; + case COMP_UNDERRUN: + urb->iso_frame_desc[idx].status = -EREMOTEIO; + break; + case COMP_OVERRUN: + urb->iso_frame_desc[idx].status = -EOVERFLOW; + break; + case COMP_BW_OVER: + urb->iso_frame_desc[idx].status = -ECOMM; + skip_td = 1; + break; + case COMP_MISSED_INT: + urb->iso_frame_desc[idx].status = -ECOMM; + skip_td = 1; + break; + case COMP_BUFF_OVER: + urb->iso_frame_desc[idx].status = -EOVERFLOW; + skip_td = 1; + break; + case COMP_STALL: + urb->iso_frame_desc[idx].status = -EPROTO; + skip_td = 1; + break; + case COMP_BABBLE: + urb->iso_frame_desc[idx].status = -EOVERFLOW; + skip_td = 1; + break; + case COMP_STOP_INVAL: + urb->iso_frame_desc[idx].status = -EREMOTEIO; + ep->stopped_td = td; + ep->stopped_trb = event_trb; + break; + case COMP_STOP: + urb->iso_frame_desc[idx].status = -EREMOTEIO; + ep->stopped_td = td; + ep->stopped_trb = event_trb; + break; + default: + urb->iso_frame_desc[idx].status = -1; + break; + } + + /* calc actual length */ + if (trb_comp_code == COMP_SUCCESS || skip_td == 1) { + urb->iso_frame_desc[idx].actual_length = + urb->iso_frame_desc[idx].length; + urb->actual_length += urb->iso_frame_desc[idx].length; + } else { + for (cur_trb = ep_ring->dequeue, + cur_seg = ep_ring->deq_seg; cur_trb != event_trb; + next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { + if (TRB_TYPE(cur_trb->generic.field[3]) != + TRB_TR_NOOP && + TRB_TYPE(cur_trb->generic.field[3]) != + TRB_LINK) + len += + TRB_LEN(cur_trb->generic.field[2]); + } + len += TRB_LEN(cur_trb->generic.field[2]) - + TRB_LEN(event->transfer_len); + urb->iso_frame_desc[idx].actual_length = len; + urb->actual_length += len; + } + + if (trb_comp_code == COMP_SUCCESS || skip_td == 1) { + /* update the ring dequeue pointer */ + while (ep_ring->dequeue != td->last_trb) + inc_deq(xhci, ep_ring, false); + inc_deq(xhci, ep_ring, false); + + /* remove td */ + list_del(&td->td_list); + if (!list_empty(&td->cancelled_td_list)) + list_del(&td->cancelled_td_list); + + kfree(urb_priv->td[idx]); + urb_priv->td[idx] = NULL; + urb_priv->td_cnt++; + } + + inc_deq(xhci, xhci->event_ring, true); + xhci_set_hc_event_deq(xhci); + + if (urb_priv->td_cnt == urb_priv->length) { + kfree(urb_priv); + 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); + } + return 0; } else { switch (trb_comp_code) { case COMP_SUCCESS: @@ -1330,9 +1453,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, &td->urb->ep->desc)) xhci_dbg(xhci, "Successful interrupt " "transfer!\n"); - else - xhci_dbg(xhci, "Successful isoc " - "transfer!\n"); status = 0; } break; @@ -1370,7 +1490,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, else status = 0; } - /* Don't overwrite a previously set error code */ + /* + * Don't overwrite a previously set error code + */ if (status == -EINPROGRESS) { if (td->urb->transfer_flags & URB_SHORT_NOT_OK) status = -EREMOTEIO; -- 1.6.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