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>
This commit is contained in:
parent
f3f6faa9ed
commit
0d370755dd
@ -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
|
|||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user