diff options
Diffstat (limited to 'drivers/input/mouse/psmouse-base.c')
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 316 |
1 files changed, 252 insertions, 64 deletions
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 4d5ecc04c5b..7665fd9ce55 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -54,10 +54,14 @@ static unsigned int psmouse_smartscroll = 1; module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); -static unsigned int psmouse_resetafter; +static unsigned int psmouse_resetafter = 5; module_param_named(resetafter, psmouse_resetafter, uint, 0644); MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); +static unsigned int psmouse_resync_time = 5; +module_param_named(resync_time, psmouse_resync_time, uint, 0644); +MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never)."); + PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO, NULL, psmouse_attr_show_protocol, psmouse_attr_set_protocol); @@ -70,12 +74,16 @@ PSMOUSE_DEFINE_ATTR(resolution, S_IWUSR | S_IRUGO, PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO, (void *) offsetof(struct psmouse, resetafter), psmouse_show_int_attr, psmouse_set_int_attr); +PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO, + (void *) offsetof(struct psmouse, resync_time), + psmouse_show_int_attr, psmouse_set_int_attr); static struct attribute *psmouse_attributes[] = { &psmouse_attr_protocol.dattr.attr, &psmouse_attr_rate.dattr.attr, &psmouse_attr_resolution.dattr.attr, &psmouse_attr_resetafter.dattr.attr, + &psmouse_attr_resync_time.dattr.attr, NULL }; @@ -98,6 +106,8 @@ __obsolete_setup("psmouse_rate="); */ static DECLARE_MUTEX(psmouse_sem); +static struct workqueue_struct *kpsmoused_wq; + struct psmouse_protocol { enum psmouse_type type; char *name; @@ -178,15 +188,79 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse, struct pt_reg } /* - * psmouse_interrupt() handles incoming characters, either gathering them into - * packets or passing them to the command routine as command output. + * __psmouse_set_state() sets new psmouse state and resets all flags. + */ + +static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) +{ + psmouse->state = new_state; + psmouse->pktcnt = psmouse->out_of_sync = 0; + psmouse->ps2dev.flags = 0; + psmouse->last = jiffies; +} + + +/* + * psmouse_set_state() sets new psmouse state and resets all flags and + * counters while holding serio lock so fighting with interrupt handler + * is not a concern. + */ + +static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) +{ + serio_pause_rx(psmouse->ps2dev.serio); + __psmouse_set_state(psmouse, new_state); + serio_continue_rx(psmouse->ps2dev.serio); +} + +/* + * psmouse_handle_byte() processes one byte of the input data stream + * by calling corresponding protocol handler. + */ + +static int psmouse_handle_byte(struct psmouse *psmouse, struct pt_regs *regs) +{ + psmouse_ret_t rc = psmouse->protocol_handler(psmouse, regs); + + switch (rc) { + case PSMOUSE_BAD_DATA: + if (psmouse->state == PSMOUSE_ACTIVATED) { + printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", + psmouse->name, psmouse->phys, psmouse->pktcnt); + if (++psmouse->out_of_sync == psmouse->resetafter) { + __psmouse_set_state(psmouse, PSMOUSE_IGNORE); + printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); + serio_reconnect(psmouse->ps2dev.serio); + return -1; + } + } + psmouse->pktcnt = 0; + break; + + case PSMOUSE_FULL_PACKET: + psmouse->pktcnt = 0; + if (psmouse->out_of_sync) { + psmouse->out_of_sync = 0; + printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", + psmouse->name, psmouse->phys); + } + break; + + case PSMOUSE_GOOD_DATA: + break; + } + return 0; +} + +/* + * psmouse_interrupt() handles incoming characters, either passing them + * for normal processing or gathering them as command response. */ static irqreturn_t psmouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs) { struct psmouse *psmouse = serio_get_drvdata(serio); - psmouse_ret_t rc; if (psmouse->state == PSMOUSE_IGNORE) goto out; @@ -208,67 +282,58 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, if (ps2_handle_response(&psmouse->ps2dev, data)) goto out; - if (psmouse->state == PSMOUSE_INITIALIZING) + if (psmouse->state <= PSMOUSE_RESYNCING) goto out; if (psmouse->state == PSMOUSE_ACTIVATED && psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { - printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", + printk(KERN_INFO "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", psmouse->name, psmouse->phys, psmouse->pktcnt); - psmouse->pktcnt = 0; + psmouse->badbyte = psmouse->packet[0]; + __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + queue_work(kpsmoused_wq, &psmouse->resync_work); + goto out; } - psmouse->last = jiffies; psmouse->packet[psmouse->pktcnt++] = data; - - if (psmouse->packet[0] == PSMOUSE_RET_BAT) { +/* + * Check if this is a new device announcement (0xAA 0x00) + */ + if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { if (psmouse->pktcnt == 1) goto out; - if (psmouse->pktcnt == 2) { - if (psmouse->packet[1] == PSMOUSE_RET_ID) { - psmouse->state = PSMOUSE_IGNORE; - serio_reconnect(serio); - goto out; - } - if (psmouse->type == PSMOUSE_SYNAPTICS) { - /* neither 0xAA nor 0x00 are valid first bytes - * for a packet in absolute mode - */ - psmouse->pktcnt = 0; - goto out; - } + if (psmouse->packet[1] == PSMOUSE_RET_ID) { + __psmouse_set_state(psmouse, PSMOUSE_IGNORE); + serio_reconnect(serio); + goto out; } - } - - rc = psmouse->protocol_handler(psmouse, regs); +/* + * Not a new device, try processing first byte normally + */ + psmouse->pktcnt = 1; + if (psmouse_handle_byte(psmouse, regs)) + goto out; - switch (rc) { - case PSMOUSE_BAD_DATA: - printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", - psmouse->name, psmouse->phys, psmouse->pktcnt); - psmouse->pktcnt = 0; + psmouse->packet[psmouse->pktcnt++] = data; + } - if (++psmouse->out_of_sync == psmouse->resetafter) { - psmouse->state = PSMOUSE_IGNORE; - printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); - serio_reconnect(psmouse->ps2dev.serio); - } - break; +/* + * See if we need to force resync because mouse was idle for too long + */ + if (psmouse->state == PSMOUSE_ACTIVATED && + psmouse->pktcnt == 1 && psmouse->resync_time && + time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { + psmouse->badbyte = psmouse->packet[0]; + __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + queue_work(kpsmoused_wq, &psmouse->resync_work); + goto out; + } - case PSMOUSE_FULL_PACKET: - psmouse->pktcnt = 0; - if (psmouse->out_of_sync) { - psmouse->out_of_sync = 0; - printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", - psmouse->name, psmouse->phys); - } - break; + psmouse->last = jiffies; + psmouse_handle_byte(psmouse, regs); - case PSMOUSE_GOOD_DATA: - break; - } -out: + out: return IRQ_HANDLED; } @@ -752,21 +817,6 @@ static void psmouse_initialize(struct psmouse *psmouse) } /* - * psmouse_set_state() sets new psmouse state and resets all flags and - * counters while holding serio lock so fighting with interrupt handler - * is not a concern. - */ - -static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) -{ - serio_pause_rx(psmouse->ps2dev.serio); - psmouse->state = new_state; - psmouse->pktcnt = psmouse->out_of_sync = 0; - psmouse->ps2dev.flags = 0; - serio_continue_rx(psmouse->ps2dev.serio); -} - -/* * psmouse_activate() enables the mouse so that we get motion reports from it. */ @@ -794,6 +844,111 @@ static void psmouse_deactivate(struct psmouse *psmouse) psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); } +/* + * psmouse_poll() - default poll hanlder. Everyone except for ALPS uses it. + */ + +static int psmouse_poll(struct psmouse *psmouse) +{ + return ps2_command(&psmouse->ps2dev, psmouse->packet, + PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); +} + + +/* + * psmouse_resync() attempts to re-validate current protocol. + */ + +static void psmouse_resync(void *p) +{ + struct psmouse *psmouse = p, *parent = NULL; + struct serio *serio = psmouse->ps2dev.serio; + psmouse_ret_t rc = PSMOUSE_GOOD_DATA; + int failed = 0, enabled = 0; + int i; + + down(&psmouse_sem); + + if (psmouse->state != PSMOUSE_RESYNCING) + goto out; + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } + +/* + * Some mice don't ACK commands sent while they are in the middle of + * transmitting motion packet. To avoid delay we use ps2_sendbyte() + * instead of ps2_command() which would wait for 200ms for an ACK + * that may never come. + * As an additional quirk ALPS touchpads may not only forget to ACK + * disable command but will stop reporting taps, so if we see that + * mouse at least once ACKs disable we will do full reconnect if ACK + * is missing. + */ + psmouse->num_resyncs++; + + if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { + if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command) + failed = 1; + } else + psmouse->acks_disable_command = 1; + +/* + * Poll the mouse. If it was reset the packet will be shorter than + * psmouse->pktsize and ps2_command will fail. We do not expect and + * do not handle scenario when mouse "upgrades" its protocol while + * disconnected since it would require additional delay. If we ever + * see a mouse that does it we'll adjust the code. + */ + if (!failed) { + if (psmouse->poll(psmouse)) + failed = 1; + else { + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + for (i = 0; i < psmouse->pktsize; i++) { + psmouse->pktcnt++; + rc = psmouse->protocol_handler(psmouse, NULL); + if (rc != PSMOUSE_GOOD_DATA) + break; + } + if (rc != PSMOUSE_FULL_PACKET) + failed = 1; + psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + } + } +/* + * Now try to enable mouse. We try to do that even if poll failed and also + * repeat our attempts 5 times, otherwise we may be left out with disabled + * mouse. + */ + for (i = 0; i < 5; i++) { + if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { + enabled = 1; + break; + } + msleep(200); + } + + if (!enabled) { + printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n", + psmouse->ps2dev.serio->phys); + failed = 1; + } + + if (failed) { + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + printk(KERN_INFO "psmouse.c: resync failed, issuing reconnect request\n"); + serio_reconnect(serio); + } else + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + + if (parent) + psmouse_activate(parent); + out: + up(&psmouse_sem); +} /* * psmouse_cleanup() resets the mouse into power-on state. @@ -822,6 +977,11 @@ static void psmouse_disconnect(struct serio *serio) psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + /* make sure we don't have a resync in progress */ + up(&psmouse_sem); + flush_workqueue(kpsmoused_wq); + down(&psmouse_sem); + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); @@ -859,6 +1019,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_proto psmouse->set_rate = psmouse_set_rate; psmouse->set_resolution = psmouse_set_resolution; + psmouse->poll = psmouse_poll; psmouse->protocol_handler = psmouse_process_byte; psmouse->pktsize = 3; @@ -874,6 +1035,23 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_proto else psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); + /* + * If mouse's packet size is 3 there is no point in polling the + * device in hopes to detect protocol reset - we won't get less + * than 3 bytes response anyhow. + */ + if (psmouse->pktsize == 3) + psmouse->resync_time = 0; + + /* + * Some smart KVMs fake response to POLL command returning just + * 3 bytes and messing up our resync logic, so if initial poll + * fails we won't try polling the device anymore. Hopefully + * such KVM will maintain initially selected protocol. + */ + if (psmouse->resync_time && psmouse->poll(psmouse)) + psmouse->resync_time = 0; + sprintf(psmouse->devname, "%s %s %s", psmouse_protocol_by_type(psmouse->type)->name, psmouse->vendor, psmouse->name); @@ -914,6 +1092,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) goto out; ps2_init(&psmouse->ps2dev, serio); + INIT_WORK(&psmouse->resync_work, psmouse_resync, psmouse); psmouse->dev = input_dev; sprintf(psmouse->phys, "%s/input0", serio->phys); @@ -934,6 +1113,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) psmouse->rate = psmouse_rate; psmouse->resolution = psmouse_resolution; psmouse->resetafter = psmouse_resetafter; + psmouse->resync_time = parent ? 0 : psmouse_resync_time; psmouse->smartscroll = psmouse_smartscroll; psmouse_switch_protocol(psmouse, NULL); @@ -1278,13 +1458,21 @@ static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) static int __init psmouse_init(void) { + kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); + if (!kpsmoused_wq) { + printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n"); + return -ENOMEM; + } + serio_register_driver(&psmouse_drv); + return 0; } static void __exit psmouse_exit(void) { serio_unregister_driver(&psmouse_drv); + destroy_workqueue(kpsmoused_wq); } module_init(psmouse_init); |