--- linux-2.4.18/drivers/usb/scanner.c	Fri Dec 21 18:41:55 2001
+++ linux/drivers/usb/scanner.c	Sun Aug 25 20:59:53 2002
@@ -410,8 +410,6 @@
 		goto out_error;
 	}
 
-	init_waitqueue_head(&scn->rd_wait_q);
-
 	scn->isopen = 1;
 
 	file->private_data = scn; /* Used by the read and write methods */
@@ -546,123 +544,187 @@
 	return ret ? ret : bytes_written;
 }
 
-static ssize_t
-read_scanner(struct file * file, char * buffer,
-             size_t count, loff_t *ppos)
-{
-	struct scn_usb_data *scn;
-	struct usb_device *dev;
-
-	ssize_t bytes_read;	/* Overall count of bytes_read */
-	ssize_t ret;
-
-	kdev_t scn_minor;
+#define SCANNER_URB_SIZE (64)
+#define SCANNER_URB_COUNT (IBUF_SIZE / sizeof (struct scanner_urb))
 
-	int partial;		/* Number of bytes successfully read */
-	int this_read;		/* Max number of bytes to read */
-	int result;
-	int rd_expire = RD_EXPIRE;
+struct scanner_urb
+{
+	struct urb          urb;
+	char                buf[SCANNER_URB_SIZE];
+};
 
-	char *ibuf;
+struct scanner_context
+{
+	wait_queue_head_t wait;
+	unsigned int      count;
+};
 
-	scn = file->private_data;
+static void
+scanner_complete (struct urb* urb)
+{
+	struct scanner_context* context;
+	unsigned long           count;
 
-	down(&(scn->sem));
+	context = urb->context;
+	count = context->count + urb->transfer_buffer_length;
+	dbg ("complete: %lu", (unsigned long) count);
+	barrier ();
+	context->count = count;
+	wake_up_sync (&context->wait);
+}
 
-	scn_minor = scn->scn_minor;
+static inline void
+scanner_dec (struct scanner_urb** urb_ptr,
+             struct scanner_urb*  urbs)
+{
+	if (urbs == *urb_ptr) {
+		*urb_ptr = urbs + (SCANNER_URB_COUNT - 1);
+	}
+	else {
+		*urb_ptr -= 1;
+	}
+}
 
-	ibuf = scn->ibuf;
+static ssize_t
+read_scanner (struct file* file,
+              char*        buffer,
+              size_t       count,
+              loff_t*      ppos)
+{
+	struct scn_usb_data*   scn;
+	struct scanner_context context;
+	struct scanner_urb*    urbs;
+	struct scanner_urb*    in;
+	struct scanner_urb*    out;
+	unsigned int           rcv;
+	unsigned int           snd;
+	char*                  ptr;
+	ssize_t                result;
+
+	dbg ("reading: %lu", (unsigned long) count);
+
+	/* early return */
+
+	if (count == 0) {
+		dbg ("nothing to do");
+		return 0;
+	}
 
-	dev = scn->scn_dev;
+	scn = file->private_data;
 
-	bytes_read = 0;
-	ret = 0;
+	/* locking */
 
-	file->f_dentry->d_inode->i_atime = CURRENT_TIME; /* Update the
-                                                            atime of
-                                                            the device
-                                                            node */
-	while (count > 0) {
-		if (signal_pending(current)) {
-			ret = -ERESTARTSYS;
-			break;
-		}
+	if (down_interruptible (&scn->sem)) {
+		dbg ("interrupted");
+		return -ERESTARTSYS;
+	}
 
-		this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
+	/* Updating atime of the device node */
 
-		result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, scn->rd_nak_timeout);
-		dbg("read stats(%d): result:%d this_read:%d partial:%d count:%d", scn_minor, result, this_read, partial, count);
+	file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 
-/*
- * Scanners are sometimes inheriently slow since they are mechanical
- * in nature.  USB bulk reads tend to timeout while the scanner is
- * positioning, resetting, warming up the lamp, etc if the timeout is
- * set too low.  A very long timeout parameter for bulk reads was used
- * to overcome this limitation, but this sometimes resulted in folks
- * having to wait for the timeout to expire after pressing Ctrl-C from
- * an application. The user was sometimes left with the impression
- * that something had hung or crashed when in fact the USB read was
- * just waiting on data.  So, the below code retains the same long
- * timeout period, but splits it up into smaller parts so that
- * Ctrl-C's are acted upon in a reasonable amount of time.
- */
+	/* initialising */
 
-		if (result == -ETIMEDOUT) { /* NAK */
-			if (!partial) { /* No data */
-				if (--rd_expire <= 0) {	/* Give it up */
-					warn("read_scanner(%d): excessive NAK's received", scn_minor);
-					ret = result;
-					break;
-				} else { /* Keep trying to read data */
-					interruptible_sleep_on_timeout(&scn->rd_wait_q, scn->rd_nak_timeout);
-					continue;
+	ptr = buffer;
+	urbs = (struct scanner_urb*) scn->ibuf;
+	memset (urbs, 0, sizeof (struct scanner_urb) * SCANNER_URB_COUNT);
+	in = out = urbs + (SCANNER_URB_COUNT - 1);
+	context.count = snd = rcv = 0;
+	init_waitqueue_head (&context.wait);
+
+	/* looping */
+
+	do {
+		/* scheduling */
+		while (((in != out) || (snd == rcv)) && (snd != count)) {
+			FILL_BULK_URB (&out->urb,
+			               scn->scn_dev,
+			               usb_rcvbulkpipe (scn->scn_dev,
+			                                scn->bulk_in_ep),
+			               &out->buf,
+			               min_t (size_t, 
+			                      count - snd, 
+			                      SCANNER_URB_SIZE),
+			               scanner_complete,
+			               &context);
+			out->urb.transfer_flags = USB_QUEUE_BULK;
+			result = usb_submit_urb (&out->urb);
+			if (result != 0) {
+				err ("schedule failed: %li", (long) result);
+				goto scanner_read_cleanup;
+			}
+			snd += out->urb.transfer_buffer_length;
+			scanner_dec (&out, urbs);
+			dbg ("scheduled: %lu", (unsigned long) snd);
+		}
+
+		/* waiting */
+
+		result = wait_event_interruptible (context.wait,
+		                                   rcv != context.count);
+		if (result != 0) {
+			dbg ("wait interrupted");
+			goto scanner_read_cleanup;
+		}
+
+		/* processing */
+
+		result = in->urb.status;
+		if ((result != 0) && (result != -EREMOTEIO)) {
+			warn ("read_scanner(%d): error:%d. "
+			      "Consult Documentation/usb/scanner.txt.",
+			      scn->scn_minor, result);
+			if (usb_endpoint_halted (
+			          scn->scn_dev,
+			          usb_rcvbulkpipe (scn->scn_dev,
+			                           scn->bulk_in_ep),
+			          0)) {
+				if (usb_clear_halt (scn->scn_dev,
+				                    scn->bulk_in_ep)) {
+					err ("read_scanner(%d): Failure to "
+					     "clear endpoint halt condition "
+					     "(%Zd).",
+					     scn->scn_minor, result);
 				}
-			} else { /* Timeout w/ some data */
-				goto data_recvd;
 			}
+			result = -EIO;
+			goto scanner_read_cleanup;
 		}
-		
-		if (result == -EPIPE) { /* No hope */
-			if(usb_clear_halt(dev, scn->bulk_in_ep)) {
-				err("read_scanner(%d): Failure to clear endpoint halt condition (%Zd).", scn_minor, ret);
-			}
-			ret = result;
-			break;
-		} else if ((result < 0) && (result != USB_ST_DATAUNDERRUN)) {
-			warn("read_scanner(%d): funky result:%d. Consult Documentation/usb/scanner.txt.", scn_minor, (int)result);
-			ret = -EIO;
-			break;
+ 		if (in->urb.actual_length == 0) {
+			result = ptr - buffer;
+			err ("no data");
+			goto scanner_read_cleanup;
 		}
+		copy_to_user (ptr, in->buf, in->urb.actual_length);
+		ptr += in->urb.actual_length;
+		rcv += in->urb.transfer_buffer_length;
+		scanner_dec (&in, urbs);
+		dbg ("received: %lu", (unsigned long) rcv);
+	}
+	while (rcv != count);
 
-	data_recvd:
+	result = ptr - buffer;
 
-#ifdef RD_DATA_DUMP
-		if (partial) {
-			unsigned char cnt, cnt_max;
-			cnt_max = (partial > 24) ? 24 : partial;
-			printk(KERN_DEBUG "dump(%d): ", scn_minor);
-			for (cnt=0; cnt < cnt_max; cnt++) {
-				printk("%X ", ibuf[cnt]);
-			}
-			printk("\n");
-		}
-#endif
+	/* cleaning up */
 
-		if (partial) { /* Data returned */
-			if (copy_to_user(buffer, ibuf, partial)) {
-				ret = -EFAULT;
-				break;
-			}
-			count -= this_read; /* Compensate for short reads */
-			bytes_read += partial; /* Keep tally of what actually was read */
-			buffer += partial;
-		} else {
-			ret = 0;
-			break;
+scanner_read_cleanup:
+
+	if (rcv != snd) {
+		dbg ("cleaning up");
+		do {
+			usb_unlink_urb (&in->urb);
+			scanner_dec (&in, urbs);
 		}
+		while (in != out);
 	}
-	up(&(scn->sem));
-	return ret ? ret : bytes_read;
+
+	/* unlocking */
+
+	up (&scn->sem);
+
+	dbg ("returning %li", (signed long) result);
+
+	return result;
 }
 
 static int
