@@ -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,85 @@ 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_PACKET ? " bad" : "");
+ }
+
+ if (m->err_log_counter && time_after(m->last, m->err_log_base + HZ) &&
+ psmouse_filter & (SUMMARIZE_ERRORS | LOG_ALL)) {
+ printk(KERN_WARNING "psmouse.c: %s at %s %lu bad packets\n",
+ m->name, m->phys, m->err_log_counter);
+ m->err_log_counter = 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\n",
+ m->name, m->phys, m->hotio_log_counter);
+ m->hotio_log_counter = 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 and general direction as the last good packet.
+ */
+ if (todo & psmouse_filter & ATTEMPT_RECOVERY) {
+ int p = m->pktcnt;
+ while (--p) {
+ if (m->packet[p] == 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] & 0x3f;
+ 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 +230,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 +325,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;
}
@@ -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];