[PATCH RFC 5/7] xHCI: isoc interrupt and error handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



>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

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux