diff options
Diffstat (limited to 'drivers/usb/core')
| -rw-r--r-- | drivers/usb/core/buffer.c | 41 | ||||
| -rw-r--r-- | drivers/usb/core/devio.c | 20 | 
2 files changed, 55 insertions, 6 deletions
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index fbb087b728dc..268ccbec88f9 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -172,3 +172,44 @@ void hcd_buffer_free(  	}  	dma_free_coherent(hcd->self.sysdev, size, addr, dma);  } + +void *hcd_buffer_alloc_pages(struct usb_hcd *hcd, +		size_t size, gfp_t mem_flags, dma_addr_t *dma) +{ +	if (size == 0) +		return NULL; + +	if (hcd->localmem_pool) +		return gen_pool_dma_alloc_align(hcd->localmem_pool, +				size, dma, PAGE_SIZE); + +	/* some USB hosts just use PIO */ +	if (!hcd_uses_dma(hcd)) { +		*dma = DMA_MAPPING_ERROR; +		return (void *)__get_free_pages(mem_flags, +				get_order(size)); +	} + +	return dma_alloc_coherent(hcd->self.sysdev, +			size, dma, mem_flags); +} + +void hcd_buffer_free_pages(struct usb_hcd *hcd, +		size_t size, void *addr, dma_addr_t dma) +{ +	if (!addr) +		return; + +	if (hcd->localmem_pool) { +		gen_pool_free(hcd->localmem_pool, +				(unsigned long)addr, size); +		return; +	} + +	if (!hcd_uses_dma(hcd)) { +		free_pages((unsigned long)addr, get_order(size)); +		return; +	} + +	dma_free_coherent(hcd->self.sysdev, size, addr, dma); +} diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e501a03d6c70..fcf68818e999 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -186,6 +186,7 @@ static int connected(struct usb_dev_state *ps)  static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)  {  	struct usb_dev_state *ps = usbm->ps; +	struct usb_hcd *hcd = bus_to_hcd(ps->dev->bus);  	unsigned long flags;  	spin_lock_irqsave(&ps->lock, flags); @@ -194,8 +195,8 @@ static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)  		list_del(&usbm->memlist);  		spin_unlock_irqrestore(&ps->lock, flags); -		usb_free_coherent(ps->dev, usbm->size, usbm->mem, -				usbm->dma_handle); +		hcd_buffer_free_pages(hcd, usbm->size, +				usbm->mem, usbm->dma_handle);  		usbfs_decrease_memory_usage(  			usbm->size + sizeof(struct usb_memory));  		kfree(usbm); @@ -234,7 +235,7 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)  	size_t size = vma->vm_end - vma->vm_start;  	void *mem;  	unsigned long flags; -	dma_addr_t dma_handle; +	dma_addr_t dma_handle = DMA_MAPPING_ERROR;  	int ret;  	ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory)); @@ -247,8 +248,8 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)  		goto error_decrease_mem;  	} -	mem = usb_alloc_coherent(ps->dev, size, GFP_USER | __GFP_NOWARN, -			&dma_handle); +	mem = hcd_buffer_alloc_pages(hcd, +			size, GFP_USER | __GFP_NOWARN, &dma_handle);  	if (!mem) {  		ret = -ENOMEM;  		goto error_free_usbm; @@ -264,7 +265,14 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)  	usbm->vma_use_count = 1;  	INIT_LIST_HEAD(&usbm->memlist); -	if (hcd->localmem_pool || !hcd_uses_dma(hcd)) { +	/* +	 * In DMA-unavailable cases, hcd_buffer_alloc_pages allocates +	 * normal pages and assigns DMA_MAPPING_ERROR to dma_handle. Check +	 * whether we are in such cases, and then use remap_pfn_range (or +	 * dma_mmap_coherent) to map normal (or DMA) pages into the user +	 * space, respectively. +	 */ +	if (dma_handle == DMA_MAPPING_ERROR) {  		if (remap_pfn_range(vma, vma->vm_start,  				    virt_to_phys(usbm->mem) >> PAGE_SHIFT,  				    size, vma->vm_page_prot) < 0) {  |