--- linux/drivers/usb-pre7/usb.c	Tue Nov 20 22:56:35 2001
+++ linux/drivers/usb/usb.c	Sun Nov 25 22:15:48 2001
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
 #include <linux/spinlock.h>
+#include <asm/scatterlist.h>
 
 #ifdef CONFIG_USB_DEBUG
 	#define DEBUG
@@ -1214,6 +1215,154 @@
 	return usb_start_wait_urb(urb,timeout,actual_length);
 }
 
+/*-------------------------------------------------------------------*/
+
+static void sg_completion (struct urb *urb)
+{
+	struct urb	*base = (struct urb *)urb->context;
+	unsigned	index = urb - base;
+
+	// remember: this one completed
+	urb->transfer_flags = 0;
+
+	// good transfer?  update, maybe complete.
+	if (urb->status == 0) {
+		base->actual_length += urb->actual_length;
+		if (index == base->number_of_packets)
+			base->complete (base);
+	}
+
+	// unlinking after a bad transfer?
+	else if (urb->status == -ECONNRESET) {
+		// ignore
+	}
+
+	// report other errors, such as short reads.  the hcd
+	// should automatically unlink anything still queued.
+	    // NOTE:  But likely they don't, as of 25-nov-2001;
+	    // I'll call that a bug ...
+	else {
+		urb->status = urb->status;
+		base->actual_length += urb->actual_length;
+		base->complete (base);
+	}
+
+	// no matter what, if this is the last URB, free them all.
+	if (index == base->number_of_packets)
+		kfree (base);
+}
+
+/**
+ * usb_submit_bulk_sg - queues an async scatter/gather bulk request
+ * @dev:		device to use for I/O
+ * @pipe: 		pipe (endpoint) to use for I/O
+ * @mem_flags: 		used to kmalloc temporary storage
+ * @urb_complete:	called on completion
+ * @urb_context:	passed to urb_complete()
+ * @sg_data:		array of s/g buffers
+ * @sg_len:		size of that array
+ *
+ * Queues an array of I/O requests as a series of bulk URBs, and issues
+ * the completion callback when those complete.  Note that such queued
+ * transfers are all terminated on the first error, and that if any other
+ * other bulk requests are queued after this one, they may need to be
+ * resubmitted.
+ *
+ * The return value is either null, indicating that no memory was available
+ * to queue the requests, or a handle that may be used to cancel the request
+ * using usb_cancel_bulk_sg().  That handle may not be used after the
+ * completion handler has been invoked.
+ *
+ * The caller may need to ensure that each buffer in the list is a multiple
+ * of the endpoint's maximum packet size.  This routine does not copy or
+ * merge buffers, and will not treat intermediate "short" packets as errors.
+ */
+void *usb_submit_bulk_sg (
+	struct usb_device	*dev,
+	int			pipe,
+	int			mem_flags,
+	void			(*urb_complete)(struct urb *),
+	void			*urb_context,
+	struct scatterlist	*sg_data,
+	unsigned		sg_len
+)
+{
+	struct urb		*urbs;
+	unsigned		i;
+	int			tmp;
+
+	tmp = (sg_len + 1) * sizeof *urbs;
+	urbs = kmalloc (tmp, mem_flags);
+	if (!urbs)
+		return 0;
+	memset (urbs, 0, tmp);
+	
+	// urbs [0] is for housekeeping and the final callback
+	urbs [0].dev = dev;
+	urbs [0].pipe = pipe;
+	urbs [0].complete = urb_complete;
+	urbs [0].context = urb_context;
+	urbs [0].number_of_packets = sg_len;
+	urbs = &urbs [1];
+
+	// the other urbs actually get queued
+	for (i = 0; i < sg_len; i++) {
+		urbs [i].dev = dev;
+		urbs [i].pipe = pipe;
+		// hmm, what about "highmem" I/O?
+		urbs [i].transfer_buffer = sg_data [i].address;
+		urbs [i].transfer_buffer_length = sg_data [i].length;
+		urbs [i].context = sg_completion;
+		urbs [i].context = &urbs [-1];
+		urbs [i].transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK;
+		tmp = usb_submit_urb (&urbs [i]);
+
+		// unlink them all out on error (rare!)
+		if (tmp != 0) {
+			int 	j;
+
+			urbs [-1].status = tmp;
+			urbs [-1].number_of_packets = i;
+			for (j = 0; j < i; j++) 
+				usb_unlink_urb (&urbs [i]);
+		}
+	}
+
+	// status for transfers, or unlink errors,
+	// is delivered asynchronously
+	return urbs;
+}
+
+
+/**
+ * usb_cancel_bulk_sg - cancels a scatter/gather bulk request
+ * @handle:	as returned by usb_submit_bulk_sg
+ * 
+ * Note that this may not be called after the completion function
+ * passed to usb_submit_bulk_sg() returns.  Also, after this is
+ * called, that completion function will be called once.
+ *
+ * Returns zero for success, else the error returned by the first
+ * urb unlink failure.
+ */
+int usb_cancel_bulk_sg (void *handle)
+{
+	struct urb	*urbs = (struct urb *)((struct urb *)handle)->context;
+	int		i;
+	int		retval = 0, status;
+
+	for (i = 1; i <= urbs->number_of_packets; i++) {
+	    if (urbs [i].transfer_flags == 0)
+		continue;
+	    status = usb_unlink_urb (&urbs [i]);
+	    if (status != 0 && retval != 0)
+		retval = status;
+	}
+	return retval;
+}
+
+/*-------------------------------------------------------------------*/
+
 /*
  * usb_get_current_frame_number()
  *
@@ -2405,6 +2554,9 @@
 EXPORT_SYMBOL(usb_get_configuration);
 EXPORT_SYMBOL(usb_set_configuration);
 EXPORT_SYMBOL(usb_get_status);
+
+EXPORT_SYMBOL(usb_submit_bulk_sg);
+EXPORT_SYMBOL(usb_cancel_bulk_sg);
 
 EXPORT_SYMBOL(usb_get_current_frame_number);
 
