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