Changes ARCH i686 -> x86 (XCC)
[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         int c;
501         uint8_t data;
502
503 #ifdef CONFIG_KB_CORE0_ONLY
504         /* Ghetto hack to avoid crashing brho's buggy nehalem. */
505         uint32_t eax, ebx, ecx, edx, family, model, stepping;
506         cpuid(0x1, 0x0, &eax, &ebx, &ecx, &edx);
507         family = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8);
508         model = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4);
509         stepping = eax & 0x0000000F;
510         if (family == 6 && model == 26 && stepping == 4)
511                 if (core_id())
512                         return -1;
513 #endif /* CONFIG_KB_CORE0_ONLY */
514
515         if ((inb(KBSTATP) & KBS_DIB) == 0)
516                 return -1;
517
518         data = inb(KBDATAP);
519
520         if (data == 0xE0) {
521                 // E0 escape character
522                 shift |= E0ESC;
523                 return 0;
524         } else if (data & 0x80) {
525                 // Key released
526                 data = (shift & E0ESC ? data : data & 0x7F);
527                 shift &= ~(shiftcode[data] | E0ESC);
528                 return 0;
529         } else if (shift & E0ESC) {
530                 // Last character was an E0 escape; or with 0x80
531                 data |= 0x80;
532                 shift &= ~E0ESC;
533         }
534
535         shift |= shiftcode[data];
536         shift ^= togglecode[data];
537
538         c = charcode[shift & (CTL | SHIFT)][data];
539
540         //Scrolling screen functionality
541         if((shift & SHIFT) && ((c == KEY_UP) || (c == KEY_PGUP))) {
542                 crt_scrolled = TRUE;
543                 scroll_screen_up();
544                 return 0;
545         }
546         else if((shift & SHIFT) && ((c == KEY_DN) || (c == KEY_PGDN))) {
547                 crt_scrolled = TRUE;
548                 scroll_screen_down();
549                 return 0;
550         }
551         else if((shift & SHIFT) && c == KEY_RT) {
552                 crt_scrolled = FALSE;
553                 reset_screen();
554                 return 0;
555         }
556
557         // On keypress other than SHIFT, reset if we were scrolled
558         if(crt_scrolled && (!(shift & SHIFT))) {
559                 crt_scrolled = FALSE;
560                 reset_screen();
561         }
562
563         //Force character to capital if caps lock on
564         if (shift & CAPSLOCK) {
565                 if ('a' <= c && c <= 'z')
566                         c += 'A' - 'a';
567                 else if ('A' <= c && c <= 'Z')
568                         c += 'a' - 'A';
569         }
570
571         // Process special keys
572         // Ctrl-Alt-Del: reboot
573         if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
574                 cprintf("Rebooting!\n");
575                 outb(0x92, 0x3); // courtesy of Chris Frost
576         }
577
578         return c;
579 }
580
581 static int kb_get_char(struct cons_dev *cdev, uint8_t *data)
582 {
583         int kb_d;
584         /* kbd_proc_data returns 0 if we should keep asking.  It return -1 when
585          * there is no data, and anything else is a char */
586         while ((kb_d = kbd_proc_data()) == 0)
587                 cpu_relax();
588         if (kb_d == -1)
589                 return -1;
590         *data = (uint8_t)kb_d;
591         return 0;
592 }
593
594 void kbd_init(void)
595 {
596         /* init and post the kb cons_dev */
597         kb.type = CONS_KB_DEV;
598         kb.val = 0;
599         kb.irq = 1;             /* default PC keyboard IRQ */
600         kb.model = "PC Keyboard";
601         kb.getc = kb_get_char;
602         SLIST_INSERT_HEAD(&cdev_list, &kb, next);
603 }
604
605 /***** General device-independent console code *****/
606
607 /* Initialize the console devices */
608 void cons_init(void)
609 {
610         cga_init();
611         kbd_init();
612         serial_init();
613 }
614
615 /* Returns 0 on success, with the char in *data */
616 int cons_get_char(struct cons_dev *cdev, uint8_t *data)
617 {
618         return cdev->getc(cdev, data);
619 }
620
621 /* Returns any available character, or 0 for none (legacy helper) */
622 int cons_get_any_char(void)
623 {
624         uint8_t c;
625         struct cons_dev *i;
626         /* First to succeed gets returned */
627         SLIST_FOREACH(i, &cdev_list, next) {
628                 if (!cons_get_char(i, &c))
629                         return c;
630         }
631         return 0;
632 }
633
634 /* output a character to all console outputs (monitor and all serials) */
635 void cons_putc(int c)
636 {
637         spin_lock_irqsave(&lock);
638         #ifndef CONFIG_SERIAL_IO
639                 serial_spam_char(c);
640         #endif
641         //lpt_putc(c);  /* very slow on the nehalem */
642         cga_putc(c);
643         spin_unlock_irqsave(&lock);
644 }
645
646 // `High'-level console I/O.  Used by readline and cprintf.
647
648 void
649 cputchar(int c)
650 {
651         cons_putc(c);
652 }
653
654 void
655 cputbuf(const char*COUNT(len) buf, int len)
656 {
657         int i;
658         for(i = 0; i < len; i++)
659                 cons_putc(buf[i]);
660 }
661
662 int
663 getchar(void)
664 {
665         int c;
666
667         while ((c = cons_get_any_char()) == 0)
668                 /* do nothing */;
669         return c;
670 }
671
672 int
673 iscons(int fdnum)
674 {
675         // used by readline
676         return 1;
677 }
678
679 /* TODO: remove us (and serial IO) */
680 void serial_send_byte(uint8_t b)
681 {
682 }
683
684 int serial_read_byte(void)
685 {
686         return -1;
687 }