xref: /plan9/sys/src/cmd/usb/kb/kb.c (revision ed868a7c1f0ac853ba7f6fe1cfc03d41d6ae4523)
183030dd5SDavid du Colombier /*
283030dd5SDavid du Colombier  * USB Human Interaction Device: keyboard and mouse.
383030dd5SDavid du Colombier  *
483030dd5SDavid du Colombier  * If there's no usb keyboard, it tries to setup the mouse, if any.
583030dd5SDavid du Colombier  * It should be started at boot time.
683030dd5SDavid du Colombier  *
783030dd5SDavid du Colombier  * Mouse events are converted to the format of mouse(3)'s
883030dd5SDavid du Colombier  * mousein file.
983030dd5SDavid du Colombier  * Keyboard keycodes are translated to scan codes and sent to kbin(3).
1083030dd5SDavid du Colombier  *
1183030dd5SDavid du Colombier  */
1283030dd5SDavid du Colombier 
1383030dd5SDavid du Colombier #include <u.h>
1483030dd5SDavid du Colombier #include <libc.h>
1583030dd5SDavid du Colombier #include <thread.h>
1683030dd5SDavid du Colombier #include "usb.h"
1783030dd5SDavid du Colombier #include "hid.h"
1883030dd5SDavid du Colombier 
19906943f9SDavid du Colombier enum
20906943f9SDavid du Colombier {
21906943f9SDavid du Colombier 	Awakemsg=0xdeaddead,
22906943f9SDavid du Colombier 	Diemsg = 0xbeefbeef,
23906943f9SDavid du Colombier };
24906943f9SDavid du Colombier 
25906943f9SDavid du Colombier typedef struct KDev KDev;
26906943f9SDavid du Colombier typedef struct Kin Kin;
27906943f9SDavid du Colombier 
28906943f9SDavid du Colombier struct KDev
29906943f9SDavid du Colombier {
30906943f9SDavid du Colombier 	Dev*	dev;		/* usb device*/
31906943f9SDavid du Colombier 	Dev*	ep;		/* endpoint to get events */
32906943f9SDavid du Colombier 	Kin*	in;		/* used to send events to kernel */
33906943f9SDavid du Colombier 	Channel*repeatc;	/* only for keyboard */
34906943f9SDavid du Colombier 	int	accel;		/* only for mouse */
35906943f9SDavid du Colombier };
36906943f9SDavid du Colombier 
37906943f9SDavid du Colombier /*
38906943f9SDavid du Colombier  * Kbdin and mousein files must be shared among all instances.
39906943f9SDavid du Colombier  */
40906943f9SDavid du Colombier struct Kin
41906943f9SDavid du Colombier {
42906943f9SDavid du Colombier 	int	ref;
43906943f9SDavid du Colombier 	int	fd;
44906943f9SDavid du Colombier 	char*	name;
45906943f9SDavid du Colombier };
46906943f9SDavid du Colombier 
4783030dd5SDavid du Colombier /*
4883030dd5SDavid du Colombier  * Map for the logitech bluetooth mouse with 8 buttons and wheels.
4983030dd5SDavid du Colombier  *	{ ptr ->mouse}
5083030dd5SDavid du Colombier  *	{ 0x01, 0x01 },	// left
5183030dd5SDavid du Colombier  *	{ 0x04, 0x02 },	// middle
5283030dd5SDavid du Colombier  *	{ 0x02, 0x04 },	// right
5383030dd5SDavid du Colombier  *	{ 0x40, 0x08 },	// up
5483030dd5SDavid du Colombier  *	{ 0x80, 0x10 },	// down
5583030dd5SDavid du Colombier  *	{ 0x10, 0x08 },	// side up
5683030dd5SDavid du Colombier  *	{ 0x08, 0x10 },	// side down
5783030dd5SDavid du Colombier  *	{ 0x20, 0x02 }, // page
5883030dd5SDavid du Colombier  * besides wheel and regular up/down report the 4th byte as 1/-1
5983030dd5SDavid du Colombier  */
6083030dd5SDavid du Colombier 
6183030dd5SDavid du Colombier /*
6283030dd5SDavid du Colombier  * key code to scan code; for the page table used by
6383030dd5SDavid du Colombier  * the logitech bluetooth keyboard.
6483030dd5SDavid du Colombier  */
65906943f9SDavid du Colombier static char sctab[256] =
6683030dd5SDavid du Colombier {
6783030dd5SDavid du Colombier [0x00]	0x0,	0x0,	0x0,	0x0,	0x1e,	0x30,	0x2e,	0x20,
6883030dd5SDavid du Colombier [0x08]	0x12,	0x21,	0x22,	0x23,	0x17,	0x24,	0x25,	0x26,
6983030dd5SDavid du Colombier [0x10]	0x32,	0x31,	0x18,	0x19,	0x10,	0x13,	0x1f,	0x14,
7083030dd5SDavid du Colombier [0x18]	0x16,	0x2f,	0x11,	0x2d,	0x15,	0x2c,	0x2,	0x3,
7183030dd5SDavid du Colombier [0x20]	0x4,	0x5,	0x6,	0x7,	0x8,	0x9,	0xa,	0xb,
7283030dd5SDavid du Colombier [0x28]	0x1c,	0x1,	0xe,	0xf,	0x39,	0xc,	0xd,	0x1a,
7383030dd5SDavid du Colombier [0x30]	0x1b,	0x2b,	0x2b,	0x27,	0x28,	0x29,	0x33,	0x34,
7483030dd5SDavid du Colombier [0x38]	0x35,	0x3a,	0x3b,	0x3c,	0x3d,	0x3e,	0x3f,	0x40,
7583030dd5SDavid du Colombier [0x40]	0x41,	0x42,	0x43,	0x44,	0x57,	0x58,	0x63,	0x46,
7683030dd5SDavid du Colombier [0x48]	0x77,	0x52,	0x47,	0x49,	0x53,	0x4f,	0x51,	0x4d,
7783030dd5SDavid du Colombier [0x50]	0x4b,	0x50,	0x48,	0x45,	0x35,	0x37,	0x4a,	0x4e,
7883030dd5SDavid du Colombier [0x58]	0x1c,	0x4f,	0x50,	0x51,	0x4b,	0x4c,	0x4d,	0x47,
7983030dd5SDavid du Colombier [0x60]	0x48,	0x49,	0x52,	0x53,	0x56,	0x7f,	0x74,	0x75,
8083030dd5SDavid du Colombier [0x68]	0x55,	0x59,	0x5a,	0x5b,	0x5c,	0x5d,	0x5e,	0x5f,
8183030dd5SDavid du Colombier [0x70]	0x78,	0x79,	0x7a,	0x7b,	0x0,	0x0,	0x0,	0x0,
8283030dd5SDavid du Colombier [0x78]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x71,
8383030dd5SDavid du Colombier [0x80]	0x73,	0x72,	0x0,	0x0,	0x0,	0x7c,	0x0,	0x0,
8483030dd5SDavid du Colombier [0x88]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
8583030dd5SDavid du Colombier [0x90]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
8683030dd5SDavid du Colombier [0x98]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
8783030dd5SDavid du Colombier [0xa0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
8883030dd5SDavid du Colombier [0xa8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
8983030dd5SDavid du Colombier [0xb0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9083030dd5SDavid du Colombier [0xb8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9183030dd5SDavid du Colombier [0xc0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9283030dd5SDavid du Colombier [0xc8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9383030dd5SDavid du Colombier [0xd0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9483030dd5SDavid du Colombier [0xd8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9583030dd5SDavid du Colombier [0xe0]	0x1d,	0x2a,	0x38,	0x7d,	0x61,	0x36,	0x64,	0x7e,
9683030dd5SDavid du Colombier [0xe8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x73,	0x72,	0x71,
9783030dd5SDavid du Colombier [0xf0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9883030dd5SDavid du Colombier [0xf8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9983030dd5SDavid du Colombier };
10083030dd5SDavid du Colombier 
101906943f9SDavid du Colombier static QLock inlck;
102906943f9SDavid du Colombier static Kin kbdin =
103906943f9SDavid du Colombier {
104906943f9SDavid du Colombier 	.ref = 0,
105906943f9SDavid du Colombier 	.name = "#Ι/kbin",
106906943f9SDavid du Colombier 	.fd = -1,
107906943f9SDavid du Colombier };
108906943f9SDavid du Colombier static Kin ptrin =
109906943f9SDavid du Colombier {
110906943f9SDavid du Colombier 	.ref = 0,
111906943f9SDavid du Colombier 	.name = "#m/mousein",
112906943f9SDavid du Colombier 	.fd = -1,
11383030dd5SDavid du Colombier };
11483030dd5SDavid du Colombier 
115906943f9SDavid du Colombier static int kbdebug;
11683030dd5SDavid du Colombier 
117d5789509SDavid du Colombier static int
118d5789509SDavid du Colombier setbootproto(KDev* f, int eid)
119d5789509SDavid du Colombier {
120d5789509SDavid du Colombier 	int r, id;
121d5789509SDavid du Colombier 
122d5789509SDavid du Colombier 	r = Rh2d|Rclass|Riface;
123d5789509SDavid du Colombier 	id = f->dev->usb->ep[eid]->iface->id;
124d5789509SDavid du Colombier 	return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
125d5789509SDavid du Colombier }
126d5789509SDavid du Colombier 
127d5789509SDavid du Colombier /*
128d5789509SDavid du Colombier  * Try to recover from a babble error. A port reset is the only way out.
129d5789509SDavid du Colombier  * BUG: we should be careful not to reset a bundle with several devices.
130d5789509SDavid du Colombier  */
131d5789509SDavid du Colombier static void
132d5789509SDavid du Colombier recoverkb(KDev *f)
133d5789509SDavid du Colombier {
134d5789509SDavid du Colombier 	int i;
135d5789509SDavid du Colombier 
136d5789509SDavid du Colombier 	close(f->dev->dfd);		/* it's for usbd now */
137d5789509SDavid du Colombier 	devctl(f->dev, "reset");
138d5789509SDavid du Colombier 	for(i = 0; i < 10; i++){
139d5789509SDavid du Colombier 		sleep(500);
140d5789509SDavid du Colombier 		if(opendevdata(f->dev, ORDWR) >= 0){
141d5789509SDavid du Colombier 			setbootproto(f, f->ep->id);
142d5789509SDavid du Colombier 			break;
143d5789509SDavid du Colombier 		}
144d5789509SDavid du Colombier 		/* else usbd still working... */
145d5789509SDavid du Colombier 	}
146d5789509SDavid du Colombier }
147d5789509SDavid du Colombier 
148906943f9SDavid du Colombier static void
149906943f9SDavid du Colombier kbfatal(KDev *kd, char *sts)
15083030dd5SDavid du Colombier {
151906943f9SDavid du Colombier 	Dev *dev;
15283030dd5SDavid du Colombier 
153906943f9SDavid du Colombier 	if(sts != nil)
15439dc1420SDavid du Colombier 		fprint(2, "kb: fatal: %s\n", sts);
15539dc1420SDavid du Colombier 	else
15639dc1420SDavid du Colombier 		fprint(2, "kb: exiting\n");
157906943f9SDavid du Colombier 	if(kd->repeatc != nil)
15839dc1420SDavid du Colombier 		nbsendul(kd->repeatc, Diemsg);
15939dc1420SDavid du Colombier 	dev = kd->dev;
16039dc1420SDavid du Colombier 	kd->dev = nil;
16139dc1420SDavid du Colombier 	if(kd->ep != nil)
162906943f9SDavid du Colombier 		closedev(kd->ep);
163906943f9SDavid du Colombier 	kd->ep = nil;
16439dc1420SDavid du Colombier 	devctl(dev, "detach");
165906943f9SDavid du Colombier 	closedev(dev);
166906943f9SDavid du Colombier 	/*
167906943f9SDavid du Colombier 	 * free(kd); done by closedev.
168906943f9SDavid du Colombier 	 */
169906943f9SDavid du Colombier 	threadexits(sts);
17083030dd5SDavid du Colombier }
17183030dd5SDavid du Colombier 
17283030dd5SDavid du Colombier static int
173906943f9SDavid du Colombier scale(KDev *f, int x)
17483030dd5SDavid du Colombier {
17583030dd5SDavid du Colombier 	int sign = 1;
17683030dd5SDavid du Colombier 
17783030dd5SDavid du Colombier 	if(x < 0){
17883030dd5SDavid du Colombier 		sign = -1;
17983030dd5SDavid du Colombier 		x = -x;
18083030dd5SDavid du Colombier 	}
18183030dd5SDavid du Colombier 	switch(x){
18283030dd5SDavid du Colombier 	case 0:
18383030dd5SDavid du Colombier 	case 1:
18483030dd5SDavid du Colombier 	case 2:
18583030dd5SDavid du Colombier 	case 3:
18683030dd5SDavid du Colombier 		break;
18783030dd5SDavid du Colombier 	case 4:
188906943f9SDavid du Colombier 		x = 6 + (f->accel>>2);
18983030dd5SDavid du Colombier 		break;
19083030dd5SDavid du Colombier 	case 5:
191906943f9SDavid du Colombier 		x = 9 + (f->accel>>1);
19283030dd5SDavid du Colombier 		break;
19383030dd5SDavid du Colombier 	default:
19483030dd5SDavid du Colombier 		x *= MaxAcc;
19583030dd5SDavid du Colombier 		break;
19683030dd5SDavid du Colombier 	}
19783030dd5SDavid du Colombier 	return sign*x;
19883030dd5SDavid du Colombier }
19983030dd5SDavid du Colombier 
200906943f9SDavid du Colombier /*
201906943f9SDavid du Colombier  * ps2 mouse is processed mostly at interrupt time.
202906943f9SDavid du Colombier  * for usb we do what we can.
203906943f9SDavid du Colombier  */
204906943f9SDavid du Colombier static void
205906943f9SDavid du Colombier sethipri(void)
206906943f9SDavid du Colombier {
207906943f9SDavid du Colombier 	char fn[30];
208906943f9SDavid du Colombier 	int fd;
209906943f9SDavid du Colombier 
210906943f9SDavid du Colombier 	snprint(fn, sizeof(fn), "/proc/%d/ctl", getpid());
211906943f9SDavid du Colombier 	fd = open(fn, OWRITE);
212906943f9SDavid du Colombier 	if(fd < 0)
213906943f9SDavid du Colombier 		return;
214906943f9SDavid du Colombier 	fprint(fd, "pri 13");
215906943f9SDavid du Colombier 	close(fd);
216906943f9SDavid du Colombier }
217906943f9SDavid du Colombier 
218906943f9SDavid du Colombier static void
21983030dd5SDavid du Colombier ptrwork(void* a)
22083030dd5SDavid du Colombier {
22183030dd5SDavid du Colombier 	static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7};
222906943f9SDavid du Colombier 	int x, y, b, c, ptrfd;
223d5789509SDavid du Colombier 	int	mfd, nerrs;
224906943f9SDavid du Colombier 	char	buf[32];
225906943f9SDavid du Colombier 	char	mbuf[80];
226906943f9SDavid du Colombier 	KDev*	f = a;
227906943f9SDavid du Colombier 	int	hipri;
22883030dd5SDavid du Colombier 
229d5789509SDavid du Colombier 	hipri = nerrs = 0;
230906943f9SDavid du Colombier 	ptrfd = f->ep->dfd;
231906943f9SDavid du Colombier 	mfd = f->in->fd;
232906943f9SDavid du Colombier 
233906943f9SDavid du Colombier 	if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
23439dc1420SDavid du Colombier 		kbfatal(f, "weird mouse maxpkt");
23583030dd5SDavid du Colombier 	for(;;){
23683030dd5SDavid du Colombier 		memset(buf, 0, sizeof buf);
23739dc1420SDavid du Colombier 		if(f->ep == nil)
23839dc1420SDavid du Colombier 			kbfatal(f, nil);
239906943f9SDavid du Colombier 		c = read(ptrfd, buf, f->ep->maxpkt);
24039dc1420SDavid du Colombier 		assert(f->dev != nil);
24139dc1420SDavid du Colombier 		assert(f->ep != nil);
242d5789509SDavid du Colombier 		if(c < 0){
24339dc1420SDavid du Colombier 			dprint(2, "kb: mouse: %s: read: %r\n", f->ep->dir);
244d5789509SDavid du Colombier 			if(++nerrs < 3){
245d5789509SDavid du Colombier 				recoverkb(f);
246d5789509SDavid du Colombier 				continue;
247d5789509SDavid du Colombier 			}
248d5789509SDavid du Colombier 		}
249906943f9SDavid du Colombier 		if(c <= 0)
250906943f9SDavid du Colombier 			kbfatal(f, nil);
25183030dd5SDavid du Colombier 		if(c < 3)
25283030dd5SDavid du Colombier 			continue;
253906943f9SDavid du Colombier 		if(f->accel){
254906943f9SDavid du Colombier 			x = scale(f, buf[1]);
255906943f9SDavid du Colombier 			y = scale(f, buf[2]);
25683030dd5SDavid du Colombier 		}else{
25783030dd5SDavid du Colombier 			x = buf[1];
25883030dd5SDavid du Colombier 			y = buf[2];
25983030dd5SDavid du Colombier 		}
26083030dd5SDavid du Colombier 		b = maptab[buf[0] & 0x7];
26183030dd5SDavid du Colombier 		if(c > 3 && buf[3] == 1)	/* up */
26283030dd5SDavid du Colombier 			b |= 0x08;
26383030dd5SDavid du Colombier 		if(c > 3 && buf[3] == -1)	/* down */
26483030dd5SDavid du Colombier 			b |= 0x10;
265d5789509SDavid du Colombier 		if(kbdebug > 1)
26639dc1420SDavid du Colombier 			fprint(2, "kb: m%11d %11d %11d\n", x, y, b);
267906943f9SDavid du Colombier 		seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b);
26839dc1420SDavid du Colombier 		if(write(mfd, mbuf, strlen(mbuf)) < 0)
26939dc1420SDavid du Colombier 			kbfatal(f, "mousein i/o");
270906943f9SDavid du Colombier 		if(hipri == 0){
271906943f9SDavid du Colombier 			sethipri();
272906943f9SDavid du Colombier 			hipri = 1;
27383030dd5SDavid du Colombier 		}
27483030dd5SDavid du Colombier 	}
27583030dd5SDavid du Colombier }
27683030dd5SDavid du Colombier 
27783030dd5SDavid du Colombier static void
278906943f9SDavid du Colombier stoprepeat(KDev *f)
27983030dd5SDavid du Colombier {
280906943f9SDavid du Colombier 	sendul(f->repeatc, Awakemsg);
28183030dd5SDavid du Colombier }
28283030dd5SDavid du Colombier 
28383030dd5SDavid du Colombier static void
284906943f9SDavid du Colombier startrepeat(KDev *f, uchar esc1, uchar sc)
28583030dd5SDavid du Colombier {
28683030dd5SDavid du Colombier 	ulong c;
28783030dd5SDavid du Colombier 
28883030dd5SDavid du Colombier 	if(esc1)
28983030dd5SDavid du Colombier 		c = SCesc1 << 8 | (sc & 0xff);
29083030dd5SDavid du Colombier 	else
29183030dd5SDavid du Colombier 		c = sc;
292906943f9SDavid du Colombier 	sendul(f->repeatc, c);
29383030dd5SDavid du Colombier }
29483030dd5SDavid du Colombier 
29583030dd5SDavid du Colombier static void
296906943f9SDavid du Colombier putscan(int kbinfd, uchar esc, uchar sc)
29783030dd5SDavid du Colombier {
298906943f9SDavid du Colombier 	uchar s[2] = {SCesc1, 0};
29983030dd5SDavid du Colombier 
30083030dd5SDavid du Colombier 	if(sc == 0x41){
301d5789509SDavid du Colombier 		kbdebug += 2;
30283030dd5SDavid du Colombier 		return;
30383030dd5SDavid du Colombier 	}
30483030dd5SDavid du Colombier 	if(sc == 0x42){
305906943f9SDavid du Colombier 		kbdebug = 0;
30683030dd5SDavid du Colombier 		return;
30783030dd5SDavid du Colombier 	}
308906943f9SDavid du Colombier 	if(kbdebug)
30983030dd5SDavid du Colombier 		fprint(2, "sc: %x %x\n", (esc? SCesc1: 0), sc);
31083030dd5SDavid du Colombier 	s[1] = sc;
31183030dd5SDavid du Colombier 	if(esc && sc != 0)
31283030dd5SDavid du Colombier 		write(kbinfd, s, 2);
31383030dd5SDavid du Colombier 	else if(sc != 0)
31483030dd5SDavid du Colombier 		write(kbinfd, s+1, 1);
31583030dd5SDavid du Colombier }
31683030dd5SDavid du Colombier 
31783030dd5SDavid du Colombier static void
318906943f9SDavid du Colombier repeatproc(void* a)
31983030dd5SDavid du Colombier {
320906943f9SDavid du Colombier 	KDev *f;
321906943f9SDavid du Colombier 	Channel *repeatc;
322906943f9SDavid du Colombier 	int kbdinfd;
323906943f9SDavid du Colombier 	ulong l, t, i;
32483030dd5SDavid du Colombier 	uchar esc1, sc;
32583030dd5SDavid du Colombier 
326906943f9SDavid du Colombier 	/*
327906943f9SDavid du Colombier 	 * too many jumps here.
328906943f9SDavid du Colombier 	 * Rewrite instead of debug, if needed.
329906943f9SDavid du Colombier 	 */
330906943f9SDavid du Colombier 	f = a;
331906943f9SDavid du Colombier 	repeatc = f->repeatc;
332906943f9SDavid du Colombier 	kbdinfd = f->in->fd;
333906943f9SDavid du Colombier 	l = Awakemsg;
334906943f9SDavid du Colombier Repeat:
335906943f9SDavid du Colombier 	if(l == Diemsg)
336906943f9SDavid du Colombier 		goto Abort;
337906943f9SDavid du Colombier 	while(l == Awakemsg)
338906943f9SDavid du Colombier 		l = recvul(repeatc);
339906943f9SDavid du Colombier 	if(l == Diemsg)
340906943f9SDavid du Colombier 		goto Abort;
34183030dd5SDavid du Colombier 	esc1 = l >> 8;
34283030dd5SDavid du Colombier 	sc = l;
343906943f9SDavid du Colombier 	t = 160;
34483030dd5SDavid du Colombier 	for(;;){
345906943f9SDavid du Colombier 		for(i = 0; i < t; i += 5){
346906943f9SDavid du Colombier 			if(l = nbrecvul(repeatc))
347906943f9SDavid du Colombier 				goto Repeat;
348906943f9SDavid du Colombier 			sleep(5);
34983030dd5SDavid du Colombier 		}
350906943f9SDavid du Colombier 		putscan(kbdinfd, esc1, sc);
351906943f9SDavid du Colombier 		t = 30;
35283030dd5SDavid du Colombier 	}
353906943f9SDavid du Colombier Abort:
354906943f9SDavid du Colombier 	chanfree(repeatc);
355906943f9SDavid du Colombier 	threadexits("aborted");
356906943f9SDavid du Colombier 
35783030dd5SDavid du Colombier }
35883030dd5SDavid du Colombier 
35983030dd5SDavid du Colombier 
36083030dd5SDavid du Colombier #define hasesc1(sc)	(((sc) > 0x47) || ((sc) == 0x38))
36183030dd5SDavid du Colombier 
36283030dd5SDavid du Colombier static void
363906943f9SDavid du Colombier putmod(int fd, uchar mods, uchar omods, uchar mask, uchar esc, uchar sc)
36483030dd5SDavid du Colombier {
365906943f9SDavid du Colombier 	/* BUG: Should be a single write */
36683030dd5SDavid du Colombier 	if((mods&mask) && !(omods&mask))
367906943f9SDavid du Colombier 		putscan(fd, esc, sc);
36883030dd5SDavid du Colombier 	if(!(mods&mask) && (omods&mask))
369906943f9SDavid du Colombier 		putscan(fd, esc, Keyup|sc);
37083030dd5SDavid du Colombier }
37183030dd5SDavid du Colombier 
37283030dd5SDavid du Colombier /*
37383030dd5SDavid du Colombier  * This routine diffs the state with the last known state
37483030dd5SDavid du Colombier  * and invents the scan codes that would have been sent
37583030dd5SDavid du Colombier  * by a non-usb keyboard in that case. This also requires supplying
37683030dd5SDavid du Colombier  * the extra esc1 byte as well as keyup flags.
37783030dd5SDavid du Colombier  * The aim is to allow future addition of other keycode pages
37883030dd5SDavid du Colombier  * for other keyboards.
37983030dd5SDavid du Colombier  */
380906943f9SDavid du Colombier static uchar
381906943f9SDavid du Colombier putkeys(KDev *f, uchar buf[], uchar obuf[], int n, uchar dk)
38283030dd5SDavid du Colombier {
38383030dd5SDavid du Colombier 	int i, j;
384906943f9SDavid du Colombier 	uchar uk;
385906943f9SDavid du Colombier 	int fd;
38683030dd5SDavid du Colombier 
387906943f9SDavid du Colombier 	fd = f->in->fd;
388906943f9SDavid du Colombier 	putmod(fd, buf[0], obuf[0], Mctrl, 0, SCctrl);
389906943f9SDavid du Colombier 	putmod(fd, buf[0], obuf[0], (1<<Mlshift), 0, SClshift);
390906943f9SDavid du Colombier 	putmod(fd, buf[0], obuf[0], (1<<Mrshift), 0, SCrshift);
391906943f9SDavid du Colombier 	putmod(fd, buf[0], obuf[0], Mcompose, 0, SCcompose);
392906943f9SDavid du Colombier 	putmod(fd, buf[0], obuf[0], Maltgr, 1, SCcompose);
39383030dd5SDavid du Colombier 
39483030dd5SDavid du Colombier 	/* Report key downs */
39583030dd5SDavid du Colombier 	for(i = 2; i < n; i++){
39683030dd5SDavid du Colombier 		for(j = 2; j < n; j++)
39783030dd5SDavid du Colombier 			if(buf[i] == obuf[j])
39883030dd5SDavid du Colombier 			 	break;
39983030dd5SDavid du Colombier 		if(j == n && buf[i] != 0){
400906943f9SDavid du Colombier 			dk = sctab[buf[i]];
401906943f9SDavid du Colombier 			putscan(fd, hasesc1(dk), dk);
402906943f9SDavid du Colombier 			startrepeat(f, hasesc1(dk), dk);
40383030dd5SDavid du Colombier 		}
40483030dd5SDavid du Colombier 	}
40583030dd5SDavid du Colombier 
40683030dd5SDavid du Colombier 	/* Report key ups */
407906943f9SDavid du Colombier 	uk = 0;
40883030dd5SDavid du Colombier 	for(i = 2; i < n; i++){
40983030dd5SDavid du Colombier 		for(j = 2; j < n; j++)
41083030dd5SDavid du Colombier 			if(obuf[i] == buf[j])
41183030dd5SDavid du Colombier 				break;
41283030dd5SDavid du Colombier 		if(j == n && obuf[i] != 0){
413906943f9SDavid du Colombier 			uk = sctab[obuf[i]];
414906943f9SDavid du Colombier 			putscan(fd, hasesc1(uk), uk|Keyup);
41583030dd5SDavid du Colombier 		}
41683030dd5SDavid du Colombier 	}
417906943f9SDavid du Colombier 	if(uk && (dk == 0 || dk == uk)){
418906943f9SDavid du Colombier 		stoprepeat(f);
419906943f9SDavid du Colombier 		dk = 0;
420906943f9SDavid du Colombier 	}
421906943f9SDavid du Colombier 	return dk;
42283030dd5SDavid du Colombier }
42383030dd5SDavid du Colombier 
42483030dd5SDavid du Colombier static int
42583030dd5SDavid du Colombier kbdbusy(uchar* buf, int n)
42683030dd5SDavid du Colombier {
42783030dd5SDavid du Colombier 	int i;
42883030dd5SDavid du Colombier 
42983030dd5SDavid du Colombier 	for(i = 1; i < n; i++)
43083030dd5SDavid du Colombier 		if(buf[i] == 0 || buf[i] != buf[0])
43183030dd5SDavid du Colombier 			return 0;
43283030dd5SDavid du Colombier 	return 1;
43383030dd5SDavid du Colombier }
43483030dd5SDavid du Colombier 
435906943f9SDavid du Colombier static void
43683030dd5SDavid du Colombier kbdwork(void *a)
43783030dd5SDavid du Colombier {
438a23bc242SDavid du Colombier 	int c, i, kbdfd, nerrs;
439a23bc242SDavid du Colombier 	uchar dk, buf[64], lbuf[64];
440a23bc242SDavid du Colombier 	char err[128];
441906943f9SDavid du Colombier 	KDev *f = a;
44283030dd5SDavid du Colombier 
443906943f9SDavid du Colombier 	kbdfd = f->ep->dfd;
444906943f9SDavid du Colombier 
445906943f9SDavid du Colombier 	if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
446906943f9SDavid du Colombier 		kbfatal(f, "weird maxpkt");
447906943f9SDavid du Colombier 
448906943f9SDavid du Colombier 	f->repeatc = chancreate(sizeof(ulong), 0);
449906943f9SDavid du Colombier 	if(f->repeatc == nil)
450906943f9SDavid du Colombier 		kbfatal(f, "chancreate failed");
451906943f9SDavid du Colombier 
452906943f9SDavid du Colombier 	proccreate(repeatproc, f, Stack);
45383030dd5SDavid du Colombier 	memset(lbuf, 0, sizeof lbuf);
454a23bc242SDavid du Colombier 	dk = nerrs = 0;
45583030dd5SDavid du Colombier 	for(;;){
45683030dd5SDavid du Colombier 		memset(buf, 0, sizeof buf);
457906943f9SDavid du Colombier 		c = read(kbdfd, buf, f->ep->maxpkt);
45839dc1420SDavid du Colombier 		assert(f->dev != nil);
45939dc1420SDavid du Colombier 		assert(f->ep != nil);
460a23bc242SDavid du Colombier 		if(c < 0){
461a23bc242SDavid du Colombier 			rerrstr(err, sizeof(err));
462d5789509SDavid du Colombier 			fprint(2, "kb: %s: read: %s\n", f->ep->dir, err);
463a23bc242SDavid du Colombier 			if(strstr(err, "babble") != 0 && ++nerrs < 3){
464a23bc242SDavid du Colombier 				recoverkb(f);
465a23bc242SDavid du Colombier 				continue;
466a23bc242SDavid du Colombier 			}
467a23bc242SDavid du Colombier 		}
468906943f9SDavid du Colombier 		if(c <= 0)
46939dc1420SDavid du Colombier 			kbfatal(f, nil);
47083030dd5SDavid du Colombier 		if(c < 3)
47183030dd5SDavid du Colombier 			continue;
47283030dd5SDavid du Colombier 		if(kbdbusy(buf + 2, c - 2))
47383030dd5SDavid du Colombier 			continue;
474d5789509SDavid du Colombier 		if(usbdebug > 2 || kbdebug > 1){
47583030dd5SDavid du Colombier 			fprint(2, "kbd mod %x: ", buf[0]);
47683030dd5SDavid du Colombier 			for(i = 2; i < c; i++)
47783030dd5SDavid du Colombier 				fprint(2, "kc %x ", buf[i]);
47883030dd5SDavid du Colombier 			fprint(2, "\n");
47983030dd5SDavid du Colombier 		}
480906943f9SDavid du Colombier 		dk = putkeys(f, buf, lbuf, f->ep->maxpkt, dk);
48183030dd5SDavid du Colombier 		memmove(lbuf, buf, c);
482a23bc242SDavid du Colombier 		nerrs = 0;
48383030dd5SDavid du Colombier 	}
48483030dd5SDavid du Colombier }
48583030dd5SDavid du Colombier 
48683030dd5SDavid du Colombier static void
487906943f9SDavid du Colombier freekdev(void *a)
48883030dd5SDavid du Colombier {
489906943f9SDavid du Colombier 	KDev *kd;
49083030dd5SDavid du Colombier 
491906943f9SDavid du Colombier 	kd = a;
492906943f9SDavid du Colombier 	if(kd->in != nil){
493906943f9SDavid du Colombier 		qlock(&inlck);
494906943f9SDavid du Colombier 		if(--kd->in->ref == 0){
495906943f9SDavid du Colombier 			close(kd->in->fd);
496906943f9SDavid du Colombier 			kd->in->fd = -1;
49783030dd5SDavid du Colombier 		}
498906943f9SDavid du Colombier 		qunlock(&inlck);
49983030dd5SDavid du Colombier 	}
50039dc1420SDavid du Colombier 	dprint(2, "freekdev\n");
501906943f9SDavid du Colombier 	free(kd);
50283030dd5SDavid du Colombier }
50383030dd5SDavid du Colombier 
50483030dd5SDavid du Colombier static void
505906943f9SDavid du Colombier kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), int accel)
506906943f9SDavid du Colombier {
507906943f9SDavid du Colombier 	KDev *kd;
508906943f9SDavid du Colombier 
509906943f9SDavid du Colombier 	qlock(&inlck);
510906943f9SDavid du Colombier 	if(in->fd < 0){
511906943f9SDavid du Colombier 		in->fd = open(in->name, OWRITE);
512906943f9SDavid du Colombier 		if(in->fd < 0){
513906943f9SDavid du Colombier 			fprint(2, "kb: %s: %r\n", in->name);
514906943f9SDavid du Colombier 			qunlock(&inlck);
515906943f9SDavid du Colombier 			return;
516906943f9SDavid du Colombier 		}
517906943f9SDavid du Colombier 	}
51839dc1420SDavid du Colombier 	in->ref++;	/* for kd->in = in */
519906943f9SDavid du Colombier 	qunlock(&inlck);
520906943f9SDavid du Colombier 	kd = d->aux = emallocz(sizeof(KDev), 1);
521906943f9SDavid du Colombier 	d->free = freekdev;
522906943f9SDavid du Colombier 	kd->in = in;
523906943f9SDavid du Colombier 	kd->dev = d;
524906943f9SDavid du Colombier 	if(setbootproto(kd, ep->id) < 0){
525906943f9SDavid du Colombier 		fprint(2, "kb: %s: bootproto: %r\n", d->dir);
526906943f9SDavid du Colombier 		return;
527906943f9SDavid du Colombier 	}
528906943f9SDavid du Colombier 	kd->accel = accel;
529906943f9SDavid du Colombier 	kd->ep = openep(d, ep->id);
530906943f9SDavid du Colombier 	if(kd->ep == nil){
531906943f9SDavid du Colombier 		fprint(2, "kb: %s: openep %d: %r\n", d->dir, ep->id);
532906943f9SDavid du Colombier 		return;
533906943f9SDavid du Colombier 	}
534906943f9SDavid du Colombier 	if(opendevdata(kd->ep, OREAD) < 0){
535906943f9SDavid du Colombier 		fprint(2, "kb: %s: opendevdata: %r\n", kd->ep->dir);
536906943f9SDavid du Colombier 		closedev(kd->ep);
537906943f9SDavid du Colombier 		kd->ep = nil;
538906943f9SDavid du Colombier 		return;
539906943f9SDavid du Colombier 	}
540906943f9SDavid du Colombier 
541906943f9SDavid du Colombier 	incref(d);
542906943f9SDavid du Colombier 	proccreate(f, kd, Stack);
543906943f9SDavid du Colombier }
544906943f9SDavid du Colombier 
545906943f9SDavid du Colombier static int
54683030dd5SDavid du Colombier usage(void)
54783030dd5SDavid du Colombier {
548*ed868a7cSDavid du Colombier 	werrstr("usage: usb/kb [-dkm] [-a n] [-N nb]");
549906943f9SDavid du Colombier 	return -1;
55083030dd5SDavid du Colombier }
55183030dd5SDavid du Colombier 
552906943f9SDavid du Colombier int
553906943f9SDavid du Colombier kbmain(Dev *d, int argc, char* argv[])
55483030dd5SDavid du Colombier {
555*ed868a7cSDavid du Colombier 	int i, kena, pena, accel, devid;
556906943f9SDavid du Colombier 	Usbdev *ud;
557906943f9SDavid du Colombier 	Ep *ep;
55883030dd5SDavid du Colombier 
559906943f9SDavid du Colombier 	kena = pena = 1;
560906943f9SDavid du Colombier 	accel = 0;
561*ed868a7cSDavid du Colombier 	devid = d->id;
56283030dd5SDavid du Colombier 	ARGBEGIN{
56383030dd5SDavid du Colombier 	case 'a':
564*ed868a7cSDavid du Colombier 		accel = strtol(EARGF(usage()), nil, 0);
56583030dd5SDavid du Colombier 		break;
56683030dd5SDavid du Colombier 	case 'd':
567906943f9SDavid du Colombier 		kbdebug++;
56883030dd5SDavid du Colombier 		break;
56983030dd5SDavid du Colombier 	case 'k':
570906943f9SDavid du Colombier 		kena = 1;
571906943f9SDavid du Colombier 		pena = 0;
57283030dd5SDavid du Colombier 		break;
57383030dd5SDavid du Colombier 	case 'm':
574906943f9SDavid du Colombier 		kena = 0;
575906943f9SDavid du Colombier 		pena = 1;
57683030dd5SDavid du Colombier 		break;
577*ed868a7cSDavid du Colombier 	case 'N':
578*ed868a7cSDavid du Colombier 		devid = atoi(EARGF(usage()));		/* ignore dev number */
579*ed868a7cSDavid du Colombier 		break;
58083030dd5SDavid du Colombier 	default:
581906943f9SDavid du Colombier 		return usage();
58283030dd5SDavid du Colombier 	}ARGEND;
583906943f9SDavid du Colombier 	if(argc != 0){
584906943f9SDavid du Colombier 		return usage();
58583030dd5SDavid du Colombier 	}
586*ed868a7cSDavid du Colombier 	USED(devid);
587906943f9SDavid du Colombier 	ud = d->usb;
588906943f9SDavid du Colombier 	d->aux = nil;
589906943f9SDavid du Colombier 	dprint(2, "kb: main: dev %s ref %ld\n", d->dir, d->ref);
590906943f9SDavid du Colombier 	for(i = 0; i < nelem(ud->ep); i++){
591906943f9SDavid du Colombier 		if((ep = ud->ep[i]) == nil)
592906943f9SDavid du Colombier 			break;
593906943f9SDavid du Colombier 		if(kena && ep->type == Eintr && ep->dir == Ein)
594906943f9SDavid du Colombier 		if(ep->iface->csp == KbdCSP)
595906943f9SDavid du Colombier 			kbstart(d, ep, &kbdin, kbdwork, accel);
596906943f9SDavid du Colombier 		if(pena && ep->type == Eintr && ep->dir == Ein)
597906943f9SDavid du Colombier 		if(ep->iface->csp == PtrCSP)
598906943f9SDavid du Colombier 			kbstart(d, ep, &ptrin, ptrwork, accel);
599906943f9SDavid du Colombier 	}
600906943f9SDavid du Colombier 	return 0;
60183030dd5SDavid du Colombier }
602