ChangeSet 1.1290.15.13, 2004/03/01 11:13:18-08:00, Richard.Curnow@superh.com

[PATCH] USB: Fix handling of bounce buffers by rh_call_control

Below is a fix for a bounce-buffer handling problem in 2.4.  The problem
is where no actual DMA by the card occurs, and the result of the
operation is inserted straight into the original buffer by software.
When bounce-buffers are being used, the memcpy inside pci_unmap_single
copies the uninitialized junk from the DMA buffer over the original
buffer, destroying the result that had been set up there.

(The problem showed up on an sh prototyping/development board where the
PCI controller (V320USC) doesn't have any access to the CPU main memory,
only to a small separate shared block of RAM that's there specifically
to provide DMA bounce buffers.)

The patch has been tested by me on sh64 and on that sh platform, and by
David on x86.


BTW, there's been a fix in 2.6 for this for quite some time, IIRC
courtesy of Russell King (I think as a result of finding the same
problem on an ARM platform with bounce-buffers).


 drivers/usb/hcd.c |   48 +++++++++++++++++++++++++++++-------------------
 1 files changed, 29 insertions(+), 19 deletions(-)


diff -Nru a/drivers/usb/hcd.c b/drivers/usb/hcd.c
--- a/drivers/usb/hcd.c	Wed Mar 17 15:48:39 2004
+++ b/drivers/usb/hcd.c	Wed Mar 17 15:48:39 2004
@@ -1201,25 +1201,27 @@
 		return status;
 
 	// NOTE:  2.5 does this if !URB_NO_DMA_MAP transfer flag
-	if (usb_pipecontrol (urb->pipe))
-		urb->setup_dma = pci_map_single (
-				hcd->pdev,
-				urb->setup_packet,
-				sizeof (struct usb_ctrlrequest),
-				PCI_DMA_TODEVICE);
-	if (urb->transfer_buffer_length != 0)
-		urb->transfer_dma = pci_map_single (
-				hcd->pdev,
-				urb->transfer_buffer,
-				urb->transfer_buffer_length,
-				usb_pipein (urb->pipe)
-				    ? PCI_DMA_FROMDEVICE
-				    : PCI_DMA_TODEVICE);
-
-	if (urb->dev == hcd->bus->root_hub)
+	
+	/* For 2.4, don't map bounce buffer if it's a root hub operation. */
+	if (urb->dev == hcd->bus->root_hub) {
 		status = rh_urb_enqueue (hcd, urb);
-	else
+	} else {
+		if (usb_pipecontrol (urb->pipe))
+			urb->setup_dma = pci_map_single (
+					hcd->pdev,
+					urb->setup_packet,
+					sizeof (struct usb_ctrlrequest),
+					PCI_DMA_TODEVICE);
+		if (urb->transfer_buffer_length != 0)
+			urb->transfer_dma = pci_map_single (
+					hcd->pdev,
+					urb->transfer_buffer,
+					urb->transfer_buffer_length,
+					usb_pipein (urb->pipe)
+					    ? PCI_DMA_FROMDEVICE
+					    : PCI_DMA_TODEVICE);
 		status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
+	}
 	return status;
 }
 
@@ -1471,6 +1473,11 @@
  */
 void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
 {
+	int is_root_hub_operation;
+
+	/* Work this out here as urb_unlink clears urb->dev */
+	is_root_hub_operation = (urb->dev == hcd->bus->root_hub);
+
 	urb_unlink (urb);
 
 	// NOTE:  a generic device/urb monitoring hook would go here.
@@ -1480,11 +1487,14 @@
 	// hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev)
 
 	// NOTE:  2.5 does this if !URB_NO_DMA_MAP transfer flag
-	if (usb_pipecontrol (urb->pipe))
+	
+	/* For 2.4, don't unmap bounce buffer if it's a root hub operation. */
+	if (usb_pipecontrol (urb->pipe) && !is_root_hub_operation)
 		pci_unmap_single (hcd->pdev, urb->setup_dma,
 				sizeof (struct usb_ctrlrequest),
 				PCI_DMA_TODEVICE);
-	if (urb->transfer_buffer_length != 0)
+
+	if ((urb->transfer_buffer_length != 0) && !is_root_hub_operation)
 		pci_unmap_single (hcd->pdev, urb->transfer_dma,
 				urb->transfer_buffer_length,
 				usb_pipein (urb->pipe)
