From 4d6dbc6f54c138ba92684d0bd6b7f42df5ccaa5b Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sun, 2 Mar 2008 08:43:44 -0500 Subject: [PATCH] Initial support for ps/2 mouse. The code doesn't work, but most things are present. --- src/biosvar.h | 11 +- src/mouse.c | 406 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 407 insertions(+), 10 deletions(-) diff --git a/src/biosvar.h b/src/biosvar.h index a5afe54..c34bb66 100644 --- a/src/biosvar.h +++ b/src/biosvar.h @@ -224,8 +224,15 @@ struct cdemu_s { struct extended_bios_data_area_s { u8 size; - u8 other1[0x3c]; - + u8 reserved1[0x21]; + u32 far_call_pointer; + u8 mouse_flag1; + u8 mouse_flag2; + u8 mouse_data[0x08]; + // 0x30 + u8 other1[0x0d]; + + // 0x3d struct fdpt_s fdpt0; struct fdpt_s fdpt1; diff --git a/src/mouse.c b/src/mouse.c index e99700a..e3c1789 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -8,17 +8,409 @@ #include "biosvar.h" // struct bregs #include "util.h" // debug_enter +static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n"; + +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 +#define RET_EINTERFACE 0x03 +#define RET_ENEEDRESEND 0x04 +#define RET_ENOHANDLER 0x05 + +// 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); + handle_ret(regs, RET_SUCCESS); +} + +#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"); + handle_ret(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 + handle_ret(regs, RET_SUCCESS); + return; + } + handle_ret(regs, RET_ENEEDRESEND); +} + +static void +mouse_15c200XX(struct bregs *regs) +{ + handle_ret(regs, RET_EINVFUNCTION); +} + +// 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) +{ + 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) { + handle_ret(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; + handle_ret(regs, RET_SUCCESS); +} + +// Set Sample Rate +static void +mouse_15c202(struct bregs *regs) +{ + if (regs->bh >= 7) { + handle_ret(regs, RET_EUNSUPPORTED); + 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); + handle_ret(regs, RET_SUCCESS); +} + +// 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 + bprintf(0, "ebda_seg=%x\n", GET_BDA(ebda_seg)); + u8 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets + if (regs->bh >= 4) { + handle_ret(regs, RET_EUNSUPPORTED); + goto done; + } + 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); + handle_ret(regs, RET_SUCCESS); + +done: + set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable +} + +// 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; + handle_ret(regs, RET_SUCCESS); +} + +// Initialize Mouse +static void +mouse_15c205(struct bregs *regs) +{ + if (regs->bh != 3) { + handle_ret(regs, RET_EINTERFACE); + return; + } + bprintf(0, "ebda_seg=%x\n", GET_BDA(ebda_seg)); + SET_EBDA(mouse_flag1, 0x00); + SET_EBDA(mouse_flag2, regs->bh); + + // Reset Mouse + mouse_15c201(regs); +} + +// Return Status +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; + handle_ret(regs, RET_SUCCESS); + 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) + handle_ret(regs, RET_EUNSUPPORTED); + else + handle_ret(regs, RET_SUCCESS); + 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); +} + +// Set Scaling Factor to 2:1 +static void +mouse_15c20602(struct bregs *regs) +{ + set_scaling(regs, 0xE7); +} + +static void +mouse_15c206XX(struct bregs *regs) +{ + BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", regs->bh); +} + +// 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) +{ + 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 + } + } else { + /* install handler */ + mouse_flags_2 |= 0x80; + } + SET_EBDA(mouse_flag2, mouse_flags_2); + handle_ret(regs, RET_SUCCESS); +} + +static void +mouse_15c2XX(struct bregs *regs) +{ + handle_ret(regs, RET_EINVFUNCTION); +} + void handle_15c2(struct bregs *regs) { - // XXX + debug_stub(regs); + + if (! CONFIG_PS2_MOUSE) { + handle_ret(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; + } } -static u8 +static void int74_function() { - // XXX - return 0; + u8 v = inb(PORT_PS2_STATUS); + if ((v & 0x21) != 0x21) + return; + + v = inb(PORT_PS2_DATA); + + u8 mouse_flags_1 = GET_EBDA(mouse_flag1); + u8 mouse_flags_2 = GET_EBDA(mouse_flag2); + + if ((mouse_flags_2 & 0x80) != 0x80) + return; + + u8 package_count = mouse_flags_2 & 0x07; + u8 index = mouse_flags_1 & 0x07; + SET_EBDA(mouse_data[index], v); + + if ((index+1) < package_count) { + mouse_flags_1++; + SET_EBDA(mouse_flag1, mouse_flags_1); + return; + } + + //BX_DEBUG_INT74("int74_function: make_farcall=1\n"); + u16 status = GET_EBDA(mouse_data[0]); + u16 X = GET_EBDA(mouse_data[1]); + u16 Y = GET_EBDA(mouse_data[2]); + SET_EBDA(mouse_flag1, 0); + // check if far call handler installed + if (! (mouse_flags_2 & 0x80)) + return; + + u32 func = GET_EBDA(far_call_pointer); + asm volatile( + "pushl %%ebp\n" + "pushfl\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" + "popfl\n" + "popl %%ebp\n" + : "+a" (func), "+b" (status), "+c" (X), "+d" (Y) + : + : "esi", "edi" + ); } // INT74h : PS/2 mouse hardware interrupt @@ -28,10 +420,8 @@ handle_74(struct bregs *regs) debug_enter(regs); irq_enable(); - u8 ret = int74_function(); - if (ret) { - // XXX - far call to ptr at ebda:0022 - } + int74_function(); irq_disable(); + eoi_both_pics(); } -- 2.25.1