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:
parent
9d7f97ccd5
commit
a5cb4057f9
|
@ -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>;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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)
|
||||
;
|
||||
|
|
Loading…
Reference in New Issue