xref: /inferno-os/os/boot/pc/kbd.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 #include	"u.h"
2 #include	"lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 
8 enum {
9 	Data=		0x60,	/* data port */
10 
11 	Status=		0x64,	/* status port */
12 	 Inready=	0x01,	/*  input character ready */
13 	 Outbusy=	0x02,	/*  output busy */
14 	 Sysflag=	0x04,	/*  system flag */
15 	 Cmddata=	0x08,	/*  cmd==0, data==1 */
16 	 Inhibit=	0x10,	/*  keyboard/mouse inhibited */
17 	 Minready=	0x20,	/*  mouse character ready */
18 	 Rtimeout=	0x40,	/*  general timeout */
19 	 Parity=	0x80,
20 
21 	Cmd=		0x64,	/* command port (write only) */
22 
23 	Spec=	0x80,
24 
25 	PF=	Spec|0x20,	/* num pad function key */
26 	View=	Spec|0x00,	/* view (shift window up) */
27 	KF=	Spec|0x40,	/* function key */
28 	Shift=	Spec|0x60,
29 	Break=	Spec|0x61,
30 	Ctrl=	Spec|0x62,
31 	Latin=	Spec|0x63,
32 	Caps=	Spec|0x64,
33 	Num=	Spec|0x65,
34 	No=	Spec|0x7F,	/* no mapping */
35 
36 	Home=	KF|13,
37 	Up=	KF|14,
38 	Pgup=	KF|15,
39 	Print=	KF|16,
40 	Left=	View,
41 	Right=	View,
42 	End=	'\r',
43 	Down=	View,
44 	Pgdown=	View,
45 	Ins=	KF|20,
46 	Del=	0x7F,
47 };
48 
49 uchar kbtab[] =
50 {
51 [0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
52 [0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
53 [0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
54 [0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
55 [0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
56 [0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
57 [0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	No,
58 [0x38]	Latin,	' ',	Caps,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
59 [0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	Home,
60 [0x48]	No,	No,	No,	No,	No,	No,	No,	No,
61 [0x50]	No,	No,	No,	No,	No,	No,	No,	KF|11,
62 [0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
63 };
64 
65 uchar kbtabshift[] =
66 {
67 [0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
68 [0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
69 [0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
70 [0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
71 [0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
72 [0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
73 [0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	No,
74 [0x38]	Latin,	' ',	Caps,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
75 [0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	KF|12,	Home,
76 [0x48]	No,	No,	No,	No,	No,	No,	No,	No,
77 [0x50]	No,	No,	No,	No,	No,	No,	No,	KF|11,
78 [0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
79 };
80 
81 uchar kbtabesc1[] =
82 {
83 [0x00]	No,	No,	No,	No,	No,	No,	No,	No,
84 [0x08]	No,	No,	No,	No,	No,	No,	No,	No,
85 [0x10]	No,	No,	No,	No,	No,	No,	No,	No,
86 [0x18]	No,	No,	No,	No,	No,	Ctrl,	No,	No,
87 [0x20]	No,	No,	No,	No,	No,	No,	No,	No,
88 [0x28]	No,	No,	No,	No,	No,	No,	No,	No,
89 [0x30]	No,	No,	No,	No,	No,	No,	No,	Print,
90 [0x38]	Latin,	No,	No,	No,	No,	No,	No,	No,
91 [0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
92 [0x48]	Up,	Pgup,	No,	Down,	No,	Right,	No,	End,
93 [0x50]	Left,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
94 [0x58]	No,	No,	No,	No,	No,	No,	No,	No,
95 };
96 
97 struct latin
98 {
99 	uchar	l;
100 	char	c[2];
101 }latintab[] = {
102 	L'¡',	"!!",	/* spanish initial ! */
103 	L'¢',	"c|",	/* cent */
104 	L'¢',	"c$",	/* cent */
105 	L'£',	"l$",	/* pound sterling */
106 	L'¤',	"g$",	/* general currency */
107 	L'¥',	"y$",	/* yen */
108 	L'¥',	"j$",	/* yen */
109 	L'¦',	"||",	/* broken vertical bar */
110 	L'§',	"SS",	/* section symbol */
111 	L'¨',	"\"\"",	/* dieresis */
112 	L'©',	"cr",	/* copyright */
113 	L'©',	"cO",	/* copyright */
114 	L'ª',	"sa",	/* super a, feminine ordinal */
115 	L'«',	"<<",	/* left angle quotation */
116 	L'¬',	"no",	/* not sign, hooked overbar */
117 	L'­',	"--",	/* soft hyphen */
118 	L'®',	"rg",	/* registered trademark */
119 	L'¯',	"__",	/* macron */
120 	L'°',	"s0",	/* degree (sup o) */
121 	L'±',	"+-",	/* plus-minus */
122 	L'²',	"s2",	/* sup 2 */
123 	L'³',	"s3",	/* sup 3 */
124 	L'´',	"''",	/* grave accent */
125 	L'µ',	"mu",	/* mu */
126 	L'¶',	"pg",	/* paragraph (pilcrow) */
127 	L'·',	"..",	/* centered . */
128 	L'¸',	",,",	/* cedilla */
129 	L'¹',	"s1",	/* sup 1 */
130 	L'º',	"so",	/* sup o */
131 	L'»',	">>",	/* right angle quotation */
132 	L'¼',	"14",	/* 1/4 */
133 	L'½',	"12",	/* 1/2 */
134 	L'¾',	"34",	/* 3/4 */
135 	L'¿',	"??",	/* spanish initial ? */
136 	L'À',	"A`",	/* A grave */
137 	L'Á',	"A'",	/* A acute */
138 	L'Â',	"A^",	/* A circumflex */
139 	L'Ã',	"A~",	/* A tilde */
140 	L'Ä',	"A\"",	/* A dieresis */
141 	L'Ä',	"A:",	/* A dieresis */
142 	L'Å',	"Ao",	/* A circle */
143 	L'Å',	"AO",	/* A circle */
144 	L'Æ',	"Ae",	/* AE ligature */
145 	L'Æ',	"AE",	/* AE ligature */
146 	L'Ç',	"C,",	/* C cedilla */
147 	L'È',	"E`",	/* E grave */
148 	L'É',	"E'",	/* E acute */
149 	L'Ê',	"E^",	/* E circumflex */
150 	L'Ë',	"E\"",	/* E dieresis */
151 	L'Ë',	"E:",	/* E dieresis */
152 	L'Ì',	"I`",	/* I grave */
153 	L'Í',	"I'",	/* I acute */
154 	L'Î',	"I^",	/* I circumflex */
155 	L'Ï',	"I\"",	/* I dieresis */
156 	L'Ï',	"I:",	/* I dieresis */
157 	L'Ð',	"D-",	/* Eth */
158 	L'Ñ',	"N~",	/* N tilde */
159 	L'Ò',	"O`",	/* O grave */
160 	L'Ó',	"O'",	/* O acute */
161 	L'Ô',	"O^",	/* O circumflex */
162 	L'Õ',	"O~",	/* O tilde */
163 	L'Ö',	"O\"",	/* O dieresis */
164 	L'Ö',	"O:",	/* O dieresis */
165 	L'Ö',	"OE",	/* O dieresis */
166 	L'Ö',	"Oe",	/* O dieresis */
167 	L'×',	"xx",	/* times sign */
168 	L'Ø',	"O/",	/* O slash */
169 	L'Ù',	"U`",	/* U grave */
170 	L'Ú',	"U'",	/* U acute */
171 	L'Û',	"U^",	/* U circumflex */
172 	L'Ü',	"U\"",	/* U dieresis */
173 	L'Ü',	"U:",	/* U dieresis */
174 	L'Ü',	"UE",	/* U dieresis */
175 	L'Ü',	"Ue",	/* U dieresis */
176 	L'Ý',	"Y'",	/* Y acute */
177 	L'Þ',	"P|",	/* Thorn */
178 	L'Þ',	"Th",	/* Thorn */
179 	L'Þ',	"TH",	/* Thorn */
180 	L'ß',	"ss",	/* sharp s */
181 	L'à',	"a`",	/* a grave */
182 	L'á',	"a'",	/* a acute */
183 	L'â',	"a^",	/* a circumflex */
184 	L'ã',	"a~",	/* a tilde */
185 	L'ä',	"a\"",	/* a dieresis */
186 	L'ä',	"a:",	/* a dieresis */
187 	L'å',	"ao",	/* a circle */
188 	L'æ',	"ae",	/* ae ligature */
189 	L'ç',	"c,",	/* c cedilla */
190 	L'è',	"e`",	/* e grave */
191 	L'é',	"e'",	/* e acute */
192 	L'ê',	"e^",	/* e circumflex */
193 	L'ë',	"e\"",	/* e dieresis */
194 	L'ë',	"e:",	/* e dieresis */
195 	L'ì',	"i`",	/* i grave */
196 	L'í',	"i'",	/* i acute */
197 	L'î',	"i^",	/* i circumflex */
198 	L'ï',	"i\"",	/* i dieresis */
199 	L'ï',	"i:",	/* i dieresis */
200 	L'ð',	"d-",	/* eth */
201 	L'ñ',	"n~",	/* n tilde */
202 	L'ò',	"o`",	/* o grave */
203 	L'ó',	"o'",	/* o acute */
204 	L'ô',	"o^",	/* o circumflex */
205 	L'õ',	"o~",	/* o tilde */
206 	L'ö',	"o\"",	/* o dieresis */
207 	L'ö',	"o:",	/* o dieresis */
208 	L'ö',	"oe",	/* o dieresis */
209 	L'÷',	"-:",	/* divide sign */
210 	L'ø',	"o/",	/* o slash */
211 	L'ù',	"u`",	/* u grave */
212 	L'ú',	"u'",	/* u acute */
213 	L'û',	"u^",	/* u circumflex */
214 	L'ü',	"u\"",	/* u dieresis */
215 	L'ü',	"u:",	/* u dieresis */
216 	L'ü',	"ue",	/* u dieresis */
217 	L'ý',	"y'",	/* y acute */
218 	L'þ',	"th",	/* thorn */
219 	L'þ',	"p|",	/* thorn */
220 	L'ÿ',	"y\"",	/* y dieresis */
221 	L'ÿ',	"y:",	/* y dieresis */
222 	0,	0,
223 };
224 
225 enum
226 {
227 	/* controller command byte */
228 	Cscs1=		(1<<6),		/* scan code set 1 */
229 	Cmousedis=	(1<<5),		/* mouse disable */
230 	Ckbddis=	(1<<4),		/* kbd disable */
231 	Csf=		(1<<2),		/* system flag */
232 	Cmouseint=	(1<<1),		/* mouse interrupt enable */
233 	Ckbdint=	(1<<0),		/* kbd interrupt enable */
234 };
235 
236 static uchar ccc;
237 
238 int
latin1(int k1,int k2)239 latin1(int k1, int k2)
240 {
241 	struct latin *l;
242 
243 	for(l=latintab; l->l; l++)
244 		if(k1==l->c[0] && k2==l->c[1])
245 			return l->l;
246 	return 0;
247 }
248 
249 /*
250  *  wait for output no longer busy
251  */
252 static int
outready(void)253 outready(void)
254 {
255 	int tries;
256 
257 	for(tries = 0; (inb(Status) & Outbusy); tries++){
258 		if(tries > 500)
259 			return -1;
260 		delay(2);
261 	}
262 	return 0;
263 }
264 
265 /*
266  *  wait for input
267  */
268 static int
inready(void)269 inready(void)
270 {
271 	int tries;
272 
273 	for(tries = 0; !(inb(Status) & Inready); tries++){
274 		if(tries > 500)
275 			return -1;
276 		delay(2);
277 	}
278 	return 0;
279 }
280 
281 /*
282  *  ask 8042 to enable the use of address bit 20
283  */
284 void
i8042a20(void)285 i8042a20(void)
286 {
287 	outready();
288 	outb(Cmd, 0xD1);
289 	outready();
290 	outb(Data, 0xDF);
291 	outready();
292 }
293 
294 /*
295  *  ask 8042 to reset the machine
296  */
297 void
i8042reset(void)298 i8042reset(void)
299 {
300 	int i, x;
301 #ifdef notdef
302 	ushort *s = (ushort*)(KZERO|0x472);
303 
304 	*s = 0x1234;		/* BIOS warm-boot flag */
305 #endif /* notdef */
306 
307 	outready();
308 	outb(Cmd, 0xFE);	/* pulse reset line (means resend on AT&T machines) */
309 	outready();
310 
311 	/*
312 	 *  Pulse it by hand (old somewhat reliable)
313 	 */
314 	x = 0xDF;
315 	for(i = 0; i < 5; i++){
316 		x ^= 1;
317 		outready();
318 		outb(Cmd, 0xD1);
319 		outready();
320 		outb(Data, x);	/* toggle reset */
321 		delay(100);
322 	}
323 }
324 
325 /*
326  *  keyboard interrupt
327  */
328 static void
i8042intr(Ureg *,void *)329 i8042intr(Ureg*, void*)
330 {
331 	int s, c;
332 	static int esc1, esc2;
333 	static int alt, caps, ctl, num, shift;
334 	static int lstate, k1, k2;
335 	int keyup;
336 
337 	/*
338 	 *  get status
339 	 */
340 	s = inb(Status);
341 	if(!(s&Inready))
342 		return;
343 
344 	/*
345 	 *  get the character
346 	 */
347 	c = inb(Data);
348 
349 	/*
350 	 *  if it's the aux port...
351 	 */
352 	if(s & Minready)
353 		return;
354 
355 	/*
356 	 *  e0's is the first of a 2 character sequence
357 	 */
358 	if(c == 0xe0){
359 		esc1 = 1;
360 		return;
361 	} else if(c == 0xe1){
362 		esc2 = 2;
363 		return;
364 	}
365 
366 	keyup = c&0x80;
367 	c &= 0x7f;
368 	if(c > sizeof kbtab){
369 		c |= keyup;
370 		if(c != 0xFF)	/* these come fairly often: CAPSLOCK U Y */
371 			print("unknown key %ux\n", c);
372 		return;
373 	}
374 
375 	if(esc1){
376 		c = kbtabesc1[c];
377 		esc1 = 0;
378 	} else if(esc2){
379 		esc2--;
380 		return;
381 	} else if(shift)
382 		c = kbtabshift[c];
383 	else
384 		c = kbtab[c];
385 
386 	if(caps && c<='z' && c>='a')
387 		c += 'A' - 'a';
388 
389 	/*
390 	 *  keyup only important for shifts
391 	 */
392 	if(keyup){
393 		switch(c){
394 		case Latin:
395 			alt = 0;
396 			break;
397 		case Shift:
398 			shift = 0;
399 			break;
400 		case Ctrl:
401 			ctl = 0;
402 			break;
403 		}
404 		return;
405 	}
406 
407 	/*
408  	 *  normal character
409 	 */
410 	if(!(c & Spec)){
411 		if(ctl){
412 			if(alt && c == Del)
413 				warp86("\nCtrl-Alt-Del\n", 0);
414 			c &= 0x1f;
415 		}
416 		switch(lstate){
417 		case 1:
418 			k1 = c;
419 			lstate = 2;
420 			return;
421 		case 2:
422 			k2 = c;
423 			lstate = 0;
424 			c = latin1(k1, k2);
425 			if(c == 0){
426 				kbdchar(k1);
427 				c = k2;
428 			}
429 			/* fall through */
430 		default:
431 			break;
432 		}
433 	} else {
434 		switch(c){
435 		case Caps:
436 			caps ^= 1;
437 			return;
438 		case Num:
439 			num ^= 1;
440 			return;
441 		case Shift:
442 			shift = 1;
443 			return;
444 		case Latin:
445 			alt = 1;
446 			lstate = 1;
447 			return;
448 		case Ctrl:
449 			ctl = 1;
450 			return;
451 		}
452 	}
453 	kbdchar(c);
454 }
455 
456 static char *initfailed = "kbd init failed\n";
457 
458 void
i8042init(void)459 i8042init(void)
460 {
461 	int c;
462 
463 	/* wait for a quiescent controller */
464 	while((c = inb(Status)) & (Outbusy | Inready))
465 		if(c & Inready)
466 			inb(Data);
467 
468 	/* get current controller command byte */
469 	outb(Cmd, 0x20);
470 	if(inready() < 0){
471 		print("kbdinit: can't read ccc\n");
472 		ccc = 0;
473 	} else
474 		ccc = inb(Data);
475 
476 	/* enable kbd xfers and interrupts */
477 	ccc &= ~Ckbddis;
478 	ccc |= Csf | Ckbdint | Cscs1;
479 	if(outready() < 0)
480 		print(initfailed);
481 	outb(Cmd, 0x60);
482 	if(outready() < 0)
483 		print(initfailed);
484 	outb(Data, ccc);
485 	if(outready() < 0)
486 		print(initfailed);
487 
488 	setvec(VectorKBD, i8042intr, 0);
489 }
490