ChangeSet 1.1673.8.19, 2004/03/25 17:16:33-08:00, stern@rowland.harvard.edu

[PATCH] USB: UHCI: Do short packet detection correctly

This patch makes some simple changes to the way the UHCI driver does short
packet detection.  The current implementation is incorrect in several
ways:

	The Short-Packet-Detect flag is set for OUT transfers, which
	yields undefined behavior according to the UHCI spec.

	It's not set for URBs with URB_SHORT_NOT_OK, which is just the
	opposite of what we want!  Those are the ones where short packets
	do matter.

	It is set for the last packet in a transfer, which causes an
	unnecessary pause in the data flow (except of course that the
	pause _is_ necessary when URB_SHORT_NOT_OK is set).

The patch also implements the URB_NO_INTERRUPT flag for bulk transfers,
which can help improve system performance by reducing interrupt overhead.


 drivers/usb/host/uhci-hcd.c |   27 +++++++++++++++++----------
 1 files changed, 17 insertions(+), 10 deletions(-)


diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c	Wed Apr 14 14:38:47 2004
+++ b/drivers/usb/host/uhci-hcd.c	Wed Apr 14 14:38:47 2004
@@ -840,13 +840,16 @@
 		urb->setup_dma);
 
 	/*
-	 * If direction is "send", change the frame from SETUP (0x2D)
-	 * to OUT (0xE1). Else change it from SETUP to IN (0x69).
+	 * 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.
 	 */
-	destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe));
-
-	if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+	if (usb_pipeout(urb->pipe))
+		destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
+	else {
+		destination ^= (USB_PID_SETUP ^ USB_PID_IN);
 		status |= TD_CTRL_SPD;
+	}
 
 	/*
 	 * Build the DATA TD's
@@ -1101,17 +1104,20 @@
 	status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
 	if (urb->dev->speed == USB_SPEED_LOW)
 		status |= TD_CTRL_LS;
-	if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+	if (usb_pipein(urb->pipe))
 		status |= TD_CTRL_SPD;
 
 	/*
 	 * Build the DATA TD's
 	 */
 	do {	/* Allow zero length packets */
-		int pktsze = len;
+		int pktsze = maxsze;
 
-		if (pktsze > maxsze)
-			pktsze = maxsze;
+		if (pktsze >= len) {
+			pktsze = len;
+			if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+				status &= ~TD_CTRL_SPD;
+		}
 
 		td = uhci_alloc_td(uhci, urb->dev);
 		if (!td)
@@ -1154,7 +1160,8 @@
 	}
 
 	/* Set the flag on the last packet */
-	td->status |= cpu_to_le32(TD_CTRL_IOC);
+	if (!(urb->transfer_flags & URB_NO_INTERRUPT))
+		td->status |= cpu_to_le32(TD_CTRL_IOC);
 
 	qh = uhci_alloc_qh(uhci, urb->dev);
 	if (!qh)
