kernel: Add a PS/2 mouse driver

This commit is contained in:
apio 2023-08-02 11:55:45 +02:00
parent 6c26236167
commit e8f3dd4cf9
Signed by: apio
GPG Key ID: B8A7D06E42258954
8 changed files with 238 additions and 3 deletions

View File

@ -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()

22
kernel/src/api/Mouse.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <luna/Types.h>
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);
}

View File

@ -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<u8, 60> scancode_queue;
CircularQueue<moon::MousePacket, 20> 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();
}

View File

@ -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<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 = -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;
}

View File

@ -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 {};
}

View File

@ -16,6 +16,7 @@ namespace DeviceRegistry
Disk = 4,
DiskPartition = 5,
Serial = 6,
Input = 7,
};
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);

View File

@ -0,0 +1,50 @@
#include "fs/devices/MouseDevice.h"
SharedPtr<MouseDevice> MouseDevice::s_mouse_device = {};
Result<void> MouseDevice::create()
{
auto device = TRY(make_shared<MouseDevice>());
s_mouse_device = device;
return DeviceRegistry::register_special_device(DeviceRegistry::Input, 0, device);
}
Result<usize> 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<usize> 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();
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "api/Mouse.h"
#include "fs/devices/DeviceRegistry.h"
#include <luna/CircularQueue.h>
class MouseDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<void> create();
Result<usize> read(u8*, usize, usize) const override;
Result<usize> 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<moon::MousePacket, 200> m_packet_queue;
static SharedPtr<MouseDevice> s_mouse_device;
};