diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 9514c2285b..c30aedc45c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -36,6 +36,7 @@ set(KERNEL_SOURCES kernel/Input/PS2/Device.cpp kernel/Input/PS2/Keyboard.cpp kernel/Input/PS2/Keymap.cpp + kernel/Input/PS2/Mouse.cpp kernel/InterruptController.cpp kernel/kernel.cpp kernel/Memory/DMARegion.cpp diff --git a/kernel/include/kernel/Input/MouseEvent.h b/kernel/include/kernel/Input/MouseEvent.h new file mode 100644 index 0000000000..41db5097a5 --- /dev/null +++ b/kernel/include/kernel/Input/MouseEvent.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace Kernel::Input +{ + + enum class MouseButton + { + Left, Right, Middle, Extra1, Extra2 + }; + + struct MouseButtonEvent + { + MouseButton button; + bool pressed; + }; + + struct MouseMoveEvent + { + int32_t rel_x; + int32_t rel_y; + }; + + struct MouseScrollEvent + { + int32_t scroll; + }; + + enum class MouseEventType + { + MouseButtonEvent, + MouseMoveEvent, + MouseScrollEvent, + }; + + struct MouseEvent + { + MouseEventType type; + union + { + MouseButtonEvent button_event; + MouseMoveEvent move_event; + MouseScrollEvent scroll_event; + }; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/Input/PS2/Mouse.h b/kernel/include/kernel/Input/PS2/Mouse.h new file mode 100644 index 0000000000..096b0ffc8d --- /dev/null +++ b/kernel/include/kernel/Input/PS2/Mouse.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +namespace Kernel::Input +{ + + class PS2Mouse final : public PS2Device + { + private: + enum Command : uint8_t + { + SET_SAMPLE_RATE = 0xF3 + }; + + public: + static BAN::ErrorOr create(PS2Controller&); + virtual void send_initialize() override; + + virtual void handle_byte(uint8_t) final override; + virtual void handle_device_command_response(uint8_t) final override; + + private: + PS2Mouse(PS2Controller& controller); + + void initialize_extensions(uint8_t); + + private: + uint8_t m_byte_buffer[10]; + uint8_t m_byte_index { 0 }; + + bool m_enabled { false }; + uint8_t m_mouse_id { 0x00 }; + uint8_t m_button_mask { 0x00 }; + + BAN::CircularQueue m_event_queue; + + Semaphore m_semaphore; + + protected: + virtual BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; + virtual bool has_data_impl() const override; + }; + +} diff --git a/kernel/kernel/Input/PS2/Controller.cpp b/kernel/kernel/Input/PS2/Controller.cpp index e533850f01..779227e587 100644 --- a/kernel/kernel/Input/PS2/Controller.cpp +++ b/kernel/kernel/Input/PS2/Controller.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -213,10 +214,9 @@ namespace Kernel::Input // Standard PS/2 Mouse if (index == 1 && (bytes[0] == 0x00)) - return BAN::Error::from_error_code(ErrorCode::PS2_UnsupportedDevice); - + m_devices[device] = TRY(PS2Mouse::create(*this)); // MF2 Keyboard - if (index == 2 && (bytes[0] == 0xAB && bytes[1] == 0x83)) + else if (index == 2 && (bytes[0] == 0xAB && bytes[1] == 0x83)) m_devices[device] = TRY(PS2Keyboard::create(*this)); if (m_devices[device]) diff --git a/kernel/kernel/Input/PS2/Mouse.cpp b/kernel/kernel/Input/PS2/Mouse.cpp new file mode 100644 index 0000000000..2abbe18e49 --- /dev/null +++ b/kernel/kernel/Input/PS2/Mouse.cpp @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include + +#define SET_MASK(byte, mask, on_off) ((on_off) ? ((byte) | (mask)) : ((byte) & ~(mask))) +#define TOGGLE_MASK(byte, mask) ((byte) ^ (mask)) + +namespace Kernel::Input +{ + + BAN::ErrorOr PS2Mouse::create(PS2Controller& controller) + { + PS2Mouse* mouse = new PS2Mouse(controller); + if (mouse == nullptr) + return BAN::Error::from_errno(ENOMEM); + return mouse; + } + + PS2Mouse::PS2Mouse(PS2Controller& controller) + : PS2Device(controller) + { } + + 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); + } + + void PS2Mouse::initialize_extensions(uint8_t byte) + { + ASSERT(!m_enabled); + + switch (byte) + { + case 0x00: + m_mouse_id = 0x00; + m_enabled = true; + break; + case 0x03: + if (m_mouse_id == 0x03) + m_enabled = true; + 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); + } + break; + case 0x04: + m_mouse_id = 0x04; + m_enabled = true; + break; + default: + dprintln("PS/2 Mouse: unknown id {2H}", byte); + break; + } + + if (m_enabled) + { + append_command_queue(Command::SET_SAMPLE_RATE, 100); + append_command_queue(PS2::DeviceCommand::ENABLE_SCANNING); + } + } + + void PS2Mouse::handle_device_command_response(uint8_t byte) + { + switch (byte) + { + default: + dwarnln("Unhandeled byte {2H}", byte); + break; + } + } + + void PS2Mouse::handle_byte(uint8_t byte) + { + if (!m_enabled) + return initialize_extensions(byte); + + m_byte_buffer[m_byte_index++] = byte; + if (!(m_byte_buffer[0] & 0x08)) + { + dprintln("PS/2 mouse: corrupted package"); + m_byte_index = 0; + } + + int packet_size = (m_mouse_id == 0x00) ? 3 : 4; + if (m_byte_index < packet_size) + return; + + uint8_t new_button_mask = m_byte_buffer[0] & 0b111; + int32_t rel_x = m_byte_buffer[1] - (((uint16_t)m_byte_buffer[0] << 4) & 0x100); + int32_t rel_y = m_byte_buffer[2] - (((uint16_t)m_byte_buffer[0] << 3) & 0x100); + int32_t rel_z = 0; + + if (m_mouse_id == 0x03) + { + rel_z = (int8_t)m_byte_buffer[3]; + } + else if (m_mouse_id == 0x04) + { + new_button_mask |= (m_byte_buffer[3] >> 1) & 0b11000; + + // sign extend z value + if (m_byte_buffer[3] & 0x08) + m_byte_buffer[3] |= 0xF0; + rel_z = (int8_t)m_byte_buffer[3]; + } + + m_byte_index = 0; + + // Max 7 events, one for each (5) button, one for movement, one for scroll + BAN::Array events; + int event_count = 0; + + auto button_index_to_button = + [](int index) -> MouseButton + { + if (index == 0) + return MouseButton::Left; + if (index == 1) + return MouseButton::Right; + if (index == 2) + return MouseButton::Middle; + if (index == 3) + return MouseButton::Extra1; + if (index == 4) + return MouseButton::Extra2; + ASSERT_NOT_REACHED(); + }; + + if (new_button_mask != m_button_mask) + { + for (int i = 0; i < 5; i++) + { + if ((new_button_mask & (1 << i)) == (m_button_mask & (1 << i))) + continue; + + auto& event = events[event_count++]; + event.type = MouseEventType::MouseButtonEvent; + event.button_event.button = button_index_to_button(i); + event.button_event.pressed = !!(new_button_mask & (1 << i)); + } + + m_button_mask = new_button_mask; + } + + if (rel_x || rel_y) + { + auto& event = events[event_count++]; + event.type = MouseEventType::MouseMoveEvent; + event.move_event.rel_x = rel_x; + event.move_event.rel_y = rel_y; + } + + if (rel_z) + { + auto& event = events[event_count++]; + event.type = MouseEventType::MouseScrollEvent; + event.scroll_event.scroll = rel_z; + } + + for (int i = 0; i < event_count; i++) + { + if (m_event_queue.full()) + { + dwarnln("PS/2 event queue full"); + m_event_queue.pop(); + } + m_event_queue.push(events[i]); + } + + m_semaphore.unblock(); + } + + BAN::ErrorOr PS2Mouse::read_impl(off_t, BAN::ByteSpan buffer) + { + if (buffer.size() < sizeof(MouseEvent)) + return BAN::Error::from_errno(ENOBUFS); + + while (true) + { + if (m_event_queue.empty()) + m_semaphore.block(); + + CriticalScope _; + if (m_event_queue.empty()) + continue; + + buffer.as() = m_event_queue.front(); + m_event_queue.pop(); + + return sizeof(MouseEvent); + } + } + + bool PS2Mouse::has_data_impl() const + { + CriticalScope _; + return !m_event_queue.empty(); + } + +}