Commit 0d370755 authored by David Vrabel's avatar David Vrabel Committed by Greg Kroah-Hartman

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: default avatarDavid Vrabel <david.vrabel@csr.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent f3f6faa9
...@@ -465,16 +465,16 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u ...@@ -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. * - the previous one isn't full.
* *
* If a new std is needed but the previous one * If a new std is needed but the previous one
* did not end on a wMaxPacketSize boundary * was not a whole number of packets then this
* then this sg list cannot be mapped onto * sg list cannot be mapped onto multiple
* multiple qTDs. Return an error and let the * qTDs. Return an error and let the caller
* caller sort it out. * sort it out.
*/ */
if (!std if (!std
|| (prev_end & (WHCI_PAGE_SIZE-1)) || (prev_end & (WHCI_PAGE_SIZE-1))
|| (dma_addr & (WHCI_PAGE_SIZE-1)) || (dma_addr & (WHCI_PAGE_SIZE-1))
|| std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { || 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; return -EINVAL;
std = qset_new_std(whc, qset, urb, mem_flags); std = qset_new_std(whc, qset, urb, mem_flags);
if (std == NULL) { if (std == NULL) {
...@@ -487,14 +487,14 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u ...@@ -487,14 +487,14 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
dma_len = dma_remaining; dma_len = dma_remaining;
/* /*
* If the remainder in this element doesn't * If the remainder of this element doesn't
* fit in a single qTD, end the qTD on a * fit in a single qTD, limit the qTD to a
* wMaxPacketSize boundary. * whole number of packets. This allows the
* remainder to go into the next qTD.
*/ */
if (std->len + dma_len > QTD_MAX_XFER_SIZE) { if (std->len + dma_len > QTD_MAX_XFER_SIZE) {
dma_len = QTD_MAX_XFER_SIZE - std->len; dma_len = (QTD_MAX_XFER_SIZE / qset->max_packet)
ep = ((dma_addr + dma_len) / qset->max_packet) * qset->max_packet; * qset->max_packet - std->len;
dma_len = ep - dma_addr;
} }
std->len += dma_len; std->len += dma_len;
......
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