X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Finput%2Fserio%2Fserio.c;h=372ca493119483006acb3deaae1cbbc7e7b0d920;hb=831441862956fffa17b9801db37e6ea1650b0f69;hp=2f76813c3a645c7d3d6a9e797df4525f19c6ac3d;hpb=30226f8199cb7f5ace767f65bcebb85941612dfc;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 2f76813..372ca49 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Serio abstraction core"); @@ -43,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); @@ -52,27 +53,27 @@ 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; 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; - 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; } @@ -81,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) @@ -114,34 +115,42 @@ 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) { - down_write(&serio_bus.subsys.rwsem); + int error; 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; } - device_bind_driver(&serio->dev); - } -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); + 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; + return error; + } + } + return 0; } static void serio_find_driver(struct serio *serio) { - down_write(&serio_bus.subsys.rwsem); - device_attach(&serio->dev); - up_write(&serio_bus.subsys.rwsem); + int error; + + 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); } @@ -150,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 { @@ -169,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); @@ -192,23 +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); - 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) @@ -270,9 +290,8 @@ static struct serio_event *serio_get_event(void) static void serio_handle_event(void) { struct serio_event *event; - struct serio_driver *serio_drv; - down(&serio_sem); + mutex_lock(&serio_mutex); /* * Note that we handle only one event here to give swsusp @@ -287,23 +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_drv = event->object; - driver_register(&serio_drv->driver); + case SERIO_ATTACH_DRIVER: + serio_attach_driver(event->object); break; default: @@ -314,7 +327,7 @@ static void serio_handle_event(void) serio_free_event(event); } - up(&serio_sem); + mutex_unlock(&serio_mutex); } /* @@ -371,6 +384,7 @@ static struct serio *serio_get_pending_child(struct serio *parent) static int serio_thread(void *nothing) { + set_freezable(); do { serio_handle_event(); wait_event_interruptible(serio_wait, @@ -447,13 +461,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 = down_interruptible(&serio_sem); - 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)) { @@ -463,15 +476,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; } - up(&serio_sem); + 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) @@ -523,15 +536,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); } /* @@ -540,6 +558,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; @@ -549,9 +569,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); + } } /* @@ -581,10 +611,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); } @@ -625,7 +655,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); } @@ -633,17 +663,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); } /* @@ -661,10 +691,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); } /* @@ -672,22 +702,12 @@ 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); -} - -/* - * 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. - */ -void __serio_unregister_port_delayed(struct serio *serio, struct module *owner) -{ - serio_queue_event(serio, owner, SERIO_UNREGISTER_PORT); + mutex_unlock(&serio_mutex); } @@ -748,24 +768,76 @@ 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) +{ + mutex_lock(&serio->drv_mutex); + if (serio->drv && serio->drv->cleanup) + serio->drv->cleanup(serio); + mutex_unlock(&serio->drv_mutex); +} + +static void serio_shutdown(struct device *dev) +{ + struct serio *serio = to_serio_port(dev); + + serio_cleanup(serio); +} -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, 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) { struct serio *serio; - down(&serio_sem); + mutex_lock(&serio_mutex); drv->manual_bind = 1; /* so serio_find_driver ignores it */ start_over: @@ -779,7 +851,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) @@ -843,11 +915,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. @@ -855,10 +941,13 @@ 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_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); @@ -870,7 +959,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) @@ -880,7 +969,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; @@ -888,7 +977,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; @@ -899,21 +988,39 @@ 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; + + error = bus_register(&serio_bus); + if (error) { + printk(KERN_ERR "serio: failed to register serio bus, error: %d\n", error); + return error; + } + serio_task = kthread_run(serio_thread, NULL, "kseriod"); if (IS_ERR(serio_task)) { - printk(KERN_ERR "serio: Failed to start kseriod\n"); - return PTR_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; } - 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; - bus_register(&serio_bus); - return 0; } @@ -923,5 +1030,5 @@ static void __exit serio_exit(void) kthread_stop(serio_task); } -module_init(serio_init); +subsys_initcall(serio_init); module_exit(serio_exit);