Luna/kernel/src/arch/x86_64/PS2Mouse.cpp

119 lines
2.9 KiB
C++
Raw Normal View History

2023-08-02 11:55:45 +02:00
#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);
2023-08-02 11:55:45 +02:00
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;
}