Kernel: Drain I8042 PS/2 keyboard output after enabling

As soon as we enable the first PS/2 port on the I8042 controller, the
output buffer may become full. We need to drain it before attempting
any new commands with the controller (such as enabling the second PS/2
port).

Fixes #10872.
This commit is contained in:
Jelle Raaijmakers 2021-11-12 00:17:30 +01:00 committed by Andreas Kling
parent 5e3fe52fc4
commit 86a1ff5204
Notes: sideshowbarker 2024-07-18 01:15:53 +09:00
2 changed files with 7 additions and 6 deletions

View file

@ -36,13 +36,12 @@ UNMAP_AFTER_INIT void I8042Controller::detect_devices()
u8 configuration;
{
SpinlockLocker lock(m_lock);
// Disable devices
drain_output_buffer();
do_wait_then_write(I8042Port::Command, I8042Command::DisableFirstPS2Port);
do_wait_then_write(I8042Port::Command, I8042Command::DisableSecondPS2Port); // ignored if it doesn't exist
// Drain buffers
do_drain();
do_wait_then_write(I8042Port::Command, I8042Command::ReadConfiguration);
configuration = do_wait_then_read(I8042Port::Buffer);
do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration);
@ -75,6 +74,8 @@ UNMAP_AFTER_INIT void I8042Controller::detect_devices()
dbgln("I8042: Keyboard port not available");
}
drain_output_buffer();
if (m_is_dual_channel) {
do_wait_then_write(I8042Port::Command, I8042Command::TestSecondPS2Port);
m_second_port_available = (do_wait_then_read(I8042Port::Buffer) == 0);
@ -152,7 +153,7 @@ bool I8042Controller::irq_process_input_buffer(HIDDevice::Type instrument_type)
return false;
}
void I8042Controller::do_drain()
void I8042Controller::drain_output_buffer()
{
for (;;) {
u8 status = IO::in8(I8042Port::Status);

View file

@ -135,7 +135,6 @@ public:
private:
I8042Controller();
void do_drain();
bool do_reset_device(HIDDevice::Type);
u8 do_send_command(HIDDevice::Type type, u8 data);
u8 do_send_command(HIDDevice::Type device, u8 command, u8 data);
@ -143,6 +142,7 @@ private:
u8 do_read_from_device(HIDDevice::Type device);
void do_wait_then_write(u8 port, u8 data);
u8 do_wait_then_read(u8 port);
void drain_output_buffer();
Spinlock m_lock;
bool m_first_port_available { false };