Files
banan-os/kernel/kernel/Input/InputDevice.cpp
Bananymous ed3924722e Kernel: Remove kernel CPU load printing
We still keep track of processor loads but the hacky printing is no
longer done in kernel space :)
2026-05-20 19:11:27 +03:00

426 lines
11 KiB
C++

#include <BAN/CircularQueue.h>
#include <kernel/Device/DeviceNumbers.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Input/InputDevice.h>
#include <kernel/Lock/SpinLockAsMutex.h>
#include <kernel/Terminal/TTY.h>
#include <LibInput/Joystick.h>
#include <LibInput/KeyEvent.h>
#include <LibInput/MouseEvent.h>
#include <sys/epoll.h>
#include <sys/sysmacros.h>
namespace Kernel
{
static SpinLock s_keyboard_lock;
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_keyboards;
static BAN::RefPtr<KeyboardDevice> s_keyboard_device;
static SpinLock s_mouse_lock;
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_mice;
static BAN::RefPtr<MouseDevice> s_mouse_device;
static SpinLock s_joystick_lock;
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_joysticks;
static SpinLock s_tty_keyboard_event_lock;
static ThreadBlocker s_tty_keyboard_event_blocker;
static BAN::CircularQueue<LibInput::RawKeyEvent, 128> s_tty_keyboard_events;
static const char* get_name_format(InputDevice::Type type)
{
switch (type)
{
case InputDevice::Type::Keyboard:
return "keyboard{}";
case InputDevice::Type::Mouse:
return "mouse{}";
case InputDevice::Type::Joystick:
return "joystick{}";
}
ASSERT_NOT_REACHED();
}
static dev_t get_rdev(InputDevice::Type type)
{
switch (type)
{
case InputDevice::Type::Keyboard:
{
SpinLockGuard _(s_keyboard_lock);
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:
{
SpinLockGuard _(s_mouse_lock);
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);
}
case InputDevice::Type::Joystick:
{
SpinLockGuard _(s_joystick_lock);
for (size_t i = 0; i < s_joysticks.size(); i++)
if (!s_joysticks[i].valid())
return makedev(DeviceNumber::Joystick, i + 1);
return makedev(DeviceNumber::Joystick, s_joysticks.size() + 1);
}
}
ASSERT_NOT_REACHED();
}
static size_t get_event_size(InputDevice::Type type)
{
switch (type)
{
case InputDevice::Type::Keyboard:
return sizeof(LibInput::RawKeyEvent);
case InputDevice::Type::Mouse:
return sizeof(LibInput::MouseEvent);
case InputDevice::Type::Joystick:
return sizeof(LibInput::JoystickEvent);
}
ASSERT_NOT_REACHED();
}
InputDevice::InputDevice(Type type)
: CharacterDevice(0440, 0, 901)
, m_type(type)
, m_event_size(get_event_size(type))
{
m_rdev = get_rdev(type);
m_name = MUST(BAN::String::formatted(get_name_format(type), minor(m_rdev) - 1));
MUST(m_event_buffer.resize(m_event_size * m_max_event_count, 0));
switch (m_type)
{
case Type::Keyboard:
{
SpinLockGuard _(s_keyboard_lock);
if (s_keyboards.size() < minor(m_rdev))
MUST(s_keyboards.resize(minor(m_rdev)));
s_keyboards[minor(m_rdev) - 1] = MUST(get_weak_ptr());
break;
}
case Type::Mouse:
{
SpinLockGuard _(s_mouse_lock);
if (s_mice.size() < minor(m_rdev))
MUST(s_mice.resize(minor(m_rdev)));
s_mice[minor(m_rdev) - 1] = MUST(get_weak_ptr());
break;
}
case Type::Joystick:
{
SpinLockGuard _(s_joystick_lock);
if (s_joysticks.size() < minor(m_rdev))
MUST(s_joysticks.resize(minor(m_rdev)));
s_joysticks[minor(m_rdev) - 1] = MUST(get_weak_ptr());
break;
}
}
}
void InputDevice::add_event(BAN::ConstByteSpan event)
{
{
SpinLockGuard _(m_event_lock);
ASSERT(event.size() == m_event_size);
if (m_type == Type::Mouse && m_event_count > 0)
{
const size_t last_index = (m_event_head + m_max_event_count - 1) % m_max_event_count;
auto& last_event = *reinterpret_cast<LibInput::MouseEvent*>(&m_event_buffer[last_index * m_event_size]);
auto& curr_event = event.as<const LibInput::MouseEvent>();
if (last_event.type == LibInput::MouseEventType::MouseMoveEvent && curr_event.type == LibInput::MouseEventType::MouseMoveEvent)
{
last_event.move_event.rel_x += curr_event.move_event.rel_x;
last_event.move_event.rel_y += curr_event.move_event.rel_y;
return;
}
if (last_event.type == LibInput::MouseEventType::MouseMoveAbsEvent && curr_event.type == LibInput::MouseEventType::MouseMoveAbsEvent)
{
last_event.move_abs_event.abs_x = curr_event.move_abs_event.abs_x;
last_event.move_abs_event.abs_y = curr_event.move_abs_event.abs_y;
return;
}
if (last_event.type == LibInput::MouseEventType::MouseScrollEvent && curr_event.type == LibInput::MouseEventType::MouseScrollEvent)
{
last_event.scroll_event.scroll += curr_event.scroll_event.scroll;
return;
}
}
if (m_type == Type::Keyboard)
{
auto& key_event = event.as<const LibInput::RawKeyEvent>();
if (key_event.modifier & LibInput::KeyEvent::Modifier::Pressed)
{
if (key_event.modifier & LibInput::KeyEvent::Modifier::LCtrl)
{
const auto processor_count = Processor::count();
switch (key_event.keycode)
{
#define DUMP_CPU_STACK_TRACE(idx) \
case LibInput::keycode_function(idx + 1): \
if (idx >= processor_count) \
break; \
Processor::send_smp_message(Processor::id_from_index(idx), { \
.type = Processor::SMPMessage::Type::StackTrace, \
.dummy = false, \
}); \
break
// F1-F12
DUMP_CPU_STACK_TRACE(0);
DUMP_CPU_STACK_TRACE(1);
DUMP_CPU_STACK_TRACE(2);
DUMP_CPU_STACK_TRACE(3);
DUMP_CPU_STACK_TRACE(4);
DUMP_CPU_STACK_TRACE(5);
DUMP_CPU_STACK_TRACE(6);
DUMP_CPU_STACK_TRACE(7);
DUMP_CPU_STACK_TRACE(8);
DUMP_CPU_STACK_TRACE(9);
DUMP_CPU_STACK_TRACE(10);
DUMP_CPU_STACK_TRACE(11);
#undef DUMP_CPU_STACK_TRACE
}
}
else switch (key_event.keycode)
{
case LibInput::keycode_function(11):
DevFileSystem::get().initiate_disk_cache_drop();
break;
case LibInput::keycode_function(12):
Kernel::panic("Keyboard kernel panic :)");
break;
}
}
if (TTY::current()->should_receive_input())
{
SpinLockGuard _(s_tty_keyboard_event_lock);
if (!s_tty_keyboard_events.full())
s_tty_keyboard_events.push(key_event);
s_tty_keyboard_event_blocker.unblock();
return;
}
}
if (m_event_count == m_max_event_count)
{
m_event_tail = (m_event_tail + 1) % m_max_event_count;
m_event_count--;
}
memcpy(&m_event_buffer[m_event_head * m_event_size], event.data(), m_event_size);
m_event_head = (m_event_head + 1) % m_max_event_count;
m_event_count++;
}
epoll_notify(EPOLLIN);
m_event_thread_blocker.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)
{
if (buffer.size() < m_event_size)
return BAN::Error::from_errno(ENOBUFS);
SpinLockGuard guard(m_event_lock);
while (m_event_count == 0)
{
// FIXME: should m_mutex be unlocked?
SpinLockGuardAsMutex smutex(guard);
TRY(Thread::current().block_or_eintr_indefinite(m_event_thread_blocker, &smutex));
}
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<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;
}
static void tty_keyboard_thread(void*)
{
static BAN::Atomic<bool> initialized = false;
[[maybe_unused]] bool old_initialized = initialized.exchange(true);
ASSERT(old_initialized == false);
for (;;)
{
LibInput::RawKeyEvent event;
{
SpinLockGuard guard(s_tty_keyboard_event_lock);
if (s_tty_keyboard_events.empty())
{
SpinLockGuardAsMutex smutex(guard);
s_tty_keyboard_event_blocker.block_indefinite(&smutex);
continue;
}
event = s_tty_keyboard_events.front();
s_tty_keyboard_events.pop();
}
TTY::current()->on_key_event(event);
}
}
BAN::ErrorOr<void> KeyboardDevice::initialize_tty_thread()
{
auto* thread = TRY(Thread::create_kernel(tty_keyboard_thread, nullptr));
ASSERT(thread);
TRY(Processor::scheduler().add_thread(thread));
return {};
}
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_name("keyboard"_sv)
{
m_rdev = makedev(DeviceNumber::Keyboard, 0);
}
void KeyboardDevice::notify()
{
epoll_notify(EPOLLIN);
SpinLockGuard _(s_keyboard_lock);
m_thread_blocker.unblock();
}
BAN::ErrorOr<size_t> KeyboardDevice::read_impl(off_t, BAN::ByteSpan buffer)
{
if (buffer.size() < sizeof(LibInput::RawKeyEvent))
return BAN::Error::from_errno(ENOBUFS);
SpinLockGuard keyboard_guard(s_keyboard_lock);
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;
}
SpinLockGuardAsMutex smutex(keyboard_guard);
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker, &smutex));
}
}
bool KeyboardDevice::can_read_impl() const
{
SpinLockGuard _(s_keyboard_lock);
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_name("mouse"_sv)
{
m_rdev = makedev(DeviceNumber::Mouse, 0);
}
void MouseDevice::notify()
{
epoll_notify(EPOLLIN);
SpinLockGuard _(s_mouse_lock);
m_thread_blocker.unblock();
}
BAN::ErrorOr<size_t> MouseDevice::read_impl(off_t, BAN::ByteSpan buffer)
{
if (buffer.size() < sizeof(LibInput::MouseEvent))
return BAN::Error::from_errno(ENOBUFS);
SpinLockGuard mouse_guard(s_mouse_lock);
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;
}
SpinLockGuardAsMutex smutex(mouse_guard);
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker, &smutex));
}
}
bool MouseDevice::can_read_impl() const
{
SpinLockGuard _(s_mouse_lock);
for (auto& weak_mouse : s_mice)
if (auto mouse = weak_mouse.lock())
if (mouse->can_read())
return true;
return false;
}
}