Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+11 −11
Original line number Original line Diff line number Diff line
@@ -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;