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;
 255bool panic_skip_console_lock;
 256
 257static unsigned addr_6845;
 258static uint16_t *crt_buf;
 259static uint16_t crt_pos;
 260
 261static uint16_t scrolling_crt_buf[SCROLLING_CRT_SIZE];
 262static uint16_t scrolling_crt_pos;
 263static uint8_t  current_crt_buf;
 264
 265void cga_init(void)
 266{
 267        volatile uint16_t *cp;
 268        uint16_t was;
 269        unsigned pos;
 270
 271        cp = (uint16_t *)(KERNBASE + CGA_BUF);
 272        was = *cp;
 273        *cp = (uint16_t) 0xA55A;
 274        if (*cp != 0xA55A) {
 275                cp = (uint16_t *)(KERNBASE + MONO_BUF);
 276                addr_6845 = MONO_BASE;
 277        } else {
 278                *cp = was;
 279                addr_6845 = CGA_BASE;
 280        }
 281
 282        /* Extract cursor location */
 283        outb(addr_6845, 14);
 284        pos = inb(addr_6845 + 1) << 8;
 285        outb(addr_6845, 15);
 286        pos |= inb(addr_6845 + 1);
 287
 288        crt_buf = (uint16_t*)cp;
 289        crt_pos = pos;
 290        scrolling_crt_pos = 0;
 291        current_crt_buf = 0;
 292
 293}
 294
 295static void set_screen(uint8_t screen_num)
 296{
 297        uint16_t leftovers = (scrolling_crt_pos % CRT_COLS);
 298        leftovers = (leftovers) ? CRT_COLS - leftovers : 0;
 299
 300        int offset = scrolling_crt_pos + leftovers - (screen_num + 1)*CRT_SIZE;
 301        offset = (offset > 0) ? offset : 0;
 302
 303        memcpy(crt_buf, scrolling_crt_buf + offset,
 304               CRT_SIZE * sizeof(uint16_t));
 305}
 306
 307static void scroll_screen_up(void)
 308{
 309        if(current_crt_buf <  (scrolling_crt_pos / CRT_SIZE))
 310                current_crt_buf++;
 311        set_screen(current_crt_buf);
 312}
 313
 314static void scroll_screen_down(void)
 315{
 316        if(current_crt_buf > 0)
 317                current_crt_buf--;
 318        set_screen(current_crt_buf);
 319}
 320
 321static void reset_screen(void)
 322{
 323        current_crt_buf = 0;
 324        set_screen(current_crt_buf);
 325}
 326
 327void cga_putc(int c)
 328{
 329        // if no attribute given, then use black on white
 330        if (!(c & ~0xFF))
 331                c |= 0x0700;
 332
 333        switch (c & 0xff) {
 334        case '\b':
 335        case 0x7f:
 336        #ifdef CONFIG_PRINTK_NO_BACKSPACE
 337                cga_putc('^');
 338                cga_putc('H');
 339        #else
 340                if (crt_pos > 0) {
 341                        crt_pos--;
 342                        scrolling_crt_pos--;
 343                        crt_buf[crt_pos] = (c & ~0xff) | ' ';
 344                        scrolling_crt_buf[scrolling_crt_pos] = crt_buf[crt_pos];
 345                }
 346        #endif /* CONFIG_PRINTK_NO_BACKSPACE */
 347                break;
 348        case '\n':
 349                crt_pos += CRT_COLS;
 350                scrolling_crt_pos += CRT_COLS;
 351                /* fallthru */
 352        case '\r':
 353                crt_pos -= (crt_pos % CRT_COLS);
 354                scrolling_crt_pos -= (scrolling_crt_pos % CRT_COLS);
 355                break;
 356        case '\t':
 357                cga_putc(' ');
 358                cga_putc(' ');
 359                cga_putc(' ');
 360                cga_putc(' ');
 361                cga_putc(' ');
 362                break;
 363        default:
 364                crt_buf[crt_pos++] = c;         /* write the character */
 365                scrolling_crt_buf[scrolling_crt_pos++] = c;
 366                break;
 367        }
 368
 369        // The purpose of this is to allow the screen to appear as if it is
 370        // scrolling as more lines are added beyond the size of the monitor.
 371        // The top line is dropped and everything is shifted up by one.
 372        if (crt_pos >= CRT_SIZE) {
 373                int i;
 374
 375                memcpy(crt_buf, crt_buf + CRT_COLS,
 376                       (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
 377                for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
 378                        crt_buf[i] = 0x0700 | ' ';
 379                crt_pos -= CRT_COLS;
 380        }
 381        // Do the same for the scrolling crt buffer when it hits its capacity
 382        if (scrolling_crt_pos >= SCROLLING_CRT_SIZE) {
 383                int i;
 384
 385                memcpy(scrolling_crt_buf, scrolling_crt_buf + CRT_COLS,
 386                       (SCROLLING_CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
 387                for (i = SCROLLING_CRT_SIZE - CRT_COLS; i < SCROLLING_CRT_SIZE;
 388                     i++)
 389                        scrolling_crt_buf[i] = 0x0700 | ' ';
 390                scrolling_crt_pos -= CRT_COLS;
 391        }
 392
 393
 394        /* move that little blinky thing */
 395        outb(addr_6845, 14);
 396        outb(addr_6845 + 1, crt_pos >> 8);
 397        outb(addr_6845, 15);
 398        outb(addr_6845 + 1, crt_pos);
 399}
 400
 401
 402/***** Keyboard input code *****/
 403
 404#define NO              0
 405
 406#define SHIFT           (1<<0)
 407#define CTL             (1<<1)
 408#define ALT             (1<<2)
 409
 410#define CAPSLOCK        (1<<3)
 411#define NUMLOCK         (1<<4)
 412#define SCROLLLOCK      (1<<5)
 413
 414#define E0ESC           (1<<6)
 415
 416static uint8_t shiftcode[256] =
 417{
 418        [0x1D] CTL,
 419        [0x2A] SHIFT,
 420        [0x36] SHIFT,
 421        [0x38] ALT,
 422        [0x9D] CTL,
 423        [0xB8] ALT
 424};
 425
 426static uint8_t togglecode[256] =
 427{
 428        [0x3A] CAPSLOCK,
 429        [0x45] NUMLOCK,
 430        [0x46] SCROLLLOCK
 431};
 432
 433static uint8_t normalmap[256] =
 434{
 435        NO,   0x1B, '1',  '2',  '3',  '4',  '5',  '6',  // 0x00
 436        '7',  '8',  '9',  '0',  '-',  '=',  '\b', '\t',
 437        'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',  // 0x10
 438        'o',  'p',  '[',  ']',  '\n', NO,   'a',  's',
 439        'd',  'f',  'g',  'h',  'j',  'k',  'l',  ';',  // 0x20
 440        '\'', '`',  NO,   '\\', 'z',  'x',  'c',  'v',
 441        'b',  'n',  'm',  ',',  '.',  '/',  NO,   '*',  // 0x30
 442        NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
 443        NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
 444        '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
 445        '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
 446        [0xC7] KEY_HOME,        [0x9C] '\n' /*KP_Enter*/,
 447        [0xB5] '/' /*KP_Div*/,  [0xC8] KEY_UP,
 448        [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
 449        [0xCD] KEY_RT,          [0xCF] KEY_END,
 450        [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
 451        [0xD2] KEY_INS,         [0xD3] KEY_DEL
 452};
 453
 454static uint8_t shiftmap[256] =
 455{
 456        NO,   033,  '!',  '@',  '#',  '$',  '%',  '^',  // 0x00
 457        '&',  '*',  '(',  ')',  '_',  '+',  '\b', '\t',
 458        'Q',  'W',  'E',  'R',  'T',  'Y',  'U',  'I',  // 0x10
 459        'O',  'P',  '{',  '}',  '\n', NO,   'A',  'S',
 460        'D',  'F',  'G',  'H',  'J',  'K',  'L',  ':',  // 0x20
 461        '"',  '~',  NO,   '|',  'Z',  'X',  'C',  'V',
 462        'B',  'N',  'M',  '<',  '>',  '?',  NO,   '*',  // 0x30
 463        NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
 464        NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
 465        '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
 466        '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
 467        [0xC7] KEY_HOME,        [0x9C] '\n' /*KP_Enter*/,
 468        [0xB5] '/' /*KP_Div*/,  [0xC8] KEY_UP,
 469        [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
 470        [0xCD] KEY_RT,          [0xCF] KEY_END,
 471        [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
 472        [0xD2] KEY_INS,         [0xD3] KEY_DEL
 473};
 474
 475#define C(x) (x - '@')
 476
 477static uint8_t ctlmap[256] =
 478{
 479        NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO,
 480        NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO,
 481        C('Q'),  C('W'),  C('E'),  C('R'),  C('T'),  C('Y'),  C('U'),  C('I'),
 482        C('O'),  C('P'),  NO,      NO,      '\r',    NO,      C('A'),  C('S'),
 483        C('D'),  C('F'),  C('G'),  C('H'),  C('J'),  C('K'),  C('L'),  NO,
 484        NO,      NO,      NO,      C('\\'), C('Z'),  C('X'),  C('C'),  C('V'),
 485        C('B'),  C('N'),  C('M'),  NO,      NO,      C('/'),  NO,      NO,
 486        [0x97] KEY_HOME,
 487        [0xB5] C('/'),          [0xC8] KEY_UP,
 488        [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
 489        [0xCD] KEY_RT,          [0xCF] KEY_END,
 490        [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
 491        [0xD2] KEY_INS,         [0xD3] KEY_DEL
 492};
 493
 494static uint8_t *charcode[4] = {
 495        normalmap,
 496        shiftmap,
 497        ctlmap,
 498        ctlmap
 499};
 500
 501/*
 502 * Get data from the keyboard.  If we finish a character, return it.  Else 0.
 503 * Return -1 if no data.
 504 */
 505static uint32_t shift;
 506static bool crt_scrolled = FALSE;
 507
 508/* TODO: i'm concerned about the (lack of) locking when scrolling the screen. */
 509static int kbd_proc_data(void)
 510{
 511#ifdef CONFIG_X86_DISABLE_KEYBOARD
 512        /* on some machines with usb keyboards, any keyboard input triggers SMM
 513         * interference on all of the cores. */
 514        return -1;
 515#endif /* CONFIG_X86_DISABLE_KEYBOARD */
 516
 517        int c;
 518        uint8_t data;
 519
 520#ifdef CONFIG_KB_CORE0_ONLY
 521        /* Ghetto hack to avoid crashing brho's buggy nehalem. */
 522        uint32_t eax, ebx, ecx, edx, family, model, stepping;
 523        cpuid(0x1, 0x0, &eax, &ebx, &ecx, &edx);
 524        family = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8);
 525        model = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4);
 526        stepping = eax & 0x0000000F;
 527        if (family == 6 && model == 26 && stepping == 4)
 528                if (core_id())
 529                        return -1;
 530#endif /* CONFIG_KB_CORE0_ONLY */
 531
 532        if ((inb(KBSTATP) & KBS_DIB) == 0)
 533                return -1;
 534
 535        data = inb(KBDATAP);
 536
 537        if (data == 0xE0) {
 538                // E0 escape character
 539                shift |= E0ESC;
 540                return 0;
 541        } else if (data & 0x80) {
 542                /* TODO: need a better check for bad key releases */
 543                if (data == 0xff)
 544                        return -1;
 545                // Key released
 546                data = (shift & E0ESC ? data : data & 0x7F);
 547                shift &= ~(shiftcode[data] | E0ESC);
 548                return 0;
 549        } else if (shift & E0ESC) {
 550                // Last character was an E0 escape; or with 0x80
 551                data |= 0x80;
 552                shift &= ~E0ESC;
 553        }
 554
 555        shift |= shiftcode[data];
 556        shift ^= togglecode[data];
 557
 558        c = charcode[shift & (CTL | SHIFT)][data];
 559
 560        //Scrolling screen functionality
 561        if((shift & SHIFT) && ((c == KEY_UP) || (c == KEY_PGUP))) {
 562                crt_scrolled = TRUE;
 563                scroll_screen_up();
 564                return 0;
 565        }
 566        else if((shift & SHIFT) && ((c == KEY_DN) || (c == KEY_PGDN))) {
 567                crt_scrolled = TRUE;
 568                scroll_screen_down();
 569                return 0;
 570        }
 571        else if((shift & SHIFT) && c == KEY_RT) {
 572                crt_scrolled = FALSE;
 573                reset_screen();
 574                return 0;
 575        }
 576
 577        // On keypress other than SHIFT, reset if we were scrolled
 578        if(crt_scrolled && (!(shift & SHIFT))) {
 579                crt_scrolled = FALSE;
 580                reset_screen();
 581        }
 582
 583        //Force character to capital if caps lock on
 584        if (shift & CAPSLOCK) {
 585                if ('a' <= c && c <= 'z')
 586                        c += 'A' - 'a';
 587                else if ('A' <= c && c <= 'Z')
 588                        c += 'a' - 'A';
 589        }
 590
 591        // Process special keys
 592        // Ctrl-Alt-Del: reboot
 593        if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
 594                cprintf("Rebooting!\n");
 595                outb(0x92, 0x3); // courtesy of Chris Frost
 596        }
 597
 598        return c;
 599}
 600
 601static int kb_get_char(struct cons_dev *cdev, uint8_t *data)
 602{
 603        int kb_d;
 604        /* kbd_proc_data returns 0 if we should keep asking.  It return -1 when
 605         * there is no data, and anything else is a char */
 606        while ((kb_d = kbd_proc_data()) == 0)
 607                cpu_relax();
 608        if (kb_d == -1)
 609                return -1;
 610        *data = (uint8_t)kb_d;
 611        return 0;
 612}
 613
 614void kbd_init(void)
 615{
 616        /* init and post the kb cons_dev */
 617        kb.type = CONS_KB_DEV;
 618        kb.val = 0;
 619        kb.irq = 1;             /* default PC keyboard IRQ */
 620        kb.model = "PC Keyboard";
 621        kb.getc = kb_get_char;
 622        SLIST_INSERT_HEAD(&cdev_list, &kb, next);
 623}
 624
 625/***** General device-independent console code *****/
 626
 627/* Initialize the console devices */
 628void cons_init(void)
 629{
 630        cga_init();
 631        kbd_init();
 632        serial_init();
 633}
 634
 635/* Returns 0 on success, with the char in *data */
 636int cons_get_char(struct cons_dev *cdev, uint8_t *data)
 637{
 638        return cdev->getc(cdev, data);
 639}
 640
 641/* Returns any available character, or 0 for none (legacy helper) */
 642int cons_get_any_char(void)
 643{
 644        uint8_t c;
 645        struct cons_dev *i;
 646        /* First to succeed gets returned */
 647        SLIST_FOREACH(i, &cdev_list, next) {
 648                if (!cons_get_char(i, &c))
 649                        return c;
 650        }
 651        return 0;
 652}
 653
 654/* output a character to all console outputs (monitor and all serials) */
 655void cons_putc(int c)
 656{
 657        void logbuf(int c);
 658
 659        if (!panic_skip_console_lock)
 660                spin_lock_irqsave(&console_lock);
 661
 662        #ifndef CONFIG_SERIAL_IO
 663                serial_spam_char(c);
 664        #endif
 665        //lpt_putc(c);  /* very slow on the nehalem */
 666        cga_putc(c);
 667        logbuf(c);
 668
 669        if (!panic_skip_console_lock)
 670                spin_unlock_irqsave(&console_lock);
 671}
 672
 673// `High'-level console I/O.  Used by readline and cprintf.
 674
 675void cputchar(int c)
 676{
 677        cons_putc(c);
 678}
 679
 680void cputbuf(const char*buf, int len)
 681{
 682        int i;
 683        for(i = 0; i < len; i++)
 684                cons_putc(buf[i]);
 685}
 686
 687int getchar(void)
 688{
 689        int c;
 690
 691        while ((c = cons_get_any_char()) == 0)
 692                /* do nothing */;
 693        return c;
 694}
 695
 696int iscons(int fdnum)
 697{
 698        // used by readline
 699        return 1;
 700}
 701
 702/* TODO: remove us (and serial IO) */
 703void serial_send_byte(uint8_t b)
 704{
 705}
 706
 707int serial_read_byte(void)
 708{
 709        return -1;
 710}
 711