diff options
Diffstat (limited to 'os/pc/devmouse.c')
| -rw-r--r-- | os/pc/devmouse.c | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/os/pc/devmouse.c b/os/pc/devmouse.c new file mode 100644 index 00000000..1b3c55c6 --- /dev/null +++ b/os/pc/devmouse.c @@ -0,0 +1,672 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * TODO + * - shift key should modify right button with non-serial mice + * + intellimouse implementation + * - acceleration for all mouse types + * + spurious interrupt 7 after probing for ps2 mouse for the first time...? + * - test with ms busmouse + * - test with logitech serial mouse + */ + +/* + * mouse types + */ +enum +{ + Mouseother, + Mouseserial, + MousePS2, + Mousebus, + Mouseintelli, + Mousemsbus, +}; + +static int mousetype; +static int mouseswap; +static int mouseport; /* port for serial mice, irq for bus mice */ +static int mousesubtype; +static int accelerated; +static QLock mouselock; + +static int msbusmousedetect(void); +static int busmousedetect(void); +static void mousectl(char *buf); +static void mouseprobe(char *buf, int len); +static void mousestatus(char *buf, int len); + +enum{ + Qdir, + Qmousectl, + Qmouseprobe, +}; + +static +Dirtab mousetab[]={ + "mousectl", {Qmousectl, 0}, 0, 0600, + "mouseprobe", {Qmouseprobe, 0}, 0, 0400, +}; + +static Chan* +mouseattach(char* spec) +{ + return devattach('m', spec); +} + +static int +mousewalk(Chan* c, char* name) +{ + return devwalk(c, name, mousetab, nelem(mousetab), devgen); +} + +static void +mousestat(Chan* c, char* db) +{ + devstat(c, db, mousetab, nelem(mousetab), devgen); +} + +static Chan* +mouseopen(Chan* c, int omode) +{ + return devopen(c, omode, mousetab, nelem(mousetab), devgen); +} + +static void +mouseclose(Chan* c) +{ + USED(c); +} + +static long +mouseread(Chan* c, void* a, long n, vlong offset) +{ + char buf[64]; + USED(offset); + + switch(c->qid.path & ~CHDIR){ + case Qdir: + return devdirread(c, a, n, mousetab, nelem(mousetab), devgen); + case Qmousectl: + qlock(&mouselock); + mousestatus(buf, sizeof(buf)); + qunlock(&mouselock); + n = readstr(offset, a, n, buf); + break; + case Qmouseprobe: + if (mousetype) + error(Emouseset); + mouseprobe(buf, sizeof(buf)); + n = readstr(offset, a, n, buf); + break; + default: + n=0; + break; + } + return n; +} + +static long +mousewrite(Chan* c, void *a, long n, vlong) +{ + char buf[64]; + if ((c->qid.path & ~CHDIR) != Qmousectl) + error(Ebadusefd); + if (n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + + qlock(&mouselock); + if (waserror()) { + qunlock(&mouselock); + nexterror(); + } + mousectl(buf); + poperror(); + qunlock(&mouselock); + return n; +} + +static void +track(int b, int dx, int dy) +{ + static uchar map[8] = {0,4,2,6,1,5,3,7}; + if (mouseswap) + b = map[b&7]; + mousetrack(b, dx, dy); +} + +static void +setintellimouse(void) +{ + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0xC8); + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0x64); + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0x50); +} + +/* + * check for an Intellimouse. + * this is only used when we know there's an 8042 aux device + */ +static int +intellimousedetect(void) +{ + int id; + setintellimouse(); + /* check whether the mouse is now in extended mode */ + id = i8042auxcmdval(0xf2); /* identify device */ + if (id != 3) { + /* + * set back to standard sample rate (100 per sec) + */ + i8042auxcmd(0xf3); + i8042auxcmd(0x64); + return 0; + } + return 1; +} + +static void +mouseprobe(char *buf, int len) +{ + USED(len); + /* + * bus mice are easiest, so probe them first + */ + if (busmousedetect()) + sprint(buf, "bus\n"); + else if (msbusmousedetect()) + sprint(buf, "msbus\n"); + else if (i8042auxdetect()) { + if (intellimousedetect()) + sprint(buf, "ps2intellimouse\n"); + else + sprint(buf, "ps2\n"); + } + else + *buf = 0; +} + + +static void +mousestatus(char *buf, int len) +{ + char *s; + USED(len); + s = buf; + switch (mousetype) { + case Mouseserial: + if (mousesubtype) + s += sprint(s, "serial %d %c\n", mouseport, mousesubtype); + else + s += sprint(s, "serial %d\n", mouseport); + break; + case MousePS2: + s += sprint(s, "ps2\n"); + break; + case Mousebus: + s += sprint(s, "bus %d\n", mouseport); + break; + case Mouseintelli: + s += sprint(s, "intelli\n"); + break; + case Mousemsbus: + s += sprint(s, "msbus %d\n", mouseport); + break; + default: + case Mouseother: + s += sprint(s, "unknown\n"); + break; + } + if (accelerated) + s += sprint(s, "accelerated\n"); + if (mouseswap) + sprint(s, "swap\n"); +} + +/* + * Logitech 5 byte packed binary mouse format, 8 bit bytes + * + * shift & right button is the same as middle button (for 2 button mice) + */ +static int +logitechmouseputc(Queue *q, int c) +{ + static short msg[5]; + static int nb; + static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 5, 3, 7}; + int dx, dy, newbuttons; + int mouseshifted; + + USED(q); + if((c&0xF0) == 0x80) + nb=0; + msg[nb] = c; + if(c & 0x80) + msg[nb] |= ~0xFF; /* sign extend */ + if(++nb == 5){ + mouseshifted = 0; /* XXX should be from keyboard shift key */ + newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)]; + dx = msg[1]+msg[3]; + dy = -(msg[2]+msg[4]); + track(newbuttons, dx, dy); + nb = 0; + } + return 0; +} + +/* + * microsoft 3 button, 7 bit bytes + * + * byte 0 - 1 L R Y7 Y6 X7 X6 + * byte 1 - 0 X5 X4 X3 X2 X1 X0 + * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0 + * byte 3 - 0 M x x x x x (optional) + * + * shift & right button is the same as middle button (for 2 button mice) + */ +static int +m3mouseputc(Queue*, int c) +{ + static uchar msg[3]; + static int nb; + static int middle; + static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 5 }; + short x; + int dx, dy, buttons; + + /* + * check bit 6 for consistency + */ + if(nb==0){ + if((c&0x40) == 0){ + /* an extra byte gets sent for the middle button */ + if(c & 0x1c) + return 0; + middle = (c&0x20) ? 2 : 0; + buttons = (mouse.b & ~2) | middle; + track(buttons, 0, 0); + return 0; + } + } + msg[nb] = c&0x3f; + if(++nb == 3){ + nb = 0; + buttons = middle | b[(msg[0]>>4)&3]; + x = (msg[0]&0x3)<<14; + dx = (x>>8) | msg[1]; + x = (msg[0]&0xc)<<12; + dy = (x>>8) | msg[2]; + track(buttons, dx, dy); + } + return 0; +} + +static void +serialmouse(int port, char *type, int setspeed) +{ + int (*putc)(Queue *, int) = 0; + char pn[KNAMELEN]; + + if(mousetype) + error(Emouseset); + + if(port >= 2 || port < 0) + error(Ebadarg); + + if (type == 0) + putc = logitechmouseputc; + else if (*type == 'M') + putc = m3mouseputc; + else + error(Ebadarg); + snprint(pn, sizeof(pn), "%d", port); + i8250mouse(pn, putc, setspeed); + mousetype = Mouseserial; + mouseport = port; + mousesubtype = (type && *type == 'M') ? 'M' : 0; +} + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & left button is the same as middle button + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[3]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 }; + int buttons, dx, dy; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + return; + + msg[nb] = c; + if(++nb == 3){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + dx = msg[1]; + dy = -msg[2]; + track(buttons, dx, dy); + } + return; +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype) + error(Emouseset); + + i8042auxenable(ps2mouseputc); + /* make mouse streaming, enabled */ + i8042auxcmd(0xEA); + i8042auxcmd(0xF4); + + mousetype = MousePS2; +} + +/* logitech bus mouse ports and commands */ +enum { + /* ports */ + BMdatap = 0x23c, + BMsigp = 0x23d, + BMctlp = 0x23e, + BMintrp = 0x23e, + BMconfigp = 0x23f, + + /* commands */ + BMintron = 0x0, + BMintroff = 0x10, + BMrxlo = 0x80, + BMrxhi = 0xa0, + BMrylo = 0xc0, + BMryhi = 0xe0, + + BMconfig = 0x91, + BMdefault = 0x90, + + BMsigval = 0xa5 +}; + +static void +busmouseintr(Ureg *, void *) +{ + char dx, dy; + uchar b; + static uchar oldb; + static Lock intrlock; + ilock(&intrlock); + outb(BMintrp, BMintroff); + outb(BMctlp, BMrxlo); + dx = inb(BMdatap) & 0xf; + outb(BMctlp, BMrxhi); + dx |= (inb(BMdatap) & 0xf) << 4; + outb(BMctlp, BMrylo); + dy = inb(BMdatap) & 0xf; + outb(BMctlp, BMryhi); + b = inb(BMdatap); + dy |= (b & 0xf) << 4; + b = ~(b >> 5) & 7; + if (dx || dy || b != oldb) { + oldb = b; + track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy); + } + iunlock(&intrlock); + outb(BMintrp, BMintron); +} + +static int +busmousedetect(void) +{ + outb(BMconfigp, BMconfig); + outb(BMsigp, BMsigval); + delay(2); + if (inb(BMsigp) != BMsigval) + return 0; + outb(BMconfigp, BMdefault); + return 1; +} + +/* + * set up a logitech bus mouse + */ +static void +busmouse(int irq) +{ + if (mousetype) + error(Emouseset); + if (!busmousedetect()) + error(Enodev); + + intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, busmouseintr, 0, BUSUNKNOWN); + outb(BMintrp, BMintron); + mousetype = Mousebus; + mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC; +} + +/* microsoft bus mouse ports and commands */ +enum { + MBMdatap= 0x23d, + MBMsigp= 0x23e, + MBMctlp= 0x23c, + MBMconfigp= 0x23f, + + MBMintron= 0x11, + MBMintroff= 0x10, + MBMrbuttons= 0x00, + MBMrx= 0x01, + MBMry= 0x02, + MBMstart= 0x80, + MBMcmd= 0x07, +}; + +static void +msbusmouseintr(Ureg *, void *) +{ + char dx, dy; + uchar b; + static uchar oldb; + static Lock intrlock; + ilock(&intrlock); + outb(MBMctlp, MBMcmd); + outb(MBMdatap, inb(MBMdatap)|0x20); + + outb(MBMctlp, MBMrx); + dx = inb(MBMdatap); + + outb(MBMctlp, MBMry); + dy = inb(MBMdatap); + + outb(MBMctlp, MBMrbuttons); + b = inb(MBMdatap) & 0x7; + + outb(MBMctlp, MBMcmd); + outb(MBMdatap, inb(MBMdatap)&0xdf); + + if (dx != 0 || dy != 0 || b != oldb) { + oldb = b; + /* XXX this is almost certainly wrong */ + track((b>>2)|(b&0x02)|((b&0x01)<<2), dx, dy); + } + iunlock(&intrlock); +} + +static int +msbusmousedetect(void) +{ + if (inb(MBMsigp) == 0xde) { + int v, i; + delay(1); + v = inb(MBMsigp); + delay(1); + for (i = 0; i < 4; i++) { + if (inb(MBMsigp) != 0xde) + break; + delay(1); + if (inb(MBMsigp) != v) + break; + delay(1); + } + if (i == 4) { + outb(MBMctlp, MBMcmd); + return 1; + } + } + return 0; +} + +static void +msbusmouse(int irq) +{ + if (mousetype) + error(Emouseset); + if (!msbusmousedetect()) + error(Enodev); + mousetype = Mousemsbus; + mouseport = irq >= 0 ? irq : VectorBUSMOUSE-VectorPIC; + intrenable(irq >= 0 ? irq+VectorPIC : VectorBUSMOUSE, msbusmouseintr, 0, BUSUNKNOWN); + outb(MBMdatap, MBMintron); +} + +static void +mousectl(char *buf) +{ + int nf, x; + char *field[10]; + nf = getfields(buf, field, 10, 1, " \t\n"); + if (nf < 1) + return; + if(strncmp(field[0], "serial", 6) == 0){ + switch(nf){ + /* the difference between these two cases is intriguing - wrtp */ + case 1: + serialmouse(atoi(field[0]+6), 0, 1); + break; + case 2: + serialmouse(atoi(field[1]), 0, 0); + break; + case 3: + default: + serialmouse(atoi(field[1]), field[2], 0); + break; + } + } else if(strcmp(field[0], "ps2") == 0){ + ps2mouse(); + } else if (strcmp(field[0], "ps2intellimouse") == 0) { + ps2mouse(); + setintellimouse(); + } else if (strncmp(field[0], "bus", 3) == 0 || strncmp(field[0], "msbus", 5) == 0) { + int irq, isms; + + isms = (field[0][0] == 'm'); + if (nf == 1) + irq = atoi(field[0] + (isms ? 5 : 3)); + else + irq = atoi(field[1]); + if (irq < 1) + irq = -1; + if (isms) + msbusmouse(irq); + else + busmouse(irq); + } else if(strcmp(field[0], "accelerated") == 0){ + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE7); + splx(x); + accelerated = 1; + break; + } + } else if(strcmp(field[0], "linear") == 0){ + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE6); + splx(x); + accelerated = 0; + break; + } + } else if(strcmp(field[0], "res") == 0){ + int n,m; + switch(nf){ + default: + n = 0x02; + m = 0x23; + break; + case 2: + n = atoi(field[1])&0x3; + m = 0x7; + break; + case 3: + n = atoi(field[1])&0x3; + m = atoi(field[2])&0x7; + break; + } + + switch(mousetype){ + case MousePS2: + x = splhi(); + i8042auxcmd(0xE8); + i8042auxcmd(n); + i8042auxcmd(0x5A); + i8042auxcmd(0x30|m); + i8042auxcmd(0x5A); + i8042auxcmd(0x20|(m>>1)); + splx(x); + break; + } + } else if(strcmp(field[0], "swap") == 0) + mouseswap ^= 1; +} + +Dev mousedevtab = { /* defaults in dev.c */ + 'm', + "mouse", + + devreset, /* devreset */ + devinit, /* devinit */ + mouseattach, + devdetach, + devclone, /* devclone */ + mousewalk, + mousestat, + mouseopen, + devcreate, /* devcreate */ + mouseclose, + mouseread, + devbread, /* devbread */ + mousewrite, + devbwrite, /* devbwrite */ + devremove, /* devremove */ + devwstat, /* devwstat */ +}; + |
