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
kbd_watchdog(int arg __unused)47 kbd_watchdog(int arg __unused)
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
kbd_send(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
scan_keyboard(unsigned char * bp,int * isauxp)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
kb_wait(void)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
set_leds(unsigned char ledmask)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
kbc_cmd0(int cmd)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
kbc_cmd1(int cmd,int data)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
kbc_read(void)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
kb_init(void)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
kbd_process(unsigned char scode)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
kbdaux_process(unsigned char scode)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
pckbd_leds(unsigned int leds)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
pckbd_intr(unsigned int UNUSED (mask))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
pckbd_alarm(clock_t stamp)456 pckbd_alarm(clock_t stamp)
457 {
458 expire_timers(stamp);
459 }
460
461 /*
462 * Initialize the driver.
463 */
464 static int
pckbd_init(int UNUSED (type),sef_init_info_t * UNUSED (info))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
pckbd_startup(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
main(void)500 main(void)
501 {
502 pckbd_startup();
503
504 inputdriver_task(&pckbd_tab);
505
506 return 0;
507 }
508