kernel: Add a PS/2 mouse driver
This commit is contained in:
parent
6c26236167
commit
e8f3dd4cf9
@ -59,6 +59,7 @@ set(SOURCES
|
|||||||
src/fs/devices/ConsoleDevice.cpp
|
src/fs/devices/ConsoleDevice.cpp
|
||||||
src/fs/devices/FramebufferDevice.cpp
|
src/fs/devices/FramebufferDevice.cpp
|
||||||
src/fs/devices/UARTDevice.cpp
|
src/fs/devices/UARTDevice.cpp
|
||||||
|
src/fs/devices/MouseDevice.cpp
|
||||||
src/fs/InitRD.cpp
|
src/fs/InitRD.cpp
|
||||||
src/binfmt/ELF.cpp
|
src/binfmt/ELF.cpp
|
||||||
src/binfmt/BinaryFormat.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/GDT.cpp
|
||||||
src/arch/x86_64/init/IDT.cpp
|
src/arch/x86_64/init/IDT.cpp
|
||||||
src/arch/x86_64/init/PIC.cpp
|
src/arch/x86_64/init/PIC.cpp
|
||||||
|
src/arch/x86_64/PS2Mouse.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
22
kernel/src/api/Mouse.h
Normal file
22
kernel/src/api/Mouse.h
Normal 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);
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
#include "arch/CPU.h"
|
#include "arch/CPU.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "api/Mouse.h"
|
||||||
#include "arch/Keyboard.h"
|
#include "arch/Keyboard.h"
|
||||||
#include "arch/Timer.h"
|
#include "arch/Timer.h"
|
||||||
#include "arch/x86_64/CPU.h"
|
#include "arch/x86_64/CPU.h"
|
||||||
#include "arch/x86_64/IO.h"
|
#include "arch/x86_64/IO.h"
|
||||||
#include "fs/devices/ConsoleDevice.h"
|
#include "fs/devices/ConsoleDevice.h"
|
||||||
|
#include "fs/devices/MouseDevice.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Scheduler.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(unsigned char irq);
|
||||||
extern void pic_eoi(Registers* regs);
|
extern void pic_eoi(Registers* regs);
|
||||||
extern void setup_idt();
|
extern void setup_idt();
|
||||||
|
extern void init_mouse();
|
||||||
|
|
||||||
static Thread* g_io_thread;
|
Thread* g_io_thread;
|
||||||
|
|
||||||
typedef void (*interrupt_handler_t)(Registers*, void*);
|
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<u8, 60> scancode_queue;
|
||||||
|
CircularQueue<moon::MousePacket, 20> mouse_queue;
|
||||||
|
|
||||||
void io_thread()
|
void io_thread()
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
u8 scancode;
|
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();
|
remap_pic();
|
||||||
|
|
||||||
|
init_mouse();
|
||||||
|
|
||||||
sync_interrupts();
|
sync_interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
118
kernel/src/arch/x86_64/PS2Mouse.cpp
Normal file
118
kernel/src/arch/x86_64/PS2Mouse.cpp
Normal 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;
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
#include "fs/devices/ConsoleDevice.h"
|
#include "fs/devices/ConsoleDevice.h"
|
||||||
#include "fs/devices/FramebufferDevice.h"
|
#include "fs/devices/FramebufferDevice.h"
|
||||||
#include "fs/devices/FullDevice.h"
|
#include "fs/devices/FullDevice.h"
|
||||||
|
#include "fs/devices/MouseDevice.h"
|
||||||
#include "fs/devices/NullDevice.h"
|
#include "fs/devices/NullDevice.h"
|
||||||
#include "fs/devices/UARTDevice.h"
|
#include "fs/devices/UARTDevice.h"
|
||||||
#include "fs/devices/ZeroDevice.h"
|
#include "fs/devices/ZeroDevice.h"
|
||||||
@ -70,6 +71,7 @@ namespace DeviceRegistry
|
|||||||
ConsoleDevice::create();
|
ConsoleDevice::create();
|
||||||
FramebufferDevice::create();
|
FramebufferDevice::create();
|
||||||
UARTDevice::create();
|
UARTDevice::create();
|
||||||
|
MouseDevice::create();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ namespace DeviceRegistry
|
|||||||
Disk = 4,
|
Disk = 4,
|
||||||
DiskPartition = 5,
|
DiskPartition = 5,
|
||||||
Serial = 6,
|
Serial = 6,
|
||||||
|
Input = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);
|
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);
|
||||||
|
50
kernel/src/fs/devices/MouseDevice.cpp
Normal file
50
kernel/src/fs/devices/MouseDevice.cpp
Normal 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();
|
||||||
|
}
|
31
kernel/src/fs/devices/MouseDevice.h
Normal file
31
kernel/src/fs/devices/MouseDevice.h
Normal 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;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user