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.
This commit is contained in:
Bananymous 2024-07-15 22:11:15 +03:00
parent 9d7f97ccd5
commit a5cb4057f9
7 changed files with 216 additions and 19 deletions

View File

@ -7,7 +7,7 @@
namespace Kernel
{
class InputDevice : public CharacterDevice
class InputDevice : public CharacterDevice, public BAN::Weakable<InputDevice>
{
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<size_t> 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<BAN::RefPtr<KeyboardDevice>> 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<size_t> 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<KeyboardDevice>;
};
class MouseDevice : public CharacterDevice
{
public:
static BAN::ErrorOr<BAN::RefPtr<MouseDevice>> 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<size_t> 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<MouseDevice>;
};
}

View File

@ -5,6 +5,7 @@
#include <kernel/Device/ZeroDevice.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/FS/TmpFS/Inode.h>
#include <kernel/Input/InputDevice.h>
#include <kernel/Lock/LockGuard.h>
#include <kernel/Process.h>
#include <kernel/Scheduler.h>
@ -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()

View File

@ -1,4 +1,5 @@
#include <kernel/Device/DeviceNumbers.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Input/InputDevice.h>
#include <kernel/Lock/LockGuard.h>
@ -10,8 +11,11 @@
namespace Kernel
{
static BAN::Atomic<uint16_t> s_next_keyboard { 0 };
static BAN::Atomic<uint16_t> s_next_mouse { 0 };
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_keyboards;
static BAN::RefPtr<KeyboardDevice> s_keyboard_device;
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_mice;
static BAN::RefPtr<MouseDevice> 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<size_t> InputDevice::read_impl(off_t, BAN::ByteSpan buffer)
@ -121,4 +149,113 @@ namespace Kernel
return m_event_size;
}
BAN::ErrorOr<size_t> 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<BAN::RefPtr<KeyboardDevice>> KeyboardDevice::create(mode_t mode, uid_t uid, gid_t gid)
{
s_keyboard_device = TRY(BAN::RefPtr<KeyboardDevice>::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<size_t> 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<BAN::RefPtr<MouseDevice>> MouseDevice::create(mode_t mode, uid_t uid, gid_t gid)
{
s_mouse_device = TRY(BAN::RefPtr<MouseDevice>::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<size_t> 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;
}
}

View File

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

View File

@ -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");

View File

@ -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");

View File

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