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