forked from Bananymous/banan-os
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:
parent
2fec718590
commit
d760239748
|
@ -11,10 +11,6 @@ namespace Kernel
|
||||||
ACPI_NoRootSDT,
|
ACPI_NoRootSDT,
|
||||||
ACPI_NoSuchHeader,
|
ACPI_NoSuchHeader,
|
||||||
ACPI_RootInvalid,
|
ACPI_RootInvalid,
|
||||||
PS2_Timeout,
|
|
||||||
PS2_SelfTest,
|
|
||||||
PS2_Reset,
|
|
||||||
PS2_UnsupportedDevice,
|
|
||||||
Ext2_Invalid,
|
Ext2_Invalid,
|
||||||
Ext2_Corrupted,
|
Ext2_Corrupted,
|
||||||
Ext2_NoInodes,
|
Ext2_NoInodes,
|
||||||
|
|
|
@ -14,8 +14,8 @@ namespace Kernel::Input::PS2
|
||||||
|
|
||||||
enum Status : uint8_t
|
enum Status : uint8_t
|
||||||
{
|
{
|
||||||
OUTPUT_FULL = (1 << 0),
|
OUTPUT_STATUS = (1 << 0),
|
||||||
INPUT_FULL = (1 << 1),
|
INPUT_STATUS = (1 << 1),
|
||||||
SYSTEM = (1 << 2),
|
SYSTEM = (1 << 2),
|
||||||
DEVICE_OR_CONTROLLER = (1 << 3),
|
DEVICE_OR_CONTROLLER = (1 << 3),
|
||||||
TIMEOUT_ERROR = (1 << 6),
|
TIMEOUT_ERROR = (1 << 6),
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/CircularQueue.h>
|
#include <BAN/UniqPtr.h>
|
||||||
#include <kernel/Device/Device.h>
|
#include <kernel/Device/Device.h>
|
||||||
|
#include <kernel/Input/PS2/Config.h>
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
|
#include <kernel/SpinLock.h>
|
||||||
|
|
||||||
namespace Kernel::Input
|
namespace Kernel::Input
|
||||||
{
|
{
|
||||||
|
@ -15,7 +17,10 @@ namespace Kernel::Input
|
||||||
static BAN::ErrorOr<void> initialize();
|
static BAN::ErrorOr<void> initialize();
|
||||||
static PS2Controller& get();
|
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:
|
private:
|
||||||
PS2Controller() = default;
|
PS2Controller() = default;
|
||||||
|
@ -25,8 +30,21 @@ namespace Kernel::Input
|
||||||
BAN::ErrorOr<void> reset_device(uint8_t);
|
BAN::ErrorOr<void> reset_device(uint8_t);
|
||||||
BAN::ErrorOr<void> set_scanning(uint8_t, bool);
|
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:
|
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 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/CircularQueue.h>
|
||||||
#include <kernel/Input/PS2/Controller.h>
|
#include <kernel/Input/PS2/Controller.h>
|
||||||
|
#include <kernel/InterruptController.h>
|
||||||
|
|
||||||
namespace Kernel::Input
|
namespace Kernel::Input
|
||||||
{
|
{
|
||||||
|
@ -13,8 +15,8 @@ namespace Kernel::Input
|
||||||
|
|
||||||
virtual void send_initialize() = 0;
|
virtual void send_initialize() = 0;
|
||||||
|
|
||||||
bool append_command_queue(uint8_t command);
|
bool append_command_queue(uint8_t command, uint8_t response_size);
|
||||||
bool append_command_queue(uint8_t command, uint8_t data);
|
bool append_command_queue(uint8_t command, uint8_t data, uint8_t response_size);
|
||||||
virtual void handle_irq() final override;
|
virtual void handle_irq() final override;
|
||||||
|
|
||||||
virtual void handle_byte(uint8_t) = 0;
|
virtual void handle_byte(uint8_t) = 0;
|
||||||
|
@ -24,13 +26,22 @@ namespace Kernel::Input
|
||||||
virtual dev_t rdev() const final override { return m_rdev; }
|
virtual dev_t rdev() const final override { return m_rdev; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void update();
|
void update_command();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class State
|
enum class State
|
||||||
{
|
{
|
||||||
Normal,
|
Normal,
|
||||||
WaitingAck,
|
WaitingAck,
|
||||||
|
WaitingResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command
|
||||||
|
{
|
||||||
|
uint8_t out_data[2];
|
||||||
|
uint8_t out_count;
|
||||||
|
uint8_t in_count;
|
||||||
|
uint8_t send_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -39,7 +50,9 @@ namespace Kernel::Input
|
||||||
|
|
||||||
PS2Controller& m_controller;
|
PS2Controller& m_controller;
|
||||||
State m_state = State::Normal;
|
State m_state = State::Normal;
|
||||||
BAN::CircularQueue<uint8_t, 10> m_command_queue;
|
BAN::CircularQueue<Command, 10> m_command_queue;
|
||||||
|
|
||||||
|
friend class PS2Controller;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,6 @@ namespace Kernel
|
||||||
"ACPI could not find root SDT header"sv,
|
"ACPI could not find root SDT header"sv,
|
||||||
"ACPI no such header"sv,
|
"ACPI no such header"sv,
|
||||||
"ACPI root invalid",
|
"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,
|
"Invalid ext2 filesystem"sv,
|
||||||
"Ext2 filesystem corrupted"sv,
|
"Ext2 filesystem corrupted"sv,
|
||||||
"Ext2 filesystem out of inodes"sv,
|
"Ext2 filesystem out of inodes"sv,
|
||||||
|
|
|
@ -10,65 +10,110 @@
|
||||||
#include <kernel/IO.h>
|
#include <kernel/IO.h>
|
||||||
#include <kernel/Timer/Timer.h>
|
#include <kernel/Timer/Timer.h>
|
||||||
|
|
||||||
|
#define DEBUG_PS2 1
|
||||||
|
|
||||||
namespace Kernel::Input
|
namespace Kernel::Input
|
||||||
{
|
{
|
||||||
|
|
||||||
static constexpr uint64_t s_device_timeout_ms = 100;
|
static constexpr uint64_t s_ps2_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 PS2Controller* s_instance = nullptr;
|
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()
|
BAN::ErrorOr<void> PS2Controller::initialize()
|
||||||
{
|
{
|
||||||
ASSERT(s_instance == nullptr);
|
ASSERT(s_instance == nullptr);
|
||||||
|
@ -96,48 +141,60 @@ namespace Kernel::Input
|
||||||
// FIXME
|
// FIXME
|
||||||
|
|
||||||
// Step 3: Disable Devices
|
// Step 3: Disable Devices
|
||||||
controller_send_command(PS2::Command::DISABLE_FIRST_PORT);
|
TRY(send_command(PS2::Command::DISABLE_FIRST_PORT));
|
||||||
controller_send_command(PS2::Command::DISABLE_SECOND_PORT);
|
TRY(send_command(PS2::Command::DISABLE_SECOND_PORT));
|
||||||
|
|
||||||
// Step 4: Flush The Output Buffer
|
// Step 4: Flush The Output Buffer
|
||||||
IO::inb(PS2::IOPort::DATA);
|
while (!read_byte().is_error())
|
||||||
|
continue;
|
||||||
|
|
||||||
// Step 5: Set the Controller Configuration Byte
|
// Step 5: Set the Controller Configuration Byte
|
||||||
controller_send_command(PS2::Command::READ_CONFIG);
|
TRY(send_command(PS2::Command::READ_CONFIG));
|
||||||
uint8_t config = wait_and_read();
|
uint8_t config = TRY(read_byte());
|
||||||
config &= ~PS2::Config::INTERRUPT_FIRST_PORT;
|
config &= ~PS2::Config::INTERRUPT_FIRST_PORT;
|
||||||
config &= ~PS2::Config::INTERRUPT_SECOND_PORT;
|
config &= ~PS2::Config::INTERRUPT_SECOND_PORT;
|
||||||
config &= ~PS2::Config::TRANSLATION_FIRST_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
|
// Step 6: Perform Controller Self Test
|
||||||
controller_send_command(PS2::Command::TEST_CONTROLLER);
|
TRY(send_command(PS2::Command::TEST_CONTROLLER));
|
||||||
if (wait_and_read() != PS2::Response::TEST_CONTROLLER_PASS)
|
if (TRY(read_byte()) != PS2::Response::TEST_CONTROLLER_PASS)
|
||||||
return BAN::Error::from_error_code(ErrorCode::PS2_SelfTest);
|
{
|
||||||
|
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
|
// 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
|
// Step 7: Determine If There Are 2 Channels
|
||||||
bool valid_ports[2] { true, false };
|
bool valid_ports[2] { true, false };
|
||||||
if (config & PS2::Config::CLOCK_SECOND_PORT)
|
if (config & PS2::Config::CLOCK_SECOND_PORT)
|
||||||
{
|
{
|
||||||
controller_send_command(PS2::Command::ENABLE_SECOND_PORT);
|
TRY(send_command(PS2::Command::ENABLE_SECOND_PORT));
|
||||||
controller_send_command(PS2::Command::READ_CONFIG);
|
TRY(send_command(PS2::Command::READ_CONFIG));
|
||||||
if (!(wait_and_read() & PS2::Config::CLOCK_SECOND_PORT))
|
if (!(TRY(read_byte()) & PS2::Config::CLOCK_SECOND_PORT))
|
||||||
|
{
|
||||||
valid_ports[1] = true;
|
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
|
// Step 8: Perform Interface Tests
|
||||||
controller_send_command(PS2::Command::TEST_FIRST_PORT);
|
TRY(send_command(PS2::Command::TEST_FIRST_PORT));
|
||||||
if (wait_and_read() != PS2::Response::TEST_FIRST_PORT_PASS)
|
if (TRY(read_byte()) != PS2::Response::TEST_FIRST_PORT_PASS)
|
||||||
|
{
|
||||||
|
dwarnln_if(DEBUG_PS2, "PS/2 first port test failed");
|
||||||
valid_ports[0] = false;
|
valid_ports[0] = false;
|
||||||
|
}
|
||||||
if (valid_ports[1])
|
if (valid_ports[1])
|
||||||
{
|
{
|
||||||
controller_send_command(PS2::Command::TEST_SECOND_PORT);
|
TRY(send_command(PS2::Command::TEST_SECOND_PORT));
|
||||||
if (wait_and_read() != PS2::Response::TEST_SECOND_PORT_PASS)
|
if (TRY(read_byte()) != PS2::Response::TEST_SECOND_PORT_PASS)
|
||||||
|
{
|
||||||
|
dwarnln_if(DEBUG_PS2, "PS/2 second port test failed");
|
||||||
valid_ports[1] = false;
|
valid_ports[1] = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!valid_ports[0] && !valid_ports[1])
|
if (!valid_ports[0] && !valid_ports[1])
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
@ -146,20 +203,32 @@ namespace Kernel::Input
|
||||||
{
|
{
|
||||||
if (!valid_ports[device])
|
if (!valid_ports[device])
|
||||||
continue;
|
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())
|
if (set_scanning(device, false).is_error())
|
||||||
|
{
|
||||||
|
dwarnln_if(DEBUG_PS2, "PS/2 could not disable device scanning");
|
||||||
valid_ports[device] = false;
|
valid_ports[device] = false;
|
||||||
}
|
}
|
||||||
|
TRY(send_command(device == 0 ? PS2::Command::DISABLE_FIRST_PORT : PS2::Command::DISABLE_SECOND_PORT));
|
||||||
|
}
|
||||||
|
|
||||||
// Step 10: Reset Devices
|
// Step 10: Reset Devices
|
||||||
for (uint8_t device = 0; device < 2; device++)
|
for (uint8_t device = 0; device < 2; device++)
|
||||||
{
|
{
|
||||||
if (!valid_ports[device])
|
if (!valid_ports[device])
|
||||||
continue;
|
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;
|
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;
|
valid_ports[device] = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 11: Initialize Device Drivers
|
// Step 11: Initialize Device Drivers
|
||||||
|
@ -168,7 +237,7 @@ namespace Kernel::Input
|
||||||
if (!valid_ports[device])
|
if (!valid_ports[device])
|
||||||
continue;
|
continue;
|
||||||
if (auto res = initialize_device(device); res.is_error())
|
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])
|
if (m_devices[0])
|
||||||
|
@ -184,11 +253,11 @@ namespace Kernel::Input
|
||||||
config |= PS2::Config::INTERRUPT_SECOND_PORT;
|
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++)
|
for (uint8_t device = 0; device < 2; device++)
|
||||||
{
|
{
|
||||||
if (m_devices[device] == nullptr)
|
if (!m_devices[device])
|
||||||
continue;
|
continue;
|
||||||
m_devices[device]->send_initialize();
|
m_devices[device]->send_initialize();
|
||||||
DevFileSystem::get().add_device(m_devices[device]);
|
DevFileSystem::get().add_device(m_devices[device]);
|
||||||
|
@ -200,13 +269,13 @@ namespace Kernel::Input
|
||||||
BAN::ErrorOr<void> PS2Controller::initialize_device(uint8_t device)
|
BAN::ErrorOr<void> PS2Controller::initialize_device(uint8_t device)
|
||||||
{
|
{
|
||||||
TRY(device_send_byte(device, PS2::DeviceCommand::IDENTIFY));
|
TRY(device_send_byte(device, PS2::DeviceCommand::IDENTIFY));
|
||||||
TRY(device_wait_ack());
|
TRY(device_read_ack(device));
|
||||||
|
|
||||||
uint8_t bytes[2] {};
|
uint8_t bytes[2] {};
|
||||||
uint8_t index = 0;
|
uint8_t index = 0;
|
||||||
for (uint8_t i = 0; i < 2; i++)
|
for (uint8_t i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
auto res = device_read_byte();
|
auto res = read_byte();
|
||||||
if (res.is_error())
|
if (res.is_error())
|
||||||
break;
|
break;
|
||||||
bytes[index++] = res.value();
|
bytes[index++] = res.value();
|
||||||
|
@ -214,37 +283,43 @@ namespace Kernel::Input
|
||||||
|
|
||||||
// Standard PS/2 Mouse
|
// Standard PS/2 Mouse
|
||||||
if (index == 1 && (bytes[0] == 0x00))
|
if (index == 1 && (bytes[0] == 0x00))
|
||||||
|
{
|
||||||
|
dprintln_if(DEBUG_PS2, "PS/2 found mouse");
|
||||||
m_devices[device] = TRY(PS2Mouse::create(*this));
|
m_devices[device] = TRY(PS2Mouse::create(*this));
|
||||||
|
}
|
||||||
// MF2 Keyboard
|
// MF2 Keyboard
|
||||||
else if (index == 2 && (bytes[0] == 0xAB && bytes[1] == 0x83))
|
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));
|
m_devices[device] = TRY(PS2Keyboard::create(*this));
|
||||||
|
}
|
||||||
|
|
||||||
if (m_devices[device])
|
if (m_devices[device])
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return BAN::Error::from_error_code(ErrorCode::PS2_UnsupportedDevice);
|
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);
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> PS2Controller::reset_device(uint8_t device)
|
BAN::ErrorOr<void> PS2Controller::reset_device(uint8_t device)
|
||||||
{
|
{
|
||||||
TRY(device_send_byte(device, PS2::DeviceCommand::RESET));
|
TRY(device_send_byte(device, PS2::DeviceCommand::RESET));
|
||||||
TRY(device_wait_ack());
|
TRY(device_read_ack(device));
|
||||||
if (TRY(device_read_byte()) != PS2::Response::SELF_TEST_PASS)
|
if (TRY(read_byte()) != PS2::Response::SELF_TEST_PASS)
|
||||||
return BAN::Error::from_error_code(ErrorCode::PS2_Reset);
|
{
|
||||||
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> PS2Controller::set_scanning(uint8_t device, bool enabled)
|
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_send_byte(device, enabled ? PS2::DeviceCommand::ENABLE_SCANNING : PS2::DeviceCommand::DISABLE_SCANNING));
|
||||||
TRY(device_wait_ack());
|
TRY(device_read_ack(device));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,28 +15,39 @@ namespace Kernel::Input
|
||||||
, m_controller(controller)
|
, 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())
|
if (m_command_queue.size() + 1 >= m_command_queue.capacity())
|
||||||
{
|
{
|
||||||
dprintln("PS/2 command queue full");
|
dprintln("PS/2 command queue full");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_command_queue.push(command);
|
m_command_queue.push(Command {
|
||||||
update();
|
.out_data = { command, 0x00 },
|
||||||
|
.out_count = 1,
|
||||||
|
.in_count = response_size,
|
||||||
|
.send_index = 0
|
||||||
|
});
|
||||||
|
update_command();
|
||||||
return true;
|
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");
|
dprintln("PS/2 command queue full");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_command_queue.push(command);
|
m_command_queue.push(Command {
|
||||||
m_command_queue.push(data);
|
.out_data = { command, data },
|
||||||
update();
|
.out_count = 2,
|
||||||
|
.in_count = response_size,
|
||||||
|
.send_index = 0
|
||||||
|
});
|
||||||
|
update_command();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,8 +55,6 @@ namespace Kernel::Input
|
||||||
{
|
{
|
||||||
uint8_t byte = IO::inb(PS2::IOPort::DATA);
|
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)
|
switch (m_state)
|
||||||
{
|
{
|
||||||
case State::WaitingAck:
|
case State::WaitingAck:
|
||||||
|
@ -53,9 +62,20 @@ namespace Kernel::Input
|
||||||
switch (byte)
|
switch (byte)
|
||||||
{
|
{
|
||||||
case PS2::Response::ACK:
|
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_command_queue.pop();
|
||||||
m_state = State::Normal;
|
m_state = State::Normal;
|
||||||
|
m_controller.unlock_command(this);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case PS2::Response::RESEND:
|
case PS2::Response::RESEND:
|
||||||
m_state = State::Normal;
|
m_state = State::Normal;
|
||||||
break;
|
break;
|
||||||
|
@ -65,6 +85,17 @@ namespace Kernel::Input
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case State::Normal:
|
||||||
{
|
{
|
||||||
handle_byte(byte);
|
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;
|
return;
|
||||||
if (m_command_queue.empty())
|
if (m_command_queue.empty())
|
||||||
return;
|
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_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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ namespace Kernel::Input
|
||||||
|
|
||||||
void PS2Keyboard::send_initialize()
|
void PS2Keyboard::send_initialize()
|
||||||
{
|
{
|
||||||
append_command_queue(Command::SET_LEDS, 0x00);
|
append_command_queue(Command::SET_LEDS, 0x00, 0);
|
||||||
append_command_queue(Command::SCANCODE, PS2::KBScancode::SET_SCANCODE_SET2);
|
append_command_queue(Command::SCANCODE, PS2::KBScancode::SET_SCANCODE_SET2, 0);
|
||||||
append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING);
|
append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PS2Keyboard::handle_device_command_response(uint8_t byte)
|
void PS2Keyboard::handle_device_command_response(uint8_t byte)
|
||||||
|
@ -148,7 +148,7 @@ namespace Kernel::Input
|
||||||
new_leds |= PS2::KBLeds::NUM_LOCK;
|
new_leds |= PS2::KBLeds::NUM_LOCK;
|
||||||
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::CapsLock)
|
if (m_modifiers & (uint8_t)Input::KeyEvent::Modifier::CapsLock)
|
||||||
new_leds |= PS2::KBLeds::CAPS_LOCK;
|
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)
|
BAN::ErrorOr<size_t> PS2Keyboard::read_impl(off_t, BAN::ByteSpan buffer)
|
||||||
|
|
|
@ -26,10 +26,10 @@ namespace Kernel::Input
|
||||||
void PS2Mouse::send_initialize()
|
void PS2Mouse::send_initialize()
|
||||||
{
|
{
|
||||||
// Query extensions
|
// Query extensions
|
||||||
append_command_queue(Command::SET_SAMPLE_RATE, 200);
|
append_command_queue(Command::SET_SAMPLE_RATE, 200, 0);
|
||||||
append_command_queue(Command::SET_SAMPLE_RATE, 100);
|
append_command_queue(Command::SET_SAMPLE_RATE, 100, 0);
|
||||||
append_command_queue(Command::SET_SAMPLE_RATE, 80);
|
append_command_queue(Command::SET_SAMPLE_RATE, 80, 0);
|
||||||
append_command_queue(PS2::DeviceCommand::IDENTIFY);
|
append_command_queue(PS2::DeviceCommand::IDENTIFY, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PS2Mouse::initialize_extensions(uint8_t byte)
|
void PS2Mouse::initialize_extensions(uint8_t byte)
|
||||||
|
@ -48,10 +48,10 @@ namespace Kernel::Input
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_mouse_id = 0x03;
|
m_mouse_id = 0x03;
|
||||||
append_command_queue(Command::SET_SAMPLE_RATE, 200);
|
append_command_queue(Command::SET_SAMPLE_RATE, 200, 0);
|
||||||
append_command_queue(Command::SET_SAMPLE_RATE, 200);
|
append_command_queue(Command::SET_SAMPLE_RATE, 200, 0);
|
||||||
append_command_queue(Command::SET_SAMPLE_RATE, 80);
|
append_command_queue(Command::SET_SAMPLE_RATE, 80, 0);
|
||||||
append_command_queue(PS2::DeviceCommand::IDENTIFY);
|
append_command_queue(PS2::DeviceCommand::IDENTIFY, 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x04:
|
case 0x04:
|
||||||
|
@ -65,8 +65,8 @@ namespace Kernel::Input
|
||||||
|
|
||||||
if (m_enabled)
|
if (m_enabled)
|
||||||
{
|
{
|
||||||
append_command_queue(Command::SET_SAMPLE_RATE, 100);
|
append_command_queue(Command::SET_SAMPLE_RATE, 100, 0);
|
||||||
append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING);
|
append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue