Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / usb / storage / libusual.c
index b1ec4a7..38faca7 100644 (file)
@@ -5,6 +5,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/device.h>
 #include <linux/usb.h>
 #include <linux/usb_usual.h>
 #include <linux/vmalloc.h>
@@ -13,6 +14,7 @@
  */
 #define USU_MOD_FL_THREAD   1  /* Thread is running */
 #define USU_MOD_FL_PRESENT  2  /* The module is loaded */
+#define USU_MOD_FL_FAILED   4  /* The module failed to load */
 
 struct mod_status {
        unsigned long fls;
@@ -33,8 +35,12 @@ static DECLARE_MUTEX_LOCKED(usu_init_notify);
 static DECLARE_COMPLETION(usu_end_notify);
 static atomic_t total_threads = ATOMIC_INIT(0);
 
+static int usu_kick(unsigned long type);
 static int usu_probe_thread(void *arg);
 
+static struct class *usu_class;
+static struct class_device *usu_class_device;
+
 /*
  * The table.
  */
@@ -111,18 +117,44 @@ int usb_usual_check_type(const struct usb_device_id *id, int caller_type)
 }
 EXPORT_SYMBOL_GPL(usb_usual_check_type);
 
+/*
+ */
+static int usu_uevent(struct class_device *class_dev,
+    char **envp, int num_envp, char *buffer, int buffer_size)
+{
+       unsigned long flags;
+       int i;
+
+       for (i = 1; i < 3; i++) {
+               spin_lock_irqsave(&usu_lock, flags);
+               if (stat[i].fls & USU_MOD_FL_FAILED) {
+                       stat[i].fls &= ~USU_MOD_FL_FAILED;
+                       spin_unlock_irqrestore(&usu_lock, flags);
+                       usu_kick(i);
+               } else {
+                       spin_unlock_irqrestore(&usu_lock, flags);
+               }
+       }
+       return 0;
+}
+
 /*
  */
 static int usu_probe(struct usb_interface *intf,
                         const struct usb_device_id *id)
 {
        unsigned long type;
-       int rc;
-       unsigned long flags;
 
        type = USB_US_TYPE(id->driver_info);
        if (type == 0)
                type = atomic_read(&usu_bias);
+       return usu_kick(type);
+}
+
+static int usu_kick(unsigned long type)
+{
+       int rc;
+       unsigned long flags;
 
        spin_lock_irqsave(&usu_lock, flags);
        if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) {
@@ -186,10 +218,14 @@ static int usu_probe_thread(void *arg)
        if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) {
                /*
                 * This should not happen, but let us keep tabs on it.
+                * One common source of this a user who builds USB statically,
+                * then uses initrd, and has a USB device. When static devices
+                * are probed, request_module() calls a fake modprobe and fails.
                 */
                printk(KERN_NOTICE "libusual: "
-                   "modprobe for %s succeeded, but module is not present\n",
+                   "request for %s succeeded, but module is not present\n",
                    bias_names[type]);
+               st->fls |= USU_MOD_FL_FAILED;
        }
        st->fls &= ~USU_MOD_FL_THREAD;
        spin_unlock_irqrestore(&usu_lock, flags);
@@ -203,9 +239,27 @@ static int __init usb_usual_init(void)
 {
        int rc;
 
+       usu_class = class_create(THIS_MODULE, "libusual");
+       if (IS_ERR(usu_class)) {
+               rc = PTR_ERR(usu_class_device);
+               goto err_class;
+       }
+       usu_class_device = class_device_create(usu_class, NULL, 0, NULL, "0");
+       if (IS_ERR(usu_class_device)) {
+               rc = PTR_ERR(usu_class_device);
+               goto err_classdev;
+       }
+       usu_class_device->uevent = usu_uevent;
+
        rc = usb_register(&usu_driver);
        up(&usu_init_notify);
        return rc;
+
+       // class_device_destroy(usu_class, 0);
+err_classdev:
+       class_destroy(usu_class);
+err_class:
+       return rc;
 }
 
 static void __exit usb_usual_exit(void)
@@ -221,6 +275,9 @@ static void __exit usb_usual_exit(void)
                wait_for_completion(&usu_end_notify);
                atomic_dec(&total_threads);
        }
+
+       class_device_destroy(usu_class, 0);
+       class_destroy(usu_class);
 }
 
 /*