USB: whci-hcd: correctly handle sg lists longer than QTD_MAX_XFER_SIZE.
authorDavid Vrabel <david.vrabel@csr.com>
Mon, 7 Dec 2009 13:50:39 +0000 (13:50 +0000)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Dec 2009 19:55:26 +0000 (11:55 -0800)
When building qTDs (sTDs) from a scatter-gather list, the length of the
qTD must be a multiple of wMaxPacketSize if the transfer continues into
another qTD.

This also fixes a link failure on configurations for 32 bit processors
with 64 bit dma_addr_t (e.g., CONFIG_HIGHMEM_64G).

Signed-off-by: David Vrabel <david.vrabel@csr.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/whci/qset.c

index 39e855a..7d4204d 100644 (file)
@@ -465,16 +465,16 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
                         * - the previous one isn't full.
                         *
                         * If a new std is needed but the previous one
-                        * did not end on a wMaxPacketSize boundary
-                        * then this sg list cannot be mapped onto
-                        * multiple qTDs.  Return an error and let the
-                        * caller sort it out.
+                        * was not a whole number of packets then this
+                        * sg list cannot be mapped onto multiple
+                        * qTDs.  Return an error and let the caller
+                        * sort it out.
                         */
                        if (!std
                            || (prev_end & (WHCI_PAGE_SIZE-1))
                            || (dma_addr & (WHCI_PAGE_SIZE-1))
                            || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) {
-                               if (prev_end % qset->max_packet != 0)
+                               if (std->len % qset->max_packet != 0)
                                        return -EINVAL;
                                std = qset_new_std(whc, qset, urb, mem_flags);
                                if (std == NULL) {
@@ -487,14 +487,14 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
                        dma_len = dma_remaining;
 
                        /*
-                        * If the remainder in this element doesn't
-                        * fit in a single qTD, end the qTD on a
-                        * wMaxPacketSize boundary.
+                        * If the remainder of this element doesn't
+                        * fit in a single qTD, limit the qTD to a
+                        * whole number of packets.  This allows the
+                        * remainder to go into the next qTD.
                         */
                        if (std->len + dma_len > QTD_MAX_XFER_SIZE) {
-                               dma_len = QTD_MAX_XFER_SIZE - std->len;
-                               ep = ((dma_addr + dma_len) / qset->max_packet) * qset->max_packet;
-                               dma_len = ep - dma_addr;
+                               dma_len = (QTD_MAX_XFER_SIZE / qset->max_packet)
+                                       * qset->max_packet - std->len;
                        }
 
                        std->len += dma_len;