xref: /minix/minix/drivers/hid/pckbd/pckbd.c (revision 0a6a1f1d)
1 /* Keyboard driver for PCs and ATs. */
2 #include <minix/drivers.h>
3 #include <minix/input.h>
4 #include <minix/inputdriver.h>
5 
6 #include "pckbd.h"
7 
8 /*
9  * Data that is to be sent to the keyboard. Each byte is ACKed by the keyboard.
10  * This is currently somewhat overpowered for its only purpose: setting LEDs.
11  */
12 static struct kbdout {
13 	unsigned char buf[KBD_OUT_BUFSZ];
14 	int offset;
15 	int avail;
16 	int expect_ack;
17 } kbdout;
18 
19 static int kbd_watchdog_set = 0;
20 static int kbd_alive = 1;
21 static minix_timer_t tmr_kbd_wd;
22 
23 static int irq_hook_id = -1;
24 static int aux_irq_hook_id = -1;
25 
26 static int kbd_state = 0;
27 
28 static unsigned char aux_bytes[3];
29 static unsigned char aux_state = 0;
30 static int aux_counter = 0;
31 static int aux_available = 0;
32 
33 static void pckbd_leds(unsigned int);
34 static void pckbd_intr(unsigned int);
35 static void pckbd_alarm(clock_t);
36 
37 static struct inputdriver pckbd_tab = {
38 	.idr_leds	= pckbd_leds,
39 	.idr_intr	= pckbd_intr,
40 	.idr_alarm	= pckbd_alarm
41 };
42 
43 /*
44  * The watchdog timer function, implementing all but the actual reset.
45  */
46 static void
47 kbd_watchdog(minix_timer_t *UNUSED(tmrp))
48 {
49 	kbd_watchdog_set = 0;
50 	if (!kbdout.avail)
51 		return;	/* Watchdog is no longer needed */
52 
53 	if (!kbd_alive)
54 		printf("PCKBD: watchdog should reset keyboard\n");
55 	kbd_alive = 0;
56 
57 	set_timer(&tmr_kbd_wd, sys_hz(), kbd_watchdog, 0);
58 
59 	kbd_watchdog_set = 1;
60 }
61 
62 /*
63  * Send queued data to the keyboard.
64  */
65 static void
66 kbd_send(void)
67 {
68 	u32_t sb;
69 	int r;
70 
71 	if (!kbdout.avail)
72 		return;
73 	if (kbdout.expect_ack)
74 		return;
75 
76 	if ((r = sys_inb(KB_STATUS, &sb)) != OK)
77 		printf("PCKBD: send sys_inb() failed (1): %d\n", r);
78 
79 	if (sb & (KB_OUT_FULL | KB_IN_FULL)) {
80 		printf("PCKBD: not sending (1): sb = 0x%x\n", sb);
81 		return;
82 	}
83 	micro_delay(KBC_IN_DELAY);
84 	if ((r = sys_inb(KB_STATUS, &sb)) != OK)
85 		printf("PCKBD: send sys_inb() failed (2): %d\n", r);
86 	if (sb & (KB_OUT_FULL | KB_IN_FULL)) {
87 		printf("PCKBD: not sending (2): sb = 0x%x\n", sb);
88 		return;
89 	}
90 
91 	/* Okay, buffer is really empty */
92 	if ((r = sys_outb(KEYBD, kbdout.buf[kbdout.offset])) != OK)
93 		printf("PCKBD: send sys_outb() failed: %d\n", r);
94 	kbdout.offset++;
95 	kbdout.avail--;
96 	kbdout.expect_ack = 1;
97 
98 	kbd_alive = 1;
99 	if (kbd_watchdog_set) {
100 		/* Set a watchdog timer for one second. */
101 		set_timer(&tmr_kbd_wd, sys_hz(), kbd_watchdog, 0);
102 
103 		kbd_watchdog_set = 1;
104 	}
105 }
106 
107 /*
108  * Try to obtain input from the keyboard.
109  */
110 static int
111 scan_keyboard(unsigned char *bp, int *isauxp)
112 {
113 	u32_t b, sb;
114 	int r;
115 
116 	if ((r = sys_inb(KB_STATUS, &sb)) != OK) {
117 		printf("PCKBD: scan sys_inb() failed (1): %d\n", r);
118 		return FALSE;
119 	}
120 	if (!(sb & KB_OUT_FULL)) {
121 		if (kbdout.avail && !kbdout.expect_ack)
122 			kbd_send();
123 		return FALSE;
124 	}
125 	if ((r = sys_inb(KEYBD, &b)) != OK) {
126 		printf("PCKBD: scan sys_inb() failed (2): %d\n", r);
127 		return FALSE;
128 	}
129 	if (!(sb & 0x40) && b == KB_ACK && kbdout.expect_ack) {
130 		kbdout.expect_ack = 0;
131 		micro_delay(KBC_IN_DELAY);
132 		kbd_send();
133 		return FALSE;
134 	}
135 	if (bp)
136 		*bp = b;
137 	if (isauxp)
138 		*isauxp = !!(sb & KB_AUX_BYTE);
139 	if (kbdout.avail && !kbdout.expect_ack) {
140 		micro_delay(KBC_IN_DELAY);
141 		kbd_send();
142 	}
143 	return TRUE;
144 }
145 
146 /*
147  * Wait until the controller is ready.  Return TRUE on success, FALSE on
148  * timeout.  Since this may discard input, only use during initialization.
149  */
150 static int
151 kb_wait(void)
152 {
153 	spin_t spin;
154 	u32_t status;
155 	int r, isaux;
156 	unsigned char byte;
157 
158 	SPIN_FOR(&spin, KBC_WAIT_TIME) {
159 		if ((r = sys_inb(KB_STATUS, &status)) != OK)
160 			printf("PCKBD: wait sys_inb() failed: %d\n", r);
161 		if (status & KB_OUT_FULL)
162 			(void) scan_keyboard(&byte, &isaux);
163 		if (!(status & (KB_IN_FULL | KB_OUT_FULL)))
164 			return TRUE;		/* wait until ready */
165 	}
166 
167 	printf("PCKBD: wait timeout\n");
168 	return FALSE;
169 }
170 
171 /*
172  * Set the LEDs on the caps, num, and scroll lock keys.
173  */
174 static void
175 set_leds(unsigned char ledmask)
176 {
177 	if (kbdout.avail == 0)
178 		kbdout.offset = 0;
179 	if (kbdout.offset + kbdout.avail + 2 > KBD_OUT_BUFSZ) {
180 		/*
181 		 * The output buffer is full.  Ignore this command.  Reset the
182 		 * ACK flag.
183 		 */
184 		kbdout.expect_ack = 0;
185 	} else {
186 		kbdout.buf[kbdout.offset+kbdout.avail] = LED_CODE;
187 		kbdout.buf[kbdout.offset+kbdout.avail+1] = ledmask;
188 		kbdout.avail += 2;
189 	}
190 	if (!kbdout.expect_ack)
191 		kbd_send();
192 }
193 
194 /*
195  * Send a command to the keyboard.
196  */
197 static void
198 kbc_cmd0(int cmd)
199 {
200 	int r;
201 
202 	kb_wait();
203 	if ((r = sys_outb(KB_COMMAND, cmd)) != OK)
204 		printf("PCKBD: cmd0 sys_outb() failed: %d\n", r);
205 }
206 
207 /*
208  * Send a command to the keyboard, including data.
209  */
210 static void
211 kbc_cmd1(int cmd, int data)
212 {
213 	int r;
214 
215 	kb_wait();
216 	if ((r = sys_outb(KB_COMMAND, cmd)) != OK)
217 		printf("PCKBD: cmd1 sys_outb() failed (1): %d\n", r);
218 	kb_wait();
219 	if ((r = sys_outb(KEYBD, data)) != OK)
220 		printf("PCKBD: cmd1 sys_outb() failed (2): %d\n", r);
221 }
222 
223 /*
224  * Wait at most one second for a byte from the keyboard or the controller.
225  */
226 static int
227 kbc_read(void)
228 {
229 	u32_t byte, status;
230 	spin_t spin;
231 	int r;
232 
233 	SPIN_FOR(&spin, KBC_READ_TIME) {
234 		if ((r = sys_inb(KB_STATUS, &status)) != OK)
235 			printf("PCKBD: read sys_inb() failed (1): %d\n", r);
236 		if (status & KB_OUT_FULL) {
237 			micro_delay(KBC_IN_DELAY);
238 			if ((r = sys_inb(KEYBD, &byte)) != OK)
239 				printf("PCKBD: read sys_inb() failed (2): "
240 				    "%d\n", r);
241 			if (status & KB_AUX_BYTE)
242 				printf("PCKBD: read got aux 0x%x\n", byte);
243 			return byte;
244 		}
245 	}
246 
247 	panic("kbc_read failed to complete");
248 }
249 
250 /*
251  * Initialize the keyboard hardware.
252  */
253 static int
254 kb_init(void)
255 {
256 	int r, ccb;
257 
258 	/* Disable the keyboard and AUX. */
259 	kbc_cmd0(KBC_DI_KBD);
260 	kbc_cmd0(KBC_DI_AUX);
261 
262 	/* Discard leftover keystroke. */
263 	scan_keyboard(NULL, NULL);
264 
265 	/* Get the current configuration byte. */
266 	kbc_cmd0(KBC_RD_RAM_CCB);
267 	ccb = kbc_read();
268 
269 	/* If bit 5 is clear, it is a single channel controler for sure.. */
270 	aux_available = (ccb & 0x10);
271 
272 	/* Execute Controller Self Test. */
273 	kbc_cmd0(0xAA);
274 	r = kbc_read();
275 	if (r != 0x55){
276 		printf("PCKBD: Controller self-test failed.\n");
277 		return EGENERIC;
278 	}
279 
280 	/* Set interrupt handler and enable keyboard IRQ. */
281 	irq_hook_id = KEYBOARD_IRQ;	/* id to be returned on interrupt */
282 	r = sys_irqsetpolicy(KEYBOARD_IRQ, IRQ_REENABLE, &irq_hook_id);
283 	if (r != OK)
284 		panic("Couldn't set keyboard IRQ policy: %d", r);
285 	if ((r = sys_irqenable(&irq_hook_id)) != OK)
286 		panic("Couldn't enable keyboard IRQs: %d", r);
287 
288 	/* Activate IRQ bit for the keyboard. */
289 	ccb |= 0x1;
290 
291 	if (aux_available != 0) {
292 		/* Set AUX interrupt handler and enable AUX IRQ. */
293 		aux_irq_hook_id = KBD_AUX_IRQ;	/* id to be returned on interrupt */
294 		r = sys_irqsetpolicy(KBD_AUX_IRQ, IRQ_REENABLE, &aux_irq_hook_id);
295 		if (r != OK)
296 			panic("Couldn't set AUX IRQ policy: %d", r);
297 		if ((r = sys_irqenable(&aux_irq_hook_id)) != OK)
298 			panic("Couldn't enable AUX IRQs: %d", r);
299 
300 		/* Activate IRQ for AUX. */
301 		ccb |= 0x2;
302 	}
303 
304 	/* Enable interrupt(s). */
305 	kbc_cmd1(KBC_WR_RAM_CCB, ccb);
306 
307 	/* Re-enable the keyboard device. */
308 	kbc_cmd0(KBC_EN_KBD);
309 
310 	if (aux_available != 0) {
311 		/* Enable the AUX device. */
312 		kbc_cmd0(KBC_EN_AUX);
313 		kbc_cmd1(0xD4, 0xF6);
314 		kbc_cmd1(0xD4, 0xF4);
315 	}
316 
317 	/* Set the initial LED state. */
318 	kb_wait();
319 
320 	set_leds(0);
321 	return OK;
322 }
323 
324 /*
325  * Process a keyboard scancode.
326  */
327 static void
328 kbd_process(unsigned char scode)
329 {
330 	int press, index, page, code;
331 
332 	press = !(scode & SCAN_RELEASE) ? INPUT_PRESS : INPUT_RELEASE;
333 	index = scode & ~SCAN_RELEASE;
334 
335 	switch (kbd_state) {
336 	case 1:
337 		page = scanmap_escaped[index].page;
338 		code = scanmap_escaped[index].code;
339 		break;
340 	case 2:
341 		kbd_state = (index == SCAN_CTRL) ? 3 : 0;
342 		return;
343 	case 3:
344 		if (index == SCAN_NUMLOCK) {
345 			page = INPUT_PAGE_KEY;
346 			code = INPUT_KEY_PAUSE;
347 			break;
348 		}
349 		/* FALLTHROUGH */
350 	default:
351 		switch (scode) {
352 		case SCAN_EXT0:
353 			kbd_state = 1;
354 			return;
355 		case SCAN_EXT1:
356 			kbd_state = 2;
357 			return;
358 		}
359 		page = scanmap_normal[index].page;
360 		code = scanmap_normal[index].code;
361 		break;
362 	}
363 
364 	if (page)
365 		inputdriver_send_event(FALSE /*mouse*/, page, code, press, 0);
366 
367 	kbd_state = 0;
368 }
369 
370 /*
371  * Process an auxiliary (mouse) scancode.
372  */
373 static void
374 kbdaux_process(unsigned char scode)
375 {
376 	u32_t delta;
377 	int i;
378 
379 	if (aux_counter == 0 && !(scode & 0x08))
380 		return;	/* resync */
381 
382 	aux_bytes[aux_counter++] = scode;
383 
384 	if (aux_counter < 3)
385 		return; /* need more first */
386 
387 	aux_counter = 0;
388 
389 	/* Send an event for each button state change. */
390 	for (i = 0; i < 3; i++) {
391 		if ((aux_state ^ aux_bytes[0]) & (1 << i)) {
392 			aux_state ^= (1 << i);
393 
394 			inputdriver_send_event(TRUE /*mouse*/,
395 			    INPUT_PAGE_BUTTON, INPUT_BUTTON_1 + i,
396 			    !!(aux_state & (1 << i)), 0);
397 		}
398 	}
399 
400 	/* Send an event for each relative mouse movement, X and/or Y. */
401 	for (i = 0; i < 2; i++) {
402 		delta = aux_bytes[1 + i];
403 		if (delta != 0) {
404 			if (aux_bytes[0] & (0x10 << i))
405 				delta |= 0xFFFFFF00; /* make signed */
406 
407 			inputdriver_send_event(TRUE /*mouse*/, INPUT_PAGE_GD,
408 			    !i ? INPUT_GD_X : INPUT_GD_Y, delta,
409 			    INPUT_FLAG_REL);
410 		}
411 	}
412 }
413 
414 /*
415  * Set keyboard LEDs.
416  */
417 static void
418 pckbd_leds(unsigned int leds)
419 {
420 	unsigned char b;
421 
422 	b = 0;
423 	if (leds & (1 << INPUT_LED_NUMLOCK)) b |= LED_NUM_LOCK;
424 	if (leds & (1 << INPUT_LED_CAPSLOCK)) b |= LED_CAPS_LOCK;
425 	if (leds & (1 << INPUT_LED_SCROLLLOCK)) b |= LED_SCROLL_LOCK;
426 
427 	set_leds(b);
428 }
429 
430 /*
431  * Process a keyboard interrupt.
432  */
433 static void
434 pckbd_intr(unsigned int UNUSED(mask))
435 {
436 	unsigned char scode;
437 	int isaux;
438 
439 	/* Fetch a character from the keyboard hardware and acknowledge it. */
440 	if (!scan_keyboard(&scode, &isaux))
441 		return;
442 
443 	if (!isaux) {
444 		/* A keyboard key press or release. */
445 		kbd_process(scode);
446 	} else {
447 		/* A mouse event. */
448 		kbdaux_process(scode);
449 	}
450 }
451 
452 /*
453  * Process a timer signal.
454  */
455 static void
456 pckbd_alarm(clock_t stamp)
457 {
458 	expire_timers(stamp);
459 }
460 
461 /*
462  * Initialize the driver.
463  */
464 static int
465 pckbd_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
466 {
467 	int flags = INPUT_DEV_KBD;
468 	/* Initialize the watchdog timer. */
469 	init_timer(&tmr_kbd_wd);
470 
471 	/* Initialize the keyboard. */
472 	int r;
473 	if((r = kb_init())!=OK){
474 		return r;
475 	}
476 
477 	/* Announce the driver's presence. */
478 	if (aux_available != 0)
479 		flags |= INPUT_DEV_MOUSE;
480 	inputdriver_announce(flags);
481 
482 	return OK;
483 }
484 
485 /*
486  * Set callback routines and let SEF initialize.
487  */
488 static void
489 pckbd_startup(void)
490 {
491 	sef_setcb_init_fresh(pckbd_init);
492 
493 	sef_startup();
494 }
495 
496 /*
497  * PC keyboard/mouse driver task.
498  */
499 int
500 main(void)
501 {
502 	pckbd_startup();
503 
504 	inputdriver_task(&pckbd_tab);
505 
506 	return 0;
507 }
508