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