mirror of
https://github.com/RGBCube/serenity
synced 2025-09-14 17:57:34 +00:00
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:
parent
3a261f5ee8
commit
bfffe88de5
15 changed files with 285 additions and 216 deletions
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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&);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue