blob: 5b87cdb539554130cfc3ec647eed68db8c58e8e6 [file] [log] [blame]
wdenkaffae2b2002-08-17 09:36:01 +00001/*
2 * (C) Copyright 2001
3 * Denis Peter, MPL AG Switzerland, d.peter@mpl.ch
4 *
5 * See file CREDITS for list of people who contributed to this
6 * project.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 * MA 02111-1307 USA
22 *
23 *
24 * Source partly derived from:
25 * linux/drivers/char/pc_keyb.c
26 *
27 *
28 */
29#include <common.h>
30#include <asm/processor.h>
31#include <devices.h>
32#include "isa.h"
33#include "kbd.h"
34
35
36unsigned char kbd_read_status(void);
37unsigned char kbd_read_input(void);
38void kbd_send_data(unsigned char data);
39void disable_8259A_irq(unsigned int irq);
40void enable_8259A_irq(unsigned int irq);
41
42/* used only by send_data - set by keyboard_interrupt */
43
44
45#undef KBG_DEBUG
46
47#ifdef KBG_DEBUG
48#define PRINTF(fmt,args...) printf (fmt ,##args)
49#else
50#define PRINTF(fmt,args...)
51#endif
52
53#define KBD_STAT_KOBF 0x01
54#define KBD_STAT_IBF 0x02
55#define KBD_STAT_SYS 0x04
56#define KBD_STAT_CD 0x08
57#define KBD_STAT_LOCK 0x10
58#define KBD_STAT_MOBF 0x20
59#define KBD_STAT_TI_OUT 0x40
60#define KBD_STAT_PARERR 0x80
61
62#define KBD_INIT_TIMEOUT 1000 /* Timeout in ms for initializing the keyboard */
63#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */
64#define KBD_TIMEOUT 2000 /* Timeout in ms for keyboard command acknowledge */
65/*
66 * Keyboard Controller Commands
67 */
68
69#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
70#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
71#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
72#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
73#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
74#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
75#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
76#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
77#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
78#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
79#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
80 initiated by the auxiliary device */
81#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
82
83/*
84 * Keyboard Commands
85 */
86
87#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
88#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
89#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
90#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */
91#define KBD_CMD_RESET 0xFF /* Reset */
92
93/*
94 * Keyboard Replies
95 */
96
97#define KBD_REPLY_POR 0xAA /* Power on reset */
98#define KBD_REPLY_ACK 0xFA /* Command ACK */
99#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
100
101/*
102 * Status Register Bits
103 */
104
105#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
106#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
107#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
108#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
109#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
110#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
111#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
112#define KBD_STAT_PERR 0x80 /* Parity error */
113
114#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF)
115
116/*
117 * Controller Mode Register Bits
118 */
119
120#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
121#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
122#define KBD_MODE_SYS 0x04 /* The system flag (?) */
123#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
124#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
125#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
126#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
127#define KBD_MODE_RFU 0x80
128
129
130#define KDB_DATA_PORT 0x60
131#define KDB_COMMAND_PORT 0x64
132
133#define LED_SCR 0x01 /* scroll lock led */
134#define LED_CAP 0x04 /* caps lock led */
135#define LED_NUM 0x02 /* num lock led */
136
137#define KBD_BUFFER_LEN 0x20 /* size of the keyboardbuffer */
138
139
140
141
142static volatile char kbd_buffer[KBD_BUFFER_LEN];
143static volatile int in_pointer = 0;
144static volatile int out_pointer = 0;
145
146
147static unsigned char num_lock = 0;
148static unsigned char caps_lock = 0;
149static unsigned char scroll_lock = 0;
150static unsigned char shift = 0;
151static unsigned char ctrl = 0;
152static unsigned char alt = 0;
153static unsigned char e0 = 0;
154static unsigned char leds = 0;
155
156#define DEVNAME "kbd"
157
158/* Simple translation table for the keys */
159
160static unsigned char kbd_plain_xlate[] = {
161 0xff,0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', /* 0x00 - 0x0f */
162 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\r',0xff, 'a', 's', /* 0x10 - 0x1f */
163 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`',0xff,'\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */
164 'b', 'n', 'm', ',', '.', '/',0xff,0xff,0xff, ' ',0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */
165 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
166 '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */
167 '\r',0xff,0xff
168 };
169
170static unsigned char kbd_shift_xlate[] = {
171 0xff,0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', /* 0x00 - 0x0f */
172 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\r',0xff, 'A', 'S', /* 0x10 - 0x1f */
173 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',0xff, '|', 'Z', 'X', 'C', 'V', /* 0x20 - 0x2f */
174 'B', 'N', 'M', '<', '>', '?',0xff,0xff,0xff, ' ',0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */
175 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
176 '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */
177 '\r',0xff,0xff
178 };
179
180static unsigned char kbd_ctrl_xlate[] = {
181 0xff,0x1b, '1',0x00, '3', '4', '5',0x1E, '7', '8', '9', '0',0x1F, '=','\b','\t', /* 0x00 - 0x0f */
182 0x11,0x17,0x05,0x12,0x14,0x18,0x15,0x09,0x0f,0x10,0x1b,0x1d,'\n',0xff,0x01,0x13, /* 0x10 - 0x1f */
183 0x04,0x06,0x08,0x09,0x0a,0x0b,0x0c, ';','\'', '~',0x00,0x1c,0x1a,0x18,0x03,0x16, /* 0x20 - 0x2f */
184 0x02,0x0e,0x0d, '<', '>', '?',0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */
185 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
186 '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */
187 '\r',0xff,0xff
188 };
189
190/******************************************************************
191 * Init
192 ******************************************************************/
193int isa_kbd_init(void)
194{
195 char* result;
196 result=kbd_initialize();
197 if(result==NULL) {
198 PRINTF("AT Keyboard initialized\n");
199 irq_install_handler(25, (interrupt_handler_t *)handle_isa_int, NULL);
200 isa_irq_install_handler(KBD_INTERRUPT, (interrupt_handler_t *)kbd_interrupt, NULL);
201 return (1);
202 }
203 else {
204 printf("%s\n",result);
205 return (-1);
206 }
207}
208
209#ifdef CFG_CONSOLE_OVERWRITE_ROUTINE
210extern int overwrite_console (void);
211#else
212int overwrite_console (void)
213{
214 return (0);
215}
216#endif
217
218int drv_isa_kbd_init (void)
219{
220 int error;
221 device_t kbddev ;
222 char *stdinname = getenv ("stdin");
223
224 if(isa_kbd_init()==-1)
225 return -1;
226 memset (&kbddev, 0, sizeof(kbddev));
227 strcpy(kbddev.name, DEVNAME);
228 kbddev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
229 kbddev.putc = NULL ;
230 kbddev.puts = NULL ;
231 kbddev.getc = kbd_getc ;
232 kbddev.tstc = kbd_testc ;
233
234 error = device_register (&kbddev);
235 if(error==0) {
236 /* check if this is the standard input device */
237 if(strcmp(stdinname,DEVNAME)==0) {
238 /* reassign the console */
239 if(overwrite_console()) {
240 return 1;
241 }
242 error=console_assign(stdin,DEVNAME);
243 if(error==0)
244 return 1;
245 else
246 return error;
247 }
248 return 1;
249 }
250 return error;
251}
252
253/******************************************************************
254 * Queue handling
255 ******************************************************************/
256/* puts character in the queue and sets up the in and out pointer */
257void kbd_put_queue(char data)
258{
259 if((in_pointer+1)==KBD_BUFFER_LEN) {
260 if(out_pointer==0) {
261 return; /* buffer full */
262 } else{
263 in_pointer=0;
264 }
265 } else {
266 if((in_pointer+1)==out_pointer)
267 return; /* buffer full */
268 in_pointer++;
269 }
270 kbd_buffer[in_pointer]=data;
271 return;
272}
273
274/* test if a character is in the queue */
275int kbd_testc(void)
276{
277 if(in_pointer==out_pointer)
278 return(0); /* no data */
279 else
280 return(1);
281}
282/* gets the character from the queue */
283int kbd_getc(void)
284{
285 char c;
286 while(in_pointer==out_pointer);
287 if((out_pointer+1)==KBD_BUFFER_LEN)
288 out_pointer=0;
289 else
290 out_pointer++;
291 c=kbd_buffer[out_pointer];
292 return (int)c;
293
294}
295
296
297/* set LEDs */
298
299void kbd_set_leds(void)
300{
301 if(caps_lock==0)
302 leds&=~LED_CAP; /* switch caps_lock off */
303 else
304 leds|=LED_CAP; /* switch on LED */
305 if(num_lock==0)
306 leds&=~LED_NUM; /* switch LED off */
307 else
308 leds|=LED_NUM; /* switch on LED */
309 if(scroll_lock==0)
310 leds&=~LED_SCR; /* switch LED off */
311 else
312 leds|=LED_SCR; /* switch on LED */
313 kbd_send_data(KBD_CMD_SET_LEDS);
314 kbd_send_data(leds);
315}
316
317
318void handle_keyboard_event(unsigned char scancode)
319{
320 unsigned char keycode;
321
322 /* Convert scancode to keycode */
323 PRINTF("scancode %x\n",scancode);
324 if(scancode==0xe0) {
325 e0=1; /* special charakters */
326 return;
327 }
328 if(e0==1) {
329 e0=0; /* delete flag */
330 if(!( ((scancode&0x7F)==0x38)|| /* the right ctrl key */
331 ((scancode&0x7F)==0x1D)|| /* the right alt key */
332 ((scancode&0x7F)==0x35)|| /* the right '/' key */
333 ((scancode&0x7F)==0x1C) )) /* the right enter key */
334 /* we swallow unknown e0 codes */
335 return;
336 }
337 /* special cntrl keys */
338 switch(scancode)
339 {
340 case 0x2A:
341 case 0x36: /* shift pressed */
342 shift=1;
343 return; /* do nothing else */
344 case 0xAA:
345 case 0xB6: /* shift released */
346 shift=0;
347 return; /* do nothing else */
348 case 0x38: /* alt pressed */
349 alt=1;
350 return; /* do nothing else */
351 case 0xB8: /* alt released */
352 alt=0;
353 return; /* do nothing else */
354 case 0x1d: /* ctrl pressed */
355 ctrl=1;
356 return; /* do nothing else */
357 case 0x9d: /* ctrl released */
358 ctrl=0;
359 return; /* do nothing else */
360 case 0x46: /* scrollock pressed */
361 scroll_lock=~scroll_lock;
362 kbd_set_leds();
363 return; /* do nothing else */
364 case 0x3A: /* capslock pressed */
365 caps_lock=~caps_lock;
366 kbd_set_leds();
367 return;
368 case 0x45: /* numlock pressed */
369 num_lock=~num_lock;
370 kbd_set_leds();
371 return;
372 case 0xC6: /* scroll lock released */
373 case 0xC5: /* num lock released */
374 case 0xBA: /* caps lock released */
375 return; /* just swallow */
376 }
377 if((scancode&0x80)==0x80) /* key released */
378 return;
379 /* now, decide which table we need */
380 if(scancode > (sizeof(kbd_plain_xlate)/sizeof(kbd_plain_xlate[0]))) { /* scancode not in list */
381 PRINTF("unkown scancode %X\n",scancode);
382 return; /* swallow it */
383 }
384 /* setup plain code first */
385 keycode=kbd_plain_xlate[scancode];
386 if(caps_lock==1) { /* caps_lock is pressed, overwrite plain code */
387 if(scancode > (sizeof(kbd_shift_xlate)/sizeof(kbd_shift_xlate[0]))) { /* scancode not in list */
388 PRINTF("unkown caps-locked scancode %X\n",scancode);
389 return; /* swallow it */
390 }
391 keycode=kbd_shift_xlate[scancode];
392 if(keycode<'A') { /* we only want the alphas capital */
393 keycode=kbd_plain_xlate[scancode];
394 }
395 }
396 if(shift==1) { /* shift overwrites caps_lock */
397 if(scancode > (sizeof(kbd_shift_xlate)/sizeof(kbd_shift_xlate[0]))) { /* scancode not in list */
398 PRINTF("unkown shifted scancode %X\n",scancode);
399 return; /* swallow it */
400 }
401 keycode=kbd_shift_xlate[scancode];
402 }
403 if(ctrl==1) { /* ctrl overwrites caps_lock and shift */
404 if(scancode > (sizeof(kbd_ctrl_xlate)/sizeof(kbd_ctrl_xlate[0]))) { /* scancode not in list */
405 PRINTF("unkown ctrl scancode %X\n",scancode);
406 return; /* swallow it */
407 }
408 keycode=kbd_ctrl_xlate[scancode];
409 }
410 /* check if valid keycode */
411 if(keycode==0xff) {
412 PRINTF("unkown scancode %X\n",scancode);
413 return; /* swallow unknown codes */
414 }
415
416 kbd_put_queue(keycode);
417 PRINTF("%x\n",keycode);
418}
419
420/*
421 * This reads the keyboard status port, and does the
422 * appropriate action.
423 *
424 */
425unsigned char handle_kbd_event(void)
426{
427 unsigned char status = kbd_read_status();
428 unsigned int work = 10000;
429
430 while ((--work > 0) && (status & KBD_STAT_OBF)) {
431 unsigned char scancode;
432
433 scancode = kbd_read_input();
434
435 /* Error bytes must be ignored to make the
436 Synaptics touchpads compaq use work */
437 /* Ignore error bytes */
438 if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR)))
439 {
440 if (status & KBD_STAT_MOUSE_OBF)
441 ; /* not supported: handle_mouse_event(scancode); */
442 else
443 handle_keyboard_event(scancode);
444 }
445 status = kbd_read_status();
446 }
447 if (!work)
448 PRINTF("pc_keyb: controller jammed (0x%02X).\n", status);
449 return status;
450}
451
452
453
454/******************************************************************************
455 * Lowlevel Part of keyboard section
456 */
457unsigned char kbd_read_status(void)
458{
459 return(in8(CFG_ISA_IO_BASE_ADDRESS + KDB_COMMAND_PORT));
460}
461
462unsigned char kbd_read_input(void)
463{
464 return(in8(CFG_ISA_IO_BASE_ADDRESS + KDB_DATA_PORT));
465}
466
467void kbd_write_command(unsigned char cmd)
468{
469 out8(CFG_ISA_IO_BASE_ADDRESS + KDB_COMMAND_PORT,cmd);
470}
471
472void kbd_write_output(unsigned char data)
473{
474 out8(CFG_ISA_IO_BASE_ADDRESS + KDB_DATA_PORT, data);
475}
476
477int kbd_read_data(void)
478{
479 int val;
480 unsigned char status;
481
482 val=-1;
483 status = kbd_read_status();
484 if (status & KBD_STAT_OBF) {
485 val = kbd_read_input();
486 if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
487 val = -2;
488 }
489 return val;
490}
491
492int kbd_wait_for_input(void)
493{
494 unsigned long timeout;
495 int val;
496
497 timeout = KBD_TIMEOUT;
498 val=kbd_read_data();
499 while(val < 0)
500 {
501 if(timeout--==0)
502 return -1;
503 udelay(1000);
504 val=kbd_read_data();
505 }
506 return val;
507}
508
509
510int kb_wait(void)
511{
512 unsigned long timeout = KBC_TIMEOUT * 10;
513
514 do {
515 unsigned char status = handle_kbd_event();
516 if (!(status & KBD_STAT_IBF))
517 return 0; /* ok */
518 udelay(1000);
519 timeout--;
520 } while (timeout);
521 return 1;
522}
523
524void kbd_write_command_w(int data)
525{
526 if(kb_wait())
527 PRINTF("timeout in kbd_write_command_w\n");
528 kbd_write_command(data);
529}
530
531void kbd_write_output_w(int data)
532{
533 if(kb_wait())
534 PRINTF("timeout in kbd_write_output_w\n");
535 kbd_write_output(data);
536}
537
538void kbd_send_data(unsigned char data)
539{
540 unsigned char status;
541 disable_8259A_irq(1); /* disable interrupt */
542 kbd_write_output_w(data);
543 status = kbd_wait_for_input();
544 if (status == KBD_REPLY_ACK)
545 enable_8259A_irq(1); /* enable interrupt */
546}
547
548
549char * kbd_initialize(void)
550{
551 int status;
552
553 in_pointer = 0; /* delete in Buffer */
554 out_pointer = 0;
555 /*
556 * Test the keyboard interface.
557 * This seems to be the only way to get it going.
558 * If the test is successful a x55 is placed in the input buffer.
559 */
560 kbd_write_command_w(KBD_CCMD_SELF_TEST);
561 if (kbd_wait_for_input() != 0x55)
562 return "Kbd: failed self test";
563 /*
564 * Perform a keyboard interface test. This causes the controller
565 * to test the keyboard clock and data lines. The results of the
566 * test are placed in the input buffer.
567 */
568 kbd_write_command_w(KBD_CCMD_KBD_TEST);
569 if (kbd_wait_for_input() != 0x00)
570 return "Kbd: interface failed self test";
571 /*
572 * Enable the keyboard by allowing the keyboard clock to run.
573 */
574 kbd_write_command_w(KBD_CCMD_KBD_ENABLE);
575 status = kbd_wait_for_input();
576 /*
577 * Reset keyboard. If the read times out
578 * then the assumption is that no keyboard is
579 * plugged into the machine.
580 * This defaults the keyboard to scan-code set 2.
581 *
582 * Set up to try again if the keyboard asks for RESEND.
583 */
584 do {
585 kbd_write_output_w(KBD_CMD_RESET);
586 status = kbd_wait_for_input();
587 if (status == KBD_REPLY_ACK)
588 break;
589 if (status != KBD_REPLY_RESEND)
590 {
591 PRINTF("status: %X\n",status);
592 return "Kbd: reset failed, no ACK";
593 }
594 } while (1);
595 if (kbd_wait_for_input() != KBD_REPLY_POR)
596 return "Kbd: reset failed, no POR";
597
598 /*
599 * Set keyboard controller mode. During this, the keyboard should be
600 * in the disabled state.
601 *
602 * Set up to try again if the keyboard asks for RESEND.
603 */
604 do {
605 kbd_write_output_w(KBD_CMD_DISABLE);
606 status = kbd_wait_for_input();
607 if (status == KBD_REPLY_ACK)
608 break;
609 if (status != KBD_REPLY_RESEND)
610 return "Kbd: disable keyboard: no ACK";
611 } while (1);
612
613 kbd_write_command_w(KBD_CCMD_WRITE_MODE);
614 kbd_write_output_w(KBD_MODE_KBD_INT
615 | KBD_MODE_SYS
616 | KBD_MODE_DISABLE_MOUSE
617 | KBD_MODE_KCC);
618
619 /* ibm powerpc portables need this to use scan-code set 1 -- Cort */
620 kbd_write_command_w(KBD_CCMD_READ_MODE);
621 if (!(kbd_wait_for_input() & KBD_MODE_KCC)) {
622 /*
623 * If the controller does not support conversion,
624 * Set the keyboard to scan-code set 1.
625 */
626 kbd_write_output_w(0xF0);
627 kbd_wait_for_input();
628 kbd_write_output_w(0x01);
629 kbd_wait_for_input();
630 }
631 kbd_write_output_w(KBD_CMD_ENABLE);
632 if (kbd_wait_for_input() != KBD_REPLY_ACK)
633 return "Kbd: enable keyboard: no ACK";
634
635 /*
636 * Finally, set the typematic rate to maximum.
637 */
638 kbd_write_output_w(KBD_CMD_SET_RATE);
639 if (kbd_wait_for_input() != KBD_REPLY_ACK)
640 return "Kbd: Set rate: no ACK";
641 kbd_write_output_w(0x00);
642 if (kbd_wait_for_input() != KBD_REPLY_ACK)
643 return "Kbd: Set rate: no ACK";
644 return NULL;
645}
646
647void kbd_interrupt(void)
648{
649 handle_kbd_event();
650}
651
652
653
654/* eof */
655