Files
ladybird/Kernel/Devices/HID/VirtIO/Input.cpp
2024-05-23 11:16:57 -06:00

447 lines
20 KiB
C++

/*
* Copyright (c) 2024, Sönke Holz <sholz8530@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Span.h>
#include <AK/String.h>
#include <Kernel/Bus/VirtIO/Transport/PCIe/TransportLink.h>
#include <Kernel/Devices/HID/Management.h>
#include <Kernel/Devices/HID/VirtIO/EvDevDefinitions.h>
#include <Kernel/Devices/HID/VirtIO/Input.h>
#include <Kernel/Sections.h>
namespace Kernel::VirtIO {
struct VirtIOInputEvent {
LittleEndian<u16> type;
LittleEndian<u16> code;
LittleEndian<u32> value;
};
static_assert(AssertSize<VirtIOInputEvent, 8>());
struct VirtIOInputConfig {
enum class Select : u8 {
Unset = 0x00,
IDName = 0x01,
IDSerial = 0x02,
IDDevIDs = 0x03,
PropBits = 0x10,
EvBits = 0x11,
AbsInfo = 0x12,
};
struct AbsInfo {
LittleEndian<u32> min;
LittleEndian<u32> max;
LittleEndian<u32> fuzz;
LittleEndian<u32> flat;
LittleEndian<u32> res;
};
static_assert(AssertSize<AbsInfo, 20>());
struct DevIDs {
LittleEndian<u16> bustype;
LittleEndian<u16> vendor;
LittleEndian<u16> product;
LittleEndian<u16> version;
};
static_assert(AssertSize<DevIDs, 8>());
Select select;
u8 subsel;
u8 size;
u8 reserved[5];
union {
char string[128];
u8 bitmap[128];
AbsInfo abs;
DevIDs ids;
} u;
};
static_assert(AssertSize<VirtIOInputConfig, 136>());
// clang-format off
static constexpr auto unshifted_evdev_key_map = to_array<KeyCodeEntry const>({
// 0x00-0x0f
{ Key_Invalid, 0xff }, { Key_Escape, 0x01 }, { Key_1, 0x02 }, { Key_2, 0x03 },
{ Key_3, 0x04 }, { Key_4, 0x05 }, { Key_5, 0x06 }, { Key_6, 0x07 },
{ Key_7, 0x08 }, { Key_8, 0x09 }, { Key_9, 0x0a }, { Key_0, 0x0b },
{ Key_Minus, 0x0c }, { Key_Equal, 0x0d }, { Key_Backspace, 0x0e }, { Key_Tab, 0x0f },
// 0x10-0x1f
{ Key_Q, 0x10 }, { Key_W, 0x11 }, { Key_E, 0x12 }, { Key_R, 0x13 },
{ Key_T, 0x14 }, { Key_Y, 0x15 }, { Key_U, 0x16 }, { Key_I, 0x17 },
{ Key_O, 0x18 }, { Key_P, 0x19 }, { Key_LeftBracket, 0x1a }, { Key_RightBracket, 0x1b },
{ Key_Return, 0x1c }, { Key_Control, 0x1d }, { Key_A, 0x1e }, { Key_S, 0x1f },
// 0x20-0x2f
{ Key_D, 0x20 }, { Key_F, 0x21 }, { Key_G, 0x22 }, { Key_H, 0x23 },
{ Key_J, 0x24 }, { Key_K, 0x25 }, { Key_L, 0x26 }, { Key_Semicolon, 0x27 },
{ Key_Apostrophe, 0x28 }, { Key_Backtick, 0x29 }, { Key_LeftShift, 0xff }, { Key_Backslash, 0x2b },
{ Key_Z, 0x2c }, { Key_X, 0x2d }, { Key_C, 0x2e }, { Key_V, 0x2f },
// 0x30-0x3f
{ Key_B, 0x30 }, { Key_N, 0x31 }, { Key_M, 0x32 }, { Key_Comma, 0x33 },
{ Key_Period, 0x34 }, { Key_Slash, 0x35 }, { Key_RightShift, 0xff }, { Key_Asterisk, 0x37 },
{ Key_Alt, 0xff }, { Key_Space, 0x39 }, { Key_CapsLock, 0xff }, { Key_F1, 0xff },
{ Key_F2, 0xff }, { Key_F3, 0xff }, { Key_F4, 0xff }, { Key_F5, 0xff },
// 0x40-0x4f
{ Key_F6, 0xff }, { Key_F7, 0xff }, { Key_F8, 0xff }, { Key_F9, 0xff },
{ Key_F10, 0xff }, { Key_NumLock, 0x45 }, { Key_ScrollLock, 0xff }, { Key_Home, 0xff },
{ Key_Up, 0xff }, { Key_PageUp, 0xff }, { Key_Minus, 0x4a }, { Key_Left, 0xff },
{ Key_Invalid, 0xff }, { Key_Right, 0xff }, { Key_Plus, 0x4e }, { Key_End, 0xff },
// 0x50-0x5f
{ Key_Down, 0xff }, { Key_PageDown, 0xff }, { Key_Insert, 0xff }, { Key_Delete, 0xff },
{ Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Backslash, 0x56 }, { Key_F11, 0xff },
{ Key_F12, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff },
{ Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff },
// 0x60-0x6f
// FIXME: Add Numpad "/" key to character map for key code 0x62
{ Key_Return, 0xff }, { Key_RightControl, 0xff }, { Key_Slash, 0xff }, { Key_SysRq, 0xff },
{ Key_RightAlt, 0xff }, { Key_Invalid, 0xff }, { Key_Home, 0xff }, { Key_Up, 0xff },
{ Key_PageUp, 0xff }, { Key_Left, 0xff }, { Key_Right, 0xff }, { Key_End, 0xff },
{ Key_Down, 0xff }, { Key_PageDown, 0xff }, { Key_Insert, 0xff }, { Key_Delete, 0xff },
// 0x70-0x7f
{ Key_Invalid, 0xff }, { Key_Mute, 0xff }, { Key_VolumeDown, 0xff }, { Key_VolumeUp, 0xff },
{ Key_Power, 0xff }, { Key_Equal, 0xff }, { Key_Invalid, 0xff }, { Key_PauseBreak, 0xff },
{ Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff },
{ Key_Invalid, 0xff }, { Key_Super, 0xff }, { Key_Super, 0xff }, { Key_Menu, 0xff },
// 0x80-0x8f
{ Key_Stop, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff },
{ Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff },
{ Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff },
{ Key_Calculator, 0xff }, { Key_Invalid, 0xff }, { Key_Sleep, 0xff }, { Key_Wake, 0xff },
});
// clang-format on
// clang-format off
static constexpr auto shifted_evdev_key_map = to_array<KeyCodeEntry const>({
// 0x00-0x0f
{ Key_Invalid, 0xff }, { Key_Escape, 0x01 }, { Key_ExclamationPoint, 0x02 }, { Key_AtSign, 0x03 },
{ Key_Hashtag, 0x04 }, { Key_Dollar, 0x05 }, { Key_Percent, 0x06 }, { Key_Circumflex, 0x07 },
{ Key_Ampersand, 0x08 }, { Key_Asterisk, 0x09 }, { Key_LeftParen, 0x0a }, { Key_RightParen, 0x0b },
{ Key_Underscore, 0x0c }, { Key_Plus, 0x0d }, { Key_Backspace, 0x0e }, { Key_Tab, 0x0f },
// 0x10-0x1f
{ Key_Q, 0x10 }, { Key_W, 0x11 }, { Key_E, 0x12 }, { Key_R, 0x13 },
{ Key_T, 0x14 }, { Key_Y, 0x15 }, { Key_U, 0x16 }, { Key_I, 0x17 },
{ Key_O, 0x18 }, { Key_P, 0x19 }, { Key_LeftBrace, 0x1a }, { Key_RightBrace, 0x1b },
{ Key_Return, 0x1c }, { Key_Control, 0x1d }, { Key_A, 0x1e }, { Key_S, 0x1f },
// 0x20-0x2f
{ Key_D, 0x20 }, { Key_F, 0x21 }, { Key_G, 0x22 }, { Key_H, 0x23 },
{ Key_J, 0x24 }, { Key_K, 0x25 }, { Key_L, 0x26 }, { Key_Colon, 0x27 },
{ Key_DoubleQuote, 0x28 }, { Key_Tilde, 0x29 }, { Key_LeftShift, 0xff }, { Key_Pipe, 0x2b },
{ Key_Z, 0x2c }, { Key_X, 0x2d }, { Key_C, 0x2e }, { Key_V, 0x2f },
// 0x30-0x3f
{ Key_B, 0x30 }, { Key_N, 0x31 }, { Key_M, 0x32 }, { Key_LessThan, 0x33 },
{ Key_GreaterThan, 0x34 }, { Key_QuestionMark, 0x35 }, { Key_RightShift, 0xff }, { Key_Asterisk, 0x37 },
{ Key_Alt, 0xff }, { Key_Space, 0x39 }, { Key_CapsLock, 0xff }, { Key_F1, 0xff },
{ Key_F2, 0xff }, { Key_F3, 0xff }, { Key_F4, 0xff }, { Key_F5, 0xff },
// 0x40-0x4f
{ Key_F6, 0xff }, { Key_F7, 0xff }, { Key_F8, 0xff }, { Key_F9, 0xff },
{ Key_F10, 0xff }, { Key_NumLock, 0xff }, { Key_ScrollLock, 0xff }, { Key_Home, 0xff },
{ Key_Up, 0xff }, { Key_PageUp, 0xff }, { Key_Minus, 0x4a }, { Key_Left, 0xff },
{ Key_Invalid, 0xff }, { Key_Right, 0xff }, { Key_Plus, 0x4e }, { Key_End, 0xff },
// 0x50-0x5f
{ Key_Down, 0xff }, { Key_PageDown, 0xff }, { Key_Insert, 0xff }, { Key_Delete, 0xff },
{ Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Pipe, 0x56 }, { Key_F11, 0xff },
{ Key_F12, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff },
{ Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff }, { Key_Invalid, 0xff },
});
// clang-format on
UNMAP_AFTER_INIT NonnullLockRefPtr<Input> Input::must_create_for_pci_instance(PCI::DeviceIdentifier const& device_identifier)
{
auto pci_transport_link = MUST(PCIeTransportLink::create(device_identifier));
return adopt_lock_ref_if_nonnull(new Input(move(pci_transport_link))).release_nonnull();
}
UNMAP_AFTER_INIT ErrorOr<void> Input::initialize_virtio_resources()
{
TRY(Device::initialize_virtio_resources());
auto const* cfg = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device));
TRY(negotiate_features([&](auto) { return 0; }));
transport_entity().config_write8(*cfg, offsetof(VirtIOInputConfig, subsel), 0);
OwnPtr<KString> name;
transport_entity().config_write8(*cfg, offsetof(VirtIOInputConfig, select), to_underlying(VirtIOInputConfig::Select::IDName));
transport_entity().read_config_atomic([&]() {
auto size = transport_entity().config_read8(*cfg, offsetof(VirtIOInputConfig, size));
if (size == 0)
return;
VERIFY(size <= sizeof(VirtIOInputConfig::u.string));
char* name_chars = nullptr;
name = MUST(KString::try_create_uninitialized(size, name_chars));
for (size_t i = 0; i < size; i++)
name_chars[i] = static_cast<char>(transport_entity().config_read8(*cfg, offsetof(VirtIOInputConfig, u.string) + i));
});
if (name)
dbgln("VirtIO::Input: Device name: {}", name);
transport_entity().config_write8(*cfg, offsetof(VirtIOInputConfig, select), to_underlying(VirtIOInputConfig::Select::AbsInfo));
transport_entity().read_config_atomic([&]() {
auto size = transport_entity().config_read8(*cfg, offsetof(VirtIOInputConfig, size));
if (size == 0)
return;
VERIFY(size == sizeof(VirtIOInputConfig::u.abs));
m_abs_min = bit_cast<LittleEndian<u32>>(transport_entity().config_read32(*cfg, offsetof(VirtIOInputConfig, u.abs.min)));
m_abs_max = bit_cast<LittleEndian<u32>>(transport_entity().config_read32(*cfg, offsetof(VirtIOInputConfig, u.abs.max)));
});
TRY(setup_queues(2));
finish_init();
auto& event_queue = get_queue(EVENTQ);
SpinlockLocker event_queue_lock(event_queue.lock());
m_event_buffer_region = TRY(MM.allocate_contiguous_kernel_region(TRY(Memory::page_round_up(event_queue.size() * sizeof(VirtIOInputEvent))), "VirtIO::Input eventq"sv, Memory::Region::Access::ReadWrite));
QueueChain event_queue_chain(event_queue);
for (size_t queue_idx = 0; queue_idx < event_queue.size(); ++queue_idx) {
auto buffer_start = m_event_buffer_region->physical_page(0)->paddr().offset(queue_idx * sizeof(VirtIOInputEvent));
auto did_add_buffer = event_queue_chain.add_buffer_to_chain(buffer_start, sizeof(VirtIOInputEvent), BufferType::DeviceWritable);
VERIFY(did_add_buffer);
supply_chain_and_notify(EVENTQ, event_queue_chain);
}
HIDManagement::the().attach_standalone_hid_device(*m_mouse_device);
HIDManagement::the().attach_standalone_hid_device(*m_keyboard_device);
return {};
}
UNMAP_AFTER_INIT Input::Input(NonnullOwnPtr<TransportEntity> transport_entity)
: VirtIO::Device(move(transport_entity))
, m_mouse_device(MouseDevice::try_to_initialize().release_value_but_fixme_should_propagate_errors())
, m_keyboard_device(KeyboardDevice::try_to_initialize().release_value_but_fixme_should_propagate_errors())
{
}
ErrorOr<void> Input::handle_device_config_change()
{
return {};
}
void Input::handle_queue_update(u16 queue_index)
{
VERIFY(queue_index == EVENTQ);
auto& queue = get_queue(EVENTQ);
SpinlockLocker queue_lock(queue.lock());
size_t used;
QueueChain popped_chain = queue.pop_used_buffer_chain(used);
while (!popped_chain.is_empty()) {
popped_chain.for_each([this](PhysicalAddress paddr, size_t) {
size_t offset = paddr.get() - m_event_buffer_region->physical_page(0)->paddr().get();
auto const& event = *reinterpret_cast<VirtIOInputEvent const*>(m_event_buffer_region->vaddr().offset(offset).as_ptr());
handle_event(event);
});
supply_chain_and_notify(EVENTQ, popped_chain);
popped_chain = queue.pop_used_buffer_chain(used);
}
}
void Input::handle_event(VirtIOInputEvent const& event)
{
// TODO: Set lock key LEDs
switch (event.type) {
case EV_SYN:
switch (event.code) {
case SYN_REPORT:
m_mouse_device->handle_mouse_packet_input_event(m_current_mouse_packet);
// Don't reset the x/y values if the last event was an absolute event, as otherwise the mouse would jump to the top left corner on events other than mouse movement.
if (m_current_mouse_packet.is_relative) {
m_current_mouse_packet.x = 0;
m_current_mouse_packet.y = 0;
}
m_current_mouse_packet.z = 0;
m_current_mouse_packet.w = 0;
break;
default:
dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown EV_SYN event code: {:#x}", event.code);
break;
}
break;
case EV_KEY:
switch (event.code) {
case BTN_LEFT:
if (event.value == 1)
m_current_mouse_packet.buttons |= MousePacket::Button::LeftButton;
else
m_current_mouse_packet.buttons &= ~MousePacket::Button::LeftButton;
break;
case BTN_RIGHT:
if (event.value == 1)
m_current_mouse_packet.buttons |= MousePacket::Button::RightButton;
else
m_current_mouse_packet.buttons &= ~MousePacket::Button::RightButton;
break;
case BTN_MIDDLE:
if (event.value == 1)
m_current_mouse_packet.buttons |= MousePacket::Button::MiddleButton;
else
m_current_mouse_packet.buttons &= ~MousePacket::Button::MiddleButton;
break;
default:
// NOTE: We only supply entropy from the keyboard device, as each MouseDevice already has a EntropySource attached to it.
m_entropy_source.add_random_event(event.code);
m_keyboard_device->update_modifier(Mod_Keypad, false);
RawKeyEvent raw_key_event;
raw_key_event.is_press_down = event.value == 1;
raw_key_event.scancode = event.code;
switch (event.code) {
case KEY_LEFTALT:
m_keyboard_device->update_modifier(Mod_Alt, raw_key_event.is_press());
break;
case KEY_LEFTCTRL:
case KEY_RIGHTCTRL:
m_keyboard_device->update_modifier(Mod_Ctrl, raw_key_event.is_press());
break;
case KEY_LEFTSHIFT:
case KEY_RIGHTSHIFT:
m_keyboard_device->update_modifier(Mod_Shift, raw_key_event.is_press());
break;
case KEY_LEFTMETA:
case KEY_RIGHTMETA:
m_keyboard_device->update_modifier(Mod_Super, raw_key_event.is_press());
break;
case KEY_RIGHTALT:
m_keyboard_device->update_modifier(Mod_AltGr, raw_key_event.is_press());
break;
default:
break;
}
if ((event.code >= KEY_KP7 && event.code <= KEY_KPDOT)
|| event.code == KEY_KPASTERISK
|| event.code == KEY_KPENTER
|| event.code == KEY_KPEQUAL
|| event.code == KEY_KPSLASH) {
m_keyboard_device->update_modifier(Mod_Keypad, true);
}
// The shift key only applies to small key codes, so only use the shifted key map if the event code is small enough.
bool use_shifted_key_map = (m_keyboard_device->modifiers() & Mod_Shift) != 0 && event.code < shifted_evdev_key_map.size();
auto key_map = use_shifted_key_map ? Span<KeyCodeEntry const>(shifted_evdev_key_map) : Span<KeyCodeEntry const>(unshifted_evdev_key_map);
if (event.code >= key_map.size()) {
dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown EV_KEY event code: {:#x}", event.code);
return;
}
raw_key_event.code_entry = key_map[event.code];
KeyEvent key_event {
.key = raw_key_event.code_entry.key_code,
.map_entry_index = raw_key_event.code_entry.map_entry_index,
.scancode = raw_key_event.scancode,
.flags = raw_key_event.is_press() ? static_cast<u8>(Is_Press) : static_cast<u8>(0),
};
if (m_keyboard_device->num_lock_on() && (m_keyboard_device->modifiers() & Mod_Shift) == 0) {
if (key_event.scancode >= KEY_KP7 && raw_key_event.scancode <= KEY_KPDOT) {
auto index = key_event.scancode - KEY_KP7;
static constexpr auto numpad_key_map = to_array<KeyCodeEntry>({
{ Key_7, 0x08 },
{ Key_8, 0x09 },
{ Key_9, 0x0a },
{ Key_Invalid, 0xff },
{ Key_4, 0x05 },
{ Key_5, 0x06 },
{ Key_6, 0x07 },
{ Key_Invalid, 0xff },
{ Key_1, 0x02 },
{ Key_2, 0x03 },
{ Key_3, 0x04 },
{ Key_0, 0x0b },
{ Key_Period, 0x34 },
});
if (numpad_key_map[index].key_code != Key_Invalid) {
key_event.key = numpad_key_map[index].key_code;
key_event.map_entry_index = numpad_key_map[index].map_entry_index;
}
}
}
m_keyboard_device->handle_input_event(key_event);
break;
}
break;
case EV_REL: {
if (event.code == REL_X) {
m_current_mouse_packet.is_relative = true;
m_current_mouse_packet.x = static_cast<int>(event.value);
} else if (event.code == REL_Y) {
m_current_mouse_packet.is_relative = true;
m_current_mouse_packet.y = -static_cast<int>(event.value);
} else if (event.code == REL_WHEEL) {
m_current_mouse_packet.z = -static_cast<int>(event.value);
} else {
dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown EV_REL event code: {:#x}", event.code);
}
break;
}
case EV_ABS: {
if (event.code == ABS_X) {
m_current_mouse_packet.is_relative = false;
m_current_mouse_packet.x = static_cast<int>((event.value - m_abs_min) * 0xffff / (m_abs_max - m_abs_min));
} else if (event.code == ABS_Y) {
m_current_mouse_packet.is_relative = false;
m_current_mouse_packet.y = static_cast<int>((event.value - m_abs_min) * 0xffff / (m_abs_max - m_abs_min));
} else {
dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown EV_ABS event code: {:#x}", event.code);
}
break;
}
default:
dbgln_if(VIRTIO_DEBUG, "VirtIO::Input: Unknown event type: {:#x}", event.type);
break;
}
}
}