Compare commits

...

5 Commits

Author SHA1 Message Date
2198dedb96
apps: Add mouse
All checks were successful
continuous-integration/drone/push Build is passing
A simple mouse packet reader.
2023-08-02 11:56:12 +02:00
cd9219df52
tools: Install kernel API headers into /usr/include/moon 2023-08-02 11:56:00 +02:00
e8f3dd4cf9
kernel: Add a PS/2 mouse driver 2023-08-02 11:55:45 +02:00
6c26236167
libluna: Add CircularQueue::is_empty() 2023-08-02 11:55:08 +02:00
b17793134e
libos: Let users change the buffering mode of a File 2023-08-02 11:54:47 +02:00
14 changed files with 299 additions and 3 deletions

View File

@ -44,3 +44,4 @@ luna_app(gol.cpp gol)
luna_app(buffer-test.cpp buffer-test)
luna_app(socket-test.cpp socket-test)
luna_app(socket-client.cpp socket-client)
luna_app(mouse.cpp mouse)

16
apps/mouse.cpp Normal file
View File

@ -0,0 +1,16 @@
#include <moon/Mouse.h>
#include <os/File.h>
Result<int> luna_main(int, char**)
{
auto mouse = TRY(os::File::open("/dev/mouse", os::File::ReadOnly));
mouse->set_buffer(os::File::NotBuffered);
while (1)
{
moon::MousePacket packet;
TRY(mouse->read_typed(packet));
os::println("Mouse packet: xdelta=%d, ydelta=%d, buttons=%d", packet.xdelta, packet.ydelta, packet.buttons);
}
}

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

View File

@ -16,6 +16,11 @@ template <typename T, usize Size> class CircularQueue
{
}
bool is_empty()
{
return m_tail.load() == m_head.load();
}
bool try_push(const T& value)
{
usize current_tail = m_tail.load(MemoryOrder::Relaxed);
@ -71,6 +76,11 @@ template <typename T> class DynamicCircularQueue
if (m_data) free_impl(m_data);
}
bool is_empty()
{
return m_tail.load() == m_head.load();
}
Result<void> set_size(usize size)
{
m_data = (T*)TRY(calloc_impl(size + 1, sizeof(T), false));

View File

@ -143,6 +143,19 @@ namespace os
*/
Result<void> read(Buffer& buf, usize size);
/**
* @brief Read an object from this File.
*
* @tparam T The type of the object to read.
* @param object A reference to the object in question.
* @return Result<void> Whether the operation succeeded.
*/
template <typename T> Result<void> read_typed(T& object)
{
TRY(raw_read((u8*)&object, sizeof(T)));
return {};
}
/**
* @brief Read the entire File's contents.
*
@ -178,6 +191,20 @@ namespace os
*/
void rewind();
enum BufferingMode
{
NotBuffered = _IONBF,
LineBuffered = _IOLBF,
FullyBuffered = _IOFBF,
};
/**
* @brief Change the buffering mode of this File.
*
* @param mode The buffering mode.
*/
void set_buffer(BufferingMode mode);
File(Badge<File>);
~File();

View File

@ -226,6 +226,11 @@ namespace os
fflush(m_file);
}
void File::set_buffer(BufferingMode mode)
{
setvbuf(m_file, NULL, mode, 0);
}
// FIXME: Do not allocate memory for printing.
Result<void> print_impl(SharedPtr<File> f, StringView fmt, va_list ap)
{

View File

@ -9,7 +9,9 @@ mkdir -p $LUNA_BASE
mkdir -p $LUNA_BASE/usr/include
mkdir -p $LUNA_BASE/usr/include/luna
mkdir -p $LUNA_BASE/usr/include/os
mkdir -p $LUNA_BASE/usr/include/moon
cp --preserve=timestamps -RT libc/include/ $LUNA_BASE/usr/include
cp --preserve=timestamps -RT libluna/include/luna/ $LUNA_BASE/usr/include/luna
cp --preserve=timestamps -RT libos/include/os/ $LUNA_BASE/usr/include/os
cp --preserve=timestamps -RT kernel/src/api/ $LUNA_BASE/usr/include/moon