x86: Handles some bad keyboard data
[akaros.git] / kern / arch / x86 / console.c
1 /* See COPYRIGHT for copyright information. */
2
3 #include <arch/x86.h>
4 #include <arch/arch.h>
5 #include <arch/console.h>
6 #include <arch/kbdreg.h>
7 #include <atomic.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <stdio.h>
11 #include <sys/queue.h>
12 #include <arch/coreid.h>
13
14 #include <ros/memlayout.h>
15
16 /***** Serial I/O code *****/
17
18 #define COM1                    0x3F8   /* irq 4 */
19 #define COM2                    0x2F8   /* irq 3 */
20 #define COM3                    0x3E8   /* irq 4 */
21 #define COM4                    0x2E8   /* irq 3 */
22
23 #define COM_RX                  0               // In:  Receive buffer (DLAB=0)
24 #define COM_DLL                 0               // Out: Divisor Latch Low (DLAB=1)
25 #define COM_DLM                 1               // Out: Divisor Latch High (DLAB=1)
26 #define COM_IER                 1               // Out: Interrupt Enable Register
27 #define COM_IER_RDI             0x01    //   Enable receiver data interrupt
28 #define COM_IIR                 2               // In:  Interrupt ID Register
29 #define COM_FCR                 2               // Out: FIFO Control Register
30 #define COM_LCR                 3               // Out: Line Control Register
31 #define COM_LCR_DLAB    0x80    //   Divisor latch access bit
32 #define COM_LCR_WLEN8   0x03    //   Wordlength: 8 bits
33 #define COM_MCR                 4               // Out: Modem Control Register
34 #define COM_MCR_RTS             0x02    // RTS complement
35 #define COM_MCR_DTR             0x01    // DTR complement
36 #define COM_MCR_OUT2    0x08    // Out2 complement
37 #define COM_MCR_GLB_IRQ 0x08    /* global irq controlled via MCR */
38 #define COM_LSR                 5               // In:  Line Status Register
39 #define COM_LSR_DATA    0x01    //   Data available
40 #define COM_LSR_READY   0x20    //   Ready to send
41 #define COM_SCRATCH             7               /* Scratch register */
42
43 /* List of all initialized console devices */
44 struct cons_dev_slist cdev_list = SLIST_HEAD_INITIALIZER(cdev_list);
45 /* need to statically allocate these, since cons_init is called so damn early */
46 struct cons_dev com1, com2, com3, com4, kb;
47
48 static int __serial_get_char(int com, uint8_t *data)
49 {
50         if (!(inb(com + COM_LSR) & COM_LSR_DATA))
51                 return -1;
52         *data = inb(com + COM_RX);
53         /* serial input sends \r a lot, but we interpret them as \n later on.  this
54          * will help userspace too, which isn't expecting the \rs.  the right answer
55          * might involve telling userspace what sort of console this is. */
56         if (*data == '\r')
57                 *data = '\n';
58         return 0;
59 }
60
61 static int serial_get_char(struct cons_dev *cdev, uint8_t *data)
62 {
63         return __serial_get_char(cdev->val, data);
64 }
65
66 static void __serial_put_char(int com, uint8_t c)
67 {
68         while (!(inb(com + COM_LSR) & COM_LSR_READY))
69                 cpu_relax();
70         outb(com, c);
71 }
72
73 /* Writes c (or some variant of) to the serial cdev */
74 static void serial_put_char(struct cons_dev *cdev, uint8_t c)
75 {
76         assert(cdev->type == CONS_SER_DEV);
77         /* We do some funky editing of a few chars, to suit what minicom seems to
78          * expect (at least for brho). */
79         switch (c & 0xff) {
80                 case '\b':
81                 case 0x7f:
82                 #ifdef CONFIG_PRINTK_NO_BACKSPACE
83                         __serial_put_char(cdev->val, (uint8_t)('^'));
84                         __serial_put_char(cdev->val, (uint8_t)('H'));
85                 #else
86                         __serial_put_char(cdev->val, '\b');
87                         __serial_put_char(cdev->val, (uint8_t)(' '));
88                         __serial_put_char(cdev->val, '\b');
89                 #endif /* CONFIG_PRINTK_NO_BACKSPACE */
90                         break;
91                 case '\n':
92                 case '\r':
93                         __serial_put_char(cdev->val, (uint8_t)('\n'));
94                         __serial_put_char(cdev->val, (uint8_t)('\r'));
95                         break;
96                 default:
97                         __serial_put_char(cdev->val, (uint8_t)(c & 0xff));
98                         break;
99         }
100 }
101
102 /* Writes c to every initialized serial port */
103 static void serial_spam_char(int c)
104 {
105         struct cons_dev *i;
106         SLIST_FOREACH(i, &cdev_list, next) {
107                 if (i->type == CONS_SER_DEV)
108                         serial_put_char(i, c);
109         }
110 }
111
112 /* http://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming \
113  * #Software_Identification_of_the_UART
114  *
115  * We return 0 for unknown (probably not there), and the char * o/w */
116 static char *__serial_detect_type(int com)
117 {
118         uint8_t val;
119         char *model = 0;
120         /* First, check that the port actually exists.  I haven't seen any
121          * documentation of the LSR 0xff check, but it seems to work on qemu and
122          * hardware (brho's nehalem).  Perhaps 0xff is the default state for
123          * 'unmapped' ports. */
124         /* Serial port doesn't exist if COM_LSR returns 0xff */
125         if (inb(com + COM_LSR) == 0xff)
126                 return model;
127         /* Try to set FIFO, then based on the bits enabled, we can tell what model
128          * it is */
129         outb(com + COM_FCR, 0xe7);
130         val = inb(com + COM_IIR);
131         if (val & (1 << 6)) {
132                 if (val & (1 << 7)) {
133                         if (val & (1 << 5))
134                                 model = "UART 16750";
135                         else
136                                 model = "UART 16550A";
137                 } else {
138                         model = "UART 16550";
139                 }
140         } else {
141                 /* no FIFO at all.  the 8250 had a buggy scratch register. */
142                 outb(com + COM_SCRATCH, 0x2a);
143                 val = inb(com + COM_SCRATCH);
144                 if (val == 0x2a)
145                         model = "UART 16450";
146                 else
147                         model = "UART 8250";
148         }
149         return model;
150 }
151
152 /* Helper: attempts to initialize the serial device cdev with COM com.  If it
153  * succeeds, the cdev will be on the cdev_list. */ 
154 static void serial_com_init(struct cons_dev *cdev, int com)
155 {
156         cdev->model = __serial_detect_type(com);
157         /* Bail if detection failed */
158         if (!cdev->model)
159                 return;
160         /* Set up the struct */
161         cdev->type = CONS_SER_DEV;
162         cdev->val = com;
163         switch (com) {
164                 case (COM1):
165                 case (COM3):
166                         cdev->irq = 4;
167                         break;
168                 case (COM2):
169                 case (COM4):
170                         cdev->irq = 3;
171                         break;
172                 default:
173                         /* not that printing is the safest thing right now... */
174                         panic("Unknown COM %d", com);
175         }
176         cdev->getc = serial_get_char;
177         /* Turn off the FIFO (not sure this is needed) */
178         outb(com + COM_FCR, 0);
179         /* Set speed; requires DLAB latch */
180         outb(com + COM_LCR, COM_LCR_DLAB);
181         /* Setting speed to 115200 (setting the divider to 1) */
182         outb(com + COM_DLL, 1);
183         outb(com + COM_DLM, 0);
184         /* 8 data bits, 1 stop bit, parity off; turn off DLAB latch */
185         outb(com + COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB);
186         /* This should turn on hardware flow control and make sure the global irq
187          * bit is on.  This bit is definitely used some hardware's 16550As, though
188          * not for qemu.  Also, on both qemu and hardware, this whole line is a
189          * noop, since the COM_MCR is already 0x0b, so we're just making sure the
190          * three bits are still turned on (and leaving other bits unchanged) */
191         outb(com + COM_MCR, inb(com + COM_MCR) | COM_MCR_RTS | COM_MCR_DTR |
192                                                  COM_MCR_GLB_IRQ);
193         /* Enable rx interrupts */
194         outb(com + COM_IER, COM_IER_RDI);
195         /* Clear any preexisting overrun indications and interrupts */
196         inb(com + COM_IIR);
197         inb(com + COM_RX);
198         /* Put us on the list of initialized cdevs (now that it is init'd) */
199         SLIST_INSERT_HEAD(&cdev_list, cdev, next);
200 }
201
202 static void serial_init(void)
203 {
204         /* attempt to init all four COMs */
205         serial_com_init(&com1, COM1);
206         serial_com_init(&com2, COM2);
207         serial_com_init(&com3, COM3);
208         serial_com_init(&com4, COM4);
209 }
210
211 /***** Parallel port output code *****/
212 // For information on PC parallel port programming, see the class References
213 // page.
214
215 // Stupid I/O delay routine necessitated by historical PC design flaws
216 static void
217 delay(void)
218 {
219         inb(0x84);
220         inb(0x84);
221         inb(0x84);
222         inb(0x84);
223 }
224
225 static void
226 lpt_putc(int c)
227 {
228         int i;
229
230         for (i = 0; !(inb(0x378+1) & 0x80) && i < 12800; i++)
231                 delay();
232         outb(0x378+0, c);
233         outb(0x378+2, 0x08|0x04|0x01);
234         outb(0x378+2, 0x08);
235 }
236
237 /***** Text-mode CGA/VGA display output with scrolling *****/
238 #define MONO_BASE       0x3B4
239 #define MONO_BUF        0xB0000
240 #define CGA_BASE        0x3D4
241 #define CGA_BUF         0xB8000
242
243 #define CRT_ROWS        25
244 #define CRT_COLS        80
245 #define CRT_SIZE        (CRT_ROWS * CRT_COLS)
246
247 #define MAX_SCROLL_LENGTH       20
248 #define SCROLLING_CRT_SIZE      (MAX_SCROLL_LENGTH * CRT_SIZE)
249
250 static spinlock_t console_lock = SPINLOCK_INITIALIZER_IRQSAVE;
251
252 static unsigned SREADONLY addr_6845;
253 static uint16_t *crt_buf;
254 static uint16_t crt_pos;
255
256 static uint16_t scrolling_crt_buf[SCROLLING_CRT_SIZE];
257 static uint16_t scrolling_crt_pos;
258 static uint8_t  current_crt_buf;
259
260 void
261 cga_init(void)
262 {
263         volatile uint16_t *cp;
264         uint16_t was;
265         unsigned pos;
266
267         cp = (uint16_t *COUNT(CRT_SIZE)) TC(KERNBASE + CGA_BUF);
268         was = *cp;
269         *cp = (uint16_t) 0xA55A;
270         if (*cp != 0xA55A) {
271                 cp = (uint16_t *COUNT(CRT_SIZE)) TC(KERNBASE + MONO_BUF);
272                 addr_6845 = SINIT(MONO_BASE);
273         } else {
274                 *cp = was;
275                 addr_6845 = SINIT(CGA_BASE);
276         }
277         
278         /* Extract cursor location */
279         outb(addr_6845, 14);
280         pos = inb(addr_6845 + 1) << 8;
281         outb(addr_6845, 15);
282         pos |= inb(addr_6845 + 1);
283
284         crt_buf = (uint16_t*)cp;
285         crt_pos = pos;
286         scrolling_crt_pos = 0;
287         current_crt_buf = 0;
288
289 }
290
291 static void set_screen(uint8_t screen_num)
292 {
293         uint16_t leftovers = (scrolling_crt_pos % CRT_COLS);
294         leftovers = (leftovers) ? CRT_COLS - leftovers : 0;
295         
296         int offset = scrolling_crt_pos + leftovers - (screen_num + 1)*CRT_SIZE;
297         offset = (offset > 0) ? offset : 0;
298
299         memcpy(crt_buf, scrolling_crt_buf + offset, CRT_SIZE * sizeof(uint16_t));
300 }
301
302 static void scroll_screen_up(void)
303 {
304         if(current_crt_buf <  (scrolling_crt_pos / CRT_SIZE))
305                 current_crt_buf++;
306         set_screen(current_crt_buf);
307 }
308
309 static void scroll_screen_down(void)
310 {
311         if(current_crt_buf > 0) 
312                 current_crt_buf--;
313         set_screen(current_crt_buf);
314 }
315
316 static void reset_screen(void)
317 {
318         current_crt_buf = 0;
319         set_screen(current_crt_buf);
320 }
321
322 void
323 cga_putc(int c)
324 {
325         // if no attribute given, then use black on white
326         if (!(c & ~0xFF))
327                 c |= 0x0700;
328
329         switch (c & 0xff) {
330         case '\b':
331         case 0x7f:
332         #ifdef CONFIG_PRINTK_NO_BACKSPACE
333                 cga_putc('^');
334                 cga_putc('H');
335         #else
336                 if (crt_pos > 0) {
337                         crt_pos--;
338                         scrolling_crt_pos--;
339                         crt_buf[crt_pos] = (c & ~0xff) | ' ';
340                         scrolling_crt_buf[scrolling_crt_pos] = crt_buf[crt_pos];
341                 }
342         #endif /* CONFIG_PRINTK_NO_BACKSPACE */
343                 break;
344         case '\n':
345                 crt_pos += CRT_COLS;
346                 scrolling_crt_pos += CRT_COLS;
347                 /* fallthru */
348         case '\r':
349                 crt_pos -= (crt_pos % CRT_COLS);
350                 scrolling_crt_pos -= (scrolling_crt_pos % CRT_COLS);
351                 break;
352         case '\t':
353                 cga_putc(' ');
354                 cga_putc(' ');
355                 cga_putc(' ');
356                 cga_putc(' ');
357                 cga_putc(' ');
358                 break;
359         default:
360                 crt_buf[crt_pos++] = c;         /* write the character */
361                 scrolling_crt_buf[scrolling_crt_pos++] = c;
362                 break;
363         }
364
365         // The purpose of this is to allow the screen to appear as if it is scrolling as
366         // more lines are added beyond the size of the monitor.  The top line is dropped
367         // and everything is shifted up by one.
368         if (crt_pos >= CRT_SIZE) {
369                 int i;
370
371                 memcpy(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
372                 for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
373                         crt_buf[i] = 0x0700 | ' ';
374                 crt_pos -= CRT_COLS;
375         }
376         // Do the same for the scrolling crt buffer when it hits its capacity
377         if (scrolling_crt_pos >= SCROLLING_CRT_SIZE) {
378                 int i;
379
380                 memcpy(scrolling_crt_buf, scrolling_crt_buf + CRT_COLS, 
381                        (SCROLLING_CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
382                 for (i = SCROLLING_CRT_SIZE - CRT_COLS; i < SCROLLING_CRT_SIZE; i++)
383                         scrolling_crt_buf[i] = 0x0700 | ' ';
384                 scrolling_crt_pos -= CRT_COLS;
385         }
386
387
388         /* move that little blinky thing */
389         outb(addr_6845, 14);
390         outb(addr_6845 + 1, crt_pos >> 8);
391         outb(addr_6845, 15);
392         outb(addr_6845 + 1, crt_pos);
393 }
394
395
396 /***** Keyboard input code *****/
397
398 #define NO              0
399
400 #define SHIFT   (1<<0)
401 #define CTL             (1<<1)
402 #define ALT             (1<<2)
403
404 #define CAPSLOCK        (1<<3)
405 #define NUMLOCK         (1<<4)
406 #define SCROLLLOCK      (1<<5)
407
408 #define E0ESC           (1<<6)
409
410 static uint8_t (SREADONLY shiftcode)[256] = 
411 {
412         [0x1D] CTL,
413         [0x2A] SHIFT,
414         [0x36] SHIFT,
415         [0x38] ALT,
416         [0x9D] CTL,
417         [0xB8] ALT
418 };
419
420 static uint8_t (SREADONLY togglecode)[256] = 
421 {
422         [0x3A] CAPSLOCK,
423         [0x45] NUMLOCK,
424         [0x46] SCROLLLOCK
425 };
426
427 static uint8_t normalmap[256] =
428 {
429         NO,   0x1B, '1',  '2',  '3',  '4',  '5',  '6',  // 0x00
430         '7',  '8',  '9',  '0',  '-',  '=',  '\b', '\t',
431         'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',  // 0x10
432         'o',  'p',  '[',  ']',  '\n', NO,   'a',  's',
433         'd',  'f',  'g',  'h',  'j',  'k',  'l',  ';',  // 0x20
434         '\'', '`',  NO,   '\\', 'z',  'x',  'c',  'v',
435         'b',  'n',  'm',  ',',  '.',  '/',  NO,   '*',  // 0x30
436         NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
437         NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
438         '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
439         '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
440         [0xC7] KEY_HOME,        [0x9C] '\n' /*KP_Enter*/,
441         [0xB5] '/' /*KP_Div*/,  [0xC8] KEY_UP,
442         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
443         [0xCD] KEY_RT,          [0xCF] KEY_END,
444         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
445         [0xD2] KEY_INS,         [0xD3] KEY_DEL
446 };
447
448 static uint8_t shiftmap[256] = 
449 {
450         NO,   033,  '!',  '@',  '#',  '$',  '%',  '^',  // 0x00
451         '&',  '*',  '(',  ')',  '_',  '+',  '\b', '\t',
452         'Q',  'W',  'E',  'R',  'T',  'Y',  'U',  'I',  // 0x10
453         'O',  'P',  '{',  '}',  '\n', NO,   'A',  'S',
454         'D',  'F',  'G',  'H',  'J',  'K',  'L',  ':',  // 0x20
455         '"',  '~',  NO,   '|',  'Z',  'X',  'C',  'V',
456         'B',  'N',  'M',  '<',  '>',  '?',  NO,   '*',  // 0x30
457         NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
458         NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
459         '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
460         '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
461         [0xC7] KEY_HOME,        [0x9C] '\n' /*KP_Enter*/,
462         [0xB5] '/' /*KP_Div*/,  [0xC8] KEY_UP,
463         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
464         [0xCD] KEY_RT,          [0xCF] KEY_END,
465         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
466         [0xD2] KEY_INS,         [0xD3] KEY_DEL
467 };
468
469 #define C(x) (x - '@')
470
471 static uint8_t ctlmap[256] = 
472 {
473         NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO, 
474         NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO, 
475         C('Q'),  C('W'),  C('E'),  C('R'),  C('T'),  C('Y'),  C('U'),  C('I'),
476         C('O'),  C('P'),  NO,      NO,      '\r',    NO,      C('A'),  C('S'),
477         C('D'),  C('F'),  C('G'),  C('H'),  C('J'),  C('K'),  C('L'),  NO, 
478         NO,      NO,      NO,      C('\\'), C('Z'),  C('X'),  C('C'),  C('V'),
479         C('B'),  C('N'),  C('M'),  NO,      NO,      C('/'),  NO,      NO,
480         [0x97] KEY_HOME,
481         [0xB5] C('/'),          [0xC8] KEY_UP,
482         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
483         [0xCD] KEY_RT,          [0xCF] KEY_END,
484         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
485         [0xD2] KEY_INS,         [0xD3] KEY_DEL
486 };
487
488 static uint8_t * COUNT(256) (SREADONLY charcode)[4] = {
489         normalmap,
490         shiftmap,
491         ctlmap,
492         ctlmap
493 };
494
495 /*
496  * Get data from the keyboard.  If we finish a character, return it.  Else 0.
497  * Return -1 if no data.
498  */
499 static uint32_t shift;
500 static bool crt_scrolled = FALSE;
501
502 /* TODO: i'm concerned about the (lack of) locking when scrolling the screen. */
503 static int
504 kbd_proc_data(void)
505 {
506 #ifdef CONFIG_X86_DISABLE_KEYBOARD
507         /* on some machines with usb keyboards, any keyboard input triggers SMM
508          * interference on all of the cores. */
509         return -1;
510 #endif /* CONFIG_X86_DISABLE_KEYBOARD */
511
512         int c;
513         uint8_t data;
514
515 #ifdef CONFIG_KB_CORE0_ONLY
516         /* Ghetto hack to avoid crashing brho's buggy nehalem. */
517         uint32_t eax, ebx, ecx, edx, family, model, stepping;
518         cpuid(0x1, 0x0, &eax, &ebx, &ecx, &edx);
519         family = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8);
520         model = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4);
521         stepping = eax & 0x0000000F;
522         if (family == 6 && model == 26 && stepping == 4)
523                 if (core_id())
524                         return -1;
525 #endif /* CONFIG_KB_CORE0_ONLY */
526
527         if ((inb(KBSTATP) & KBS_DIB) == 0)
528                 return -1;
529
530         data = inb(KBDATAP);
531
532         if (data == 0xE0) {
533                 // E0 escape character
534                 shift |= E0ESC;
535                 return 0;
536         } else if (data & 0x80) {
537                 /* TODO: need a better check for bad key releases */
538                 if (data == 0xff)
539                         return -1;
540                 // Key released
541                 data = (shift & E0ESC ? data : data & 0x7F);
542                 shift &= ~(shiftcode[data] | E0ESC);
543                 return 0;
544         } else if (shift & E0ESC) {
545                 // Last character was an E0 escape; or with 0x80
546                 data |= 0x80;
547                 shift &= ~E0ESC;
548         }
549
550         shift |= shiftcode[data];
551         shift ^= togglecode[data];
552
553         c = charcode[shift & (CTL | SHIFT)][data];
554
555         //Scrolling screen functionality
556         if((shift & SHIFT) && ((c == KEY_UP) || (c == KEY_PGUP))) {
557                 crt_scrolled = TRUE;
558                 scroll_screen_up();
559                 return 0;
560         }
561         else if((shift & SHIFT) && ((c == KEY_DN) || (c == KEY_PGDN))) {
562                 crt_scrolled = TRUE;
563                 scroll_screen_down();
564                 return 0;
565         }
566         else if((shift & SHIFT) && c == KEY_RT) {
567                 crt_scrolled = FALSE;
568                 reset_screen();
569                 return 0;
570         }
571
572         // On keypress other than SHIFT, reset if we were scrolled
573         if(crt_scrolled && (!(shift & SHIFT))) {
574                 crt_scrolled = FALSE;
575                 reset_screen();
576         }
577
578         //Force character to capital if caps lock on
579         if (shift & CAPSLOCK) {
580                 if ('a' <= c && c <= 'z')
581                         c += 'A' - 'a';
582                 else if ('A' <= c && c <= 'Z')
583                         c += 'a' - 'A';
584         }
585
586         // Process special keys
587         // Ctrl-Alt-Del: reboot
588         if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
589                 cprintf("Rebooting!\n");
590                 outb(0x92, 0x3); // courtesy of Chris Frost
591         }
592
593         return c;
594 }
595
596 static int kb_get_char(struct cons_dev *cdev, uint8_t *data)
597 {
598         int kb_d;
599         /* kbd_proc_data returns 0 if we should keep asking.  It return -1 when
600          * there is no data, and anything else is a char */
601         while ((kb_d = kbd_proc_data()) == 0)
602                 cpu_relax();
603         if (kb_d == -1)
604                 return -1;
605         *data = (uint8_t)kb_d;
606         return 0;
607 }
608
609 void kbd_init(void)
610 {
611         /* init and post the kb cons_dev */
612         kb.type = CONS_KB_DEV;
613         kb.val = 0;
614         kb.irq = 1;             /* default PC keyboard IRQ */
615         kb.model = "PC Keyboard";
616         kb.getc = kb_get_char;
617         SLIST_INSERT_HEAD(&cdev_list, &kb, next);
618 }
619
620 /***** General device-independent console code *****/
621
622 /* Initialize the console devices */
623 void cons_init(void)
624 {
625         cga_init();
626         kbd_init();
627         serial_init();
628 }
629
630 /* Returns 0 on success, with the char in *data */
631 int cons_get_char(struct cons_dev *cdev, uint8_t *data)
632 {
633         return cdev->getc(cdev, data);
634 }
635
636 /* Returns any available character, or 0 for none (legacy helper) */
637 int cons_get_any_char(void)
638 {
639         uint8_t c;
640         struct cons_dev *i;
641         /* First to succeed gets returned */
642         SLIST_FOREACH(i, &cdev_list, next) {
643                 if (!cons_get_char(i, &c))
644                         return c;
645         }
646         return 0;
647 }
648
649 /* output a character to all console outputs (monitor and all serials) */
650 void cons_putc(int c)
651 {
652         void logbuf(int c);
653         #ifdef CONFIG_TRACE_LOCKS
654         int8_t irq_state = 0;
655         disable_irqsave(&irq_state);
656         __spin_lock(&console_lock);
657         #else
658         spin_lock_irqsave(&console_lock);
659         #endif
660
661         #ifndef CONFIG_SERIAL_IO
662                 serial_spam_char(c);
663         #endif
664         //lpt_putc(c);  /* very slow on the nehalem */
665         cga_putc(c);
666         logbuf(c);
667
668         #ifdef CONFIG_TRACE_LOCKS
669         __spin_unlock(&console_lock);
670         enable_irqsave(&irq_state);
671         #else
672         spin_unlock_irqsave(&console_lock);
673         #endif
674 }
675
676 // `High'-level console I/O.  Used by readline and cprintf.
677
678 void
679 cputchar(int c)
680 {
681         cons_putc(c);
682 }
683
684 void
685 cputbuf(const char*COUNT(len) buf, int len)
686 {
687         int i;
688         for(i = 0; i < len; i++)
689                 cons_putc(buf[i]);
690 }
691
692 int
693 getchar(void)
694 {
695         int c;
696
697         while ((c = cons_get_any_char()) == 0)
698                 /* do nothing */;
699         return c;
700 }
701
702 int
703 iscons(int fdnum)
704 {
705         // used by readline
706         return 1;
707 }
708
709 /* TODO: remove us (and serial IO) */
710 void serial_send_byte(uint8_t b)
711 {
712 }
713
714 int serial_read_byte(void)
715 {
716         return -1;
717 }