diff options
Diffstat (limited to 'drivers/usb/core/usb.c')
| -rw-r--r-- | drivers/usb/core/usb.c | 80 | 
1 files changed, 80 insertions, 0 deletions
| diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 118fa4c93a79..fca7735fc660 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1030,6 +1030,86 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,  }  EXPORT_SYMBOL_GPL(usb_free_coherent); +/** + * usb_alloc_noncoherent - allocate dma-noncoherent buffer for URB_NO_xxx_DMA_MAP + * @dev: device the buffer will be used with + * @size: requested buffer size + * @mem_flags: affect whether allocation may block + * @dma: used to return DMA address of buffer + * @dir: DMA transfer direction + * @table: used to return sg_table of allocated memory + * + * To explicit manage the memory ownership for the kernel vs the device by + * USB core, the user needs save sg_table to urb->sgt. Then USB core will + * do DMA sync for CPU and device properly. + * + * When the buffer is no longer used, free it with usb_free_noncoherent(). + * + * Return: Either null (indicating no buffer could be allocated), or the + * cpu-space pointer to a buffer that may be used to perform DMA to the + * specified device.  Such cpu-space buffers are returned along with the DMA + * address (through the pointer provided). + */ +void *usb_alloc_noncoherent(struct usb_device *dev, size_t size, +			    gfp_t mem_flags, dma_addr_t *dma, +			    enum dma_data_direction dir, +			    struct sg_table **table) +{ +	struct device *dmadev; +	struct sg_table *sgt; +	void *buffer; + +	if (!dev || !dev->bus) +		return NULL; + +	dmadev = bus_to_hcd(dev->bus)->self.sysdev; + +	sgt = dma_alloc_noncontiguous(dmadev, size, dir, mem_flags, 0); +	if (!sgt) +		return NULL; + +	buffer = dma_vmap_noncontiguous(dmadev, size, sgt); +	if (!buffer) { +		dma_free_noncontiguous(dmadev, size, sgt, dir); +		return NULL; +	} + +	*table = sgt; +	*dma = sg_dma_address(sgt->sgl); + +	return buffer; +} +EXPORT_SYMBOL_GPL(usb_alloc_noncoherent); + +/** + * usb_free_noncoherent - free memory allocated with usb_alloc_noncoherent() + * @dev: device the buffer was used with + * @size: requested buffer size + * @addr: CPU address of buffer + * @dir: DMA transfer direction + * @table: describe the allocated and DMA mapped memory, + * + * This reclaims an I/O buffer, letting it be reused.  The memory must have + * been allocated using usb_alloc_noncoherent(), and the parameters must match + * those provided in that allocation request. + */ +void usb_free_noncoherent(struct usb_device *dev, size_t size, +			  void *addr, enum dma_data_direction dir, +			  struct sg_table *table) +{ +	struct device *dmadev; + +	if (!dev || !dev->bus) +		return; +	if (!addr) +		return; + +	dmadev = bus_to_hcd(dev->bus)->self.sysdev; +	dma_vunmap_noncontiguous(dmadev, addr); +	dma_free_noncontiguous(dmadev, size, table, dir); +} +EXPORT_SYMBOL_GPL(usb_free_noncoherent); +  /*   * Notifications of device and interface registration   */ | 
