1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 20:37:34 +00:00

Kernel: Make i8042 controller initialization sequence more robust

The setting of scan code set sequence is removed, as it's buggy and
could lead the controller to fail immediately when doing self-test
afterwards. We will restore it when we understand how to do so safely.

Allow the user to determine a preferred detection path with a new kernel
command line argument. The defualt option is to check i8042 presence
with an ACPI check and if necessary - an "aggressive" test to determine
i8042 existence in the system.
Also, keep the i8042 controller pointer on the stack, so don't assign
m_i8042_controller member pointer if it does not exist.
This commit is contained in:
Liav A 2022-12-22 03:46:22 +02:00 committed by Jelle Raaijmakers
parent fc5bcd8476
commit 0f7cc468b2
6 changed files with 62 additions and 34 deletions

View file

@ -107,23 +107,12 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
TRY(do_wait_then_write(I8042Port::Command, I8042Command::ReadConfiguration));
configuration = TRY(do_wait_then_read(I8042Port::Buffer));
TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration));
configuration &= ~I8042ConfigurationFlag::FirstPS2PortInterrupt;
configuration &= ~I8042ConfigurationFlag::SecondPS2PortInterrupt;
// Note: The default BIOS on the QEMU microvm machine type (qboot) doesn't
// behave like SeaBIOS, which means it doesn't set first port scan code translation.
// However we rely on compatibility feature of the i8042 to send scan codes of set 1.
// To ensure that the controller is always outputting correct scan codes, set it
// to scan code 2 (because SeaBIOS on regular QEMU machine does this for us) and enable
// first port translation to ensure all scan codes are translated to scan code set 1.
configuration |= I8042ConfigurationFlag::FirstPS2PortTranslation;
TRY(do_wait_then_write(I8042Port::Buffer, configuration));
TRY(do_wait_then_write(I8042Port::Buffer, I8042Command::SetScanCodeSet));
TRY(do_wait_then_write(I8042Port::Buffer, 0x2));
m_is_dual_channel = (configuration & I8042ConfigurationFlag::SecondPS2PortClock) != 0;
dbgln("I8042: {} channel controller", m_is_dual_channel ? "Dual" : "Single");
TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration));
TRY(do_wait_then_write(I8042Port::Buffer, configuration));
// Perform controller self-test
TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestPS2Controller));
@ -134,8 +123,12 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
TRY(do_wait_then_write(I8042Port::Buffer, configuration));
} else {
dbgln("I8042: Controller self test failed");
return Error::from_errno(EIO);
}
m_is_dual_channel = (configuration & I8042ConfigurationFlag::SecondPS2PortClock) != 0;
dbgln("I8042: {} channel controller", m_is_dual_channel ? "Dual" : "Single");
// 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));