This source file includes following definitions.
- kb_wait
- send_cmd
- keyboard_interrupt
- put_queue
- puts_queue
- applkey
- enter
- caps_toggle
- caps_on
- show_ptregs
- hold
- pause
- num
- lastcons
- send_intr
- scrll_forw
- scrll_back
- boot_it
- compose
- SAK
- do_spec
- do_lowercase
- do_self
- do_dead
- handle_diacr
- do_cons
- do_fn
- do_pad
- do_cur
- do_shift
- compute_shiftstate
- do_meta
- do_ascii
- do_lock
- send_data
- kbd_bh
- hard_reset_now
- kbd_init
1
2
3
4
5
6
7
8
9
10
11
12
13
14 #define KEYBOARD_IRQ 1
15
16 #include <linux/sched.h>
17 #include <linux/interrupt.h>
18 #include <linux/tty.h>
19 #include <linux/tty_flip.h>
20 #include <linux/mm.h>
21 #include <linux/ptrace.h>
22 #include <linux/config.h>
23 #include <linux/signal.h>
24 #include <linux/string.h>
25
26 #include <asm/bitops.h>
27
28 #include "kbd_kern.h"
29 #include "diacr.h"
30 #include "vt_kern.h"
31
32 #define SIZE(x) (sizeof(x)/sizeof((x)[0]))
33
34 #define KBD_REPORT_ERR
35 #define KBD_REPORT_UNKN
36
37 #ifndef KBD_DEFMODE
38 #define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
39 #endif
40
41 #ifndef KBD_DEFLEDS
42
43
44
45
46 #define KBD_DEFLEDS 0
47 #endif
48
49 #ifndef KBD_DEFLOCK
50 #define KBD_DEFLOCK 0
51 #endif
52
53
54
55
56
57
58
59 #define REALLY_SLOW_IO
60 #define SLOW_IO_BY_JUMPING
61 #include <asm/io.h>
62 #include <asm/system.h>
63
64 extern void poke_blanked_console(void);
65 extern void ctrl_alt_del(void);
66 extern void change_console(unsigned int new_console);
67 extern void scrollback(int);
68 extern void scrollfront(int);
69
70 #define fake_keyboard_interrupt() \
71 __asm__ __volatile__("int $0x21")
72
73 unsigned char kbd_read_mask = 0x01;
74
75
76
77
78
79
80
81
82 static unsigned char k_down[NR_SHIFT] = {0, };
83
84 static unsigned long key_down[8] = { 0, };
85
86 static int want_console = -1;
87 static int last_console = 0;
88 static int dead_key_next = 0;
89
90
91
92
93
94 int shift_state = 0;
95 static int npadch = -1;
96 static unsigned char diacr = 0;
97 static char rep = 0;
98 struct kbd_struct kbd_table[NR_CONSOLES];
99 static struct tty_struct **ttytab;
100 static struct kbd_struct * kbd = kbd_table;
101 static struct tty_struct * tty = NULL;
102
103
104 static volatile unsigned char acknowledge = 0;
105 static volatile unsigned char resend = 0;
106
107 typedef void (*k_hand)(unsigned char value, char up_flag);
108 typedef void (k_handfn)(unsigned char value, char up_flag);
109
110 static k_handfn
111 do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
112 do_meta, do_ascii, do_lock, do_lowercase;
113
114 static k_hand key_handler[] = {
115 do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
116 do_meta, do_ascii, do_lock, do_lowercase
117 };
118
119
120 const int max_vals[] = {
121 255, NR_FUNC - 1, 15, 17, 4, 255, 3, NR_SHIFT,
122 255, 9, 3, 255
123 };
124
125 const int NR_TYPES = SIZE(max_vals);
126
127 static void put_queue(int);
128 static unsigned char handle_diacr(unsigned char);
129 static void SAK(void);
130
131
132 static struct pt_regs * pt_regs;
133
134 static inline void kb_wait(void)
135 {
136 int i;
137
138 for (i=0; i<0x10000; i++)
139 if ((inb_p(0x64) & 0x02) == 0)
140 break;
141 }
142
143 static inline void send_cmd(unsigned char c)
144 {
145 kb_wait();
146 outb(c,0x64);
147 }
148
149
150
151
152
153 #define E0_BASE 96
154
155 #define E0_KPENTER (E0_BASE+0)
156 #define E0_RCTRL (E0_BASE+1)
157 #define E0_KPSLASH (E0_BASE+2)
158 #define E0_PRSCR (E0_BASE+3)
159 #define E0_RALT (E0_BASE+4)
160 #define E0_BREAK (E0_BASE+5)
161 #define E0_HOME (E0_BASE+6)
162 #define E0_UP (E0_BASE+7)
163 #define E0_PGUP (E0_BASE+8)
164 #define E0_LEFT (E0_BASE+9)
165 #define E0_RIGHT (E0_BASE+10)
166 #define E0_END (E0_BASE+11)
167 #define E0_DOWN (E0_BASE+12)
168 #define E0_PGDN (E0_BASE+13)
169 #define E0_INS (E0_BASE+14)
170 #define E0_DEL (E0_BASE+15)
171
172 #define E0_MACRO (E0_BASE+16)
173
174 #define E0_F13 (E0_BASE+17)
175 #define E0_F14 (E0_BASE+18)
176 #define E0_HELP (E0_BASE+19)
177 #define E0_DO (E0_BASE+20)
178 #define E0_F17 (E0_BASE+21)
179 #define E0_KPMINPLUS (E0_BASE+22)
180
181 #define E1_PAUSE (E0_BASE+23)
182
183 static unsigned char e0_keys[128] = {
184 0, 0, 0, 0, 0, 0, 0, 0,
185 0, 0, 0, 0, 0, 0, 0, 0,
186 0, 0, 0, 0, 0, 0, 0, 0,
187 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0,
188 0, 0, 0, 0, 0, 0, 0, 0,
189 0, 0, 0, 0, 0, 0, 0, 0,
190 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR,
191 E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,
192 E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,
193 E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, E0_KPMINPLUS, E0_END,
194 E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,
195 0, 0, 0, 0, 0, 0, 0, 0,
196 0, 0, 0, 0, 0, 0, 0, 0,
197 0, 0, 0, 0, 0, 0, 0, E0_MACRO,
198 0, 0, 0, 0, 0, 0, 0, 0,
199 0, 0, 0, 0, 0, 0, 0, 0
200 };
201
202 static void keyboard_interrupt(int int_pt_regs)
203 {
204 unsigned char scancode;
205 static unsigned int prev_scancode = 0;
206 char up_flag;
207 char raw_mode;
208
209 pt_regs = (struct pt_regs *) int_pt_regs;
210 send_cmd(0xAD);
211 kb_wait();
212 if ((inb_p(0x64) & kbd_read_mask) != 0x01)
213 goto end_kbd_intr;
214 scancode = inb(0x60);
215 mark_bh(KEYBOARD_BH);
216 if (scancode == 0xfa) {
217 acknowledge = 1;
218 goto end_kbd_intr;
219 } else if (scancode == 0xfe) {
220 resend = 1;
221 goto end_kbd_intr;
222 } else if (scancode == 0) {
223 #ifdef KBD_REPORT_ERR
224 printk("keyboard buffer overflow\n");
225 #endif
226 goto end_kbd_intr;
227 } else if (scancode == 0xff) {
228 #ifdef KBD_REPORT_ERR
229 printk("keyboard error\n");
230 #endif
231 prev_scancode = 0;
232 goto end_kbd_intr;
233 }
234 tty = ttytab[fg_console];
235 kbd = kbd_table + fg_console;
236 if ((raw_mode = vc_kbd_mode(kbd,VC_RAW))) {
237 put_queue(scancode);
238
239
240
241 }
242 if (scancode == 0xe0 || scancode == 0xe1) {
243 prev_scancode = scancode;
244 goto end_kbd_intr;
245 }
246
247
248
249
250 up_flag = (scancode & 0200);
251 scancode &= 0x7f;
252
253 if (prev_scancode) {
254
255
256
257
258 if (prev_scancode != 0xe0) {
259 if (prev_scancode == 0xe1 && scancode == 0x1d) {
260 prev_scancode = 0x100;
261 goto end_kbd_intr;
262 } else if (prev_scancode == 0x100 && scancode == 0x45) {
263 scancode = E1_PAUSE;
264 prev_scancode = 0;
265 } else {
266 printk("keyboard: unknown e1 escape sequence\n");
267 prev_scancode = 0;
268 goto end_kbd_intr;
269 }
270 } else {
271 prev_scancode = 0;
272
273
274
275
276
277
278
279
280
281
282
283
284
285 if (scancode == 0x2a || scancode == 0x36)
286 goto end_kbd_intr;
287
288 if (e0_keys[scancode])
289 scancode = e0_keys[scancode];
290 else if (!raw_mode) {
291 #ifdef KBD_REPORT_UNKN
292 printk("keyboard: unknown scancode e0 %02x\n", scancode);
293 #endif
294 goto end_kbd_intr;
295 }
296 }
297 } else if (scancode >= E0_BASE && !raw_mode) {
298 #ifdef KBD_REPORT_UNKN
299 printk("keyboard: scancode (%02x) not in range 00 - %2x\n",
300 scancode, E0_BASE - 1);
301 #endif
302 goto end_kbd_intr;
303 }
304
305
306
307
308
309
310
311
312
313 if (up_flag) {
314 clear_bit(scancode, key_down);
315 rep = 0;
316 } else
317 rep = set_bit(scancode, key_down);
318
319 if (raw_mode)
320 goto end_kbd_intr;
321
322 if (vc_kbd_mode(kbd, VC_MEDIUMRAW)) {
323 put_queue(scancode + up_flag);
324 goto end_kbd_intr;
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 if (!rep ||
341 (vc_kbd_mode(kbd,VC_REPEAT) && tty &&
342 (L_ECHO(tty) || (tty->driver.chars_in_buffer(tty) == 0)))) {
343 u_short key_code;
344 u_char type;
345
346
347 int shift_final = shift_state ^ kbd->lockstate;
348
349 key_code = key_map[shift_final][scancode];
350 type = KTYP(key_code);
351
352 if (type == KT_LETTER) {
353 type = KT_LATIN;
354 if (vc_kbd_led(kbd,VC_CAPSLOCK))
355 key_code = key_map[shift_final ^ (1<<KG_SHIFT)][scancode];
356 }
357 (*key_handler[type])(key_code & 0xff, up_flag);
358 }
359
360 end_kbd_intr:
361 send_cmd(0xAE);
362 }
363
364 static void put_queue(int ch)
365 {
366 wake_up(&keypress_wait);
367 if (tty) {
368 tty_insert_flip_char(tty, ch, 0);
369 tty_schedule_flip(tty);
370 }
371 }
372
373 static void puts_queue(char *cp)
374 {
375 wake_up(&keypress_wait);
376 if (!tty)
377 return;
378
379 while (*cp) {
380 tty_insert_flip_char(tty, *cp, 0);
381 cp++;
382 }
383 tty_schedule_flip(tty);
384 }
385
386 static void applkey(int key, char mode)
387 {
388 static char buf[] = { 0x1b, 'O', 0x00, 0x00 };
389
390 buf[1] = (mode ? 'O' : '[');
391 buf[2] = key;
392 puts_queue(buf);
393 }
394
395 static void enter(void)
396 {
397 put_queue(13);
398 if (vc_kbd_mode(kbd,VC_CRLF))
399 put_queue(10);
400 }
401
402 static void caps_toggle(void)
403 {
404 if (rep)
405 return;
406 chg_vc_kbd_led(kbd,VC_CAPSLOCK);
407 }
408
409 static void caps_on(void)
410 {
411 if (rep)
412 return;
413 set_vc_kbd_led(kbd,VC_CAPSLOCK);
414 }
415
416 static void show_ptregs(void)
417 {
418 if (!pt_regs)
419 return;
420 printk("\n");
421 printk("EIP: %04x:%08lx",0xffff & pt_regs->cs,pt_regs->eip);
422 if (pt_regs->cs & 3)
423 printk(" ESP: %04x:%08lx",0xffff & pt_regs->ss,pt_regs->esp);
424 printk(" EFLAGS: %08lx\n",pt_regs->eflags);
425 printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
426 pt_regs->orig_eax,pt_regs->ebx,pt_regs->ecx,pt_regs->edx);
427 printk("ESI: %08lx EDI: %08lx EBP: %08lx",
428 pt_regs->esi, pt_regs->edi, pt_regs->ebp);
429 printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
430 0xffff & pt_regs->ds,0xffff & pt_regs->es,
431 0xffff & pt_regs->fs,0xffff & pt_regs->gs);
432 }
433
434 static void hold(void)
435 {
436 if (rep || !tty)
437 return;
438
439
440
441
442
443
444 if (tty->stopped)
445 start_tty(tty);
446 else
447 stop_tty(tty);
448 }
449
450 #if 0
451
452 static void pause(void)
453 {
454 chg_vc_kbd_mode(kbd,VC_PAUSE);
455 }
456 #endif
457
458 static void num(void)
459 {
460 if (vc_kbd_mode(kbd,VC_APPLIC)) {
461 applkey('P', 1);
462 return;
463 }
464 if (!rep)
465 chg_vc_kbd_led(kbd,VC_NUMLOCK);
466 }
467
468 static void lastcons(void)
469 {
470
471 want_console = last_console;
472 }
473
474 static void send_intr(void)
475 {
476 if (!tty || (tty->termios && I_IGNBRK(tty)))
477 return;
478 tty_insert_flip_char(tty, 0, TTY_BREAK);
479 }
480
481 static void scrll_forw(void)
482 {
483 scrollfront(0);
484 }
485
486 static void scrll_back(void)
487 {
488 scrollback(0);
489 }
490
491 static void boot_it(void)
492 {
493 ctrl_alt_del();
494 }
495
496 static void compose(void)
497 {
498 dead_key_next = 1;
499 }
500
501 static void SAK(void)
502 {
503 do_SAK(tty);
504 #if 0
505
506
507
508
509
510
511
512
513
514 clr_vc_kbd_flag(kbd, VC_RAW);
515 clr_vc_kbd_flag(kbd, VC_MEDIUMRAW);
516 vt_cons[fg_console].vc_mode = KD_TEXT;
517 vt_cons[fg_console].vt_mode.mode = VT_AUTO;
518 vt_cons[fg_console].vt_mode.waitv = 0;
519 vt_cons[fg_console].vt_mode.relsig = 0;
520 vt_cons[fg_console].vt_mode.acqsig = 0;
521 vt_cons[fg_console].vt_mode.frsig = 0;
522 vt_cons[fg_console].vt_pid = -1;
523 vt_cons[fg_console].vt_newvt = -1;
524 unblank_screen();
525 #endif
526 }
527
528 static void do_spec(unsigned char value, char up_flag)
529 {
530 typedef void (*fnp)(void);
531 fnp fn_table[] = {
532 NULL, enter, show_ptregs, show_mem,
533 show_state, send_intr, lastcons, caps_toggle,
534 num, hold, scrll_forw, scrll_back,
535 boot_it, caps_on, compose, SAK
536 };
537
538 if (up_flag)
539 return;
540 if (value >= SIZE(fn_table))
541 return;
542 if (!fn_table[value])
543 return;
544 fn_table[value]();
545 }
546
547 static void do_lowercase(unsigned char value, char up_flag)
548 {
549 printk("keyboard.c: do_lowercase was called - impossible\n");
550 }
551
552 static void do_self(unsigned char value, char up_flag)
553 {
554 if (up_flag)
555 return;
556
557 if (diacr)
558 value = handle_diacr(value);
559
560 if (dead_key_next) {
561 dead_key_next = 0;
562 diacr = value;
563 return;
564 }
565
566 put_queue(value);
567 }
568
569 #define A_GRAVE '`'
570 #define A_ACUTE '\''
571 #define A_CFLEX '^'
572 #define A_TILDE '~'
573 #define A_DIAER '"'
574 static unsigned char ret_diacr[] =
575 {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
576
577
578
579
580 static void do_dead(unsigned char value, char up_flag)
581 {
582 if (up_flag)
583 return;
584
585 value = ret_diacr[value];
586 if (diacr == value) {
587 diacr = 0;
588 put_queue(value);
589 return;
590 }
591 diacr = value;
592 }
593
594
595
596
597
598 unsigned char handle_diacr(unsigned char ch)
599 {
600 int d = diacr;
601 int i;
602
603 diacr = 0;
604 if (ch == ' ')
605 return d;
606
607 for (i = 0; i < accent_table_size; i++) {
608 if (accent_table[i].diacr == d && accent_table[i].base == ch)
609 return accent_table[i].result;
610 }
611
612 put_queue(d);
613 return ch;
614 }
615
616 static void do_cons(unsigned char value, char up_flag)
617 {
618 if (up_flag)
619 return;
620 want_console = value;
621 }
622
623 static void do_fn(unsigned char value, char up_flag)
624 {
625 if (up_flag)
626 return;
627 if (value < SIZE(func_table))
628 puts_queue(func_table[value]);
629 else
630 printk("do_fn called with value=%d\n", value);
631 }
632
633 static void do_pad(unsigned char value, char up_flag)
634 {
635 static char *pad_chars = "0123456789+-*/\015,.?";
636 static char *app_map = "pqrstuvwxylSRQMnn?";
637
638 if (up_flag)
639 return;
640
641
642 if (vc_kbd_mode(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) {
643 applkey(app_map[value], 1);
644 return;
645 }
646
647 if (!vc_kbd_led(kbd,VC_NUMLOCK))
648 switch (value) {
649 case KVAL(K_PCOMMA):
650 case KVAL(K_PDOT):
651 do_fn(KVAL(K_REMOVE), 0);
652 return;
653 case KVAL(K_P0):
654 do_fn(KVAL(K_INSERT), 0);
655 return;
656 case KVAL(K_P1):
657 do_fn(KVAL(K_SELECT), 0);
658 return;
659 case KVAL(K_P2):
660 do_cur(KVAL(K_DOWN), 0);
661 return;
662 case KVAL(K_P3):
663 do_fn(KVAL(K_PGDN), 0);
664 return;
665 case KVAL(K_P4):
666 do_cur(KVAL(K_LEFT), 0);
667 return;
668 case KVAL(K_P6):
669 do_cur(KVAL(K_RIGHT), 0);
670 return;
671 case KVAL(K_P7):
672 do_fn(KVAL(K_FIND), 0);
673 return;
674 case KVAL(K_P8):
675 do_cur(KVAL(K_UP), 0);
676 return;
677 case KVAL(K_P9):
678 do_fn(KVAL(K_PGUP), 0);
679 return;
680 case KVAL(K_P5):
681 applkey('G', vc_kbd_mode(kbd, VC_APPLIC));
682 return;
683 }
684
685 put_queue(pad_chars[value]);
686 if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF))
687 put_queue(10);
688 }
689
690 static void do_cur(unsigned char value, char up_flag)
691 {
692 static char *cur_chars = "BDCA";
693 if (up_flag)
694 return;
695
696 applkey(cur_chars[value], vc_kbd_mode(kbd,VC_CKMODE));
697 }
698
699 static void do_shift(unsigned char value, char up_flag)
700 {
701 int old_state = shift_state;
702
703 if (rep)
704 return;
705
706
707 if (value == KVAL(K_CAPSSHIFT)) {
708 value = KVAL(K_SHIFT);
709 if (!up_flag)
710 clr_vc_kbd_led(kbd, VC_CAPSLOCK);
711 }
712
713 if (up_flag) {
714
715
716 if (k_down[value])
717 k_down[value]--;
718 } else
719 k_down[value]++;
720
721 if (k_down[value])
722 shift_state |= (1 << value);
723 else
724 shift_state &= ~ (1 << value);
725
726
727 if (up_flag && shift_state != old_state && npadch != -1) {
728 put_queue(npadch);
729 npadch = -1;
730 }
731 }
732
733
734
735 void compute_shiftstate(void)
736 {
737 int i, j, k, sym, val;
738
739 shift_state = 0;
740 for(i=0; i < SIZE(k_down); i++)
741 k_down[i] = 0;
742
743 for(i=0; i < SIZE(key_down); i++)
744 if(key_down[i]) {
745 k = (i<<5);
746 for(j=0; j<32; j++,k++)
747 if(test_bit(k, key_down)) {
748 sym = key_map[0][k];
749 if(KTYP(sym) == KT_SHIFT) {
750 val = KVAL(sym);
751 k_down[val]++;
752 shift_state |= (1<<val);
753 }
754 }
755 }
756 }
757
758 static void do_meta(unsigned char value, char up_flag)
759 {
760 if (up_flag)
761 return;
762
763 if (vc_kbd_mode(kbd, VC_META)) {
764 put_queue('\033');
765 put_queue(value);
766 } else
767 put_queue(value | 0x80);
768 }
769
770 static void do_ascii(unsigned char value, char up_flag)
771 {
772 if (up_flag)
773 return;
774
775 if (npadch == -1)
776 npadch = value;
777 else
778 npadch = (npadch * 10 + value) % 1000;
779 }
780
781 static void do_lock(unsigned char value, char up_flag)
782 {
783 if (up_flag || rep)
784 return;
785 chg_vc_kbd_lock(kbd, value);
786 }
787
788
789
790
791
792
793 static int send_data(unsigned char data)
794 {
795 int retries = 3;
796 int i;
797
798 do {
799 kb_wait();
800 acknowledge = 0;
801 resend = 0;
802 outb_p(data, 0x60);
803 for(i=0; i<0x20000; i++) {
804 inb_p(0x64);
805 if (acknowledge)
806 return 1;
807 if (resend)
808 break;
809 }
810 if (!resend)
811 return 0;
812 } while (retries-- > 0);
813 return 0;
814 }
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829 static void kbd_bh(void * unused)
830 {
831 static unsigned char old_leds = 0xff;
832 unsigned char leds = kbd_table[fg_console].ledstate;
833
834 if (leds != old_leds) {
835 old_leds = leds;
836 if (!send_data(0xed) || !send_data(leds))
837 send_data(0xf4);
838 }
839 if (want_console >= 0) {
840 if (want_console != fg_console) {
841 last_console = fg_console;
842 change_console(want_console);
843 }
844 want_console = -1;
845 }
846 poke_blanked_console();
847 cli();
848 if ((inb_p(0x64) & kbd_read_mask) == 0x01)
849 fake_keyboard_interrupt();
850 sti();
851 }
852
853 long no_idt[2] = {0, 0};
854
855
856
857
858
859
860 void hard_reset_now(void)
861 {
862 int i, j;
863 extern unsigned long pg0[1024];
864
865 sti();
866
867 pg0[0] = 7;
868 *((unsigned short *)0x472) = 0x1234;
869 for (;;) {
870 for (i=0; i<100; i++) {
871 kb_wait();
872 for(j = 0; j < 100000 ; j++)
873 ;
874 outb(0xfe,0x64);
875 }
876 __asm__("\tlidt _no_idt");
877 }
878 }
879
880 unsigned long kbd_init(unsigned long kmem_start)
881 {
882 int i;
883 struct kbd_struct * kbd;
884 extern struct tty_driver console_driver;
885
886 kbd = kbd_table + 0;
887 for (i = 0 ; i < NR_CONSOLES ; i++,kbd++) {
888 kbd->ledstate = KBD_DEFLEDS;
889 kbd->default_ledstate = KBD_DEFLEDS;
890 kbd->lockstate = KBD_DEFLOCK;
891 kbd->modeflags = KBD_DEFMODE;
892 }
893 ttytab = console_driver.table;
894
895 bh_base[KEYBOARD_BH].routine = kbd_bh;
896 request_irq(KEYBOARD_IRQ,keyboard_interrupt);
897 mark_bh(KEYBOARD_BH);
898 return kmem_start;
899 }