1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 01:50:46 +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:
Liav A 2023-05-12 18:04:01 +03:00 committed by Andrew Kaster
parent d276cac82c
commit 89a8920764
13 changed files with 392 additions and 272 deletions

View File

@ -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;
};
}

View File

@ -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;

View File

@ -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&);
};
}

View File

@ -6,17 +6,37 @@
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/x86_64/IO.h>
#include <Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.h>
#include <Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.h>
#include <Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.h>
#include <Kernel/Arch/x86_64/ISABus/I8042Controller.h>
#include <Kernel/Bus/SerialIO/Device.h>
#include <Kernel/Devices/HID/KeyboardDevice.h>
#include <Kernel/Devices/HID/MouseDevice.h>
#include <Kernel/Devices/HID/PS2/KeyboardDevice.h>
#include <Kernel/Devices/HID/PS2/MouseDevice.h>
#include <Kernel/Devices/HID/ScanCodeEvent.h>
#include <Kernel/Sections.h>
namespace Kernel {
#define IRQ_FIRST_PORT 1
#define IRQ_SECOND_PORT 12
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<I8042ControllerIRQHandler>> I8042ControllerIRQHandler::try_create(I8042Controller const& controller, u8 irq_number)
{
return adopt_nonnull_own_or_enomem(new I8042ControllerIRQHandler(controller, irq_number));
}
bool I8042ControllerIRQHandler::handle_irq(RegisterState const&)
{
return m_controller->handle_irq({}, interrupt_number());
}
UNMAP_AFTER_INIT I8042ControllerIRQHandler::I8042ControllerIRQHandler(I8042Controller const& controller, u8 irq_number)
: IRQHandler(irq_number)
, m_controller(controller)
{
}
UNMAP_AFTER_INIT ErrorOr<NonnullRefPtr<I8042Controller>> I8042Controller::create()
{
return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) I8042Controller()));
@ -26,6 +46,18 @@ UNMAP_AFTER_INIT I8042Controller::I8042Controller()
{
}
bool I8042Controller::handle_irq(Badge<I8042ControllerIRQHandler>, u8 irq_number)
{
// NOTE: The controller will read the data and call handle_byte_read_from_serial_input
// for the appropriate device.
VERIFY(irq_number == IRQ_FIRST_PORT || irq_number == IRQ_SECOND_PORT);
PortIndex port_index = I8042PortIndex::FirstPort;
if (irq_number == IRQ_SECOND_PORT) {
port_index = I8042PortIndex::SecondPort;
}
return irq_process_input_buffer(port_index);
}
UNMAP_AFTER_INIT bool I8042Controller::check_existence_via_probing(Badge<HIDManagement>)
{
{
@ -50,7 +82,7 @@ UNMAP_AFTER_INIT bool I8042Controller::check_existence_via_probing(Badge<HIDMana
}
{
auto result = do_wait_then_read(I8042Port::Buffer);
auto result = do_wait_then_read_any_input(I8042Port::Buffer);
if (result.is_error()) {
dbgln("I8042: Trying to read configuration failed during the existence test, error {}", result.error());
return false;
@ -89,7 +121,6 @@ UNMAP_AFTER_INIT bool I8042Controller::check_existence_via_probing(Badge<HIDMana
UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
{
u8 configuration;
{
SpinlockLocker lock(m_lock);
@ -100,7 +131,7 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
TRY(do_wait_then_write(I8042Port::Command, I8042Command::DisableSecondPS2Port)); // ignored if it doesn't exist
TRY(do_wait_then_write(I8042Port::Command, I8042Command::ReadConfiguration));
configuration = TRY(do_wait_then_read(I8042Port::Buffer));
configuration = TRY(do_wait_then_read_any_input(I8042Port::Buffer));
configuration &= ~I8042ConfigurationFlag::FirstPS2PortInterrupt;
configuration &= ~I8042ConfigurationFlag::SecondPS2PortInterrupt;
configuration |= I8042ConfigurationFlag::FirstPS2PortTranslation;
@ -110,7 +141,7 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
// Perform controller self-test
TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestPS2Controller));
auto self_test_result = TRY(do_wait_then_read(I8042Port::Buffer));
auto self_test_result = TRY(do_wait_then_read_any_input(I8042Port::Buffer));
if (self_test_result == I8042Response::ControllerTestPassed) {
// Restore configuration in case the controller reset
TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration));
@ -125,7 +156,7 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
// Test ports and enable them if available
TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestFirstPS2Port));
auto first_port_test_result = TRY(do_wait_then_read(I8042Port::Buffer));
auto first_port_test_result = TRY(do_wait_then_read_any_input(I8042Port::Buffer));
m_first_port_available = (first_port_test_result == 0);
if (m_first_port_available) {
@ -140,7 +171,7 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
if (m_is_dual_channel) {
TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestSecondPS2Port));
auto test_second_port_result = TRY(do_wait_then_read(I8042Port::Buffer));
auto test_second_port_result = TRY(do_wait_then_read_any_input(I8042Port::Buffer));
m_second_port_available = (test_second_port_result == 0);
if (m_second_port_available) {
TRY(do_wait_then_write(I8042Port::Command, I8042Command::EnableSecondPS2Port));
@ -163,9 +194,9 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
// Try to detect and initialize the devices
if (m_first_port_available) {
// FIXME: Actually figure out the connected PS2 device type
m_first_ps2_port.device_type = PS2Device::Type::Keyboard;
m_first_ps2_port.device_type = PS2DeviceType::StandardKeyboard;
auto keyboard_device = TRY(KeyboardDevice::try_to_initialize());
auto error_or_device = PS2KeyboardDevice::try_to_initialize(*this, *keyboard_device);
auto error_or_device = PS2KeyboardDevice::try_to_initialize(*this, I8042PortIndex::FirstPort, *keyboard_device);
if (error_or_device.is_error()) {
dbgln("I8042: Keyboard device failed to initialize, disable");
m_first_port_available = false;
@ -175,7 +206,7 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
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(PS2Device::Type::Keyboard, I8042Command::DisablePacketStreaming));
TRY(do_send_command(I8042PortIndex::FirstPort, I8042Command::DisablePacketStreaming));
TRY(do_wait_then_write(I8042Port::Buffer, I8042Command::SetScanCodeSet));
TRY(do_wait_then_write(I8042Port::Buffer, 0x2));
@ -187,12 +218,12 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
}
if (m_second_port_available) {
// FIXME: Actually figure out the connected PS2 device type
m_second_ps2_port.device_type = PS2Device::Type::Mouse;
m_second_ps2_port.device_type = PS2DeviceType::StandardMouse;
auto mouse_device = TRY(MouseDevice::try_to_initialize());
auto vmmouse_device_or_error = VMWareMouseDevice::try_to_initialize(*this, *mouse_device);
auto vmmouse_device_or_error = VMWareMouseDevice::try_to_initialize(*this, I8042PortIndex::SecondPort, *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, *mouse_device);
auto mouse_device_or_error = PS2MouseDevice::try_to_initialize(*this, I8042PortIndex::SecondPort, *mouse_device);
if (mouse_device_or_error.is_error()) {
dbgln("I8042: Mouse device failed to initialize, disable");
m_second_port_available = false;
@ -210,15 +241,70 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
}
}
m_irq_handlers[0] = TRY(I8042ControllerIRQHandler::try_create(*this, IRQ_FIRST_PORT));
m_irq_handlers[1] = TRY(I8042ControllerIRQHandler::try_create(*this, IRQ_SECOND_PORT));
// Enable IRQs after both are detected and initialized
if (m_first_ps2_port.device)
m_first_ps2_port.device->enable_interrupts();
m_irq_handlers[0]->enable_irq();
if (m_second_ps2_port.device)
m_second_ps2_port.device->enable_interrupts();
m_irq_handlers[1]->enable_irq();
return {};
}
bool I8042Controller::irq_process_input_buffer(PS2Device::Type instrument_type)
ErrorOr<void> I8042Controller::send_command(PortIndex port_index, DeviceCommand command)
{
switch (command) {
case DeviceCommand::GetDeviceID: {
SpinlockLocker lock(m_lock);
return do_send_command(port_index, I8042Command::GetDeviceID);
}
case DeviceCommand::EnablePacketStreaming: {
SpinlockLocker lock(m_lock);
return do_send_command(port_index, I8042Command::EnablePacketStreaming);
}
case DeviceCommand::DisablePacketStreaming: {
SpinlockLocker lock(m_lock);
return do_send_command(port_index, I8042Command::DisablePacketStreaming);
}
case DeviceCommand::SetDefaults: {
SpinlockLocker lock(m_lock);
return do_send_command(port_index, I8042Command::SetDefaults);
}
// NOTE: The sample rate command is supported only with sending data byte with it!
case DeviceCommand::SetSampleRate:
return EOPNOTSUPP;
default:
return EINVAL;
}
}
ErrorOr<void> I8042Controller::send_command(PortIndex port_index, DeviceCommand command, u8 data)
{
switch (command) {
// NOTE: Only the sample rate command supports sending data byte with it!
case DeviceCommand::SetSampleRate: {
SpinlockLocker lock(m_lock);
return do_send_command(port_index, I8042Command::SetSampleRate, data);
}
case DeviceCommand::GetDeviceID:
return EOPNOTSUPP;
case DeviceCommand::EnablePacketStreaming:
return EOPNOTSUPP;
case DeviceCommand::DisablePacketStreaming:
return EOPNOTSUPP;
case DeviceCommand::SetDefaults:
return EOPNOTSUPP;
default:
return EINVAL;
}
}
bool I8042Controller::irq_process_input_buffer(PortIndex port_index)
{
VERIFY(Processor::current_in_irq());
@ -228,17 +314,16 @@ bool I8042Controller::irq_process_input_buffer(PS2Device::Type instrument_type)
u8 byte = IO::in8(I8042Port::Buffer);
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);
if (port_index == I8042PortIndex::FirstPort)
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);
else if (port_index == I8042PortIndex::SecondPort)
selected_port = &m_second_ps2_port;
} else {
else
return false;
}
selected_port->device->irq_handle_byte_read(byte);
if (!selected_port->device)
return false;
selected_port->device->handle_byte_read_from_serial_input(byte);
return true;
}
@ -255,18 +340,14 @@ ErrorOr<void> I8042Controller::drain_output_buffer()
return Error::from_errno(EBUSY);
}
ErrorOr<void> I8042Controller::do_reset_device(PS2Device::Type device)
ErrorOr<void> I8042Controller::do_reset_device(PortIndex port_index)
{
VERIFY(device != PS2Device::Type::Unknown);
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
auto reset_result = TRY(do_send_command(device, I8042Command::Reset));
// FIXME: Is this the correct errno value for this?
if (reset_result != I8042Response::Acknowledge)
return Error::from_errno(EIO);
TRY(do_send_command(port_index, I8042Command::Reset));
// Wait until we get the self-test result
auto self_test_result = TRY(do_wait_then_read(I8042Port::Buffer));
auto self_test_result = TRY(do_wait_then_read_any_input(I8042Port::Buffer));
// FIXME: Is this the correct errno value for this?
if (self_test_result != I8042Response::Success)
@ -274,32 +355,28 @@ ErrorOr<void> I8042Controller::do_reset_device(PS2Device::Type device)
return {};
}
ErrorOr<u8> I8042Controller::do_send_command(PS2Device::Type device, u8 command)
ErrorOr<void> I8042Controller::do_send_command(PortIndex port_index, u8 command)
{
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
TRY(do_write_to_device(port_index, command));
return {};
}
ErrorOr<void> I8042Controller::do_send_command(PortIndex port_index, u8 command, u8 data)
{
VERIFY(device != PS2Device::Type::Unknown);
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
return do_write_to_device(device, command);
TRY(do_write_to_device(port_index, command));
TRY(do_write_to_device(port_index, data));
return {};
}
ErrorOr<u8> I8042Controller::do_send_command(PS2Device::Type device, u8 command, u8 data)
ErrorOr<void> I8042Controller::do_write_to_device(PortIndex port_index, u8 data)
{
VERIFY(device != PS2Device::Type::Unknown);
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
u8 response = TRY(do_write_to_device(device, command));
if (response == I8042Response::Acknowledge)
response = TRY(do_write_to_device(device, data));
return response;
}
ErrorOr<u8> I8042Controller::do_write_to_device(PS2Device::Type device, u8 data)
{
VERIFY(device != PS2Device::Type::Unknown);
VERIFY(m_lock.is_locked());
VERIFY(!Processor::current_in_irq());
@ -307,39 +384,61 @@ ErrorOr<u8> I8042Controller::do_write_to_device(PS2Device::Type device, u8 data)
int attempts = 0;
u8 response;
do {
if (device != PS2Device::Type::Keyboard) {
TRY(prepare_for_output());
if (port_index != I8042PortIndex::FirstPort) {
VERIFY(port_index == I8042PortIndex::SecondPort);
TRY(prepare_for_any_output());
IO::out8(I8042Port::Command, I8042Command::WriteSecondPS2PortInputBuffer);
}
TRY(prepare_for_output());
TRY(prepare_for_any_output());
IO::out8(I8042Port::Buffer, data);
response = TRY(do_wait_then_read(I8042Port::Buffer));
response = TRY(do_wait_then_read_any_input(I8042Port::Buffer));
} while (response == I8042Response::Resend && ++attempts < 250);
if (attempts >= 250)
dbgln("Failed to write byte to device, gave up");
return response;
if (attempts >= 250 || response == I8042Response::Resend) {
dbgln("I8042: Failed to write byte to device, gave up");
return Error::from_errno(EBUSY);
}
return {};
}
ErrorOr<u8> I8042Controller::do_read_from_device(PS2Device::Type device)
ErrorOr<u8> I8042Controller::do_read_from_device(PortIndex port_index)
{
VERIFY(device != PS2Device::Type::Unknown);
TRY(prepare_for_input(device));
TRY(prepare_for_input(port_index));
return IO::in8(I8042Port::Buffer);
}
ErrorOr<void> I8042Controller::prepare_for_input(PS2Device::Type device)
ErrorOr<void> I8042Controller::prepare_for_any_input()
{
VERIFY(m_lock.is_locked());
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 == PS2Device::Type::Unknown)
return {};
}
return Error::from_errno(EBUSY);
}
ErrorOr<void> I8042Controller::prepare_for_input(PortIndex port_index)
{
VERIFY(m_lock.is_locked());
u8 const second_port_flag = port_index == I8042PortIndex::FirstPort ? 0 : I8042StatusFlag::SecondPS2PortOutputBuffer;
PS2Port* port = nullptr;
if (port_index == I8042PortIndex::FirstPort)
port = &m_first_ps2_port;
else if (port_index == I8042PortIndex::SecondPort)
port = &m_second_ps2_port;
else
return Error::from_errno(ENODEV);
for (int attempt = 0; attempt < 1000; attempt++) {
u8 status = IO::in8(I8042Port::Status);
if (!(status & I8042StatusFlag::OutputBuffer)) {
microseconds_delay(1000);
continue;
}
if (!port->device_type.has_value() || port->device_type.value() == PS2DeviceType::Unknown)
return {};
if ((status & I8042StatusFlag::SecondPS2PortOutputBuffer) == second_port_flag)
return {};
@ -348,7 +447,7 @@ ErrorOr<void> I8042Controller::prepare_for_input(PS2Device::Type device)
return Error::from_errno(EBUSY);
}
ErrorOr<void> I8042Controller::prepare_for_output()
ErrorOr<void> I8042Controller::prepare_for_any_output()
{
VERIFY(m_lock.is_locked());
for (int attempt = 0; attempt < 250; attempt++) {
@ -375,16 +474,15 @@ UNMAP_AFTER_INIT u8 I8042Controller::do_read(u8 port)
ErrorOr<void> I8042Controller::do_wait_then_write(u8 port, u8 data)
{
VERIFY(m_lock.is_locked());
TRY(prepare_for_output());
TRY(prepare_for_any_output());
IO::out8(port, data);
return {};
}
ErrorOr<u8> I8042Controller::do_wait_then_read(u8 port)
ErrorOr<u8> I8042Controller::do_wait_then_read_any_input(u8 port)
{
VERIFY(m_lock.is_locked());
TRY(prepare_for_input(PS2Device::Type::Unknown));
TRY(prepare_for_any_input());
return IO::in8(port);
}
}

View File

@ -8,9 +8,9 @@
#include <AK/Types.h>
#include <Kernel/Bus/SerialIO/Controller.h>
#include <Kernel/Devices/HID/Controller.h>
#include <Kernel/Devices/HID/KeyboardDevice.h>
#include <Kernel/Devices/HID/MouseDevice.h>
#include <Kernel/Bus/SerialIO/Device.h>
#include <Kernel/Bus/SerialIO/PS2Definitions.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Locking/Spinlock.h>
namespace Kernel {
@ -67,35 +67,25 @@ enum I8042Response : u8 {
Resend = 0xFE,
};
class I8042Controller;
class PS2KeyboardDevice;
class PS2MouseDevice;
class PS2Device {
class I8042Controller;
class I8042ControllerIRQHandler final
: public IRQHandler {
public:
virtual ~PS2Device() = default;
static ErrorOr<NonnullOwnPtr<I8042ControllerIRQHandler>> try_create(I8042Controller const&, u8 irq_number);
virtual void irq_handle_byte_read(u8 byte) = 0;
virtual void enable_interrupts() = 0;
private:
I8042ControllerIRQHandler(I8042Controller const& controller, u8 irq_number);
enum class Type {
Unknown = 0,
Keyboard,
Mouse,
};
// ^IRQHandler
virtual bool handle_irq(RegisterState const&) override;
virtual StringView purpose() const override { return "I8042ControllerIRQHandler"sv; }
virtual Type instrument_type() const = 0;
protected:
explicit PS2Device(I8042Controller const& ps2_controller)
: m_i8042_controller(ps2_controller)
{
}
NonnullRefPtr<I8042Controller> const m_i8042_controller;
NonnullRefPtr<I8042Controller> const m_controller;
};
class PS2KeyboardDevice;
class PS2MouseDevice;
class HIDManagement;
class I8042Controller final : public SerialIOController {
friend class PS2KeyboardDevice;
@ -105,59 +95,47 @@ public:
static ErrorOr<NonnullRefPtr<I8042Controller>> create();
ErrorOr<void> detect_devices();
virtual ErrorOr<void> send_command(PortIndex, DeviceCommand command) override;
virtual ErrorOr<void> send_command(PortIndex, DeviceCommand command, u8 data) override;
ErrorOr<void> reset_device(PS2Device::Type device)
virtual ErrorOr<void> reset_device(PortIndex port_index) override
{
SpinlockLocker lock(m_lock);
return do_reset_device(device);
return do_reset_device(port_index);
}
ErrorOr<u8> send_command(PS2Device::Type device, u8 command)
virtual ErrorOr<u8> read_from_device(PortIndex port_index) override
{
SpinlockLocker lock(m_lock);
return do_send_command(device, command);
return do_read_from_device(port_index);
}
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(PS2Device::Type device)
{
SpinlockLocker lock(m_lock);
return do_read_from_device(device);
}
ErrorOr<void> wait_then_write(u8 port, u8 data)
{
SpinlockLocker lock(m_lock);
return do_wait_then_write(port, data);
}
ErrorOr<u8> wait_then_read(u8 port)
{
SpinlockLocker lock(m_lock);
return do_wait_then_read(port);
}
ErrorOr<void> prepare_for_output();
ErrorOr<void> prepare_for_input(PS2Device::Type);
bool irq_process_input_buffer(PS2Device::Type);
virtual ErrorOr<void> prepare_for_input(PortIndex) override;
// Note: This function exists only for the initialization process of the controller
bool check_existence_via_probing(Badge<HIDManagement>);
bool handle_irq(Badge<I8042ControllerIRQHandler>, u8 irq_number);
private:
I8042Controller();
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);
bool irq_process_input_buffer(PortIndex);
ErrorOr<void> prepare_for_any_output();
ErrorOr<void> prepare_for_any_input();
ErrorOr<void> do_reset_device(PortIndex);
ErrorOr<void> do_send_command(PortIndex port_index, u8 data);
ErrorOr<void> do_send_command(PortIndex port_index, u8 command, u8 data);
ErrorOr<void> do_write_to_device(PortIndex port_index, u8 data);
ErrorOr<u8> do_read_from_device(PortIndex port_index);
ErrorOr<void> do_wait_then_write(u8 port, u8 data);
ErrorOr<u8> do_wait_then_read(u8 port);
// NOTE: The meaning of "any input" here is that this is not attached
// to any PS2 port, but rather we accept any serial input, which is vital
// when reading values before initializing any actual PS2 device!
ErrorOr<u8> do_wait_then_read_any_input(u8 port);
ErrorOr<void> drain_output_buffer();
// Note: These functions exist only for the initialization process of the controller
@ -169,6 +147,11 @@ private:
bool m_second_port_available { false };
bool m_is_dual_channel { false };
enum I8042PortIndex {
FirstPort = 0,
SecondPort = 1,
};
// 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
@ -180,10 +163,10 @@ private:
// 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;
OwnPtr<SerialIODevice> 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;
Optional<PS2DeviceType> device_type;
};
// NOTE: Each i8042 controller can have at most 2 devices - a mouse and keyboard,
@ -192,6 +175,8 @@ private:
PS2Port m_first_ps2_port;
// NOTE: This is usually used as the AUX port.
PS2Port m_second_ps2_port;
Array<OwnPtr<I8042ControllerIRQHandler>, 2> m_irq_handlers;
};
}

View File

@ -7,6 +7,7 @@
#pragma once
#include <AK/AtomicRefCounted.h>
#include <AK/DistinctNumeric.h>
#include <AK/Error.h>
#include <AK/IntrusiveList.h>
#include <AK/NonnullRefPtr.h>
@ -20,8 +21,25 @@ class SerialIOController : public AtomicRefCounted<SerialIOController> {
friend class HIDManagement;
public:
enum class DeviceCommand : u8 {
GetDeviceID,
SetSampleRate,
EnablePacketStreaming,
DisablePacketStreaming,
SetDefaults,
};
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, PortIndex);
virtual ~SerialIOController() = default;
virtual ErrorOr<void> reset_device(PortIndex) = 0;
virtual ErrorOr<void> send_command(PortIndex, DeviceCommand command) = 0;
virtual ErrorOr<void> send_command(PortIndex, DeviceCommand command, u8 data) = 0;
virtual ErrorOr<u8> read_from_device(PortIndex) = 0;
virtual ErrorOr<void> prepare_for_input(PortIndex) = 0;
protected:
SerialIOController() = default;

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DistinctNumeric.h>
#include <AK/Types.h>
#include <Kernel/Bus/SerialIO/Controller.h>
namespace Kernel {
class SerialIODevice {
public:
virtual ~SerialIODevice() = default;
virtual void handle_byte_read_from_serial_input(u8 byte) = 0;
SerialIOController::PortIndex attached_port_index() const { return m_attached_port_index; }
SerialIOController& attached_controller() { return m_serial_io_controller; }
SerialIOController const& attached_controller() const { return m_serial_io_controller; }
protected:
SerialIODevice(SerialIOController const& serial_io_controller, SerialIOController::PortIndex attached_port_index)
: m_serial_io_controller(serial_io_controller)
, m_attached_port_index(attached_port_index)
{
}
private:
NonnullRefPtr<SerialIOController> const m_serial_io_controller;
SerialIOController::PortIndex m_attached_port_index { 0 };
};
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DistinctNumeric.h>
#include <AK/Types.h>
namespace Kernel {
// NOTE: This list is derived from https://wiki.osdev.org/%228042%22_PS/2_Controller#Detecting_PS.2F2_Device_Types
enum class PS2DeviceType {
Unknown,
ATKeyboard,
StandardMouse,
ScrollWheelMouse,
MouseWith5Buttons,
MF2Keyboard,
ThinkPadKeyboard,
NCDKeyboard,
HostConnected122KeysKeyboard,
StandardKeyboard, // 122 keys keyboard
JapaneseGKeyboard,
JapanesePKeyboard,
JapaneseAKeyboard,
NCDSunKeyboard,
};
}

View File

@ -60,6 +60,8 @@ set(KERNEL_SOURCES
Devices/HID/KeyboardDevice.cpp
Devices/HID/Management.cpp
Devices/HID/MouseDevice.cpp
Devices/HID/PS2/KeyboardDevice.cpp
Devices/HID/PS2/MouseDevice.cpp
Devices/Generic/ConsoleDevice.cpp
Devices/Generic/DeviceControlDevice.cpp
Devices/Generic/FullDevice.cpp
@ -389,8 +391,6 @@ if ("${SERENITY_ARCH}" STREQUAL "x86_64")
Arch/x86_64/Time/RTC.cpp
Arch/x86_64/PCSpeaker.cpp
Arch/x86_64/ISABus/HID/PS2KeyboardDevice.cpp
Arch/x86_64/ISABus/HID/PS2MouseDevice.cpp
Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp
Arch/x86_64/ISABus/I8042Controller.cpp
Arch/x86_64/ISABus/IDEController.cpp

View File

@ -1,15 +1,16 @@
/*
* 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>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Types.h>
#include <Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.h>
#include <Kernel/Bus/SerialIO/PS2Definitions.h>
#include <Kernel/Debug.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/HID/Management.h>
#include <Kernel/Devices/HID/PS2/KeyboardDevice.h>
#include <Kernel/Devices/HID/ScanCodeEvent.h>
#include <Kernel/Sections.h>
#include <Kernel/TTY/ConsoleManagement.h>
@ -18,9 +19,7 @@
namespace Kernel {
#define IRQ_KEYBOARD 1
void PS2KeyboardDevice::irq_handle_byte_read(u8 byte)
void PS2KeyboardDevice::handle_byte_read_from_serial_input(u8 byte)
{
u8 ch = byte & 0x7f;
bool pressed = !(byte & 0x80);
@ -34,35 +33,27 @@ void PS2KeyboardDevice::irq_handle_byte_read(u8 byte)
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"));
dbgln_if(KEYBOARD_DEBUG, "Keyboard::handle_byte_read_from_serial_input: {:#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&)
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<PS2KeyboardDevice>> PS2KeyboardDevice::try_to_initialize(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, KeyboardDevice const& keyboard_device)
{
// 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)));
auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) PS2KeyboardDevice(serial_io_controller, port_index, keyboard_device)));
TRY(device->initialize());
return device;
}
UNMAP_AFTER_INIT ErrorOr<void> PS2KeyboardDevice::initialize()
{
return m_i8042_controller->reset_device(PS2Device::Type::Keyboard);
return attached_controller().reset_device(attached_port_index());
}
// 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)
UNMAP_AFTER_INIT PS2KeyboardDevice::PS2KeyboardDevice(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, KeyboardDevice const& keyboard_device)
: SerialIODevice(serial_io_controller, port_index)
, m_keyboard_device(keyboard_device)
{
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <Kernel/API/KeyCode.h>
#include <Kernel/Bus/SerialIO/Device.h>
#include <Kernel/Devices/HID/KeyboardDevice.h>
#include <Kernel/Security/Random.h>
namespace Kernel {
class PS2KeyboardDevice final : public SerialIODevice {
friend class DeviceManagement;
public:
static ErrorOr<NonnullOwnPtr<PS2KeyboardDevice>> try_to_initialize(SerialIOController const&, SerialIOController::PortIndex port_index, KeyboardDevice const&);
virtual ~PS2KeyboardDevice() override;
ErrorOr<void> initialize();
// ^SerialIODevice
virtual void handle_byte_read_from_serial_input(u8 byte) override;
private:
PS2KeyboardDevice(SerialIOController const&, SerialIOController::PortIndex port_index, KeyboardDevice const&);
bool m_has_e0_prefix { false };
NonnullRefPtr<KeyboardDevice> const m_keyboard_device;
};
}

View File

@ -1,39 +1,30 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* 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/Arch/Delay.h>
#include <Kernel/Debug.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/HID/PS2/MouseDevice.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)
UNMAP_AFTER_INIT PS2MouseDevice::PS2MouseDevice(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, MouseDevice const& mouse_device)
: SerialIODevice(serial_io_controller, port_index)
, 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)
void PS2MouseDevice::handle_byte_read_from_serial_input(u8 byte)
{
auto commit_packet = [this]() {
m_data_state = 0;
@ -131,58 +122,48 @@ MousePacket PS2MouseDevice::parse_data_packet(RawPacket const& raw_packet)
ErrorOr<u8> PS2MouseDevice::get_device_id()
{
TRY(send_command(I8042Command::GetDeviceID));
TRY(send_command(SerialIOController::DeviceCommand::GetDeviceID));
return read_from_device();
}
ErrorOr<u8> PS2MouseDevice::read_from_device()
{
return m_i8042_controller->read_from_device(instrument_type());
return attached_controller().read_from_device(attached_port_index());
}
ErrorOr<u8> PS2MouseDevice::send_command(u8 command)
ErrorOr<void> PS2MouseDevice::send_command(SerialIOController::DeviceCommand 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;
TRY(attached_controller().send_command(attached_port_index(), command));
return {};
}
ErrorOr<u8> PS2MouseDevice::send_command(u8 command, u8 data)
ErrorOr<void> PS2MouseDevice::send_command(SerialIOController::DeviceCommand 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;
TRY(attached_controller().send_command(attached_port_index(), command, data));
return {};
}
ErrorOr<void> PS2MouseDevice::set_sample_rate(u8 rate)
{
TRY(send_command(I8042Command::SetSampleRate, rate));
TRY(send_command(SerialIOController::DeviceCommand::SetSampleRate, rate));
return {};
}
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<PS2MouseDevice>> PS2MouseDevice::try_to_initialize(I8042Controller const& ps2_controller, MouseDevice const& mouse_device)
UNMAP_AFTER_INIT ErrorOr<NonnullOwnPtr<PS2MouseDevice>> PS2MouseDevice::try_to_initialize(SerialIOController const& serial_io_controller, SerialIOController::PortIndex port_index, MouseDevice const& mouse_device)
{
auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) PS2MouseDevice(ps2_controller, mouse_device)));
auto device = TRY(adopt_nonnull_own_or_enomem(new (nothrow) PS2MouseDevice(serial_io_controller, port_index, mouse_device)));
TRY(device->initialize());
return device;
}
UNMAP_AFTER_INIT ErrorOr<void> PS2MouseDevice::initialize()
{
TRY(m_i8042_controller->reset_device(instrument_type()));
TRY(attached_controller().reset_device(attached_port_index()));
u8 device_id = TRY(read_from_device());
TRY(send_command(I8042Command::SetDefaults));
TRY(send_command(I8042Command::EnablePacketStreaming));
TRY(send_command(SerialIOController::DeviceCommand::SetDefaults));
TRY(send_command(SerialIOController::DeviceCommand::EnablePacketStreaming));
if (device_id != PS2MOUSE_INTELLIMOUSE_ID) {
// Send magical wheel initiation sequence.

View File

@ -1,43 +1,33 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* 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/Bus/SerialIO/Device.h>
#include <Kernel/Bus/SerialIO/PS2Definitions.h>
#include <Kernel/Devices/HID/MouseDevice.h>
#include <Kernel/Security/Random.h>
namespace Kernel {
class PS2MouseDevice : public IRQHandler
, public PS2Device {
class PS2MouseDevice : public SerialIODevice {
friend class DeviceManagement;
public:
static ErrorOr<NonnullOwnPtr<PS2MouseDevice>> try_to_initialize(I8042Controller const&, MouseDevice const&);
static ErrorOr<NonnullOwnPtr<PS2MouseDevice>> try_to_initialize(SerialIOController const&, SerialIOController::PortIndex, 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; }
// ^SerialIODevice
virtual void handle_byte_read_from_serial_input(u8 byte) override;
protected:
PS2MouseDevice(I8042Controller const&, MouseDevice const&);
// ^IRQHandler
virtual bool handle_irq(RegisterState const&) override;
PS2MouseDevice(SerialIOController const&, SerialIOController::PortIndex, MouseDevice const&);
struct RawPacket {
union {
@ -47,8 +37,8 @@ protected:
};
ErrorOr<u8> read_from_device();
ErrorOr<u8> send_command(u8 command);
ErrorOr<u8> send_command(u8 command, u8 data);
ErrorOr<void> send_command(SerialIOController::DeviceCommand command);
ErrorOr<void> send_command(SerialIOController::DeviceCommand command, u8 data);
MousePacket parse_data_packet(RawPacket const&);
ErrorOr<void> set_sample_rate(u8);
ErrorOr<u8> get_device_id();