Rewrite ps2 port (keyboard/mouse) handling.
authorKevin O'Connor <kevin@koconnor.net>
Sun, 20 Jul 2008 14:08:59 +0000 (10:08 -0400)
committerKevin O'Connor <kevin@koconnor.net>
Sun, 20 Jul 2008 14:08:59 +0000 (10:08 -0400)
Use command sending code for communicating with the ps2 port.

Makefile
src/biosvar.h
src/kbd.c
src/mouse.c
src/ps2port.c [new file with mode: 0644]
src/ps2port.h [new file with mode: 0644]

index c7ab73a8dea4c8d176661ba1773b1b11b247a3ce..abe60a7818b01ccbc1f011df8882325319590514 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,8 +9,8 @@ OUT=out/
 
 # Source files
 SRCBOTH=output.c util.c floppy.c ata.c system.c mouse.c kbd.c pci.c \
-        serial.c clock.c pic.c cdrom.c
-SRC16=$(SRCBOTH) disk.c apm.c pcibios.c
+        serial.c clock.c pic.c cdrom.c ps2port.c
+SRC16=$(SRCBOTH) disk.c apm.c pcibios.c vgahooks.c
 SRC32=$(SRCBOTH) post.c shadow.c post_menu.c memmap.c coreboot.c boot.c \
       acpi.c pirtable.c smm.c smpdetect.c mptable.c smbios.c pciinit.c
 TABLESRC=font.c cbt.c floppy_dbt.c
index 808f83b81c169dae780f3aa1627b1a9e691bb648..3d03cecd64332dce31a7cecad4733be7b2823397 100644 (file)
@@ -258,6 +258,8 @@ struct extended_bios_data_area_s {
     // 0x5d
     u8 other2[0xC4];
 
+    u8 ps2ctr;
+
     // Physical memory available.
     u32 ram_size;
     u16 e820_count;
index d63c8c37f38a454a50d7fd4fde0a1c8ba07e7658..b47b3ca56767336b1a28e6be4e95ac2308d03ff5 100644 (file)
--- a/src/kbd.c
+++ b/src/kbd.c
 #include "config.h" // CONFIG_*
 #include "pic.h" // eoi_pic1
 #include "bregs.h" // struct bregs
+#include "ps2port.h" // i8042_flush
 
-//--------------------------------------------------------------------------
-// keyboard_panic
-//--------------------------------------------------------------------------
-static void
-keyboard_panic(u16 status)
-{
-    // If you're getting a 993 keyboard panic here,
-    // please see the comment in keyboard_init
-
-    BX_PANIC("Keyboard error:%u\n",status);
-}
-
-static void
-kbd_flush(u8 code)
-{
-    u16 max = 0xffff;
-    while ((inb(PORT_PS2_STATUS) & 0x02) && (--max > 0))
-        outb(code, PORT_DIAG);
-    if (!max && code != 0xff)
-        keyboard_panic(code);
-}
-
-static void
-kbd_waitdata(u8 code)
-{
-    u16 max = 0xffff;
-    while ( ((inb(PORT_PS2_STATUS) & 0x01) == 0) && (--max>0) )
-        outb(code, PORT_DIAG);
-    if (!max)
-        keyboard_panic(code);
-}
-
-//--------------------------------------------------------------------------
-// keyboard_init
-//--------------------------------------------------------------------------
-// this file is based on LinuxBIOS implementation of keyboard.c
 static void
 keyboard_init()
 {
     if (CONFIG_COREBOOT)
         // Coreboot already does low-level keyboard init.
-        return;
-
-    /* ------------------- Flush buffers ------------------------*/
-    /* Wait until buffer is empty */
-    kbd_flush(0xff);
+        goto end;
 
     /* flush incoming keys */
-    u16 max=0x2000;
-    while (--max > 0) {
-        outb(0x00, PORT_DIAG);
-        if (inb(PORT_PS2_STATUS) & 0x01) {
-            inb(PORT_PS2_DATA);
-            max = 0x2000;
-        }
-    }
-
-    // Due to timer issues, and if the IPS setting is > 15000000,
-    // the incoming keys might not be flushed here. That will
-    // cause a panic a few lines below.  See sourceforge bug report :
-    // [ 642031 ] FATAL: Keyboard RESET error:993
-
-    /* ------------------- controller side ----------------------*/
-    /* send cmd = 0xAA, self test 8042 */
-    outb(0xaa, PORT_PS2_STATUS);
-
-    kbd_flush(0x00);
-    kbd_waitdata(0x01);
-
-    /* read self-test result, 0x55 should be returned from 0x60 */
-    if (inb(PORT_PS2_DATA) != 0x55)
-        keyboard_panic(991);
+    int ret = i8042_flush();
+    if (ret)
+        return;
 
-    /* send cmd = 0xAB, keyboard interface test */
-    outb(0xab, PORT_PS2_STATUS);
+    // Controller self-test.
+    u8 param[2];
+    ret = i8042_command(I8042_CMD_CTL_TEST, param);
+    if (ret)
+        return;
+    if (param[0] != 0x55) {
+        dprintf(1, "i8042 self test failed (got %x not 0x55\n", param[0]);
+        return;
+    }
 
-    kbd_flush(0x10);
-    kbd_waitdata(0x11);
+    // Controller keyboard test.
+    ret = i8042_command(I8042_CMD_KBD_TEST, param);
+    if (ret)
+        return;
+    if (param[0] != 0x00) {
+        dprintf(1, "i8042 keyboard test failed (got %x not 0x00\n", param[0]);
+        return;
+    }
 
-    /* read keyboard interface test result, */
-    /* 0x00 should be returned form 0x60 */
-    if (inb(PORT_PS2_DATA) != 0x00)
-        keyboard_panic(992);
+    // Enable keyboard and mouse ports.
+    ret = i8042_command(I8042_CMD_KBD_ENABLE, NULL);
+    if (ret)
+        return;
+    ret = i8042_command(I8042_CMD_AUX_ENABLE, NULL);
+    if (ret)
+        return;
 
-    /* Enable Keyboard clock */
-    outb(0xae, PORT_PS2_STATUS);
-    outb(0xa8, PORT_PS2_STATUS);
 
     /* ------------------- keyboard side ------------------------*/
-    /* reset kerboard and self test  (keyboard side) */
-    outb(0xff, PORT_PS2_DATA);
-
-    kbd_flush(0x20);
-    kbd_waitdata(0x21);
-
-    /* keyboard should return ACK */
-    if (inb(PORT_PS2_DATA) != 0xfa)
-        keyboard_panic(993);
-
-    kbd_waitdata(0x31);
-
-    if (inb(PORT_PS2_DATA) != 0xaa)
-        keyboard_panic(994);
+    /* reset keyboard and self test  (keyboard side) */
+    ret = kbd_command(ATKBD_CMD_RESET_BAT, param);
+    if (ret)
+        return;
+    if (param[0] != 0xaa) {
+        dprintf(1, "keyboard self test failed (got %x not 0xaa\n", param[0]);
+        return;
+    }
 
     /* Disable keyboard */
-    outb(0xf5, PORT_PS2_DATA);
-
-    kbd_flush(0x40);
-    kbd_waitdata(0x41);
-
-    /* keyboard should return ACK */
-    if (inb(PORT_PS2_DATA) != 0xfa)
-        keyboard_panic(995);
-
-    /* Write Keyboard Mode */
-    outb(0x60, PORT_PS2_STATUS);
-
-    kbd_flush(0x50);
-
-    /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
-    outb(0x61, PORT_PS2_DATA);
+    ret = kbd_command(ATKBD_CMD_RESET_DIS, NULL);
+    if (ret)
+        return;
 
-    kbd_flush(0x60);
+end:
+    // Keyboard Mode: scan code convert, disable mouse, enable IRQ 1
+    SET_EBDA(ps2ctr, I8042_CTR_AUXDIS | I8042_CTR_XLATE | I8042_CTR_KBDINT);
 
     /* Enable keyboard */
-    outb(0xf4, PORT_PS2_DATA);
-
-    kbd_flush(0x70);
-    kbd_waitdata(0x71);
-
-    /* keyboard should return ACK */
-    if (inb(PORT_PS2_DATA) != 0xfa)
-        keyboard_panic(996);
+    ret = kbd_command(ATKBD_CMD_ENABLE, NULL);
+    if (ret)
+        return;
 
-    outb(0x77, PORT_DIAG);
+    dprintf(1, "keyboard initialized\n");
 }
 
 void
@@ -286,29 +217,13 @@ handle_1609(struct bregs *regs)
 static void
 handle_160a(struct bregs *regs)
 {
-    outb(0xf2, PORT_PS2_DATA);
-    /* Wait for data */
-    u16 max=0xffff;
-    while ( ((inb(PORT_PS2_STATUS) & 0x01) == 0) && (--max>0) )
-        outb(0x00, PORT_DIAG);
-    if (!max)
-        return;
-    if (inb(PORT_PS2_DATA) != 0xfa) {
+    u8 param[2];
+    int ret = kbd_command(ATKBD_CMD_GETID, param);
+    if (ret) {
         regs->bx = 0;
         return;
     }
-    u16 kbd_code = 0;
-    u8 count = 2;
-    do {
-        max=0xffff;
-        while ( ((inb(PORT_PS2_STATUS) & 0x01) == 0) && (--max>0) )
-            outb(0x00, PORT_DIAG);
-        if (max>0x0) {
-            kbd_code >>= 8;
-            kbd_code |= (inb(PORT_PS2_DATA) << 8);
-        }
-    } while (--count>0);
-    regs->bx = kbd_code;
+    regs->bx = (param[1] << 8) | param[0];
 }
 
 // read MF-II keyboard input
@@ -372,23 +287,18 @@ handle_16a2(struct bregs *regs)
 static void
 set_leds()
 {
-    u8 shift_flags = GET_BDA(kbd_flag0);
-    u8 led_flags = GET_BDA(kbd_led);
-    if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) == 0)
+    u8 shift_flags = (GET_BDA(kbd_flag0) >> 4) & 0x07;
+    u8 kbd_led = GET_BDA(kbd_led);
+    u8 led_flags = kbd_led & 0x07;
+    if (shift_flags == led_flags)
         return;
 
-    outb(0xed, PORT_PS2_DATA);
-    while ((inb(PORT_PS2_STATUS) & 0x01) == 0)
-        outb(0x21, PORT_DIAG);
-    if (inb(PORT_PS2_DATA) == 0xfa) {
-        led_flags &= 0xf8;
-        led_flags |= (shift_flags >> 4) & 0x07;
-        outb(led_flags & 0x07, PORT_PS2_DATA);
-        while ((inb(PORT_PS2_STATUS) & 0x01) == 0)
-            outb(0x21, PORT_DIAG);
-        inb(PORT_PS2_DATA);
-        SET_BDA(kbd_led, led_flags);
-    }
+    int ret = kbd_command(ATKBD_CMD_SETLEDS, &shift_flags);
+    if (ret)
+        // Error
+        return;
+    kbd_led = (kbd_led & ~0x07) | shift_flags;
+    SET_BDA(kbd_led, kbd_led);
 }
 
 // INT 16h Keyboard Service Entry Point
@@ -397,10 +307,10 @@ handle_16(struct bregs *regs)
 {
     debug_enter(regs, DEBUG_HDL_16);
 
-    set_leds();
-
     irq_enable();
 
+    set_leds();
+
     switch (regs->ah) {
     case 0x00: handle_1600(regs); break;
     case 0x01: handle_1601(regs); break;
@@ -707,15 +617,14 @@ handle_09()
 {
     debug_isr(DEBUG_ISR_09);
 
-    // disable keyboard
-    outb(0xad, PORT_PS2_STATUS);
-
-    // Make sure there really is a keyboard irq pending.
-    if (! (get_pic1_isr() & PIC1_IRQ1))
-        goto done;
-
     // read key from keyboard controller
+    u8 v = inb(PORT_PS2_STATUS);
+    if ((v & 0x21) != 0x01) {
+        dprintf(1, "int09 but no keyboard data.\n");
+        goto done;
+    }
     u8 key = inb(PORT_PS2_DATA);
+
     irq_enable();
     if (CONFIG_KBD_CALL_INT15_4F) {
         // allow for keyboard intercept
@@ -732,9 +641,7 @@ handle_09()
     process_key(key);
 
     irq_disable();
-    eoi_pic1();
 
 done:
-    // enable keyboard
-    outb(0xae, PORT_PS2_STATUS);
+    eoi_pic1();
 }
index cb471db57c6aff46ebbd6c4881909990db1ba9ba..22d0148fdae5b92706d777d2ca32df35f04e76af 100644 (file)
@@ -9,8 +9,7 @@
 #include "util.h" // debug_isr
 #include "pic.h" // unmask_pic2
 #include "bregs.h" // struct bregs
-
-static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
+#include "ps2port.h" // aux_command
 
 void
 mouse_setup()
@@ -24,75 +23,6 @@ mouse_setup()
     unmask_pic2(PIC2_IRQ12);
 }
 
-static void
-set_kbd_command_byte(u8 command_byte)
-{
-    if (inb(PORT_PS2_STATUS) & 0x02)
-        BX_PANIC(panic_msg_keyb_buffer_full, "setkbdcomm");
-    outb(0xD4, PORT_PS2_STATUS);
-
-    outb(0x60, PORT_PS2_STATUS); // write command byte
-    outb(command_byte, PORT_PS2_DATA);
-}
-
-static u8
-inhibit_mouse_int_and_events()
-{
-    // Turn off IRQ generation and aux data line
-    if (inb(PORT_PS2_STATUS) & 0x02)
-        BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
-    outb(0x20, PORT_PS2_STATUS); // get command byte
-    while ((inb(PORT_PS2_STATUS) & 0x01) != 0x01)
-        ;
-    u8 prev_command_byte = inb(PORT_PS2_DATA);
-    u8 command_byte = prev_command_byte;
-    //while ( (inb(PORT_PS2_STATUS) & 0x02) );
-    if ( inb(PORT_PS2_STATUS) & 0x02 )
-        BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
-    command_byte &= 0xfd; // turn off IRQ 12 generation
-    command_byte |= 0x20; // disable mouse serial clock line
-    outb(0x60, PORT_PS2_STATUS); // write command byte
-    outb(command_byte, PORT_PS2_DATA);
-    return prev_command_byte;
-}
-
-static void
-enable_mouse_int_and_events()
-{
-    // Turn on IRQ generation and aux data line
-    if ( inb(PORT_PS2_STATUS) & 0x02 )
-        BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
-    outb(0x20, PORT_PS2_STATUS); // get command byte
-    while ((inb(PORT_PS2_STATUS) & 0x01) != 0x01)
-        ;
-    u8 command_byte = inb(PORT_PS2_DATA);
-    //while ( (inb(PORT_PS2_STATUS) & 0x02) );
-    if (inb(PORT_PS2_STATUS) & 0x02)
-        BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
-    command_byte |= 0x02; // turn on IRQ 12 generation
-    command_byte &= 0xdf; // enable mouse serial clock line
-    outb(0x60, PORT_PS2_STATUS); // write command byte
-    outb(command_byte, PORT_PS2_DATA);
-}
-
-static void
-send_to_mouse_ctrl(u8 sendbyte)
-{
-    // wait for chance to write to ctrl
-    if (inb(PORT_PS2_STATUS) & 0x02)
-        BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
-    outb(0xD4, PORT_PS2_STATUS);
-    outb(sendbyte, PORT_PS2_DATA);
-}
-
-static void
-get_mouse_data(u8 *data)
-{
-    while ((inb(PORT_PS2_STATUS) & 0x21) != 0x21)
-        ;
-    *data = inb(PORT_PS2_DATA);
-}
-
 #define RET_SUCCESS      0x00
 #define RET_EINVFUNCTION 0x01
 #define RET_EINVINPUT    0x02
@@ -100,39 +30,48 @@ get_mouse_data(u8 *data)
 #define RET_ENEEDRESEND  0x04
 #define RET_ENOHANDLER   0x05
 
+static int
+disable_mouse()
+{
+    u8 ps2ctr = GET_EBDA(ps2ctr);
+    ps2ctr |= I8042_CTR_AUXDIS;
+    ps2ctr &= ~I8042_CTR_AUXINT;
+    SET_EBDA(ps2ctr, ps2ctr);
+
+    return aux_command(PSMOUSE_CMD_DISABLE, NULL);
+}
+
 // Disable Mouse
 static void
 mouse_15c20000(struct bregs *regs)
 {
-    inhibit_mouse_int_and_events(); // disable IRQ12 and packets
-    send_to_mouse_ctrl(0xF5); // disable mouse command
-    u8 mouse_data1;
-    get_mouse_data(&mouse_data1);
-    set_code_success(regs);
+    int ret = disable_mouse();
+    if (ret)
+        set_code_fail(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
 }
 
-#define BX_DEBUG_INT15(args...)
-
 // Enable Mouse
 static void
 mouse_15c20001(struct bregs *regs)
 {
     u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
     if ((mouse_flags_2 & 0x80) == 0) {
-        BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
         set_code_fail(regs, RET_ENOHANDLER);
         return;
     }
-    inhibit_mouse_int_and_events(); // disable IRQ12 and packets
-    send_to_mouse_ctrl(0xF4); // enable mouse command
-    u8 mouse_data1;
-    get_mouse_data(&mouse_data1);
-    if (mouse_data1 == 0xFA) {
-        enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
+
+    u8 ps2ctr = GET_EBDA(ps2ctr);
+    ps2ctr &= ~I8042_CTR_AUXDIS;
+    ps2ctr |= I8042_CTR_AUXINT;
+    SET_EBDA(ps2ctr, ps2ctr);
+
+    int ret = aux_command(PSMOUSE_CMD_ENABLE, NULL);
+    if (ret)
+        set_code_fail(regs, RET_ENEEDRESEND);
+    else
         set_code_success(regs);
-        return;
-    }
-    set_code_fail(regs, RET_ENEEDRESEND);
 }
 
 static void
@@ -156,24 +95,14 @@ mouse_15c200(struct bregs *regs)
 static void
 mouse_15c201(struct bregs *regs)
 {
-    inhibit_mouse_int_and_events(); // disable IRQ12 and packets
-    send_to_mouse_ctrl(0xFF); // reset mouse command
-    u8 mouse_data1, mouse_data2, mouse_data3;
-    get_mouse_data(&mouse_data3);
-    // if no mouse attached, it will return RESEND
-    if (mouse_data3 == 0xfe) {
+    u8 param[2];
+    int ret = aux_command(PSMOUSE_CMD_RESET_BAT, param);
+    if (ret) {
         set_code_fail(regs, RET_ENEEDRESEND);
         return;
     }
-    if (mouse_data3 != 0xfa)
-        BX_PANIC("Mouse reset returned %02x (should be ack)\n"
-                 , (unsigned)mouse_data3);
-    get_mouse_data(&mouse_data1);
-    get_mouse_data(&mouse_data2);
-    // turn IRQ12 and packet generation on
-    enable_mouse_int_and_events();
-    regs->bl = mouse_data1;
-    regs->bh = mouse_data2;
+    regs->bl = param[0];
+    regs->bh = param[1];
     set_code_success(regs);
 }
 
@@ -181,19 +110,17 @@ mouse_15c201(struct bregs *regs)
 static void
 mouse_15c202(struct bregs *regs)
 {
-    if (regs->bh >= 7) {
-        set_code_fail(regs, RET_EUNSUPPORTED);
+    static u8 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200};
+    if (regs->bh >= ARRAY_SIZE(sample_rates)) {
+        set_code_fail(regs, RET_EINVINPUT);
         return;
     }
-    u8 mouse_data1 = regs->bh * 20;
-    if (!mouse_data1)
-        mouse_data1 = 10;
-    send_to_mouse_ctrl(0xF3); // set sample rate command
-    u8 mouse_data2;
-    get_mouse_data(&mouse_data2);
-    send_to_mouse_ctrl(mouse_data1);
-    get_mouse_data(&mouse_data2);
-    set_code_success(regs);
+    u8 mouse_data1 = GET_VAR(CS, sample_rates[regs->bh]);
+    int ret = aux_command(PSMOUSE_CMD_SETRATE, &mouse_data1);
+    if (ret)
+        set_code_fail(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
 }
 
 // Set Resolution
@@ -205,38 +132,29 @@ mouse_15c203(struct bregs *regs)
     //      1 =  50 dpi, 2 counts per millimeter
     //      2 = 100 dpi, 4 counts per millimeter
     //      3 = 200 dpi, 8 counts per millimeter
-    u8 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
     if (regs->bh >= 4) {
-        set_code_fail(regs, RET_EUNSUPPORTED);
-        goto done;
+        set_code_fail(regs, RET_EINVINPUT);
+        return;
     }
-    send_to_mouse_ctrl(0xE8); // set resolution command
-    u8 mouse_data1;
-    get_mouse_data(&mouse_data1);
-    if (mouse_data1 != 0xfa)
-        BX_PANIC("Mouse status returned %02x (should be ack)\n"
-                 , (unsigned)mouse_data1);
-    send_to_mouse_ctrl(regs->bh);
-    get_mouse_data(&mouse_data1);
-    if (mouse_data1 != 0xfa)
-        BX_PANIC("Mouse status returned %02x (should be ack)\n"
-                 , (unsigned)mouse_data1);
-    set_code_success(regs);
-
-done:
-    set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
+    u8 param = regs->bh;
+    int ret = aux_command(PSMOUSE_CMD_SETRES, &param);
+    if (ret)
+        set_code_fail(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
 }
 
 // Get Device ID
 static void
 mouse_15c204(struct bregs *regs)
 {
-    inhibit_mouse_int_and_events(); // disable IRQ12 and packets
-    send_to_mouse_ctrl(0xF2); // get mouse ID command
-    u8 mouse_data1, mouse_data2;
-    get_mouse_data(&mouse_data1);
-    get_mouse_data(&mouse_data2);
-    regs->bh = mouse_data2;
+    u8 param[2];
+    int ret = aux_command(PSMOUSE_CMD_GETID, param);
+    if (ret) {
+        set_code_fail(regs, RET_ENEEDRESEND);
+        return;
+    }
+    regs->bh = param[0];
     set_code_success(regs);
 }
 
@@ -259,55 +177,44 @@ mouse_15c205(struct bregs *regs)
 static void
 mouse_15c20600(struct bregs *regs)
 {
-    u8 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
-    send_to_mouse_ctrl(0xE9); // get mouse info command
-    u8 mouse_data1, mouse_data2, mouse_data3;
-    get_mouse_data(&mouse_data1);
-    if (mouse_data1 != 0xfa)
-        BX_PANIC("Mouse status returned %02x (should be ack)\n"
-                 , (unsigned)mouse_data1);
-    get_mouse_data(&mouse_data1);
-    get_mouse_data(&mouse_data2);
-    get_mouse_data(&mouse_data3);
-    regs->bl = mouse_data1;
-    regs->cl = mouse_data2;
-    regs->dl = mouse_data3;
+    u8 param[3];
+    int ret = aux_command(PSMOUSE_CMD_GETINFO, param);
+    if (ret) {
+        set_code_fail(regs, RET_ENEEDRESEND);
+        return;
+    }
+    regs->bl = param[0];
+    regs->cl = param[1];
+    regs->dl = param[2];
     set_code_success(regs);
-    set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
-}
-
-static void
-set_scaling(struct bregs *regs, u8 cmd)
-{
-    u8 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
-    send_to_mouse_ctrl(0xE6);
-    u8 mouse_data1;
-    get_mouse_data(&mouse_data1);
-    if (mouse_data1 != 0xFA)
-        set_code_fail(regs, RET_EUNSUPPORTED);
-    else
-        set_code_success(regs);
-    set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
 }
 
 // Set Scaling Factor to 1:1
 static void
 mouse_15c20601(struct bregs *regs)
 {
-    set_scaling(regs, 0xE6);
+    int ret = aux_command(PSMOUSE_CMD_SETSCALE11, NULL);
+    if (ret)
+        set_code_fail(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
 }
 
 // Set Scaling Factor to 2:1
 static void
 mouse_15c20602(struct bregs *regs)
 {
-    set_scaling(regs, 0xE7);
+    int ret = aux_command(PSMOUSE_CMD_SETSCALE21, NULL);
+    if (ret)
+        set_code_fail(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
 }
 
 static void
 mouse_15c206XX(struct bregs *regs)
 {
-    BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", regs->bh);
+    set_code_fail(regs, RET_EINVFUNCTION);
 }
 
 // Return Status & Set Scaling Factor...
@@ -327,19 +234,19 @@ static void
 mouse_15c207(struct bregs *regs)
 {
     u32 farptr = (regs->es << 16) | regs->bx;
-    SET_EBDA(far_call_pointer, farptr);
     u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
     if (! farptr) {
         /* remove handler */
         if ((mouse_flags_2 & 0x80) != 0) {
             mouse_flags_2 &= ~0x80;
-            inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+            disable_mouse();
         }
     } else {
         /* install handler */
         mouse_flags_2 |= 0x80;
     }
     SET_EBDA(mouse_flag2, mouse_flags_2);
+    SET_EBDA(far_call_pointer, farptr);
     set_code_success(regs);
 }
 
@@ -359,6 +266,8 @@ handle_15c2(struct bregs *regs)
         return;
     }
 
+    irq_enable();
+
     switch (regs->al) {
     case 0x00: mouse_15c200(regs); break;
     case 0x01: mouse_15c201(regs); break;
@@ -376,9 +285,10 @@ static void
 int74_function()
 {
     u8 v = inb(PORT_PS2_STATUS);
-    if ((v & 0x21) != 0x21)
+    if ((v & 0x21) != 0x21) {
+        dprintf(1, "int74 but no mouse data.\n");
         return;
-
+    }
     v = inb(PORT_PS2_DATA);
 
     u8 mouse_flags_1 = GET_EBDA(mouse_flag1);
diff --git a/src/ps2port.c b/src/ps2port.c
new file mode 100644 (file)
index 0000000..f707064
--- /dev/null
@@ -0,0 +1,240 @@
+// Support for handling the PS/2 mouse/keyboard ports.
+//
+// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Based on code Copyright (c) 1999-2004 Vojtech Pavlik
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "ioport.h" // inb
+#include "util.h" // dprintf
+#include "biosvar.h" // GET_EBDA
+#include "ps2port.h" // kbd_command
+
+
+/****************************************************************
+ * Low level i8042 commands.
+ ****************************************************************/
+
+// Timeout value.
+#define I8042_CTL_TIMEOUT       10000
+
+#define I8042_BUFFER_SIZE       16
+
+static void
+udelay(int usecs)
+{
+    // XXX - implement real udelay
+    outb(0x00, PORT_DIAG);
+}
+
+static int
+i8042_wait_read(void)
+{
+    int i;
+    for (i=0; i<I8042_CTL_TIMEOUT; i++) {
+        u8 status = inb(PORT_PS2_STATUS);
+        if (status & I8042_STR_OBF)
+            return 0;
+        udelay(50);
+    }
+    dprintf(1, "i8042 timeout on wait read\n");
+    return -1;
+}
+
+static int
+i8042_wait_write(void)
+{
+    int i;
+    for (i=0; i<I8042_CTL_TIMEOUT; i++) {
+        u8 status = inb(PORT_PS2_STATUS);
+        if (! (status & I8042_STR_IBF))
+            return 0;
+        udelay(50);
+    }
+    dprintf(1, "i8042 timeout on wait write\n");
+    return -1;
+}
+
+int
+i8042_flush(void)
+{
+    unsigned long flags = irq_save();
+
+    int i;
+    for (i=0; i<I8042_BUFFER_SIZE; i++) {
+        u8 status = inb(PORT_PS2_STATUS);
+        if (! (status & I8042_STR_OBF)) {
+            irq_restore(flags);
+            return 0;
+        }
+        udelay(50);
+        inb(PORT_PS2_DATA);
+    }
+
+    irq_restore(flags);
+    dprintf(1, "i8042 timeout on flush\n");
+    return -1;
+}
+
+static int
+__i8042_command(int command, u8 *param)
+{
+    int receive = (command >> 8) & 0xf;
+    int send = (command >> 12) & 0xf;
+
+    // Send the command.
+    int ret = i8042_wait_write();
+    if (ret)
+        return ret;
+    outb(command, PORT_PS2_STATUS);
+
+    // Send parameters (if any).
+    int i;
+    for (i = 0; i < send; i++) {
+        ret = i8042_wait_write();
+        if (ret)
+            return ret;
+        outb(param[i], PORT_PS2_DATA);
+    }
+
+    // Receive parameters (if any).
+    for (i = 0; i < receive; i++) {
+        ret = i8042_wait_read();
+        if (ret)
+            return ret;
+        param[i] = inb(PORT_PS2_DATA);
+    }
+
+    return 0;
+}
+
+int
+i8042_command(int command, u8 *param)
+{
+    unsigned long flags = irq_save();
+    int ret = __i8042_command(command, param);
+    irq_restore(flags);
+    if (ret)
+        dprintf(2, "i8042 command %x failed\n", command);
+    return ret;
+}
+
+static int
+i8042_kbd_write(u8 c)
+{
+    unsigned long flags = irq_save();
+
+    int ret = i8042_wait_write();
+    if (! ret)
+        outb(c, PORT_PS2_DATA);
+
+    irq_restore(flags);
+
+    return ret;
+}
+
+static int
+i8042_aux_write(u8 c)
+{
+    return i8042_command(I8042_CMD_AUX_SEND, &c);
+}
+
+
+/****************************************************************
+ * Device commands.
+ ****************************************************************/
+
+#define PS2_RET_ACK             0xfa
+#define PS2_RET_NAK             0xfe
+
+static int
+ps2_sendbyte(int aux, u8 command)
+{
+    int ret;
+    if (aux)
+        ret = i8042_aux_write(command);
+    else
+        ret = i8042_kbd_write(command);
+    if (ret)
+        return ret;
+
+    // Read ack.
+    ret = i8042_wait_read();
+    if (ret)
+        return ret;
+    u8 ack = inb(PORT_PS2_DATA);
+    if (ack != PS2_RET_ACK) {
+        dprintf(1, "Missing ack (got %x not %x)\n", ack, PS2_RET_ACK);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+ps2_command(int aux, int command, u8 *param)
+{
+    int ret2;
+    int receive = (command >> 8) & 0xf;
+    int send = (command >> 12) & 0xf;
+
+    // Disable interrupts and keyboard/mouse.
+    u8 ps2ctr = GET_EBDA(ps2ctr);
+    u8 newctr = ps2ctr;
+    if (aux)
+        newctr |= I8042_CTR_KBDDIS;
+    else
+        newctr |= I8042_CTR_AUXDIS;
+    newctr &= ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT);
+    dprintf(6, "i8042 ctr old=%x new=%x\n", ps2ctr, newctr);
+    int ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr);
+    if (ret)
+        return ret;
+
+    // Send command.
+    ret = ps2_sendbyte(aux, command);
+    if (ret)
+        goto fail;
+
+    // Send parameters (if any).
+    int i;
+    for (i = 0; i < send; i++) {
+        ret = ps2_sendbyte(aux, command);
+        if (ret)
+            goto fail;
+    }
+
+    // Receive parameters (if any).
+    for (i = 0; i < receive; i++) {
+        ret = i8042_wait_read();
+        if (ret)
+            goto fail;
+        param[i] = inb(PORT_PS2_DATA);
+    }
+
+fail:
+    // Restore interrupts and keyboard/mouse.
+    ret2 = i8042_command(I8042_CMD_CTL_WCTR, &ps2ctr);
+    if (ret2)
+        return ret2;
+
+    return ret;
+}
+
+int
+kbd_command(int command, u8 *param)
+{
+    int ret = ps2_command(0, command, param);
+    if (ret)
+        dprintf(2, "keyboard command %x failed\n", command);
+    return ret;
+}
+
+int
+aux_command(int command, u8 *param)
+{
+    int ret = ps2_command(1, command, param);
+    if (ret)
+        dprintf(2, "mouse command %x failed\n", command);
+    return ret;
+}
diff --git a/src/ps2port.h b/src/ps2port.h
new file mode 100644 (file)
index 0000000..fa73a06
--- /dev/null
@@ -0,0 +1,62 @@
+// Basic ps2 port (keyboard/mouse) command handling.
+#ifndef __PS2PORT_H
+#define __PS2PORT_H
+
+#include "types.h" // u8
+
+// Standard commands.
+#define I8042_CMD_CTL_RCTR      0x0120
+#define I8042_CMD_CTL_WCTR      0x1060
+#define I8042_CMD_CTL_TEST      0x01aa
+
+#define I8042_CMD_KBD_TEST      0x01ab
+#define I8042_CMD_KBD_DISABLE   0x00ad
+#define I8042_CMD_KBD_ENABLE    0x00ae
+
+#define I8042_CMD_AUX_DISABLE   0x00a7
+#define I8042_CMD_AUX_ENABLE    0x00a8
+#define I8042_CMD_AUX_SEND      0x10d4
+
+// Keyboard commands
+#define ATKBD_CMD_SETLEDS       0x10ed
+#define ATKBD_CMD_GETID         0x02f2
+#define ATKBD_CMD_ENABLE        0x00f4
+#define ATKBD_CMD_RESET_DIS     0x00f5
+#define ATKBD_CMD_RESET_BAT     0x01ff
+
+// Mouse commands
+#define PSMOUSE_CMD_SETSCALE11  0x00e6
+#define PSMOUSE_CMD_SETSCALE21  0x00e7
+#define PSMOUSE_CMD_SETRES      0x10e8
+#define PSMOUSE_CMD_GETINFO     0x03e9
+#define PSMOUSE_CMD_GETID       0x02f2
+#define PSMOUSE_CMD_SETRATE     0x10f3
+#define PSMOUSE_CMD_ENABLE      0x00f4
+#define PSMOUSE_CMD_DISABLE     0x00f5
+#define PSMOUSE_CMD_RESET_BAT   0x02ff
+
+// Status register bits.
+#define I8042_STR_PARITY        0x80
+#define I8042_STR_TIMEOUT       0x40
+#define I8042_STR_AUXDATA       0x20
+#define I8042_STR_KEYLOCK       0x10
+#define I8042_STR_CMDDAT        0x08
+#define I8042_STR_MUXERR        0x04
+#define I8042_STR_IBF           0x02
+#define I8042_STR_OBF           0x01
+
+// Control register bits.
+#define I8042_CTR_KBDINT        0x01
+#define I8042_CTR_AUXINT        0x02
+#define I8042_CTR_IGNKEYLOCK    0x08
+#define I8042_CTR_KBDDIS        0x10
+#define I8042_CTR_AUXDIS        0x20
+#define I8042_CTR_XLATE         0x40
+
+// functions
+int i8042_flush(void);
+int i8042_command(int command, u8 *param);
+int kbd_command(int command, u8 *param);
+int aux_command(int command, u8 *param);
+
+#endif // ps2port.h