Make the worst symptoms of a failing mouse go away. Drop packets that fail basic sniff tests and packets reported at excessive rates. Drop packets reporting unrepresentable speeds. Attempt to recover synchronization in case of a dropped input byte. Log summaries of actions taken. Allow logging of rejected or all packets. The solution to failing hardware is to replace the hardware, but is it better to have to do it on the hardware's schedule not mine? All behaviors optional, all but the last default, all controlled by bitflags in /sys. It's a straight filter on incoming completed packets, see the code for dropout recovery which works well for the mouse that got me to do this. To effectively disable the patch write 0 to /sys/module/psmouse/parameters/filter. My mouse failed while in XP, but I didn't know it - it seemed it'd need cleaning soon. On booting into Linux, it was dangerous. The difference was consistent, making it seem the problem wasn't the mouse. Tested on AMD64, full coverage - syslog is a good argument for replacing this mouse soon. No one else has tried this yet. Signed-off-by: Jim Hill <gjthill@xxxxxxxxx> --- Hi, this is my first patch, I hope I got the procedures right, apologies in advance if I goofed something. Happy New Year and all, Jim --- drivers/input/mouse/psmouse-base.c | 122 ++++++++++++++++++++++++++++++++++++ drivers/input/mouse/psmouse.h | 8 +++ 2 files changed, 130 insertions(+), 0 deletions(-) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index cd9d0c9..3664c68 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -69,6 +69,22 @@ static unsigned int psmouse_resync_time; 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)."); +enum { + DROP_BAD = 1, + SUMMARIZE_ERRORS = 2, + LOG_REJECTS = 4, + LOG_ALL = 8, + ATTEMPT_RECOVERY = 16, + DROP_CLAMPED = 32, + DROP_PACKET = DROP_CLAMPED | DROP_BAD +}; +static unsigned int psmouse_filter = DROP_BAD | SUMMARIZE_ERRORS | + ATTEMPT_RECOVERY | DROP_CLAMPED; +module_param_named(filter, psmouse_filter, uint, 0644); +MODULE_PARM_DESC(filter, "1 = drop invalid or hotio packets, +2=summary-log, " + "+4=log-rejected, +8=log-all, +16=attempt-recovery, " + "+32=drop-clamped."); + PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO, NULL, psmouse_attr_show_protocol, psmouse_attr_set_protocol); @@ -119,6 +135,99 @@ struct psmouse_protocol { int (*init)(struct psmouse *); }; +static int psmouse_filter_packet(struct psmouse *m) +{ + int todo = 0; + + int xoflow = m->packet[0]>>6 & 1; + int yoflow = m->packet[0]>>7 & 1; + + if ((m->packet[0] & 0x08) != 0x08) + todo |= DROP_BAD + ATTEMPT_RECOVERY; + else if ((xoflow | yoflow) && psmouse_filter & DROP_CLAMPED) + todo |= DROP_CLAMPED; + + if (todo & DROP_PACKET) { + todo |= LOG_REJECTS; + if (m->err_log_counter == 0) + m->err_log_base = m->last; + ++m->err_log_counter; + } + + if (time_after(m->last, m->interval_base + HZ/m->rate)) { + m->interval_pkts = 0; + m->interval_base = m->last; + } + if (m->interval_pkts > m->rate/HZ + 1) { + if (m->hotio_log_counter == 0) + m->hotio_log_base = m->last; + ++m->hotio_log_counter; + todo |= DROP_BAD; + } + ++m->interval_pkts; + + if ((todo & psmouse_filter & LOG_REJECTS) | + (psmouse_filter & LOG_ALL)) { + unsigned long long packet = 0; + int p; + for (p = 0; p < m->pktcnt; ++p) + packet = packet<<8 | m->packet[p]; + printk(KERN_INFO "psmouse.c: packet %0*llx%s\n", p*2, packet, + todo & DROP_BAD ? " rejected" : ""); + } + + if (m->err_log_counter && time_after(m->last, m->err_log_base + HZ) && + psmouse_filter & (SUMMARIZE_ERRORS | LOG_ALL)) { + if (m->err_log_counter && psmouse_filter & + (SUMMARIZE_ERRORS | LOG_ALL)) { + printk(KERN_WARNING "psmouse.c: %s at %s " + "%lu rejected packets%s\n", + m->name, m->phys, m->err_log_counter, + psmouse_filter & ATTEMPT_RECOVERY ? + " recovery attempted" : + psmouse_filter & DROP_BAD ? + " ignored" : + ""); + } + m->err_log_counter = 0; + m->err_log_base = 0; + } + + if (m->hotio_log_counter && time_after(m->last, m->hotio_log_base + HZ) + && psmouse_filter & (SUMMARIZE_ERRORS | LOG_ALL)) { + printk(KERN_WARNING "psmouse.c: %s at %s " + "%lu excess packets%s\n", + m->name, m->phys, m->hotio_log_counter, + psmouse_filter & DROP_BAD ? + " ignored" : ""); + m->hotio_log_counter = 0; + m->hotio_log_base = 0; + } + + /* + * Take a flyer on recovery, works ok on dropped bytes. Work backwards + * from end looking for a byte that could be a valid start-byte with + * the same buttons down as the last valid packet. + */ + if (todo & psmouse_filter & ATTEMPT_RECOVERY) { + int p = m->pktcnt; + while (--p) { + if ((m->packet[p] & 0xcf) == m->last_mbstate) { + m->pktcnt -= p; + memmove(m->packet, m->packet+p, m->pktcnt); + return todo; /* <-- */ + } + } + todo &= ~ATTEMPT_RECOVERY; + } + + if (todo & psmouse_filter & DROP_PACKET) + return todo & psmouse_filter; + if (!(todo & DROP_PACKET)) + m->last_mbstate = m->packet[0] & 0x0f; + return 0; +} + /* * psmouse_process_byte() analyzes the PS/2 data stream and reports * relevant events to the input module once full packet has arrived. @@ -135,6 +244,13 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) /* * Full packet accumulated, process it */ + { + int check = psmouse_filter_packet(psmouse); + if (check & ATTEMPT_RECOVERY) + return PSMOUSE_GOOD_DATA; + if (check & DROP_PACKET) + return PSMOUSE_FULL_PACKET; + } /* * Scroll wheel on IntelliMice, scroll buttons on NetMice @@ -223,6 +339,12 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta psmouse->pktcnt = psmouse->out_of_sync_cnt = 0; psmouse->ps2dev.flags = 0; psmouse->last = jiffies; + psmouse->err_log_base = 0; + psmouse->interval_base = 0; + psmouse->hotio_log_base = 0; + psmouse->err_log_counter = 0; + psmouse->interval_pkts = 0; + psmouse->hotio_log_counter = 0; } diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 593e910..9d90417 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -47,12 +47,19 @@ struct psmouse { unsigned char pktcnt; unsigned char pktsize; unsigned char type; + unsigned char last_mbstate; bool ignore_parity; bool acks_disable_command; unsigned int model; unsigned long last; unsigned long out_of_sync_cnt; unsigned long num_resyncs; + unsigned long interval_base; + unsigned long interval_pkts; + unsigned long hotio_log_base; + unsigned long hotio_log_counter; + unsigned long err_log_base; + unsigned long err_log_counter; enum psmouse_state state; char devname[64]; char phys[32]; @@ -61,6 +68,7 @@ struct psmouse { unsigned int resolution; unsigned int resetafter; unsigned int resync_time; + bool smartscroll; /* Logitech only */ psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse); -- 1.7.2.3 -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html