354 lines
8.1 KiB
C
354 lines
8.1 KiB
C
// 16bit code to handle mouse events.
|
|
//
|
|
// 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 LGPLv3 license.
|
|
|
|
#include "biosvar.h" // GET_EBDA
|
|
#include "util.h" // debug_isr
|
|
#include "pic.h" // eoi_pic2
|
|
#include "bregs.h" // struct bregs
|
|
#include "ps2port.h" // aux_command
|
|
|
|
void
|
|
mouse_setup()
|
|
{
|
|
if (! CONFIG_PS2_MOUSE)
|
|
return;
|
|
dprintf(3, "init mouse\n");
|
|
// pointing device installed
|
|
SETBITS_BDA(equipment_list_flags, 0x04);
|
|
enable_hwirq(12, entry_74);
|
|
}
|
|
|
|
#define RET_SUCCESS 0x00
|
|
#define RET_EINVFUNCTION 0x01
|
|
#define RET_EINVINPUT 0x02
|
|
#define RET_EINTERFACE 0x03
|
|
#define RET_ENEEDRESEND 0x04
|
|
#define RET_ENOHANDLER 0x05
|
|
|
|
static int
|
|
disable_mouse(u16 ebda_seg)
|
|
{
|
|
u8 ps2ctr = GET_EBDA2(ebda_seg, ps2ctr);
|
|
ps2ctr |= I8042_CTR_AUXDIS;
|
|
ps2ctr &= ~I8042_CTR_AUXINT;
|
|
SET_EBDA2(ebda_seg, ps2ctr, ps2ctr);
|
|
|
|
return aux_command(PSMOUSE_CMD_DISABLE, NULL);
|
|
}
|
|
|
|
// Disable Mouse
|
|
static void
|
|
mouse_15c20000(struct bregs *regs)
|
|
{
|
|
u16 ebda_seg = get_ebda_seg();
|
|
int ret = disable_mouse(ebda_seg);
|
|
if (ret)
|
|
set_code_fail(regs, RET_ENEEDRESEND);
|
|
else
|
|
set_code_success(regs);
|
|
}
|
|
|
|
// Enable Mouse
|
|
static void
|
|
mouse_15c20001(struct bregs *regs)
|
|
{
|
|
u16 ebda_seg = get_ebda_seg();
|
|
u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
|
|
if ((mouse_flags_2 & 0x80) == 0) {
|
|
set_code_fail(regs, RET_ENOHANDLER);
|
|
return;
|
|
}
|
|
|
|
u8 ps2ctr = GET_EBDA2(ebda_seg, ps2ctr);
|
|
ps2ctr &= ~I8042_CTR_AUXDIS;
|
|
ps2ctr |= I8042_CTR_AUXINT;
|
|
SET_EBDA2(ebda_seg, ps2ctr, ps2ctr);
|
|
|
|
int ret = aux_command(PSMOUSE_CMD_ENABLE, NULL);
|
|
if (ret)
|
|
set_code_fail(regs, RET_ENEEDRESEND);
|
|
else
|
|
set_code_success(regs);
|
|
}
|
|
|
|
static void
|
|
mouse_15c200XX(struct bregs *regs)
|
|
{
|
|
set_code_fail(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)
|
|
{
|
|
u8 param[2];
|
|
int ret = aux_command(PSMOUSE_CMD_RESET_BAT, param);
|
|
if (ret != 0 && ret != 2) {
|
|
set_code_fail(regs, RET_ENEEDRESEND);
|
|
return;
|
|
}
|
|
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_fail(regs, RET_EINVINPUT);
|
|
return;
|
|
}
|
|
u8 mouse_data1 = GET_GLOBAL(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
|
|
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_fail(regs, RET_EINVINPUT);
|
|
return;
|
|
}
|
|
u8 param = regs->bh;
|
|
int ret = aux_command(PSMOUSE_CMD_SETRES, ¶m);
|
|
if (ret)
|
|
set_code_fail(regs, RET_ENEEDRESEND);
|
|
else
|
|
set_code_success(regs);
|
|
}
|
|
|
|
// Get Device ID
|
|
static void
|
|
mouse_15c204(struct bregs *regs)
|
|
{
|
|
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);
|
|
}
|
|
|
|
// Initialize Mouse
|
|
static void
|
|
mouse_15c205(struct bregs *regs)
|
|
{
|
|
if (regs->bh != 3) {
|
|
set_code_fail(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 = 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 Scaling Factor to 1:1
|
|
static void
|
|
mouse_15c20601(struct bregs *regs)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
set_code_fail(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)
|
|
{
|
|
u32 farptr = (regs->es << 16) | regs->bx;
|
|
u16 ebda_seg = get_ebda_seg();
|
|
u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
|
|
if (! farptr) {
|
|
/* remove handler */
|
|
if ((mouse_flags_2 & 0x80) != 0) {
|
|
mouse_flags_2 &= ~0x80;
|
|
disable_mouse(ebda_seg);
|
|
}
|
|
} 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_fail(regs, RET_EINVFUNCTION);
|
|
}
|
|
|
|
void
|
|
handle_15c2(struct bregs *regs)
|
|
{
|
|
//debug_stub(regs);
|
|
|
|
if (! CONFIG_PS2_MOUSE) {
|
|
set_code_fail(regs, RET_EUNSUPPORTED);
|
|
return;
|
|
}
|
|
|
|
irq_enable();
|
|
|
|
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 void
|
|
process_mouse(u8 data)
|
|
{
|
|
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;
|
|
}
|
|
|
|
//BX_DEBUG_INT74("int74_function: make_farcall=1\n");
|
|
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);
|
|
|
|
u32 func = GET_EBDA2(ebda_seg, far_call_pointer);
|
|
|
|
irq_enable();
|
|
asm volatile(
|
|
"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"
|
|
"cld\n"
|
|
:
|
|
: "r"(func), "r"(status), "r"(X), "r"(Y)
|
|
: "cc"
|
|
);
|
|
irq_disable();
|
|
}
|
|
|
|
// INT74h : PS/2 mouse hardware interrupt
|
|
void VISIBLE16
|
|
handle_74()
|
|
{
|
|
debug_isr(DEBUG_ISR_74);
|
|
if (! CONFIG_PS2_MOUSE)
|
|
goto done;
|
|
|
|
u8 v = inb(PORT_PS2_STATUS);
|
|
if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA))
|
|
!= (I8042_STR_OBF|I8042_STR_AUXDATA)) {
|
|
dprintf(1, "mouse irq but no mouse data.\n");
|
|
goto done;
|
|
}
|
|
v = inb(PORT_PS2_DATA);
|
|
|
|
process_mouse(v);
|
|
|
|
done:
|
|
eoi_pic2();
|
|
}
|