xref: /plan9-contrib/sys/src/9/mtx/kbd.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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 	Data=		0x60,		/* data port */
11 
12 	Status=		0x64,		/* status port */
13 	 Inready=	0x01,		/*  input character ready */
14 	 Outbusy=	0x02,		/*  output busy */
15 	 Sysflag=	0x04,		/*  system flag */
16 	 Cmddata=	0x08,		/*  cmd==0, data==1 */
17 	 Inhibit=	0x10,		/*  keyboard/mouse inhibited */
18 	 Minready=	0x20,		/*  mouse character ready */
19 	 Rtimeout=	0x40,		/*  general timeout */
20 	 Parity=	0x80,
21 
22 	Cmd=		0x64,		/* command port (write only) */
23 
24 	Spec=		0x80,
25 
26 	PF=		Spec|0x20,	/* num pad function key */
27 	View=		Spec|0x00,	/* view (shift window up) */
28 	KF=		0xF000,	/* function key (begin Unicode private space) */
29 	Shift=		Spec|0x60,
30 	Break=		Spec|0x61,
31 	Ctrl=		Spec|0x62,
32 	Latin=		Spec|0x63,
33 	Caps=		Spec|0x64,
34 	Num=		Spec|0x65,
35 	Middle=		Spec|0x66,
36 	No=		0x00,		/* peter */
37 
38 	Home=		KF|13,
39 	Up=		KF|14,
40 	Pgup=		KF|15,
41 	Print=		KF|16,
42 	Left=		KF|17,
43 	Right=		KF|18,
44 	End=		'\r',
45 	Down=		View,
46 	Pgdown=		KF|19,
47 	Ins=		KF|20,
48 	Del=		0x7F,
49 	Scroll=		KF|21,
50 };
51 
52 /*
53  * The codes at 0x79 and 0x81 are produed by the PFU Happy Hacking keyboard.
54  * A 'standard' keyboard doesn't produce anything above 0x58.
55  */
56 Rune kbtab[] =
57 {
58 [0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
59 [0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
60 [0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
61 [0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
62 [0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
63 [0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
64 [0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*',
65 [0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
66 [0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
67 [0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
68 [0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
69 [0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
70 [0x60]	No,	No,	No,	No,	No,	No,	No,	No,
71 [0x68]	No,	No,	No,	No,	No,	No,	No,	No,
72 [0x70]	No,	No,	No,	No,	No,	No,	No,	No,
73 [0x78]	No,	View,	No,	Up,	No,	No,	No,	No,
74 };
75 
76 Rune kbtabshift[] =
77 {
78 [0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
79 [0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
80 [0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
81 [0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
82 [0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
83 [0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
84 [0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*',
85 [0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
86 [0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
87 [0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
88 [0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
89 [0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
90 [0x60]	No,	No,	No,	No,	No,	No,	No,	No,
91 [0x68]	No,	No,	No,	No,	No,	No,	No,	No,
92 [0x70]	No,	No,	No,	No,	No,	No,	No,	No,
93 [0x78]	No,	Up,	No,	Up,	No,	No,	No,	No,
94 };
95 
96 Rune kbtabesc1[] =
97 {
98 [0x00]	No,	No,	No,	No,	No,	No,	No,	No,
99 [0x08]	No,	No,	No,	No,	No,	No,	No,	No,
100 [0x10]	No,	No,	No,	No,	No,	No,	No,	No,
101 [0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
102 [0x20]	No,	No,	No,	No,	No,	No,	No,	No,
103 [0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
104 [0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
105 [0x38]	Latin,	No,	No,	No,	No,	No,	No,	No,
106 [0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
107 [0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
108 [0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
109 [0x58]	No,	No,	No,	No,	No,	No,	No,	No,
110 [0x60]	No,	No,	No,	No,	No,	No,	No,	No,
111 [0x68]	No,	No,	No,	No,	No,	No,	No,	No,
112 [0x70]	No,	No,	No,	No,	No,	No,	No,	No,
113 [0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
114 };
115 
116 enum
117 {
118 	/* controller command byte */
119 	Cscs1=		(1<<6),		/* scan code set 1 */
120 	Cauxdis=	(1<<5),		/* mouse disable */
121 	Ckbddis=	(1<<4),		/* kbd disable */
122 	Csf=		(1<<2),		/* system flag */
123 	Cauxint=	(1<<1),		/* mouse interrupt enable */
124 	Ckbdint=	(1<<0),		/* kbd interrupt enable */
125 };
126 
127 static Lock i8042lock;
128 static uchar ccc;
129 static void (*auxputc)(int, int);
130 
131 /*
132  *  wait for output no longer busy
133  */
134 static int
outready(void)135 outready(void)
136 {
137 	int tries;
138 
139 	for(tries = 0; (inb(Status) & Outbusy); tries++){
140 		if(tries > 500)
141 			return -1;
142 		delay(2);
143 	}
144 	return 0;
145 }
146 
147 /*
148  *  wait for input
149  */
150 static int
inready(void)151 inready(void)
152 {
153 	int tries;
154 
155 	for(tries = 0; !(inb(Status) & Inready); tries++){
156 		if(tries > 500)
157 			return -1;
158 		delay(2);
159 	}
160 	return 0;
161 }
162 
163 /*
164  *  ask 8042 to reset the machine
165  */
166 void
i8042reset(void)167 i8042reset(void)
168 {
169 	ushort *s = KADDR(0x472);
170 	int i, x;
171 
172 	*s = 0x1234;		/* BIOS warm-boot flag */
173 
174 	/*
175 	 *  newer reset the machine command
176 	 */
177 	outready();
178 	outb(Cmd, 0xFE);
179 	outready();
180 
181 	/*
182 	 *  Pulse it by hand (old somewhat reliable)
183 	 */
184 	x = 0xDF;
185 	for(i = 0; i < 5; i++){
186 		x ^= 1;
187 		outready();
188 		outb(Cmd, 0xD1);
189 		outready();
190 		outb(Data, x);	/* toggle reset */
191 		delay(100);
192 	}
193 }
194 
195 int
i8042auxcmd(int cmd)196 i8042auxcmd(int cmd)
197 {
198 	unsigned int c;
199 	int tries;
200 
201 	c = 0;
202 	tries = 0;
203 
204 	ilock(&i8042lock);
205 	do{
206 		if(tries++ > 2)
207 			break;
208 		if(outready() < 0)
209 			break;
210 		outb(Cmd, 0xD4);
211 		if(outready() < 0)
212 			break;
213 		outb(Data, cmd);
214 		if(outready() < 0)
215 			break;
216 		if(inready() < 0)
217 			break;
218 		c = inb(Data);
219 	} while(c == 0xFE || c == 0);
220 	iunlock(&i8042lock);
221 
222 	if(c != 0xFA){
223 		print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
224 		return -1;
225 	}
226 	return 0;
227 }
228 
229 /*
230  *  keyboard interrupt
231  */
232 static void
i8042intr(Ureg *,void *)233 i8042intr(Ureg*, void*)
234 {
235 	int s, c, i;
236 	static int esc1, esc2;
237 	static int alt, caps, ctl, num, shift;
238 	static int collecting, nk;
239 	static Rune kc[5];
240 	int keyup;
241 
242 	/*
243 	 *  get status
244 	 */
245 	lock(&i8042lock);
246 	s = inb(Status);
247 	if(!(s&Inready)){
248 		unlock(&i8042lock);
249 		return;
250 	}
251 
252 	/*
253 	 *  get the character
254 	 */
255 	c = inb(Data);
256 	unlock(&i8042lock);
257 
258 	/*
259 	 *  if it's the aux port...
260 	 */
261 	if(s & Minready){
262 		if(auxputc != nil)
263 			auxputc(c, shift);
264 		return;
265 	}
266 
267 	/*
268 	 *  e0's is the first of a 2 character sequence
269 	 */
270 	if(c == 0xe0){
271 		esc1 = 1;
272 		return;
273 	} else if(c == 0xe1){
274 		esc2 = 2;
275 		return;
276 	}
277 
278 	keyup = c&0x80;
279 	c &= 0x7f;
280 	if(c > sizeof kbtab){
281 		c |= keyup;
282 		if(c != 0xFF)	/* these come fairly often: CAPSLOCK U Y */
283 			print("unknown key %ux\n", c);
284 		return;
285 	}
286 
287 	if(esc1){
288 		c = kbtabesc1[c];
289 		esc1 = 0;
290 	} else if(esc2){
291 		esc2--;
292 		return;
293 	} else if(shift)
294 		c = kbtabshift[c];
295 	else
296 		c = kbtab[c];
297 
298 	if(caps && c<='z' && c>='a')
299 		c += 'A' - 'a';
300 
301 	/*
302 	 *  keyup only important for shifts
303 	 */
304 	if(keyup){
305 		switch(c){
306 		case Latin:
307 			alt = 0;
308 			break;
309 		case Shift:
310 			shift = 0;
311 			break;
312 		case Ctrl:
313 			ctl = 0;
314 			break;
315 		}
316 		return;
317 	}
318 
319 	/*
320  	 *  normal character
321 	 */
322 	if(!(c & (Spec|KF))){
323 		if(ctl){
324 			if(alt && c == Del)
325 				exit(0);
326 			c &= 0x1f;
327 		}
328 		if(!collecting){
329 			kbdputc(kbdq, c);
330 			return;
331 		}
332 		kc[nk++] = c;
333 		c = latin1(kc, nk);
334 		if(c < -1)	/* need more keystrokes */
335 			return;
336 		if(c != -1)	/* valid sequence */
337 			kbdputc(kbdq, c);
338 		else	/* dump characters */
339 			for(i=0; i<nk; i++)
340 				kbdputc(kbdq, kc[i]);
341 		nk = 0;
342 		collecting = 0;
343 		return;
344 	} else {
345 		switch(c){
346 		case Caps:
347 			caps ^= 1;
348 			return;
349 		case Num:
350 			num ^= 1;
351 			return;
352 		case Shift:
353 			shift = 1;
354 			return;
355 		case Latin:
356 			alt = 1;
357 			collecting = 1;
358 			nk = 0;
359 			return;
360 		case Ctrl:
361 			ctl = 1;
362 			return;
363 		}
364 	}
365 	kbdputc(kbdq, c);
366 }
367 
368 void
i8042auxenable(void (* putc)(int,int))369 i8042auxenable(void (*putc)(int, int))
370 {
371 	char *err = "i8042: aux init failed\n";
372 
373 	/* enable kbd/aux xfers and interrupts */
374 	ccc &= ~Cauxdis;
375 	ccc |= Cauxint;
376 
377 	ilock(&i8042lock);
378 	if(outready() < 0)
379 		print(err);
380 	outb(Cmd, 0x60);			/* write control register */
381 	if(outready() < 0)
382 		print(err);
383 	outb(Data, ccc);
384 	if(outready() < 0)
385 		print(err);
386 	outb(Cmd, 0xA8);			/* auxilliary device enable */
387 	if(outready() < 0){
388 		iunlock(&i8042lock);
389 		return;
390 	}
391 	auxputc = putc;
392 	intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
393 	iunlock(&i8042lock);
394 }
395 
396 void
kbdinit(void)397 kbdinit(void)
398 {
399 	int c;
400 
401 	kbdq = qopen(4*1024, 0, 0, 0);
402 	if(kbdq == nil)
403 		panic("kbdinit");
404 	qnoblock(kbdq, 1);
405 
406 	ioalloc(Data, 1, 0, "kbd");
407 	ioalloc(Cmd, 1, 0, "kbd");
408 
409 	intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
410 
411 	/* wait for a quiescent controller */
412 	while((c = inb(Status)) & (Outbusy | Inready))
413 		if(c & Inready)
414 			inb(Data);
415 
416 	/* get current controller command byte */
417 	outb(Cmd, 0x20);
418 	if(inready() < 0){
419 		print("kbdinit: can't read ccc\n");
420 		ccc = 0;
421 	} else
422 		ccc = inb(Data);
423 
424 	/* enable kbd xfers and interrupts */
425 	/* disable mouse */
426 	ccc &= ~Ckbddis;
427 	ccc |= Csf | Ckbdint | Cscs1;
428 	if(outready() < 0)
429 		print("kbd init failed\n");
430 	outb(Cmd, 0x60);
431 	if(outready() < 0)
432 		print("kbd init failed\n");
433 	outb(Data, ccc);
434 	outready();
435 }
436