Subject: kern/15359: Extending intellimouse support to 5-button mice
To: None <gnats-bugs@gnats.netbsd.org>
From: seebs <seebs@ged.plethora.net>
List: netbsd-bugs
Date: 01/24/2002 21:51:26
>Number: 15359
>Category: kern
>Synopsis: psmi driver *almost* supports 5-button mice
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: kern-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Thu Jan 24 19:54:00 PST 2002
>Closed-Date:
>Last-Modified:
>Originator: seebs
>Release: NetBSD 1.5ZA
>Organization:
>Environment:
System: NetBSD ged.plethora.net 1.5ZA NetBSD 1.5ZA (GED) #17: Thu Jan 24 21:40:46 CST 2002 root@ged.plethora.net:/usr/src/sys/arch/i386/compile/GED i386
Architecture: i386
Machine: i386
>Description:
The intellimouse protocol, as described by psm_intelli.c, consists
of sending a mouse several rate change commands, and then checking to
see if it now gives a specific unexpected response. There is a
further extension to this protocol, which provides support for
5-button mice. In this version of the protocol, the rate change
commands and expected response are slightly different.
It turns out to be easy to produce a godawful kluge that allows the
extra buttons to be used.
>How-To-Repeat:
Get a Kensington "expert mouse pro".
>Fix:
Calling this a "fix" is like calling Windows "a good desktop
environment". However, it serves as a proof of concept; with this
patch in place, my Kensington Expert Mouse generates buttons 0-4,
instead of just 0-2, when I check its output with "od -x
/dev/wsmouse".
I am deeply offended by the enclosed code, but it appears to work.
I suspect that, down a similar path, the psm driver could be modified
to support all three of the likely mouse protocols; simply test for
the 5-button form first (because these mice will, of course, respond
correctly to the 3-button protocol), if that fails, try the 3-button
protocol, if that fails, use the "standard" 3-button protocol which
doesn't support the wheel.
However, I'm too lazy to do this right, so I'll just send it in as
effective documentation. Someone wishing to know where these numbers
came from might want to review:
http://govschl.ndsu.nodak.edu/~achapwes/PICmicro/mouse/mouse.html
which is where I got the protocol info. Special thanks to a friend of
a friend who pointed out that my laptop's BIOS was probably silently
altering protocol messages from the external mouse to avoid
"compatability problems".
*** psm_intelli.c.orig Thu Jan 24 21:02:07 2002
--- psm_intelli.c Thu Jan 24 21:40:35 2002
***************
*** 40,45 ****
--- 40,49 ----
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
+ enum intellimouse_protocol {
+ BUTTONS_3, BUTTONS_5, BUTTONS_MAX
+ };
+
struct pmsi_softc { /* driver status information */
struct device sc_dev;
***************
*** 51,56 ****
--- 55,62 ----
int inputstate;
u_int buttons, oldbuttons; /* mouse button status */
signed char dx, dy;
+ enum intellimouse_protocol protocol;
+ unsigned long bytes;
struct device *sc_wsmousedev;
};
***************
*** 77,113 ****
pmsi_disable,
};
! static int pmsi_setintellimode __P((pckbc_tag_t, pckbc_slot_t));
static int
! pmsi_setintellimode(tag, slot)
pckbc_tag_t tag;
pckbc_slot_t slot;
{
u_char cmd[2], resp[1];
! int i, res;
! static u_char rates[] = {200, 200, 80};
! cmd[0] = PMS_SET_SAMPLE;
! for (i = 0; i < 3; i++) {
! cmd[1] = rates[i];
! res = pckbc_poll_cmd(tag, slot, cmd, 2, 0, 0, 0);
if (res)
return (res);
}
!
! cmd[0] = PMS_SEND_DEV_ID;
! res = pckbc_poll_cmd(tag, slot, cmd, 1, 1, resp, 0);
! if (res)
! return (res);
! if (resp[0] != 3) {
! #ifdef DEBUG
! printf("pmsiprobe: resp[0] -> %d [expected 3]\n", resp[0]);
! #endif
! return (ENXIO);
! }
!
! return (0);
}
int
--- 83,129 ----
pmsi_disable,
};
! static int pmsi_setintellimode __P((struct pmsi_softc *, pckbc_tag_t, pckbc_slot_t));
static int
! pmsi_setintellimode(sc, tag, slot)
! struct pmsi_softc *sc;
pckbc_tag_t tag;
pckbc_slot_t slot;
{
u_char cmd[2], resp[1];
! int i, p, res;
! struct {
! int rates[3];
! int response;
! enum intellimouse_protocol protocol;
! } protocols[] = {
! { { 200, 200, 80 }, 4, BUTTONS_5 },
! { { 200, 100, 80 }, 3, BUTTONS_3 },
! };
!
! for (p = 0; p < sizeof(protocols)/sizeof(protocols[0]); ++p) {
!
! cmd[0] = PMS_SET_SAMPLE;
! for (i = 0; i < 3; i++) {
! cmd[1] = protocols[p].rates[i];
! res = pckbc_poll_cmd(tag, slot, cmd, 2, 0, 0, 0);
! if (res)
! return (res);
! }
! cmd[0] = PMS_SEND_DEV_ID;
! res = pckbc_poll_cmd(tag, slot, cmd, 1, 1, resp, 0);
if (res)
return (res);
+ if (resp[0] != protocols[p].response) {
+ printf("pmsiprobe [protocol %d]: resp[0] -> %d [expected %d]\n", p, resp[0], protocols[p].response);
+ }
+ if (sc)
+ sc->protocol = protocols[p].protocol;
+ return 0;
}
! return ENXIO;
}
int
***************
*** 148,154 ****
return (0);
}
! if ((res = pmsi_setintellimode(pa->pa_tag, pa->pa_slot))) {
#ifdef DEBUG
printf("pmsiprobe: intellimode -> %d\n", res);
#endif
--- 164,170 ----
return (0);
}
! if ((res = pmsi_setintellimode(0, pa->pa_tag, pa->pa_slot))) {
#ifdef DEBUG
printf("pmsiprobe: intellimode -> %d\n", res);
#endif
***************
*** 186,192 ****
return;
}
#endif
! res = pmsi_setintellimode(pa->pa_tag, pa->pa_slot);
#ifdef DEBUG
if (res) {
printf("pmsiattach: error setting intelli mode\n");
--- 202,208 ----
return;
}
#endif
! res = pmsi_setintellimode(sc, pa->pa_tag, pa->pa_slot);
#ifdef DEBUG
if (res) {
printf("pmsiattach: error setting intelli mode\n");
***************
*** 240,246 ****
if (res)
printf("pmsi_enable: command error\n");
! if ((res = pmsi_setintellimode(sc->sc_kbctag, sc->sc_kbcslot))) {
#ifdef DEBUG
printf("pmsi_enable: intellimode -> %d\n", res);
#endif
--- 256,262 ----
if (res)
printf("pmsi_enable: command error\n");
! if ((res = pmsi_setintellimode(sc, sc->sc_kbctag, sc->sc_kbcslot))) {
#ifdef DEBUG
printf("pmsi_enable: intellimode -> %d\n", res);
#endif
***************
*** 336,341 ****
--- 352,359 ----
#define PS2LBUTMASK 0x01
#define PS2RBUTMASK 0x02
#define PS2MBUTMASK 0x04
+ #define PS2_4BUTMASK 0x10
+ #define PS2_5BUTMASK 0x20
void pmsiinput(vsc, data)
void *vsc;
***************
*** 353,358 ****
--- 371,377 ----
switch (sc->inputstate) {
case 0:
+ sc->bytes = (data << 24);
if ((data & 0xc0) == 0) { /* no ovfl, bit 3 == 1 too? */
sc->buttons = ((data & PS2LBUTMASK) ? 0x1 : 0) |
((data & PS2MBUTMASK) ? 0x2 : 0) |
***************
*** 362,367 ****
--- 381,387 ----
break;
case 1:
+ sc->bytes |= (data << 16);
sc->dx = data;
/* Bounding at -127 avoids a bug in XFree86. */
sc->dx = (sc->dx == -128) ? -127 : sc->dx;
***************
*** 369,384 ****
break;
case 2:
sc->dy = data;
sc->dy = (sc->dy == -128) ? -127 : sc->dy;
++sc->inputstate;
break;
case 3:
dz = data;
! dz = (dz == -128) ? -127 : dz;
sc->inputstate = 0;
changed = (sc->buttons ^ sc->oldbuttons);
sc->oldbuttons = sc->buttons;
--- 389,421 ----
break;
case 2:
+ sc->bytes |= (data << 8);
sc->dy = data;
sc->dy = (sc->dy == -128) ? -127 : sc->dy;
++sc->inputstate;
break;
case 3:
+ sc->bytes |= data;
dz = data;
! dz &= 0xf;
! if (sc->protocol == BUTTONS_5) {
! if (dz & 0x8)
! dz -= 16;
! if (data & PS2_4BUTMASK)
! sc->buttons |= 0x8;
! if (data & PS2_5BUTMASK)
! sc->buttons |= 0x10;
! } else {
! if (dz == -128)
! dz = -127;
! }
sc->inputstate = 0;
+ #if 0
+ /* used only for debugging protocol */
+ printf("psmi: packet 0x%08lx\n", sc->bytes);
+ #endif
changed = (sc->buttons ^ sc->oldbuttons);
sc->oldbuttons = sc->buttons;
>Release-Note:
>Audit-Trail:
>Unformatted: