plan9fox/sys/src/cmd/usb/kb/kb.c
2011-03-30 19:35:09 +03:00

601 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* USB Human Interaction Device: keyboard and mouse.
*
* If there's no usb keyboard, it tries to setup the mouse, if any.
* It should be started at boot time.
*
* Mouse events are converted to the format of mouse(3)'s
* mousein file.
* Keyboard keycodes are translated to scan codes and sent to kbin(3).
*
*/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "usb.h"
#include "hid.h"
enum
{
Awakemsg=0xdeaddead,
Diemsg = 0xbeefbeef,
};
typedef struct KDev KDev;
typedef struct Kin Kin;
struct KDev
{
Dev* dev; /* usb device*/
Dev* ep; /* endpoint to get events */
Kin* in; /* used to send events to kernel */
Channel*repeatc; /* only for keyboard */
int accel; /* only for mouse */
};
/*
* Kbdin and mousein files must be shared among all instances.
*/
struct Kin
{
int ref;
int fd;
char* name;
};
/*
* Map for the logitech bluetooth mouse with 8 buttons and wheels.
* { ptr ->mouse}
* { 0x01, 0x01 }, // left
* { 0x04, 0x02 }, // middle
* { 0x02, 0x04 }, // right
* { 0x40, 0x08 }, // up
* { 0x80, 0x10 }, // down
* { 0x10, 0x08 }, // side up
* { 0x08, 0x10 }, // side down
* { 0x20, 0x02 }, // page
* besides wheel and regular up/down report the 4th byte as 1/-1
*/
/*
* key code to scan code; for the page table used by
* the logitech bluetooth keyboard.
*/
static char sctab[256] =
{
[0x00] 0x0, 0x0, 0x0, 0x0, 0x1e, 0x30, 0x2e, 0x20,
[0x08] 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26,
[0x10] 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14,
[0x18] 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x2, 0x3,
[0x20] 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
[0x28] 0x1c, 0x1, 0xe, 0xf, 0x39, 0xc, 0xd, 0x1a,
[0x30] 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34,
[0x38] 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
[0x40] 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0x63, 0x46,
[0x48] 0x77, 0x52, 0x47, 0x49, 0x53, 0x4f, 0x51, 0x4d,
[0x50] 0x4b, 0x50, 0x48, 0x45, 0x35, 0x37, 0x4a, 0x4e,
[0x58] 0x1c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47,
[0x60] 0x48, 0x49, 0x52, 0x53, 0x56, 0x7f, 0x74, 0x75,
[0x68] 0x55, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
[0x70] 0x78, 0x79, 0x7a, 0x7b, 0x0, 0x0, 0x0, 0x0,
[0x78] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71,
[0x80] 0x73, 0x72, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0,
[0x88] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0x90] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0x98] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xa0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xa8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xb0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xb8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xc0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xc8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xd0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xd8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xe0] 0x1d, 0x2a, 0x38, 0x7d, 0x61, 0x36, 0x64, 0x7e,
[0xe8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x73, 0x72, 0x71,
[0xf0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
[0xf8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
static QLock inlck;
static Kin kbdin =
{
.ref = 0,
.name = "#Ι/kbin",
.fd = -1,
};
static Kin ptrin =
{
.ref = 0,
.name = "#m/mousein",
.fd = -1,
};
static int kbdebug;
static int
setbootproto(KDev* f, int eid)
{
int r, id;
r = Rh2d|Rclass|Riface;
id = f->dev->usb->ep[eid]->iface->id;
return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
}
/*
* Try to recover from a babble error. A port reset is the only way out.
* BUG: we should be careful not to reset a bundle with several devices.
*/
static void
recoverkb(KDev *f)
{
int i;
close(f->dev->dfd); /* it's for usbd now */
devctl(f->dev, "reset");
for(i = 0; i < 10; i++){
sleep(500);
if(opendevdata(f->dev, ORDWR) >= 0){
setbootproto(f, f->ep->id);
break;
}
/* else usbd still working... */
}
}
static void
kbfatal(KDev *kd, char *sts)
{
Dev *dev;
if(sts != nil)
fprint(2, "kb: fatal: %s\n", sts);
else
fprint(2, "kb: exiting\n");
if(kd->repeatc != nil)
nbsendul(kd->repeatc, Diemsg);
dev = kd->dev;
kd->dev = nil;
if(kd->ep != nil)
closedev(kd->ep);
kd->ep = nil;
devctl(dev, "detach");
closedev(dev);
/*
* free(kd); done by closedev.
*/
threadexits(sts);
}
static int
scale(KDev *f, int x)
{
int sign = 1;
if(x < 0){
sign = -1;
x = -x;
}
switch(x){
case 0:
case 1:
case 2:
case 3:
break;
case 4:
x = 6 + (f->accel>>2);
break;
case 5:
x = 9 + (f->accel>>1);
break;
default:
x *= MaxAcc;
break;
}
return sign*x;
}
/*
* ps2 mouse is processed mostly at interrupt time.
* for usb we do what we can.
*/
static void
sethipri(void)
{
char fn[30];
int fd;
snprint(fn, sizeof(fn), "/proc/%d/ctl", getpid());
fd = open(fn, OWRITE);
if(fd < 0)
return;
fprint(fd, "pri 13");
close(fd);
}
static void
ptrwork(void* a)
{
static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7};
int x, y, b, c, ptrfd;
int mfd, nerrs;
char buf[32];
char mbuf[80];
KDev* f = a;
int hipri;
hipri = nerrs = 0;
ptrfd = f->ep->dfd;
mfd = f->in->fd;
if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
kbfatal(f, "weird mouse maxpkt");
for(;;){
memset(buf, 0, sizeof buf);
if(f->ep == nil)
kbfatal(f, nil);
c = read(ptrfd, buf, f->ep->maxpkt);
assert(f->dev != nil);
assert(f->ep != nil);
if(c < 0){
dprint(2, "kb: mouse: %s: read: %r\n", f->ep->dir);
if(++nerrs < 3){
recoverkb(f);
continue;
}
}
if(c <= 0)
kbfatal(f, nil);
if(c < 3)
continue;
if(f->accel){
x = scale(f, buf[1]);
y = scale(f, buf[2]);
}else{
x = buf[1];
y = buf[2];
}
b = maptab[buf[0] & 0x7];
if(c > 3 && buf[3] == 1) /* up */
b |= 0x08;
if(c > 3 && buf[3] == -1) /* down */
b |= 0x10;
if(kbdebug > 1)
fprint(2, "kb: m%11d %11d %11d\n", x, y, b);
seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b);
if(write(mfd, mbuf, strlen(mbuf)) < 0)
kbfatal(f, "mousein i/o");
if(hipri == 0){
sethipri();
hipri = 1;
}
}
}
static void
stoprepeat(KDev *f)
{
sendul(f->repeatc, Awakemsg);
}
static void
startrepeat(KDev *f, uchar esc1, uchar sc)
{
ulong c;
if(esc1)
c = SCesc1 << 8 | (sc & 0xff);
else
c = sc;
sendul(f->repeatc, c);
}
static void
putscan(int kbinfd, uchar esc, uchar sc)
{
uchar s[2] = {SCesc1, 0};
if(sc == 0x41){
kbdebug += 2;
return;
}
if(sc == 0x42){
kbdebug = 0;
return;
}
if(kbdebug)
fprint(2, "sc: %x %x\n", (esc? SCesc1: 0), sc);
s[1] = sc;
if(esc && sc != 0)
write(kbinfd, s, 2);
else if(sc != 0)
write(kbinfd, s+1, 1);
}
static void
repeatproc(void* a)
{
KDev *f;
Channel *repeatc;
int kbdinfd;
ulong l, t, i;
uchar esc1, sc;
/*
* too many jumps here.
* Rewrite instead of debug, if needed.
*/
f = a;
repeatc = f->repeatc;
kbdinfd = f->in->fd;
l = Awakemsg;
Repeat:
if(l == Diemsg)
goto Abort;
while(l == Awakemsg)
l = recvul(repeatc);
if(l == Diemsg)
goto Abort;
esc1 = l >> 8;
sc = l;
t = 160;
for(;;){
for(i = 0; i < t; i += 5){
if(l = nbrecvul(repeatc))
goto Repeat;
sleep(5);
}
putscan(kbdinfd, esc1, sc);
t = 30;
}
Abort:
chanfree(repeatc);
threadexits("aborted");
}
#define hasesc1(sc) (((sc) > 0x47) || ((sc) == 0x38))
static void
putmod(int fd, uchar mods, uchar omods, uchar mask, uchar esc, uchar sc)
{
/* BUG: Should be a single write */
if((mods&mask) && !(omods&mask))
putscan(fd, esc, sc);
if(!(mods&mask) && (omods&mask))
putscan(fd, esc, Keyup|sc);
}
/*
* This routine diffs the state with the last known state
* and invents the scan codes that would have been sent
* by a non-usb keyboard in that case. This also requires supplying
* the extra esc1 byte as well as keyup flags.
* The aim is to allow future addition of other keycode pages
* for other keyboards.
*/
static uchar
putkeys(KDev *f, uchar buf[], uchar obuf[], int n, uchar dk)
{
int i, j;
uchar uk;
int fd;
fd = f->in->fd;
putmod(fd, buf[0], obuf[0], Mctrl, 0, SCctrl);
putmod(fd, buf[0], obuf[0], (1<<Mlshift), 0, SClshift);
putmod(fd, buf[0], obuf[0], (1<<Mrshift), 0, SCrshift);
putmod(fd, buf[0], obuf[0], Mcompose, 0, SCcompose);
putmod(fd, buf[0], obuf[0], Maltgr, 1, SCcompose);
/* Report key downs */
for(i = 2; i < n; i++){
for(j = 2; j < n; j++)
if(buf[i] == obuf[j])
break;
if(j == n && buf[i] != 0){
dk = sctab[buf[i]];
putscan(fd, hasesc1(dk), dk);
startrepeat(f, hasesc1(dk), dk);
}
}
/* Report key ups */
uk = 0;
for(i = 2; i < n; i++){
for(j = 2; j < n; j++)
if(obuf[i] == buf[j])
break;
if(j == n && obuf[i] != 0){
uk = sctab[obuf[i]];
putscan(fd, hasesc1(uk), uk|Keyup);
}
}
if(uk && (dk == 0 || dk == uk)){
stoprepeat(f);
dk = 0;
}
return dk;
}
static int
kbdbusy(uchar* buf, int n)
{
int i;
for(i = 1; i < n; i++)
if(buf[i] == 0 || buf[i] != buf[0])
return 0;
return 1;
}
static void
kbdwork(void *a)
{
int c, i, kbdfd, nerrs;
uchar dk, buf[64], lbuf[64];
char err[128];
KDev *f = a;
kbdfd = f->ep->dfd;
if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
kbfatal(f, "weird maxpkt");
f->repeatc = chancreate(sizeof(ulong), 0);
if(f->repeatc == nil)
kbfatal(f, "chancreate failed");
proccreate(repeatproc, f, Stack);
memset(lbuf, 0, sizeof lbuf);
dk = nerrs = 0;
for(;;){
memset(buf, 0, sizeof buf);
c = read(kbdfd, buf, f->ep->maxpkt);
assert(f->dev != nil);
assert(f->ep != nil);
if(c < 0){
rerrstr(err, sizeof(err));
fprint(2, "kb: %s: read: %s\n", f->ep->dir, err);
if(strstr(err, "babble") != 0 && ++nerrs < 3){
recoverkb(f);
continue;
}
}
if(c <= 0)
kbfatal(f, nil);
if(c < 3)
continue;
if(kbdbusy(buf + 2, c - 2))
continue;
if(usbdebug > 2 || kbdebug > 1){
fprint(2, "kbd mod %x: ", buf[0]);
for(i = 2; i < c; i++)
fprint(2, "kc %x ", buf[i]);
fprint(2, "\n");
}
dk = putkeys(f, buf, lbuf, f->ep->maxpkt, dk);
memmove(lbuf, buf, c);
nerrs = 0;
}
}
static void
freekdev(void *a)
{
KDev *kd;
kd = a;
if(kd->in != nil){
qlock(&inlck);
if(--kd->in->ref == 0){
close(kd->in->fd);
kd->in->fd = -1;
}
qunlock(&inlck);
}
dprint(2, "freekdev\n");
free(kd);
}
static void
kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), int accel)
{
KDev *kd;
qlock(&inlck);
if(in->fd < 0){
in->fd = open(in->name, OWRITE);
if(in->fd < 0){
fprint(2, "kb: %s: %r\n", in->name);
qunlock(&inlck);
return;
}
}
in->ref++; /* for kd->in = in */
qunlock(&inlck);
kd = d->aux = emallocz(sizeof(KDev), 1);
d->free = freekdev;
kd->in = in;
kd->dev = d;
if(setbootproto(kd, ep->id) < 0){
fprint(2, "kb: %s: bootproto: %r\n", d->dir);
return;
}
kd->accel = accel;
kd->ep = openep(d, ep->id);
if(kd->ep == nil){
fprint(2, "kb: %s: openep %d: %r\n", d->dir, ep->id);
return;
}
if(opendevdata(kd->ep, OREAD) < 0){
fprint(2, "kb: %s: opendevdata: %r\n", kd->ep->dir);
closedev(kd->ep);
kd->ep = nil;
return;
}
incref(d);
proccreate(f, kd, Stack);
}
static int
usage(void)
{
werrstr("usage: usb/kb [-dkm] [-a n] [-N nb]");
return -1;
}
int
kbmain(Dev *d, int argc, char* argv[])
{
int i, kena, pena, accel, devid;
Usbdev *ud;
Ep *ep;
kena = pena = 1;
accel = 0;
devid = d->id;
ARGBEGIN{
case 'a':
accel = strtol(EARGF(usage()), nil, 0);
break;
case 'd':
kbdebug++;
break;
case 'k':
kena = 1;
pena = 0;
break;
case 'm':
kena = 0;
pena = 1;
break;
case 'N':
devid = atoi(EARGF(usage())); /* ignore dev number */
break;
default:
return usage();
}ARGEND;
if(argc != 0){
return usage();
}
USED(devid);
ud = d->usb;
d->aux = nil;
dprint(2, "kb: main: dev %s ref %ld\n", d->dir, d->ref);
for(i = 0; i < nelem(ud->ep); i++){
if((ep = ud->ep[i]) == nil)
break;
if(kena && ep->type == Eintr && ep->dir == Ein)
if(ep->iface->csp == KbdCSP)
kbstart(d, ep, &kbdin, kbdwork, accel);
if(pena && ep->type == Eintr && ep->dir == Ein)
if(ep->iface->csp == PtrCSP)
kbstart(d, ep, &ptrin, ptrwork, accel);
}
return 0;
}