Hi,

This patches (from Sergey Vlasov) fixes the following problem: When
the scanner is unplugged during the scan, the usage count doesn't seem
to be decremented and therefore the scanner module can't be unloaded.

While without the patch the bug occurs almost everytime I unplug the
scanner, it hasn't ever occured with the patch.

Easier (less intrusive) approaches are welcome.

The patch is sone ontop of the new id/silence/endpoint patches from
today.

2.5.x patches for all the patches will follow once I get 2.5. running
here :-)

Bye,
  Henning
  
diff -u -r linux-2.4.21-pre1.endpoint/drivers/usb/scanner.c 
linux-2.4.21-pre1.nohang/drivers/usb/scanner.c
--- linux-2.4.21-pre1.endpoint/drivers/usb/scanner.c    2002-12-19 22:08:17.000000000 
+0100
+++ linux-2.4.21-pre1.nohang/drivers/usb/scanner.c      2002-12-19 22:54:20.000000000 
++0100
@@ -324,6 +324,9 @@
  *    - Don't clutter syslog with "Unable to access minor data" messages.
  *    - Accept scanners with only one bulk (in) endpoint (thanks to Sergey
  *      Vlasov <[EMAIL PROTECTED]>).
+ *    - Fix the "can't rmmod scanner" bug that sometimes occured when the
+ *      scanner was unplugged while opened (thanks to Sergey Vlasov 
+ *      <[EMAIL PROTECTED]>).
  *
  * TODO
  *    - Remove the 2/3 endpoint limitation
@@ -363,6 +366,21 @@
  */ 
 #include "scanner.h"
 
+static void usb_scanner_free(struct scn_usb_data *scn);
+
+static inline void
+usb_scanner_inc_use(struct scn_usb_data *scn)
+{
+       atomic_inc(&scn->users);
+}
+
+static inline void
+usb_scanner_dec_use(struct scn_usb_data *scn)
+{
+       if (atomic_dec_and_test(&scn->users))
+               usb_scanner_free(scn);
+}
+
 static void
 irq_scanner(struct urb *urb)
 {
@@ -400,26 +418,33 @@
 
        MOD_INC_USE_COUNT;
 
-       down(&scn_mutex);
-
        scn_minor = USB_SCN_MINOR(inode);
 
        dbg("open_scanner: scn_minor:%d", scn_minor);
 
-       if (!p_scn_table[scn_minor]) {
+       if (down_interruptible(&scn_mutex) != 0) {
+               MOD_DEC_USE_COUNT;
+               return -ERESTARTSYS;
+       }
+
+       scn = p_scn_table[scn_minor];
+       if (!scn) {
                up(&scn_mutex);
                MOD_DEC_USE_COUNT;
                dbg("open_scanner(%d): Unable to access minor data", scn_minor);
                return -ENODEV;
        }
 
-       scn = p_scn_table[scn_minor];
+       usb_scanner_inc_use(scn); /* Make sure the device won't go away */
 
-       dev = scn->scn_dev;
+       up(&scn_mutex); /* Now handled by the above */
 
-       down(&(scn->sem));      /* Now protect the scn_usb_data structure */ 
+       if (down_interruptible(&(scn->open_sem)) != 0) {
+               err = -ERESTARTSYS;
+               goto out_error_nounlock;
+       }
 
-       up(&scn_mutex); /* Now handled by the above */
+       dev = scn->scn_dev;
 
        if (!dev) {
                err("open_scanner(%d): Scanner device not present", scn_minor);
@@ -447,11 +472,13 @@
 
 
 out_error:
+       up(&(scn->open_sem)); /* Wake up any possible contending processes */
 
-       up(&(scn->sem)); /* Wake up any possible contending processes */
-
-       if (err)
+out_error_nounlock:
+       if (err) {
+               usb_scanner_dec_use(scn);
                MOD_DEC_USE_COUNT;
+       }
 
        return err;
 }
@@ -461,27 +488,15 @@
 {
        struct scn_usb_data *scn;
 
-       kdev_t scn_minor;
-
-       scn_minor = USB_SCN_MINOR (inode);
-
-       dbg("close_scanner: scn_minor:%d", scn_minor);
+       scn = file->private_data;
 
-       if (!p_scn_table[scn_minor]) {
-               err("close_scanner(%d): invalid scn_minor", scn_minor);
-               return -ENODEV;
-       }
+       dbg("close_scanner: scn_minor:%d", scn->scn_minor);
 
-       down(&scn_mutex);
-
-       scn = p_scn_table[scn_minor];
-       down(&(scn->sem));
        scn->isopen = 0;
 
        file->private_data = NULL;
 
-       up(&scn_mutex);
-       up(&(scn->sem));
+       usb_scanner_dec_use(scn);
 
        MOD_DEC_USE_COUNT;
 
@@ -508,7 +523,8 @@
 
        scn = file->private_data;
 
-       down(&(scn->sem));
+       if (down_interruptible(&(scn->sem)) != 0)
+               return -ERESTARTSYS;
 
        if (!scn->bulk_out_ep) {
                /* This scanner does not have a bulk-out endpoint */
@@ -602,7 +618,8 @@
 
        scn = file->private_data;
 
-       down(&(scn->sem));
+       if (down_interruptible(&(scn->sem)) != 0)
+               return -ERESTARTSYS;
 
        scn_minor = scn->scn_minor;
 
@@ -704,18 +721,14 @@
 ioctl_scanner(struct inode *inode, struct file *file,
              unsigned int cmd, unsigned long arg)
 {
+       struct scn_usb_data *scn;
        struct usb_device *dev;
 
        kdev_t scn_minor;
 
-       scn_minor = USB_SCN_MINOR(inode);
-
-       if (!p_scn_table[scn_minor]) {
-               err("ioctl_scanner(%d): invalid scn_minor", scn_minor);
-               return -ENODEV;
-       }
-
-       dev = p_scn_table[scn_minor]->scn_dev;
+       scn = file->private_data;
+       dev = scn->scn_dev;
+       scn_minor = scn->scn_minor;
 
        switch (cmd)
        {
@@ -1000,7 +1013,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;
@@ -1015,6 +1028,8 @@
        }
        memset (scn, 0, sizeof(struct scn_usb_data));
 
+       atomic_set(&scn->users, 1);
+       init_MUTEX(&(scn->open_sem));
        init_MUTEX(&(scn->sem)); /* Initializes to unlocked */
 
        dbg ("probe_scanner(%d): Address of scn:%p", scn_minor, scn);
@@ -1080,6 +1095,7 @@
        scn->intr_ep = have_intr;
        scn->present = 1;
        scn->scn_dev = dev;
+       usb_inc_dev_use(dev);
        scn->scn_minor = scn_minor;
        scn->isopen = 0;
 
@@ -1101,29 +1117,37 @@
 }
 
 static void
-disconnect_scanner(struct usb_device *dev, void *ptr)
+usb_scanner_free(struct scn_usb_data *scn)
 {
-       struct scn_usb_data *scn = (struct scn_usb_data *) ptr;
-
-       down (&scn_mutex);
-       down (&(scn->sem));
+       dbg("usb_scanner_free(%d): freeing scn=%p", scn->scn_minor, scn);
 
        if(scn->intr_ep) {
                dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor);
                usb_unlink_urb(&scn->scn_irq);
        }
-        usb_driver_release_interface(&scanner_driver,
-                &scn->scn_dev->actconfig->interface[scn->ifnum]);
 
+       usb_dec_dev_use(scn->scn_dev);
        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;
+
+       down (&scn_mutex);
+        usb_driver_release_interface(&scanner_driver,
+                &scn->scn_dev->actconfig->interface[scn->ifnum]);
+
        dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor);
        devfs_unregister(scn->devfs);
        p_scn_table[scn->scn_minor] = NULL;
-       up (&(scn->sem));
-       kfree (scn);
        up (&scn_mutex);
+
+       usb_scanner_dec_use(scn);
 }
 
 static struct
diff -u -r linux-2.4.21-pre1.endpoint/drivers/usb/scanner.h 
linux-2.4.21-pre1.nohang/drivers/usb/scanner.h
--- linux-2.4.21-pre1.endpoint/drivers/usb/scanner.h    2002-12-19 21:53:46.000000000 
+0100
+++ linux-2.4.21-pre1.nohang/drivers/usb/scanner.h      2002-12-19 23:01:15.000000000 
++0100
@@ -285,6 +285,7 @@
 static DECLARE_MUTEX (scn_mutex); /* Initializes to unlocked */
 
 struct scn_usb_data {
+       atomic_t users;         /* Reference count */
        struct usb_device *scn_dev;
        devfs_handle_t devfs;   /* devfs device */
        struct urb scn_irq;
@@ -296,6 +297,7 @@
        char *obuf, *ibuf;      /* transfer buffers */
        char bulk_in_ep, bulk_out_ep, intr_ep; /* Endpoint assignments */
        wait_queue_head_t rd_wait_q; /* read timeouts */
+       struct semaphore open_sem; /* lock to prevent concurrent opens */
        struct semaphore sem; /* lock to prevent concurrent reads or writes */
        unsigned int rd_nak_timeout; /* Seconds to wait before read() timeout. */
 };


-------------------------------------------------------
This SF.NET email is sponsored by: Geek Gift Procrastinating?
Get the perfect geek gift now!  Before the Holidays pass you by.
T H I N K G E E K . C O M      http://www.thinkgeek.com/sf/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to