2004-10-30 05:38:20

by Dmitry Torokhov

[permalink] [raw]
Subject: KVMs & psmouse losing sync

Hi,

Here is my next attempt at resolving the problem with psmouse losing sync
when used with KVMs that suppress psmouse announcements when switching back
to linux box.

The new parameter psmouse.poll is used to have the driver periodically send
"enable" command to the mouse if there was no data from the device in last
<poll> msec. If the command errors or times out the driver assumes that the
mouse was disconnected and next time there is data on the same serio port
the driver will attempt to reconnect.

The advantages of this approach is that it should work well regardless of
the protocol (normally it is very hard to differentiate between bare PS/2
and PS2++) and driver will not pass any bad packets to userpace. The main
cpms is that we're toast if KVM ACKs the command even when mouse/kbd are
connected to another box.

The patch is against vanilla 2.6.9, boot with psmouse.poll=500. I would
appreciate comments/testing. Thanks!

--
Dmitry

diff -urN --exclude-from=/usr/src/exclude 2.6.9/drivers/input/mouse/psmouse-base.c linux-2.6.9/drivers/input/mouse/psmouse-base.c
--- 2.6.9/drivers/input/mouse/psmouse-base.c 2004-10-18 16:53:43.000000000 -0500
+++ linux-2.6.9/drivers/input/mouse/psmouse-base.c 2004-10-29 22:06:34.000000000 -0500
@@ -49,6 +49,10 @@
module_param_named(resetafter, psmouse_resetafter, uint, 0);
MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");

+static unsigned int psmouse_poll;
+module_param_named(poll, psmouse_poll, uint, 0);
+MODULE_PARM_DESC(poll, "Poll interval to verify presence of the device (0 to disable).");
+
__obsolete_setup("psmouse_noext");
__obsolete_setup("psmouse_resolution=");
__obsolete_setup("psmouse_smartscroll=");
@@ -139,6 +143,24 @@
if (psmouse->state == PSMOUSE_IGNORE)
goto out;

+ if (unlikely(psmouse->state == PSMOUSE_LOST_HEARTBIT)) {
+ printk(KERN_INFO "psmouse: new data after losing heartbit, reconnecting...\n");
+ psmouse->state = PSMOUSE_IGNORE;
+ serio_reconnect(psmouse->serio);
+ goto out;
+ }
+
+ if (unlikely(psmouse->state == PSMOUSE_WAIT_HEARTBIT)) {
+ if ((flags & (SERIO_PARITY|SERIO_TIMEOUT)) ||
+ data != PSMOUSE_RET_ACK) {
+ printk(KERN_INFO "psmouse.c: %s at %s lost heartbit\n",
+ psmouse->name, psmouse->phys);
+ psmouse->state = PSMOUSE_LOST_HEARTBIT;
+ } else
+ psmouse->state = PSMOUSE_ACTIVATED;
+ goto out;
+ }
+
if (flags & (SERIO_PARITY|SERIO_TIMEOUT)) {
if (psmouse->state == PSMOUSE_ACTIVATED)
printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n",
@@ -266,6 +288,31 @@
return IRQ_HANDLED;
}

+static void psmouse_heartbit(unsigned long p)
+{
+ struct psmouse *psmouse = (struct psmouse *)p;
+
+ if (!psmouse_poll || psmouse->state != PSMOUSE_ACTIVATED)
+ return;
+
+ if (time_before(jiffies, psmouse->last + msecs_to_jiffies(psmouse_poll))) {
+ /* we recently had some data, just reschedule timer */
+ mod_timer(&psmouse->heartbit, psmouse->last + msecs_to_jiffies(psmouse_poll));
+ } else {
+ /* last data was long ago, ping the mouse */
+ serio_pause_rx(psmouse->serio);
+ if (serio_write(psmouse->serio, PSMOUSE_CMD_ENABLE) < 0) {
+ psmouse->state = PSMOUSE_IGNORE;
+ serio_continue_rx(psmouse->serio);
+ serio_reconnect(psmouse->serio);
+ return;
+ }
+ psmouse->state = PSMOUSE_WAIT_HEARTBIT;
+ serio_continue_rx(psmouse->serio);
+ mod_timer(&psmouse->heartbit, jiffies + msecs_to_jiffies(psmouse_poll));
+ }
+}
+
/*
* psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge.
* It doesn't handle retransmission, though it could - because when there would
@@ -674,6 +721,9 @@
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys);

psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+
+ if (psmouse_poll)
+ mod_timer(&psmouse->heartbit, jiffies + msecs_to_jiffies(psmouse_poll));
}


@@ -711,7 +761,9 @@
struct psmouse *psmouse, *parent;

psmouse = serio->private;
+
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ del_timer_sync(&psmouse->heartbit);

if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
@@ -756,6 +808,9 @@
memset(psmouse, 0, sizeof(struct psmouse));

init_waitqueue_head(&psmouse->wait);
+ init_timer(&psmouse->heartbit);
+ psmouse->heartbit.data = (unsigned long)psmouse;
+ psmouse->heartbit.function = psmouse_heartbit;
init_input_dev(&psmouse->dev);
psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
diff -urN --exclude-from=/usr/src/exclude 2.6.9/drivers/input/mouse/psmouse.h linux-2.6.9/drivers/input/mouse/psmouse.h
--- 2.6.9/drivers/input/mouse/psmouse.h 2004-10-18 16:55:06.000000000 -0500
+++ linux-2.6.9/drivers/input/mouse/psmouse.h 2004-10-29 21:31:06.000000000 -0500
@@ -28,6 +28,8 @@
PSMOUSE_INITIALIZING,
PSMOUSE_CMD_MODE,
PSMOUSE_ACTIVATED,
+ PSMOUSE_WAIT_HEARTBIT,
+ PSMOUSE_LOST_HEARTBIT,
};

/* psmouse protocol handler return codes */
@@ -61,6 +63,8 @@
/* Used to signal completion from interrupt handler */
wait_queue_head_t wait;

+ struct timer_list heartbit;
+
psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
int (*reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);


2004-10-31 17:05:37

by Thomas Svedberg

[permalink] [raw]
Subject: Re: KVMs & psmouse losing sync

Dmitry Torokhov wrote:
> Hi,
>
> Here is my next attempt at resolving the problem with psmouse losing sync
> when used with KVMs that suppress psmouse announcements when switching back
> to linux box.
>
> The new parameter psmouse.poll is used to have the driver periodically send
> "enable" command to the mouse if there was no data from the device in last
> <poll> msec. If the command errors or times out the driver assumes that the
> mouse was disconnected and next time there is data on the same serio port
> the driver will attempt to reconnect.
>
> The advantages of this approach is that it should work well regardless of
> the protocol (normally it is very hard to differentiate between bare PS/2
> and PS2++) and driver will not pass any bad packets to userpace. The main
> cpms is that we're toast if KVM ACKs the command even when mouse/kbd are
> connected to another box.
>
> The patch is against vanilla 2.6.9, boot with psmouse.poll=500. I would
> appreciate comments/testing. Thanks!

Works great with my Belkin Omni Cube, thank you!

--
/ Thomas
.......................................................................
Thomas Svedberg
Department of Applied Mechanics
Chalmers University of Technology

Address: SE-412 96 G?teborg, SWEDEN
E-mail : [email protected], [email protected]
Phone : +46 31 772 1522
Fax : +46 31 772 3827
.......................................................................