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