diff options
author | David Vrabel <david.vrabel@csr.com> | 2009-12-07 08:50:39 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 14:55:26 -0500 |
commit | 0d370755dd4ad3d119818579cfa3eb2e9978b3eb (patch) | |
tree | f8b7fa0640f156f4780f3c37a24e7af1f08729c2 /drivers/usb | |
parent | f3f6faa9edf67c1018270793e0547b0f81abb47e (diff) |
USB: whci-hcd: correctly handle sg lists longer than QTD_MAX_XFER_SIZE.
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>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/whci/qset.c | 22 |
1 files changed, 11 insertions, 11 deletions
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index 39e855a55c63..7d4204db0f61 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c | |||
@@ -465,16 +465,16 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u | |||
465 | * - the previous one isn't full. | 465 | * - the previous one isn't full. |
466 | * | 466 | * |
467 | * If a new std is needed but the previous one | 467 | * If a new std is needed but the previous one |
468 | * did not end on a wMaxPacketSize boundary | 468 | * was not a whole number of packets then this |
469 | * then this sg list cannot be mapped onto | 469 | * sg list cannot be mapped onto multiple |
470 | * multiple qTDs. Return an error and let the | 470 | * qTDs. Return an error and let the caller |
471 | * caller sort it out. | 471 | * sort it out. |
472 | */ | 472 | */ |
473 | if (!std | 473 | if (!std |
474 | || (prev_end & (WHCI_PAGE_SIZE-1)) | 474 | || (prev_end & (WHCI_PAGE_SIZE-1)) |
475 | || (dma_addr & (WHCI_PAGE_SIZE-1)) | 475 | || (dma_addr & (WHCI_PAGE_SIZE-1)) |
476 | || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { | 476 | || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { |
477 | if (prev_end % qset->max_packet != 0) | 477 | if (std->len % qset->max_packet != 0) |
478 | return -EINVAL; | 478 | return -EINVAL; |
479 | std = qset_new_std(whc, qset, urb, mem_flags); | 479 | std = qset_new_std(whc, qset, urb, mem_flags); |
480 | if (std == NULL) { | 480 | if (std == NULL) { |
@@ -487,14 +487,14 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u | |||
487 | dma_len = dma_remaining; | 487 | dma_len = dma_remaining; |
488 | 488 | ||
489 | /* | 489 | /* |
490 | * If the remainder in this element doesn't | 490 | * If the remainder of this element doesn't |
491 | * fit in a single qTD, end the qTD on a | 491 | * fit in a single qTD, limit the qTD to a |
492 | * wMaxPacketSize boundary. | 492 | * whole number of packets. This allows the |
493 | * remainder to go into the next qTD. | ||
493 | */ | 494 | */ |
494 | if (std->len + dma_len > QTD_MAX_XFER_SIZE) { | 495 | if (std->len + dma_len > QTD_MAX_XFER_SIZE) { |
495 | dma_len = QTD_MAX_XFER_SIZE - std->len; | 496 | dma_len = (QTD_MAX_XFER_SIZE / qset->max_packet) |
496 | ep = ((dma_addr + dma_len) / qset->max_packet) * qset->max_packet; | 497 | * qset->max_packet - std->len; |
497 | dma_len = ep - dma_addr; | ||
498 | } | 498 | } |
499 | 499 | ||
500 | std->len += dma_len; | 500 | std->len += dma_len; |