Merge git://oss.sgi.com:8090/oss/git/xfs-2.6
[powerpc.git] / drivers / input / serio / serio.c
index f367695..6521034 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/kthread.h>
+#include <linux/mutex.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Serio abstraction core");
@@ -52,16 +53,14 @@ EXPORT_SYMBOL(serio_rescan);
 EXPORT_SYMBOL(serio_reconnect);
 
 /*
- * serio_sem protects entire serio subsystem and is taken every time
+ * serio_mutex protects entire serio subsystem and is taken every time
  * serio port or driver registrered or unregistered.
  */
-static DECLARE_MUTEX(serio_sem);
+static DEFINE_MUTEX(serio_mutex);
 
 static LIST_HEAD(serio_list);
 
-static struct bus_type serio_bus = {
-       .name = "serio",
-};
+static struct bus_type serio_bus;
 
 static void serio_add_port(struct serio *serio);
 static void serio_destroy_port(struct serio *serio);
@@ -72,9 +71,9 @@ static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
 {
        int retval;
 
-       down(&serio->drv_sem);
+       mutex_lock(&serio->drv_mutex);
        retval = drv->connect(serio, drv);
-       up(&serio->drv_sem);
+       mutex_unlock(&serio->drv_mutex);
 
        return retval;
 }
@@ -83,20 +82,20 @@ static int serio_reconnect_driver(struct serio *serio)
 {
        int retval = -1;
 
-       down(&serio->drv_sem);
+       mutex_lock(&serio->drv_mutex);
        if (serio->drv && serio->drv->reconnect)
                retval = serio->drv->reconnect(serio);
-       up(&serio->drv_sem);
+       mutex_unlock(&serio->drv_mutex);
 
        return retval;
 }
 
 static void serio_disconnect_driver(struct serio *serio)
 {
-       down(&serio->drv_sem);
+       mutex_lock(&serio->drv_mutex);
        if (serio->drv)
                serio->drv->disconnect(serio);
-       up(&serio->drv_sem);
+       mutex_unlock(&serio->drv_mutex);
 }
 
 static int serio_match_port(const struct serio_device_id *ids, struct serio *serio)
@@ -197,6 +196,7 @@ static void serio_queue_event(void *object, struct module *owner,
        if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
                if (!try_module_get(owner)) {
                        printk(KERN_WARNING "serio: Can't get module reference, dropping event %d\n", event_type);
+                       kfree(event);
                        goto out;
                }
 
@@ -269,14 +269,20 @@ static struct serio_event *serio_get_event(void)
        return event;
 }
 
-static void serio_handle_events(void)
+static void serio_handle_event(void)
 {
        struct serio_event *event;
        struct serio_driver *serio_drv;
 
-       down(&serio_sem);
+       mutex_lock(&serio_mutex);
 
-       while ((event = serio_get_event())) {
+       /*
+        * Note that we handle only one event here to give swsusp
+        * a chance to freeze kseriod thread. Serio events should
+        * be pretty rare so we are not concerned about taking
+        * performance hit.
+        */
+       if ((event = serio_get_event())) {
 
                switch (event->type) {
                        case SERIO_REGISTER_PORT:
@@ -310,7 +316,7 @@ static void serio_handle_events(void)
                serio_free_event(event);
        }
 
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 /*
@@ -368,7 +374,7 @@ static struct serio *serio_get_pending_child(struct serio *parent)
 static int serio_thread(void *nothing)
 {
        do {
-               serio_handle_events();
+               serio_handle_event();
                wait_event_interruptible(serio_wait,
                        kthread_should_stop() || !list_empty(&serio_event_list));
                try_to_freeze();
@@ -389,6 +395,14 @@ static ssize_t serio_show_description(struct device *dev, struct device_attribut
        return sprintf(buf, "%s\n", serio->name);
 }
 
+static ssize_t serio_show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct serio *serio = to_serio_port(dev);
+
+       return sprintf(buf, "serio:ty%02Xpr%02Xid%02Xex%02X\n",
+                       serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
+}
+
 static ssize_t serio_show_id_type(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
@@ -437,7 +451,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
        struct device_driver *drv;
        int retval;
 
-       retval = down_interruptible(&serio_sem);
+       retval = mutex_lock_interruptible(&serio_mutex);
        if (retval)
                return retval;
 
@@ -457,7 +471,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
                retval = -EINVAL;
        }
 
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 
        return retval;
 }
@@ -487,6 +501,7 @@ static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *
 
 static struct device_attribute serio_device_attrs[] = {
        __ATTR(description, S_IRUGO, serio_show_description, NULL),
+       __ATTR(modalias, S_IRUGO, serio_show_modalias, NULL),
        __ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver),
        __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode),
        __ATTR_NULL
@@ -511,7 +526,7 @@ static void serio_init_port(struct serio *serio)
        __module_get(THIS_MODULE);
 
        spin_lock_init(&serio->lock);
-       init_MUTEX(&serio->drv_sem);
+       mutex_init(&serio->drv_mutex);
        device_initialize(&serio->dev);
        snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id),
                 "serio%ld", (long)atomic_inc_return(&serio_no) - 1);
@@ -648,10 +663,10 @@ void __serio_register_port(struct serio *serio, struct module *owner)
  */
 void serio_unregister_port(struct serio *serio)
 {
-       down(&serio_sem);
+       mutex_lock(&serio_mutex);
        serio_disconnect_port(serio);
        serio_destroy_port(serio);
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 /*
@@ -659,17 +674,17 @@ void serio_unregister_port(struct serio *serio)
  */
 void serio_unregister_child_port(struct serio *serio)
 {
-       down(&serio_sem);
+       mutex_lock(&serio_mutex);
        if (serio->child) {
                serio_disconnect_port(serio->child);
                serio_destroy_port(serio->child);
        }
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 /*
  * Submits register request to kseriod for subsequent execution.
- * Can be used when it is not obvious whether the serio_sem is
+ * Can be used when it is not obvious whether the serio_mutex is
  * taken or not and when delayed execution is feasible.
  */
 void __serio_unregister_port_delayed(struct serio *serio, struct module *owner)
@@ -735,11 +750,15 @@ static int serio_driver_remove(struct device *dev)
        return 0;
 }
 
+static struct bus_type serio_bus = {
+       .name = "serio",
+       .probe = serio_driver_probe,
+       .remove = serio_driver_remove,
+};
+
 void __serio_register_driver(struct serio_driver *drv, struct module *owner)
 {
        drv->driver.bus = &serio_bus;
-       drv->driver.probe = serio_driver_probe;
-       drv->driver.remove = serio_driver_remove;
 
        serio_queue_event(drv, owner, SERIO_REGISTER_DRIVER);
 }
@@ -748,7 +767,7 @@ void serio_unregister_driver(struct serio_driver *drv)
 {
        struct serio *serio;
 
-       down(&serio_sem);
+       mutex_lock(&serio_mutex);
        drv->manual_bind = 1;   /* so serio_find_driver ignores it */
 
 start_over:
@@ -762,7 +781,7 @@ start_over:
        }
 
        driver_unregister(&drv->driver);
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 static void serio_set_drv(struct serio *serio, struct serio_driver *drv)
@@ -785,40 +804,41 @@ static int serio_bus_match(struct device *dev, struct device_driver *drv)
 
 #ifdef CONFIG_HOTPLUG
 
-#define PUT_ENVP(fmt, val)                                             \
-do {                                                                   \
-       envp[i++] = buffer;                                             \
-       length += snprintf(buffer, buffer_size - length, fmt, val);     \
-       if (buffer_size - length <= 0 || i >= num_envp)                 \
-               return -ENOMEM;                                         \
-       length++;                                                       \
-       buffer += length;                                               \
-} while (0)
-static int serio_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
+#define SERIO_ADD_UEVENT_VAR(fmt, val...)                              \
+       do {                                                            \
+               int err = add_uevent_var(envp, num_envp, &i,    \
+                                       buffer, buffer_size, &len,      \
+                                       fmt, val);                      \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
 {
        struct serio *serio;
        int i = 0;
-       int length = 0;
+       int len = 0;
 
        if (!dev)
                return -ENODEV;
 
        serio = to_serio_port(dev);
 
-       PUT_ENVP("SERIO_TYPE=%02x", serio->id.type);
-       PUT_ENVP("SERIO_PROTO=%02x", serio->id.proto);
-       PUT_ENVP("SERIO_ID=%02x", serio->id.id);
-       PUT_ENVP("SERIO_EXTRA=%02x", serio->id.extra);
-
+       SERIO_ADD_UEVENT_VAR("SERIO_TYPE=%02x", serio->id.type);
+       SERIO_ADD_UEVENT_VAR("SERIO_PROTO=%02x", serio->id.proto);
+       SERIO_ADD_UEVENT_VAR("SERIO_ID=%02x", serio->id.id);
+       SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra);
+       SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",
+                               serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
        envp[i] = NULL;
 
        return 0;
 }
-#undef PUT_ENVP
+#undef SERIO_ADD_UEVENT_VAR
 
 #else
 
-static int serio_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
+static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
 {
        return -ENODEV;
 }
@@ -840,7 +860,7 @@ static int serio_resume(struct device *dev)
        return 0;
 }
 
-/* called from serio_driver->connect/disconnect methods under serio_sem */
+/* called from serio_driver->connect/disconnect methods under serio_mutex */
 int serio_open(struct serio *serio, struct serio_driver *drv)
 {
        serio_set_drv(serio, drv);
@@ -852,7 +872,7 @@ int serio_open(struct serio *serio, struct serio_driver *drv)
        return 0;
 }
 
-/* called from serio_driver->connect/disconnect methods under serio_sem */
+/* called from serio_driver->connect/disconnect methods under serio_mutex */
 void serio_close(struct serio *serio)
 {
        if (serio->close)
@@ -892,7 +912,7 @@ static int __init serio_init(void)
        serio_bus.dev_attrs = serio_device_attrs;
        serio_bus.drv_attrs = serio_driver_attrs;
        serio_bus.match = serio_bus_match;
-       serio_bus.hotplug = serio_hotplug;
+       serio_bus.uevent = serio_uevent;
        serio_bus.resume = serio_resume;
        bus_register(&serio_bus);
 
@@ -905,5 +925,5 @@ static void __exit serio_exit(void)
        kthread_stop(serio_task);
 }
 
-module_init(serio_init);
+subsys_initcall(serio_init);
 module_exit(serio_exit);