Commit fadb62b6 authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: mousedev - better handle button presses when under load

Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 0a30246c
...@@ -50,6 +50,7 @@ MODULE_PARM_DESC(yres, "Vertical screen resolution"); ...@@ -50,6 +50,7 @@ MODULE_PARM_DESC(yres, "Vertical screen resolution");
struct mousedev_motion { struct mousedev_motion {
int dx, dy, dz; int dx, dy, dz;
unsigned long buttons;
}; };
struct mousedev { struct mousedev {
...@@ -62,21 +63,31 @@ struct mousedev { ...@@ -62,21 +63,31 @@ struct mousedev {
struct input_handle handle; struct input_handle handle;
struct mousedev_motion packet; struct mousedev_motion packet;
unsigned long buttons;
unsigned int pkt_count; unsigned int pkt_count;
int old_x[4], old_y[4]; int old_x[4], old_y[4];
unsigned int touch; unsigned int touch;
}; };
enum mousedev_emul {
MOUSEDEV_EMUL_PS2,
MOUSEDEV_EMUL_IMPS,
MOUSEDEV_EMUL_EXPS
} __attribute__ ((packed));
#define PACKET_QUEUE_LEN 16
struct mousedev_list { struct mousedev_list {
struct fasync_struct *fasync; struct fasync_struct *fasync;
struct mousedev *mousedev; struct mousedev *mousedev;
struct list_head node; struct list_head node;
int dx, dy, dz;
unsigned long buttons; struct mousedev_motion packets[PACKET_QUEUE_LEN];
unsigned int head, tail;
spinlock_t packet_lock;
signed char ps2[6]; signed char ps2[6];
unsigned char ready, buffer, bufsiz; unsigned char ready, buffer, bufsiz;
unsigned char mode, imexseq, impsseq; unsigned char imexseq, impsseq;
enum mousedev_emul mode;
}; };
#define MOUSEDEV_SEQ_LEN 6 #define MOUSEDEV_SEQ_LEN 6
...@@ -165,24 +176,40 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int ...@@ -165,24 +176,40 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int
} }
if (value) { if (value) {
set_bit(index, &mousedev->buttons); set_bit(index, &mousedev->packet.buttons);
set_bit(index, &mousedev_mix.buttons); set_bit(index, &mousedev_mix.packet.buttons);
} else { } else {
clear_bit(index, &mousedev->buttons); clear_bit(index, &mousedev->packet.buttons);
clear_bit(index, &mousedev_mix.buttons); clear_bit(index, &mousedev_mix.packet.buttons);
} }
} }
static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_motion *packet) static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_motion *packet)
{ {
struct mousedev_list *list; struct mousedev_list *list;
struct mousedev_motion *p;
unsigned long flags;
list_for_each_entry(list, &mousedev->list, node) { list_for_each_entry(list, &mousedev->list, node) {
list->dx += packet->dx; spin_lock_irqsave(&list->packet_lock, flags);
list->dy += packet->dy;
list->dz += packet->dz; p = &list->packets[list->head];
list->buttons = mousedev->buttons; if (list->ready && p->buttons != packet->buttons) {
unsigned int new_head = (list->head + 1) % PACKET_QUEUE_LEN;
if (new_head != list->tail) {
p = &list->packets[list->head = new_head];
memset(p, 0, sizeof(struct mousedev_motion));
}
}
p->dx += packet->dx;
p->dy += packet->dy;
p->dz += packet->dz;
p->buttons = mousedev->packet.buttons;
list->ready = 1; list->ready = 1;
spin_unlock_irqrestore(&list->packet_lock, flags);
kill_fasync(&list->fasync, SIGIO, POLL_IN); kill_fasync(&list->fasync, SIGIO, POLL_IN);
} }
...@@ -237,7 +264,7 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig ...@@ -237,7 +264,7 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
mousedev_notify_readers(mousedev, &mousedev->packet); mousedev_notify_readers(mousedev, &mousedev->packet);
mousedev_notify_readers(&mousedev_mix, &mousedev->packet); mousedev_notify_readers(&mousedev_mix, &mousedev->packet);
memset(&mousedev->packet, 0, sizeof(struct mousedev_motion)); mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
} }
break; break;
} }
...@@ -322,6 +349,7 @@ static int mousedev_open(struct inode * inode, struct file * file) ...@@ -322,6 +349,7 @@ static int mousedev_open(struct inode * inode, struct file * file)
return -ENOMEM; return -ENOMEM;
memset(list, 0, sizeof(struct mousedev_list)); memset(list, 0, sizeof(struct mousedev_list));
spin_lock_init(&list->packet_lock);
list->mousedev = mousedev_table[i]; list->mousedev = mousedev_table[i];
list_add_tail(&list->node, &mousedev_table[i]->list); list_add_tail(&list->node, &mousedev_table[i]->list);
file->private_data = list; file->private_data = list;
...@@ -341,32 +369,56 @@ static int mousedev_open(struct inode * inode, struct file * file) ...@@ -341,32 +369,56 @@ static int mousedev_open(struct inode * inode, struct file * file)
return 0; return 0;
} }
static void mousedev_packet(struct mousedev_list *list, unsigned char off) static inline int mousedev_limit_delta(int delta, int limit)
{ {
list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07); return delta > limit ? limit : (delta < -limit ? -limit : delta);
list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx)); }
list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy));
list->dx -= list->ps2[off + 1]; static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data)
list->dy -= list->ps2[off + 2]; {
list->bufsiz = off + 3; struct mousedev_motion *p;
unsigned long flags;
if (list->mode == 2) {
list->ps2[off + 3] = (list->dz > 7 ? 7 : (list->dz < -7 ? -7 : list->dz)); spin_lock_irqsave(&list->packet_lock, flags);
list->dz -= list->ps2[off + 3]; p = &list->packets[list->tail];
list->ps2[off + 3] = (list->ps2[off + 3] & 0x0f) | ((list->buttons & 0x18) << 1);
list->bufsiz++; ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
} else { ps2_data[1] = mousedev_limit_delta(p->dx, 127);
list->ps2[off] |= ((list->buttons & 0x10) >> 3) | ((list->buttons & 0x08) >> 1); ps2_data[2] = mousedev_limit_delta(p->dy, 127);
p->dx -= ps2_data[1];
p->dy -= ps2_data[2];
switch (list->mode) {
case MOUSEDEV_EMUL_EXPS:
ps2_data[3] = mousedev_limit_delta(p->dz, 127);
p->dz -= ps2_data[3];
ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
list->bufsiz = 4;
break;
case MOUSEDEV_EMUL_IMPS:
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
ps2_data[3] = mousedev_limit_delta(p->dz, 127);
p->dz -= ps2_data[3];
list->bufsiz = 4;
break;
case MOUSEDEV_EMUL_PS2:
default:
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
p->dz = 0;
list->bufsiz = 3;
break;
} }
if (list->mode == 1) { if (!p->dx && !p->dy && !p->dz) {
list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz)); if (list->tail != list->head)
list->dz -= list->ps2[off + 3]; list->tail = (list->tail + 1) % PACKET_QUEUE_LEN;
list->bufsiz++; if (list->tail == list->head)
list->ready = 0;
} }
if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0; spin_unlock_irqrestore(&list->packet_lock, flags);
list->buffer = list->bufsiz;
} }
...@@ -384,31 +436,31 @@ static ssize_t mousedev_write(struct file * file, const char * buffer, size_t co ...@@ -384,31 +436,31 @@ static ssize_t mousedev_write(struct file * file, const char * buffer, size_t co
if (c == mousedev_imex_seq[list->imexseq]) { if (c == mousedev_imex_seq[list->imexseq]) {
if (++list->imexseq == MOUSEDEV_SEQ_LEN) { if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
list->imexseq = 0; list->imexseq = 0;
list->mode = 2; list->mode = MOUSEDEV_EMUL_EXPS;
} }
} else list->imexseq = 0; } else list->imexseq = 0;
if (c == mousedev_imps_seq[list->impsseq]) { if (c == mousedev_imps_seq[list->impsseq]) {
if (++list->impsseq == MOUSEDEV_SEQ_LEN) { if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
list->impsseq = 0; list->impsseq = 0;
list->mode = 1; list->mode = MOUSEDEV_EMUL_IMPS;
} }
} else list->impsseq = 0; } else list->impsseq = 0;
list->ps2[0] = 0xfa; list->ps2[0] = 0xfa;
list->bufsiz = 1;
switch (c) { switch (c) {
case 0xeb: /* Poll */ case 0xeb: /* Poll */
mousedev_packet(list, 1); mousedev_packet(list, &list->ps2[1]);
list->bufsiz++; /* account for leading ACK */
break; break;
case 0xf2: /* Get ID */ case 0xf2: /* Get ID */
switch (list->mode) { switch (list->mode) {
case 0: list->ps2[1] = 0; break; case MOUSEDEV_EMUL_PS2: list->ps2[1] = 0; break;
case 1: list->ps2[1] = 3; break; case MOUSEDEV_EMUL_IMPS: list->ps2[1] = 3; break;
case 2: list->ps2[1] = 4; break; case MOUSEDEV_EMUL_EXPS: list->ps2[1] = 4; break;
} }
list->bufsiz = 2; list->bufsiz = 2;
break; break;
...@@ -419,13 +471,15 @@ static ssize_t mousedev_write(struct file * file, const char * buffer, size_t co ...@@ -419,13 +471,15 @@ static ssize_t mousedev_write(struct file * file, const char * buffer, size_t co
break; break;
case 0xff: /* Reset */ case 0xff: /* Reset */
list->impsseq = 0; list->impsseq = list->imexseq = 0;
list->imexseq = 0; list->mode = MOUSEDEV_EMUL_PS2;
list->mode = 0; list->ps2[1] = 0xaa; list->ps2[2] = 0x00;
list->ps2[1] = 0xaa;
list->ps2[2] = 0x00;
list->bufsiz = 3; list->bufsiz = 3;
break; break;
default:
list->bufsiz = 1;
break;
} }
list->buffer = list->bufsiz; list->buffer = list->bufsiz;
...@@ -451,8 +505,10 @@ static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, lo ...@@ -451,8 +505,10 @@ static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, lo
if (retval) if (retval)
return retval; return retval;
if (!list->buffer && list->ready) if (!list->buffer && list->ready) {
mousedev_packet(list, 0); mousedev_packet(list, list->ps2);
list->buffer = list->bufsiz;
}
if (count > list->buffer) if (count > list->buffer)
count = list->buffer; count = list->buffer;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment