# 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                  *
  *-------------------------------------------------------------------*/