Search code examples
cx86osdevbare-metalprotected-mode

Read the keyboard in protected mode


I'm trying to do a PS/2 keyboard controller, and I can't get it work.

outb(0x60, 0xED);
outb(0x60, 2); /* Turn on CapsLock LED (doesn't works)*/
char c = 0;
while (c != 1)
{
    if (inb(0x60) != c)
    {
        c = inb(0x60);
        if (c > 0)
            putch(scan2char(c));
    }
}

scan2char function:

char scan2char(char scn)
{
    char keych;
    switch (scn)
    {
        case 0x15:
            keych = 'q';
            break;
        case 0x1D:
            keych = 'w';
            break;
        case 0x24:
            keych = 'e';
            break;
        case 0x2D:
            keych = 'r';
            break;
        case 0x2C:
            keych = 't';
            break;
        case 0x35:
            keych = 'y';
            break;
    }
    return keych;
}

It reads the keys, but the keyboard layout shifted in an weird way; e.x: if I press 9 I get q, if I press 0 I get w, you understand. At first I thought it might be the putch function, but doing some tests, I saw that putch wasn't the problem.


Solution

  • There's 2 or more separate pieces of hardware involved:

    • the PS/2 controller (which is mostly just a glorified serial port controller)
    • whatever happens to be plugged into the first PS/2 port (keyboard, mouse, touch screen, bar-code scanner, ...)
    • whatever happens to be plugged into the second PS/2 port

    When there's separate pieces of hardware involved it's best to have separate drivers:

    • something that handles the PS/2 controller; including handling "hot-insert device", device identification and starting the appropriate driver for the identified device; and including providing a "get_byte()/send_byte()" interface for other drivers to use.

    • a driver for each kind of device that might be plugged in (keyboard, mouse, touch screen, bar-code scanner, ...); which doesn't touch any of the PS/2 controller's IO ports and only communicates with the PS/2 controller driver (via. the "get_byte()/send_byte()" interface provided by the PS/2 controller driver).

    Note that (in general, excluding "hard-wired device" scenarios in laptops) there is absolutely no reason why you can't have two PS/2 keyboards plugged in (with 2 separate instances of the exact same "PS/2 keyboard driver" running); or two PS/2 mouses, or a bar code scanner and a touch screen (with no keyboard and no mouse), or any other combination of any types of devices in any PS/2 ports. Also (if you like portability) there's no reason why the same PS/2 keyboard/mouse/whatever device drivers can't work "as is" (with a recompile and nothing more) on completely different architectures with completely different PS/2 controllers (e.g. the PL050 PS/2 controller that some ARM systems have), simply because the driver for the completely different PS/2 controller can provide the exact same "get_byte()/send_byte()" interface.

    For the "8042" PS/2 controller on 80x86 PCs; you should probably read (if not follow) the initialisation sequence described here: https://wiki.osdev.org/%228042%22_PS/2_Controller#Initialising_the_PS.2F2_Controller

    If you don't initialise the PS/2 controller properly (e.g. just use "random whatever state the thing happened to be left in by boot loader") then it may have been left with an awful translation feature enabled where data from the device is deliberately mangled by the controller for backward compatibility with the original IBM XT machines (from before scan code set 2 existed); where a modern keyboard sends bytes for scan code set 2, but the PS/2 controller converts them into "scan code set 1 compatible" values, causing things like (e.g.) "if you press 9 you get q, if you press 0 you get w, ...".