Kernel: Do a big rewrite of PS/2 code

Command sending+response reading is now synchronized. This allows
bochs to properly initialize both mouse and keyboard simultaneously.

Also status register is checked EVERY time read/write to other IO
ports is performed.
This commit is contained in:
Bananymous 2024-01-07 17:25:06 +02:00
parent 2fec718590
commit d760239748
9 changed files with 264 additions and 124 deletions

View File

@ -11,10 +11,6 @@ namespace Kernel
ACPI_NoRootSDT,
ACPI_NoSuchHeader,
ACPI_RootInvalid,
PS2_Timeout,
PS2_SelfTest,
PS2_Reset,
PS2_UnsupportedDevice,
Ext2_Invalid,
Ext2_Corrupted,
Ext2_NoInodes,

View File

@ -14,8 +14,8 @@ namespace Kernel::Input::PS2
enum Status : uint8_t
{
OUTPUT_FULL = (1 << 0),
INPUT_FULL = (1 << 1),
OUTPUT_STATUS = (1 << 0),
INPUT_STATUS = (1 << 1),
SYSTEM = (1 << 2),
DEVICE_OR_CONTROLLER = (1 << 3),
TIMEOUT_ERROR = (1 << 6),

View File

@ -1,8 +1,10 @@
#pragma once
#include <BAN/CircularQueue.h>
#include <BAN/UniqPtr.h>
#include <kernel/Device/Device.h>
#include <kernel/Input/PS2/Config.h>
#include <kernel/InterruptController.h>
#include <kernel/SpinLock.h>
namespace Kernel::Input
{
@ -15,7 +17,10 @@ namespace Kernel::Input
static BAN::ErrorOr<void> initialize();
static PS2Controller& get();
void send_byte(const PS2Device*, uint8_t);
BAN::ErrorOr<void> device_send_byte(PS2Device* device, uint8_t byte);
[[nodiscard]] bool lock_command(PS2Device*);
void unlock_command(PS2Device*);
private:
PS2Controller() = default;
@ -25,8 +30,21 @@ namespace Kernel::Input
BAN::ErrorOr<void> reset_device(uint8_t);
BAN::ErrorOr<void> set_scanning(uint8_t, bool);
BAN::ErrorOr<uint8_t> read_byte();
BAN::ErrorOr<void> send_byte(uint16_t port, uint8_t byte);
BAN::ErrorOr<void> send_command(PS2::Command command);
BAN::ErrorOr<void> send_command(PS2::Command command, uint8_t data);
BAN::ErrorOr<void> device_send_byte(uint8_t device_index, uint8_t byte);
BAN::ErrorOr<void> device_read_ack(uint8_t device_index);
private:
PS2Device* m_devices[2] { nullptr, nullptr };
BAN::RefPtr<PS2Device> m_devices[2];
RecursiveSpinLock m_lock;
PS2Device* m_executing_device { nullptr };
PS2Device* m_pending_device { nullptr };
};
}

View File

@ -1,6 +1,8 @@
#pragma once
#include <BAN/CircularQueue.h>
#include <kernel/Input/PS2/Controller.h>
#include <kernel/InterruptController.h>
namespace Kernel::Input
{
@ -13,8 +15,8 @@ namespace Kernel::Input
virtual void send_initialize() = 0;
bool append_command_queue(uint8_t command);
bool append_command_queue(uint8_t command, uint8_t data);
bool append_command_queue(uint8_t command, uint8_t response_size);
bool append_command_queue(uint8_t command, uint8_t data, uint8_t response_size);
virtual void handle_irq() final override;
virtual void handle_byte(uint8_t) = 0;
@ -24,13 +26,22 @@ namespace Kernel::Input
virtual dev_t rdev() const final override { return m_rdev; }
private:
void update();
void update_command();
private:
enum class State
{
Normal,
WaitingAck,
WaitingResponse,
};
struct Command
{
uint8_t out_data[2];
uint8_t out_count;
uint8_t in_count;
uint8_t send_index;
};
private:
@ -39,7 +50,9 @@ namespace Kernel::Input
PS2Controller& m_controller;
State m_state = State::Normal;
BAN::CircularQueue<uint8_t, 10> m_command_queue;
BAN::CircularQueue<Command, 10> m_command_queue;
friend class PS2Controller;
};
}

View File

@ -8,10 +8,6 @@ namespace Kernel
"ACPI could not find root SDT header"sv,
"ACPI no such header"sv,
"ACPI root invalid",
"PS/2 device timeout"sv,
"PS/2 controller self test failed"sv,
"PS/2 reset failed"sv,
"PS/2 unsupported device"sv,
"Invalid ext2 filesystem"sv,
"Ext2 filesystem corrupted"sv,
"Ext2 filesystem out of inodes"sv,

View File

@ -10,65 +10,110 @@
#include <kernel/IO.h>
#include <kernel/Timer/Timer.h>
#define DEBUG_PS2 1
namespace Kernel::Input
{
static constexpr uint64_t s_device_timeout_ms = 100;
static void controller_send_command(PS2::Command command)
{
IO::outb(PS2::IOPort::COMMAND, command);
}
static void controller_send_command(PS2::Command command, uint8_t data)
{
IO::outb(PS2::IOPort::COMMAND, command);
while (IO::inb(PS2::IOPort::STATUS) & PS2::Status::INPUT_FULL)
continue;
IO::outb(PS2::IOPort::DATA, data);
}
static uint8_t wait_and_read()
{
while (!(IO::inb(PS2::IOPort::STATUS) & PS2::Status::OUTPUT_FULL))
continue;
return IO::inb(PS2::IOPort::DATA);
}
static BAN::ErrorOr<void> device_send_byte(uint8_t device, uint8_t byte)
{
if (device == 1)
IO::outb(PS2::IOPort::COMMAND, PS2::Command::WRITE_TO_SECOND_PORT);
uint64_t timeout = SystemTimer::get().ms_since_boot() + s_device_timeout_ms;
while (SystemTimer::get().ms_since_boot() < timeout)
{
if (!(IO::inb(PS2::IOPort::STATUS) & PS2::Status::INPUT_FULL))
{
IO::outb(PS2::IOPort::DATA, byte);
return {};
}
}
return BAN::Error::from_error_code(ErrorCode::PS2_Timeout);
}
static BAN::ErrorOr<uint8_t> device_read_byte()
{
uint64_t timeout = SystemTimer::get().ms_since_boot() + s_device_timeout_ms;
while (SystemTimer::get().ms_since_boot() < timeout)
if (IO::inb(PS2::IOPort::STATUS) & PS2::Status::OUTPUT_FULL)
return IO::inb(PS2::IOPort::DATA);
return BAN::Error::from_error_code(ErrorCode::PS2_Timeout);
}
static BAN::ErrorOr<void> device_wait_ack()
{
while (TRY(device_read_byte()) != PS2::Response::ACK)
continue;;
return {};
}
static constexpr uint64_t s_ps2_timeout_ms = 100;
static PS2Controller* s_instance = nullptr;
BAN::ErrorOr<void> PS2Controller::send_byte(uint16_t port, uint8_t byte)
{
LockGuard _(m_lock);
uint64_t timeout = SystemTimer::get().ms_since_boot() + s_ps2_timeout_ms;
while (SystemTimer::get().ms_since_boot() < timeout)
{
if (IO::inb(PS2::IOPort::STATUS) & PS2::Status::INPUT_STATUS)
continue;
IO::outb(port, byte);
return {};
}
return BAN::Error::from_errno(ETIMEDOUT);
}
BAN::ErrorOr<uint8_t> PS2Controller::read_byte()
{
LockGuard _(m_lock);
uint64_t timeout = SystemTimer::get().ms_since_boot() + s_ps2_timeout_ms;
while (SystemTimer::get().ms_since_boot() < timeout)
{
if (!(IO::inb(PS2::IOPort::STATUS) & PS2::Status::OUTPUT_STATUS))
continue;
return IO::inb(PS2::IOPort::DATA);
}
return BAN::Error::from_errno(ETIMEDOUT);
}
BAN::ErrorOr<void> PS2Controller::send_command(PS2::Command command)
{
LockGuard _(m_lock);
TRY(send_byte(PS2::IOPort::COMMAND, command));
return {};
}
BAN::ErrorOr<void> PS2Controller::send_command(PS2::Command command, uint8_t data)
{
LockGuard _(m_lock);
TRY(send_byte(PS2::IOPort::COMMAND, command));
TRY(send_byte(PS2::IOPort::DATA, data));
return {};
}
bool PS2Controller::lock_command(PS2Device* device)
{
if (m_executing_device && m_executing_device != device)
{
ASSERT(!m_pending_device || m_pending_device == device);
m_pending_device = device;
return false;
}
m_executing_device = device;
return true;
}
void PS2Controller::unlock_command(PS2Device* device)
{
ASSERT(m_executing_device == device);
m_executing_device = nullptr;
if (m_pending_device)
{
m_executing_device = m_pending_device;
m_pending_device = nullptr;
m_executing_device->update_command();
}
}
BAN::ErrorOr<void> PS2Controller::device_send_byte(uint8_t device_index, uint8_t byte)
{
LockGuard _(m_lock);
if (device_index == 1)
TRY(send_byte(PS2::IOPort::COMMAND, PS2::Command::WRITE_TO_SECOND_PORT));
TRY(send_byte(PS2::IOPort::DATA, byte));
return {};
}
BAN::ErrorOr<void> PS2Controller::device_send_byte(PS2Device* device, uint8_t byte)
{
ASSERT(device);
ASSERT(device == m_devices[0].ptr() || device == m_devices[1].ptr());
TRY(device_send_byte(device == m_devices[0].ptr() ? 0 : 1, byte));
return {};
}
BAN::ErrorOr<void> PS2Controller::device_read_ack(uint8_t device_index)
{
LockGuard _(m_lock);
if (TRY(read_byte()) != PS2::Response::ACK)
{
dwarnln_if(DEBUG_PS2, "PS/2 device on port {} did not respond with expected ACK", device_index);
return BAN::Error::from_errno(EBADMSG);
}
return {};
}
BAN::ErrorOr<void> PS2Controller::initialize()
{
ASSERT(s_instance == nullptr);
@ -96,48 +141,60 @@ namespace Kernel::Input
// FIXME
// Step 3: Disable Devices
controller_send_command(PS2::Command::DISABLE_FIRST_PORT);
controller_send_command(PS2::Command::DISABLE_SECOND_PORT);
TRY(send_command(PS2::Command::DISABLE_FIRST_PORT));
TRY(send_command(PS2::Command::DISABLE_SECOND_PORT));
// Step 4: Flush The Output Buffer
IO::inb(PS2::IOPort::DATA);
while (!read_byte().is_error())
continue;
// Step 5: Set the Controller Configuration Byte
controller_send_command(PS2::Command::READ_CONFIG);
uint8_t config = wait_and_read();
TRY(send_command(PS2::Command::READ_CONFIG));
uint8_t config = TRY(read_byte());
config &= ~PS2::Config::INTERRUPT_FIRST_PORT;
config &= ~PS2::Config::INTERRUPT_SECOND_PORT;
config &= ~PS2::Config::TRANSLATION_FIRST_PORT;
controller_send_command(PS2::Command::WRITE_CONFIG, config);
TRY(send_command(PS2::Command::WRITE_CONFIG, config));
// Step 6: Perform Controller Self Test
controller_send_command(PS2::Command::TEST_CONTROLLER);
if (wait_and_read() != PS2::Response::TEST_CONTROLLER_PASS)
return BAN::Error::from_error_code(ErrorCode::PS2_SelfTest);
TRY(send_command(PS2::Command::TEST_CONTROLLER));
if (TRY(read_byte()) != PS2::Response::TEST_CONTROLLER_PASS)
{
dwarnln_if(DEBUG_PS2, "PS/2 Controller test failed");
return BAN::Error::from_errno(ENODEV);
}
// NOTE: self test might reset the device so we set the config byte again
controller_send_command(PS2::Command::WRITE_CONFIG, config);
TRY(send_command(PS2::Command::WRITE_CONFIG, config));
// Step 7: Determine If There Are 2 Channels
bool valid_ports[2] { true, false };
if (config & PS2::Config::CLOCK_SECOND_PORT)
{
controller_send_command(PS2::Command::ENABLE_SECOND_PORT);
controller_send_command(PS2::Command::READ_CONFIG);
if (!(wait_and_read() & PS2::Config::CLOCK_SECOND_PORT))
TRY(send_command(PS2::Command::ENABLE_SECOND_PORT));
TRY(send_command(PS2::Command::READ_CONFIG));
if (!(TRY(read_byte()) & PS2::Config::CLOCK_SECOND_PORT))
{
valid_ports[1] = true;
controller_send_command(PS2::Command::DISABLE_SECOND_PORT);
TRY(send_command(PS2::Command::DISABLE_SECOND_PORT));
}
}
// Step 8: Perform Interface Tests
controller_send_command(PS2::Command::TEST_FIRST_PORT);
if (wait_and_read() != PS2::Response::TEST_FIRST_PORT_PASS)
TRY(send_command(PS2::Command::TEST_FIRST_PORT));
if (TRY(read_byte()) != PS2::Response::TEST_FIRST_PORT_PASS)
{
dwarnln_if(DEBUG_PS2, "PS/2 first port test failed");
valid_ports[0] = false;
}
if (valid_ports[1])
{
controller_send_command(PS2::Command::TEST_SECOND_PORT);
if (wait_and_read() != PS2::Response::TEST_SECOND_PORT_PASS)
TRY(send_command(PS2::Command::TEST_SECOND_PORT));
if (TRY(read_byte()) != PS2::Response::TEST_SECOND_PORT_PASS)
{
dwarnln_if(DEBUG_PS2, "PS/2 second port test failed");
valid_ports[1] = false;
}
}
if (!valid_ports[0] && !valid_ports[1])
return {};
@ -146,20 +203,32 @@ namespace Kernel::Input
{
if (!valid_ports[device])
continue;
controller_send_command(device == 0 ? PS2::Command::ENABLE_FIRST_PORT : PS2::Command::ENABLE_SECOND_PORT);
TRY(send_command(device == 0 ? PS2::Command::ENABLE_FIRST_PORT : PS2::Command::ENABLE_SECOND_PORT));
if (set_scanning(device, false).is_error())
{
dwarnln_if(DEBUG_PS2, "PS/2 could not disable device scanning");
valid_ports[device] = false;
}
TRY(send_command(device == 0 ? PS2::Command::DISABLE_FIRST_PORT : PS2::Command::DISABLE_SECOND_PORT));
}
// Step 10: Reset Devices
for (uint8_t device = 0; device < 2; device++)
{
if (!valid_ports[device])
continue;
if (reset_device(device).is_error())
if (auto ret = reset_device(device); ret.is_error())
{
dwarnln_if(DEBUG_PS2, "PS/2 device reset failed: {}", ret.error());
valid_ports[device] = false;
if (set_scanning(device, false).is_error())
continue;
}
if (auto ret = set_scanning(device, false); ret.is_error())
{
dwarnln_if(DEBUG_PS2, "PS/2 device scan disabling failed: {}", ret.error());
valid_ports[device] = false;
continue;
}
}
// Step 11: Initialize Device Drivers
@ -168,7 +237,7 @@ namespace Kernel::Input
if (!valid_ports[device])
continue;
if (auto res = initialize_device(device); res.is_error())
dprintln("{}", res.error());
dwarnln_if(DEBUG_PS2, "PS/2 device initialization failed: {}", res.error());
}
if (m_devices[0])
@ -184,11 +253,11 @@ namespace Kernel::Input
config |= PS2::Config::INTERRUPT_SECOND_PORT;
}
controller_send_command(PS2::Command::WRITE_CONFIG, config);
TRY(send_command(PS2::Command::WRITE_CONFIG, config));
for (uint8_t device = 0; device < 2; device++)
{
if (m_devices[device] == nullptr)
if (!m_devices[device])
continue;
m_devices[device]->send_initialize();
DevFileSystem::get().add_device(m_devices[device]);
@ -200,13 +269,13 @@ namespace Kernel::Input
BAN::ErrorOr<void> PS2Controller::initialize_device(uint8_t device)
{
TRY(device_send_byte(device, PS2::DeviceCommand::IDENTIFY));
TRY(device_wait_ack());
TRY(device_read_ack(device));
uint8_t bytes[2] {};
uint8_t index = 0;
for (uint8_t i = 0; i < 2; i++)
{
auto res = device_read_byte();
auto res = read_byte();
if (res.is_error())
break;
bytes[index++] = res.value();
@ -214,37 +283,43 @@ namespace Kernel::Input
// Standard PS/2 Mouse
if (index == 1 && (bytes[0] == 0x00))
{
dprintln_if(DEBUG_PS2, "PS/2 found mouse");
m_devices[device] = TRY(PS2Mouse::create(*this));
}
// MF2 Keyboard
else if (index == 2 && (bytes[0] == 0xAB && bytes[1] == 0x83))
{
dprintln_if(DEBUG_PS2, "PS/2 found keyboard");
m_devices[device] = TRY(PS2Keyboard::create(*this));
}
if (m_devices[device])
return {};
return BAN::Error::from_error_code(ErrorCode::PS2_UnsupportedDevice);
}
void PS2Controller::send_byte(const PS2Device* device, uint8_t byte)
{
ASSERT(device != nullptr && (device == m_devices[0] || device == m_devices[1]));
uint8_t device_index = (device == m_devices[0]) ? 0 : 1;
MUST(device_send_byte(device_index, byte));
dprintln_if(DEBUG_PS2, "PS/2 unsupported device {2H} {2H} ({} bytes) on port {}", bytes[0], bytes[1], index, device);
return BAN::Error::from_errno(ENOTSUP);
}
BAN::ErrorOr<void> PS2Controller::reset_device(uint8_t device)
{
TRY(device_send_byte(device, PS2::DeviceCommand::RESET));
TRY(device_wait_ack());
if (TRY(device_read_byte()) != PS2::Response::SELF_TEST_PASS)
return BAN::Error::from_error_code(ErrorCode::PS2_Reset);
TRY(device_read_ack(device));
if (TRY(read_byte()) != PS2::Response::SELF_TEST_PASS)
{
dwarnln_if(DEBUG_PS2, "PS/2 device self test failed");
return BAN::Error::from_errno(ENODEV);
}
// device might send extra data
while (!read_byte().is_error())
continue;
return {};
}
BAN::ErrorOr<void> PS2Controller::set_scanning(uint8_t device, bool enabled)
{
TRY(device_send_byte(device, enabled ? PS2::DeviceCommand::ENABLE_SCANNING : PS2::DeviceCommand::DISABLE_SCANNING));
TRY(device_wait_ack());
TRY(device_read_ack(device));
return {};
}

View File

@ -15,28 +15,39 @@ namespace Kernel::Input
, m_controller(controller)
{ }
bool PS2Device::append_command_queue(uint8_t command)
bool PS2Device::append_command_queue(uint8_t command, uint8_t response_size)
{
CriticalScope _;
if (m_command_queue.size() + 1 >= m_command_queue.capacity())
{
dprintln("PS/2 command queue full");
return false;
}
m_command_queue.push(command);
update();
m_command_queue.push(Command {
.out_data = { command, 0x00 },
.out_count = 1,
.in_count = response_size,
.send_index = 0
});
update_command();
return true;
}
bool PS2Device::append_command_queue(uint8_t command, uint8_t data)
bool PS2Device::append_command_queue(uint8_t command, uint8_t data, uint8_t response_size)
{
if (m_command_queue.size() + 2 >= m_command_queue.capacity())
CriticalScope _;
if (m_command_queue.size() + 1 >= m_command_queue.capacity())
{
dprintln("PS/2 command queue full");
return false;
}
m_command_queue.push(command);
m_command_queue.push(data);
update();
m_command_queue.push(Command {
.out_data = { command, data },
.out_count = 2,
.in_count = response_size,
.send_index = 0
});
update_command();
return true;
}
@ -44,8 +55,6 @@ namespace Kernel::Input
{
uint8_t byte = IO::inb(PS2::IOPort::DATA);
// NOTE: This implementation does not allow using commands
// that respond with more bytes than ACK
switch (m_state)
{
case State::WaitingAck:
@ -53,9 +62,20 @@ namespace Kernel::Input
switch (byte)
{
case PS2::Response::ACK:
{
auto& command = m_command_queue.front();
if (++command.send_index < command.out_count)
m_state = State::Normal;
else if (command.in_count > 0)
m_state = State::WaitingResponse;
else
{
m_command_queue.pop();
m_state = State::Normal;
m_controller.unlock_command(this);
}
break;
}
case PS2::Response::RESEND:
m_state = State::Normal;
break;
@ -65,6 +85,17 @@ namespace Kernel::Input
}
break;
}
case State::WaitingResponse:
{
if (--m_command_queue.front().in_count <= 0)
{
m_command_queue.pop();
m_state = State::Normal;
m_controller.unlock_command(this);
}
handle_byte(byte);
break;
}
case State::Normal:
{
handle_byte(byte);
@ -72,17 +103,28 @@ namespace Kernel::Input
}
}
update();
update_command();
}
void PS2Device::update()
void PS2Device::update_command()
{
if (m_state == State::WaitingAck)
ASSERT(!interrupts_enabled());
if (m_state != State::Normal)
return;
if (m_command_queue.empty())
return;
const auto& command = m_command_queue.front();
ASSERT(command.send_index < command.out_count);
if (!m_controller.lock_command(this))
return;
m_state = State::WaitingAck;
m_controller.send_byte(this, m_command_queue.front());
auto ret = m_controller.device_send_byte(this, command.out_data[command.send_index]);
if (ret.is_error())
dwarnln("Could not send byte to device: {}", ret.error());
}
}

View File

@ -25,9 +25,9 @@ namespace Kernel::Input
void PS2Keyboard::send_initialize()
{
append_command_queue(Command::SET_LEDS, 0x00);
append_command_queue(Command::SCANCODE, PS2::KBScancode::SET_SCANCODE_SET2);
append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING);
append_command_queue(Command::SET_LEDS, 0x00, 0);
append_command_queue(Command::SCANCODE, PS2::KBScancode::SET_SCANCODE_SET2, 0);
append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING, 0);
}
void PS2Keyboard::handle_device_command_response(uint8_t byte)
@ -148,7 +148,7 @@ namespace Kernel::Input
new_leds |= PS2::KBLeds::NUM_LOCK;
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::CapsLock)
new_leds |= PS2::KBLeds::CAPS_LOCK;
append_command_queue(Command::SET_LEDS, new_leds);
append_command_queue(Command::SET_LEDS, new_leds, 0);
}
BAN::ErrorOr<size_t> PS2Keyboard::read_impl(off_t, BAN::ByteSpan buffer)

View File

@ -26,10 +26,10 @@ namespace Kernel::Input
void PS2Mouse::send_initialize()
{
// Query extensions
append_command_queue(Command::SET_SAMPLE_RATE, 200);
append_command_queue(Command::SET_SAMPLE_RATE, 100);
append_command_queue(Command::SET_SAMPLE_RATE, 80);
append_command_queue(PS2::DeviceCommand::IDENTIFY);
append_command_queue(Command::SET_SAMPLE_RATE, 200, 0);
append_command_queue(Command::SET_SAMPLE_RATE, 100, 0);
append_command_queue(Command::SET_SAMPLE_RATE, 80, 0);
append_command_queue(PS2::DeviceCommand::IDENTIFY, 1);
}
void PS2Mouse::initialize_extensions(uint8_t byte)
@ -48,10 +48,10 @@ namespace Kernel::Input
else
{
m_mouse_id = 0x03;
append_command_queue(Command::SET_SAMPLE_RATE, 200);
append_command_queue(Command::SET_SAMPLE_RATE, 200);
append_command_queue(Command::SET_SAMPLE_RATE, 80);
append_command_queue(PS2::DeviceCommand::IDENTIFY);
append_command_queue(Command::SET_SAMPLE_RATE, 200, 0);
append_command_queue(Command::SET_SAMPLE_RATE, 200, 0);
append_command_queue(Command::SET_SAMPLE_RATE, 80, 0);
append_command_queue(PS2::DeviceCommand::IDENTIFY, 1);
}
break;
case 0x04:
@ -65,8 +65,8 @@ namespace Kernel::Input
if (m_enabled)
{
append_command_queue(Command::SET_SAMPLE_RATE, 100);
append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING);
append_command_queue(Command::SET_SAMPLE_RATE, 100, 0);
append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING, 0);
}
}