Kernel/HID: Untie the PS2 protocol, i8042 hardware and generic devices

For a very long time, the kernel had only support for basic PS/2 devices
such as the PS2 AT keyboard and regular PS2 mouse (with a scroll wheel).

To adapt to this, we had very simple abstractions in place, essentially,
the PS2 devices were registered as IRQ handlers (IRQ 1 and 12), and when
an interrupt was triggered, we simply had to tell the I8042Controller to
fetch a byte for us, then send it back to the appropriate device for
further processing and queueing of either a key event, or a mouse packet
so userspace can do something meaningful about it.

When we added the VMWare mouse integration feature it was easily adapted
to this paradigm, requiring small changes across the handling code for
these devices.

This patch is a major cleanup for any future advancements in the HID
subsystem.
It ensures we do things in a much more sane manner:
- We stop using LockRefPtrs. Currently, after the initialization of the
  i8042 controller, we never have to change RefPtrs in that class, as we
  simply don't support PS2 hotplugging currently.
  Also, we remove the unnecessary getters for keyboard and mouse devices
  which also returned a LockRefPtr.
- There's a clear separation between PS2 devices and the actual device
  nodes that normally exist in /dev. PS2 devices are not polled, because
  when the user uses these devices, they will trigger an IRQ which when
  is handled, could produce either a MousePacket or KeyEvent, depending
  on the device state.
  The separation is crucial for buses that are polled, for example - USB
  is a polled bus and will not generate an IRQ for HID devices.
- There's a clear separation in roles of each structure. The PS2 devices
  which are attached to a I8042Controller object are managing the device
  state, while the generic MouseDevice and KeyboardDevice manage all
  related tasks of a CharacterDevice, as well as interpreting scan code
  events and mouse relative/absolute coordinates.
This commit is contained in:
Liav A 2023-04-08 17:17:19 +03:00 committed by Jelle Raaijmakers
parent 3a261f5ee8
commit bfffe88de5
15 changed files with 285 additions and 216 deletions

View file

@ -129,12 +129,12 @@ int const key_code_count = Key_Menu;
enum KeyModifier {
Mod_None = 0x00,
Mod_Alt = 0x01,
Mod_Ctrl = 0x02,
Mod_Shift = 0x04,
Mod_Super = 0x08,
Mod_AltGr = 0x10,
Mod_Mask = 0x1f,
Mod_Alt = (1 << 0),
Mod_Ctrl = (1 << 1),
Mod_Shift = (1 << 2),
Mod_Super = (1 << 3),
Mod_AltGr = (1 << 4),
Mod_Mask = Mod_Alt | Mod_Ctrl | Mod_Shift | Mod_Super | Mod_AltGr,
Is_Press = 0x80,
};

View file

@ -10,10 +10,8 @@
#include <Kernel/Debug.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/HID/Management.h>
#include <Kernel/Scheduler.h>
#include <Kernel/Devices/HID/ScanCodeEvent.h>
#include <Kernel/Sections.h>
#include <Kernel/TTY/ConsoleManagement.h>
#include <Kernel/WorkQueue.h>
namespace Kernel {
@ -23,89 +21,46 @@ void PS2KeyboardDevice::irq_handle_byte_read(u8 byte)
{
u8 ch = byte & 0x7f;
bool pressed = !(byte & 0x80);
m_entropy_source.add_random_event(byte);
if (byte == 0xe0) {
m_has_e0_prefix = true;
return;
}
if ((m_modifiers == (Mod_Alt | Mod_Shift) || m_modifiers == (Mod_Ctrl | Mod_Alt | Mod_Shift)) && byte == 0x58) {
// Alt+Shift+F12 pressed, dump some kernel state to the debug console.
ConsoleManagement::the().switch_to_debug();
Scheduler::dump_scheduler_state(m_modifiers == (Mod_Ctrl | Mod_Alt | Mod_Shift));
}
ScanCodeEvent event {};
event.pressed = pressed;
event.e0_prefix = m_has_e0_prefix;
m_has_e0_prefix = false;
dbgln_if(KEYBOARD_DEBUG, "Keyboard::irq_handle_byte_read: {:#02x} {}", ch, (pressed ? "down" : "up"));
switch (ch) {
case 0x38:
if (m_has_e0_prefix)
update_modifier(Mod_AltGr, pressed);
else
update_modifier(Mod_Alt, pressed);
break;
case 0x1d:
update_modifier(Mod_Ctrl, pressed);
break;
case 0x5b:
m_left_super_pressed = pressed;
update_modifier(Mod_Super, m_left_super_pressed || m_right_super_pressed);
break;
case 0x5c:
m_right_super_pressed = pressed;
update_modifier(Mod_Super, m_left_super_pressed || m_right_super_pressed);
break;
case 0x2a:
m_left_shift_pressed = pressed;
update_modifier(Mod_Shift, m_left_shift_pressed || m_right_shift_pressed);
break;
case 0x36:
m_right_shift_pressed = pressed;
update_modifier(Mod_Shift, m_left_shift_pressed || m_right_shift_pressed);
break;
}
switch (ch) {
case I8042Response::Acknowledge:
break;
default:
if ((m_modifiers & Mod_Alt) != 0 && ch >= 2 && ch <= ConsoleManagement::s_max_virtual_consoles + 1) {
// FIXME: Do something sanely here if we can't allocate a work queue?
MUST(g_io_work->try_queue([ch]() {
ConsoleManagement::the().switch_to(ch - 0x02);
}));
}
key_state_changed(ch, pressed);
}
event.scan_code_value = ch;
m_keyboard_device->handle_scan_code_input_event(event);
}
bool PS2KeyboardDevice::handle_irq(RegisterState const&)
{
// The controller will read the data and call irq_handle_byte_read
// for the appropriate device
return m_i8042_controller->irq_process_input_buffer(HIDDevice::Type::Keyboard);
return m_i8042_controller->irq_process_input_buffer(instrument_type());
}
UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<PS2KeyboardDevice>> PS2KeyboardDevice::try_to_initialize(I8042Controller const& ps2_controller)
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<PS2KeyboardDevice>> PS2KeyboardDevice::try_to_initialize(I8042Controller const& ps2_controller, KeyboardDevice const& keyboard_device)
{
auto keyboard_device = TRY(DeviceManagement::try_create_device<PS2KeyboardDevice>(ps2_controller));
TRY(keyboard_device->initialize());
return keyboard_device;
auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) PS2KeyboardDevice(ps2_controller, keyboard_device)));
TRY(device->initialize());
return device;
}
UNMAP_AFTER_INIT ErrorOr<void> PS2KeyboardDevice::initialize()
{
return m_i8042_controller->reset_device(HIDDevice::Type::Keyboard);
return m_i8042_controller->reset_device(PS2Device::Type::Keyboard);
}
// FIXME: UNMAP_AFTER_INIT might not be correct, because in practice PS/2 devices
// are hot pluggable.
UNMAP_AFTER_INIT PS2KeyboardDevice::PS2KeyboardDevice(I8042Controller const& ps2_controller)
UNMAP_AFTER_INIT PS2KeyboardDevice::PS2KeyboardDevice(I8042Controller const& ps2_controller, KeyboardDevice const& keyboard_device)
: IRQHandler(IRQ_KEYBOARD)
, KeyboardDevice()
, I8042Device(ps2_controller)
, PS2Device(ps2_controller)
, m_keyboard_device(keyboard_device)
{
}

View file

@ -10,39 +10,39 @@
#include <AK/Types.h>
#include <Kernel/API/KeyCode.h>
#include <Kernel/Arch/x86_64/ISABus/I8042Controller.h>
#include <Kernel/Devices/HID/KeyboardDevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Random.h>
namespace Kernel {
class PS2KeyboardDevice final : public IRQHandler
, public KeyboardDevice
, public I8042Device {
, public PS2Device {
friend class DeviceManagement;
public:
static ErrorOr<NonnullLockRefPtr<PS2KeyboardDevice>> try_to_initialize(I8042Controller const&);
static ErrorOr<NonnullOwnPtr<PS2KeyboardDevice>> try_to_initialize(I8042Controller const&, KeyboardDevice const&);
virtual ~PS2KeyboardDevice() override;
ErrorOr<void> initialize();
virtual StringView purpose() const override { return class_name(); }
virtual StringView purpose() const override { return "PS2KeyboardDevice"sv; }
// ^I8042Device
// ^PS2Device
virtual void irq_handle_byte_read(u8 byte) override;
virtual void enable_interrupts() override
{
enable_irq();
}
virtual Type instrument_type() const override { return Type::Keyboard; }
private:
explicit PS2KeyboardDevice(I8042Controller const&);
PS2KeyboardDevice(I8042Controller const&, KeyboardDevice const&);
// ^IRQHandler
virtual bool handle_irq(RegisterState const&) override;
// ^CharacterDevice
virtual StringView class_name() const override { return "KeyboardDevice"sv; }
bool m_has_e0_prefix { false };
NonnullRefPtr<KeyboardDevice> const m_keyboard_device;
};
}

View file

@ -17,10 +17,10 @@ namespace Kernel {
#define PS2MOUSE_INTELLIMOUSE_ID 0x03
#define PS2MOUSE_INTELLIMOUSE_EXPLORER_ID 0x04
UNMAP_AFTER_INIT PS2MouseDevice::PS2MouseDevice(I8042Controller const& ps2_controller)
UNMAP_AFTER_INIT PS2MouseDevice::PS2MouseDevice(I8042Controller const& ps2_controller, MouseDevice const& mouse_device)
: IRQHandler(IRQ_MOUSE)
, MouseDevice()
, I8042Device(ps2_controller)
, PS2Device(ps2_controller)
, m_mouse_device(mouse_device)
{
}
@ -35,21 +35,14 @@ bool PS2MouseDevice::handle_irq(RegisterState const&)
void PS2MouseDevice::irq_handle_byte_read(u8 byte)
{
auto commit_packet = [&] {
auto commit_packet = [this]() {
m_data_state = 0;
dbgln_if(PS2MOUSE_DEBUG, "PS2Mouse: {}, {} {} {}",
m_data.bytes[1],
m_data.bytes[2],
(m_data.bytes[0] & 1) ? "Left" : "",
(m_data.bytes[0] & 2) ? "Right" : "");
m_entropy_source.add_random_event(m_data.dword);
{
SpinlockLocker lock(m_queue_lock);
m_queue.enqueue(parse_data_packet(m_data));
}
evaluate_block_conditions();
m_mouse_device->handle_mouse_packet_input_event(parse_data_packet(m_data));
};
VERIFY(m_data_state < sizeof(m_data.bytes) / sizeof(m_data.bytes[0]));
@ -59,25 +52,26 @@ void PS2MouseDevice::irq_handle_byte_read(u8 byte)
case 0:
if (!(byte & 0x08)) {
dbgln("PS2Mouse: Stream out of sync.");
break;
return;
}
++m_data_state;
break;
return;
case 1:
++m_data_state;
break;
return;
case 2:
if (m_has_wheel) {
++m_data_state;
break;
return;
}
commit_packet();
break;
return;
case 3:
VERIFY(m_has_wheel);
commit_packet();
break;
return;
}
VERIFY_NOT_REACHED();
}
MousePacket PS2MouseDevice::parse_data_packet(RawPacket const& raw_packet)
@ -173,11 +167,11 @@ ErrorOr<void> PS2MouseDevice::set_sample_rate(u8 rate)
return {};
}
UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<PS2MouseDevice>> PS2MouseDevice::try_to_initialize(I8042Controller const& ps2_controller)
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<PS2MouseDevice>> PS2MouseDevice::try_to_initialize(I8042Controller const& ps2_controller, MouseDevice const& mouse_device)
{
auto mouse_device = TRY(DeviceManagement::try_create_device<PS2MouseDevice>(ps2_controller));
TRY(mouse_device->initialize());
return mouse_device;
auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) PS2MouseDevice(ps2_controller, mouse_device)));
TRY(device->initialize());
return device;
}
UNMAP_AFTER_INIT ErrorOr<void> PS2MouseDevice::initialize()

View file

@ -9,33 +9,32 @@
#include <AK/CircularQueue.h>
#include <Kernel/API/MousePacket.h>
#include <Kernel/Arch/x86_64/ISABus/I8042Controller.h>
#include <Kernel/Devices/HID/MouseDevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Random.h>
namespace Kernel {
class PS2MouseDevice : public IRQHandler
, public MouseDevice
, public I8042Device {
, public PS2Device {
friend class DeviceManagement;
public:
static ErrorOr<NonnullLockRefPtr<PS2MouseDevice>> try_to_initialize(I8042Controller const&);
static ErrorOr<NonnullOwnPtr<PS2MouseDevice>> try_to_initialize(I8042Controller const&, MouseDevice const&);
ErrorOr<void> initialize();
virtual ~PS2MouseDevice() override;
virtual StringView purpose() const override { return class_name(); }
virtual StringView purpose() const override { return "PS2MouseDevice"sv; }
// ^I8042Device
// ^PS2Device
virtual void irq_handle_byte_read(u8 byte) override;
virtual void enable_interrupts() override
{
enable_irq();
}
virtual Type instrument_type() const override { return Type::Mouse; }
protected:
explicit PS2MouseDevice(I8042Controller const&);
PS2MouseDevice(I8042Controller const&, MouseDevice const&);
// ^IRQHandler
virtual bool handle_irq(RegisterState const&) override;
@ -58,6 +57,8 @@ protected:
RawPacket m_data;
bool m_has_wheel { false };
bool m_has_five_buttons { false };
NonnullRefPtr<MouseDevice> const m_mouse_device;
};
}

View file

@ -11,16 +11,16 @@
namespace Kernel {
UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<VMWareMouseDevice>> VMWareMouseDevice::try_to_initialize(I8042Controller const& ps2_controller)
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<VMWareMouseDevice>> VMWareMouseDevice::try_to_initialize(I8042Controller const& ps2_controller, MouseDevice const& mouse_device)
{
// FIXME: return the correct error
if (!VMWareBackdoor::the())
return Error::from_errno(EIO);
if (!VMWareBackdoor::the()->vmmouse_is_absolute())
return Error::from_errno(EIO);
auto mouse_device = TRY(DeviceManagement::try_create_device<VMWareMouseDevice>(ps2_controller));
TRY(mouse_device->initialize());
return mouse_device;
auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) VMWareMouseDevice(ps2_controller, mouse_device)));
TRY(device->initialize());
return device;
}
void VMWareMouseDevice::irq_handle_byte_read(u8)
@ -42,17 +42,12 @@ void VMWareMouseDevice::irq_handle_byte_read(u8)
VERIFY(number_of_mouse_event_bytes % 4 == 0);
auto mouse_packet = backdoor->receive_mouse_packet();
m_entropy_source.add_random_event(mouse_packet);
{
SpinlockLocker lock(m_queue_lock);
m_queue.enqueue(mouse_packet);
}
m_mouse_device->handle_mouse_packet_input_event(mouse_packet);
}
evaluate_block_conditions();
}
VMWareMouseDevice::VMWareMouseDevice(I8042Controller const& ps2_controller)
: PS2MouseDevice(ps2_controller)
VMWareMouseDevice::VMWareMouseDevice(I8042Controller const& ps2_controller, MouseDevice const& mouse_device)
: PS2MouseDevice(ps2_controller, mouse_device)
{
}
VMWareMouseDevice::~VMWareMouseDevice() = default;

View file

@ -18,14 +18,14 @@ namespace Kernel {
class VMWareMouseDevice final : public PS2MouseDevice {
public:
friend class DeviceManagement;
static ErrorOr<NonnullLockRefPtr<VMWareMouseDevice>> try_to_initialize(I8042Controller const&);
static ErrorOr<NonnullOwnPtr<VMWareMouseDevice>> try_to_initialize(I8042Controller const&, MouseDevice const&);
virtual ~VMWareMouseDevice() override;
// ^I8042Device
// ^PS2Device
virtual void irq_handle_byte_read(u8 byte) override;
private:
explicit VMWareMouseDevice(I8042Controller const&);
VMWareMouseDevice(I8042Controller const&, MouseDevice const&);
};
}

View file

@ -159,55 +159,63 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
// Try to detect and initialize the devices
if (m_first_port_available) {
auto error_or_device = PS2KeyboardDevice::try_to_initialize(*this);
// FIXME: Actually figure out the connected PS2 device type
m_first_ps2_port.device_type = PS2Device::Type::Keyboard;
auto keyboard_device = TRY(KeyboardDevice::try_to_initialize());
auto error_or_device = PS2KeyboardDevice::try_to_initialize(*this, *keyboard_device);
if (error_or_device.is_error()) {
dbgln("I8042: Keyboard device failed to initialize, disable");
m_first_port_available = false;
configuration &= ~I8042ConfigurationFlag::FirstPS2PortInterrupt;
configuration |= I8042ConfigurationFlag::FirstPS2PortClock;
m_keyboard_device = nullptr;
m_first_ps2_port.device = nullptr;
m_first_ps2_port.device_type = {};
SpinlockLocker lock(m_lock);
// NOTE: Before setting the actual scan code set, stop packet streaming entirely.
TRY(send_command(HIDDevice::Type::Keyboard, I8042Command::DisablePacketStreaming));
TRY(send_command(PS2Device::Type::Keyboard, I8042Command::DisablePacketStreaming));
TRY(do_wait_then_write(I8042Port::Buffer, I8042Command::SetScanCodeSet));
TRY(do_wait_then_write(I8042Port::Buffer, 0x2));
TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration));
TRY(do_wait_then_write(I8042Port::Buffer, configuration));
} else {
m_keyboard_device = error_or_device.release_value();
m_first_ps2_port.device = error_or_device.release_value();
}
}
if (m_second_port_available) {
auto vmmouse_device_or_error = VMWareMouseDevice::try_to_initialize(*this);
// FIXME: Actually figure out the connected PS2 device type
m_second_ps2_port.device_type = PS2Device::Type::Mouse;
auto mouse_device = TRY(MouseDevice::try_to_initialize());
auto vmmouse_device_or_error = VMWareMouseDevice::try_to_initialize(*this, *mouse_device);
if (vmmouse_device_or_error.is_error()) {
// FIXME: is there something to do with the VMWare errors?
auto mouse_device_or_error = PS2MouseDevice::try_to_initialize(*this);
auto mouse_device_or_error = PS2MouseDevice::try_to_initialize(*this, *mouse_device);
if (mouse_device_or_error.is_error()) {
dbgln("I8042: Mouse device failed to initialize, disable");
m_second_port_available = false;
configuration |= I8042ConfigurationFlag::SecondPS2PortClock;
m_mouse_device = nullptr;
m_second_ps2_port.device = nullptr;
m_second_ps2_port.device_type = {};
SpinlockLocker lock(m_lock);
TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration));
TRY(do_wait_then_write(I8042Port::Buffer, configuration));
} else {
m_mouse_device = mouse_device_or_error.release_value();
m_second_ps2_port.device = mouse_device_or_error.release_value();
}
} else {
m_mouse_device = vmmouse_device_or_error.release_value();
m_second_ps2_port.device = vmmouse_device_or_error.release_value();
}
}
// Enable IRQs after both are detected and initialized
if (m_keyboard_device)
m_keyboard_device->enable_interrupts();
if (m_mouse_device)
m_mouse_device->enable_interrupts();
if (m_first_ps2_port.device)
m_first_ps2_port.device->enable_interrupts();
if (m_second_ps2_port.device)
m_second_ps2_port.device->enable_interrupts();
return {};
}
bool I8042Controller::irq_process_input_buffer(HIDDevice::Type instrument_type)
bool I8042Controller::irq_process_input_buffer(PS2Device::Type instrument_type)
{
VERIFY(Processor::current_in_irq());
@ -215,17 +223,20 @@ bool I8042Controller::irq_process_input_buffer(HIDDevice::Type instrument_type)
if (!(status & I8042StatusFlag::OutputBuffer))
return false;
u8 byte = IO::in8(I8042Port::Buffer);
if (instrument_type == HIDDevice::Type::Mouse) {
VERIFY(m_mouse_device);
static_cast<PS2MouseDevice&>(*m_mouse_device).irq_handle_byte_read(byte);
return true;
PS2Port* selected_port = nullptr;
if (m_first_ps2_port.device_type.has_value() && m_first_ps2_port.device_type.value() == instrument_type) {
VERIFY(m_first_ps2_port.device);
selected_port = &m_first_ps2_port;
} else if (m_second_ps2_port.device_type.has_value() && m_second_ps2_port.device_type.value() == instrument_type) {
VERIFY(m_second_ps2_port.device);
selected_port = &m_second_ps2_port;
} else {
return false;
}
if (instrument_type == HIDDevice::Type::Keyboard) {
VERIFY(m_keyboard_device);
static_cast<PS2KeyboardDevice&>(*m_keyboard_device).irq_handle_byte_read(byte);
return true;
}
return false;
selected_port->device->irq_handle_byte_read(byte);
return true;
}
ErrorOr<void> I8042Controller::drain_output_buffer()
@ -241,9 +252,9 @@ ErrorOr<void> I8042Controller::drain_output_buffer()
return Error::from_errno(EBUSY);
}
ErrorOr<void> I8042Controller::do_reset_device(HIDDevice::Type device)
ErrorOr<void> I8042Controller::do_reset_device(PS2Device::Type device)
{
VERIFY(device != HIDDevice::Type::Unknown);
VERIFY(device != PS2Device::Type::Unknown);
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
@ -260,9 +271,9 @@ ErrorOr<void> I8042Controller::do_reset_device(HIDDevice::Type device)
return {};
}
ErrorOr<u8> I8042Controller::do_send_command(HIDDevice::Type device, u8 command)
ErrorOr<u8> I8042Controller::do_send_command(PS2Device::Type device, u8 command)
{
VERIFY(device != HIDDevice::Type::Unknown);
VERIFY(device != PS2Device::Type::Unknown);
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
@ -270,9 +281,9 @@ ErrorOr<u8> I8042Controller::do_send_command(HIDDevice::Type device, u8 command)
return do_write_to_device(device, command);
}
ErrorOr<u8> I8042Controller::do_send_command(HIDDevice::Type device, u8 command, u8 data)
ErrorOr<u8> I8042Controller::do_send_command(PS2Device::Type device, u8 command, u8 data)
{
VERIFY(device != HIDDevice::Type::Unknown);
VERIFY(device != PS2Device::Type::Unknown);
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
@ -283,9 +294,9 @@ ErrorOr<u8> I8042Controller::do_send_command(HIDDevice::Type device, u8 command,
return response;
}
ErrorOr<u8> I8042Controller::do_write_to_device(HIDDevice::Type device, u8 data)
ErrorOr<u8> I8042Controller::do_write_to_device(PS2Device::Type device, u8 data)
{
VERIFY(device != HIDDevice::Type::Unknown);
VERIFY(device != PS2Device::Type::Unknown);
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
@ -293,7 +304,7 @@ ErrorOr<u8> I8042Controller::do_write_to_device(HIDDevice::Type device, u8 data)
int attempts = 0;
u8 response;
do {
if (device != HIDDevice::Type::Keyboard) {
if (device != PS2Device::Type::Keyboard) {
TRY(prepare_for_output());
IO::out8(I8042Port::Command, I8042Command::WriteSecondPS2PortInputBuffer);
}
@ -307,25 +318,25 @@ ErrorOr<u8> I8042Controller::do_write_to_device(HIDDevice::Type device, u8 data)
return response;
}
ErrorOr<u8> I8042Controller::do_read_from_device(HIDDevice::Type device)
ErrorOr<u8> I8042Controller::do_read_from_device(PS2Device::Type device)
{
VERIFY(device != HIDDevice::Type::Unknown);
VERIFY(device != PS2Device::Type::Unknown);
TRY(prepare_for_input(device));
return IO::in8(I8042Port::Buffer);
}
ErrorOr<void> I8042Controller::prepare_for_input(HIDDevice::Type device)
ErrorOr<void> I8042Controller::prepare_for_input(PS2Device::Type device)
{
VERIFY(m_lock.is_locked());
u8 const second_port_flag = device == HIDDevice::Type::Keyboard ? 0 : I8042StatusFlag::SecondPS2PortOutputBuffer;
u8 const second_port_flag = device == PS2Device::Type::Keyboard ? 0 : I8042StatusFlag::SecondPS2PortOutputBuffer;
for (int attempt = 0; attempt < 1000; attempt++) {
u8 status = IO::in8(I8042Port::Status);
if (!(status & I8042StatusFlag::OutputBuffer)) {
microseconds_delay(1000);
continue;
}
if (device == HIDDevice::Type::Unknown)
if (device == PS2Device::Type::Unknown)
return {};
if ((status & I8042StatusFlag::SecondPS2PortOutputBuffer) == second_port_flag)
return {};
@ -369,7 +380,7 @@ ErrorOr<void> I8042Controller::do_wait_then_write(u8 port, u8 data)
ErrorOr<u8> I8042Controller::do_wait_then_read(u8 port)
{
VERIFY(m_lock.is_locked());
TRY(prepare_for_input(HIDDevice::Type::Unknown));
TRY(prepare_for_input(PS2Device::Type::Unknown));
return IO::in8(port);
}

View file

@ -10,6 +10,7 @@
#include <Kernel/Devices/HID/Controller.h>
#include <Kernel/Devices/HID/KeyboardDevice.h>
#include <Kernel/Devices/HID/MouseDevice.h>
#include <Kernel/Devices/HID/ScanCodeEvent.h>
#include <Kernel/Locking/Spinlock.h>
namespace Kernel {
@ -69,15 +70,23 @@ enum I8042Response : u8 {
class I8042Controller;
class PS2KeyboardDevice;
class PS2MouseDevice;
class I8042Device {
class PS2Device {
public:
virtual ~I8042Device() = default;
virtual ~PS2Device() = default;
virtual void irq_handle_byte_read(u8 byte) = 0;
virtual void enable_interrupts() = 0;
enum class Type {
Unknown = 0,
Keyboard,
Mouse,
};
virtual Type instrument_type() const = 0;
protected:
explicit I8042Device(I8042Controller const& ps2_controller)
explicit PS2Device(I8042Controller const& ps2_controller)
: m_i8042_controller(ps2_controller)
{
}
@ -97,24 +106,24 @@ public:
ErrorOr<void> detect_devices();
ErrorOr<void> reset_device(HIDDevice::Type device)
ErrorOr<void> reset_device(PS2Device::Type device)
{
SpinlockLocker lock(m_lock);
return do_reset_device(device);
}
ErrorOr<u8> send_command(HIDDevice::Type device, u8 command)
ErrorOr<u8> send_command(PS2Device::Type device, u8 command)
{
SpinlockLocker lock(m_lock);
return do_send_command(device, command);
}
ErrorOr<u8> send_command(HIDDevice::Type device, u8 command, u8 data)
ErrorOr<u8> send_command(PS2Device::Type device, u8 command, u8 data)
{
SpinlockLocker lock(m_lock);
return do_send_command(device, command, data);
}
ErrorOr<u8> read_from_device(HIDDevice::Type device)
ErrorOr<u8> read_from_device(PS2Device::Type device)
{
SpinlockLocker lock(m_lock);
return do_read_from_device(device);
@ -133,20 +142,20 @@ public:
}
ErrorOr<void> prepare_for_output();
ErrorOr<void> prepare_for_input(HIDDevice::Type);
ErrorOr<void> prepare_for_input(PS2Device::Type);
bool irq_process_input_buffer(HIDDevice::Type);
bool irq_process_input_buffer(PS2Device::Type);
// Note: This function exists only for the initialization process of the controller
bool check_existence_via_probing(Badge<HIDManagement>);
private:
I8042Controller();
ErrorOr<void> do_reset_device(HIDDevice::Type);
ErrorOr<u8> do_send_command(HIDDevice::Type type, u8 data);
ErrorOr<u8> do_send_command(HIDDevice::Type device, u8 command, u8 data);
ErrorOr<u8> do_write_to_device(HIDDevice::Type device, u8 data);
ErrorOr<u8> do_read_from_device(HIDDevice::Type device);
ErrorOr<void> do_reset_device(PS2Device::Type);
ErrorOr<u8> do_send_command(PS2Device::Type type, u8 data);
ErrorOr<u8> do_send_command(PS2Device::Type device, u8 command, u8 data);
ErrorOr<u8> do_write_to_device(PS2Device::Type device, u8 data);
ErrorOr<u8> do_read_from_device(PS2Device::Type device);
ErrorOr<void> do_wait_then_write(u8 port, u8 data);
ErrorOr<u8> do_wait_then_read(u8 port);
ErrorOr<void> drain_output_buffer();
@ -159,8 +168,30 @@ private:
bool m_first_port_available { false };
bool m_second_port_available { false };
bool m_is_dual_channel { false };
LockRefPtr<PS2MouseDevice> m_mouse_device;
LockRefPtr<PS2KeyboardDevice> m_keyboard_device;
// NOTE: Each i8042 controller can have at most 2 ports - a regular (traditional
// ATKBD) port and AUX port (for mouse devices mostly).
// However, the specification for i8042 controller, as well as decent hardware
// implementations and software drivers actually allow a user to still operate
// a keyboard and mouse even if they were connected in reverse (i.e. keyboard
// was connected to AUX port, and mouse was connected to the traditional ATKBD port).
//
// Please note, that if the keyboard and mouse devices are connected in reverse, then ATKBD translation mode
// cannot be sanely enabled due to obvious peripheral devices' protocol differences, and will result
// in misproper data being sent back.
struct PS2Port {
OwnPtr<PS2Device> device;
// NOTE: This value is being used as 1:1 map between the I8042 port being handled, to
// the either the MouseDevice or KeyboardDevice being attached.
Optional<PS2Device::Type> device_type;
};
// NOTE: Each i8042 controller can have at most 2 devices - a mouse and keyboard,
// mouse and a mouse, or keyboard and a keyboard.
// NOTE: This is usually used as the ATKBD port.
PS2Port m_first_ps2_port;
// NOTE: This is usually used as the AUX port.
PS2Port m_second_ps2_port;
};
}

View file

@ -12,14 +12,6 @@
namespace Kernel {
class HIDDevice : public CharacterDevice {
public:
enum class Type {
Unknown = 0,
Keyboard,
Mouse,
};
virtual Type instrument_type() const = 0;
protected:
HIDDevice(MajorNumber major, MinorNumber minor)

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2021-2023, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2021, Edwin Hoksberg <mail@edwinhoksberg.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -9,9 +9,14 @@
#include <AK/Assertions.h>
#include <AK/Types.h>
#include <Kernel/API/Ioctl.h>
#include <Kernel/API/KeyCode.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/HID/KeyboardDevice.h>
#include <Kernel/Scheduler.h>
#include <Kernel/Sections.h>
#include <Kernel/TTY/ConsoleManagement.h>
#include <Kernel/TTY/VirtualConsole.h>
#include <Kernel/WorkQueue.h>
namespace Kernel {
@ -209,16 +214,58 @@ static constexpr KeyCode shifted_key_map[0x100] = {
Key_Menu,
};
void KeyboardDevice::key_state_changed(u8 scan_code, bool pressed)
void KeyboardDevice::handle_scan_code_input_event(ScanCodeEvent event)
{
KeyCode key = (m_modifiers & Mod_Shift) ? shifted_key_map[scan_code] : unshifted_key_map[scan_code];
m_entropy_source.add_random_event(event.scan_code_value);
switch (event.scan_code_value) {
case 0x38:
if (event.e0_prefix)
update_modifier(Mod_AltGr, event.pressed);
else
update_modifier(Mod_Alt, event.pressed);
break;
case 0x1d:
update_modifier(Mod_Ctrl, event.pressed);
break;
case 0x5b:
m_left_super_pressed = event.pressed;
update_modifier(Mod_Super, m_left_super_pressed || m_right_super_pressed);
break;
case 0x5c:
m_right_super_pressed = event.pressed;
update_modifier(Mod_Super, m_left_super_pressed || m_right_super_pressed);
break;
case 0x2a:
m_left_shift_pressed = event.pressed;
update_modifier(Mod_Shift, m_left_shift_pressed || m_right_shift_pressed);
break;
case 0x36:
m_right_shift_pressed = event.pressed;
update_modifier(Mod_Shift, m_left_shift_pressed || m_right_shift_pressed);
break;
}
if (key == Key_NumLock && pressed)
KeyCode key = (m_modifiers & Mod_Shift) ? shifted_key_map[event.scan_code_value] : unshifted_key_map[event.scan_code_value];
if ((m_modifiers == (Mod_Alt | Mod_Shift) || m_modifiers == (Mod_Ctrl | Mod_Alt | Mod_Shift)) && key == Key_F12) {
// Alt+Shift+F12 pressed, dump some kernel state to the debug console.
ConsoleManagement::the().switch_to_debug();
Scheduler::dump_scheduler_state(m_modifiers == (Mod_Ctrl | Mod_Alt | Mod_Shift));
}
if ((m_modifiers & Mod_Alt) != 0 && key >= Key_1 && key <= Key_1 + ConsoleManagement::s_max_virtual_consoles + 1) {
// FIXME: Do something sanely here if we can't allocate a work queue?
MUST(g_io_work->try_queue([key]() {
ConsoleManagement::the().switch_to(key - Key_1);
}));
}
if (key == Key_NumLock && event.pressed)
m_num_lock_on = !m_num_lock_on;
if (m_num_lock_on && !m_has_e0_prefix) {
if (scan_code >= 0x47 && scan_code <= 0x53) {
u8 index = scan_code - 0x47;
if (m_num_lock_on && !event.e0_prefix) {
if (event.scan_code_value >= 0x47 && event.scan_code_value <= 0x53) {
u8 index = event.scan_code_value - 0x47;
constexpr KeyCode numpad_key_map[13] = { Key_7, Key_8, Key_9, Key_Invalid, Key_4, Key_5, Key_6, Key_Invalid, Key_1, Key_2, Key_3, Key_0, Key_Comma };
KeyCode newKey = numpad_key_map[index];
@ -228,48 +275,51 @@ void KeyboardDevice::key_state_changed(u8 scan_code, bool pressed)
}
}
Event event;
event.key = key;
event.scancode = m_has_e0_prefix ? 0xe000 + scan_code : scan_code;
event.flags = m_modifiers;
event.e0_prefix = m_has_e0_prefix;
event.caps_lock_on = m_caps_lock_on;
event.code_point = HIDManagement::the().get_char_from_character_map(event);
Event queued_event;
queued_event.key = key;
queued_event.scancode = event.e0_prefix ? 0xe000 + event.scan_code_value : event.scan_code_value;
queued_event.flags = m_modifiers;
queued_event.e0_prefix = event.e0_prefix;
queued_event.caps_lock_on = m_caps_lock_on;
queued_event.code_point = HIDManagement::the().get_char_from_character_map(queued_event);
// If using a non-QWERTY layout, event.key needs to be updated to be the same as event.code_point
KeyCode mapped_key = code_point_to_key_code(event.code_point);
// If using a non-QWERTY layout, queued_event.key needs to be updated to be the same as event.code_point
KeyCode mapped_key = code_point_to_key_code(queued_event.code_point);
if (mapped_key != KeyCode::Key_Invalid) {
event.key = mapped_key;
queued_event.key = mapped_key;
key = mapped_key;
}
if (!g_caps_lock_remapped_to_ctrl && key == Key_CapsLock && pressed)
if (!g_caps_lock_remapped_to_ctrl && key == Key_CapsLock && event.pressed)
m_caps_lock_on = !m_caps_lock_on;
if (g_caps_lock_remapped_to_ctrl && key == Key_CapsLock) {
m_caps_lock_to_ctrl_pressed = pressed;
m_caps_lock_to_ctrl_pressed = event.pressed;
update_modifier(Mod_Ctrl, m_caps_lock_to_ctrl_pressed);
}
if (pressed)
event.flags |= Is_Press;
if (event.pressed)
queued_event.flags |= Is_Press;
{
SpinlockLocker locker(HIDManagement::the().m_client_lock);
if (HIDManagement::the().m_client)
HIDManagement::the().m_client->on_key_pressed(event);
HIDManagement::the().m_client->on_key_pressed(queued_event);
}
{
SpinlockLocker lock(m_queue_lock);
m_queue.enqueue(event);
m_queue.enqueue(queued_event);
}
m_has_e0_prefix = false;
evaluate_block_conditions();
}
ErrorOr<NonnullRefPtr<KeyboardDevice>> KeyboardDevice::try_to_initialize()
{
return *TRY(DeviceManagement::try_create_device<KeyboardDevice>());
}
// FIXME: UNMAP_AFTER_INIT is fine for now, but for hot-pluggable devices
// like USB keyboards, we need to remove this
UNMAP_AFTER_INIT KeyboardDevice::KeyboardDevice()

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2021-2023, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2021, Edwin Hoksberg <mail@edwinhoksberg.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -12,15 +12,20 @@
#include <Kernel/API/KeyCode.h>
#include <Kernel/Devices/CharacterDevice.h>
#include <Kernel/Devices/HID/Device.h>
#include <Kernel/Devices/HID/ScanCodeEvent.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Random.h>
namespace Kernel {
class KeyboardDevice : public HIDDevice {
friend class DeviceManagement;
public:
using Event = KeyEvent;
static ErrorOr<NonnullRefPtr<KeyboardDevice>> try_to_initialize();
virtual ~KeyboardDevice() override;
// ^CharacterDevice
@ -29,8 +34,7 @@ public:
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EINVAL; }
virtual bool can_write(OpenFileDescription const&, u64) const override { return true; }
// ^HIDDevice
virtual Type instrument_type() const override { return Type::Keyboard; }
void handle_scan_code_input_event(ScanCodeEvent);
// ^File
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override;
@ -54,7 +58,6 @@ protected:
bool m_caps_lock_to_ctrl_pressed { false };
bool m_caps_lock_on { false };
bool m_num_lock_on { false };
bool m_has_e0_prefix { false };
bool m_left_shift_pressed { false };
bool m_right_shift_pressed { false };
bool m_left_super_pressed { false };

View file

@ -1,19 +1,35 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2021-2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/HID/Management.h>
#include <Kernel/Devices/HID/MouseDevice.h>
namespace Kernel {
ErrorOr<NonnullRefPtr<MouseDevice>> MouseDevice::try_to_initialize()
{
return *TRY(DeviceManagement::try_create_device<MouseDevice>());
}
MouseDevice::MouseDevice()
: HIDDevice(10, HIDManagement::the().generate_minor_device_number_for_mouse())
{
}
void MouseDevice::handle_mouse_packet_input_event(MousePacket packet)
{
m_entropy_source.add_random_event(packet);
{
SpinlockLocker lock(m_queue_lock);
m_queue.enqueue(packet);
}
evaluate_block_conditions();
}
MouseDevice::~MouseDevice() = default;
bool MouseDevice::can_read(OpenFileDescription const&, u64) const

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2021-2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -18,7 +18,10 @@
namespace Kernel {
class MouseDevice : public HIDDevice {
friend class DeviceManagement;
public:
static ErrorOr<NonnullRefPtr<MouseDevice>> try_to_initialize();
virtual ~MouseDevice() override;
// ^CharacterDevice
@ -27,8 +30,7 @@ public:
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EINVAL; }
virtual bool can_write(OpenFileDescription const&, u64) const override { return true; }
// ^HIDDevice
virtual Type instrument_type() const override { return Type::Mouse; }
void handle_mouse_packet_input_event(MousePacket);
protected:
MouseDevice();

View file

@ -0,0 +1,19 @@
/*
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
namespace Kernel {
struct ScanCodeEvent {
u32 scan_code_value { 0 };
bool pressed { false };
bool e0_prefix { false };
};
}