Merge branch 'drm-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied...
[powerpc.git] / drivers / input / serio / serio.c
index 5f1d403..5895202 100644 (file)
@@ -45,7 +45,6 @@ EXPORT_SYMBOL(serio_interrupt);
 EXPORT_SYMBOL(__serio_register_port);
 EXPORT_SYMBOL(serio_unregister_port);
 EXPORT_SYMBOL(serio_unregister_child_port);
-EXPORT_SYMBOL(__serio_unregister_port_delayed);
 EXPORT_SYMBOL(__serio_register_driver);
 EXPORT_SYMBOL(serio_unregister_driver);
 EXPORT_SYMBOL(serio_open);
@@ -63,11 +62,10 @@ static LIST_HEAD(serio_list);
 
 static struct bus_type serio_bus;
 
-static void serio_add_driver(struct serio_driver *drv);
 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)
 {
@@ -117,18 +115,18 @@ static int serio_match_port(const struct serio_device_id *ids, struct serio *ser
  * Basic serio -> driver core mappings
  */
 
-static void serio_bind_driver(struct serio *serio, struct serio_driver *drv)
+static int 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 (serio_connect_driver(serio, drv)) {
                        serio->dev.driver = NULL;
-                       goto out;
+                       return -ENODEV;
                }
+
                error = device_bind_driver(&serio->dev);
                if (error) {
                        printk(KERN_WARNING
@@ -138,31 +136,21 @@ static void serio_bind_driver(struct serio *serio, struct serio_driver *drv)
                                drv->description, error);
                        serio_disconnect_driver(serio);
                        serio->dev.driver = NULL;
-                       goto out;
+                       return error;
                }
        }
- out:
-       up_write(&serio_bus.subsys.rwsem);
-}
-
-static void serio_release_driver(struct serio *serio)
-{
-       down_write(&serio_bus.subsys.rwsem);
-       device_release_driver(&serio->dev);
-       up_write(&serio_bus.subsys.rwsem);
+       return 0;
 }
 
 static void serio_find_driver(struct serio *serio)
 {
        int error;
 
-       down_write(&serio_bus.subsys.rwsem);
        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);
 }
 
 
@@ -171,11 +159,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 {
@@ -190,11 +177,12 @@ static LIST_HEAD(serio_event_list);
 static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
 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);
 
@@ -213,24 +201,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);
-                       kfree(event);
-                       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)
@@ -308,22 +306,17 @@ static void serio_handle_event(void)
                                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_add_driver(event->object);
+                       case SERIO_ATTACH_DRIVER:
+                               serio_attach_driver(event->object);
                                break;
 
                        default:
@@ -467,13 +460,12 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
 {
        struct serio *serio = to_serio_port(dev);
        struct device_driver *drv;
-       int retval;
+       int error;
 
-       retval = mutex_lock_interruptible(&serio_mutex);
-       if (retval)
-               return retval;
+       error = mutex_lock_interruptible(&serio_mutex);
+       if (error)
+               return error;
 
-       retval = count;
        if (!strncmp(buf, "none", count)) {
                serio_disconnect_port(serio);
        } else if (!strncmp(buf, "reconnect", count)) {
@@ -483,15 +475,15 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
                serio_find_driver(serio);
        } else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
                serio_disconnect_port(serio);
-               serio_bind_driver(serio, to_serio_driver(drv));
+               error = serio_bind_driver(serio, to_serio_driver(drv));
                put_driver(drv);
        } else {
-               retval = -EINVAL;
+               error = -EINVAL;
        }
 
        mutex_unlock(&serio_mutex);
 
-       return retval;
+       return error ? error : count;
 }
 
 static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf)
@@ -662,7 +654,7 @@ static void serio_disconnect_port(struct serio *serio)
                do {
                        parent = s->parent;
 
-                       serio_release_driver(s);
+                       device_release_driver(&s->dev);
                        serio_destroy_port(s);
                } while ((s = parent) != serio);
        }
@@ -670,17 +662,17 @@ static void serio_disconnect_port(struct serio *serio)
        /*
         * Ok, no children left, now disconnect this port
         */
-       serio_release_driver(serio);
+       device_release_driver(&serio->dev);
 }
 
 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);
 }
 
 /*
@@ -717,16 +709,6 @@ void serio_unregister_child_port(struct serio *serio)
        mutex_unlock(&serio_mutex);
 }
 
-/*
- * Submits register request to kseriod for subsequent execution.
- * 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)
-{
-       serio_queue_event(serio, owner, SERIO_UNREGISTER_PORT);
-}
-
 
 /*
  * Serio driver operations
@@ -785,28 +767,67 @@ 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,
-};
+static void serio_cleanup(struct serio *serio)
+{
+       if (serio->drv && serio->drv->cleanup)
+               serio->drv->cleanup(serio);
+}
 
-static void serio_add_driver(struct serio_driver *drv)
+static void serio_shutdown(struct device *dev)
+{
+       struct serio *serio = to_serio_port(dev);
+
+       serio_cleanup(serio);
+}
+
+static void serio_attach_driver(struct serio_driver *drv)
 {
        int error;
 
-       error = driver_register(&drv->driver);
+       error = driver_attach(&drv->driver);
        if (error)
-               printk(KERN_ERR
-                       "serio: driver_register() failed for %s, error: %d\n",
+               printk(KERN_WARNING
+                       "serio: driver_attach() failed for %s with error %d\n",
                        drv->driver.name, error);
 }
 
-void __serio_register_driver(struct serio_driver *drv, struct module *owner)
+int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
 {
+       int manual_bind = drv->manual_bind;
+       int error;
+
        drv->driver.bus = &serio_bus;
+       drv->driver.owner = owner;
+       drv->driver.mod_name = mod_name;
+
+       /*
+        * 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;
+               }
+       }
 
-       serio_queue_event(drv, owner, SERIO_REGISTER_DRIVER);
+       return 0;
 }
 
 void serio_unregister_driver(struct serio_driver *drv)
@@ -891,11 +912,25 @@ static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buf
 
 #endif /* CONFIG_HOTPLUG */
 
+#ifdef CONFIG_PM
+static int serio_suspend(struct device *dev, pm_message_t state)
+{
+       if (dev->power.power_state.event != state.event) {
+               if (state.event == PM_EVENT_SUSPEND)
+                       serio_cleanup(to_serio_port(dev));
+
+               dev->power.power_state = state;
+       }
+
+       return 0;
+}
+
 static int serio_resume(struct device *dev)
 {
        struct serio *serio = to_serio_port(dev);
 
-       if (serio_reconnect_driver(serio)) {
+       if (dev->power.power_state.event != PM_EVENT_ON &&
+           serio_reconnect_driver(serio)) {
                /*
                 * Driver re-probing can take a while, so better let kseriod
                 * deal with it.
@@ -903,8 +938,11 @@ static int serio_resume(struct device *dev)
                serio_rescan(serio);
        }
 
+       dev->power.power_state = PMSG_ON;
+
        return 0;
 }
+#endif /* CONFIG_PM */
 
 /* called from serio_driver->connect/disconnect methods under serio_mutex */
 int serio_open(struct serio *serio, struct serio_driver *drv)
@@ -947,15 +985,25 @@ 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,
+       .shutdown       = serio_shutdown,
+#ifdef CONFIG_PM
+       .suspend        = serio_suspend,
+       .resume         = serio_resume,
+#endif
+};
+
 static int __init serio_init(void)
 {
        int error;
 
-       serio_bus.dev_attrs = serio_device_attrs;
-       serio_bus.drv_attrs = serio_driver_attrs;
-       serio_bus.match = serio_bus_match;
-       serio_bus.uevent = serio_uevent;
-       serio_bus.resume = serio_resume;
        error = bus_register(&serio_bus);
        if (error) {
                printk(KERN_ERR "serio: failed to register serio bus, error: %d\n", error);