forked from Bananymous/banan-os
Kernel: Implement PS/2 mouse driver
This is realtively simple driver that queries extensions (scroll + extra buttons) from mouse and reads mouse packages.
This commit is contained in:
parent
d1e187570e
commit
e45b544a39
|
@ -36,6 +36,7 @@ set(KERNEL_SOURCES
|
||||||
kernel/Input/PS2/Device.cpp
|
kernel/Input/PS2/Device.cpp
|
||||||
kernel/Input/PS2/Keyboard.cpp
|
kernel/Input/PS2/Keyboard.cpp
|
||||||
kernel/Input/PS2/Keymap.cpp
|
kernel/Input/PS2/Keymap.cpp
|
||||||
|
kernel/Input/PS2/Mouse.cpp
|
||||||
kernel/InterruptController.cpp
|
kernel/InterruptController.cpp
|
||||||
kernel/kernel.cpp
|
kernel/kernel.cpp
|
||||||
kernel/Memory/DMARegion.cpp
|
kernel/Memory/DMARegion.cpp
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/Input/MouseEvent.h>
|
||||||
|
#include <kernel/Input/PS2/Device.h>
|
||||||
|
#include <kernel/Semaphore.h>
|
||||||
|
|
||||||
|
namespace Kernel::Input
|
||||||
|
{
|
||||||
|
|
||||||
|
class PS2Mouse final : public PS2Device
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum Command : uint8_t
|
||||||
|
{
|
||||||
|
SET_SAMPLE_RATE = 0xF3
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<PS2Mouse*> 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<MouseEvent, 25> m_event_queue;
|
||||||
|
|
||||||
|
Semaphore m_semaphore;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||||
|
virtual bool has_data_impl() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
#include <kernel/Input/PS2/Config.h>
|
#include <kernel/Input/PS2/Config.h>
|
||||||
#include <kernel/Input/PS2/Controller.h>
|
#include <kernel/Input/PS2/Controller.h>
|
||||||
#include <kernel/Input/PS2/Keyboard.h>
|
#include <kernel/Input/PS2/Keyboard.h>
|
||||||
|
#include <kernel/Input/PS2/Mouse.h>
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
#include <kernel/IO.h>
|
#include <kernel/IO.h>
|
||||||
#include <kernel/Timer/Timer.h>
|
#include <kernel/Timer/Timer.h>
|
||||||
|
@ -213,10 +214,9 @@ namespace Kernel::Input
|
||||||
|
|
||||||
// Standard PS/2 Mouse
|
// Standard PS/2 Mouse
|
||||||
if (index == 1 && (bytes[0] == 0x00))
|
if (index == 1 && (bytes[0] == 0x00))
|
||||||
return BAN::Error::from_error_code(ErrorCode::PS2_UnsupportedDevice);
|
m_devices[device] = TRY(PS2Mouse::create(*this));
|
||||||
|
|
||||||
// MF2 Keyboard
|
// 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));
|
m_devices[device] = TRY(PS2Keyboard::create(*this));
|
||||||
|
|
||||||
if (m_devices[device])
|
if (m_devices[device])
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
#include <kernel/CriticalScope.h>
|
||||||
|
#include <kernel/FS/DevFS/FileSystem.h>
|
||||||
|
#include <kernel/Input/PS2/Config.h>
|
||||||
|
#include <kernel/Input/PS2/Mouse.h>
|
||||||
|
|
||||||
|
#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*> 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<MouseEvent, 7> 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<size_t> 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<MouseEvent>() = m_event_queue.front();
|
||||||
|
m_event_queue.pop();
|
||||||
|
|
||||||
|
return sizeof(MouseEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PS2Mouse::has_data_impl() const
|
||||||
|
{
|
||||||
|
CriticalScope _;
|
||||||
|
return !m_event_queue.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue