119 lines
2.9 KiB
C++
119 lines
2.9 KiB
C++
#include "Log.h"
|
|
#include "api/Mouse.h"
|
|
#include "arch/CPU.h"
|
|
#include "arch/x86_64/IO.h"
|
|
#include "fs/devices/MouseDevice.h"
|
|
#include "thread/Thread.h"
|
|
|
|
extern Thread* g_io_thread;
|
|
|
|
static u8 g_mouse_packet[3];
|
|
static int g_index = 0;
|
|
|
|
#define PS2_MOUSE_Y_OVERFLOW 0x80
|
|
#define PS2_MOUSE_X_OVERFLOW 0x40
|
|
#define PS2_MOUSE_Y_SIGN 0x20
|
|
#define PS2_MOUSE_X_SIGN 0x10
|
|
#define PS2_MOUSE_ALWAYS_1 0x08
|
|
#define PS2_MOUSE_MIDDLE_BTN 0x04
|
|
#define PS2_MOUSE_RIGHT_BTN 0x02
|
|
#define PS2_MOUSE_LEFT_BTN 0x01
|
|
|
|
extern CircularQueue<moon::MousePacket, 20> mouse_queue;
|
|
|
|
static void process_mouse_event(u8 data)
|
|
{
|
|
if (g_index == 0)
|
|
{
|
|
// NOTE: https://wiki.osdev.org/Mouse_Input#PS2_Mouse says to discard the packet if X or Y overflow is 1, but
|
|
// https://wiki.osdev.org/PS2_Mouse uses it. Discard for now.
|
|
if (data & PS2_MOUSE_X_OVERFLOW) return;
|
|
if (data & PS2_MOUSE_Y_OVERFLOW) return;
|
|
|
|
if ((data & PS2_MOUSE_ALWAYS_1) == 0) return;
|
|
}
|
|
|
|
g_mouse_packet[g_index++] = data;
|
|
if (g_index < 3) return;
|
|
g_index = 0;
|
|
|
|
moon::MousePacket packet;
|
|
packet.xdelta = g_mouse_packet[1];
|
|
packet.ydelta = g_mouse_packet[2];
|
|
packet.buttons = 0;
|
|
|
|
u8 flags = g_mouse_packet[0];
|
|
if (flags & PS2_MOUSE_X_SIGN) packet.xdelta = -(256 - packet.xdelta);
|
|
if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -(256 - packet.ydelta);
|
|
|
|
if (flags & PS2_MOUSE_MIDDLE_BTN) packet.buttons |= moon::MouseButton::Middle;
|
|
if (flags & PS2_MOUSE_RIGHT_BTN) packet.buttons |= moon::MouseButton::Right;
|
|
if (flags & PS2_MOUSE_LEFT_BTN) packet.buttons |= moon::MouseButton::Left;
|
|
|
|
MouseDevice::add_mouse_event(packet);
|
|
}
|
|
|
|
static void mouse_interrupt(Registers*, void*)
|
|
{
|
|
const u8 data = IO::inb(0x60);
|
|
process_mouse_event(data);
|
|
}
|
|
|
|
#define PS2_MOUSE_TIMEOUT 100000
|
|
|
|
static void mouse_wait()
|
|
{
|
|
int timeout = PS2_MOUSE_TIMEOUT;
|
|
while (--timeout)
|
|
{
|
|
if ((IO::inb(0x64) & 0b10) == 0) return;
|
|
CPU::pause();
|
|
}
|
|
}
|
|
|
|
static void mouse_wait_for_input()
|
|
{
|
|
int timeout = PS2_MOUSE_TIMEOUT;
|
|
while (--timeout)
|
|
{
|
|
if (IO::inb(0x64) & 0b1) return;
|
|
CPU::pause();
|
|
}
|
|
}
|
|
|
|
static void mouse_write_data(u8 byte)
|
|
{
|
|
mouse_wait();
|
|
IO::outb(0x64, 0xD4);
|
|
mouse_wait();
|
|
IO::outb(0x60, byte);
|
|
}
|
|
|
|
void init_mouse()
|
|
{
|
|
CPU::register_interrupt(12, mouse_interrupt, nullptr);
|
|
|
|
IO::outb(0x64, 0xA8); // Enable PS/2 auxiliary port
|
|
|
|
mouse_wait();
|
|
IO::outb(0x64, 0x20); // Get Compaq status byte
|
|
mouse_wait_for_input();
|
|
u8 status = IO::inb(0x60);
|
|
status |= 0x02; // Enable IRQ12
|
|
status &= ~0x20; // Disable Mouse Clock
|
|
mouse_wait();
|
|
IO::outb(0x64, 0x60); // Set Compaq status byte
|
|
mouse_wait();
|
|
IO::outb(0x60, status);
|
|
|
|
mouse_write_data(0xF6); // Reset defaults
|
|
mouse_wait();
|
|
IO::inb(0x60);
|
|
|
|
mouse_write_data(0xF4); // Send automatic packets when the mouse moves or is clicked
|
|
mouse_wait();
|
|
IO::inb(0x60);
|
|
|
|
g_index = 0;
|
|
}
|