[PATCH][1/4] usb: dma bounce buffer support using HCD_BOUNCE flag
Magnus Damm <[email protected]> Mon, 14 Jan 2008 11:08:39 +0900
| Newsgroups | gmane.linux.usb.devel |
|---|---|
| Message-ID | <[email protected]> |
usb: dma bounce buffer support using HCD_BOUNCE flag This patch adds dma bounce buffer support to the usb core. These buffers can be enabled with the HCD_BOUNCE flag, and they make sure that all data passed to the host controller is allocated using dma_alloc_coherent(). Signed-off-by: Magnus Damm <[email protected]> --- drivers/usb/core/buffer.c | 7 +- drivers/usb/core/hcd.c | 115 +++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/core/hcd.h | 1 3 files changed, 116 insertions(+), 7 deletions(-) --- 0001/drivers/usb/core/buffer.c +++ work/drivers/usb/core/buffer.c 2008-01-13 18:52:08.000000000 +0900 @@ -53,7 +53,8 @@ int hcd_buffer_create(struct usb_hcd *hc char name[16]; int i, size; - if (!hcd->self.controller->dma_mask) + if (!hcd->self.controller->dma_mask && + !(hcd->driver->flags & HCD_BOUNCE)) return 0; for (i = 0; i < HCD_BUFFER_POOLS; i++) { @@ -107,7 +108,7 @@ void *hcd_buffer_alloc( int i; /* some USB hosts just use PIO */ - if (!bus->controller->dma_mask) { + if (!bus->controller->dma_mask && !(hcd->driver->flags & HCD_BOUNCE)) { *dma = ~(dma_addr_t) 0; return kmalloc(size, mem_flags); } @@ -132,7 +133,7 @@ void hcd_buffer_free( if (!addr) return; - if (!bus->controller->dma_mask) { + if (!bus->controller->dma_mask && !(hcd->driver->flags & HCD_BOUNCE)) { kfree(addr); return; } --- 0001/drivers/usb/core/hcd.c +++ work/drivers/usb/core/hcd.c 2008-01-13 18:52:08.000000000 +0900 @@ -35,6 +35,7 @@ #include <linux/mutex.h> #include <asm/irq.h> #include <asm/byteorder.h> +#include <asm/unaligned.h> #include <linux/platform_device.h> #include <linux/workqueue.h> @@ -1112,13 +1113,88 @@ void usb_hcd_unlink_urb_from_ep(struct u } EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep); -static void map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +static int hcd_alloc_coherent(struct usb_bus *bus, + gfp_t mem_flags, dma_addr_t *dma_handle, + void **vaddr_handle, size_t size, + enum dma_data_direction dir) { + unsigned char *vaddr; + + vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr), + mem_flags, dma_handle); + if (!vaddr) + return -ENOMEM; + + put_unaligned((unsigned long)*vaddr_handle, + (unsigned long *)(vaddr + size)); + + if (dir == DMA_TO_DEVICE) + memcpy(vaddr, *vaddr_handle, size); + + *vaddr_handle = vaddr; + return 0; +} + +static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, + void **vaddr_handle, size_t size, + enum dma_data_direction dir) +{ + unsigned char *vaddr = *vaddr_handle; + + vaddr = (void *)get_unaligned((unsigned long *)(vaddr + size)); + + if (dir == DMA_FROM_DEVICE) + memcpy(vaddr, *vaddr_handle, size); + + hcd_buffer_free(bus, size + sizeof(vaddr), *vaddr_handle, *dma_handle); + + *vaddr_handle = vaddr; + *dma_handle = 0; +} + +static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + + int status = 0; + /* Map the URB's buffers for DMA access. * Lower level HCD code should use *_dma exclusively, * unless it uses pio or talks to another transport. */ - if (hcd->self.uses_dma && !is_root_hub(urb->dev)) { + if (is_root_hub(urb->dev)) + return 0; + + if (hcd->driver->flags & HCD_BOUNCE) { + if (usb_endpoint_xfer_control(&urb->ep->desc) + && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + status = hcd_alloc_coherent(urb->dev->bus, mem_flags, + &urb->setup_dma, + (void **)&urb->setup_packet, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + + if (status == 0 && urb->transfer_buffer_length != 0 + && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { + status = hcd_alloc_coherent(urb->dev->bus, mem_flags, + &urb->transfer_dma, + &urb->transfer_buffer, + urb->transfer_buffer_length, + usb_urb_dir_in(urb) + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE); + if (status && usb_endpoint_xfer_control(&urb->ep->desc) + && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + hcd_free_coherent(urb->dev->bus, + &urb->setup_dma, + (void **)&urb->setup_packet, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + } + return status; + } + + if (hcd->self.uses_dma) { if (usb_endpoint_xfer_control(&urb->ep->desc) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) urb->setup_dma = dma_map_single ( @@ -1136,11 +1212,34 @@ static void map_urb_for_dma(struct usb_h ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } + return 0; } static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { - if (hcd->self.uses_dma && !is_root_hub(urb->dev)) { + if (is_root_hub(urb->dev)) + return; + + if (hcd->driver->flags & HCD_BOUNCE) { + if (usb_endpoint_xfer_control(&urb->ep->desc) + && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + hcd_free_coherent(urb->dev->bus, &urb->setup_dma, + (void **)&urb->setup_packet, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (urb->transfer_buffer_length != 0 + && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) + hcd_free_coherent(urb->dev->bus, + &urb->transfer_dma, + &urb->transfer_buffer, + urb->transfer_buffer_length, + usb_urb_dir_in(urb) + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE); + return; + } + + if (hcd->self.uses_dma) { if (usb_endpoint_xfer_control(&urb->ep->desc) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) dma_unmap_single(hcd->self.controller, urb->setup_dma, @@ -1185,7 +1284,12 @@ int usb_hcd_submit_urb (struct urb *urb, * URBs must be submitted in process context with interrupts * enabled. */ - map_urb_for_dma(hcd, urb); + status = map_urb_for_dma(hcd, urb, mem_flags); + if (unlikely(status)) { + usbmon_urb_submit_error(&hcd->self, urb, status); + goto error; + } + if (is_root_hub(urb->dev)) status = rh_urb_enqueue(hcd, urb); else @@ -1194,6 +1298,9 @@ int usb_hcd_submit_urb (struct urb *urb, if (unlikely(status)) { usbmon_urb_submit_error(&hcd->self, urb, status); unmap_urb_for_dma(hcd, urb); + } + error: + if (unlikely(status)) { urb->hcpriv = NULL; INIT_LIST_HEAD(&urb->urb_list); atomic_dec(&urb->use_count); --- 0001/drivers/usb/core/hcd.h +++ work/drivers/usb/core/hcd.h 2008-01-13 19:54:10.000000000 +0900 @@ -165,6 +165,7 @@ struct hc_driver { int flags; #define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */ +#define HCD_BOUNCE 0x0002 /* HC needs bounce buffers */ #define HCD_USB11 0x0010 /* USB 1.1 */ #define HCD_USB2 0x0020 /* USB 2.0 */ ------------------------------------------------------------------------- Check out the new SourceForge.net Marketplace. It's the best place to buy or sell services for just about anything Open Source. http://ad.doubleclick.net/clk;164216239;13503038;w?http://sf.net/marketplace _______________________________________________ [email protected] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel