Commit f4f441db authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB dma and scatterlists

This patch (almost all from DaveM) wraps up the DMA API work
by adding the scatterlist map/sync/unmap support.  And removes
the corresponding FIXME.
parent aef9f2f7
...@@ -182,5 +182,47 @@ void hcd_buffer_unmap ( ...@@ -182,5 +182,47 @@ void hcd_buffer_unmap (
: PCI_DMA_TODEVICE); : PCI_DMA_TODEVICE);
} }
int hcd_buffer_map_sg (
struct usb_bus *bus,
struct scatterlist *sg,
int *n_hw_ents,
int nents,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
// FIXME DMA-Mappings for struct scatterlist // FIXME pci_map_sg() has no standard failure mode!
*n_hw_ents = pci_map_sg(hcd->pdev, sg, nents,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
return 0;
}
void hcd_buffer_sync_sg (
struct usb_bus *bus,
struct scatterlist *sg,
int n_hw_ents,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
pci_dma_sync_sg(hcd->pdev, sg, n_hw_ents,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
void hcd_buffer_unmap_sg (
struct usb_bus *bus,
struct scatterlist *sg,
int n_hw_ents,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
pci_unmap_sg(hcd->pdev, sg, n_hw_ents,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
...@@ -1270,6 +1270,9 @@ struct usb_operations usb_hcd_operations = { ...@@ -1270,6 +1270,9 @@ struct usb_operations usb_hcd_operations = {
.buffer_map = hcd_buffer_map, .buffer_map = hcd_buffer_map,
.buffer_dmasync = hcd_buffer_dmasync, .buffer_dmasync = hcd_buffer_dmasync,
.buffer_unmap = hcd_buffer_unmap, .buffer_unmap = hcd_buffer_unmap,
.buffer_map_sg = hcd_buffer_map_sg,
.buffer_dmasync_sg = hcd_buffer_sync_sg,
.buffer_unmap_sg = hcd_buffer_unmap_sg,
}; };
EXPORT_SYMBOL (usb_hcd_operations); EXPORT_SYMBOL (usb_hcd_operations);
......
...@@ -155,7 +155,15 @@ struct usb_operations { ...@@ -155,7 +155,15 @@ struct usb_operations {
dma_addr_t dma, dma_addr_t dma,
size_t size, int direction); size_t size, int direction);
// FIXME also: buffer_sg_map (), buffer_sg_unmap () int (*buffer_map_sg) (struct usb_bus *bus,
struct scatterlist *sg, int *n_hw_ents,
int nents, int direction);
void (*buffer_dmasync_sg) (struct usb_bus *bus,
struct scatterlist *sg,
int n_hw_ents, int direction);
void (*buffer_unmap_sg) (struct usb_bus *bus,
struct scatterlist *sg,
int n_hw_ents, int direction);
}; };
/* each driver provides one of these, and hardware init support */ /* each driver provides one of these, and hardware init support */
...@@ -246,6 +254,13 @@ void hcd_buffer_dmasync (struct usb_bus *bus, ...@@ -246,6 +254,13 @@ void hcd_buffer_dmasync (struct usb_bus *bus,
void hcd_buffer_unmap (struct usb_bus *bus, void hcd_buffer_unmap (struct usb_bus *bus,
dma_addr_t dma, dma_addr_t dma,
size_t size, int direction); size_t size, int direction);
int hcd_buffer_map_sg (struct usb_bus *bus, struct scatterlist *sg,
int *n_hw_ents, int nents, int direction);
void hcd_buffer_sync_sg (struct usb_bus *bus, struct scatterlist *sg,
int n_hw_ents, int direction);
void hcd_buffer_unmap_sg (struct usb_bus *bus, struct scatterlist *sg,
int n_hw_ents, int direction);
/* generic bus glue, needed for host controllers that don't use PCI */ /* generic bus glue, needed for host controllers that don't use PCI */
extern struct usb_operations usb_hcd_operations; extern struct usb_operations usb_hcd_operations;
......
...@@ -1532,6 +1532,116 @@ void usb_buffer_unmap (struct urb *urb) ...@@ -1532,6 +1532,116 @@ void usb_buffer_unmap (struct urb *urb)
: USB_DIR_OUT); : USB_DIR_OUT);
} }
/**
* usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
* @dev: device to which the scatterlist will be mapped
* @pipe: endpoint defining the mapping direction
* @sg: the scatterlist to map
* @nents: the number of entries in the scatterlist
*
* Return value is either < 0 (indicating no buffers could be mapped), or
* the number of DMA mapping array entries in the scatterlist.
*
* The caller is responsible for placing the resulting DMA addresses from
* the scatterlist into URB transfer buffer pointers, and for setting the
* URB_NO_DMA_MAP transfer flag in each of those URBs.
*
* Top I/O rates come from queuing URBs, instead of waiting for each one
* to complete before starting the next I/O. This is particularly easy
* to do with scatterlists. Just allocate and submit one URB for each DMA
* mapping entry returned, stopping on the first error or when all succeed.
*
* This call would normally be used when translating scatterlist requests,
* rather than usb_buffer_map(), since on some hardware (with IOMMUs) it
* may be able to coalesce mappings for improved I/O efficiency.
*
* Reverse the effect of this call with usb_buffer_unmap_sg().
*/
int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int nents)
{
struct usb_bus *bus;
struct usb_operations *op;
int n_hw_ents;
if (!dev
|| usb_pipecontrol (pipe)
|| !(bus = dev->bus)
|| !(op = bus->op)
|| !op->buffer_map_sg)
return -1;
if (op->buffer_map_sg (bus,
sg,
&n_hw_ents,
nents,
usb_pipein (pipe)
? USB_DIR_IN
: USB_DIR_OUT))
return -1;
return n_hw_ents;
}
/**
* usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
* @dev: device to which the scatterlist will be mapped
* @pipe: endpoint defining the mapping direction
* @sg: the scatterlist to synchronize
* @n_hw_ents: the positive return value from usb_buffer_map_sg
*
* Use this when you are re-using a scatterlist's data buffers for
* another USB request.
*/
void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct usb_operations *op;
if (!dev
|| !(bus = dev->bus)
|| !(op = bus->op)
|| !op->buffer_dmasync_sg)
return;
op->buffer_dmasync_sg (bus,
sg,
n_hw_ents,
usb_pipein (pipe)
? USB_DIR_IN
: USB_DIR_OUT);
}
/**
* usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
* @dev: device to which the scatterlist will be mapped
* @pipe: endpoint defining the mapping direction
* @sg: the scatterlist to unmap
* @n_hw_ents: the positive return value from usb_buffer_map_sg
*
* Reverses the effect of usb_buffer_map_sg().
*/
void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct usb_operations *op;
if (!dev
|| !(bus = dev->bus)
|| !(op = bus->op)
|| !op->buffer_unmap_sg)
return;
op->buffer_unmap_sg (bus,
sg,
n_hw_ents,
usb_pipein (pipe)
? USB_DIR_IN
: USB_DIR_OUT);
}
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
struct list_head *usb_driver_get_list(void) struct list_head *usb_driver_get_list(void)
{ {
...@@ -1612,4 +1722,8 @@ EXPORT_SYMBOL (usb_buffer_map); ...@@ -1612,4 +1722,8 @@ EXPORT_SYMBOL (usb_buffer_map);
EXPORT_SYMBOL (usb_buffer_dmasync); EXPORT_SYMBOL (usb_buffer_dmasync);
EXPORT_SYMBOL (usb_buffer_unmap); EXPORT_SYMBOL (usb_buffer_unmap);
EXPORT_SYMBOL (usb_buffer_map_sg);
EXPORT_SYMBOL (usb_buffer_dmasync_sg);
EXPORT_SYMBOL (usb_buffer_unmap_sg);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -1036,6 +1036,14 @@ struct urb *usb_buffer_map (struct urb *urb); ...@@ -1036,6 +1036,14 @@ struct urb *usb_buffer_map (struct urb *urb);
void usb_buffer_dmasync (struct urb *urb); void usb_buffer_dmasync (struct urb *urb);
void usb_buffer_unmap (struct urb *urb); void usb_buffer_unmap (struct urb *urb);
struct scatterlist;
int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int nents);
void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents);
void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents);
/*-------------------------------------------------------------------* /*-------------------------------------------------------------------*
* SYNCHRONOUS CALL SUPPORT * * SYNCHRONOUS CALL SUPPORT *
*-------------------------------------------------------------------*/ *-------------------------------------------------------------------*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment