fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / input / serio / serio.c
index 0beacb7..f0ce822 100644 (file)
 #include <linux/serio.h>
 #include <linux/errno.h>
 #include <linux/wait.h>
-#include <linux/completion.h>
 #include <linux/sched.h>
-#include <linux/smp_lock.h>
 #include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/freezer.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Serio abstraction core");
@@ -43,8 +44,8 @@ MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(serio_interrupt);
 EXPORT_SYMBOL(__serio_register_port);
 EXPORT_SYMBOL(serio_unregister_port);
-EXPORT_SYMBOL(__serio_unregister_port_delayed);
-EXPORT_SYMBOL(__serio_register_driver);
+EXPORT_SYMBOL(serio_unregister_child_port);
+EXPORT_SYMBOL(serio_register_driver);
 EXPORT_SYMBOL(serio_unregister_driver);
 EXPORT_SYMBOL(serio_open);
 EXPORT_SYMBOL(serio_close);
@@ -52,21 +53,50 @@ 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);
 static void serio_reconnect_port(struct serio *serio);
 static void serio_disconnect_port(struct serio *serio);
+static void serio_attach_driver(struct serio_driver *drv);
+
+static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
+{
+       int retval;
+
+       mutex_lock(&serio->drv_mutex);
+       retval = drv->connect(serio, drv);
+       mutex_unlock(&serio->drv_mutex);
+
+       return retval;
+}
+
+static int serio_reconnect_driver(struct serio *serio)
+{
+       int retval = -1;
+
+       mutex_lock(&serio->drv_mutex);
+       if (serio->drv && serio->drv->reconnect)
+               retval = serio->drv->reconnect(serio);
+       mutex_unlock(&serio->drv_mutex);
+
+       return retval;
+}
+
+static void serio_disconnect_driver(struct serio *serio)
+{
+       mutex_lock(&serio->drv_mutex);
+       if (serio->drv)
+               serio->drv->disconnect(serio);
+       mutex_unlock(&serio->drv_mutex);
+}
 
 static int serio_match_port(const struct serio_device_id *ids, struct serio *serio)
 {
@@ -87,17 +117,29 @@ static int serio_match_port(const struct serio_device_id *ids, struct serio *ser
 
 static void serio_bind_driver(struct serio *serio, struct serio_driver *drv)
 {
+       int error;
+
        down_write(&serio_bus.subsys.rwsem);
 
        if (serio_match_port(drv->id_table, serio)) {
                serio->dev.driver = &drv->driver;
-               if (drv->connect(serio, drv)) {
+               if (serio_connect_driver(serio, drv)) {
+                       serio->dev.driver = NULL;
+                       goto out;
+               }
+               error = device_bind_driver(&serio->dev);
+               if (error) {
+                       printk(KERN_WARNING
+                               "serio: device_bind_driver() failed "
+                               "for %s (%s) and %s, error: %d\n",
+                               serio->phys, serio->name,
+                               drv->description, error);
+                       serio_disconnect_driver(serio);
                        serio->dev.driver = NULL;
                        goto out;
                }
-               device_bind_driver(&serio->dev);
        }
-out:
+ out:
        up_write(&serio_bus.subsys.rwsem);
 }
 
@@ -110,8 +152,14 @@ static void serio_release_driver(struct serio *serio)
 
 static void serio_find_driver(struct serio *serio)
 {
+       int error;
+
        down_write(&serio_bus.subsys.rwsem);
-       device_attach(&serio->dev);
+       error = device_attach(&serio->dev);
+       if (error < 0)
+               printk(KERN_WARNING
+                       "serio: device_attach() failed for %s (%s), error: %d\n",
+                       serio->phys, serio->name, error);
        up_write(&serio_bus.subsys.rwsem);
 }
 
@@ -121,11 +169,10 @@ static void serio_find_driver(struct serio *serio)
  */
 
 enum serio_event_type {
-       SERIO_RESCAN,
-       SERIO_RECONNECT,
+       SERIO_RESCAN_PORT,
+       SERIO_RECONNECT_PORT,
        SERIO_REGISTER_PORT,
-       SERIO_UNREGISTER_PORT,
-       SERIO_REGISTER_DRIVER,
+       SERIO_ATTACH_DRIVER,
 };
 
 struct serio_event {
@@ -138,24 +185,24 @@ struct serio_event {
 static DEFINE_SPINLOCK(serio_event_lock);      /* protects serio_event_list */
 static LIST_HEAD(serio_event_list);
 static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
-static DECLARE_COMPLETION(serio_exited);
-static int serio_pid;
+static struct task_struct *serio_task;
 
-static void serio_queue_event(void *object, struct module *owner,
-                             enum serio_event_type event_type)
+static int serio_queue_event(void *object, struct module *owner,
+                            enum serio_event_type event_type)
 {
        unsigned long flags;
        struct serio_event *event;
+       int retval = 0;
 
        spin_lock_irqsave(&serio_event_lock, flags);
 
        /*
-        * Scan event list for the other events for the same serio port,
+        * Scan event list for the other events for the same serio port,
         * starting with the most recent one. If event is the same we
         * do not need add new one. If event is of different type we
         * need to add this event and should not look further because
         * we need to preseve sequence of distinct events.
-        */
+        */
        list_for_each_entry_reverse(event, &serio_event_list, node) {
                if (event->object == object) {
                        if (event->type == event_type)
@@ -164,23 +211,34 @@ 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);
-                       goto out;
-               }
-
-               event->type = event_type;
-               event->object = object;
-               event->owner = owner;
+       event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
+       if (!event) {
+               printk(KERN_ERR
+                       "serio: Not enough memory to queue event %d\n",
+                       event_type);
+               retval = -ENOMEM;
+               goto out;
+       }
 
-               list_add_tail(&event->node, &serio_event_list);
-               wake_up(&serio_wait);
-       } else {
-               printk(KERN_ERR "serio: Not enough memory to queue event %d\n", event_type);
+       if (!try_module_get(owner)) {
+               printk(KERN_WARNING
+                       "serio: Can't get module reference, dropping event %d\n",
+                       event_type);
+               kfree(event);
+               retval = -EINVAL;
+               goto out;
        }
+
+       event->type = event_type;
+       event->object = object;
+       event->owner = owner;
+
+       list_add_tail(&event->node, &serio_event_list);
+       wake_up(&serio_wait);
+
 out:
        spin_unlock_irqrestore(&serio_event_lock, flags);
+       return retval;
 }
 
 static void serio_free_event(struct serio_event *event)
@@ -239,37 +297,36 @@ 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:
                                serio_add_port(event->object);
                                break;
 
-                       case SERIO_UNREGISTER_PORT:
-                               serio_disconnect_port(event->object);
-                               serio_destroy_port(event->object);
-                               break;
-
-                       case SERIO_RECONNECT:
+                       case SERIO_RECONNECT_PORT:
                                serio_reconnect_port(event->object);
                                break;
 
-                       case SERIO_RESCAN:
+                       case SERIO_RESCAN_PORT:
                                serio_disconnect_port(event->object);
                                serio_find_driver(event->object);
                                break;
 
-                       case SERIO_REGISTER_DRIVER:
-                               serio_drv = event->object;
-                               driver_register(&serio_drv->driver);
+                       case SERIO_ATTACH_DRIVER:
+                               serio_attach_driver(event->object);
                                break;
 
                        default:
@@ -280,7 +337,7 @@ static void serio_handle_events(void)
                serio_free_event(event);
        }
 
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 }
 
 /*
@@ -337,20 +394,15 @@ static struct serio *serio_get_pending_child(struct serio *parent)
 
 static int serio_thread(void *nothing)
 {
-       lock_kernel();
-       daemonize("kseriod");
-       allow_signal(SIGTERM);
-
        do {
-               serio_handle_events();
-               wait_event_interruptible(serio_wait, !list_empty(&serio_event_list));
-               try_to_freeze(PF_FREEZE);
-       } while (!signal_pending(current));
+               serio_handle_event();
+               wait_event_interruptible(serio_wait,
+                       kthread_should_stop() || !list_empty(&serio_event_list));
+               try_to_freeze();
+       } while (!kthread_should_stop());
 
        printk(KERN_DEBUG "serio: kseriod exiting\n");
-
-       unlock_kernel();
-       complete_and_exit(&serio_exited, 0);
+       return 0;
 }
 
 
@@ -358,31 +410,39 @@ static int serio_thread(void *nothing)
  * Serio port operations
  */
 
-static ssize_t serio_show_description(struct device *dev, char *buf)
+static ssize_t serio_show_description(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%s\n", serio->name);
 }
 
-static ssize_t serio_show_id_type(struct device *dev, char *buf)
+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);
        return sprintf(buf, "%02x\n", serio->id.type);
 }
 
-static ssize_t serio_show_id_proto(struct device *dev, char *buf)
+static ssize_t serio_show_id_proto(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%02x\n", serio->id.proto);
 }
 
-static ssize_t serio_show_id_id(struct device *dev, char *buf)
+static ssize_t serio_show_id_id(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%02x\n", serio->id.id);
 }
 
-static ssize_t serio_show_id_extra(struct device *dev, char *buf)
+static ssize_t serio_show_id_extra(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%02x\n", serio->id.extra);
@@ -406,13 +466,13 @@ static struct attribute_group serio_id_attr_group = {
        .attrs  = serio_device_id_attrs,
 };
 
-static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t count)
+static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct serio *serio = to_serio_port(dev);
        struct device_driver *drv;
        int retval;
 
-       retval = down_interruptible(&serio_sem);
+       retval = mutex_lock_interruptible(&serio_mutex);
        if (retval)
                return retval;
 
@@ -432,18 +492,18 @@ static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t c
                retval = -EINVAL;
        }
 
-       up(&serio_sem);
+       mutex_unlock(&serio_mutex);
 
        return retval;
 }
 
-static ssize_t serio_show_bind_mode(struct device *dev, char *buf)
+static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct serio *serio = to_serio_port(dev);
        return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
 }
 
-static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t count)
+static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct serio *serio = to_serio_port(dev);
        int retval;
@@ -462,6 +522,7 @@ static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t c
 
 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
@@ -485,15 +546,20 @@ static void serio_init_port(struct serio *serio)
 
        __module_get(THIS_MODULE);
 
+       INIT_LIST_HEAD(&serio->node);
        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);
        serio->dev.bus = &serio_bus;
        serio->dev.release = serio_release_port;
-       if (serio->parent)
+       if (serio->parent) {
                serio->dev.parent = &serio->parent->dev;
+               serio->depth = serio->parent->depth + 1;
+       } else
+               serio->depth = 0;
+       lockdep_set_subclass(&serio->lock, serio->depth);
 }
 
 /*
@@ -502,6 +568,8 @@ static void serio_init_port(struct serio *serio)
  */
 static void serio_add_port(struct serio *serio)
 {
+       int error;
+
        if (serio->parent) {
                serio_pause_rx(serio->parent);
                serio->parent->child = serio;
@@ -511,9 +579,19 @@ static void serio_add_port(struct serio *serio)
        list_add_tail(&serio->node, &serio_list);
        if (serio->start)
                serio->start(serio);
-       device_add(&serio->dev);
-       sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group);
-       serio->registered = 1;
+       error = device_add(&serio->dev);
+       if (error)
+               printk(KERN_ERR
+                       "serio: device_add() failed for %s (%s), error: %d\n",
+                       serio->phys, serio->name, error);
+       else {
+               serio->registered = 1;
+               error = sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group);
+               if (error)
+                       printk(KERN_ERR
+                               "serio: sysfs_create_group() failed for %s (%s), error: %d\n",
+                               serio->phys, serio->name, error);
+       }
 }
 
 /*
@@ -543,10 +621,10 @@ static void serio_destroy_port(struct serio *serio)
        if (serio->registered) {
                sysfs_remove_group(&serio->dev.kobj, &serio_id_attr_group);
                device_del(&serio->dev);
-               list_del_init(&serio->node);
                serio->registered = 0;
        }
 
+       list_del_init(&serio->node);
        serio_remove_pending_events(serio);
        put_device(&serio->dev);
 }
@@ -557,7 +635,7 @@ static void serio_destroy_port(struct serio *serio)
 static void serio_reconnect_port(struct serio *serio)
 {
        do {
-               if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) {
+               if (serio_reconnect_driver(serio)) {
                        serio_disconnect_port(serio);
                        serio_find_driver(serio);
                        /* Ok, old children are now gone, we are done */
@@ -600,12 +678,12 @@ static void serio_disconnect_port(struct serio *serio)
 
 void serio_rescan(struct serio *serio)
 {
-       serio_queue_event(serio, NULL, SERIO_RESCAN);
+       serio_queue_event(serio, NULL, SERIO_RESCAN_PORT);
 }
 
 void serio_reconnect(struct serio *serio)
 {
-       serio_queue_event(serio, NULL, SERIO_RECONNECT);
+       serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
 }
 
 /*
@@ -623,20 +701,23 @@ 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);
 }
 
 /*
- * Submits register request to kseriod for subsequent execution.
- * Can be used when it is not obvious whether the serio_sem is
- * taken or not and when delayed execution is feasible.
+ * Safely unregisters child port if one is present.
  */
-void __serio_unregister_port_delayed(struct serio *serio, struct module *owner)
+void serio_unregister_child_port(struct serio *serio)
 {
-       serio_queue_event(serio, owner, SERIO_UNREGISTER_PORT);
+       mutex_lock(&serio_mutex);
+       if (serio->child) {
+               serio_disconnect_port(serio->child);
+               serio_destroy_port(serio->child);
+       }
+       mutex_unlock(&serio_mutex);
 }
 
 
@@ -686,32 +767,70 @@ static int serio_driver_probe(struct device *dev)
        struct serio *serio = to_serio_port(dev);
        struct serio_driver *drv = to_serio_driver(dev->driver);
 
-       return drv->connect(serio, drv);
+       return serio_connect_driver(serio, drv);
 }
 
 static int serio_driver_remove(struct device *dev)
 {
        struct serio *serio = to_serio_port(dev);
-       struct serio_driver *drv = to_serio_driver(dev->driver);
 
-       drv->disconnect(serio);
+       serio_disconnect_driver(serio);
        return 0;
 }
 
-void __serio_register_driver(struct serio_driver *drv, struct module *owner)
+static void serio_attach_driver(struct serio_driver *drv)
+{
+       int error;
+
+       error = driver_attach(&drv->driver);
+       if (error)
+               printk(KERN_WARNING
+                       "serio: driver_attach() failed for %s with error %d\n",
+                       drv->driver.name, error);
+}
+
+int serio_register_driver(struct serio_driver *drv)
 {
+       int manual_bind = drv->manual_bind;
+       int error;
+
        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);
+       /*
+        * Temporarily disable automatic binding because probing
+        * takes long time and we are better off doing it in kseriod
+        */
+       drv->manual_bind = 1;
+
+       error = driver_register(&drv->driver);
+       if (error) {
+               printk(KERN_ERR
+                       "serio: driver_register() failed for %s, error: %d\n",
+                       drv->driver.name, error);
+               return error;
+       }
+
+       /*
+        * Restore original bind mode and let kseriod bind the
+        * driver to free ports
+        */
+       if (!manual_bind) {
+               drv->manual_bind = 0;
+               error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
+               if (error) {
+                       driver_unregister(&drv->driver);
+                       return error;
+               }
+       }
+
+       return 0;
 }
 
 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:
@@ -725,16 +844,14 @@ 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)
 {
-       down(&serio->drv_sem);
        serio_pause_rx(serio);
        serio->drv = drv;
        serio_continue_rx(serio);
-       up(&serio->drv_sem);
 }
 
 static int serio_bus_match(struct device *dev, struct device_driver *drv)
@@ -750,40 +867,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;
 }
@@ -794,7 +912,7 @@ static int serio_resume(struct device *dev)
 {
        struct serio *serio = to_serio_port(dev);
 
-       if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) {
+       if (serio_reconnect_driver(serio)) {
                /*
                 * Driver re-probing can take a while, so better let kseriod
                 * deal with it.
@@ -805,7 +923,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);
@@ -817,7 +935,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)
@@ -827,7 +945,7 @@ void serio_close(struct serio *serio)
 }
 
 irqreturn_t serio_interrupt(struct serio *serio,
-               unsigned char data, unsigned int dfl, struct pt_regs *regs)
+               unsigned char data, unsigned int dfl)
 {
        unsigned long flags;
        irqreturn_t ret = IRQ_NONE;
@@ -835,7 +953,7 @@ irqreturn_t serio_interrupt(struct serio *serio,
        spin_lock_irqsave(&serio->lock, flags);
 
         if (likely(serio->drv)) {
-                ret = serio->drv->interrupt(serio, data, dfl, regs);
+                ret = serio->drv->interrupt(serio, data, dfl);
        } else if (!dfl && serio->registered) {
                serio_rescan(serio);
                ret = IRQ_HANDLED;
@@ -846,19 +964,34 @@ irqreturn_t serio_interrupt(struct serio *serio,
        return ret;
 }
 
+static struct bus_type serio_bus = {
+       .name           = "serio",
+       .dev_attrs      = serio_device_attrs,
+       .drv_attrs      = serio_driver_attrs,
+       .match          = serio_bus_match,
+       .uevent         = serio_uevent,
+       .probe          = serio_driver_probe,
+       .remove         = serio_driver_remove,
+       .resume         = serio_resume,
+};
+
 static int __init serio_init(void)
 {
-       if (!(serio_pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL))) {
-               printk(KERN_ERR "serio: Failed to start kseriod\n");
-               return -1;
+       int error;
+
+       error = bus_register(&serio_bus);
+       if (error) {
+               printk(KERN_ERR "serio: failed to register serio bus, error: %d\n", error);
+               return error;
        }
 
-       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.resume = serio_resume;
-       bus_register(&serio_bus);
+       serio_task = kthread_run(serio_thread, NULL, "kseriod");
+       if (IS_ERR(serio_task)) {
+               bus_unregister(&serio_bus);
+               error = PTR_ERR(serio_task);
+               printk(KERN_ERR "serio: Failed to start kseriod, error: %d\n", error);
+               return error;
+       }
 
        return 0;
 }
@@ -866,9 +999,8 @@ static int __init serio_init(void)
 static void __exit serio_exit(void)
 {
        bus_unregister(&serio_bus);
-       kill_proc(serio_pid, SIGTERM, 1);
-       wait_for_completion(&serio_exited);
+       kthread_stop(serio_task);
 }
 
-module_init(serio_init);
+subsys_initcall(serio_init);
 module_exit(serio_exit);