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