/*
* Copyright (C) 2006-2011 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
*
*/
#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;
uint8_t response;
while ((inb(0x64) & 0x21) != 0x21 && retries)
--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