[PATCH 1/2] USB: Allow HCDs to specifiy their DMA alignment requirements

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If these are not met use a bounce buffer.

This is done by adding a member dma_align_shift to
struct hc_driver. Defaulting to zero causes unmodified
HCDs to be assumed to be byte aligned DMA capable.

Signed-off-by: Martin Fuzzey <mfuzzey@xxxxxxxxx>

---

 drivers/usb/core/hcd.c  |   54 +++++++++++++++++++++++++++++++++++++----------
 include/linux/usb.h     |    1 +
 include/linux/usb/hcd.h |    2 ++
 3 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 12742f1..8ad9367 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1286,12 +1286,19 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
 				urb->transfer_dma,
 				urb->transfer_buffer_length,
 				dir);
-	else if (urb->transfer_flags & URB_DMA_MAP_SINGLE)
+	else if (urb->transfer_flags & URB_DMA_MAP_SINGLE) {
 		dma_unmap_single(hcd->self.controller,
 				urb->transfer_dma,
 				urb->transfer_buffer_length,
 				dir);
-	else if (urb->transfer_flags & URB_MAP_LOCAL)
+		if (urb->bounce_buffer) {
+			if (dir == DMA_FROM_DEVICE)
+				memcpy(urb->transfer_buffer,
+					urb->bounce_buffer,
+					urb->transfer_buffer_length);
+			kfree(urb->bounce_buffer);
+		}
+	} else if (urb->transfer_flags & URB_MAP_LOCAL)
 		hcd_free_coherent(urb->dev->bus,
 				&urb->transfer_dma,
 				&urb->transfer_buffer,
@@ -1373,16 +1380,39 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
 				else
 					urb->transfer_flags |= URB_DMA_MAP_PAGE;
 			} else {
-				urb->transfer_dma = dma_map_single(
-						hcd->self.controller,
-						urb->transfer_buffer,
-						urb->transfer_buffer_length,
-						dir);
-				if (dma_mapping_error(hcd->self.controller,
-						urb->transfer_dma))
-					ret = -EAGAIN;
-				else
-					urb->transfer_flags |= URB_DMA_MAP_SINGLE;
+				void *buffer = urb->transfer_buffer;
+
+				if (IS_ALIGNED((unsigned long)buffer,
+						1 << hcd->driver->dma_align_shift))
+					urb->bounce_buffer = NULL;
+				else {
+					if (dir == DMA_TO_DEVICE)
+						buffer = kmemdup(
+							buffer,
+							urb->transfer_buffer_length,
+							mem_flags);
+					else
+						buffer = kmalloc(
+							urb->transfer_buffer_length,
+							mem_flags);
+
+					if (!buffer)
+						ret = -ENOMEM;
+					urb->bounce_buffer = buffer;
+				}
+
+				if (buffer) {
+					urb->transfer_dma = dma_map_single(
+							hcd->self.controller,
+							buffer,
+							urb->transfer_buffer_length,
+							dir);
+					if (dma_mapping_error(hcd->self.controller,
+							urb->transfer_dma))
+						ret = -EAGAIN;
+					else
+						urb->transfer_flags |= URB_DMA_MAP_SINGLE;
+				}
 			}
 		} else if (hcd->driver->flags & HCD_LOCAL_MEM) {
 			ret = hcd_alloc_coherent(
diff --git a/include/linux/usb.h b/include/linux/usb.h
index d5922a8..4c53145 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1180,6 +1180,7 @@ struct urb {
 	atomic_t use_count;		/* concurrent submissions counter */
 	atomic_t reject;		/* submissions will fail */
 	int unlinked;			/* unlink error code */
+	void *bounce_buffer;		/* DMA bounce buffer for alignment */
 
 	/* public: documented fields in the urb that can be used by drivers */
 	struct list_head urb_list;	/* list head for use by the urb's
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 2e3a4ea..a27df53 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -178,6 +178,8 @@ struct hc_driver {
 	const char	*description;	/* "ehci-hcd" etc */
 	const char	*product_desc;	/* product/vendor string */
 	size_t		hcd_priv_size;	/* size of private data */
+	unsigned	dma_align_shift; /* Aligment requirement for DMA:
+						0=byte 1=2 byte, 2=4 byte... */
 
 	/* irq handler */
 	irqreturn_t	(*irq) (struct usb_hcd *hcd);

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux