]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/input/serio/serio.c
Input: serio/gameport - use 'long' system workqueue
[mv-sheeva.git] / drivers / input / serio / serio.c
index c3b626e9eae7c075cd7597779ddbef4becb731f6..ba70058e2be3ae2bc6a3289c08d541f336a4aad8 100644 (file)
 #include <linux/module.h>
 #include <linux/serio.h>
 #include <linux/errno.h>
-#include <linux/wait.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
 #include <linux/mutex.h>
-#include <linux/freezer.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Serio abstraction core");
@@ -45,7 +43,7 @@ MODULE_LICENSE("GPL");
 
 /*
  * serio_mutex protects entire serio subsystem and is taken every time
- * serio port or driver registrered or unregistered.
+ * serio port or driver registered or unregistered.
  */
 static DEFINE_MUTEX(serio_mutex);
 
@@ -56,7 +54,7 @@ static struct bus_type serio_bus;
 static void serio_add_port(struct serio *serio);
 static int serio_reconnect_port(struct serio *serio);
 static void serio_disconnect_port(struct serio *serio);
-static void serio_reconnect_chain(struct serio *serio);
+static void serio_reconnect_subtree(struct serio *serio);
 static void serio_attach_driver(struct serio_driver *drv);
 
 static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
@@ -152,7 +150,7 @@ static void serio_find_driver(struct serio *serio)
 enum serio_event_type {
        SERIO_RESCAN_PORT,
        SERIO_RECONNECT_PORT,
-       SERIO_RECONNECT_CHAIN,
+       SERIO_RECONNECT_SUBTREE,
        SERIO_REGISTER_PORT,
        SERIO_ATTACH_DRIVER,
 };
@@ -166,58 +164,22 @@ 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 struct task_struct *serio_task;
 
-static int serio_queue_event(void *object, struct module *owner,
-                            enum serio_event_type event_type)
+static struct serio_event *serio_get_event(void)
 {
+       struct serio_event *event = NULL;
        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,
-        * 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)
-                               goto out;
-                       break;
-               }
-       }
-
-       event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
-       if (!event) {
-               pr_err("Not enough memory to queue event %d\n", event_type);
-               retval = -ENOMEM;
-               goto out;
-       }
-
-       if (!try_module_get(owner)) {
-               pr_warning("Can't get module reference, dropping event %d\n",
-                          event_type);
-               kfree(event);
-               retval = -EINVAL;
-               goto out;
+       if (!list_empty(&serio_event_list)) {
+               event = list_first_entry(&serio_event_list,
+                                        struct serio_event, node);
+               list_del_init(&event->node);
        }
 
-       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;
+       return event;
 }
 
 static void serio_free_event(struct serio_event *event)
@@ -226,7 +188,8 @@ static void serio_free_event(struct serio_event *event)
        kfree(event);
 }
 
-static void serio_remove_duplicate_events(struct serio_event *event)
+static void serio_remove_duplicate_events(void *object,
+                                         enum serio_event_type type)
 {
        struct serio_event *e, *next;
        unsigned long flags;
@@ -234,13 +197,13 @@ static void serio_remove_duplicate_events(struct serio_event *event)
        spin_lock_irqsave(&serio_event_lock, flags);
 
        list_for_each_entry_safe(e, next, &serio_event_list, node) {
-               if (event->object == e->object) {
+               if (object == e->object) {
                        /*
                         * If this event is of different type we should not
                         * look further - we only suppress duplicate events
                         * that were sent back-to-back.
                         */
-                       if (event->type != e->type)
+                       if (type != e->type)
                                break;
 
                        list_del_init(&e->node);
@@ -251,25 +214,7 @@ static void serio_remove_duplicate_events(struct serio_event *event)
        spin_unlock_irqrestore(&serio_event_lock, flags);
 }
 
-
-static struct serio_event *serio_get_event(void)
-{
-       struct serio_event *event = NULL;
-       unsigned long flags;
-
-       spin_lock_irqsave(&serio_event_lock, flags);
-
-       if (!list_empty(&serio_event_list)) {
-               event = list_first_entry(&serio_event_list,
-                                        struct serio_event, node);
-               list_del_init(&event->node);
-       }
-
-       spin_unlock_irqrestore(&serio_event_lock, flags);
-       return event;
-}
-
-static void serio_handle_event(void)
+static void serio_handle_event(struct work_struct *work)
 {
        struct serio_event *event;
 
@@ -292,8 +237,8 @@ static void serio_handle_event(void)
                        serio_find_driver(event->object);
                        break;
 
-               case SERIO_RECONNECT_CHAIN:
-                       serio_reconnect_chain(event->object);
+               case SERIO_RECONNECT_SUBTREE:
+                       serio_reconnect_subtree(event->object);
                        break;
 
                case SERIO_ATTACH_DRIVER:
@@ -301,13 +246,66 @@ static void serio_handle_event(void)
                        break;
                }
 
-               serio_remove_duplicate_events(event);
+               serio_remove_duplicate_events(event->object, event->type);
                serio_free_event(event);
        }
 
        mutex_unlock(&serio_mutex);
 }
 
+static DECLARE_WORK(serio_event_work, serio_handle_event);
+
+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,
+        * 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)
+                               goto out;
+                       break;
+               }
+       }
+
+       event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
+       if (!event) {
+               pr_err("Not enough memory to queue event %d\n", event_type);
+               retval = -ENOMEM;
+               goto out;
+       }
+
+       if (!try_module_get(owner)) {
+               pr_warning("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);
+       queue_work(system_long_wq, &serio_event_work);
+
+out:
+       spin_unlock_irqrestore(&serio_event_lock, flags);
+       return retval;
+}
+
 /*
  * Remove all events that have been submitted for a given
  * object, be it serio port or driver.
@@ -330,12 +328,10 @@ static void serio_remove_pending_events(void *object)
 }
 
 /*
- * Destroy child serio port (if any) that has not been fully registered yet.
+ * Locate child serio port (if any) that has not been fully registered yet.
  *
- * Note that we rely on the fact that port can have only one child and therefore
- * only one child registration request can be pending. Additionally, children
- * are registered by driver's connect() handler so there can't be a grandchild
- * pending registration together with a child.
+ * Children are registered by driver's connect() handler so there can't be a
+ * grandchild pending registration together with a child.
  */
 static struct serio *serio_get_pending_child(struct serio *parent)
 {
@@ -359,18 +355,6 @@ static struct serio *serio_get_pending_child(struct serio *parent)
        return child;
 }
 
-static int serio_thread(void *nothing)
-{
-       do {
-               serio_handle_event();
-               wait_event_interruptible(serio_wait,
-                       kthread_should_stop() || !list_empty(&serio_event_list));
-       } while (!kthread_should_stop());
-
-       return 0;
-}
-
-
 /*
  * Serio port operations
  */
@@ -449,14 +433,16 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
        if (!strncmp(buf, "none", count)) {
                serio_disconnect_port(serio);
        } else if (!strncmp(buf, "reconnect", count)) {
-               serio_reconnect_chain(serio);
+               serio_reconnect_subtree(serio);
        } else if (!strncmp(buf, "rescan", count)) {
                serio_disconnect_port(serio);
                serio_find_driver(serio);
+               serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
        } else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
                serio_disconnect_port(serio);
                error = serio_bind_driver(serio, to_serio_driver(drv));
                put_driver(drv);
+               serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
        } else {
                error = -EINVAL;
        }
@@ -516,6 +502,8 @@ static void serio_init_port(struct serio *serio)
        __module_get(THIS_MODULE);
 
        INIT_LIST_HEAD(&serio->node);
+       INIT_LIST_HEAD(&serio->child_node);
+       INIT_LIST_HEAD(&serio->children);
        spin_lock_init(&serio->lock);
        mutex_init(&serio->drv_mutex);
        device_initialize(&serio->dev);
@@ -538,12 +526,13 @@ static void serio_init_port(struct serio *serio)
  */
 static void serio_add_port(struct serio *serio)
 {
+       struct serio *parent = serio->parent;
        int error;
 
-       if (serio->parent) {
-               serio_pause_rx(serio->parent);
-               serio->parent->child = serio;
-               serio_continue_rx(serio->parent);
+       if (parent) {
+               serio_pause_rx(parent);
+               list_add_tail(&serio->child_node, &parent->children);
+               serio_continue_rx(parent);
        }
 
        list_add_tail(&serio->node, &serio_list);
@@ -559,15 +548,14 @@ static void serio_add_port(struct serio *serio)
 }
 
 /*
- * serio_destroy_port() completes deregistration process and removes
+ * serio_destroy_port() completes unregistration process and removes
  * port from the system
  */
 static void serio_destroy_port(struct serio *serio)
 {
        struct serio *child;
 
-       child = serio_get_pending_child(serio);
-       if (child) {
+       while ((child = serio_get_pending_child(serio)) != NULL) {
                serio_remove_pending_events(child);
                put_device(&child->dev);
        }
@@ -577,7 +565,7 @@ static void serio_destroy_port(struct serio *serio)
 
        if (serio->parent) {
                serio_pause_rx(serio->parent);
-               serio->parent->child = NULL;
+               list_del_init(&serio->child_node);
                serio_continue_rx(serio->parent);
                serio->parent = NULL;
        }
@@ -609,46 +597,82 @@ static int serio_reconnect_port(struct serio *serio)
 }
 
 /*
- * Reconnect serio port and all its children (re-initialize attached devices)
+ * Reconnect serio port and all its children (re-initialize attached
+ * devices).
  */
-static void serio_reconnect_chain(struct serio *serio)
+static void serio_reconnect_subtree(struct serio *root)
 {
+       struct serio *s = root;
+       int error;
+
        do {
-               if (serio_reconnect_port(serio)) {
-                       /* Ok, old children are now gone, we are done */
-                       break;
+               error = serio_reconnect_port(s);
+               if (!error) {
+                       /*
+                        * Reconnect was successful, move on to do the
+                        * first child.
+                        */
+                       if (!list_empty(&s->children)) {
+                               s = list_first_entry(&s->children,
+                                                    struct serio, child_node);
+                               continue;
+                       }
+               }
+
+               /*
+                * Either it was a leaf node or reconnect failed and it
+                * became a leaf node. Continue reconnecting starting with
+                * the next sibling of the parent node.
+                */
+               while (s != root) {
+                       struct serio *parent = s->parent;
+
+                       if (!list_is_last(&s->child_node, &parent->children)) {
+                               s = list_entry(s->child_node.next,
+                                              struct serio, child_node);
+                               break;
+                       }
+
+                       s = parent;
                }
-               serio = serio->child;
-       } while (serio);
+       } while (s != root);
 }
 
 /*
  * serio_disconnect_port() unbinds a port from its driver. As a side effect
- * all child ports are unbound and destroyed.
+ * all children ports are unbound and destroyed.
  */
 static void serio_disconnect_port(struct serio *serio)
 {
-       struct serio *s, *parent;
+       struct serio *s = serio;
+
+       /*
+        * Children ports should be disconnected and destroyed
+        * first; we travel the tree in depth-first order.
+        */
+       while (!list_empty(&serio->children)) {
+
+               /* Locate a leaf */
+               while (!list_empty(&s->children))
+                       s = list_first_entry(&s->children,
+                                            struct serio, child_node);
 
-       if (serio->child) {
                /*
-                * Children ports should be disconnected and destroyed
-                * first, staring with the leaf one, since we don't want
-                * to do recursion
+                * Prune this leaf node unless it is the one we
+                * started with.
                 */
-               for (s = serio; s->child; s = s->child)
-                       /* empty */;
-
-               do {
-                       parent = s->parent;
+               if (s != serio) {
+                       struct serio *parent = s->parent;
 
                        device_release_driver(&s->dev);
                        serio_destroy_port(s);
-               } while ((s = parent) != serio);
+
+                       s = parent;
+               }
        }
 
        /*
-        * Ok, no children left, now disconnect this port
+        * OK, no children left, now disconnect this port.
         */
        device_release_driver(&serio->dev);
 }
@@ -661,7 +685,7 @@ EXPORT_SYMBOL(serio_rescan);
 
 void serio_reconnect(struct serio *serio)
 {
-       serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN);
+       serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
 }
 EXPORT_SYMBOL(serio_reconnect);
 
@@ -689,14 +713,16 @@ void serio_unregister_port(struct serio *serio)
 EXPORT_SYMBOL(serio_unregister_port);
 
 /*
- * Safely unregisters child port if one is present.
+ * Safely unregisters children ports if they are present.
  */
 void serio_unregister_child_port(struct serio *serio)
 {
+       struct serio *s, *next;
+
        mutex_lock(&serio_mutex);
-       if (serio->child) {
-               serio_disconnect_port(serio->child);
-               serio_destroy_port(serio->child);
+       list_for_each_entry_safe(s, next, &serio->children, child_node) {
+               serio_disconnect_port(s);
+               serio_destroy_port(s);
        }
        mutex_unlock(&serio_mutex);
 }
@@ -1003,21 +1029,18 @@ static int __init serio_init(void)
                return error;
        }
 
-       serio_task = kthread_run(serio_thread, NULL, "kseriod");
-       if (IS_ERR(serio_task)) {
-               bus_unregister(&serio_bus);
-               error = PTR_ERR(serio_task);
-               pr_err("Failed to start kseriod, error: %d\n", error);
-               return error;
-       }
-
        return 0;
 }
 
 static void __exit serio_exit(void)
 {
        bus_unregister(&serio_bus);
-       kthread_stop(serio_task);
+
+       /*
+        * There should not be any outstanding events but work may
+        * still be scheduled so simply cancel it.
+        */
+       cancel_work_sync(&serio_event_work);
 }
 
 subsys_initcall(serio_init);