grml...
[seabios.git] / src / mouse.c
index 6364c68621491286721327b87314786731a7d3aa..e26cf694ca5bcc37791d533c564071f1ee863efd 100644 (file)
 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
 // Copyright (C) 2002  MandrakeSoft S.A.
 //
-// This file may be distributed under the terms of the GNU GPLv3 license.
+// This file may be distributed under the terms of the GNU LGPLv3 license.
 
-#include "biosvar.h" // struct bregs
-#include "util.h" // debug_enter
+#include "biosvar.h" // GET_EBDA
+#include "util.h" // debug_isr
+#include "pic.h" // eoi_pic2
+#include "bregs.h" // struct bregs
+#include "ps2port.h" // ps2_mouse_command
+#include "usb-hid.h" // usb_mouse_command
 
 void
-handle_15c2(struct bregs *regs)
+mouse_setup(void)
+{
+    if (! CONFIG_MOUSE)
+        return;
+    dprintf(3, "init mouse\n");
+    // pointing device installed
+    SETBITS_BDA(equipment_list_flags, 0x04);
+}
+
+static inline int
+mouse_command(int command, u8 *param)
 {
+    if (usb_mouse_active())
+        return usb_mouse_command(command, param);
+    return ps2_mouse_command(command, param);
 }
 
-static u8
-int74_function()
+#define RET_SUCCESS      0x00
+#define RET_EINVFUNCTION 0x01
+#define RET_EINVINPUT    0x02
+#define RET_EINTERFACE   0x03
+#define RET_ENEEDRESEND  0x04
+#define RET_ENOHANDLER   0x05
+
+// Disable Mouse
+static void
+mouse_15c20000(struct bregs *regs)
 {
-    return 0;
+    int ret = mouse_command(PSMOUSE_CMD_DISABLE, NULL);
+    if (ret)
+        set_code_invalid(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
 }
 
-// INT74h : PS/2 mouse hardware interrupt
-void VISIBLE
-handle_74(struct bregs *regs)
+// Enable Mouse
+static void
+mouse_15c20001(struct bregs *regs)
 {
-    debug_enter(regs);
+    u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
+    if ((mouse_flags_2 & 0x80) == 0) {
+        set_code_invalid(regs, RET_ENOHANDLER);
+        return;
+    }
+
+    int ret = mouse_command(PSMOUSE_CMD_ENABLE, NULL);
+    if (ret)
+        set_code_invalid(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
+}
+
+static void
+mouse_15c200XX(struct bregs *regs)
+{
+    set_code_unimplemented(regs, RET_EINVFUNCTION);
+}
 
-    irq_enable();
-    u8 ret = int74_function();
+// Disable/Enable Mouse
+static void
+mouse_15c200(struct bregs *regs)
+{
+    switch (regs->bh) {
+    case 0x00: mouse_15c20000(regs); break;
+    case 0x01: mouse_15c20001(regs); break;
+    default:   mouse_15c200XX(regs); break;
+    }
+}
+
+// Reset Mouse
+static void
+mouse_15c201(struct bregs *regs)
+{
+    u8 param[2];
+    int ret = mouse_command(PSMOUSE_CMD_RESET_BAT, param);
     if (ret) {
-        // XXX - far call to ptr at ebda:0022
+        set_code_invalid(regs, RET_ENEEDRESEND);
+        return;
     }
-    irq_disable();
-    eoi_both_pics();
+    regs->bl = param[0];
+    regs->bh = param[1];
+    set_code_success(regs);
+}
+
+// Set Sample Rate
+static void
+mouse_15c202(struct bregs *regs)
+{
+    static u8 VAR16 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200};
+    if (regs->bh >= ARRAY_SIZE(sample_rates)) {
+        set_code_invalid(regs, RET_EINVINPUT);
+        return;
+    }
+    u8 mouse_data1 = GET_GLOBAL(sample_rates[regs->bh]);
+    int ret = mouse_command(PSMOUSE_CMD_SETRATE, &mouse_data1);
+    if (ret)
+        set_code_invalid(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
+}
+
+// Set Resolution
+static void
+mouse_15c203(struct bregs *regs)
+{
+    // BH:
+    //      0 =  25 dpi, 1 count  per millimeter
+    //      1 =  50 dpi, 2 counts per millimeter
+    //      2 = 100 dpi, 4 counts per millimeter
+    //      3 = 200 dpi, 8 counts per millimeter
+    if (regs->bh >= 4) {
+        set_code_invalid(regs, RET_EINVINPUT);
+        return;
+    }
+    u8 param = regs->bh;
+    int ret = mouse_command(PSMOUSE_CMD_SETRES, &param);
+    if (ret)
+        set_code_invalid(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
+}
+
+// Get Device ID
+static void
+mouse_15c204(struct bregs *regs)
+{
+    u8 param[2];
+    int ret = mouse_command(PSMOUSE_CMD_GETID, param);
+    if (ret) {
+        set_code_invalid(regs, RET_ENEEDRESEND);
+        return;
+    }
+    regs->bh = param[0];
+    set_code_success(regs);
+}
+
+// Initialize Mouse
+static void
+mouse_15c205(struct bregs *regs)
+{
+    if (regs->bh != 3) {
+        set_code_invalid(regs, RET_EINTERFACE);
+        return;
+    }
+    u16 ebda_seg = get_ebda_seg();
+    SET_EBDA2(ebda_seg, mouse_flag1, 0x00);
+    SET_EBDA2(ebda_seg, mouse_flag2, regs->bh);
+
+    // Reset Mouse
+    mouse_15c201(regs);
+}
+
+// Return Status
+static void
+mouse_15c20600(struct bregs *regs)
+{
+    u8 param[3];
+    int ret = mouse_command(PSMOUSE_CMD_GETINFO, param);
+    if (ret) {
+        set_code_invalid(regs, RET_ENEEDRESEND);
+        return;
+    }
+    regs->bl = param[0];
+    regs->cl = param[1];
+    regs->dl = param[2];
+    set_code_success(regs);
+}
+
+// Set Scaling Factor to 1:1
+static void
+mouse_15c20601(struct bregs *regs)
+{
+    int ret = mouse_command(PSMOUSE_CMD_SETSCALE11, NULL);
+    if (ret)
+        set_code_invalid(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
+}
+
+// Set Scaling Factor to 2:1
+static void
+mouse_15c20602(struct bregs *regs)
+{
+    int ret = mouse_command(PSMOUSE_CMD_SETSCALE21, NULL);
+    if (ret)
+        set_code_invalid(regs, RET_ENEEDRESEND);
+    else
+        set_code_success(regs);
+}
+
+static void
+mouse_15c206XX(struct bregs *regs)
+{
+    set_code_unimplemented(regs, RET_EINVFUNCTION);
+}
+
+// Return Status & Set Scaling Factor...
+static void
+mouse_15c206(struct bregs *regs)
+{
+    switch (regs->bh) {
+    case 0x00: mouse_15c20600(regs); break;
+    case 0x01: mouse_15c20601(regs); break;
+    case 0x02: mouse_15c20602(regs); break;
+    default:   mouse_15c206XX(regs); break;
+    }
+}
+
+// Set Mouse Handler Address
+static void
+mouse_15c207(struct bregs *regs)
+{
+    struct segoff_s farptr = SEGOFF(regs->es, regs->bx);
+    u16 ebda_seg = get_ebda_seg();
+    u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
+    if (! farptr.segoff) {
+        /* remove handler */
+        if ((mouse_flags_2 & 0x80) != 0) {
+            mouse_flags_2 &= ~0x80;
+            mouse_command(PSMOUSE_CMD_DISABLE, NULL);
+        }
+    } else {
+        /* install handler */
+        mouse_flags_2 |= 0x80;
+    }
+    SET_EBDA2(ebda_seg, mouse_flag2, mouse_flags_2);
+    SET_EBDA2(ebda_seg, far_call_pointer, farptr);
+    set_code_success(regs);
+}
+
+static void
+mouse_15c2XX(struct bregs *regs)
+{
+    set_code_unimplemented(regs, RET_EINVFUNCTION);
+}
+
+void
+handle_15c2(struct bregs *regs)
+{
+    //debug_stub(regs);
+
+    if (! CONFIG_MOUSE) {
+        set_code_invalid(regs, RET_EUNSUPPORTED);
+        return;
+    }
+
+    switch (regs->al) {
+    case 0x00: mouse_15c200(regs); break;
+    case 0x01: mouse_15c201(regs); break;
+    case 0x02: mouse_15c202(regs); break;
+    case 0x03: mouse_15c203(regs); break;
+    case 0x04: mouse_15c204(regs); break;
+    case 0x05: mouse_15c205(regs); break;
+    case 0x06: mouse_15c206(regs); break;
+    case 0x07: mouse_15c207(regs); break;
+    default:   mouse_15c2XX(regs); break;
+    }
+}
+
+void noinline
+process_mouse(u8 data)
+{
+    if (!CONFIG_MOUSE)
+        return;
+
+    u16 ebda_seg = get_ebda_seg();
+    u8 mouse_flags_1 = GET_EBDA2(ebda_seg, mouse_flag1);
+    u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
+
+    if (! (mouse_flags_2 & 0x80))
+        // far call handler not installed
+        return;
+
+    u8 package_count = mouse_flags_2 & 0x07;
+    u8 index = mouse_flags_1 & 0x07;
+    SET_EBDA2(ebda_seg, mouse_data[index], data);
+
+    if ((index+1) < package_count) {
+        mouse_flags_1++;
+        SET_EBDA2(ebda_seg, mouse_flag1, mouse_flags_1);
+        return;
+    }
+
+    u16 status = GET_EBDA2(ebda_seg, mouse_data[0]);
+    u16 X      = GET_EBDA2(ebda_seg, mouse_data[1]);
+    u16 Y      = GET_EBDA2(ebda_seg, mouse_data[2]);
+    SET_EBDA2(ebda_seg, mouse_flag1, 0);
+
+    struct segoff_s func = GET_EBDA2(ebda_seg, far_call_pointer);
+    dprintf(16, "mouse farcall s=%04x x=%04x y=%04x func=%04x:%04x\n"
+            , status, X, Y, func.seg, func.offset);
+
+    asm volatile(
+        "pushl %%ebp\n"
+        "sti\n"
+
+        "pushl %0\n"
+        "pushw %w1\n"  // status
+        "pushw %w2\n"  // X
+        "pushw %w3\n"  // Y
+        "pushw $0\n"   // Z
+        "lcallw *8(%%esp)\n"
+        "addl $12, %%esp\n"
+
+        "cli\n"
+        "cld\n"
+        "popl %%ebp"
+        : "+a"(func.segoff), "+c"(status), "+d"(X), "+b"(Y)
+        :
+        : "edi", "esi", "cc", "memory");
 }