xref: /inferno-os/os/js/kbd.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 
9 enum
10 {
11 	Data=		0x60,	/* data port */
12 
13 	Status=		0x64,	/* status port */
14 	 Inready=	0x01,	/*  input character ready */
15 	 Outbusy=	0x02,	/*  output busy */
16 	 Sysflag=	0x04,	/*  system flag */
17 	 Cmddata=	0x08,	/*  cmd==0, data==1 */
18 	 Inhibit=	0x10,	/*  keyboard/mouse inhibited */
19 	 Minready=	0x20,	/*  mouse character ready */
20 	 Rtimeout=	0x40,	/*  general timeout */
21 	 Parity=	0x80,
22 
23 	Cmd=		0x64,	/* command port (write only) */
24 
25 	CTdata=		0x0,	/* chips & Technologies ps2 data port */
26 	CTstatus=	0x1,	/* chips & Technologies ps2 status port */
27 	 Enable=	1<<7,
28 	 Clear=		1<<6,
29 	 Error=		1<<5,
30 	 Intenable=	1<<4,
31 	 Reset=		1<<3,
32 	 Tready=	1<<2,
33 	 Rready=	1<<1,
34 	 Idle=		1<<0,
35 
36 	Spec=	0x80,
37 
38 	PF=	Spec|0x20,	/* num pad function key */
39 	View=	Spec|0x00,	/* view (shift window up) */
40 	KF=	Spec|0x40,	/* function key */
41 	Shift=	Spec|0x60,
42 	Break=	Spec|0x61,
43 	Ctrl=	Spec|0x62,
44 	Latin=	Spec|0x63,
45 	Caps=	Spec|0x64,
46 	Num=	Spec|0x65,
47 	Middle=	Spec|0x66,
48 	No=	0x00,		/* peter */
49 
50 	Home=	KF|13,
51 	Up=	KF|14,
52 	Pgup=	KF|15,
53 	Print=	KF|16,
54 	Left=	View,
55 	Right=	View,
56 	End=	'\r',
57 	Down=	View,
58 	Pgdown=	View,
59 	Ins=	KF|20,
60 	Del=	0x7F,
61 
62 	Rbutton=4,
63 	Mbutton=2,
64 	Lbutton=1,
65 };
66 
67 uchar
68 kbtab[] = {
69 [0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
70 [0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
71 [0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
72 [0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
73 [0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
74 [0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
75 [0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*',
76 [0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
77 [0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	'7',
78 [0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
79 [0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
80 [0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
81 };
82 
83 uchar
84 kbtabshift[] = {
85 [0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
86 [0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
87 [0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
88 [0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
89 [0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
90 [0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
91 [0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*',
92 [0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
93 [0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	'7',
94 [0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
95 [0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
96 [0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
97 };
98 
99 uchar
100 kbtabesc1[] = {
101 [0x00]	No,	No,	No,	No,	No,	No,	No,	No,
102 [0x08]	No,	No,	No,	No,	No,	No,	No,	No,
103 [0x10]	No,	No,	No,	No,	No,	No,	No,	No,
104 [0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
105 [0x20]	No,	No,	No,	No,	No,	No,	No,	No,
106 [0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
107 [0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
108 [0x38]	Latin,	No,	No,	No,	No,	No,	No,	No,
109 [0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
110 [0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
111 [0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
112 [0x58]	No,	No,	No,	No,	No,	No,	No,	No,
113 };
114 
115 static	int	keybuttons;
116 static	uchar	ccc;
117 static	int	shift;
118 
119 enum
120 {
121 	/* controller command byte */
122 	Cscs1=		(1<<6),		/* scan code set 1 */
123 	Cmousedis=	(1<<5),		/* mouse disable */
124 	Ckbddis=	(1<<4),		/* kbd disable */
125 	Csf=		(1<<2),		/* system flag */
126 	Cmouseint=	(1<<1),		/* mouse interrupt enable */
127 	Ckbdint=	(1<<0),		/* kbd interrupt enable */
128 };
129 
130 /*
131  *  wait for output no longer busy
132  */
133 static int
134 outready(void)
135 {
136 	int tries;
137 
138 	for(tries = 0; (superio_readctl() & Outbusy); tries++){
139 		if(tries > 500)
140 			return -1;
141 		microdelay(2);
142 	}
143 	return 0;
144 }
145 
146 /*
147  *  wait for input
148  */
149 static int
150 inready(void)
151 {
152 	int tries;
153 
154 	for(tries = 0; !(superio_readctl() & Inready); tries++){
155 		if(tries > 500)
156 			return -1;
157 		microdelay(2);
158 	}
159 	return 0;
160 }
161 
162 /*
163  *  send a command to the mouse
164  */
165 static int
166 mousecmd(int cmd)
167 {
168 	unsigned int c;
169 	int tries;
170 
171 	c = 0;
172 	tries = 0;
173 	do{
174 		if(tries++ > 2)
175 			break;
176 		if(outready() < 0)
177 			break;
178 		superio_writectl(0xD4);
179 		if(outready() < 0)
180 			break;
181 		superio_writedata(cmd);
182 		if(outready() < 0)
183 			break;
184 		if(inready() < 0)
185 			break;
186 		c = superio_readdata();
187 	} while(c == 0xFE || c == 0);
188 
189 	if(c != 0xFA){
190 		print("mouse returns %2.2ux to the %2.2ux command\n", c, cmd);
191 		return -1;
192 	}
193 	return 0;
194 }
195 
196 /*
197  *  ask 8042 to enable the use of address bit 20
198  */
199 void
200 i8042a20(void)
201 {
202 	outready();
203 	superio_writectl(0xD1);
204 	outready();
205 	superio_writedata(0xDF);
206 	outready();
207 }
208 
209 /*
210  *  ask 8042 to reset the machine
211  */
212 void
213 i8042reset(void)
214 {
215 	ushort *s = (ushort*)(KZERO|0x472);
216 	int i, x;
217 
218 	*s = 0x1234;		/* BIOS warm-boot flag */
219 
220 	/*
221 	 *  newer reset the machine command
222 	 */
223 	outready();
224 	superio_writectl(0xFE);
225 	outready();
226 
227 	/*
228 	 *  Pulse it by hand (old somewhat reliable)
229 	 */
230 	x = 0xDF;
231 	for(i = 0; i < 5; i++){
232 		x ^= 1;
233 		outready();
234 		superio_writectl(0xD1);
235 		outready();
236 		superio_writedata(x);	/* toggle reset */
237 		microdelay(100);
238 	}
239 }
240 
241 /*
242  *  ps/2 mouse message is three bytes
243  *
244  *	byte 0 -	0 0 SDY SDX 1 M R L
245  *	byte 1 -	DX
246  *	byte 2 -	DY
247  *
248  *  shift & left button is the same as middle button
249  */
250 static int
251 ps2mouseputc(int c)
252 {
253 	static short msg[3];
254 	static int nb;
255 	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
256 	int buttons, dx, dy;
257 
258 	/*
259 	 *  check byte 0 for consistency
260 	 */
261 	if(nb==0 && (c&0xc8)!=0x08)
262 		return 0;
263 
264 	msg[nb] = c;
265 	if(++nb == 3) {
266 		nb = 0;
267 		if(msg[0] & 0x10)
268 			msg[1] |= 0xFF00;
269 		if(msg[0] & 0x20)
270 			msg[2] |= 0xFF00;
271 
272 		buttons = b[(msg[0]&7) | (shift ? 8 : 0)] | keybuttons;
273 		dx = msg[1];
274 		dy = -msg[2];
275 		mousetrack(buttons, dx, dy, 1);
276 	}
277 	return 0;
278 }
279 
280 /*
281  *  keyboard interrupt
282  */
283 void
284 kbdintr(void)
285 {
286 	int s, c, i;
287 	static int esc1, esc2;
288 	static int caps;
289 	static int ctl;
290 	static int num;
291 	static int collecting, nk;
292 	static int alt;
293 	static Rune kc[5];
294 	int keyup;
295 
296 	/*
297 	 *  get status
298 	 */
299 	s = superio_readctl();
300 	if(!(s&Inready))
301 		return;
302 
303 	/*
304 	 *  get the character
305 	 */
306 	c = superio_readdata();
307 
308 	/*
309 	 *  if it's the mouse...
310 	 */
311 	if(s & Minready) {
312 		ps2mouseputc(c);
313 		return;
314 	}
315 
316 	/*
317 	 *  e0's is the first of a 2 character sequence
318 	 */
319 	if(c == 0xe0){
320 		esc1 = 1;
321 		return;
322 	} else if(c == 0xe1){
323 		esc2 = 2;
324 		return;
325 	}
326 
327 	keyup = c&0x80;
328 	c &= 0x7f;
329 	if(c > sizeof kbtab){
330 /*		print("unknown key %ux\n", c|keyup); */
331 		return;
332 	}
333 
334 	if(esc1){
335 		c = kbtabesc1[c];
336 		esc1 = 0;
337 	} else if(esc2){
338 		esc2--;
339 		return;
340 	} else if(shift)
341 		c = kbtabshift[c];
342 	else
343 		c = kbtab[c];
344 
345 	if(caps && c<='z' && c>='a')
346 		c += 'A' - 'a';
347 
348 	/*
349 	 *  keyup only important for shifts
350 	 */
351 	if(keyup){
352 		switch(c){
353 		case Latin:
354 			alt = 0;
355 			break;
356 		case Shift:
357 			shift = 0;
358 			break;
359 		case Ctrl:
360 			ctl = 0;
361 			break;
362 		}
363 		return;
364 	}
365 
366 	/*
367  	 *  normal character
368 	 */
369 	if(!(c & Spec)){
370 		if(ctl){
371 			if(alt && c == Del)
372 				exit(0);
373 			c &= 0x1f;
374 		}
375 		if(!collecting){
376 			kbdputc(kbdq, c);
377 			return;
378 		}
379 		kc[nk++] = c;
380 		c = latin1(kc, nk);
381 		if(c < -1)	/* need more keystrokes */
382 			return;
383 		if(c != -1)	/* valid sequence */
384 			kbdputc(kbdq, c);
385 		else	/* dump characters */
386 			for(i=0; i<nk; i++)
387 				kbdputc(kbdq, kc[i]);
388 		nk = 0;
389 		collecting = 0;
390 		return;
391 	} else {
392 		switch(c){
393 		case Caps:
394 			caps ^= 1;
395 			return;
396 		case Num:
397 			num ^= 1;
398 			return;
399 		case Shift:
400 			shift = 1;
401 			return;
402 		case Latin:
403 			alt = 1;
404 			collecting = 1;
405 			nk = 0;
406 			return;
407 		case Ctrl:
408 			ctl = 1;
409 			return;
410 		}
411 	}
412 	kbdputc(kbdq, c);
413 }
414 
415 /*
416  *  set up a ps2 mouse
417  */
418 static void
419 ps2mouse(void)
420 {
421 	int x;
422 
423 	/* enable kbd/mouse xfers and interrupts */
424 	x = splhi();
425 	ccc &= ~Cmousedis;
426 	ccc |= Cmouseint;
427 	if(outready() < 0)
428 		print("mouse init failed\n");
429 	superio_writectl(0x60);
430 	if(outready() < 0)
431 		print("mouse init failed\n");
432 	superio_writedata(ccc);
433 	if(outready() < 0)
434 		print("mouse init failed\n");
435 	superio_writectl(0xA8);
436 	if(outready() < 0){
437 		splx(x);
438 		return;
439 	}
440 
441 	/* make mouse streaming, enabled */
442 	mousecmd(0xEA);
443 	mousecmd(0xF4);
444 	splx(x);
445 }
446 
447 void
448 kbdinit(void)
449 {
450 	int c;
451 
452 	kbdq = qopen(4*1024, 0, 0, 0);
453 	qnoblock(kbdq, 1);
454 
455 	/* wait for a quiescent controller */
456 	while((c = superio_readctl()) & (Outbusy | Inready))
457 		if(c & Inready)
458 			superio_readdata();
459 
460 	/* get current controller command byte */
461 	superio_writectl(0x20);
462 	if(inready() < 0){
463 		print("kbdinit: can't read ccc\n");
464 		ccc = 0;
465 	} else
466 		ccc = superio_readdata();
467 
468 	/* enable kbd xfers and interrupts */
469 	/* disable mouse */
470 	ccc &= ~Ckbddis;
471 	ccc |= Csf | Ckbdint | Cscs1 | Cmousedis;
472 	if(outready() < 0)
473 		print("kbd init failed\n");
474 	superio_writectl(0x60);
475 	if(outready() < 0)
476 		print("kbd init failed\n");
477 	superio_writedata(ccc);
478 	outready();
479 
480 	/* Assume ps2 mouse */
481 	ps2mouse();
482 }
483