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)