--- kernel-source-2.4.21/drivers/usb/scanner.c.alt-usbscanner-fix	2003-07-07 21:51:05 +0400
+++ kernel-source-2.4.21/drivers/usb/scanner.c	2003-07-07 22:13:33 +0400
@@ -411,6 +411,8 @@
  */ 
 #include "scanner.h"
 
+static void purge_scanner(struct scn_usb_data *scn);
+
 static void
 irq_scanner(struct urb *urb)
 {
@@ -501,28 +503,20 @@
 static int
 close_scanner(struct inode * inode, struct file * file)
 {
-	struct scn_usb_data *scn;
-
-	kdev_t scn_minor;
+	struct scn_usb_data *scn = file->private_data;
 
-	scn_minor = USB_SCN_MINOR (inode);
-
-	dbg("close_scanner: scn_minor:%d", scn_minor);
-
-	if (!p_scn_table[scn_minor]) {
-		err("close_scanner(%d): invalid scn_minor", scn_minor);
-		return -ENODEV;
-	}
-
-	down(&scn_mutex);
-
-	scn = p_scn_table[scn_minor];
 	down(&(scn->sem));
 	scn->isopen = 0;
 
 	file->private_data = NULL;
 
-	up(&scn_mutex);
+	if (!scn->present) {
+		/* The device was unplugged while open - need to clean up */
+		up(&(scn->sem));
+		purge_scanner(scn);
+		return 0;
+	}
+
 	up(&(scn->sem));
 
 	return 0;
@@ -550,6 +544,12 @@
 
 	down(&(scn->sem));
 
+	if (!scn->present) {
+		/* The device was unplugged while open */
+		up(&(scn->sem));
+		return -ENODEV;
+	}
+
 	if (!scn->bulk_out_ep) {
 		/* This scanner does not have a bulk-out endpoint */
 		up(&(scn->sem));
@@ -644,6 +644,12 @@
 
 	down(&(scn->sem));
 
+	if (!scn->present) {
+		/* The device was unplugged while open */
+		up(&(scn->sem));
+		return -ENODEV;
+	}
+
 	scn_minor = scn->scn_minor;
 
 	ibuf = scn->ibuf;
@@ -751,6 +757,12 @@
 	scn = file->private_data;
 	down(&(scn->sem));
 
+	if (!scn->present) {
+		/* The device was unplugged while open */
+		up(&(scn->sem));
+		return -ENODEV;
+	}
+
 	dev = scn->scn_dev;
 
 	switch (cmd)
@@ -978,7 +990,7 @@
 	}
 
 /* Check to make sure that the last slot isn't already taken */
-	if (p_scn_table[scn_minor]) {
+	if (scn_minor >= SCN_MAX_MNR) {
 		err("probe_scanner: No more minor devices remaining.");
 		up(&scn_mutex);
 		return NULL;
@@ -1018,6 +1030,8 @@
 /* Ok, now initialize all the relevant values */
 	if (!(scn->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
 		err("probe_scanner(%d): Not enough memory for the output buffer.", scn_minor);
+		if (have_intr)
+			usb_unlink_urb(&scn->scn_irq);
 		kfree(scn);
 		up(&scn_mutex);
 		return NULL;
@@ -1027,6 +1041,8 @@
 	if (!(scn->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
 		err("probe_scanner(%d): Not enough memory for the input buffer.", scn_minor);
 		kfree(scn->obuf);
+		if (have_intr)
+			usb_unlink_urb(&scn->scn_irq);
 		kfree(scn);
 		up(&scn_mutex);
 		return NULL;
@@ -1080,6 +1096,14 @@
 }
 
 static void
+purge_scanner(struct scn_usb_data *scn)
+{
+	kfree(scn->ibuf);
+	kfree(scn->obuf);
+	kfree(scn);
+}
+
+static void
 disconnect_scanner(struct usb_device *dev, void *ptr)
 {
 	struct scn_usb_data *scn = (struct scn_usb_data *) ptr;
@@ -1094,15 +1118,22 @@
         usb_driver_release_interface(&scanner_driver,
                 &scn->scn_dev->actconfig->interface[scn->ifnum]);
 
-	kfree(scn->ibuf);
-	kfree(scn->obuf);
-
 	dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor);
 	devfs_unregister(scn->devfs);
 	p_scn_table[scn->scn_minor] = NULL;
+
+	if (scn->isopen) {
+		/* The device is still open - cleanup must be delayed */
+		scn->present = 0;
+		up(&(scn->sem));
+		up(&scn_mutex);
+		return;
+	}
+
 	up (&(scn->sem));
-	kfree (scn);
 	up (&scn_mutex);
+
+	purge_scanner(scn);
 }
 
 static struct
