#include <stdlib.h>
#ifdef _DEUB
#define TRACE0(f) printf(f)
#define TRACE1(f, a) printf(f, a)
#define TRACE2(f, a, b) printf(f, a, b)
#else
#define TRACE0(f)
#define TRACE1(f, a)
#define TRACE2(f, a, b)
#endif
/*
* General keyboard defines --
* needed for i8042 port (PS/2 controller)
*/
#define KEYB_PORT 0x60 /* keyboard port */
#define KEYB_CTRL 0x64 /* keyboard controller port */
#define KCTRL_ENABLE_AUX 0xA8 /* enable aux port (PS/2 mouse) */
#define KCTRL_WRITE_CMD_BYTE 0x60 /* write to command register */
#define KCTRL_WRITE_AUX 0xD4 /* write next byte at port 60 to aux port */
/* flags for KCTRL_WRITE_CMD_BYTE */
#define KCTRL_IRQ1 0x01
#define KCTRL_IRQ12 0x02
#define KCTRL_SYS 0x04
#define KCTRL_OVERRIDE_INHIBIT 0x08
#define KCTRL_DISABLE_KEYB 0x10
#define KCTRL_DISABLE_AUX 0x20
#define KCTRL_TRANSLATE_XT 0x40
/* commands to keyboard */
#define KEYB_SET_LEDS 0xED
#define KEYB_SET_SCANCODE_SET 0xF0
#define KEYB_IDENTIFY 0xF2
#define KEYB_SET_TYPEMATIC 0xF3
#define KEYB_ENABLE 0xF4
#define KEYB_RESET_DISABLE 0xF5
#define KEYB_ALL_TYPM_MAKE_BRK 0xFA
/* default ACK from keyboard following command */
#define KEYB_ACK "\xFA"
/* commands to aux device (PS/2 mouse) */
#define AUX_INFORMATION 0xE9
#define AUX_ENABLE 0xF4
#define AUX_IDENTIFY 0xF2
#define AUX_RESET 0xFF
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;
typedef enum { false, true } bool;
#define msleep thrSleep
/*
* Ask the OS to call func() when the specified IRQ occurs.
* The parameter provided (context) will be passed to the function.
*/
void sysRegisterIrq(int irq, void (__cdecl *func)(void*, int), void* context);
/* Pauses execution for ms milliseconds */
void msleep(unsigned ms);
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifdef _MSC_VER
#pragma warning(disable:4035)
#define enable() __asm sti
byte in(word port)
{
__asm
{
xor eax, eax
mov dx, port
in al, dx
}
}
void out(word port, byte data)
{
__asm
{
movzx eax, data
mov dx, port
out dx, al
}
}
#else
static inline void enable() { asm("sti"); }
static inline byte in(word port)
{
byte ret;
asm("movw %1,%%dx ;"
"inb %%dx,%%al ;"
"movb %%al,%0" : "=r" (ret) : "r" (port) : "eax");
return ret;
}
#define out(port, value) \
asm("outb %%al, %%dx" : : "d" (port), "a" (value) : "eax", "edx")
#endif
static word interrupts;
typedef struct Ps2Mouse Ps2Mouse;
struct Ps2Mouse
{
bool has_wheel;
int x, y, xmax, xmin, ymax, ymin, wheel;
dword buttons;
};
void kbdWrite(word port, byte data)
{
dword timeout;
byte stat;
for (timeout = 500000L; timeout != 0; timeout--)
{
stat = in(KEYB_CTRL);
if ((stat & 0x02) == 0)
break;
}
if (timeout != 0)
out(port, data);
}
byte kbdRead()
{
unsigned long Timeout;
byte Stat, Data;
for (Timeout = 50000L; Timeout != 0; Timeout--)
{
Stat = in(KEYB_CTRL);
/* loop until 8042 output buffer full */
if ((Stat & 0x01) != 0)
{
Data = in(KEYB_PORT);
/* loop if parity error or receive timeout */
if((Stat & 0xC0) == 0)
return Data;
}
}
return -1;
}
byte kbdWriteRead(word port, byte data, const char* expect)
{
int RetVal;
kbdWrite(port, data);
for (; *expect; expect++)
{
RetVal = kbdRead();
if ((byte) *expect != RetVal)
{
TRACE2("[keyboard] error: expected 0x%x, got 0x%x\n",
*expect, RetVal);
return RetVal;
}
}
return 0;
}
void ps2Isr(void* ctx, int irq);
Ps2Mouse* ps2Init()
{
/* These strings nicked from gpm (I_imps2): I don't know how they work... */
static byte s1[] = { 0xF3, 0xC8, 0xF3, 0x64, 0xF3, 0x50, 0 };
static byte s2[] = { 0xF6, 0xE6, 0xF4, 0xF3, 0x64, 0xE8, 0x03, 0 };
Ps2Mouse* ctx;
const byte* ch;
byte id;
ctx = (Ps2Mouse*) malloc(sizeof(Ps2Mouse));
/* enable the aux port */
kbdWrite(KEYB_CTRL, KCTRL_ENABLE_AUX);
for (ch = s1; *ch; ch++)
{
kbdWrite(KEYB_CTRL, KCTRL_WRITE_AUX);
kbdWriteRead(KEYB_PORT, *ch, KEYB_ACK);
}
/* Bochs doesn't like this bit... */
#if 0
for (ch = s2; *ch; ch++)
{
kbdWrite(KEYB_CTRL, KCTRL_WRITE_AUX);
kbdWriteRead(KEYB_PORT, *ch, KEYB_ACK);
}
#endif
msleep(10);
/* Identify mouse -- regular PS/2 mice should return zero here.
Unfortunately, my Intellimouse PS/2 also returns zero unless it has
been given the string 's2' above. Bochs doesn't support wheeled mice
and panics when it receives the F6h above. Fix needed. */
kbdWrite(KEYB_CTRL, KCTRL_WRITE_AUX);
kbdWriteRead(KEYB_PORT, AUX_IDENTIFY, KEYB_ACK);
id = kbdRead();
ctx->has_wheel = id == 3;
kbdWrite(KEYB_CTRL, KCTRL_WRITE_AUX);
kbdWriteRead(KEYB_PORT, 0xF3, KEYB_ACK);
kbdWrite(KEYB_CTRL, KCTRL_WRITE_AUX);
kbdWriteRead(KEYB_PORT, 0xFF, KEYB_ACK);
kbdWrite(KEYB_CTRL, KCTRL_WRITE_AUX);
kbdWriteRead(KEYB_PORT, AUX_INFORMATION, KEYB_ACK);
TRACE1(L"[mouse] status = %d\n", kbdRead());
TRACE1(L"[mouse] resolution = %d\n", kbdRead());
TRACE1(L"[mouse] sample rate = %d\n", kbdRead());
/* enable aux device (mouse) */
kbdWrite(KEYB_CTRL, KCTRL_WRITE_AUX);
kbdWriteRead(KEYB_PORT, AUX_ENABLE, KEYB_ACK);
sysRegisterIrq(12, ps2Isr, ctx);
ctx->xmin = ctx->ymin = 0;
ctx->xmax = 320;
ctx->ymax = 200;
ctx->x = (ctx->xmin + ctx->xmax) / 2;
ctx->y = (ctx->ymin + ctx->ymax) / 2;
return ctx;
}
void ps2Delete(Ps2Mouse* ctx)
{
/* may need to shut down hardware here */
sysRegisterIrq(12, NULL, NULL);
free(ctx);
}
byte ps2AuxRead()
{
byte Stat, Data;
enable();
while (true)
{
while ((interrupts & 0x1000) == 0)
;
interrupts &= ~0x1000;
Stat = in(KEYB_CTRL);
if ((Stat & 0x01) != 0)
{
Data = in(KEYB_PORT);
/* loop if parity error or receive timeout */
if((Stat & 0xC0) == 0)
return Data;
}
}
return (byte) -1;
}
void ps2Packet(Ps2Mouse* ctx)
{
int but, dx, dy, dw;
byte buf[4];
//putwchar(L'[');
buf[0] = ps2AuxRead();
if (buf[0] == (byte) -1)
{
//putwchar(')');
return;
}
buf[1] = ps2AuxRead();
buf[2] = ps2AuxRead();
if (ctx->has_wheel)
buf[3] = ps2AuxRead();
else
buf[3] = 0;
//putwchar(L']');
//putwchar(L'\r');
/*putwchar(buf[0]);
putwchar(buf[1]);
putwchar(buf[2]);
putwchar(buf[3]);*/
/* Extract the data from the bytes read.
From svgalib ms.c. */
but = (buf[0] & 0x04) >> 1 | /* Middle */
(buf[0] & 0x02) >> 1 | /* Right */
(buf[0] & 0x01) << 2; /* Left */
dx = (buf[0] & 0x10) ? buf[1] - 256 : buf[1];
dy = (buf[0] & 0x20) ? -(buf[2] - 256) : -buf[2];
dw = (int) ((signed char) buf[3]);
if (dx > 5 || dx < -5)
dx *= 4;
if (dy > 5 || dy < -5)
dy *= 4;
ctx->x += dx;
ctx->y += dy;
ctx->x = min(ctx->x, ctx->xmax);
ctx->x = max(ctx->x, ctx->xmin);
ctx->y = min(ctx->y, ctx->ymax);
ctx->y = max(ctx->y, ctx->ymin);
ctx->buttons = but;
ctx->wheel += dw;
}
void ps2Isr(void* ctx, int irq)
{
static int in_isr = 0;
byte stat;
stat = in(KEYB_CTRL);
if ((stat & 0x01) != 0)
{
interrupts |= 1 << irq;
if (!in_isr)
{
in_isr++;
ps2Packet((Ps2Mouse*) ctx);
in_isr--;
}
}
}