[PATCH 12/44] UHCI: short control URBs get a status stage

Greg Kroah-Hartman <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
From: Alan Stern <[email protected]>

It has recently been pointed out that short control transfers should
have a status stage, even if they generate an error because
URB_SHORT_NOT_OK was set.  This patch (as935) changes uhci-hcd to
enable the status stage when this happens.

Signed-off-by: Alan Stern <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
 drivers/usb/host/uhci-q.c |   59 +++++++++++++++++++++++++++++----------------
 1 files changed, 38 insertions(+), 21 deletions(-)

diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 4aed305..3bb908c 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -827,8 +827,10 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
 	 * If direction is "send", change the packet ID from SETUP (0x2D)
 	 * to OUT (0xE1).  Else change it from SETUP to IN (0x69) and
 	 * set Short Packet Detect (SPD) for all data packets.
+	 *
+	 * 0-length transfers always get treated as "send".
 	 */
-	if (usb_pipeout(urb->pipe))
+	if (usb_pipeout(urb->pipe) || len == 0)
 		destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
 	else {
 		destination ^= (USB_PID_SETUP ^ USB_PID_IN);
@@ -839,7 +841,12 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
 	 * Build the DATA TDs
 	 */
 	while (len > 0) {
-		int pktsze = min(len, maxsze);
+		int pktsze = maxsze;
+
+		if (len <= pktsze) {		/* The last data packet */
+			pktsze = len;
+			status &= ~TD_CTRL_SPD;
+		}
 
 		td = uhci_alloc_td(uhci);
 		if (!td)
@@ -866,20 +873,10 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
 		goto nomem;
 	*plink = LINK_TO_TD(td);
 
-	/*
-	 * It's IN if the pipe is an output pipe or we're not expecting
-	 * data back.
-	 */
-	destination &= ~TD_TOKEN_PID_MASK;
-	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
-		destination |= USB_PID_IN;
-	else
-		destination |= USB_PID_OUT;
-
+	/* Change direction for the status transaction */
+	destination ^= (USB_PID_IN ^ USB_PID_OUT);
 	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */
 
-	status &= ~TD_CTRL_SPD;
-
 	uhci_add_td_to_urbp(td, urbp);
 	uhci_fill_td(td, status | TD_CTRL_IOC,
 			destination | uhci_explen(0), 0);
@@ -1185,10 +1182,18 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
 				}
 			}
 
+		/* Did we receive a short packet? */
 		} else if (len < uhci_expected_length(td_token(td))) {
 
-			/* We received a short packet */
-			if (urb->transfer_flags & URB_SHORT_NOT_OK)
+			/* For control transfers, go to the status TD if
+			 * this isn't already the last data TD */
+			if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+				if (td->list.next != urbp->td_list.prev)
+					ret = 1;
+			}
+
+			/* For bulk and interrupt, this may be an error */
+			else if (urb->transfer_flags & URB_SHORT_NOT_OK)
 				ret = -EREMOTEIO;
 
 			/* Fixup needed only if this isn't the URB's last TD */
@@ -1208,10 +1213,6 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
 
 err:
 	if (ret < 0) {
-		/* In case a control transfer gets an error
-		 * during the setup stage */
-		urb->actual_length = max(urb->actual_length, 0);
-
 		/* Note that the queue has stopped and save
 		 * the next toggle value */
 		qh->element = UHCI_PTR_TERM;
@@ -1489,9 +1490,25 @@ __acquires(uhci->lock)
 {
 	struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
 
+	if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
+
+		/* urb->actual_length < 0 means the setup transaction didn't
+		 * complete successfully.  Either it failed or the URB was
+		 * unlinked first.  Regardless, don't confuse people with a
+		 * negative length. */
+		urb->actual_length = max(urb->actual_length, 0);
+
+		/* Report erroneous short transfers */
+		if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
+				urb->actual_length <
+					urb->transfer_buffer_length &&
+				urb->status == 0))
+			urb->status = -EREMOTEIO;
+	}
+
 	/* When giving back the first URB in an Isochronous queue,
 	 * reinitialize the QH's iso-related members for the next URB. */
-	if (qh->type == USB_ENDPOINT_XFER_ISOC &&
+	else if (qh->type == USB_ENDPOINT_XFER_ISOC &&
 			urbp->node.prev == &qh->queue &&
 			urbp->node.next != &qh->queue) {
 		struct urb *nurb = list_entry(urbp->node.next,
-- 
1.5.2.2


-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.