diff options
Diffstat (limited to 'drivers/input/serio')
-rw-r--r-- | drivers/input/serio/i8042-x86ia64io.h | 7 | ||||
-rw-r--r-- | drivers/input/serio/i8042.c | 52 | ||||
-rw-r--r-- | drivers/input/serio/libps2.c | 9 | ||||
-rw-r--r-- | drivers/input/serio/serio.c | 170 | ||||
-rw-r--r-- | drivers/input/serio/serio_raw.c | 5 |
5 files changed, 144 insertions, 99 deletions
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 8738edda6610..d36bd5475b6d 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -111,6 +111,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, }, { + .ident = "Fujitsu Lifebook P7010", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), + }, + }, + { .ident = "Fujitsu Lifebook P5020D", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 09b06e605b50..debe9445488c 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -106,6 +106,7 @@ static unsigned char i8042_ctr; static unsigned char i8042_mux_present; static unsigned char i8042_kbd_irq_registered; static unsigned char i8042_aux_irq_registered; +static unsigned char i8042_suppress_kbd_ack; static struct platform_device *i8042_platform_device; static irqreturn_t i8042_interrupt(int irq, void *dev_id); @@ -254,25 +255,10 @@ static int i8042_kbd_write(struct serio *port, unsigned char c) static int i8042_aux_write(struct serio *serio, unsigned char c) { struct i8042_port *port = serio->port_data; - int retval; - -/* - * Send the byte out. - */ - - if (port->mux == -1) - retval = i8042_command(&c, I8042_CMD_AUX_SEND); - else - retval = i8042_command(&c, I8042_CMD_MUX_SEND + port->mux); -/* - * Make sure the interrupt happens and the character is received even - * in the case the IRQ isn't wired, so that we can receive further - * characters later. - */ - - i8042_interrupt(0, NULL); - return retval; + return i8042_command(&c, port->mux == -1 ? + I8042_CMD_AUX_SEND : + I8042_CMD_MUX_SEND + port->mux); } /* @@ -316,7 +302,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) unsigned char str, data; unsigned int dfl; unsigned int port_no; - int ret; + int ret = 1; spin_lock_irqsave(&i8042_lock, flags); str = i8042_read_status(); @@ -336,23 +322,27 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) dfl = 0; if (str & I8042_STR_MUXERR) { dbg("MUX error, status is %02x, data is %02x", str, data); - switch (data) { - default: /* * When MUXERR condition is signalled the data register can only contain * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately - * it is not always the case. Some KBC just get confused which port the - * data came from and signal error leaving the data intact. They _do not_ - * revert to legacy mode (actually I've never seen KBC reverting to legacy - * mode yet, when we see one we'll add proper handling). - * Anyway, we will assume that the data came from the same serio last byte + * it is not always the case. Some KBCs also report 0xfc when there is + * nothing connected to the port while others sometimes get confused which + * port the data came from and signal error leaving the data intact. They + * _do not_ revert to legacy mode (actually I've never seen KBC reverting + * to legacy mode yet, when we see one we'll add proper handling). + * Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the + * rest assume that the data came from the same serio last byte * was transmitted (if transmission happened not too long ago). */ + + switch (data) { + default: if (time_before(jiffies, last_transmit + HZ/10)) { str = last_str; break; } /* fall through - report timeout */ + case 0xfc: case 0xfd: case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break; case 0xff: dfl = SERIO_PARITY; data = 0xfe; break; @@ -378,10 +368,16 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_TIMEOUT ? ", timeout" : ""); + if (unlikely(i8042_suppress_kbd_ack)) + if (port_no == I8042_KBD_PORT_NO && + (data == 0xfa || data == 0xfe)) { + i8042_suppress_kbd_ack = 0; + goto out; + } + if (likely(port->exists)) serio_interrupt(port->serio, data, dfl); - ret = 1; out: return IRQ_RETVAL(ret); } @@ -842,11 +838,13 @@ static long i8042_panic_blink(long count) led ^= 0x01 | 0x04; while (i8042_read_status() & I8042_STR_IBF) DELAY; + i8042_suppress_kbd_ack = 1; i8042_write_data(0xed); /* set leds */ DELAY; while (i8042_read_status() & I8042_STR_IBF) DELAY; DELAY; + i8042_suppress_kbd_ack = 1; i8042_write_data(led); DELAY; last_blink = count; diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index dcb16b5cbec0..b3e84d3bb7f7 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -189,7 +189,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) return -1; } - mutex_lock_nested(&ps2dev->cmd_mutex, SINGLE_DEPTH_NESTING); + mutex_lock(&ps2dev->cmd_mutex); serio_pause_rx(ps2dev->serio); ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; @@ -251,9 +251,9 @@ EXPORT_SYMBOL(ps2_command); * ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.) */ -static void ps2_execute_scheduled_command(void *data) +static void ps2_execute_scheduled_command(struct work_struct *work) { - struct ps2work *ps2work = data; + struct ps2work *ps2work = container_of(work, struct ps2work, work); ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command); kfree(ps2work); @@ -278,7 +278,7 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman ps2work->ps2dev = ps2dev; ps2work->command = command; memcpy(ps2work->param, param, send); - INIT_WORK(&ps2work->work, ps2_execute_scheduled_command, ps2work); + INIT_WORK(&ps2work->work, ps2_execute_scheduled_command); if (!schedule_work(&ps2work->work)) { kfree(ps2work); @@ -296,6 +296,7 @@ EXPORT_SYMBOL(ps2_schedule_command); void ps2_init(struct ps2dev *ps2dev, struct serio *serio) { mutex_init(&ps2dev->cmd_mutex); + lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth); init_waitqueue_head(&ps2dev->wait); ps2dev->serio = serio; } diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 960fae3c3cea..f0ce822c1028 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -35,6 +35,7 @@ #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"); @@ -44,8 +45,7 @@ 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_register_driver); EXPORT_SYMBOL(serio_unregister_driver); EXPORT_SYMBOL(serio_open); EXPORT_SYMBOL(serio_close); @@ -62,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) { @@ -118,6 +117,8 @@ 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)) { @@ -126,9 +127,19 @@ static void serio_bind_driver(struct serio *serio, struct serio_driver *drv) serio->dev.driver = NULL; goto out; } - device_bind_driver(&serio->dev); + 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; + } } -out: + out: up_write(&serio_bus.subsys.rwsem); } @@ -158,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 { @@ -177,11 +187,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); @@ -200,24 +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); - 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) @@ -295,22 +316,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: @@ -538,8 +554,12 @@ static void serio_init_port(struct serio *serio) "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); } /* @@ -658,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); } /* @@ -700,16 +720,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 @@ -768,28 +778,52 @@ 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_add_driver(struct serio_driver *drv) +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) { + int manual_bind = drv->manual_bind; + int error; + drv->driver.bus = &serio_bus; - 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) @@ -930,15 +964,21 @@ 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) { 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); diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index ba2a2035d648..088ebc348ba3 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -297,7 +297,7 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv) serio_raw->dev.minor = PSMOUSE_MINOR; serio_raw->dev.name = serio_raw->name; - serio_raw->dev.dev = &serio->dev; + serio_raw->dev.parent = &serio->dev; serio_raw->dev.fops = &serio_raw_fops; err = misc_register(&serio_raw->dev); @@ -389,8 +389,7 @@ static struct serio_driver serio_raw_drv = { static int __init serio_raw_init(void) { - serio_register_driver(&serio_raw_drv); - return 0; + return serio_register_driver(&serio_raw_drv); } static void __exit serio_raw_exit(void) |