#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 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; }