Compare commits

...

7 Commits

Author SHA1 Message Date
Bananymous a5cb4057f9 Kernel: Implement unified input files for device hot-plugging support
/dev/keyboard and /dev/mouse can be read for events from any attached
keyboard or mouse respectively. This makes device hot-plugging support
pretty much automatic for TTY, GUI, and whatever takes input.
2024-07-15 22:11:15 +03:00
Bananymous 9d7f97ccd5 Kernel: Implement DevFileSystem::remove_device
This function cleanly removes the devices from the whole filesystem.

USB devices are now removed from the filesystem as soon as they are
destroyed.
2024-07-15 22:10:42 +03:00
Bananymous 0578d41500 Kernel: Implement WriteCombining memory
This makes framebuffer much faster on real hardware
2024-07-15 22:09:09 +03:00
Bananymous 42c3fa24f0 Kernel: Add support for HID Report ID and parsing all collections
Only the first top-level collection is used for the device, but that
seems to generally be what keyboard and mouse use for input.
2024-07-15 15:51:07 +03:00
Bananymous 60b396fee5 Kernel: Add mouse move and scroll event merging back
This makes mouse work much faster when reading can't keep up with the
amount of events.
2024-07-15 15:45:20 +03:00
Bananymous 4cd9252ff6 Kernel: Fix USB code
Fix USB Keyboard scan code table for bottom row of keyboard

Support multiple interfaces on a single USB device

Add usb mouse to default qemu settings
2024-07-15 11:48:48 +03:00
Bananymous 75875d3a8f Kernel: Set interval and average trb length on configure endpoint
Real controllers seem to require this while spec 4.8.2.4 says that they
should be left as zero.
2024-07-15 11:46:28 +03:00
25 changed files with 501 additions and 116 deletions

View File

@ -20,6 +20,7 @@ namespace Kernel
static PageTable* s_kernel = nullptr;
static bool s_has_nxe = false;
static bool s_has_pge = false;
static bool s_has_pat = false;
static paddr_t s_global_pdpte = 0;
@ -32,8 +33,6 @@ namespace Kernel
result |= Flags::Execute;
if (entry & Flags::Reserved)
result |= Flags::Reserved;
if (entry & Flags::CacheDisable)
result |= Flags::CacheDisable;
if (entry & Flags::UserSupervisor)
result |= Flags::UserSupervisor;
if (entry & Flags::ReadWrite)
@ -51,6 +50,9 @@ namespace Kernel
if (CPUID::has_pge())
s_has_pge = true;
if (CPUID::has_pat())
s_has_pat = true;
ASSERT(s_kernel == nullptr);
s_kernel = new PageTable();
ASSERT(s_kernel);
@ -82,6 +84,17 @@ namespace Kernel
);
}
if (s_has_pat)
{
asm volatile(
"movl $0x277, %%ecx;"
"rdmsr;"
"movw $0x0401, %%dx;"
"wrmsr;"
::: "eax", "ecx", "edx", "memory"
);
}
// enable write protect
asm volatile(
"movl %%cr0, %%eax;"
@ -316,7 +329,7 @@ namespace Kernel
unmap_page(page * PAGE_SIZE);
}
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags)
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type)
{
ASSERT(vaddr);
ASSERT(vaddr != fast_page());
@ -338,8 +351,14 @@ namespace Kernel
extra_flags |= 1ull << 63;
if (flags & Flags::Reserved)
extra_flags |= Flags::Reserved;
if (flags & Flags::CacheDisable)
extra_flags |= Flags::CacheDisable;
if (s_has_pat)
{
if (memory_type == MemoryType::WriteCombining)
extra_flags |= (1ull << 7);
if (memory_type == MemoryType::WriteThrough)
extra_flags |= (1ull << 7) | (1ull << 3);
}
// NOTE: we add present here, since it has to be available in higher level structures
flags_t uwr_flags = (flags & (Flags::UserSupervisor | Flags::ReadWrite)) | Flags::Present;
@ -367,7 +386,7 @@ namespace Kernel
invalidate(vaddr);
}
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags)
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
{
ASSERT(vaddr);
ASSERT(paddr % PAGE_SIZE == 0);
@ -377,7 +396,7 @@ namespace Kernel
SpinLockGuard _(m_lock);
for (size_t page = 0; page < page_count; page++)
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags);
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags, memory_type);
}
uint64_t PageTable::get_page_data(vaddr_t vaddr) const

View File

@ -56,8 +56,6 @@ namespace Kernel
result |= Flags::Execute;
if (entry & Flags::Reserved)
result |= Flags::Reserved;
if (entry & Flags::CacheDisable)
result |= Flags::CacheDisable;
if (entry & Flags::UserSupervisor)
result |= Flags::UserSupervisor;
if (entry & Flags::ReadWrite)
@ -106,6 +104,15 @@ namespace Kernel
);
}
// 64-bit always has PAT, set PAT4 = WC, PAT5 = WT
asm volatile(
"movl $0x277, %%ecx;"
"rdmsr;"
"movw $0x0401, %%dx;"
"wrmsr;"
::: "eax", "ecx", "edx", "memory"
);
// enable write protect
asm volatile(
"movq %%cr0, %%rax;"
@ -367,7 +374,7 @@ namespace Kernel
unmap_page(page * PAGE_SIZE);
}
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags)
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type)
{
ASSERT(vaddr);
ASSERT(vaddr != fast_page());
@ -393,8 +400,11 @@ namespace Kernel
extra_flags |= 1ull << 63;
if (flags & Flags::Reserved)
extra_flags |= Flags::Reserved;
if (flags & Flags::CacheDisable)
extra_flags |= Flags::CacheDisable;
if (memory_type == MemoryType::WriteCombining)
extra_flags |= (1ull << 7);
if (memory_type == MemoryType::WriteThrough)
extra_flags |= (1ull << 7) | (1ull << 3);
// NOTE: we add present here, since it has to be available in higher level structures
flags_t uwr_flags = (flags & (Flags::UserSupervisor | Flags::ReadWrite)) | Flags::Present;
@ -434,7 +444,7 @@ namespace Kernel
invalidate(vaddr);
}
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags)
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
{
ASSERT(is_canonical(vaddr));
@ -446,7 +456,7 @@ namespace Kernel
SpinLockGuard _(m_lock);
for (size_t page = 0; page < page_count; page++)
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags);
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags, memory_type);
}
uint64_t PageTable::get_page_data(vaddr_t vaddr) const

View File

@ -79,5 +79,6 @@ namespace CPUID
bool is_64_bit();
bool has_nxe();
bool has_pge();
bool has_pat();
}

View File

@ -18,6 +18,8 @@ namespace Kernel
void initialize_device_updater();
void add_device(BAN::RefPtr<Device>);
void remove_device(BAN::RefPtr<Device>);
void add_inode(BAN::StringView path, BAN::RefPtr<TmpInode>);
void initiate_sync(bool should_block);

View File

@ -7,7 +7,7 @@
namespace Kernel
{
class InputDevice : public CharacterDevice
class InputDevice : public CharacterDevice, public BAN::Weakable<InputDevice>
{
public:
enum class Type
@ -19,6 +19,9 @@ namespace Kernel
public:
InputDevice(Type type);
BAN::StringView name() const final override { return m_name; }
dev_t rdev() const final override { return m_rdev; }
protected:
void add_event(BAN::ConstByteSpan);
@ -28,13 +31,16 @@ namespace Kernel
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
virtual BAN::StringView name() const final override { return m_name; }
virtual dev_t rdev() const final override { return m_rdev; }
private:
BAN::ErrorOr<size_t> read_non_block(BAN::ByteSpan);
private:
const dev_t m_rdev;
const BAN::String m_name;
const Type m_type;
mutable SpinLock m_event_lock;
Semaphore m_event_semaphore;
@ -45,6 +51,63 @@ namespace Kernel
size_t m_event_tail { 0 };
size_t m_event_head { 0 };
size_t m_event_count { 0 };
friend class KeyboardDevice;
friend class MouseDevice;
};
class KeyboardDevice : public CharacterDevice
{
public:
static BAN::ErrorOr<BAN::RefPtr<KeyboardDevice>> create(mode_t mode, uid_t uid, gid_t gid);
void notify() { m_semaphore.unblock(); }
private:
KeyboardDevice(mode_t mode, uid_t uid, gid_t gid);
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
bool can_read_impl() const override;
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
BAN::StringView name() const final override { return m_name; }
dev_t rdev() const final override { return m_rdev; }
private:
const dev_t m_rdev;
const BAN::StringView m_name;
Semaphore m_semaphore;
friend class BAN::RefPtr<KeyboardDevice>;
};
class MouseDevice : public CharacterDevice
{
public:
static BAN::ErrorOr<BAN::RefPtr<MouseDevice>> create(mode_t mode, uid_t uid, gid_t gid);
void notify() { m_semaphore.unblock(); }
private:
MouseDevice(mode_t mode, uid_t uid, gid_t gid);
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
bool can_read_impl() const override;
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
BAN::StringView name() const final override { return m_name; }
dev_t rdev() const final override { return m_rdev; }
private:
const dev_t m_rdev;
const BAN::StringView m_name;
Semaphore m_semaphore;
friend class BAN::RefPtr<MouseDevice>;
};
}

View File

@ -29,12 +29,17 @@ namespace Kernel
Present = (1 << 0),
ReadWrite = (1 << 1),
UserSupervisor = (1 << 2),
CacheDisable = (1 << 4),
Reserved = (1 << 9),
Execute = (1 << 15),
Used = Present | Reserved,
};
enum MemoryType
{
Normal,
WriteCombining,
WriteThrough,
};
public:
static void initialize();
@ -93,8 +98,8 @@ namespace Kernel
void unmap_page(vaddr_t);
void unmap_range(vaddr_t, size_t bytes);
void map_range_at(paddr_t, vaddr_t, size_t bytes, flags_t);
void map_page_at(paddr_t, vaddr_t, flags_t);
void map_range_at(paddr_t, vaddr_t, size_t bytes, flags_t, MemoryType = MemoryType::Normal);
void map_page_at(paddr_t, vaddr_t, flags_t, MemoryType = MemoryType::Normal);
paddr_t physical_address_of(vaddr_t) const;
flags_t get_page_flags(vaddr_t) const;

View File

@ -10,13 +10,20 @@ namespace Kernel
{
enum class SpeedClass
{
None,
LowSpeed,
FullSpeed,
HighSpeed,
SuperSpeed,
};
enum class EndpointType
{
Control = 0b00,
Isochronous = 0b01,
Bulk = 0b10,
Interrupt = 0b11,
};
enum class DeviceBaseClass : uint8_t
{
CommunicationAndCDCControl = 0x02,

View File

@ -53,7 +53,9 @@ namespace Kernel
};
public:
USBDevice() = default;
USBDevice(USB::SpeedClass speed_class)
: m_speed_class(speed_class)
{}
virtual ~USBDevice() = default;
BAN::ErrorOr<void> initialize();
@ -72,12 +74,14 @@ namespace Kernel
private:
BAN::ErrorOr<ConfigurationDescriptor> parse_configuration(size_t index);
protected:
const USB::SpeedClass m_speed_class;
private:
DeviceDescriptor m_descriptor;
BAN::UniqPtr<DMARegion> m_dma_buffer;
// FIXME: support more than one interface from a configuration
BAN::UniqPtr<USBClassDriver> m_class_driver;
BAN::Vector<BAN::UniqPtr<USBClassDriver>> m_class_drivers;
};
}

View File

@ -17,6 +17,7 @@ namespace Kernel
uint16_t usage_id;
Type type;
uint8_t report_id;
uint32_t report_count;
uint32_t report_size;
@ -77,15 +78,17 @@ namespace Kernel
BAN::ErrorOr<void> initialize();
void forward_collection_inputs(const USBHID::Collection&, BAN::ConstByteSpan& data, size_t bit_offset);
void forward_collection_inputs(const USBHID::Collection&, BAN::Optional<uint8_t> report_id, BAN::ConstByteSpan& data, size_t bit_offset);
private:
USBDevice& m_device;
USBDevice::InterfaceDescriptor m_interface;
const uint8_t m_interface_index;
bool m_uses_report_id { false };
uint8_t m_endpoint_id { 0 };
USBHID::Collection m_collection;
BAN::Vector<USBHID::Collection> m_collections;
BAN::RefPtr<USBHIDDevice> m_hid_device;
friend class BAN::UniqPtr<USBHIDDriver>;

View File

@ -43,11 +43,7 @@ namespace Kernel
BAN::ErrorOr<void> initialize_control_endpoint() override;
private:
XHCIDevice(XHCIController& controller, uint32_t port_id, uint32_t slot_id)
: m_controller(controller)
, m_port_id(port_id)
, m_slot_id(slot_id)
{}
XHCIDevice(XHCIController& controller, uint32_t port_id, uint32_t slot_id);
~XHCIDevice();
BAN::ErrorOr<void> update_actual_max_packet_size();
@ -55,6 +51,8 @@ namespace Kernel
void advance_endpoint_enqueue(Endpoint&, bool chain);
static uint64_t calculate_port_bits_per_second(XHCIController&, uint32_t port_id);
private:
static constexpr uint32_t m_transfer_ring_trb_count = PAGE_SIZE / sizeof(XHCI::TRB);

View File

@ -57,6 +57,13 @@ namespace CPUID
return edx & CPUID::EDX_PGE;
}
bool has_pat()
{
uint32_t ecx, edx;
get_features(ecx, edx);
return edx & CPUID::EDX_PAT;
}
const char* feature_string_ecx(uint32_t feat)
{
switch (feat)

View File

@ -68,7 +68,8 @@ namespace Kernel
m_video_memory_paddr & PAGE_ADDR_MASK,
m_video_memory_vaddr,
video_memory_pages * PAGE_SIZE,
PageTable::Flags::ReadWrite | PageTable::Flags::Present
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
PageTable::WriteCombining
);
m_video_buffer = TRY(VirtualRange::create_to_vaddr_range(

View File

@ -5,6 +5,7 @@
#include <kernel/Device/ZeroDevice.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/FS/TmpFS/Inode.h>
#include <kernel/Input/InputDevice.h>
#include <kernel/Lock/LockGuard.h>
#include <kernel/Process.h>
#include <kernel/Scheduler.h>
@ -26,6 +27,8 @@ namespace Kernel
s_instance->add_device(MUST(DebugDevice::create(0666, 0, 0)));
s_instance->add_device(MUST(NullDevice::create(0666, 0, 0)));
s_instance->add_device(MUST(ZeroDevice::create(0666, 0, 0)));
s_instance->add_device(MUST(KeyboardDevice::create(0440, 0, 901)));
s_instance->add_device(MUST(MouseDevice::create(0440, 0, 901)));
}
DevFileSystem& DevFileSystem::get()
@ -112,6 +115,21 @@ namespace Kernel
MUST(m_devices.push_back(device));
}
void DevFileSystem::remove_device(BAN::RefPtr<Device> device)
{
LockGuard _(m_device_lock);
ASSERT(!device->name().contains('/'));
MUST(static_cast<TmpDirectoryInode*>(root_inode().ptr())->unlink(device->name()));
for (size_t i = 0; i < m_devices.size(); i++)
{
if (m_devices[i] == device)
{
m_devices.remove(i);
break;
}
}
}
void DevFileSystem::add_inode(BAN::StringView path, BAN::RefPtr<TmpInode> inode)
{
ASSERT(!inode->is_device());

View File

@ -1,4 +1,5 @@
#include <kernel/Device/DeviceNumbers.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Input/InputDevice.h>
#include <kernel/Lock/LockGuard.h>
@ -10,8 +11,11 @@
namespace Kernel
{
static BAN::Atomic<uint16_t> s_next_keyboard { 0 };
static BAN::Atomic<uint16_t> s_next_mouse { 0 };
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_keyboards;
static BAN::RefPtr<KeyboardDevice> s_keyboard_device;
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_mice;
static BAN::RefPtr<MouseDevice> s_mouse_device;
static const char* get_name_format(InputDevice::Type type)
{
@ -30,9 +34,15 @@ namespace Kernel
switch (type)
{
case InputDevice::Type::Keyboard:
return makedev(DeviceNumber::Keyboard, s_next_keyboard++);
for (size_t i = 0; i < s_keyboards.size(); i++)
if (!s_keyboards[i].valid())
return makedev(DeviceNumber::Keyboard, i + 1);
return makedev(DeviceNumber::Keyboard, s_keyboards.size() + 1);
case InputDevice::Type::Mouse:
return makedev(DeviceNumber::Mouse, s_next_mouse++);
for (size_t i = 0; i < s_mice.size(); i++)
if (!s_mice[i].valid())
return makedev(DeviceNumber::Mouse, i + 1);
return makedev(DeviceNumber::Mouse, s_mice.size() + 1);
}
ASSERT_NOT_REACHED();
}
@ -52,10 +62,25 @@ namespace Kernel
InputDevice::InputDevice(Type type)
: CharacterDevice(0440, 0, 901)
, m_rdev(get_rdev(type))
, m_name(MUST(BAN::String::formatted(get_name_format(type), minor(m_rdev))))
, m_name(MUST(BAN::String::formatted(get_name_format(type), minor(m_rdev) - 1)))
, m_type(type)
, m_event_size(get_event_size(type))
{
MUST(m_event_buffer.resize(m_event_size * m_max_event_count, 0));
if (m_type == Type::Keyboard)
{
if (s_keyboards.size() < minor(m_rdev))
MUST(s_keyboards.resize(minor(m_rdev)));
s_keyboards[minor(m_rdev) - 1] = MUST(get_weak_ptr());
}
if (m_type == Type::Mouse)
{
if (s_mice.size() < minor(m_rdev))
MUST(s_mice.resize(minor(m_rdev)));
s_mice[minor(m_rdev) - 1] = MUST(get_weak_ptr());
}
}
void InputDevice::add_event(BAN::ConstByteSpan event)
@ -63,6 +88,25 @@ namespace Kernel
SpinLockGuard _(m_event_lock);
ASSERT(event.size() == m_event_size);
if (m_type == Type::Mouse && m_event_count > 0)
{
const size_t last_index = (m_event_head + m_max_event_count - 1) % m_max_event_count;
auto& last_event = *reinterpret_cast<LibInput::MouseEvent*>(&m_event_buffer[last_index * m_event_size]);
auto& curr_event = event.as<const LibInput::MouseEvent>();
if (last_event.type == LibInput::MouseEventType::MouseMoveEvent && curr_event.type == LibInput::MouseEventType::MouseMoveEvent)
{
last_event.move_event.rel_x += curr_event.move_event.rel_x;
last_event.move_event.rel_y += curr_event.move_event.rel_y;
return;
}
if (last_event.type == LibInput::MouseEventType::MouseScrollEvent && curr_event.type == LibInput::MouseEventType::MouseScrollEvent)
{
last_event.scroll_event.scroll += curr_event.scroll_event.scroll;
return;
}
}
if (m_event_count == m_max_event_count)
{
m_event_tail = (m_event_tail + 1) % m_max_event_count;
@ -74,6 +118,10 @@ namespace Kernel
m_event_count++;
m_event_semaphore.unblock();
if (m_type == Type::Keyboard && s_keyboard_device)
s_keyboard_device->notify();
if (m_type == Type::Mouse && s_mouse_device)
s_mouse_device->notify();
}
BAN::ErrorOr<size_t> InputDevice::read_impl(off_t, BAN::ByteSpan buffer)
@ -101,4 +149,113 @@ namespace Kernel
return m_event_size;
}
BAN::ErrorOr<size_t> InputDevice::read_non_block(BAN::ByteSpan buffer)
{
if (buffer.size() < m_event_size)
return BAN::Error::from_errno(ENOBUFS);
SpinLockGuard _(m_event_lock);
if (m_event_count == 0)
return 0;
memcpy(buffer.data(), &m_event_buffer[m_event_tail * m_event_size], m_event_size);
m_event_tail = (m_event_tail + 1) % m_max_event_count;
m_event_count--;
return m_event_size;
}
BAN::ErrorOr<BAN::RefPtr<KeyboardDevice>> KeyboardDevice::create(mode_t mode, uid_t uid, gid_t gid)
{
s_keyboard_device = TRY(BAN::RefPtr<KeyboardDevice>::create(mode, uid, gid));
return s_keyboard_device;
}
KeyboardDevice::KeyboardDevice(mode_t mode, uid_t uid, gid_t gid)
: CharacterDevice(mode, uid, gid)
, m_rdev(makedev(DeviceNumber::Keyboard, 0))
, m_name("keyboard"_sv)
{}
BAN::ErrorOr<size_t> KeyboardDevice::read_impl(off_t, BAN::ByteSpan buffer)
{
if (buffer.size() < sizeof(LibInput::RawKeyEvent))
return BAN::Error::from_errno(ENOBUFS);
for (;;)
{
for (auto& weak_keyboard : s_keyboards)
{
auto keyboard = weak_keyboard.lock();
if (!keyboard)
continue;
auto bytes = TRY(keyboard->read_non_block(buffer));
if (bytes > 0)
return bytes;
}
LockFreeGuard _(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_semaphore));
}
}
bool KeyboardDevice::can_read_impl() const
{
for (auto& weak_keyboard : s_keyboards)
if (auto keyboard = weak_keyboard.lock())
if (keyboard->can_read())
return true;
return false;
}
BAN::ErrorOr<BAN::RefPtr<MouseDevice>> MouseDevice::create(mode_t mode, uid_t uid, gid_t gid)
{
s_mouse_device = TRY(BAN::RefPtr<MouseDevice>::create(mode, uid, gid));
return s_mouse_device;
}
MouseDevice::MouseDevice(mode_t mode, uid_t uid, gid_t gid)
: CharacterDevice(mode, uid, gid)
, m_rdev(makedev(DeviceNumber::Mouse, 0))
, m_name("mouse"_sv)
{}
BAN::ErrorOr<size_t> MouseDevice::read_impl(off_t, BAN::ByteSpan buffer)
{
if (buffer.size() < sizeof(LibInput::MouseEvent))
return BAN::Error::from_errno(ENOBUFS);
for (;;)
{
for (auto& weak_mouse : s_mice)
{
auto mouse = weak_mouse.lock();
if (!mouse)
continue;
auto bytes = TRY(mouse->read_non_block(buffer));
if (bytes > 0)
return bytes;
}
LockFreeGuard _(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_semaphore));
}
}
bool MouseDevice::can_read_impl() const
{
for (auto& weak_mouse : s_mice)
if (auto mouse = weak_mouse.lock())
if (mouse->can_read())
return true;
return false;
}
}

View File

@ -26,7 +26,7 @@ namespace Kernel
vaddr_guard.disable();
paddr_guard.disable();
PageTable::kernel().map_range_at(paddr, vaddr, size, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present);
PageTable::kernel().map_range_at(paddr, vaddr, size, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
return BAN::UniqPtr<DMARegion>::adopt(region_ptr);
}

View File

@ -652,7 +652,8 @@ namespace Kernel::PCI
m_vaddr = PageTable::kernel().reserve_free_contiguous_pages(needed_pages, KERNEL_OFFSET);
if (m_vaddr == 0)
return BAN::Error::from_errno(ENOMEM);
PageTable::kernel().map_range_at(m_paddr, m_vaddr, m_size, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present);
PageTable::kernel().map_range_at(m_paddr, m_vaddr, m_size, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
return {};
}

View File

@ -83,10 +83,10 @@ namespace Kernel
Process::create_kernel(
[](void*)
{
auto file_or_error = VirtualFileSystem::get().file_from_absolute_path({ 0, 0, 0, 0 }, "/dev/keyboard0"_sv, O_RDONLY);
auto file_or_error = VirtualFileSystem::get().file_from_absolute_path({ 0, 0, 0, 0 }, "/dev/keyboard"_sv, O_RDONLY);
if (file_or_error.is_error())
{
dprintln("no input device found");
dprintln("no keyboard found");
return;
}

View File

@ -154,7 +154,7 @@ namespace Kernel
break;
case USB::InterfaceBaseClass::HID:
if (auto result = USBHIDDriver::create(*this, interface, j); !result.is_error())
m_class_driver = result.release_value();
TRY(m_class_drivers.push_back(result.release_value()));
break;
case USB::InterfaceBaseClass::Physical:
dprintln_if(DEBUG_USB, "Found Physical interface");
@ -218,7 +218,7 @@ namespace Kernel
break;
}
if (m_class_driver)
if (!m_class_drivers.empty())
{
dprintln("Successfully initialized USB interface");
return {};
@ -309,8 +309,8 @@ namespace Kernel
void USBDevice::handle_input_data(BAN::ConstByteSpan data, uint8_t endpoint_id)
{
if (m_class_driver)
m_class_driver->handle_input_data(data, endpoint_id);
for (auto& driver : m_class_drivers)
driver->handle_input_data(data, endpoint_id);
}
USB::SpeedClass USBDevice::determine_speed_class(uint64_t bits_per_second)

View File

@ -6,6 +6,7 @@
#include <kernel/USB/HID/Mouse.h>
#define DEBUG_HID 0
#define DUMP_HID_REPORT 0
namespace Kernel
{
@ -42,8 +43,8 @@ namespace Kernel
BAN::Optional<int32_t> physical_minimum;
BAN::Optional<int32_t> physical_maximum;
// FIXME: support units
BAN::Optional<uint8_t> report_id;
BAN::Optional<uint32_t> report_size;
// FIXME: support report id
BAN::Optional<uint32_t> report_count;
};
@ -57,11 +58,11 @@ namespace Kernel
using namespace USBHID;
#if DEBUG_HID
static void dump_hid_collection(const Collection& collection, size_t indent);
#if DUMP_HID_REPORT
static void dump_hid_collection(const Collection& collection, size_t indent, bool use_report_id);
#endif
static BAN::ErrorOr<Collection> parse_report_descriptor(BAN::ConstByteSpan report_data);
static BAN::ErrorOr<BAN::Vector<Collection>> parse_report_descriptor(BAN::ConstByteSpan report_data, bool& out_use_report_id);
BAN::ErrorOr<BAN::UniqPtr<USBHIDDriver>> USBHIDDriver::create(USBDevice& device, const USBDevice::InterfaceDescriptor& interface, uint8_t interface_index)
{
@ -77,7 +78,10 @@ namespace Kernel
{}
USBHIDDriver::~USBHIDDriver()
{}
{
if (m_hid_device)
DevFileSystem::get().remove_device(m_hid_device);
}
BAN::ErrorOr<void> USBHIDDriver::initialize()
{
@ -143,7 +147,6 @@ namespace Kernel
TRY(m_device.send_request(request, 0));
}
Collection collection {};
const auto& hid_descriptor = *reinterpret_cast<const HIDDescriptor*>(m_interface.misc_descriptors[hid_descriptor_index].data());
dprintln_if(DEBUG_HID, "HID descriptor ({} bytes)", m_interface.misc_descriptors[hid_descriptor_index].size());
@ -153,7 +156,7 @@ namespace Kernel
dprintln_if(DEBUG_HID, " bCountryCode: {}", hid_descriptor.bCountryCode);
dprintln_if(DEBUG_HID, " bNumDescriptors: {}", hid_descriptor.bNumDescriptors);
bool report_descriptor_parsed = false;
BAN::Vector<Collection> collections;
for (size_t i = 0; i < hid_descriptor.bNumDescriptors; i++)
{
auto descriptor = hid_descriptor.descriptors[i];
@ -163,11 +166,6 @@ namespace Kernel
dprintln_if(DEBUG_HID, "Skipping HID descriptor type 0x{2H}", descriptor.bDescriptorType);
continue;
}
if (report_descriptor_parsed)
{
dwarnln("Multiple report descriptors specified");
return BAN::Error::from_errno(ENOTSUP);
}
if (descriptor.wItemLength > dma_buffer->size())
{
@ -194,40 +192,37 @@ namespace Kernel
dprintln_if(DEBUG_HID, "Parsing {} byte report descriptor", +descriptor.wItemLength);
auto report_data = BAN::ConstByteSpan(reinterpret_cast<uint8_t*>(dma_buffer->vaddr()), descriptor.wItemLength);
collection = TRY(parse_report_descriptor(report_data));
report_descriptor_parsed = true;
auto new_collections = TRY(parse_report_descriptor(report_data, m_uses_report_id));
for (auto& collection : new_collections)
TRY(collections.push_back(BAN::move(collection)));
}
if (!report_descriptor_parsed)
if (collections.empty())
{
dwarnln("No report descriptors specified");
dwarnln("No collections specified for HID device");
return BAN::Error::from_errno(EFAULT);
}
if (collection.usage_page != 0x01)
// FIXME: Handle other collections?
if (collections.front().usage_page != 0x01)
{
dwarnln("Top most collection is not generic desktop page");
return BAN::Error::from_errno(EFAULT);
}
#if DEBUG_HID
{
SpinLockGuard _(Debug::s_debug_lock);
dump_hid_collection(collection, 0);
}
#endif
switch (collection.usage_id)
switch (collections.front().usage_id)
{
case 0x02:
m_hid_device = TRY(BAN::RefPtr<USBMouse>::create());
dprintln("Initialized an USB Mouse");
break;
case 0x06:
m_hid_device = TRY(BAN::RefPtr<USBKeyboard>::create());
dprintln("Initialized an USB Keyboard");
break;
default:
dwarnln("Unsupported generic descript page usage 0x{2H}", collection.usage_id);
dwarnln("Unsupported generic descript page usage 0x{2H}", collections.front().usage_id);
return BAN::Error::from_errno(ENOTSUP);
}
DevFileSystem::get().add_device(m_hid_device);
@ -235,14 +230,14 @@ namespace Kernel
const auto& endpoint_descriptor = m_interface.endpoints[endpoint_index].descriptor;
m_endpoint_id = (endpoint_descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint_descriptor.bEndpointAddress & 0x80);
m_collection = BAN::move(collection);
m_collections = BAN::move(collections);
TRY(m_device.initialize_endpoint(endpoint_descriptor));
return {};
}
void USBHIDDriver::forward_collection_inputs(const Collection& collection, BAN::ConstByteSpan& data, size_t bit_offset)
void USBHIDDriver::forward_collection_inputs(const Collection& collection, BAN::Optional<uint8_t> report_id, BAN::ConstByteSpan& data, size_t bit_offset)
{
const auto extract_bits =
[data](size_t bit_offset, size_t bit_count, bool as_unsigned) -> int64_t
@ -281,7 +276,7 @@ namespace Kernel
{
if (entry.has<Collection>())
{
forward_collection_inputs(entry.get<Collection>(), data, bit_offset);
forward_collection_inputs(entry.get<Collection>(), report_id, data, bit_offset);
continue;
}
@ -289,6 +284,8 @@ namespace Kernel
const auto& input = entry.get<Report>();
if (input.type != Report::Type::Input)
continue;
if (report_id.value_or(input.report_id) != input.report_id)
continue;
ASSERT(input.report_size <= 32);
@ -326,7 +323,9 @@ namespace Kernel
void USBHIDDriver::handle_input_data(BAN::ConstByteSpan data, uint8_t endpoint_id)
{
ASSERT(m_endpoint_id == endpoint_id);
// If this packet is not for us, skip it
if (m_endpoint_id != endpoint_id)
return;
if constexpr(DEBUG_HID)
{
@ -345,21 +344,32 @@ namespace Kernel
dprintln_if(DEBUG_HID, "Received {} bytes from endpoint {}: {}", data.size(), endpoint_id, buffer);
}
BAN::Optional<uint8_t> report_id;
if (m_uses_report_id)
{
report_id = data[0];
data = data.slice(1);
}
m_hid_device->start_report();
forward_collection_inputs(m_collection, data, 0);
// FIXME: Handle other collections?
forward_collection_inputs(m_collections.front(), report_id, data, 0);
m_hid_device->stop_report();
}
BAN::ErrorOr<Collection> parse_report_descriptor(BAN::ConstByteSpan report_data)
BAN::ErrorOr<BAN::Vector<Collection>> parse_report_descriptor(BAN::ConstByteSpan report_data, bool& out_use_report_id)
{
BAN::Vector<GlobalState> global_stack;
GlobalState global_state;
LocalState local_state;
BAN::Optional<Collection> result;
BAN::Vector<Collection> result_stack;
BAN::Vector<Collection> collection_stack;
bool one_has_report_id = false;
bool all_has_report_id = true;
const auto extract_report_item =
[&](bool as_unsigned) -> int64_t
{
@ -411,6 +421,11 @@ namespace Kernel
return BAN::Error::from_errno(EFAULT);
}
if (global_state.report_id.has_value())
one_has_report_id = true;
else
all_has_report_id = false;
const int64_t logical_minimum = global_state.logical_minimum.value();
const int64_t logical_maximum = get_correct_sign(
global_state.logical_minimum.value(),
@ -436,6 +451,7 @@ namespace Kernel
item.usage_minimum = local_state.usage_minimum.value();
item.usage_maximum = local_state.usage_maximum.value();
item.type = type;
item.report_id = global_state.report_id.value_or(0);
item.report_count = global_state.report_count.value();
item.report_size = global_state.report_size.value();
item.logical_minimum = logical_minimum;
@ -454,6 +470,7 @@ namespace Kernel
item.usage_minimum = 0;
item.usage_maximum = 0;
item.type = type;
item.report_id = global_state.report_id.value_or(0);
item.report_count = global_state.report_count.value();
item.report_size = global_state.report_size.value();
item.logical_minimum = 0;
@ -466,9 +483,10 @@ namespace Kernel
return {};
}
for (size_t i = 0; i < global_state.report_count.value(); i++)
for (size_t i = 0; i < local_state.usage_stack.size(); i++)
{
const uint32_t usage = local_state.usage_stack[BAN::Math::min<size_t>(i, local_state.usage_stack.size() - 1)];
const uint32_t usage = local_state.usage_stack[i];
const uint32_t count = (i + 1 < local_state.usage_stack.size()) ? 1 : global_state.report_count.value() - i;
Report item;
item.usage_page = (usage >> 16) ? (usage >> 16) : global_state.usage_page.value();
@ -476,7 +494,8 @@ namespace Kernel
item.usage_minimum = 0;
item.usage_maximum = 0;
item.type = type;
item.report_count = 1;
item.report_id = global_state.report_id.value_or(0);
item.report_count = count;
item.report_size = global_state.report_size.value();
item.logical_minimum = logical_minimum;
item.logical_maximum = logical_maximum;
@ -554,12 +573,7 @@ namespace Kernel
}
if (collection_stack.size() == 1)
{
if (result.has_value())
{
dwarnln("Multiple top-level collections not supported");
return BAN::Error::from_errno(ENOTSUP);
}
result = BAN::move(collection_stack.back());
TRY(result_stack.push_back(BAN::move(collection_stack.back())));
collection_stack.pop_back();
}
else
@ -605,8 +619,16 @@ namespace Kernel
global_state.report_size = extract_report_item(true);
break;
case 0b1000: // report id
dwarnln("Report IDs are not supported");
return BAN::Error::from_errno(ENOTSUP);
{
auto report_id = extract_report_item(true);
if (report_id > 0xFF)
{
dwarnln("Multi-byte report id");
return BAN::Error::from_errno(EFAULT);
}
global_state.report_id = report_id;
break;
}
case 0b1001: // report count
global_state.report_count = extract_report_item(true);
break;
@ -663,23 +685,38 @@ namespace Kernel
report_data = report_data.slice(1 + item_size);
}
if (!result.has_value())
if (result_stack.empty())
{
dwarnln("No collection defined in report descriptor");
return BAN::Error::from_errno(EFAULT);
}
return result.release_value();
if (one_has_report_id != all_has_report_id)
{
dwarnln("Some but not all reports have report id");
return BAN::Error::from_errno(EFAULT);
}
#if DEBUG_HID
#if DUMP_HID_REPORT
{
SpinLockGuard _(Debug::s_debug_lock);
for (const auto& collection : result_stack)
dump_hid_collection(collection, 0, one_has_report_id);
}
#endif
out_use_report_id = one_has_report_id;
return BAN::move(result_stack);
}
#if DUMP_HID_REPORT
static void print_indent(size_t indent)
{
for (size_t i = 0; i < indent; i++)
Debug::putchar(' ');
}
static void dump_hid_report(const Report& report, size_t indent)
static void dump_hid_report(const Report& report, size_t indent, bool use_report_id)
{
const char* report_type = "";
switch (report.type)
@ -691,6 +728,12 @@ namespace Kernel
print_indent(indent);
BAN::Formatter::println(Debug::putchar, "report {}", report_type);
if (use_report_id)
{
print_indent(indent + 4);
BAN::Formatter::println(Debug::putchar, "report id: {2H}", report.report_id);
}
print_indent(indent + 4);
BAN::Formatter::println(Debug::putchar, "usage page: {2H}", report.usage_page);
@ -722,7 +765,7 @@ namespace Kernel
BAN::Formatter::println(Debug::putchar, "pmaximum: {}", report.physical_maximum);
}
static void dump_hid_collection(const Collection& collection, size_t indent)
static void dump_hid_collection(const Collection& collection, size_t indent, bool use_report_id)
{
print_indent(indent);
BAN::Formatter::println(Debug::putchar, "collection {}", collection.type);
@ -732,9 +775,9 @@ namespace Kernel
for (const auto& entry : collection.entries)
{
if (entry.has<Collection>())
dump_hid_collection(entry.get<Collection>(), indent + 4);
dump_hid_collection(entry.get<Collection>(), indent + 4, use_report_id);
if (entry.has<Report>())
dump_hid_report(entry.get<Report>(), indent + 4);
dump_hid_report(entry.get<Report>(), indent + 4, use_report_id);
}
}
#endif

View File

@ -155,10 +155,10 @@ namespace Kernel
s_scancode_to_keycode[0x37] = keycode_normal(3, 10);
s_scancode_to_keycode[0x38] = keycode_normal(3, 11);
s_scancode_to_keycode[0xE5] = keycode_normal(3, 12);
s_scancode_to_keycode[0xE0] = keycode_normal(4, 1);
s_scancode_to_keycode[0xE3] = keycode_normal(4, 2);
s_scancode_to_keycode[0xE2] = keycode_normal(4, 3);
s_scancode_to_keycode[0x2C] = keycode_normal(4, 4);
s_scancode_to_keycode[0xE0] = keycode_normal(4, 0);
s_scancode_to_keycode[0xE3] = keycode_normal(4, 1);
s_scancode_to_keycode[0xE2] = keycode_normal(4, 2);
s_scancode_to_keycode[0x2C] = keycode_normal(4, 3);
s_scancode_to_keycode[0xE6] = keycode_normal(4, 5);
s_scancode_to_keycode[0xE4] = keycode_normal(4, 6);

View File

@ -15,6 +15,21 @@ namespace Kernel
return TRY(BAN::UniqPtr<XHCIDevice>::create(controller, port_id, slot_id));
}
uint64_t XHCIDevice::calculate_port_bits_per_second(XHCIController& controller, uint32_t port_id)
{
const uint32_t portsc = controller.operational_regs().ports[port_id - 1].portsc;
const uint32_t speed_id = (portsc >> XHCI::PORTSC::PORT_SPEED_SHIFT) & XHCI::PORTSC::PORT_SPEED_MASK;
return controller.port(port_id).speed_id_to_speed[speed_id];
}
XHCIDevice::XHCIDevice(XHCIController& controller, uint32_t port_id, uint32_t slot_id)
: USBDevice(determine_speed_class(calculate_port_bits_per_second(controller, port_id)))
, m_controller(controller)
, m_port_id(port_id)
, m_slot_id(slot_id)
{}
XHCIDevice::~XHCIDevice()
{
XHCI::TRB disable_slot { .disable_slot_command {} };
@ -32,11 +47,9 @@ namespace Kernel
const uint32_t portsc = m_controller.operational_regs().ports[m_port_id - 1].portsc;
const uint32_t speed_id = (portsc >> XHCI::PORTSC::PORT_SPEED_SHIFT) & XHCI::PORTSC::PORT_SPEED_MASK;
const uint32_t bits_per_second = m_controller.port(m_port_id).speed_id_to_speed[speed_id];
const auto speed_class = determine_speed_class(bits_per_second);
m_endpoints[0].max_packet_size = 0;
switch (speed_class)
switch (m_speed_class)
{
case USB::SpeedClass::LowSpeed:
case USB::SpeedClass::FullSpeed:
@ -97,7 +110,7 @@ namespace Kernel
}
// NOTE: Full speed devices can have other max packet sizes than 8
if (speed_class == USB::SpeedClass::FullSpeed)
if (m_speed_class == USB::SpeedClass::FullSpeed)
TRY(update_actual_max_packet_size());
return {};
@ -155,6 +168,39 @@ namespace Kernel
return {};
}
// 6.2.3.6 Interval
static uint32_t determine_interval(const USBEndpointDescriptor& endpoint_descriptor, USB::SpeedClass speed_class)
{
auto ep_type = static_cast<USB::EndpointType>(endpoint_descriptor.bDescriptorType & 0x03);
switch (speed_class)
{
case USB::SpeedClass::HighSpeed:
// maximum NAK rate
if (ep_type == USB::EndpointType::Control || ep_type == USB::EndpointType::Bulk)
return (endpoint_descriptor.bInterval == 0) ? 0 : BAN::Math::clamp<uint32_t>(
BAN::Math::ilog2<uint32_t>(endpoint_descriptor.bInterval), 0, 15
);
// fall through
case USB::SpeedClass::SuperSpeed:
if (ep_type == USB::EndpointType::Isochronous || ep_type == USB::EndpointType::Interrupt)
return BAN::Math::clamp<uint32_t>(endpoint_descriptor.bInterval - 1, 0, 15);
return 0;
case USB::SpeedClass::FullSpeed:
if (ep_type == USB::EndpointType::Isochronous)
return BAN::Math::clamp<uint32_t>(endpoint_descriptor.bInterval + 2, 3, 18);
// fall through
case USB::SpeedClass::LowSpeed:
if (ep_type == USB::EndpointType::Isochronous || ep_type == USB::EndpointType::Interrupt)
return (endpoint_descriptor.bInterval == 0) ? 0 : BAN::Math::clamp<uint32_t>(
BAN::Math::ilog2<uint32_t>(endpoint_descriptor.bInterval * 8), 3, 10
);
return 0;
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> XHCIDevice::initialize_endpoint(const USBEndpointDescriptor& endpoint_descriptor)
{
const uint32_t endpoint_id = (endpoint_descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint_descriptor.bEndpointAddress & 0x80);
@ -195,6 +241,9 @@ namespace Kernel
ASSERT((endpoint_descriptor.bmAttributes & 0x03) == 3);
ASSERT(m_controller.port(m_port_id).revision_major == 2);
const uint32_t max_esit_payload = endpoint_context.max_packet_size * (endpoint_context.max_burst_size + 1);
const uint32_t interval = determine_interval(endpoint_descriptor, m_speed_class);
memset(&endpoint_context, 0, context_size);
endpoint_context.endpoint_type = XHCI::EndpointType::InterruptIn;
endpoint_context.max_packet_size = endpoint.max_packet_size;
@ -202,9 +251,10 @@ namespace Kernel
endpoint_context.mult = 0;
endpoint_context.error_count = 3;
endpoint_context.tr_dequeue_pointer = endpoint.transfer_ring->paddr() | 1;
const uint32_t max_esit_payload = endpoint_context.max_packet_size * (endpoint_context.max_burst_size + 1);
endpoint_context.max_esit_payload_lo = max_esit_payload & 0xFFFF;
endpoint_context.max_esit_payload_hi = max_esit_payload >> 16;
endpoint_context.average_trb_length = max_esit_payload;
endpoint_context.interval = interval;
}
XHCI::TRB configure_endpoint { .configure_endpoint_command = {} };

View File

@ -205,9 +205,8 @@ static void init2(void*)
// Initialize empty keymap
MUST(LibInput::KeyboardLayout::initialize());
// FIXME: implement device hot plugging, so multiple devices is possible
//if (auto res = PS2Controller::initialize(); res.is_error())
// dprintln("{}", res.error());
if (auto res = PS2Controller::initialize(); res.is_error())
dprintln("{}", res.error());
MUST(NetworkManager::initialize());
@ -216,9 +215,6 @@ static void init2(void*)
PCI::PCIManager::get().initialize_devices();
dprintln("PCI devices initialized");
// FIXME: This is very hacky way to wait until USB stack is initialized
SystemTimer::get().sleep(500);
VirtualFileSystem::initialize(cmdline.root);
dprintln("VFS initialized");

View File

@ -31,6 +31,6 @@ qemu-system-$QEMU_ARCH \
-drive format=raw,id=disk,file=${BANAN_DISK_IMAGE_PATH},if=none \
-device e1000e,netdev=net \
-netdev user,id=net \
-device qemu-xhci -device usb-kbd \
-device qemu-xhci -device usb-kbd -device usb-mouse \
$DISK_ARGS \
$@ \

View File

@ -158,11 +158,11 @@ int main()
MUST(LibInput::KeyboardLayout::initialize());
MUST(LibInput::KeyboardLayout::get().load_from_file("/usr/share/keymaps/us.keymap"_sv));
int keyboard_fd = open("/dev/keyboard0", O_RDONLY);
int keyboard_fd = open("/dev/keyboard", O_RDONLY);
if (keyboard_fd == -1)
perror("open");
int mouse_fd = open("/dev/mouse0", O_RDONLY);
int mouse_fd = open("/dev/mouse", O_RDONLY);
if (mouse_fd == -1)
perror("open");

View File

@ -50,7 +50,7 @@ void cleanup()
int main(int argc, char** argv)
{
const char* fb_path = "/dev/fb0";
const char* mouse_path = "/dev/mouse0";
const char* mouse_path = "/dev/mouse";
if (argc == 1)
;