From e8f3dd4cf97d77e23bf47df8b314fdccea399261 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 2 Aug 2023 11:55:45 +0200 Subject: [PATCH] kernel: Add a PS/2 mouse driver --- kernel/CMakeLists.txt | 2 + kernel/src/api/Mouse.h | 22 +++++ kernel/src/arch/x86_64/CPU.cpp | 15 ++- kernel/src/arch/x86_64/PS2Mouse.cpp | 118 +++++++++++++++++++++++ kernel/src/fs/devices/DeviceRegistry.cpp | 2 + kernel/src/fs/devices/DeviceRegistry.h | 1 + kernel/src/fs/devices/MouseDevice.cpp | 50 ++++++++++ kernel/src/fs/devices/MouseDevice.h | 31 ++++++ 8 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 kernel/src/api/Mouse.h create mode 100644 kernel/src/arch/x86_64/PS2Mouse.cpp create mode 100644 kernel/src/fs/devices/MouseDevice.cpp create mode 100644 kernel/src/fs/devices/MouseDevice.h diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index fe8fe087..601e671a 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -59,6 +59,7 @@ set(SOURCES src/fs/devices/ConsoleDevice.cpp src/fs/devices/FramebufferDevice.cpp src/fs/devices/UARTDevice.cpp + src/fs/devices/MouseDevice.cpp src/fs/InitRD.cpp src/binfmt/ELF.cpp src/binfmt/BinaryFormat.cpp @@ -80,6 +81,7 @@ if("${LUNA_ARCH}" MATCHES "x86_64") src/arch/x86_64/init/GDT.cpp src/arch/x86_64/init/IDT.cpp src/arch/x86_64/init/PIC.cpp + src/arch/x86_64/PS2Mouse.cpp ) endif() diff --git a/kernel/src/api/Mouse.h b/kernel/src/api/Mouse.h new file mode 100644 index 00000000..b1760b44 --- /dev/null +++ b/kernel/src/api/Mouse.h @@ -0,0 +1,22 @@ +#pragma once +#include + +namespace moon +{ + enum MouseButton + { + Left = (1 << 0), + Middle = (1 << 1), + Right = (1 << 2), + }; + + struct [[gnu::packed]] MousePacket + { + i16 xdelta; + i16 ydelta; + u8 buttons; + u8 _padding[3]; + }; + + static_assert(sizeof(MousePacket) == 8); +} diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index b306459b..c1c85ef2 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -1,10 +1,12 @@ #include "arch/CPU.h" #include "Log.h" +#include "api/Mouse.h" #include "arch/Keyboard.h" #include "arch/Timer.h" #include "arch/x86_64/CPU.h" #include "arch/x86_64/IO.h" #include "fs/devices/ConsoleDevice.h" +#include "fs/devices/MouseDevice.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -27,8 +29,9 @@ extern void change_pic_masks(u8 pic1_mask, u8 pic2_mask); extern void pic_eoi(unsigned char irq); extern void pic_eoi(Registers* regs); extern void setup_idt(); +extern void init_mouse(); -static Thread* g_io_thread; +Thread* g_io_thread; typedef void (*interrupt_handler_t)(Registers*, void*); @@ -139,15 +142,19 @@ extern "C" void handle_x86_exception(Registers* regs) } CircularQueue scancode_queue; +CircularQueue mouse_queue; void io_thread() { while (true) { u8 scancode; - while (!scancode_queue.try_pop(scancode)) kernel_wait_for_event(); + moon::MousePacket packet; - ConsoleDevice::did_press_or_release_key(scancode); + if (scancode_queue.try_pop(scancode)) { ConsoleDevice::did_press_or_release_key(scancode); } + else if (mouse_queue.try_pop(packet)) { MouseDevice::add_mouse_event(packet); } + else + kernel_wait_for_event(); } } @@ -261,6 +268,8 @@ namespace CPU remap_pic(); + init_mouse(); + sync_interrupts(); } diff --git a/kernel/src/arch/x86_64/PS2Mouse.cpp b/kernel/src/arch/x86_64/PS2Mouse.cpp new file mode 100644 index 00000000..3d9aa167 --- /dev/null +++ b/kernel/src/arch/x86_64/PS2Mouse.cpp @@ -0,0 +1,118 @@ +#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 = -packet.xdelta; + if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -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; +} diff --git a/kernel/src/fs/devices/DeviceRegistry.cpp b/kernel/src/fs/devices/DeviceRegistry.cpp index 7ea54229..ca792e85 100644 --- a/kernel/src/fs/devices/DeviceRegistry.cpp +++ b/kernel/src/fs/devices/DeviceRegistry.cpp @@ -4,6 +4,7 @@ #include "fs/devices/ConsoleDevice.h" #include "fs/devices/FramebufferDevice.h" #include "fs/devices/FullDevice.h" +#include "fs/devices/MouseDevice.h" #include "fs/devices/NullDevice.h" #include "fs/devices/UARTDevice.h" #include "fs/devices/ZeroDevice.h" @@ -70,6 +71,7 @@ namespace DeviceRegistry ConsoleDevice::create(); FramebufferDevice::create(); UARTDevice::create(); + MouseDevice::create(); return {}; } diff --git a/kernel/src/fs/devices/DeviceRegistry.h b/kernel/src/fs/devices/DeviceRegistry.h index 290e6c74..e4a8504d 100644 --- a/kernel/src/fs/devices/DeviceRegistry.h +++ b/kernel/src/fs/devices/DeviceRegistry.h @@ -16,6 +16,7 @@ namespace DeviceRegistry Disk = 4, DiskPartition = 5, Serial = 6, + Input = 7, }; Result> fetch_special_device(u32 major, u32 minor); diff --git a/kernel/src/fs/devices/MouseDevice.cpp b/kernel/src/fs/devices/MouseDevice.cpp new file mode 100644 index 00000000..db781f94 --- /dev/null +++ b/kernel/src/fs/devices/MouseDevice.cpp @@ -0,0 +1,50 @@ +#include "fs/devices/MouseDevice.h" + +SharedPtr MouseDevice::s_mouse_device = {}; + +Result MouseDevice::create() +{ + auto device = TRY(make_shared()); + s_mouse_device = device; + return DeviceRegistry::register_special_device(DeviceRegistry::Input, 0, device); +} + +Result MouseDevice::read(u8* buf, usize, usize length) const +{ + usize nread = 0; + while (length >= sizeof(moon::MousePacket)) + { + if (!m_packet_queue.try_pop(*(moon::MousePacket*)buf)) break; + + buf += sizeof(moon::MousePacket); + length -= sizeof(moon::MousePacket); + nread += sizeof(moon::MousePacket); + } + + return nread; +} + +Result MouseDevice::write(const u8* buf, usize, usize length) +{ + usize nwritten = 0; + while (length >= sizeof(moon::MousePacket)) + { + if (!m_packet_queue.try_push(*(const moon::MousePacket*)buf)) break; + + buf += sizeof(moon::MousePacket); + length -= sizeof(moon::MousePacket); + nwritten += sizeof(moon::MousePacket); + } + + return nwritten; +} + +void MouseDevice::add_mouse_event(const moon::MousePacket& packet) +{ + if (s_mouse_device) s_mouse_device->m_packet_queue.try_push(packet); +} + +bool MouseDevice::will_block_if_read() const +{ + return m_packet_queue.is_empty(); +} diff --git a/kernel/src/fs/devices/MouseDevice.h b/kernel/src/fs/devices/MouseDevice.h new file mode 100644 index 00000000..1b408758 --- /dev/null +++ b/kernel/src/fs/devices/MouseDevice.h @@ -0,0 +1,31 @@ +#pragma once +#include "api/Mouse.h" +#include "fs/devices/DeviceRegistry.h" +#include + +class MouseDevice : public Device +{ + public: + // Initializer for DeviceRegistry. + static Result create(); + + Result read(u8*, usize, usize) const override; + + Result write(const u8*, usize, usize) override; + + static void add_mouse_event(const moon::MousePacket& packet); + + bool will_block_if_read() const override; + + StringView device_path() const override + { + return "mouse"; + } + + virtual ~MouseDevice() = default; + + private: + mutable CircularQueue m_packet_queue; + + static SharedPtr s_mouse_device; +};