From a5cb4057f98bab0e52b2c40e3aa43ea2c71f63a7 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Mon, 15 Jul 2024 22:11:15 +0300 Subject: [PATCH] Kernel: Implement unified input files for device hot-plugging support /dev/keyboard and /dev/mouse can be read for events from any attached keyboard or mouse respectively. This makes device hot-plugging support pretty much automatic for TTY, GUI, and whatever takes input. --- kernel/include/kernel/Input/InputDevice.h | 67 +++++++++- kernel/kernel/FS/DevFS/FileSystem.cpp | 3 + kernel/kernel/Input/InputDevice.cpp | 147 +++++++++++++++++++++- kernel/kernel/Terminal/TTY.cpp | 4 +- kernel/kernel/kernel.cpp | 8 +- userspace/programs/WindowServer/main.cpp | 4 +- userspace/tests/test-mouse/main.cpp | 2 +- 7 files changed, 216 insertions(+), 19 deletions(-) diff --git a/kernel/include/kernel/Input/InputDevice.h b/kernel/include/kernel/Input/InputDevice.h index 6cb69e961f..a01efda436 100644 --- a/kernel/include/kernel/Input/InputDevice.h +++ b/kernel/include/kernel/Input/InputDevice.h @@ -7,7 +7,7 @@ namespace Kernel { - class InputDevice : public CharacterDevice + class InputDevice : public CharacterDevice, public BAN::Weakable { public: enum class Type @@ -19,6 +19,9 @@ namespace Kernel public: InputDevice(Type type); + BAN::StringView name() const final override { return m_name; } + dev_t rdev() const final override { return m_rdev; } + protected: void add_event(BAN::ConstByteSpan); @@ -28,8 +31,9 @@ namespace Kernel bool can_write_impl() const override { return false; } bool has_error_impl() const override { return false; } - virtual BAN::StringView name() const final override { return m_name; } - virtual dev_t rdev() const final override { return m_rdev; } + + private: + BAN::ErrorOr read_non_block(BAN::ByteSpan); private: const dev_t m_rdev; @@ -47,6 +51,63 @@ namespace Kernel size_t m_event_tail { 0 }; size_t m_event_head { 0 }; size_t m_event_count { 0 }; + + friend class KeyboardDevice; + friend class MouseDevice; + }; + + + + class KeyboardDevice : public CharacterDevice + { + public: + static BAN::ErrorOr> create(mode_t mode, uid_t uid, gid_t gid); + + void notify() { m_semaphore.unblock(); } + + private: + KeyboardDevice(mode_t mode, uid_t uid, gid_t gid); + BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; + + bool can_read_impl() const override; + bool can_write_impl() const override { return false; } + bool has_error_impl() const override { return false; } + + BAN::StringView name() const final override { return m_name; } + dev_t rdev() const final override { return m_rdev; } + + private: + const dev_t m_rdev; + const BAN::StringView m_name; + Semaphore m_semaphore; + + friend class BAN::RefPtr; + }; + + class MouseDevice : public CharacterDevice + { + public: + static BAN::ErrorOr> create(mode_t mode, uid_t uid, gid_t gid); + + void notify() { m_semaphore.unblock(); } + + private: + MouseDevice(mode_t mode, uid_t uid, gid_t gid); + BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; + + bool can_read_impl() const override; + bool can_write_impl() const override { return false; } + bool has_error_impl() const override { return false; } + + BAN::StringView name() const final override { return m_name; } + dev_t rdev() const final override { return m_rdev; } + + private: + const dev_t m_rdev; + const BAN::StringView m_name; + Semaphore m_semaphore; + + friend class BAN::RefPtr; }; } diff --git a/kernel/kernel/FS/DevFS/FileSystem.cpp b/kernel/kernel/FS/DevFS/FileSystem.cpp index 902700c517..5316bd4761 100644 --- a/kernel/kernel/FS/DevFS/FileSystem.cpp +++ b/kernel/kernel/FS/DevFS/FileSystem.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,8 @@ namespace Kernel s_instance->add_device(MUST(DebugDevice::create(0666, 0, 0))); s_instance->add_device(MUST(NullDevice::create(0666, 0, 0))); s_instance->add_device(MUST(ZeroDevice::create(0666, 0, 0))); + s_instance->add_device(MUST(KeyboardDevice::create(0440, 0, 901))); + s_instance->add_device(MUST(MouseDevice::create(0440, 0, 901))); } DevFileSystem& DevFileSystem::get() diff --git a/kernel/kernel/Input/InputDevice.cpp b/kernel/kernel/Input/InputDevice.cpp index 68d2c3c587..9a85b5aa30 100644 --- a/kernel/kernel/Input/InputDevice.cpp +++ b/kernel/kernel/Input/InputDevice.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -10,8 +11,11 @@ namespace Kernel { - static BAN::Atomic s_next_keyboard { 0 }; - static BAN::Atomic s_next_mouse { 0 }; + static BAN::Vector> s_keyboards; + static BAN::RefPtr s_keyboard_device; + + static BAN::Vector> s_mice; + static BAN::RefPtr s_mouse_device; static const char* get_name_format(InputDevice::Type type) { @@ -30,9 +34,15 @@ namespace Kernel switch (type) { case InputDevice::Type::Keyboard: - return makedev(DeviceNumber::Keyboard, s_next_keyboard++); + for (size_t i = 0; i < s_keyboards.size(); i++) + if (!s_keyboards[i].valid()) + return makedev(DeviceNumber::Keyboard, i + 1); + return makedev(DeviceNumber::Keyboard, s_keyboards.size() + 1); case InputDevice::Type::Mouse: - return makedev(DeviceNumber::Mouse, s_next_mouse++); + for (size_t i = 0; i < s_mice.size(); i++) + if (!s_mice[i].valid()) + return makedev(DeviceNumber::Mouse, i + 1); + return makedev(DeviceNumber::Mouse, s_mice.size() + 1); } ASSERT_NOT_REACHED(); } @@ -52,11 +62,25 @@ namespace Kernel InputDevice::InputDevice(Type type) : CharacterDevice(0440, 0, 901) , m_rdev(get_rdev(type)) - , m_name(MUST(BAN::String::formatted(get_name_format(type), minor(m_rdev)))) + , m_name(MUST(BAN::String::formatted(get_name_format(type), minor(m_rdev) - 1))) , m_type(type) , m_event_size(get_event_size(type)) { MUST(m_event_buffer.resize(m_event_size * m_max_event_count, 0)); + + if (m_type == Type::Keyboard) + { + if (s_keyboards.size() < minor(m_rdev)) + MUST(s_keyboards.resize(minor(m_rdev))); + s_keyboards[minor(m_rdev) - 1] = MUST(get_weak_ptr()); + } + + if (m_type == Type::Mouse) + { + if (s_mice.size() < minor(m_rdev)) + MUST(s_mice.resize(minor(m_rdev))); + s_mice[minor(m_rdev) - 1] = MUST(get_weak_ptr()); + } } void InputDevice::add_event(BAN::ConstByteSpan event) @@ -94,6 +118,10 @@ namespace Kernel m_event_count++; m_event_semaphore.unblock(); + if (m_type == Type::Keyboard && s_keyboard_device) + s_keyboard_device->notify(); + if (m_type == Type::Mouse && s_mouse_device) + s_mouse_device->notify(); } BAN::ErrorOr InputDevice::read_impl(off_t, BAN::ByteSpan buffer) @@ -121,4 +149,113 @@ namespace Kernel return m_event_size; } + BAN::ErrorOr InputDevice::read_non_block(BAN::ByteSpan buffer) + { + if (buffer.size() < m_event_size) + return BAN::Error::from_errno(ENOBUFS); + + SpinLockGuard _(m_event_lock); + + if (m_event_count == 0) + return 0; + + memcpy(buffer.data(), &m_event_buffer[m_event_tail * m_event_size], m_event_size); + m_event_tail = (m_event_tail + 1) % m_max_event_count; + m_event_count--; + + return m_event_size; + } + + + + BAN::ErrorOr> KeyboardDevice::create(mode_t mode, uid_t uid, gid_t gid) + { + s_keyboard_device = TRY(BAN::RefPtr::create(mode, uid, gid)); + return s_keyboard_device; + } + + KeyboardDevice::KeyboardDevice(mode_t mode, uid_t uid, gid_t gid) + : CharacterDevice(mode, uid, gid) + , m_rdev(makedev(DeviceNumber::Keyboard, 0)) + , m_name("keyboard"_sv) + {} + + BAN::ErrorOr KeyboardDevice::read_impl(off_t, BAN::ByteSpan buffer) + { + if (buffer.size() < sizeof(LibInput::RawKeyEvent)) + return BAN::Error::from_errno(ENOBUFS); + + for (;;) + { + for (auto& weak_keyboard : s_keyboards) + { + auto keyboard = weak_keyboard.lock(); + if (!keyboard) + continue; + + auto bytes = TRY(keyboard->read_non_block(buffer)); + if (bytes > 0) + return bytes; + } + + LockFreeGuard _(m_mutex); + TRY(Thread::current().block_or_eintr_indefinite(m_semaphore)); + } + } + + bool KeyboardDevice::can_read_impl() const + { + for (auto& weak_keyboard : s_keyboards) + if (auto keyboard = weak_keyboard.lock()) + if (keyboard->can_read()) + return true; + return false; + } + + + + BAN::ErrorOr> MouseDevice::create(mode_t mode, uid_t uid, gid_t gid) + { + s_mouse_device = TRY(BAN::RefPtr::create(mode, uid, gid)); + return s_mouse_device; + } + + MouseDevice::MouseDevice(mode_t mode, uid_t uid, gid_t gid) + : CharacterDevice(mode, uid, gid) + , m_rdev(makedev(DeviceNumber::Mouse, 0)) + , m_name("mouse"_sv) + {} + + BAN::ErrorOr MouseDevice::read_impl(off_t, BAN::ByteSpan buffer) + { + if (buffer.size() < sizeof(LibInput::MouseEvent)) + return BAN::Error::from_errno(ENOBUFS); + + for (;;) + { + for (auto& weak_mouse : s_mice) + { + auto mouse = weak_mouse.lock(); + if (!mouse) + continue; + + auto bytes = TRY(mouse->read_non_block(buffer)); + if (bytes > 0) + return bytes; + } + + LockFreeGuard _(m_mutex); + TRY(Thread::current().block_or_eintr_indefinite(m_semaphore)); + } + } + + bool MouseDevice::can_read_impl() const + { + for (auto& weak_mouse : s_mice) + if (auto mouse = weak_mouse.lock()) + if (mouse->can_read()) + return true; + return false; + } + } diff --git a/kernel/kernel/Terminal/TTY.cpp b/kernel/kernel/Terminal/TTY.cpp index 45ec8db7ea..92b920194a 100644 --- a/kernel/kernel/Terminal/TTY.cpp +++ b/kernel/kernel/Terminal/TTY.cpp @@ -83,10 +83,10 @@ namespace Kernel Process::create_kernel( [](void*) { - auto file_or_error = VirtualFileSystem::get().file_from_absolute_path({ 0, 0, 0, 0 }, "/dev/keyboard0"_sv, O_RDONLY); + auto file_or_error = VirtualFileSystem::get().file_from_absolute_path({ 0, 0, 0, 0 }, "/dev/keyboard"_sv, O_RDONLY); if (file_or_error.is_error()) { - dprintln("no input device found"); + dprintln("no keyboard found"); return; } diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 584084589d..9bf7cef465 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -205,9 +205,8 @@ static void init2(void*) // Initialize empty keymap MUST(LibInput::KeyboardLayout::initialize()); - // FIXME: implement device hot plugging, so multiple devices is possible - //if (auto res = PS2Controller::initialize(); res.is_error()) - // dprintln("{}", res.error()); + if (auto res = PS2Controller::initialize(); res.is_error()) + dprintln("{}", res.error()); MUST(NetworkManager::initialize()); @@ -216,9 +215,6 @@ static void init2(void*) PCI::PCIManager::get().initialize_devices(); dprintln("PCI devices initialized"); - // FIXME: This is very hacky way to wait until USB stack is initialized - SystemTimer::get().sleep(500); - VirtualFileSystem::initialize(cmdline.root); dprintln("VFS initialized"); diff --git a/userspace/programs/WindowServer/main.cpp b/userspace/programs/WindowServer/main.cpp index 755940630a..35ff850b3e 100644 --- a/userspace/programs/WindowServer/main.cpp +++ b/userspace/programs/WindowServer/main.cpp @@ -158,11 +158,11 @@ int main() MUST(LibInput::KeyboardLayout::initialize()); MUST(LibInput::KeyboardLayout::get().load_from_file("/usr/share/keymaps/us.keymap"_sv)); - int keyboard_fd = open("/dev/keyboard0", O_RDONLY); + int keyboard_fd = open("/dev/keyboard", O_RDONLY); if (keyboard_fd == -1) perror("open"); - int mouse_fd = open("/dev/mouse0", O_RDONLY); + int mouse_fd = open("/dev/mouse", O_RDONLY); if (mouse_fd == -1) perror("open"); diff --git a/userspace/tests/test-mouse/main.cpp b/userspace/tests/test-mouse/main.cpp index 906a9505e0..f9017d4b02 100644 --- a/userspace/tests/test-mouse/main.cpp +++ b/userspace/tests/test-mouse/main.cpp @@ -50,7 +50,7 @@ void cleanup() int main(int argc, char** argv) { const char* fb_path = "/dev/fb0"; - const char* mouse_path = "/dev/mouse0"; + const char* mouse_path = "/dev/mouse"; if (argc == 1) ;