X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Finput%2Fserio%2Fserio.c;h=f0ce822c1028741efabecf0cef37e1e356754eb5;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=0beacb77ee18372585282a5aab58b04f06a615a9;hpb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;p=linux-2.6.git diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 0beacb77e..f0ce822c1 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -31,10 +31,11 @@ #include #include #include -#include #include -#include #include +#include +#include +#include MODULE_AUTHOR("Vojtech Pavlik "); 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);