diff --git a/Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.h b/Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.h deleted file mode 100644 index 250e7fd48b..0000000000 --- a/Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class PS2KeyboardDevice final : public IRQHandler - , public PS2Device { - friend class DeviceManagement; - -public: - static ErrorOr> try_to_initialize(I8042Controller const&, KeyboardDevice const&); - virtual ~PS2KeyboardDevice() override; - ErrorOr 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 const m_keyboard_device; -}; - -} diff --git a/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp b/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp index 7bc88266c0..3915a69b35 100644 --- a/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp +++ b/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp @@ -11,19 +11,19 @@ namespace Kernel { -UNMAP_AFTER_INIT ErrorOr> VMWareMouseDevice::try_to_initialize(I8042Controller const& ps2_controller, MouseDevice const& mouse_device) +UNMAP_AFTER_INIT ErrorOr> 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; diff --git a/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.h b/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.h index 65059d33e8..6c9d8b19d5 100644 --- a/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.h +++ b/Kernel/Arch/x86_64/ISABus/HID/VMWareMouseDevice.h @@ -8,9 +8,8 @@ #include #include -#include #include -#include +#include #include namespace Kernel { @@ -18,14 +17,14 @@ namespace Kernel { class VMWareMouseDevice final : public PS2MouseDevice { public: friend class DeviceManagement; - static ErrorOr> try_to_initialize(I8042Controller const&, MouseDevice const&); + static ErrorOr> 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&); }; } diff --git a/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp b/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp index 7c9c65721b..a675e33f06 100644 --- a/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp +++ b/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp @@ -6,17 +6,37 @@ #include #include -#include -#include #include #include +#include #include #include +#include +#include #include #include namespace Kernel { +#define IRQ_FIRST_PORT 1 +#define IRQ_SECOND_PORT 12 + +UNMAP_AFTER_INIT ErrorOr> 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> 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, 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) { { @@ -50,7 +82,7 @@ UNMAP_AFTER_INIT bool I8042Controller::check_existence_via_probing(Badge I8042Controller::detect_devices() { - u8 configuration; { SpinlockLocker lock(m_lock); @@ -100,7 +131,7 @@ UNMAP_AFTER_INIT ErrorOr 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 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 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 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 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 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 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 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 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 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 I8042Controller::drain_output_buffer() return Error::from_errno(EBUSY); } -ErrorOr I8042Controller::do_reset_device(PS2Device::Type device) +ErrorOr 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 I8042Controller::do_reset_device(PS2Device::Type device) return {}; } -ErrorOr I8042Controller::do_send_command(PS2Device::Type device, u8 command) +ErrorOr 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 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 I8042Controller::do_send_command(PS2Device::Type device, u8 command, u8 data) +ErrorOr 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 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 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 I8042Controller::do_read_from_device(PS2Device::Type device) +ErrorOr 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 I8042Controller::prepare_for_input(PS2Device::Type device) +ErrorOr 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 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 I8042Controller::prepare_for_input(PS2Device::Type device) return Error::from_errno(EBUSY); } -ErrorOr I8042Controller::prepare_for_output() +ErrorOr 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 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 I8042Controller::do_wait_then_read(u8 port) +ErrorOr 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); } - } diff --git a/Kernel/Arch/x86_64/ISABus/I8042Controller.h b/Kernel/Arch/x86_64/ISABus/I8042Controller.h index 0837defb3b..833c6e978f 100644 --- a/Kernel/Arch/x86_64/ISABus/I8042Controller.h +++ b/Kernel/Arch/x86_64/ISABus/I8042Controller.h @@ -8,9 +8,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include 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> 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 const m_i8042_controller; + NonnullRefPtr const m_controller; }; -class PS2KeyboardDevice; -class PS2MouseDevice; class HIDManagement; class I8042Controller final : public SerialIOController { friend class PS2KeyboardDevice; @@ -105,59 +95,47 @@ public: static ErrorOr> create(); ErrorOr detect_devices(); + virtual ErrorOr send_command(PortIndex, DeviceCommand command) override; + virtual ErrorOr send_command(PortIndex, DeviceCommand command, u8 data) override; - ErrorOr reset_device(PS2Device::Type device) + virtual ErrorOr reset_device(PortIndex port_index) override { SpinlockLocker lock(m_lock); - return do_reset_device(device); + return do_reset_device(port_index); } - - ErrorOr send_command(PS2Device::Type device, u8 command) + virtual ErrorOr 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 send_command(PS2Device::Type device, u8 command, u8 data) - { - SpinlockLocker lock(m_lock); - return do_send_command(device, command, data); - } - - ErrorOr read_from_device(PS2Device::Type device) - { - SpinlockLocker lock(m_lock); - return do_read_from_device(device); - } - - ErrorOr wait_then_write(u8 port, u8 data) - { - SpinlockLocker lock(m_lock); - return do_wait_then_write(port, data); - } - - ErrorOr wait_then_read(u8 port) - { - SpinlockLocker lock(m_lock); - return do_wait_then_read(port); - } - - ErrorOr prepare_for_output(); - ErrorOr prepare_for_input(PS2Device::Type); - - bool irq_process_input_buffer(PS2Device::Type); + virtual ErrorOr prepare_for_input(PortIndex) override; // Note: This function exists only for the initialization process of the controller bool check_existence_via_probing(Badge); + bool handle_irq(Badge, u8 irq_number); + private: I8042Controller(); - ErrorOr do_reset_device(PS2Device::Type); - ErrorOr do_send_command(PS2Device::Type type, u8 data); - ErrorOr do_send_command(PS2Device::Type device, u8 command, u8 data); - ErrorOr do_write_to_device(PS2Device::Type device, u8 data); - ErrorOr do_read_from_device(PS2Device::Type device); + + bool irq_process_input_buffer(PortIndex); + + ErrorOr prepare_for_any_output(); + + ErrorOr prepare_for_any_input(); + + ErrorOr do_reset_device(PortIndex); + ErrorOr do_send_command(PortIndex port_index, u8 data); + ErrorOr do_send_command(PortIndex port_index, u8 command, u8 data); + ErrorOr do_write_to_device(PortIndex port_index, u8 data); + ErrorOr do_read_from_device(PortIndex port_index); ErrorOr do_wait_then_write(u8 port, u8 data); - ErrorOr 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 do_wait_then_read_any_input(u8 port); + ErrorOr 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 device; + OwnPtr 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 device_type; + Optional 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, 2> m_irq_handlers; }; } diff --git a/Kernel/Bus/SerialIO/Controller.h b/Kernel/Bus/SerialIO/Controller.h index 8c357e4337..6dee6430f1 100644 --- a/Kernel/Bus/SerialIO/Controller.h +++ b/Kernel/Bus/SerialIO/Controller.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -20,8 +21,25 @@ class SerialIOController : public AtomicRefCounted { 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 reset_device(PortIndex) = 0; + virtual ErrorOr send_command(PortIndex, DeviceCommand command) = 0; + virtual ErrorOr send_command(PortIndex, DeviceCommand command, u8 data) = 0; + + virtual ErrorOr read_from_device(PortIndex) = 0; + virtual ErrorOr prepare_for_input(PortIndex) = 0; + protected: SerialIOController() = default; diff --git a/Kernel/Bus/SerialIO/Device.h b/Kernel/Bus/SerialIO/Device.h new file mode 100644 index 0000000000..327c49d5a4 --- /dev/null +++ b/Kernel/Bus/SerialIO/Device.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +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 const m_serial_io_controller; + SerialIOController::PortIndex m_attached_port_index { 0 }; +}; + +} diff --git a/Kernel/Bus/SerialIO/PS2Definitions.h b/Kernel/Bus/SerialIO/PS2Definitions.h new file mode 100644 index 0000000000..0435c8c4d4 --- /dev/null +++ b/Kernel/Bus/SerialIO/PS2Definitions.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +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, +}; + +} diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 1b79a362f8..1aceb78b85 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -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 diff --git a/Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.cpp b/Kernel/Devices/HID/PS2/KeyboardDevice.cpp similarity index 58% rename from Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.cpp rename to Kernel/Devices/HID/PS2/KeyboardDevice.cpp index 0bc9cbfdc4..b62e612071 100644 --- a/Kernel/Arch/x86_64/ISABus/HID/PS2KeyboardDevice.cpp +++ b/Kernel/Devices/HID/PS2/KeyboardDevice.cpp @@ -1,15 +1,16 @@ /* * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2021, Liav A. + * Copyright (c) 2021-2023, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ #include -#include +#include #include #include #include +#include #include #include #include @@ -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> 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> 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 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) { } diff --git a/Kernel/Devices/HID/PS2/KeyboardDevice.h b/Kernel/Devices/HID/PS2/KeyboardDevice.h new file mode 100644 index 0000000000..66387b4792 --- /dev/null +++ b/Kernel/Devices/HID/PS2/KeyboardDevice.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { + +class PS2KeyboardDevice final : public SerialIODevice { + friend class DeviceManagement; + +public: + static ErrorOr> try_to_initialize(SerialIOController const&, SerialIOController::PortIndex port_index, KeyboardDevice const&); + virtual ~PS2KeyboardDevice() override; + ErrorOr 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 const m_keyboard_device; +}; + +} diff --git a/Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.cpp b/Kernel/Devices/HID/PS2/MouseDevice.cpp similarity index 70% rename from Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.cpp rename to Kernel/Devices/HID/PS2/MouseDevice.cpp index a646dfda36..fd5c837642 100644 --- a/Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.cpp +++ b/Kernel/Devices/HID/PS2/MouseDevice.cpp @@ -1,39 +1,30 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2023, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include +#include #include #include +#include #include 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 PS2MouseDevice::get_device_id() { - TRY(send_command(I8042Command::GetDeviceID)); + TRY(send_command(SerialIOController::DeviceCommand::GetDeviceID)); return read_from_device(); } ErrorOr PS2MouseDevice::read_from_device() { - return m_i8042_controller->read_from_device(instrument_type()); + return attached_controller().read_from_device(attached_port_index()); } -ErrorOr PS2MouseDevice::send_command(u8 command) +ErrorOr 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(I8042Response::Acknowledge)); - return Error::from_errno(EIO); - } - return response; + TRY(attached_controller().send_command(attached_port_index(), command)); + return {}; } -ErrorOr PS2MouseDevice::send_command(u8 command, u8 data) +ErrorOr 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(I8042Response::Acknowledge)); - return Error::from_errno(EIO); - } - return response; + TRY(attached_controller().send_command(attached_port_index(), command, data)); + return {}; } ErrorOr PS2MouseDevice::set_sample_rate(u8 rate) { - TRY(send_command(I8042Command::SetSampleRate, rate)); + TRY(send_command(SerialIOController::DeviceCommand::SetSampleRate, rate)); return {}; } -UNMAP_AFTER_INIT ErrorOr> PS2MouseDevice::try_to_initialize(I8042Controller const& ps2_controller, MouseDevice const& mouse_device) +UNMAP_AFTER_INIT ErrorOr> 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 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. diff --git a/Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.h b/Kernel/Devices/HID/PS2/MouseDevice.h similarity index 52% rename from Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.h rename to Kernel/Devices/HID/PS2/MouseDevice.h index d3325dbf1a..5311667658 100644 --- a/Kernel/Arch/x86_64/ISABus/HID/PS2MouseDevice.h +++ b/Kernel/Devices/HID/PS2/MouseDevice.h @@ -1,43 +1,33 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2023, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include #include -#include -#include +#include +#include +#include #include namespace Kernel { -class PS2MouseDevice : public IRQHandler - , public PS2Device { +class PS2MouseDevice : public SerialIODevice { friend class DeviceManagement; public: - static ErrorOr> try_to_initialize(I8042Controller const&, MouseDevice const&); + static ErrorOr> try_to_initialize(SerialIOController const&, SerialIOController::PortIndex, MouseDevice const&); ErrorOr 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 read_from_device(); - ErrorOr send_command(u8 command); - ErrorOr send_command(u8 command, u8 data); + ErrorOr send_command(SerialIOController::DeviceCommand command); + ErrorOr send_command(SerialIOController::DeviceCommand command, u8 data); MousePacket parse_data_packet(RawPacket const&); ErrorOr set_sample_rate(u8); ErrorOr get_device_id();