Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
673 lines (612 sloc) 12 KB
#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 */
};