Commit 6bfb2519 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[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.
parent 59941877
...@@ -840,13 +840,16 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur ...@@ -840,13 +840,16 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
urb->setup_dma); urb->setup_dma);
/* /*
* If direction is "send", change the frame from SETUP (0x2D) * If direction is "send", change the packet ID from SETUP (0x2D)
* to OUT (0xE1). Else change it from SETUP to IN (0x69). * 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 (usb_pipeout(urb->pipe))
destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) else {
destination ^= (USB_PID_SETUP ^ USB_PID_IN);
status |= TD_CTRL_SPD; status |= TD_CTRL_SPD;
}
/* /*
* Build the DATA TD's * Build the DATA TD's
...@@ -1101,17 +1104,20 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb ...@@ -1101,17 +1104,20 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
status = uhci_maxerr(3) | TD_CTRL_ACTIVE; status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
if (urb->dev->speed == USB_SPEED_LOW) if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS; status |= TD_CTRL_LS;
if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) if (usb_pipein(urb->pipe))
status |= TD_CTRL_SPD; status |= TD_CTRL_SPD;
/* /*
* Build the DATA TD's * Build the DATA TD's
*/ */
do { /* Allow zero length packets */ do { /* Allow zero length packets */
int pktsze = len; int pktsze = maxsze;
if (pktsze > maxsze) if (pktsze >= len) {
pktsze = maxsze; pktsze = len;
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
status &= ~TD_CTRL_SPD;
}
td = uhci_alloc_td(uhci, urb->dev); td = uhci_alloc_td(uhci, urb->dev);
if (!td) if (!td)
...@@ -1154,6 +1160,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb ...@@ -1154,6 +1160,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
} }
/* Set the flag on the last packet */ /* Set the flag on the last packet */
if (!(urb->transfer_flags & URB_NO_INTERRUPT))
td->status |= cpu_to_le32(TD_CTRL_IOC); td->status |= cpu_to_le32(TD_CTRL_IOC);
qh = uhci_alloc_qh(uhci, urb->dev); qh = uhci_alloc_qh(uhci, urb->dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment