Some platforms don't have DMA, but we should still be able to build USB drivers for these platforms. They could still be used through vhci_hcd, usbip_host, or maybe something like USB passthrough in UML from a capable host. This is admittedly ugly with the #ifdefs, but those are necessary to get around linker errors like these: drivers/built-in.o: In function `dma_unmap_sg_attrs': include/linux/dma-mapping.h:183: undefined reference to `bad_dma_ops' drivers/built-in.o: In function `dma_unmap_single_attrs': include/linux/dma-mapping.h:148: undefined reference to `bad_dma_ops' drivers/built-in.o: In function `dma_map_sg_attrs': include/linux/dma-mapping.h:168: undefined reference to `bad_dma_ops' drivers/built-in.o: In function `dma_map_page': include/linux/dma-mapping.h:196: undefined reference to `bad_dma_ops' drivers/built-in.o: In function `dma_mapping_error': include/linux/dma-mapping.h:430: undefined reference to `bad_dma_ops' drivers/built-in.o:include/linux/dma-mapping.h:131: more undefined references to `bad_dma_ops' follow Greg KG suggested just defining these for UML [1] but according to Richard Weinberger this has come up before and it was decided not to do that just for the sake of making drivers build [2]. [1]: http://www.spinics.net/lists/linux-usb/msg136306.html [2]: http://www.spinics.net/lists/linux-usb/msg136308.html If any of the new warnings trigger, the correct solution is almost certainly to add a CONFIG_HAS_DMA dependency in the Kconfig menu for the responsible driver -- that driver's functions will hopefully appear somewhere in the stack trace. v2: Reduce the number of #ifdefs by moving code out into helpers. Signed-off-by: Vegard Nossum <vegard.nossum@xxxxxxxxxx> --- drivers/usb/core/buffer.c | 94 ++++++++++++----- drivers/usb/core/hcd.c | 257 ++++++++++++++++++++++++++++++++++------------ include/linux/usb/hcd.h | 6 ++ 3 files changed, 266 insertions(+), 91 deletions(-) diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 89f2e77..17f416d 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -44,6 +44,28 @@ void __init usb_init_pool_max(void) /* SETUP primitives */ +static int _hcd_buffer_create(struct usb_hcd *hcd) +{ +#ifdef CONFIG_HAS_DMA + char name[16]; + int i, size; + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + size = pool_max[i]; + if (!size) + continue; + snprintf(name, sizeof(name), "buffer-%d", size); + hcd->pool[i] = dma_pool_create(name, hcd->self.controller, + size, size, 0); + if (!hcd->pool[i]) { + hcd_buffer_destroy(hcd); + return -ENOMEM; + } + } +#endif + return 0; +} + /** * hcd_buffer_create - initialize buffer pools * @hcd: the bus whose buffer pools are to be initialized @@ -59,26 +81,11 @@ void __init usb_init_pool_max(void) */ int hcd_buffer_create(struct usb_hcd *hcd) { - char name[16]; - int i, size; - if (!hcd->self.controller->dma_mask && !(hcd->driver->flags & HCD_LOCAL_MEM)) return 0; - for (i = 0; i < HCD_BUFFER_POOLS; i++) { - size = pool_max[i]; - if (!size) - continue; - snprintf(name, sizeof(name), "buffer-%d", size); - hcd->pool[i] = dma_pool_create(name, hcd->self.controller, - size, size, 0); - if (!hcd->pool[i]) { - hcd_buffer_destroy(hcd); - return -ENOMEM; - } - } - return 0; + return _hcd_buffer_create(hcd); } @@ -91,6 +98,7 @@ int hcd_buffer_create(struct usb_hcd *hcd) */ void hcd_buffer_destroy(struct usb_hcd *hcd) { +#ifdef CONFIG_HAS_DMA int i; for (i = 0; i < HCD_BUFFER_POOLS; i++) { @@ -101,6 +109,7 @@ void hcd_buffer_destroy(struct usb_hcd *hcd) hcd->pool[i] = NULL; } } +#endif } @@ -108,6 +117,27 @@ void hcd_buffer_destroy(struct usb_hcd *hcd) * better sharing and to leverage mm/slab.c intelligence. */ +static void *_hcd_buffer_alloc( + struct usb_hcd *hcd, + size_t size, + gfp_t mem_flags, + dma_addr_t *dma +) +{ +#ifdef CONFIG_HAS_DMA + int i; + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + if (size <= pool_max[i]) + return dma_pool_alloc(hcd->pool[i], mem_flags, dma); + } + return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags); +#else + WARN_ON_NO_DMA(); + return NULL; +#endif +} + void *hcd_buffer_alloc( struct usb_bus *bus, size_t size, @@ -116,7 +146,6 @@ void *hcd_buffer_alloc( ) { struct usb_hcd *hcd = bus_to_hcd(bus); - int i; /* some USB hosts just use PIO */ if (!bus->controller->dma_mask && @@ -125,11 +154,27 @@ void *hcd_buffer_alloc( return kmalloc(size, mem_flags); } + return _hcd_buffer_alloc(hcd, size, mem_flags, dma); +} + +static void _hcd_buffer_free( + struct usb_hcd *hcd, + size_t size, + void *addr, + dma_addr_t dma +) +{ +#ifdef CONFIG_HAS_DMA + int i; + for (i = 0; i < HCD_BUFFER_POOLS; i++) { - if (size <= pool_max[i]) - return dma_pool_alloc(hcd->pool[i], mem_flags, dma); + if (size <= pool_max[i]) { + dma_pool_free(hcd->pool[i], addr, dma); + return; + } } - return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags); + dma_free_coherent(hcd->self.controller, size, addr, dma); +#endif } void hcd_buffer_free( @@ -140,7 +185,6 @@ void hcd_buffer_free( ) { struct usb_hcd *hcd = bus_to_hcd(bus); - int i; if (!addr) return; @@ -151,11 +195,5 @@ void hcd_buffer_free( return; } - for (i = 0; i < HCD_BUFFER_POOLS; i++) { - if (size <= pool_max[i]) { - dma_pool_free(hcd->pool[i], addr, dma); - return; - } - } - dma_free_coherent(hcd->self.controller, size, addr, dma); + _hcd_buffer_free(hcd, size, addr, dma); } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index df0e3b9..157592c 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1406,13 +1406,76 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } +#ifdef CONFIG_HAS_DMA +static void _usb_hcd_unmap_urb_setup_for_dma_single(struct usb_hcd *hcd, + struct urb *urb) +{ + dma_unmap_single(hcd->self.controller, + urb->setup_dma, + sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); +} + +static void _usb_hcd_unmap_urb_for_dma_sg(struct usb_hcd *hcd, + struct urb *urb, + enum dma_data_direction dir) +{ + dma_unmap_sg(hcd->self.controller, + urb->sg, + urb->num_sgs, + dir); +} + +static void _usb_hcd_unmap_urb_for_dma_page(struct usb_hcd *hcd, + struct urb *urb, + enum dma_data_direction dir) +{ + dma_unmap_page(hcd->self.controller, + urb->transfer_dma, + urb->transfer_buffer_length, + dir); +} + +static void _usb_hcd_unmap_urb_for_dma_single(struct usb_hcd *hcd, + struct urb *urb, + enum dma_data_direction dir) +{ + dma_unmap_single(hcd->self.controller, + urb->transfer_dma, + urb->transfer_buffer_length, + dir); +} + +#else /* !CONFIG_HAS_DMA */ + +static void _usb_hcd_unmap_urb_setup_for_dma_single(struct usb_hcd *hcd, + struct urb *urb) +{ +} + +static void _usb_hcd_unmap_urb_for_dma_sg(struct usb_hcd *hcd, + struct urb *urb, + enum dma_data_direction dir) +{ +} + +static void _usb_hcd_unmap_urb_for_dma_page(struct usb_hcd *hcd, + struct urb *urb, + enum dma_data_direction dir) +{ +} + +static void _usb_hcd_unmap_urb_for_dma_single(struct usb_hcd *hcd, + struct urb *urb, + enum dma_data_direction dir) +{ +} +#endif + void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) { if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) - dma_unmap_single(hcd->self.controller, - urb->setup_dma, - sizeof(struct usb_ctrlrequest), - DMA_TO_DEVICE); + _usb_hcd_unmap_urb_setup_for_dma_single(hcd, urb); else if (urb->transfer_flags & URB_SETUP_MAP_LOCAL) hcd_free_coherent(urb->dev->bus, &urb->setup_dma, @@ -1441,20 +1504,11 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_flags & URB_DMA_MAP_SG) - dma_unmap_sg(hcd->self.controller, - urb->sg, - urb->num_sgs, - dir); + _usb_hcd_unmap_urb_for_dma_sg(hcd, urb, dir); else if (urb->transfer_flags & URB_DMA_MAP_PAGE) - dma_unmap_page(hcd->self.controller, - urb->transfer_dma, - urb->transfer_buffer_length, - dir); + _usb_hcd_unmap_urb_for_dma_page(hcd, urb, dir); else if (urb->transfer_flags & URB_DMA_MAP_SINGLE) - dma_unmap_single(hcd->self.controller, - urb->transfer_dma, - urb->transfer_buffer_length, - dir); + _usb_hcd_unmap_urb_for_dma_single(hcd, urb, dir); else if (urb->transfer_flags & URB_MAP_LOCAL) hcd_free_coherent(urb->dev->bus, &urb->transfer_dma, @@ -1477,6 +1531,124 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); } +#ifdef CONFIG_HAS_DMA +static int _usb_hcd_map_urb_setup_for_dma_single(struct usb_hcd *hcd, + struct urb *urb) +{ + int ret = 0; + + urb->setup_dma = dma_map_single( + hcd->self.controller, + urb->setup_packet, + sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (dma_mapping_error(hcd->self.controller, + urb->setup_dma)) + ret = -EAGAIN; + else + urb->transfer_flags |= URB_SETUP_MAP_SINGLE; + + return ret; +} + +static int _usb_hcd_map_urb_for_dma_sg(struct usb_hcd *hcd, struct urb *urb, + enum dma_data_direction dir) +{ + int ret = 0; + int n; + + n = dma_map_sg( + hcd->self.controller, + urb->sg, + urb->num_sgs, + dir); + if (n <= 0) + ret = -EAGAIN; + else + urb->transfer_flags |= URB_DMA_MAP_SG; + + urb->num_mapped_sgs = n; + if (n != urb->num_sgs) + urb->transfer_flags |= + URB_DMA_SG_COMBINED; + + return ret; +} + +static int _usb_hcd_map_urb_for_dma_page(struct usb_hcd *hcd, struct urb *urb, + enum dma_data_direction dir) +{ + int ret = 0; + struct scatterlist *sg = urb->sg; + + urb->transfer_dma = dma_map_page( + hcd->self.controller, + sg_page(sg), + sg->offset, + urb->transfer_buffer_length, + dir); + if (dma_mapping_error(hcd->self.controller, + urb->transfer_dma)) + ret = -EAGAIN; + else + urb->transfer_flags |= URB_DMA_MAP_PAGE; + + return ret; +} + +static int _usb_hcd_map_urb_for_dma_single(struct usb_hcd *hcd, + struct urb *urb, + enum dma_data_direction dir) +{ + int ret = 0; + + 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; + + return ret; +} + + +#else /* !CONFIG_HAS_DMA */ + +static int _usb_hcd_map_urb_setup_for_dma_single(struct usb_hcd *hcd, + struct urb *urb) +{ + WARN_ON_NO_DMA(); + return -EINVAL; +} + +static int _usb_hcd_map_urb_for_dma_sg(struct usb_hcd *hcd, struct urb *urb, + enum dma_data_direction dir) +{ + WARN_ON_NO_DMA(); + return -EINVAL; +} + +static int _usb_hcd_map_urb_for_dma_page(struct usb_hcd *hcd, struct urb *urb, + enum dma_data_direction dir) +{ + WARN_ON_NO_DMA(); + return -EINVAL; +} + +static int _usb_hcd_map_urb_for_dma_single(struct usb_hcd *hcd, + struct urb *urb, + enum dma_data_direction dir) +{ + WARN_ON_NO_DMA(); + return -EINVAL; +} +#endif + int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { @@ -1493,15 +1665,9 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, if (hcd->self.uses_pio_for_control) return ret; if (hcd->self.uses_dma) { - urb->setup_dma = dma_map_single( - hcd->self.controller, - urb->setup_packet, - sizeof(struct usb_ctrlrequest), - DMA_TO_DEVICE); - if (dma_mapping_error(hcd->self.controller, - urb->setup_dma)) - return -EAGAIN; - urb->transfer_flags |= URB_SETUP_MAP_SINGLE; + ret = _usb_hcd_map_urb_setup_for_dma_single(hcd, urb); + if (ret) + return ret; } else if (hcd->driver->flags & HCD_LOCAL_MEM) { ret = hcd_alloc_coherent( urb->dev->bus, mem_flags, @@ -1520,54 +1686,19 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { if (hcd->self.uses_dma) { if (urb->num_sgs) { - int n; - /* We don't support sg for isoc transfers ! */ if (usb_endpoint_xfer_isoc(&urb->ep->desc)) { WARN_ON(1); return -EINVAL; } - - n = dma_map_sg( - hcd->self.controller, - urb->sg, - urb->num_sgs, - dir); - if (n <= 0) - ret = -EAGAIN; - else - urb->transfer_flags |= URB_DMA_MAP_SG; - urb->num_mapped_sgs = n; - if (n != urb->num_sgs) - urb->transfer_flags |= - URB_DMA_SG_COMBINED; + ret = _usb_hcd_map_urb_for_dma_sg(hcd, urb, dir); } else if (urb->sg) { - struct scatterlist *sg = urb->sg; - urb->transfer_dma = dma_map_page( - hcd->self.controller, - sg_page(sg), - sg->offset, - urb->transfer_buffer_length, - dir); - if (dma_mapping_error(hcd->self.controller, - urb->transfer_dma)) - ret = -EAGAIN; - else - urb->transfer_flags |= URB_DMA_MAP_PAGE; + ret = _usb_hcd_map_urb_for_dma_page(hcd, urb, dir); } else if (is_vmalloc_addr(urb->transfer_buffer)) { WARN_ONCE(1, "transfer buffer not dma capable\n"); ret = -EAGAIN; } 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; + ret = _usb_hcd_map_urb_for_dma_single(hcd, urb, dir); } } else if (hcd->driver->flags & HCD_LOCAL_MEM) { ret = hcd_alloc_coherent( diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 4dcf844..1e90e59 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -409,6 +409,12 @@ static inline bool hcd_periodic_completion_in_progress(struct usb_hcd *hcd, return hcd->high_prio_bh.completing_ep == ep; } +#ifndef CONFIG_HAS_DMA +/* If this ever triggers, the correct fix is almost certainly + * to add a CONFIG_HAS_DMA dependency in the Kconfig for that driver. */ +#define WARN_ON_NO_DMA() WARN_ONCE(1, "HCD driver tried to use DMA memory") +#endif + extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb, int status); -- 1.9.1 -- 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