mirror of
https://github.com/RGBCube/serenity
synced 2025-07-28 16:37:47 +00:00
Kernel: Untie PS2 mouse and keyboard devices from i8042 implementation
To ensure actual PS2 code is not tied to the i8042 code, we make them separated in the following ways: - PS2KeyboardDevice and PS2MouseDevice classes are no longer inheriting from the IRQHandler class. Instead we have specific IRQHandler derived class for the i8042 controller implementation, which is used to ensure that we don't end up mixing PS2 code with low-level interrupt handling functionality. In the future this means that we could add a driver for other PS2 controllers that might have only one interrupt handler but multiple PS2 devices are attached, therefore, making it easier to put the right propagation flow from the controller driver all the way to the HID core code. - A simple abstraction layer is added between the PS2 command set which devices could use and the actual implementation low-level commands. This means that the code in PS2MouseDevice and PS2KeyboardDevice classes is no longer tied to i8042 implementation-specific commands, so now these objects could send PS2 commands to their PS2 controller and get a PS2Response which abstracts the given response too.
This commit is contained in:
parent
d276cac82c
commit
89a8920764
13 changed files with 392 additions and 272 deletions
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/HID/Management.h>
|
||||
#include <Kernel/Devices/HID/ScanCodeEvent.h>
|
||||
#include <Kernel/Sections.h>
|
||||
#include <Kernel/TTY/ConsoleManagement.h>
|
||||
#include <Kernel/Tasks/Scheduler.h>
|
||||
#include <Kernel/Tasks/WorkQueue.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
#define IRQ_KEYBOARD 1
|
||||
|
||||
void PS2KeyboardDevice::irq_handle_byte_read(u8 byte)
|
||||
{
|
||||
u8 ch = byte & 0x7f;
|
||||
bool pressed = !(byte & 0x80);
|
||||
if (byte == 0xe0) {
|
||||
m_has_e0_prefix = true;
|
||||
return;
|
||||
}
|
||||
|
||||
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"));
|
||||
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(instrument_type());
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<PS2KeyboardDevice>> PS2KeyboardDevice::try_to_initialize(I8042Controller const& ps2_controller, KeyboardDevice const& 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(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, KeyboardDevice const& keyboard_device)
|
||||
: IRQHandler(IRQ_KEYBOARD)
|
||||
, PS2Device(ps2_controller)
|
||||
, m_keyboard_device(keyboard_device)
|
||||
{
|
||||
}
|
||||
|
||||
// FIXME: UNMAP_AFTER_INIT might not be correct, because in practice PS/2 devices
|
||||
// are hot pluggable.
|
||||
UNMAP_AFTER_INIT PS2KeyboardDevice::~PS2KeyboardDevice() = default;
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/CircularQueue.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/API/KeyCode.h>
|
||||
#include <Kernel/Arch/x86_64/ISABus/I8042Controller.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Security/Random.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class PS2KeyboardDevice final : public IRQHandler
|
||||
, public PS2Device {
|
||||
friend class DeviceManagement;
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<PS2KeyboardDevice>> try_to_initialize(I8042Controller const&, KeyboardDevice const&);
|
||||
virtual ~PS2KeyboardDevice() override;
|
||||
ErrorOr<void> initialize();
|
||||
|
||||
virtual StringView purpose() const override { return "PS2KeyboardDevice"sv; }
|
||||
|
||||
// ^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:
|
||||
PS2KeyboardDevice(I8042Controller const&, KeyboardDevice const&);
|
||||
|
||||
// ^IRQHandler
|
||||
virtual bool handle_irq(RegisterState const&) override;
|
||||
|
||||
bool m_has_e0_prefix { false };
|
||||
|
||||
NonnullRefPtr<KeyboardDevice> const m_keyboard_device;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Arch/x86_64/Hypervisor/VMWareBackdoor.h>
|
||||
#include <Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Sections.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
#define IRQ_MOUSE 12
|
||||
|
||||
#define PS2MOUSE_INTELLIMOUSE_ID 0x03
|
||||
#define PS2MOUSE_INTELLIMOUSE_EXPLORER_ID 0x04
|
||||
|
||||
UNMAP_AFTER_INIT PS2MouseDevice::PS2MouseDevice(I8042Controller const& ps2_controller, MouseDevice const& mouse_device)
|
||||
: IRQHandler(IRQ_MOUSE)
|
||||
, PS2Device(ps2_controller)
|
||||
, m_mouse_device(mouse_device)
|
||||
{
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT PS2MouseDevice::~PS2MouseDevice() = default;
|
||||
|
||||
bool PS2MouseDevice::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(instrument_type());
|
||||
}
|
||||
|
||||
void PS2MouseDevice::irq_handle_byte_read(u8 byte)
|
||||
{
|
||||
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_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]));
|
||||
m_data.bytes[m_data_state] = byte;
|
||||
|
||||
switch (m_data_state) {
|
||||
case 0:
|
||||
if (!(byte & 0x08)) {
|
||||
dbgln("PS2Mouse: Stream out of sync.");
|
||||
return;
|
||||
}
|
||||
++m_data_state;
|
||||
return;
|
||||
case 1:
|
||||
++m_data_state;
|
||||
return;
|
||||
case 2:
|
||||
if (m_has_wheel) {
|
||||
++m_data_state;
|
||||
return;
|
||||
}
|
||||
commit_packet();
|
||||
return;
|
||||
case 3:
|
||||
VERIFY(m_has_wheel);
|
||||
commit_packet();
|
||||
return;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
MousePacket PS2MouseDevice::parse_data_packet(RawPacket const& raw_packet)
|
||||
{
|
||||
int x = raw_packet.bytes[1];
|
||||
int y = raw_packet.bytes[2];
|
||||
int z = 0;
|
||||
int w = 0;
|
||||
if (m_has_wheel) {
|
||||
// FIXME: For non-Intellimouse, this is a full byte.
|
||||
// However, for now, m_has_wheel is only set for Intellimouse.
|
||||
z = (char)(raw_packet.bytes[3] & 0x0f);
|
||||
|
||||
// -1 in 4 bits
|
||||
if (z == 15)
|
||||
z = -1;
|
||||
|
||||
if ((raw_packet.bytes[3] & 0xc0) == 0x40) {
|
||||
// FIXME: Scroll only functions correctly when the sign is flipped there
|
||||
w = -z;
|
||||
z = 0;
|
||||
} else {
|
||||
w = 0;
|
||||
}
|
||||
}
|
||||
bool x_overflow = raw_packet.bytes[0] & 0x40;
|
||||
bool y_overflow = raw_packet.bytes[0] & 0x80;
|
||||
bool x_sign = raw_packet.bytes[0] & 0x10;
|
||||
bool y_sign = raw_packet.bytes[0] & 0x20;
|
||||
if (x && x_sign)
|
||||
x -= 0x100;
|
||||
if (y && y_sign)
|
||||
y -= 0x100;
|
||||
if (x_overflow || y_overflow) {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
MousePacket packet;
|
||||
packet.x = x;
|
||||
packet.y = y;
|
||||
packet.z = z;
|
||||
packet.w = w;
|
||||
packet.buttons = raw_packet.bytes[0] & 0x07;
|
||||
|
||||
if (m_has_five_buttons) {
|
||||
if (raw_packet.bytes[3] & 0x10)
|
||||
packet.buttons |= MousePacket::BackwardButton;
|
||||
if (raw_packet.bytes[3] & 0x20)
|
||||
packet.buttons |= MousePacket::ForwardButton;
|
||||
}
|
||||
|
||||
packet.is_relative = true;
|
||||
dbgln_if(PS2MOUSE_DEBUG, "PS2 Relative Mouse: Buttons {:x}", packet.buttons);
|
||||
dbgln_if(PS2MOUSE_DEBUG, "Mouse: X {}, Y {}, Z {}, W {}", packet.x, packet.y, packet.z, packet.w);
|
||||
return packet;
|
||||
}
|
||||
|
||||
ErrorOr<u8> PS2MouseDevice::get_device_id()
|
||||
{
|
||||
TRY(send_command(I8042Command::GetDeviceID));
|
||||
return read_from_device();
|
||||
}
|
||||
|
||||
ErrorOr<u8> PS2MouseDevice::read_from_device()
|
||||
{
|
||||
return m_i8042_controller->read_from_device(instrument_type());
|
||||
}
|
||||
|
||||
ErrorOr<u8> PS2MouseDevice::send_command(u8 command)
|
||||
{
|
||||
u8 response = TRY(m_i8042_controller->send_command(instrument_type(), command));
|
||||
|
||||
if (response != I8042Response::Acknowledge) {
|
||||
dbgln("PS2MouseDevice: Command {} got {} but expected ack: {}", command, response, static_cast<u8>(I8042Response::Acknowledge));
|
||||
return Error::from_errno(EIO);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
ErrorOr<u8> PS2MouseDevice::send_command(u8 command, u8 data)
|
||||
{
|
||||
u8 response = TRY(m_i8042_controller->send_command(instrument_type(), command, data));
|
||||
if (response != I8042Response::Acknowledge) {
|
||||
dbgln("PS2MouseDevice: Command {} got {} but expected ack: {}", command, response, static_cast<u8>(I8042Response::Acknowledge));
|
||||
return Error::from_errno(EIO);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
ErrorOr<void> PS2MouseDevice::set_sample_rate(u8 rate)
|
||||
{
|
||||
TRY(send_command(I8042Command::SetSampleRate, rate));
|
||||
return {};
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<PS2MouseDevice>> PS2MouseDevice::try_to_initialize(I8042Controller const& ps2_controller, MouseDevice const& 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()
|
||||
{
|
||||
TRY(m_i8042_controller->reset_device(instrument_type()));
|
||||
|
||||
u8 device_id = TRY(read_from_device());
|
||||
|
||||
TRY(send_command(I8042Command::SetDefaults));
|
||||
|
||||
TRY(send_command(I8042Command::EnablePacketStreaming));
|
||||
|
||||
if (device_id != PS2MOUSE_INTELLIMOUSE_ID) {
|
||||
// Send magical wheel initiation sequence.
|
||||
TRY(set_sample_rate(200));
|
||||
TRY(set_sample_rate(100));
|
||||
TRY(set_sample_rate(80));
|
||||
device_id = TRY(get_device_id());
|
||||
}
|
||||
if (device_id == PS2MOUSE_INTELLIMOUSE_ID) {
|
||||
m_has_wheel = true;
|
||||
dmesgln("PS2MouseDevice: Mouse wheel enabled!");
|
||||
} else {
|
||||
dmesgln("PS2MouseDevice: No mouse wheel detected!");
|
||||
}
|
||||
|
||||
if (device_id == PS2MOUSE_INTELLIMOUSE_ID) {
|
||||
// Try to enable 5 buttons as well!
|
||||
TRY(set_sample_rate(200));
|
||||
TRY(set_sample_rate(200));
|
||||
TRY(set_sample_rate(80));
|
||||
device_id = TRY(get_device_id());
|
||||
}
|
||||
|
||||
if (device_id == PS2MOUSE_INTELLIMOUSE_EXPLORER_ID) {
|
||||
m_has_five_buttons = true;
|
||||
dmesgln("PS2MouseDevice: 5 buttons enabled!");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/CircularQueue.h>
|
||||
#include <Kernel/API/MousePacket.h>
|
||||
#include <Kernel/Arch/x86_64/ISABus/I8042Controller.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Security/Random.h>
|
||||
|
||||
namespace Kernel {
|
||||
class PS2MouseDevice : public IRQHandler
|
||||
, public PS2Device {
|
||||
friend class DeviceManagement;
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<PS2MouseDevice>> try_to_initialize(I8042Controller const&, MouseDevice const&);
|
||||
ErrorOr<void> initialize();
|
||||
|
||||
virtual ~PS2MouseDevice() override;
|
||||
|
||||
virtual StringView purpose() const override { return "PS2MouseDevice"sv; }
|
||||
|
||||
// ^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:
|
||||
PS2MouseDevice(I8042Controller const&, MouseDevice const&);
|
||||
|
||||
// ^IRQHandler
|
||||
virtual bool handle_irq(RegisterState const&) override;
|
||||
|
||||
struct RawPacket {
|
||||
union {
|
||||
u32 dword;
|
||||
u8 bytes[4];
|
||||
};
|
||||
};
|
||||
|
||||
ErrorOr<u8> read_from_device();
|
||||
ErrorOr<u8> send_command(u8 command);
|
||||
ErrorOr<u8> send_command(u8 command, u8 data);
|
||||
MousePacket parse_data_packet(RawPacket const&);
|
||||
ErrorOr<void> set_sample_rate(u8);
|
||||
ErrorOr<u8> get_device_id();
|
||||
|
||||
u8 m_data_state { 0 };
|
||||
RawPacket m_data;
|
||||
bool m_has_wheel { false };
|
||||
bool m_has_five_buttons { false };
|
||||
|
||||
NonnullRefPtr<MouseDevice> const m_mouse_device;
|
||||
};
|
||||
|
||||
}
|
|
@ -11,19 +11,19 @@
|
|||
|
||||
namespace Kernel {
|
||||
|
||||
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<VMWareMouseDevice>> VMWareMouseDevice::try_to_initialize(I8042Controller const& ps2_controller, MouseDevice const& mouse_device)
|
||||
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<VMWareMouseDevice>> VMWareMouseDevice::try_to_initialize(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, 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 device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) VMWareMouseDevice(ps2_controller, mouse_device)));
|
||||
auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) VMWareMouseDevice(serial_io_controller, port_index, mouse_device)));
|
||||
TRY(device->initialize());
|
||||
return device;
|
||||
}
|
||||
|
||||
void VMWareMouseDevice::irq_handle_byte_read(u8)
|
||||
void VMWareMouseDevice::handle_byte_read_from_serial_input(u8)
|
||||
{
|
||||
auto backdoor = VMWareBackdoor::the();
|
||||
VERIFY(backdoor);
|
||||
|
@ -46,8 +46,8 @@ void VMWareMouseDevice::irq_handle_byte_read(u8)
|
|||
}
|
||||
}
|
||||
|
||||
VMWareMouseDevice::VMWareMouseDevice(I8042Controller const& ps2_controller, MouseDevice const& mouse_device)
|
||||
: PS2MouseDevice(ps2_controller, mouse_device)
|
||||
VMWareMouseDevice::VMWareMouseDevice(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, MouseDevice const& mouse_device)
|
||||
: PS2MouseDevice(serial_io_controller, port_index, mouse_device)
|
||||
{
|
||||
}
|
||||
VMWareMouseDevice::~VMWareMouseDevice() = default;
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
|
||||
#include <AK/CircularQueue.h>
|
||||
#include <Kernel/API/MousePacket.h>
|
||||
#include <Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.h>
|
||||
#include <Kernel/Arch/x86_64/ISABus/I8042Controller.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Devices/HID/PS2/MouseDevice.h>
|
||||
#include <Kernel/Security/Random.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
@ -18,14 +17,14 @@ namespace Kernel {
|
|||
class VMWareMouseDevice final : public PS2MouseDevice {
|
||||
public:
|
||||
friend class DeviceManagement;
|
||||
static ErrorOr<NonnullOwnPtr<VMWareMouseDevice>> try_to_initialize(I8042Controller const&, MouseDevice const&);
|
||||
static ErrorOr<NonnullOwnPtr<VMWareMouseDevice>> try_to_initialize(SerialIOController const&, SerialIOController::PortIndex, MouseDevice const&);
|
||||
virtual ~VMWareMouseDevice() override;
|
||||
|
||||
// ^PS2Device
|
||||
virtual void irq_handle_byte_read(u8 byte) override;
|
||||
virtual void handle_byte_read_from_serial_input(u8 byte) override;
|
||||
|
||||
private:
|
||||
VMWareMouseDevice(I8042Controller const&, MouseDevice const&);
|
||||
VMWareMouseDevice(SerialIOController const&, SerialIOController::PortIndex, MouseDevice const&);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue