diff options
Diffstat (limited to 'src/VBox/Devices/PC/BIOS/ps2mouse.c')
-rw-r--r-- | src/VBox/Devices/PC/BIOS/ps2mouse.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/src/VBox/Devices/PC/BIOS/ps2mouse.c b/src/VBox/Devices/PC/BIOS/ps2mouse.c new file mode 100644 index 00000000..6b485f68 --- /dev/null +++ b/src/VBox/Devices/PC/BIOS/ps2mouse.c @@ -0,0 +1,452 @@ +/* $Id: ps2mouse.c $ */ +/** @file + * PC BIOS - ??? + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * -------------------------------------------------------------------- + * + * This code is based on: + * + * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment + * + * Copyright (C) 2002 MandrakeSoft S.A. + * + * MandrakeSoft S.A. + * 43, rue d'Aboukir + * 75002 Paris - France + * http://www.linux-mandrake.com/ + * http://www.mandrakesoft.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + + +#include <stdint.h> +#include "biosint.h" +#include "inlines.h" + + +#if DEBUG_INT15_MS +# define BX_DEBUG_INT15_MS(...) BX_DEBUG(__VA_ARGS__) +#else +# define BX_DEBUG_INT15_MS(...) +#endif + +#if DEBUG_INT74 +# define BX_DEBUG_INT74(...) BX_DEBUG(__VA_ARGS__) +#else +# define BX_DEBUG_INT74(...) +#endif + +#if BX_USE_PS2_MOUSE + +static const char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n"; + +uint8_t send_to_mouse_ctrl(uint8_t sendbyte) +{ + BX_DEBUG_INT15_MS("send %02x to mouse:\n", sendbyte); + // wait for chance to write to ctrl + if (inb(0x64) & 0x02) + BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse"); + outb(0x64, 0xD4); + outb(0x60, sendbyte); + return(0); +} + + +uint8_t get_mouse_data(uint8_t __far *data) +{ + int retries = 10000; /* ~150ms timeout */ + uint8_t response; + + while ((inb(0x64) & 0x21) != 0x21 && retries) + { + /* Wait until the 15us refresh counter toggles. */ + response = inb(0x61) & 0x10; + while((inb(0x61) & 0x10) == response) + ; + --retries; + } + + if (!retries) + return(1); + + response = inb(0x60); + *data = response; + return(0); +} + +void set_kbd_command_byte(uint8_t command_byte) +{ + if (inb(0x64) & 0x02) + BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm"); + + outb(0x64, 0x60); // write command byte + outb(0x60, command_byte); +} + + +void BIOSCALL int74_function(volatile uint16_t make_farcall, volatile uint16_t Z, + volatile uint16_t Y, volatile uint16_t X, volatile uint16_t status) +{ + uint16_t ebda_seg=read_word(0x0040,0x000E); + uint8_t in_byte, index, package_count; + uint8_t mouse_flags_1, mouse_flags_2; + + BX_DEBUG_INT74("entering int74_function\n"); + make_farcall = 0; + + in_byte = inb(0x64); + if ( (in_byte & 0x21) != 0x21 ) { + return; + } + in_byte = inb(0x60); + BX_DEBUG_INT74("int74: read byte %02x\n", in_byte); + + mouse_flags_1 = read_byte(ebda_seg, 0x0026); + mouse_flags_2 = read_byte(ebda_seg, 0x0027); + + if ( (mouse_flags_2 & 0x80) != 0x80 ) { + return; + } + + package_count = mouse_flags_2 & 0x07; + index = mouse_flags_1 & 0x07; + write_byte(ebda_seg, 0x28 + index, in_byte); + + if ( index >= package_count ) { + BX_DEBUG_INT74("int74_function: make_farcall=1\n"); + status = read_byte(ebda_seg, 0x0028 + 0); + X = read_byte(ebda_seg, 0x0028 + 1); + Y = read_byte(ebda_seg, 0x0028 + 2); + Z = 0; + mouse_flags_1 = 0; + // check if far call handler installed + if (mouse_flags_2 & 0x80) + make_farcall = 1; + } + else { + mouse_flags_1++; + } + write_byte(ebda_seg, 0x0026, mouse_flags_1); +} + +void BIOSCALL int15_function_mouse(pusha_regs_t regs, uint16_t ES, uint16_t DS, volatile uint16_t FLAGS) +{ + uint16_t ebda_seg=read_word(0x0040,0x000E); + uint8_t mouse_flags_1, mouse_flags_2; + uint16_t mouse_driver_seg; + uint16_t mouse_driver_offset; + uint8_t mouse_cmd; + uint8_t ret, mouse_data1, mouse_data2, mouse_data3; + + BX_DEBUG_INT15_MS("int15 AX=%04x\n",regs.u.r16.ax); + + // Return Codes status in AH + // ========================= + // 00: success + // 01: invalid subfunction (AL > 7) + // 02: invalid input value (out of allowable range) + // 03: interface error + // 04: resend command received from mouse controller, + // device driver should attempt command again + // 05: cannot enable mouse, since no far call has been installed + // 80/86: mouse service not implemented + + if (regs.u.r8.al > 7) { + BX_DEBUG_INT15_MS("unsupported subfn\n"); + // invalid function + SET_CF(); + regs.u.r8.ah = 1; + return; + } + + // Valid subfn; disable AUX input and IRQ12, assume no error + set_kbd_command_byte(0x65); + CLEAR_CF(); + regs.u.r8.ah = 0; + + switch (regs.u.r8.al) { + case 0: // Disable/Enable Mouse + BX_DEBUG_INT15_MS("case 0: "); + if (regs.u.r8.bh > 1) { + BX_DEBUG_INT15_MS("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh); + // invalid subfunction + SET_CF(); + regs.u.r8.ah = 1; + break; + } + mouse_flags_2 = read_byte(ebda_seg, 0x0027); + if ( (mouse_flags_2 & 0x80) == 0 ) { + BX_DEBUG_INT15_MS("INT 15h C2 Enable/Disable Mouse, no far call handler\n"); + SET_CF(); + regs.u.r8.ah = 5; // no far call installed + break; + } + if (regs.u.r8.bh == 0) { + BX_DEBUG_INT15_MS("Disable Mouse\n"); + mouse_cmd = 0xF5; // disable mouse command + } else { + BX_DEBUG_INT15_MS("Enable Mouse\n"); + mouse_cmd = 0xF4; // enable mouse command + } + + ret = send_to_mouse_ctrl(mouse_cmd); // disable mouse command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if ( (ret == 0) || (mouse_data1 == 0xFA) ) { + // success + break; + } + } + + // interface error + SET_CF(); + regs.u.r8.ah = 3; + break; + + case 5: // Initialize Mouse + // Valid package sizes are 1 to 8 + if ( (regs.u.r8.bh < 1) || (regs.u.r8.bh > 8) ) { + SET_CF(); + regs.u.r8.ah = 2; // invalid input + break; + } + mouse_flags_2 = read_byte(ebda_seg, 0x0027); + mouse_flags_2 = (mouse_flags_2 & 0xf8) | (regs.u.r8.bh - 1); + write_byte(ebda_seg, 0x0027, mouse_flags_2); + // fall through! + + case 1: // Reset Mouse + BX_DEBUG_INT15_MS("case 1 or 5:\n"); + // clear current package byte index + mouse_flags_1 = read_byte(ebda_seg, 0x0026); + mouse_flags_1 = mouse_flags_1 & 0xf8; + write_byte(ebda_seg, 0x0026, mouse_flags_1); + ret = send_to_mouse_ctrl(0xFF); // reset mouse command + if (ret == 0) { + ret = get_mouse_data(&mouse_data3); + // if no mouse attached, it will return RESEND + if (mouse_data3 == 0xfe) { + SET_CF(); + regs.u.r8.ah = 4; // resend + break; + } + if (mouse_data3 != 0xfa) + BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3); + if ( ret == 0 ) { + ret = get_mouse_data(&mouse_data1); + if ( ret == 0 ) { + ret = get_mouse_data(&mouse_data2); + if ( ret == 0 ) { + // success + regs.u.r8.bl = mouse_data1; + regs.u.r8.bh = mouse_data2; + break; + } + } + } + } + + // interface error + SET_CF(); + regs.u.r8.ah = 3; + break; + + case 2: // Set Sample Rate + BX_DEBUG_INT15_MS("case 2:\n"); + switch (regs.u.r8.bh) { + case 0: mouse_data1 = 10; break; // 10 reports/sec + case 1: mouse_data1 = 20; break; // 20 reports/sec + case 2: mouse_data1 = 40; break; // 40 reports/sec + case 3: mouse_data1 = 60; break; // 60 reports/sec + case 4: mouse_data1 = 80; break; // 80 reports/sec + case 5: mouse_data1 = 100; break; // 100 reports/sec (default) + case 6: mouse_data1 = 200; break; // 200 reports/sec + default: mouse_data1 = 0; + } + if (mouse_data1 > 0) { + ret = send_to_mouse_ctrl(0xF3); // set sample rate command + if (ret == 0) { + ret = get_mouse_data(&mouse_data2); + ret = send_to_mouse_ctrl(mouse_data1); + ret = get_mouse_data(&mouse_data2); + // success + } else { + // interface error + SET_CF(); + regs.u.r8.ah = 3; + } + } else { + // invalid input + SET_CF(); + regs.u.r8.ah = 2; + } + break; + + case 3: // Set Resolution + BX_DEBUG_INT15_MS("case 3:\n"); + // BX: + // 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.u.r8.bh < 4) { + ret = send_to_mouse_ctrl(0xE8); // set resolution command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if (mouse_data1 != 0xfa) + BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); + ret = send_to_mouse_ctrl(regs.u.r8.bh); + ret = get_mouse_data(&mouse_data1); + if (mouse_data1 != 0xfa) + BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); + // success + } else { + // interface error + SET_CF(); + regs.u.r8.ah = 3; + } + } else { + // invalid input + SET_CF(); + regs.u.r8.ah = 2; + } + break; + + case 4: // Get Device ID + BX_DEBUG_INT15_MS("case 4:\n"); + ret = send_to_mouse_ctrl(0xF2); // get mouse ID command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + ret = get_mouse_data(&mouse_data2); + regs.u.r8.bh = mouse_data2; + // success + } else { + // interface error + SET_CF(); + regs.u.r8.ah = 3; + } + break; + + case 6: // Return Status & Set Scaling Factor... + BX_DEBUG_INT15_MS("case 6:\n"); + switch (regs.u.r8.bh) { + case 0: // Return Status + ret = send_to_mouse_ctrl(0xE9); // get mouse info command + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if (mouse_data1 != 0xfa) + BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); + if (ret == 0) { + ret = get_mouse_data(&mouse_data1); + if ( ret == 0 ) { + ret = get_mouse_data(&mouse_data2); + if ( ret == 0 ) { + ret = get_mouse_data(&mouse_data3); + if ( ret == 0 ) { + regs.u.r8.bl = mouse_data1; + regs.u.r8.cl = mouse_data2; + regs.u.r8.dl = mouse_data3; + // success + break; + } + } + } + } + } + + // interface error + SET_CF(); + regs.u.r8.ah = 3; + break; + + case 1: // Set Scaling Factor to 1:1 + case 2: // Set Scaling Factor to 2:1 + if (regs.u.r8.bh == 1) { + ret = send_to_mouse_ctrl(0xE6); + } else { + ret = send_to_mouse_ctrl(0xE7); + } + if (ret == 0) { + get_mouse_data(&mouse_data1); + ret = (mouse_data1 != 0xFA); + } + if (ret != 0) { + // interface error + SET_CF(); + regs.u.r8.ah = 3; + } + break; + + default: + BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh); + // invalid subfunction + SET_CF(); + regs.u.r8.ah = 1; + } + break; + + case 7: // Set Mouse Handler Address + BX_DEBUG_INT15_MS("case 7:\n"); + mouse_driver_seg = ES; + mouse_driver_offset = regs.u.r16.bx; + write_word(ebda_seg, 0x0022, mouse_driver_offset); + write_word(ebda_seg, 0x0024, mouse_driver_seg); + mouse_flags_2 = read_byte(ebda_seg, 0x0027); + if (mouse_driver_offset == 0 && mouse_driver_seg == 0) { + /* remove handler */ + if ( (mouse_flags_2 & 0x80) != 0 ) { + mouse_flags_2 &= ~0x80; + } + } + else { + /* install handler */ + mouse_flags_2 |= 0x80; + } + write_byte(ebda_seg, 0x0027, mouse_flags_2); + break; + + default: + BX_PANIC("INT 15h C2 default case entered\n"); + // invalid subfunction + SET_CF(); + regs.u.r8.ah = 1; + } + BX_DEBUG_INT15_MS("returning cf = %u, ah = %02x\n", (unsigned)GET_CF(), (unsigned)regs.u.r8.ah); + // Re-enable AUX input and IRQ12 + set_kbd_command_byte(0x47); +} +#endif // BX_USE_PS2_MOUSE |