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)