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