# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.545 -> 1.546 # drivers/usb/core/hcd.h 1.14 -> 1.15 # include/linux/usb.h 1.47 -> 1.48 # drivers/usb/core/buffer.c 1.1 -> 1.2 # drivers/usb/core/usb.c 1.83 -> 1.84 # drivers/usb/core/hcd.c 1.31 -> 1.32 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/08/28 david-b@pacbell.net 1.546 # [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. # -------------------------------------------- # diff -Nru a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c --- a/drivers/usb/core/buffer.c Wed Aug 28 23:20:31 2002 +++ b/drivers/usb/core/buffer.c Wed Aug 28 23:20:31 2002 @@ -182,5 +182,47 @@ : 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); +} diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c Wed Aug 28 23:20:31 2002 +++ b/drivers/usb/core/hcd.c Wed Aug 28 23:20:31 2002 @@ -1270,6 +1270,9 @@ .buffer_map = hcd_buffer_map, .buffer_dmasync = hcd_buffer_dmasync, .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); diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h --- a/drivers/usb/core/hcd.h Wed Aug 28 23:20:31 2002 +++ b/drivers/usb/core/hcd.h Wed Aug 28 23:20:31 2002 @@ -155,7 +155,15 @@ dma_addr_t dma, 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 */ @@ -246,6 +254,13 @@ void hcd_buffer_unmap (struct usb_bus *bus, dma_addr_t dma, 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 */ extern struct usb_operations usb_hcd_operations; diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c --- a/drivers/usb/core/usb.c Wed Aug 28 23:20:31 2002 +++ b/drivers/usb/core/usb.c Wed Aug 28 23:20:31 2002 @@ -1532,6 +1532,116 @@ : 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 struct list_head *usb_driver_get_list(void) { @@ -1611,5 +1721,9 @@ EXPORT_SYMBOL (usb_buffer_map); EXPORT_SYMBOL (usb_buffer_dmasync); 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"); diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Wed Aug 28 23:20:31 2002 +++ b/include/linux/usb.h Wed Aug 28 23:20:31 2002 @@ -1036,6 +1036,14 @@ void usb_buffer_dmasync (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 * *-------------------------------------------------------------------*/