Compare commits
12 Commits
8ddab05ed3
...
baa4e6475a
Author | SHA1 | Date |
---|---|---|
Bananymous | baa4e6475a | |
Bananymous | ac5c77ee2c | |
Bananymous | 1efc6a1385 | |
Bananymous | 442ea8a692 | |
Bananymous | 749be67df3 | |
Bananymous | a97a574718 | |
Bananymous | 110a45bee6 | |
Bananymous | f120da3aca | |
Bananymous | 240684bc1f | |
Bananymous | 62003d96f3 | |
Bananymous | e905634343 | |
Bananymous | 14dce1abac |
|
@ -18,8 +18,6 @@ namespace BAN
|
|||
constexpr Optional(const Optional&);
|
||||
constexpr Optional(const T&);
|
||||
constexpr Optional(T&&);
|
||||
template<typename... Args>
|
||||
constexpr Optional(Args&&...);
|
||||
|
||||
~Optional();
|
||||
|
||||
|
@ -40,6 +38,8 @@ namespace BAN
|
|||
constexpr T release_value();
|
||||
constexpr T& value();
|
||||
constexpr const T& value() const;
|
||||
constexpr T& value_or(T&);
|
||||
constexpr const T& value_or(const T&) const;
|
||||
|
||||
constexpr void clear();
|
||||
|
||||
|
@ -83,14 +83,6 @@ namespace BAN
|
|||
new (m_storage) T(move(value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
constexpr Optional<T>::Optional(Args&&... args)
|
||||
: m_has_value(true)
|
||||
{
|
||||
new (m_storage) T(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T>::~Optional()
|
||||
{
|
||||
|
@ -112,7 +104,7 @@ namespace BAN
|
|||
{
|
||||
clear();
|
||||
m_has_value = other.has_value();
|
||||
if (other.has_value)
|
||||
if (other.has_value())
|
||||
new (m_storage) T(other.value());
|
||||
return *this;
|
||||
}
|
||||
|
@ -185,6 +177,22 @@ namespace BAN
|
|||
return (const T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T& Optional<T>::value_or(T& empty)
|
||||
{
|
||||
if (!has_value())
|
||||
return empty;
|
||||
return (T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& Optional<T>::value_or(const T& empty) const
|
||||
{
|
||||
if (!has_value())
|
||||
return empty;
|
||||
return (const T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void Optional<T>::clear()
|
||||
{
|
||||
|
|
|
@ -286,6 +286,16 @@ namespace BAN
|
|||
return **reinterpret_cast<const remove_reference_t<T>**>(m_storage);
|
||||
}
|
||||
|
||||
bool has_value() const
|
||||
{
|
||||
return m_index != invalid_index();
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return has_value();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_index != invalid_index())
|
||||
|
|
|
@ -34,6 +34,7 @@ set(KERNEL_SOURCES
|
|||
kernel/FS/VirtualFileSystem.cpp
|
||||
kernel/GDT.cpp
|
||||
kernel/IDT.cpp
|
||||
kernel/Input/InputDevice.cpp
|
||||
kernel/Input/PS2/Controller.cpp
|
||||
kernel/Input/PS2/Device.cpp
|
||||
kernel/Input/PS2/Keyboard.cpp
|
||||
|
@ -93,6 +94,13 @@ set(KERNEL_SOURCES
|
|||
kernel/Timer/PIT.cpp
|
||||
kernel/Timer/RTC.cpp
|
||||
kernel/Timer/Timer.cpp
|
||||
kernel/USB/Device.cpp
|
||||
kernel/USB/HID/HIDDriver.cpp
|
||||
kernel/USB/HID/Keyboard.cpp
|
||||
kernel/USB/HID/Mouse.cpp
|
||||
kernel/USB/USBManager.cpp
|
||||
kernel/USB/XHCI/Controller.cpp
|
||||
kernel/USB/XHCI/Device.cpp
|
||||
icxxabi.cpp
|
||||
)
|
||||
|
||||
|
@ -169,7 +177,8 @@ target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
|
|||
target_compile_options(kernel PUBLIC -O2 -g)
|
||||
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-literal-suffix -fno-rtti -fno-exceptions>)
|
||||
target_compile_options(kernel PUBLIC -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
|
||||
target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -Wall -Wextra -Werror -Wstack-usage=1024 -fno-omit-frame-pointer -mgeneral-regs-only)
|
||||
target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -fno-omit-frame-pointer -fstrict-volatile-bitfields -mgeneral-regs-only)
|
||||
target_compile_options(kernel PUBLIC -Wall -Wextra -Werror -Wstack-usage=1024)
|
||||
|
||||
# This might not work with other toolchains
|
||||
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
|
||||
|
|
|
@ -42,20 +42,20 @@ namespace __cxxabiv1
|
|||
{
|
||||
using __guard = uint64_t;
|
||||
|
||||
int __cxa_guard_acquire (__guard* g)
|
||||
extern "C" int __cxa_guard_acquire (__guard* g)
|
||||
{
|
||||
uint8_t* byte = reinterpret_cast<uint8_t*>(g);
|
||||
uint8_t zero = 0;
|
||||
return __atomic_compare_exchange_n(byte, &zero, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
void __cxa_guard_release (__guard* g)
|
||||
extern "C" void __cxa_guard_release (__guard* g)
|
||||
{
|
||||
uint8_t* byte = reinterpret_cast<uint8_t*>(g);
|
||||
__atomic_store_n(byte, 0, __ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
void __cxa_guard_abort (__guard*)
|
||||
extern "C" void __cxa_guard_abort (__guard*)
|
||||
{
|
||||
Kernel::panic("__cxa_guard_abort");
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace Kernel
|
|||
Null,
|
||||
Zero,
|
||||
Debug,
|
||||
Input,
|
||||
Keyboard,
|
||||
Mouse,
|
||||
SCSI,
|
||||
NVMeController,
|
||||
NVMeNamespace,
|
||||
|
|
|
@ -20,8 +20,6 @@ namespace Kernel
|
|||
void add_device(BAN::RefPtr<Device>);
|
||||
void add_inode(BAN::StringView path, BAN::RefPtr<TmpInode>);
|
||||
|
||||
int get_next_input_device() const;
|
||||
|
||||
void initiate_sync(bool should_block);
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/ByteSpan.h>
|
||||
|
||||
#include <kernel/Device/Device.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class InputDevice : public CharacterDevice
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
Mouse,
|
||||
Keyboard,
|
||||
};
|
||||
|
||||
public:
|
||||
InputDevice(Type type);
|
||||
|
||||
protected:
|
||||
void add_event(BAN::ConstByteSpan);
|
||||
|
||||
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||
|
||||
bool can_read_impl() const override { SpinLockGuard _(m_event_lock); return m_event_count > 0; }
|
||||
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:
|
||||
const dev_t m_rdev;
|
||||
const BAN::String m_name;
|
||||
|
||||
mutable SpinLock m_event_lock;
|
||||
Semaphore m_event_semaphore;
|
||||
|
||||
static constexpr size_t m_max_event_count { 128 };
|
||||
|
||||
BAN::Vector<uint8_t> m_event_buffer;
|
||||
const size_t m_event_size;
|
||||
size_t m_event_tail { 0 };
|
||||
size_t m_event_head { 0 };
|
||||
size_t m_event_count { 0 };
|
||||
};
|
||||
|
||||
}
|
|
@ -60,6 +60,7 @@ namespace Kernel::Input
|
|||
|
||||
private:
|
||||
BAN::RefPtr<PS2Device> m_devices[2];
|
||||
|
||||
Mutex m_mutex;
|
||||
|
||||
BAN::CircularQueue<Command, 128> m_command_queue;
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/Input/PS2/Controller.h>
|
||||
#include <kernel/Input/InputDevice.h>
|
||||
#include <kernel/Interruptable.h>
|
||||
|
||||
namespace Kernel::Input
|
||||
{
|
||||
|
||||
class PS2Device : public CharacterDevice, public Interruptable
|
||||
class PS2Device : public Interruptable, public InputDevice
|
||||
{
|
||||
public:
|
||||
virtual ~PS2Device() {}
|
||||
|
||||
virtual void send_initialize() = 0;
|
||||
|
||||
virtual void command_timedout(uint8_t* command_data, uint8_t command_size) = 0;
|
||||
|
@ -21,17 +20,10 @@ namespace Kernel::Input
|
|||
|
||||
virtual void handle_byte(uint8_t) = 0;
|
||||
|
||||
virtual BAN::StringView name() const final override { return m_name; }
|
||||
virtual dev_t rdev() const final override { return m_rdev; }
|
||||
|
||||
virtual void update() final override { m_controller.update_command_queue(); }
|
||||
protected:
|
||||
PS2Device(PS2Controller&, InputDevice::Type type);
|
||||
|
||||
protected:
|
||||
PS2Device(PS2Controller&);
|
||||
|
||||
private:
|
||||
const dev_t m_rdev;
|
||||
const BAN::String m_name;
|
||||
PS2Controller& m_controller;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Array.h>
|
||||
#include <BAN/CircularQueue.h>
|
||||
#include <kernel/Input/PS2/Device.h>
|
||||
#include <kernel/Input/PS2/Keymap.h>
|
||||
#include <kernel/Semaphore.h>
|
||||
#include <LibInput/KeyEvent.h>
|
||||
|
||||
namespace Kernel::Input
|
||||
{
|
||||
|
@ -20,13 +17,15 @@ namespace Kernel::Input
|
|||
};
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<PS2Keyboard*> create(PS2Controller&);
|
||||
static BAN::ErrorOr<BAN::RefPtr<PS2Keyboard>> create(PS2Controller&);
|
||||
virtual void send_initialize() override;
|
||||
|
||||
virtual void command_timedout(uint8_t* command_data, uint8_t command_size) final override;
|
||||
|
||||
virtual void handle_byte(uint8_t) final override;
|
||||
|
||||
virtual void update() final override { m_controller.update_command_queue(); }
|
||||
|
||||
private:
|
||||
PS2Keyboard(PS2Controller& controller);
|
||||
|
||||
|
@ -37,22 +36,11 @@ namespace Kernel::Input
|
|||
uint8_t m_byte_index { 0 };
|
||||
|
||||
uint8_t m_scancode_set { 0xFF };
|
||||
|
||||
uint16_t m_modifiers { 0 };
|
||||
|
||||
BAN::CircularQueue<LibInput::RawKeyEvent, 50> m_event_queue;
|
||||
SpinLock m_event_lock;
|
||||
|
||||
PS2Keymap m_keymap;
|
||||
|
||||
Semaphore m_semaphore;
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||
|
||||
virtual bool can_read_impl() const override { return !m_event_queue.empty(); }
|
||||
virtual bool can_write_impl() const override { return false; }
|
||||
virtual bool has_error_impl() const override { return false; }
|
||||
friend class BAN::RefPtr<PS2Keyboard>;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/Input/PS2/Device.h>
|
||||
#include <kernel/Semaphore.h>
|
||||
#include <LibInput/MouseEvent.h>
|
||||
|
||||
namespace Kernel::Input
|
||||
{
|
||||
|
@ -16,13 +14,15 @@ namespace Kernel::Input
|
|||
};
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<PS2Mouse*> create(PS2Controller&);
|
||||
static BAN::ErrorOr<BAN::RefPtr<PS2Mouse>> create(PS2Controller&);
|
||||
virtual void send_initialize() override;
|
||||
|
||||
virtual void command_timedout(uint8_t* command_data, uint8_t command_size) final override { (void)command_data; (void)command_size; }
|
||||
|
||||
virtual void handle_byte(uint8_t) final override;
|
||||
|
||||
virtual void update() final override { m_controller.update_command_queue(); }
|
||||
|
||||
private:
|
||||
PS2Mouse(PS2Controller& controller);
|
||||
|
||||
|
@ -36,17 +36,7 @@ namespace Kernel::Input
|
|||
uint8_t m_mouse_id { 0x00 };
|
||||
uint8_t m_button_mask { 0x00 };
|
||||
|
||||
BAN::CircularQueue<LibInput::MouseEvent, 128> m_event_queue;
|
||||
SpinLock m_event_lock;
|
||||
|
||||
Semaphore m_semaphore;
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||
|
||||
virtual bool can_read_impl() const override { return !m_event_queue.empty(); }
|
||||
virtual bool can_write_impl() const override { return false; }
|
||||
virtual bool has_error_impl() const override { return false; }
|
||||
friend class BAN::RefPtr<PS2Mouse>;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBController
|
||||
{
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
namespace USB
|
||||
{
|
||||
enum class SpeedClass
|
||||
{
|
||||
None,
|
||||
LowSpeed,
|
||||
FullSpeed,
|
||||
HighSpeed,
|
||||
SuperSpeed,
|
||||
};
|
||||
|
||||
enum class DeviceBaseClass : uint8_t
|
||||
{
|
||||
CommunicationAndCDCControl = 0x02,
|
||||
Hub = 0x09,
|
||||
BillboardDeviceClass = 0x11,
|
||||
DiagnosticDevice = 0xDC,
|
||||
Miscellaneous = 0xEF,
|
||||
VendorSpecific = 0xFF,
|
||||
};
|
||||
|
||||
enum class InterfaceBaseClass : uint8_t
|
||||
{
|
||||
Audio = 0x01,
|
||||
CommunicationAndCDCControl = 0x02,
|
||||
HID = 0x03,
|
||||
Physical = 0x05,
|
||||
Image = 0x06,
|
||||
Printer = 0x07,
|
||||
MassStorage = 0x08,
|
||||
CDCData = 0x0A,
|
||||
SmartCard = 0x0B,
|
||||
ContentSecurity = 0x0D,
|
||||
Video = 0x0E,
|
||||
PersonalHealthcare = 0x0F,
|
||||
AudioVideoDevice = 0x10,
|
||||
USBTypeCBridgeClass = 0x12,
|
||||
USBBulkDisplayProtocolDeviceClass = 0x13,
|
||||
MCTPOverUSBProtocolEndpointDeviceClass = 0x14,
|
||||
I3CDeviceClass = 0x3C,
|
||||
DiagnosticDevice = 0xDC,
|
||||
WirelessController = 0xE0,
|
||||
Miscellaneous = 0xEF,
|
||||
ApplicationSpecific = 0xFE,
|
||||
VendorSpecific = 0xFF,
|
||||
};
|
||||
|
||||
enum DescriptorType : uint8_t
|
||||
{
|
||||
DEVICE = 1,
|
||||
CONFIGURATION = 2,
|
||||
STRING = 3,
|
||||
INTERFACE = 4,
|
||||
ENDPOINT = 5,
|
||||
DEVICE_QUALIFIER = 6,
|
||||
OTHER_SPEED_CONFIGURATION = 7,
|
||||
INTERFACE_POWER = 8,
|
||||
};
|
||||
|
||||
enum RequestType : uint8_t
|
||||
{
|
||||
HostToDevice = 0b0 << 7,
|
||||
DeviceToHost = 0b1 << 7,
|
||||
|
||||
Standard = 0b00 << 5,
|
||||
Class = 0b01 << 5,
|
||||
Vendor = 0b10 << 5,
|
||||
|
||||
Device = 0b00000,
|
||||
Interface = 0b00001,
|
||||
Endpoint = 0b00010,
|
||||
Other = 0b00011,
|
||||
};
|
||||
|
||||
enum Request : uint8_t
|
||||
{
|
||||
GET_STATUS = 0,
|
||||
CLEAR_FEATURE = 1,
|
||||
SET_FEATURE = 3,
|
||||
SET_ADDRESS = 5,
|
||||
GET_DESCRIPTOR = 6,
|
||||
SET_DESCRIPTOR = 7,
|
||||
GET_CONFIGURATION = 8,
|
||||
SET_CONFIGURATION = 9,
|
||||
GET_INTERFACE = 10,
|
||||
SET_INTERFACE = 11,
|
||||
SYNC_FRAME = 12,
|
||||
};
|
||||
}
|
||||
|
||||
struct USBDeviceDescriptor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdUSB;
|
||||
uint8_t bDeviceClass;
|
||||
uint8_t bDeviceSubClass;
|
||||
uint8_t bDeviceProtocol;
|
||||
uint8_t bMaxPacketSize0;
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
uint16_t bcdDevice;
|
||||
uint8_t iManufacturer;
|
||||
uint8_t iProduct;
|
||||
uint8_t iSerialNumber;
|
||||
uint8_t bNumConfigurations;
|
||||
};
|
||||
static_assert(sizeof(USBDeviceDescriptor) == 18);
|
||||
|
||||
struct USBConfigurationDescriptor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t wTotalLength __attribute__((packed));
|
||||
uint8_t bNumInterfaces;
|
||||
uint8_t bConfigurationValue;
|
||||
uint8_t iConfiguration;
|
||||
uint8_t bmAttributes;
|
||||
uint8_t bMaxPower;
|
||||
};
|
||||
static_assert(sizeof(USBConfigurationDescriptor) == 9);
|
||||
static constexpr size_t foo = sizeof(USBConfigurationDescriptor);
|
||||
|
||||
struct USBInterfaceDescriptor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bInterfaceNumber;
|
||||
uint8_t bAlternateSetting;
|
||||
uint8_t bNumEndpoints;
|
||||
uint8_t bInterfaceClass;
|
||||
uint8_t bInterfaceSubClass;
|
||||
uint8_t bInterfaceProtocol;
|
||||
uint8_t iInterface;
|
||||
};
|
||||
static_assert(sizeof(USBInterfaceDescriptor) == 9);
|
||||
|
||||
struct USBEndpointDescriptor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bmAttributes;
|
||||
uint16_t wMaxPacketSize __attribute__((packed));
|
||||
uint8_t bInterval;
|
||||
};
|
||||
static_assert(sizeof(USBEndpointDescriptor) == 7);
|
||||
|
||||
struct USBDeviceRequest
|
||||
{
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
};
|
||||
static_assert(sizeof(USBDeviceRequest) == 8);
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/ByteSpan.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
|
||||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
#include <kernel/USB/Controller.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBClassDriver
|
||||
{
|
||||
BAN_NON_COPYABLE(USBClassDriver);
|
||||
BAN_NON_MOVABLE(USBClassDriver);
|
||||
|
||||
public:
|
||||
USBClassDriver() = default;
|
||||
virtual ~USBClassDriver() = default;
|
||||
|
||||
virtual void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id) = 0;
|
||||
};
|
||||
|
||||
class USBDevice
|
||||
{
|
||||
BAN_NON_COPYABLE(USBDevice);
|
||||
BAN_NON_MOVABLE(USBDevice);
|
||||
|
||||
public:
|
||||
struct EndpointDescriptor
|
||||
{
|
||||
USBEndpointDescriptor descriptor;
|
||||
};
|
||||
|
||||
struct InterfaceDescriptor
|
||||
{
|
||||
USBInterfaceDescriptor descriptor;
|
||||
BAN::Vector<EndpointDescriptor> endpoints;
|
||||
BAN::Vector<BAN::Vector<uint8_t>> misc_descriptors;
|
||||
};
|
||||
|
||||
struct ConfigurationDescriptor
|
||||
{
|
||||
USBConfigurationDescriptor desciptor;
|
||||
BAN::Vector<InterfaceDescriptor> interfaces;
|
||||
};
|
||||
|
||||
struct DeviceDescriptor
|
||||
{
|
||||
USBDeviceDescriptor descriptor;
|
||||
BAN::Vector<ConfigurationDescriptor> configurations;
|
||||
};
|
||||
|
||||
public:
|
||||
USBDevice() = default;
|
||||
virtual ~USBDevice() = default;
|
||||
|
||||
BAN::ErrorOr<void> initialize();
|
||||
|
||||
const BAN::Vector<ConfigurationDescriptor>& configurations() { return m_descriptor.configurations; }
|
||||
|
||||
virtual BAN::ErrorOr<void> initialize_endpoint(const USBEndpointDescriptor&) = 0;
|
||||
virtual BAN::ErrorOr<size_t> send_request(const USBDeviceRequest&, paddr_t buffer) = 0;
|
||||
|
||||
static USB::SpeedClass determine_speed_class(uint64_t bits_per_second);
|
||||
|
||||
protected:
|
||||
void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id);
|
||||
virtual BAN::ErrorOr<void> initialize_control_endpoint() = 0;
|
||||
|
||||
private:
|
||||
BAN::ErrorOr<ConfigurationDescriptor> parse_configuration(size_t index);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/Input/InputDevice.h>
|
||||
#include <kernel/USB/Device.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
namespace USBHID
|
||||
{
|
||||
|
||||
struct Report
|
||||
{
|
||||
enum class Type { Input, Output, Feature };
|
||||
|
||||
uint16_t usage_page;
|
||||
uint16_t usage_id;
|
||||
Type type;
|
||||
|
||||
uint32_t report_count;
|
||||
uint32_t report_size;
|
||||
|
||||
uint32_t usage_minimum;
|
||||
uint32_t usage_maximum;
|
||||
|
||||
int64_t logical_minimum;
|
||||
int64_t logical_maximum;
|
||||
|
||||
int64_t physical_minimum;
|
||||
int64_t physical_maximum;
|
||||
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
struct Collection
|
||||
{
|
||||
uint16_t usage_page;
|
||||
uint16_t usage_id;
|
||||
uint8_t type;
|
||||
|
||||
BAN::Vector<BAN::Variant<Collection, Report>> entries;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class USBHIDDevice : public InputDevice
|
||||
{
|
||||
BAN_NON_COPYABLE(USBHIDDevice);
|
||||
BAN_NON_MOVABLE(USBHIDDevice);
|
||||
|
||||
public:
|
||||
USBHIDDevice(InputDevice::Type type)
|
||||
: InputDevice(type)
|
||||
{}
|
||||
virtual ~USBHIDDevice() = default;
|
||||
|
||||
virtual void start_report() = 0;
|
||||
virtual void stop_report() = 0;
|
||||
|
||||
virtual void handle_variable(uint16_t usage_page, uint16_t usage, int64_t state) = 0;
|
||||
virtual void handle_array(uint16_t usage_page, uint16_t usage) = 0;
|
||||
};
|
||||
|
||||
class USBHIDDriver final : public USBClassDriver
|
||||
{
|
||||
BAN_NON_COPYABLE(USBHIDDriver);
|
||||
BAN_NON_MOVABLE(USBHIDDriver);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<USBHIDDriver>> create(USBDevice&, const USBDevice::InterfaceDescriptor&, uint8_t interface_index);
|
||||
|
||||
void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id) override;
|
||||
|
||||
private:
|
||||
USBHIDDriver(USBDevice&, const USBDevice::InterfaceDescriptor&, uint8_t interface_index);
|
||||
~USBHIDDriver();
|
||||
|
||||
BAN::ErrorOr<void> initialize();
|
||||
|
||||
void forward_collection_inputs(const USBHID::Collection&, BAN::ConstByteSpan& data, size_t bit_offset);
|
||||
|
||||
private:
|
||||
USBDevice& m_device;
|
||||
USBDevice::InterfaceDescriptor m_interface;
|
||||
const uint8_t m_interface_index;
|
||||
|
||||
uint8_t m_endpoint_id { 0 };
|
||||
USBHID::Collection m_collection;
|
||||
BAN::RefPtr<USBHIDDevice> m_hid_device;
|
||||
|
||||
friend class BAN::UniqPtr<USBHIDDriver>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/USB/HID/HIDDriver.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBKeyboard final : public USBHIDDevice
|
||||
{
|
||||
BAN_NON_COPYABLE(USBKeyboard);
|
||||
BAN_NON_MOVABLE(USBKeyboard);
|
||||
|
||||
public:
|
||||
void start_report() override;
|
||||
void stop_report() override;
|
||||
|
||||
void handle_variable(uint16_t usage_page, uint16_t usage, int64_t state) override;
|
||||
void handle_array(uint16_t usage_page, uint16_t usage) override;
|
||||
|
||||
private:
|
||||
USBKeyboard()
|
||||
: USBHIDDevice(InputDevice::Type::Keyboard)
|
||||
{}
|
||||
~USBKeyboard() = default;
|
||||
|
||||
private:
|
||||
BAN::Array<bool, 0x100> m_keyboard_state { false };
|
||||
BAN::Array<bool, 0x100> m_keyboard_state_temp { false };
|
||||
uint8_t m_toggle_mask { 0 };
|
||||
|
||||
friend class BAN::RefPtr<USBKeyboard>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/USB/HID/HIDDriver.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBMouse final : public USBHIDDevice
|
||||
{
|
||||
BAN_NON_COPYABLE(USBMouse);
|
||||
BAN_NON_MOVABLE(USBMouse);
|
||||
|
||||
public:
|
||||
void start_report() override;
|
||||
void stop_report() override;
|
||||
|
||||
void handle_variable(uint16_t usage_page, uint16_t usage, int64_t state) override;
|
||||
void handle_array(uint16_t usage_page, uint16_t usage) override;
|
||||
|
||||
private:
|
||||
USBMouse()
|
||||
: USBHIDDevice(InputDevice::Type::Mouse)
|
||||
{}
|
||||
~USBMouse() = default;
|
||||
|
||||
private:
|
||||
BAN::Array<bool, 5> m_button_state { false };
|
||||
BAN::Array<bool, 5> m_button_state_temp { false };
|
||||
int64_t m_pointer_x { 0 };
|
||||
int64_t m_pointer_y { 0 };
|
||||
int64_t m_wheel { 0 };
|
||||
|
||||
friend class BAN::RefPtr<USBMouse>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/PCI.h>
|
||||
#include <kernel/USB/Controller.h>
|
||||
#include <kernel/USB/Definitions.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class USBManager
|
||||
{
|
||||
BAN_NON_COPYABLE(USBManager);
|
||||
BAN_NON_MOVABLE(USBManager);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<void> initialize();
|
||||
static USBManager& get();
|
||||
|
||||
BAN::ErrorOr<void> add_controller(PCI::Device& pci_device);
|
||||
|
||||
private:
|
||||
USBManager() = default;
|
||||
|
||||
private:
|
||||
BAN::Vector<BAN::UniqPtr<USBController>> m_controllers;
|
||||
|
||||
friend class BAN::UniqPtr<USBManager>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <kernel/Lock/Mutex.h>
|
||||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
#include <kernel/USB/XHCI/Definitions.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class XHCIDevice;
|
||||
|
||||
class XHCIController : public USBController, public Interruptable
|
||||
{
|
||||
BAN_NON_COPYABLE(XHCIController);
|
||||
BAN_NON_MOVABLE(XHCIController);
|
||||
|
||||
public:
|
||||
struct Port
|
||||
{
|
||||
uint8_t revision_major { 0 };
|
||||
uint8_t revision_minor { 0 };
|
||||
uint8_t slot_type { 0 };
|
||||
uint8_t slot_id { 0 };
|
||||
|
||||
uint64_t speed_id_to_speed[0x10] {
|
||||
0,
|
||||
12'000'000,
|
||||
1'500'000,
|
||||
480'000'000,
|
||||
5'000'000'000,
|
||||
10'000'000'000,
|
||||
5'000'000'000,
|
||||
10'000'000'000,
|
||||
};
|
||||
};
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<XHCIController>> initialize(PCI::Device&);
|
||||
|
||||
void handle_irq() final override;
|
||||
|
||||
private:
|
||||
XHCIController(PCI::Device& pci_device);
|
||||
~XHCIController();
|
||||
|
||||
BAN::ErrorOr<void> initialize_impl();
|
||||
BAN::ErrorOr<void> initialize_ports();
|
||||
BAN::ErrorOr<void> initialize_primary_interrupter();
|
||||
|
||||
BAN::ErrorOr<void> reset_controller();
|
||||
|
||||
void port_updater_task();
|
||||
|
||||
BAN::ErrorOr<void> initialize_slot(int port_index);
|
||||
|
||||
BAN::ErrorOr<XHCI::TRB> send_command(const XHCI::TRB&);
|
||||
void advance_command_enqueue();
|
||||
|
||||
bool context_size_set() { return capability_regs().hccparams1.context_size; }
|
||||
|
||||
const Port& port(uint32_t port_id) const { return m_ports[port_id - 1]; }
|
||||
|
||||
volatile XHCI::CapabilityRegs& capability_regs();
|
||||
volatile XHCI::OperationalRegs& operational_regs();
|
||||
volatile XHCI::RuntimeRegs& runtime_regs();
|
||||
volatile uint32_t& doorbell_reg(uint32_t slot_id);
|
||||
volatile uint64_t& dcbaa_reg(uint32_t slot_id);
|
||||
|
||||
const volatile XHCI::TRB& current_event_trb();
|
||||
|
||||
private:
|
||||
static constexpr uint32_t m_command_ring_trb_count = 256;
|
||||
static constexpr uint32_t m_event_ring_trb_count = 252;
|
||||
|
||||
Mutex m_mutex;
|
||||
|
||||
Process* m_port_updater { nullptr };
|
||||
Semaphore m_port_semaphore;
|
||||
BAN::Atomic<bool> m_port_changed { false };
|
||||
|
||||
PCI::Device& m_pci_device;
|
||||
BAN::UniqPtr<PCI::BarRegion> m_configuration_bar;
|
||||
BAN::UniqPtr<DMARegion> m_dcbaa_region;
|
||||
|
||||
BAN::UniqPtr<DMARegion> m_command_ring_region;
|
||||
uint32_t m_command_enqueue { 0 };
|
||||
bool m_command_cycle { 1 };
|
||||
|
||||
BAN::UniqPtr<DMARegion> m_event_ring_region;
|
||||
uint32_t m_event_dequeue { 0 };
|
||||
bool m_event_cycle { 1 };
|
||||
|
||||
BAN::Vector<XHCI::TRB> m_command_completions;
|
||||
|
||||
BAN::Vector<Port> m_ports;
|
||||
BAN::Vector<BAN::UniqPtr<XHCIDevice>> m_slots;
|
||||
|
||||
friend class XHCIDevice;
|
||||
friend class BAN::UniqPtr<XHCIController>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,626 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Kernel::XHCI
|
||||
{
|
||||
|
||||
struct CapabilityRegs
|
||||
{
|
||||
uint32_t caplength : 8;
|
||||
uint32_t : 8;
|
||||
uint32_t minor_revision : 8;
|
||||
uint32_t major_revision : 8;
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hcsparams1_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t max_slots : 8;
|
||||
uint32_t max_interrupters : 11;
|
||||
uint32_t : 5;
|
||||
uint32_t max_ports : 8;
|
||||
} hcsparams1;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hcsparams2_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t isochronous_scheduling_threshold : 4;
|
||||
uint32_t event_ring_segment_table_max : 4;
|
||||
uint32_t : 13;
|
||||
uint32_t max_scratchpad_buffers_hi : 5;
|
||||
uint32_t scratchpad_restore : 1;
|
||||
uint32_t max_scratchpad_buffers_lo : 5;
|
||||
} hcsparams2;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hcsparams3_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t u1_device_exit_latency : 8;
|
||||
uint32_t : 8;
|
||||
uint32_t u2_device_exit_latency : 16;
|
||||
} hcsparams3;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hccparams1_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t addressing_capability64 : 1;
|
||||
uint32_t bw_negotation_capability : 1;
|
||||
uint32_t context_size : 1;
|
||||
uint32_t port_power_control : 1;
|
||||
uint32_t port_indicators : 1;
|
||||
uint32_t light_hc_reset_capability : 1;
|
||||
uint32_t latency_tolerance_messaging_capability : 1;
|
||||
uint32_t no_secondary_sid_support : 1;
|
||||
uint32_t parse_all_event_data : 1;
|
||||
uint32_t stopped_short_packet_capability : 1;
|
||||
uint32_t stopped_edtla_capability : 1;
|
||||
uint32_t contiguous_frame_id_capability : 1;
|
||||
uint32_t maximum_primary_stream_array_size : 4;
|
||||
uint32_t xhci_extended_capabilities_pointer : 16;
|
||||
} hccparams1;
|
||||
};
|
||||
|
||||
uint32_t dboff;
|
||||
uint32_t rstoff;
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t hccparams2_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t u3_entry_capability : 1;
|
||||
uint32_t configure_endpoint_command_max_latency_too_large_capability : 1;
|
||||
uint32_t force_save_context_capability : 1;
|
||||
uint32_t compliance_transition_capability : 1;
|
||||
uint32_t large_esit_payload_capability : 1;
|
||||
uint32_t configuration_information_capability : 1;
|
||||
uint32_t extended_tbc_capability : 1;
|
||||
uint32_t extended_tbc_trb_status_capability : 1;
|
||||
uint32_t get_set_extended_property_capability : 1;
|
||||
uint32_t virtualiaztion_based_trusted_io_capability : 1;
|
||||
uint32_t : 22;
|
||||
} hccparams2;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(CapabilityRegs) == 0x20);
|
||||
|
||||
struct PortRegs
|
||||
{
|
||||
uint32_t portsc;
|
||||
uint32_t portmsc;
|
||||
uint32_t portli;
|
||||
uint32_t porthlpmc;
|
||||
};
|
||||
static_assert(sizeof(PortRegs) == 0x10);
|
||||
|
||||
struct OperationalRegs
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t usbcmd_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t run_stop : 1;
|
||||
uint32_t host_controller_reset : 1;
|
||||
uint32_t interrupter_enable : 1;
|
||||
uint32_t host_system_error_enable : 1;
|
||||
uint32_t : 3;
|
||||
uint32_t light_host_controller_reset : 1;
|
||||
uint32_t controller_save_state : 1;
|
||||
uint32_t controller_restore_state : 1;
|
||||
uint32_t enable_wrap_event : 1;
|
||||
uint32_t enable_u3_mfindex_stop : 1;
|
||||
uint32_t : 1;
|
||||
uint32_t cem_enable : 1;
|
||||
uint32_t extended_tbc_enable : 1;
|
||||
uint32_t extended_tbc_trb_status_enable : 1;
|
||||
uint32_t vtio_enable : 1;
|
||||
uint32_t : 15;
|
||||
} usbcmd;
|
||||
};
|
||||
|
||||
uint32_t usbsts;
|
||||
uint32_t pagesize;
|
||||
uint32_t __reserved0[2];
|
||||
uint32_t dnctrl;
|
||||
uint32_t crcr_lo;
|
||||
uint32_t crcr_hi;
|
||||
uint32_t __reserved1[4];
|
||||
uint32_t dcbaap_lo;
|
||||
uint32_t dcbaap_hi;
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t config_raw;
|
||||
struct
|
||||
{
|
||||
uint32_t max_device_slots_enabled : 8;
|
||||
uint32_t u3_entry_enable : 1;
|
||||
uint32_t configuration_information_enable : 1;
|
||||
uint32_t : 22;
|
||||
} config;
|
||||
};
|
||||
|
||||
uint32_t __reserved2[241];
|
||||
PortRegs ports[];
|
||||
};
|
||||
static_assert(sizeof(OperationalRegs) == 0x400);
|
||||
|
||||
struct InterrupterRegs
|
||||
{
|
||||
uint32_t iman;
|
||||
uint32_t imod;
|
||||
uint32_t erstsz;
|
||||
uint32_t __reserved;
|
||||
uint64_t erstba;
|
||||
uint64_t erdp;
|
||||
};
|
||||
static_assert(sizeof(InterrupterRegs) == 0x20);
|
||||
|
||||
struct RuntimeRegs
|
||||
{
|
||||
uint32_t mfindex;
|
||||
uint32_t __reserved[7];
|
||||
InterrupterRegs irs[];
|
||||
};
|
||||
static_assert(sizeof(RuntimeRegs) == 0x20);
|
||||
|
||||
struct TRB
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint64_t parameter;
|
||||
uint32_t status;
|
||||
uint16_t cycle : 1;
|
||||
uint16_t ent : 1;
|
||||
uint16_t : 8;
|
||||
uint16_t trb_type : 6;
|
||||
uint16_t control;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t dword0;
|
||||
uint32_t dword1;
|
||||
uint32_t dword2;
|
||||
uint32_t dword3;
|
||||
} raw;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t data_buffer_pointer : 64;
|
||||
|
||||
uint32_t trb_transfer_length : 17;
|
||||
uint32_t td_size : 5;
|
||||
uint32_t interrupt_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t evaluate_next_trb : 1;
|
||||
uint32_t interrupt_on_short_packet : 1;
|
||||
uint32_t no_snoop : 1;
|
||||
uint32_t chain_bit : 1;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t immediate_data : 1;
|
||||
uint32_t : 2;
|
||||
uint32_t block_event_interrupt : 1;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 16;
|
||||
} normal;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t bmRequestType : 8;
|
||||
uint32_t bRequest : 8;
|
||||
uint32_t wValue : 16;
|
||||
|
||||
uint32_t wIndex : 16;
|
||||
uint32_t wLength : 16;
|
||||
|
||||
uint32_t trb_transfer_length : 17;
|
||||
uint32_t : 5;
|
||||
uint32_t interrupt_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 4;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t immediate_data : 1;
|
||||
uint32_t : 3;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t transfer_type : 2;
|
||||
uint32_t : 14;
|
||||
} setup_stage;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t data_buffer_pointer : 64;
|
||||
|
||||
uint32_t trb_transfer_length : 17;
|
||||
uint32_t td_size : 5;
|
||||
uint32_t interrupt_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t evaluate_next_trb : 1;
|
||||
uint32_t interrupt_on_short_packet : 1;
|
||||
uint32_t no_snoop : 1;
|
||||
uint32_t chain_bit : 1;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t immediate_data : 1;
|
||||
uint32_t : 3;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t direction : 1;
|
||||
uint32_t : 15;
|
||||
} data_stage;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
|
||||
uint32_t : 22;
|
||||
uint32_t interrupter_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t evaluate_next_trb : 1;
|
||||
uint32_t : 2;
|
||||
uint32_t chain_bit : 1;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t : 4;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t direction : 1;
|
||||
uint32_t : 15;
|
||||
} status_stage;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t trb_pointer : 64;
|
||||
|
||||
uint32_t trb_transfer_length : 24;
|
||||
uint32_t completion_code : 8;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 1;
|
||||
uint32_t event_data : 1;
|
||||
uint32_t : 7;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t endpoint_id : 5;
|
||||
uint32_t : 3;
|
||||
uint32_t slot_id : 8;
|
||||
} transfer_event;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t command_trb_pointer : 64;
|
||||
uint32_t command_completion_parameter : 24;
|
||||
uint32_t completion_code : 8;
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t vf_id : 8;
|
||||
uint32_t slot_id : 8;
|
||||
} command_completion_event;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t : 24;
|
||||
uint32_t port_id : 8;
|
||||
uint32_t : 32;
|
||||
uint32_t : 24;
|
||||
uint32_t completion_code : 8;
|
||||
uint32_t cycle : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 16;
|
||||
} port_status_chage_event;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t cycle : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t slot_type : 5;
|
||||
uint32_t : 11;
|
||||
} enable_slot_command;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t cycle : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 8;
|
||||
uint32_t slot_id : 8;
|
||||
} disable_slot_command;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t input_context_pointer : 64;
|
||||
uint32_t : 32;
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 8;
|
||||
uint32_t block_set_address_request : 1;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 8;
|
||||
uint32_t slot_id : 8;
|
||||
} address_device_command;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t input_context_pointer : 64;
|
||||
uint32_t : 32;
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t : 8;
|
||||
uint32_t deconfigure : 1;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 8;
|
||||
uint32_t slot_id : 8;
|
||||
} configure_endpoint_command;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t ring_segment_ponter : 64;
|
||||
|
||||
uint32_t : 22;
|
||||
uint32_t interrupter_target : 10;
|
||||
|
||||
uint32_t cycle_bit : 1;
|
||||
uint32_t toggle_cycle : 1;
|
||||
uint32_t : 2;
|
||||
uint32_t chain_bit : 1;
|
||||
uint32_t interrupt_on_completion : 1;
|
||||
uint32_t : 4;
|
||||
uint32_t trb_type : 6;
|
||||
uint32_t : 16;
|
||||
} link_trb;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(TRB) == 0x10);
|
||||
|
||||
struct EventRingTableEntry
|
||||
{
|
||||
uint64_t rsba;
|
||||
uint32_t rsz;
|
||||
uint32_t __reserved;
|
||||
};
|
||||
static_assert(sizeof(EventRingTableEntry) == 0x10);
|
||||
|
||||
struct ExtendedCap
|
||||
{
|
||||
uint32_t capability_id : 8;
|
||||
uint32_t next_capability : 8;
|
||||
uint32_t : 16;
|
||||
};
|
||||
static_assert(sizeof(ExtendedCap) == 4);
|
||||
|
||||
struct SupportedPrococolCap
|
||||
{
|
||||
uint32_t capability_id : 8;
|
||||
uint32_t next_capability : 8;
|
||||
uint32_t minor_revision : 8;
|
||||
uint32_t major_revision : 8;
|
||||
uint32_t name_string : 32;
|
||||
uint32_t compatible_port_offset : 8;
|
||||
uint32_t compatible_port_count : 8;
|
||||
uint32_t protocol_defied : 12;
|
||||
uint32_t protocol_speed_id_count : 4;
|
||||
uint32_t protocol_slot_type : 5;
|
||||
uint32_t : 27;
|
||||
};
|
||||
static_assert(sizeof(SupportedPrococolCap) == 0x10);
|
||||
|
||||
struct SlotContext
|
||||
{
|
||||
uint32_t route_string : 20;
|
||||
uint32_t speed : 4;
|
||||
uint32_t : 1;
|
||||
uint32_t multi_tt : 1;
|
||||
uint32_t hub : 1;
|
||||
uint32_t context_entries : 5;
|
||||
|
||||
uint16_t max_exit_latency;
|
||||
uint8_t root_hub_port_number;
|
||||
uint8_t number_of_ports;
|
||||
|
||||
uint32_t parent_hub_slot_id : 8;
|
||||
uint32_t parent_port_number : 8;
|
||||
uint32_t tt_think_time : 2;
|
||||
uint32_t : 4;
|
||||
uint32_t interrupter_target : 10;
|
||||
|
||||
uint32_t usb_device_address : 8;
|
||||
uint32_t : 19;
|
||||
uint32_t slot_state : 5;
|
||||
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
};
|
||||
static_assert(sizeof(SlotContext) == 0x20);
|
||||
|
||||
struct EndpointContext
|
||||
{
|
||||
uint32_t endpoint_state : 3;
|
||||
uint32_t : 5;
|
||||
uint32_t mult : 2;
|
||||
uint32_t max_primary_streams : 5;
|
||||
uint32_t linear_stream_array : 1;
|
||||
uint32_t interval : 8;
|
||||
uint32_t max_esit_payload_hi : 8;
|
||||
|
||||
uint32_t : 1;
|
||||
uint32_t error_count : 2;
|
||||
uint32_t endpoint_type : 3;
|
||||
uint32_t : 1;
|
||||
uint32_t host_initiate_disable : 1;
|
||||
uint32_t max_burst_size : 8;
|
||||
uint32_t max_packet_size : 16;
|
||||
|
||||
// LSB is dequeue cycle state
|
||||
uint64_t tr_dequeue_pointer;
|
||||
|
||||
uint32_t average_trb_length : 16;
|
||||
uint32_t max_esit_payload_lo : 16;
|
||||
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
};
|
||||
static_assert(sizeof(EndpointContext) == 0x20);
|
||||
|
||||
struct InputControlContext
|
||||
{
|
||||
uint32_t drop_context_flags;
|
||||
uint32_t add_context_flags;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint32_t : 32;
|
||||
uint8_t configuration_value;
|
||||
uint8_t interface_number;
|
||||
uint8_t alternate_setting;
|
||||
uint8_t : 8;
|
||||
};
|
||||
static_assert(sizeof(InputControlContext) == 0x20);
|
||||
|
||||
enum USBSTS
|
||||
{
|
||||
HCHalted = 1 << 0,
|
||||
HostSystemError = 1 << 2,
|
||||
EventInterrupt = 1 << 3,
|
||||
PortChangeDetect = 1 << 4,
|
||||
SaveStateStatus = 1 << 8,
|
||||
RstoreStateStatus = 1 << 9,
|
||||
SaveRestoreError = 1 << 10,
|
||||
ControllerNotReady = 1 << 11,
|
||||
HostControllerError = 1 << 12,
|
||||
};
|
||||
|
||||
enum CRCR : uint32_t
|
||||
{
|
||||
RingCycleState = 1 << 0,
|
||||
CommandStop = 1 << 1,
|
||||
CommandAbort = 1 << 2,
|
||||
CommandRingRunning = 1 << 3,
|
||||
};
|
||||
|
||||
enum IMAN : uint32_t
|
||||
{
|
||||
InterruptPending = 1 << 0,
|
||||
InterruptEnable = 1 << 1,
|
||||
};
|
||||
|
||||
enum ERDP
|
||||
{
|
||||
EventHandlerBusy = 1 << 3,
|
||||
};
|
||||
|
||||
enum PORTSC : uint32_t
|
||||
{
|
||||
CCS = 1u << 0,
|
||||
PED = 1u << 1,
|
||||
OCA = 1u << 3,
|
||||
PR = 1u << 4,
|
||||
PP = 1u << 9,
|
||||
LWS = 1u << 16,
|
||||
CSC = 1u << 17,
|
||||
PEC = 1u << 18,
|
||||
WRC = 1u << 19,
|
||||
OCC = 1u << 20,
|
||||
PRC = 1u << 21,
|
||||
PLC = 1u << 22,
|
||||
CEC = 1u << 23,
|
||||
CAS = 1u << 24,
|
||||
WCE = 1u << 25,
|
||||
WDE = 1u << 26,
|
||||
WOE = 1u << 27,
|
||||
DR = 1u << 30,
|
||||
WPR = 1u << 31,
|
||||
|
||||
PLS_SHIFT = 5,
|
||||
PLS_MASK = 0xF,
|
||||
PORT_SPEED_SHIFT = 10,
|
||||
PORT_SPEED_MASK = 0xF,
|
||||
PIC_SHIFT = 14,
|
||||
PIC_MASK = 0x3,
|
||||
};
|
||||
|
||||
enum ExtendedCapabilityID : uint8_t
|
||||
{
|
||||
USBLegacySupport = 1,
|
||||
SupportedProtocol = 2,
|
||||
ExtendedPowerManagement = 3,
|
||||
IOVirtualization = 4,
|
||||
MessageInterrupt = 5,
|
||||
LocalMemory = 6,
|
||||
USBDebugCapability = 10,
|
||||
ExtendedMessageInterrupt = 17,
|
||||
};
|
||||
|
||||
enum TRBType
|
||||
{
|
||||
Normal = 1,
|
||||
SetupStage = 2,
|
||||
DataStage = 3,
|
||||
StatusStage = 4,
|
||||
|
||||
Link = 6,
|
||||
|
||||
EnableSlotCommand = 9,
|
||||
DisableSlotCommand = 10,
|
||||
AddressDeviceCommand = 11,
|
||||
ConfigureEndpointCommand = 12,
|
||||
EvaluateContextCommand = 13,
|
||||
ResetEndpointCommand = 14,
|
||||
StopEndpointCommand = 15,
|
||||
SetTRDequeuePointerCommand = 16,
|
||||
ResetDeviceCommand = 17,
|
||||
ForceEventCommand = 18,
|
||||
NegotiateBandwidthCommand = 19,
|
||||
SetLatencyToleranceValueCommand = 20,
|
||||
GetPortBandwidthCommand = 21,
|
||||
ForceHeaderCommand = 22,
|
||||
NoOpCommand = 23,
|
||||
GetExtendedPropertyCommand = 24,
|
||||
SetExtendedPropertyCommand = 25,
|
||||
|
||||
TransferEvent = 32,
|
||||
CommandCompletionEvent = 33,
|
||||
PortStatusChangeEvent = 34,
|
||||
BandwidthRequestEvent = 35,
|
||||
DoorbellEvent = 36,
|
||||
HostControllerEvent = 37,
|
||||
DeviceNotificationEvent = 38,
|
||||
MFINDEXWrapEvent = 39,
|
||||
};
|
||||
|
||||
enum EndpointType
|
||||
{
|
||||
IsochOut = 1,
|
||||
BulkOut = 2,
|
||||
InterruptOut = 3,
|
||||
Control = 4,
|
||||
IsochIn = 5,
|
||||
BulkIn = 6,
|
||||
InterruptIn = 7,
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Array.h>
|
||||
#include <BAN/RefPtr.h>
|
||||
|
||||
#include <kernel/USB/Device.h>
|
||||
#include <kernel/USB/XHCI/Controller.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class XHCIDevice final : public USBDevice
|
||||
{
|
||||
BAN_NON_COPYABLE(XHCIDevice);
|
||||
BAN_NON_MOVABLE(XHCIDevice);
|
||||
|
||||
public:
|
||||
struct Endpoint
|
||||
{
|
||||
BAN::UniqPtr<DMARegion> transfer_ring;
|
||||
uint32_t max_packet_size { 0 };
|
||||
uint32_t dequeue_index { 0 };
|
||||
uint32_t enqueue_index { 0 };
|
||||
bool cycle_bit { 1 };
|
||||
|
||||
Mutex mutex;
|
||||
volatile uint32_t transfer_count { 0 };
|
||||
volatile XHCI::TRB completion_trb;
|
||||
|
||||
BAN::UniqPtr<DMARegion> data_region;
|
||||
void(XHCIDevice::*callback)(XHCI::TRB);
|
||||
};
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<XHCIDevice>> create(XHCIController&, uint32_t port_id, uint32_t slot_id);
|
||||
|
||||
BAN::ErrorOr<void> initialize_endpoint(const USBEndpointDescriptor&) override;
|
||||
BAN::ErrorOr<size_t> send_request(const USBDeviceRequest&, paddr_t buffer) override;
|
||||
|
||||
void on_transfer_event(const volatile XHCI::TRB&);
|
||||
|
||||
protected:
|
||||
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();
|
||||
BAN::ErrorOr<void> update_actual_max_packet_size();
|
||||
|
||||
void on_interrupt_endpoint_event(XHCI::TRB);
|
||||
|
||||
void advance_endpoint_enqueue(Endpoint&, bool chain);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t m_transfer_ring_trb_count = PAGE_SIZE / sizeof(XHCI::TRB);
|
||||
|
||||
XHCIController& m_controller;
|
||||
const uint32_t m_port_id;
|
||||
const uint32_t m_slot_id;
|
||||
|
||||
Mutex m_mutex;
|
||||
|
||||
BAN::UniqPtr<DMARegion> m_input_context;
|
||||
BAN::UniqPtr<DMARegion> m_output_context;
|
||||
|
||||
BAN::Array<Endpoint, 31> m_endpoints;
|
||||
|
||||
friend class BAN::UniqPtr<XHCIDevice>;
|
||||
};
|
||||
|
||||
}
|
|
@ -119,10 +119,4 @@ namespace Kernel
|
|||
MUST(static_cast<TmpDirectoryInode*>(root_inode().ptr())->link_inode(*inode, path));
|
||||
}
|
||||
|
||||
int DevFileSystem::get_next_input_device() const
|
||||
{
|
||||
static BAN::Atomic<dev_t> next_dev = 0;
|
||||
return next_dev++;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
#include <kernel/Device/DeviceNumbers.h>
|
||||
#include <kernel/Input/InputDevice.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
|
||||
#include <LibInput/KeyEvent.h>
|
||||
#include <LibInput/MouseEvent.h>
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static BAN::Atomic<uint16_t> s_next_keyboard { 0 };
|
||||
static BAN::Atomic<uint16_t> s_next_mouse { 0 };
|
||||
|
||||
static const char* get_name_format(InputDevice::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case InputDevice::Type::Keyboard:
|
||||
return "keyboard{}";
|
||||
case InputDevice::Type::Mouse:
|
||||
return "mouse{}";
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
static dev_t get_rdev(InputDevice::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case InputDevice::Type::Keyboard:
|
||||
return makedev(DeviceNumber::Keyboard, s_next_keyboard++);
|
||||
case InputDevice::Type::Mouse:
|
||||
return makedev(DeviceNumber::Mouse, s_next_mouse++);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
static size_t get_event_size(InputDevice::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case InputDevice::Type::Keyboard:
|
||||
return sizeof(LibInput::RawKeyEvent);
|
||||
case InputDevice::Type::Mouse:
|
||||
return sizeof(LibInput::MouseEvent);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
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_event_size(get_event_size(type))
|
||||
{
|
||||
MUST(m_event_buffer.resize(m_event_size * m_max_event_count, 0));
|
||||
}
|
||||
|
||||
void InputDevice::add_event(BAN::ConstByteSpan event)
|
||||
{
|
||||
SpinLockGuard _(m_event_lock);
|
||||
ASSERT(event.size() == m_event_size);
|
||||
|
||||
if (m_event_count == m_max_event_count)
|
||||
{
|
||||
m_event_tail = (m_event_tail + 1) % m_max_event_count;
|
||||
m_event_count--;
|
||||
}
|
||||
|
||||
memcpy(&m_event_buffer[m_event_head * m_event_size], event.data(), m_event_size);
|
||||
m_event_head = (m_event_head + 1) % m_max_event_count;
|
||||
m_event_count++;
|
||||
|
||||
m_event_semaphore.unblock();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> InputDevice::read_impl(off_t, BAN::ByteSpan buffer)
|
||||
{
|
||||
if (buffer.size() < m_event_size)
|
||||
return BAN::Error::from_errno(ENOBUFS);
|
||||
|
||||
auto state = m_event_lock.lock();
|
||||
while (m_event_count == 0)
|
||||
{
|
||||
m_event_lock.unlock(state);
|
||||
{
|
||||
LockFreeGuard _(m_mutex);
|
||||
TRY(Thread::current().block_or_eintr_indefinite(m_event_semaphore));
|
||||
}
|
||||
state = m_event_lock.lock();
|
||||
}
|
||||
|
||||
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--;
|
||||
|
||||
m_event_lock.unlock(state);
|
||||
|
||||
return m_event_size;
|
||||
}
|
||||
|
||||
}
|
|
@ -89,9 +89,9 @@ namespace Kernel::Input
|
|||
uint8_t PS2Controller::get_device_index(PS2Device* device) const
|
||||
{
|
||||
ASSERT(device);
|
||||
if (m_devices[0] && device == m_devices[0].ptr())
|
||||
if (m_devices[0].ptr() == device)
|
||||
return 0;
|
||||
if (m_devices[1] && device == m_devices[1].ptr())
|
||||
if (m_devices[1].ptr() == device)
|
||||
return 1;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
@ -359,12 +359,12 @@ namespace Kernel::Input
|
|||
TRY(send_command(PS2::Command::WRITE_CONFIG, config));
|
||||
|
||||
// Send device initialization sequence after interrupts are enabled
|
||||
for (uint8_t device = 0; device < 2; device++)
|
||||
for (uint8_t i = 0; i < 2; i++)
|
||||
{
|
||||
if (!m_devices[device])
|
||||
if (!m_devices[i])
|
||||
continue;
|
||||
m_devices[device]->send_initialize();
|
||||
DevFileSystem::get().add_device(m_devices[device]);
|
||||
m_devices[i]->send_initialize();
|
||||
DevFileSystem::get().add_device(m_devices[i]);
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
#include <kernel/Device/DeviceNumbers.h>
|
||||
#include <kernel/FS/DevFS/FileSystem.h>
|
||||
#include <kernel/Input/PS2/Config.h>
|
||||
#include <kernel/Input/PS2/Device.h>
|
||||
#include <kernel/IO.h>
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
namespace Kernel::Input
|
||||
{
|
||||
|
||||
PS2Device::PS2Device(PS2Controller& controller)
|
||||
: CharacterDevice(0440, 0, 901)
|
||||
, m_rdev(makedev(DeviceNumber::Input, DevFileSystem::get().get_next_input_device()))
|
||||
, m_name(MUST(BAN::String::formatted("input{}", minor(m_rdev))))
|
||||
PS2Device::PS2Device(PS2Controller& controller, InputDevice::Type type)
|
||||
: InputDevice(type)
|
||||
, m_controller(controller)
|
||||
{ }
|
||||
|
||||
|
|
|
@ -4,20 +4,18 @@
|
|||
#include <kernel/Input/PS2/Keyboard.h>
|
||||
#include <kernel/Thread.h>
|
||||
#include <LibInput/KeyboardLayout.h>
|
||||
#include <LibInput/KeyEvent.h>
|
||||
|
||||
namespace Kernel::Input
|
||||
{
|
||||
|
||||
BAN::ErrorOr<PS2Keyboard*> PS2Keyboard::create(PS2Controller& controller)
|
||||
BAN::ErrorOr<BAN::RefPtr<PS2Keyboard>> PS2Keyboard::create(PS2Controller& controller)
|
||||
{
|
||||
PS2Keyboard* keyboard = new PS2Keyboard(controller);
|
||||
if (keyboard == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
return keyboard;
|
||||
return TRY(BAN::RefPtr<PS2Keyboard>::create(controller));
|
||||
}
|
||||
|
||||
PS2Keyboard::PS2Keyboard(PS2Controller& controller)
|
||||
: PS2Device(controller)
|
||||
: PS2Device(controller, InputDevice::Type::Keyboard)
|
||||
{ }
|
||||
|
||||
void PS2Keyboard::send_initialize()
|
||||
|
@ -166,17 +164,7 @@ namespace Kernel::Input
|
|||
RawKeyEvent event;
|
||||
event.modifier = m_modifiers | (released ? 0 : KeyModifier::Pressed);
|
||||
event.keycode = keycode.value();
|
||||
|
||||
SpinLockGuard _(m_event_lock);
|
||||
|
||||
if (m_event_queue.full())
|
||||
{
|
||||
dwarnln("PS/2 event queue full");
|
||||
m_event_queue.pop();
|
||||
}
|
||||
m_event_queue.push(event);
|
||||
|
||||
m_semaphore.unblock();
|
||||
add_event(BAN::ConstByteSpan::from(event));
|
||||
}
|
||||
|
||||
void PS2Keyboard::update_leds()
|
||||
|
@ -193,30 +181,4 @@ namespace Kernel::Input
|
|||
append_command_queue(Command::SET_LEDS, new_leds, 0);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> PS2Keyboard::read_impl(off_t, BAN::ByteSpan buffer)
|
||||
{
|
||||
using LibInput::RawKeyEvent;
|
||||
|
||||
if (buffer.size() < sizeof(RawKeyEvent))
|
||||
return BAN::Error::from_errno(ENOBUFS);
|
||||
|
||||
auto state = m_event_lock.lock();
|
||||
while (m_event_queue.empty())
|
||||
{
|
||||
m_event_lock.unlock(state);
|
||||
{
|
||||
LockFreeGuard _(m_mutex);
|
||||
TRY(Thread::current().block_or_eintr_indefinite(m_semaphore));
|
||||
}
|
||||
state = m_event_lock.lock();
|
||||
}
|
||||
|
||||
buffer.as<RawKeyEvent>() = m_event_queue.front();
|
||||
m_event_queue.pop();
|
||||
|
||||
m_event_lock.unlock(state);
|
||||
|
||||
return sizeof(RawKeyEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <kernel/Input/PS2/Config.h>
|
||||
#include <kernel/Input/PS2/Mouse.h>
|
||||
#include <kernel/Thread.h>
|
||||
#include <LibInput/MouseEvent.h>
|
||||
|
||||
#define SET_MASK(byte, mask, on_off) ((on_off) ? ((byte) | (mask)) : ((byte) & ~(mask)))
|
||||
#define TOGGLE_MASK(byte, mask) ((byte) ^ (mask))
|
||||
|
@ -10,16 +11,13 @@
|
|||
namespace Kernel::Input
|
||||
{
|
||||
|
||||
BAN::ErrorOr<PS2Mouse*> PS2Mouse::create(PS2Controller& controller)
|
||||
BAN::ErrorOr<BAN::RefPtr<PS2Mouse>> PS2Mouse::create(PS2Controller& controller)
|
||||
{
|
||||
PS2Mouse* mouse = new PS2Mouse(controller);
|
||||
if (mouse == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
return mouse;
|
||||
return TRY(BAN::RefPtr<PS2Mouse>::create(controller));
|
||||
}
|
||||
|
||||
PS2Mouse::PS2Mouse(PS2Controller& controller)
|
||||
: PS2Device(controller)
|
||||
: PS2Device(controller, InputDevice::Type::Mouse)
|
||||
{ }
|
||||
|
||||
void PS2Mouse::send_initialize()
|
||||
|
@ -110,10 +108,6 @@ namespace Kernel::Input
|
|||
|
||||
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
|
||||
{
|
||||
|
@ -137,10 +131,11 @@ namespace Kernel::Input
|
|||
if ((new_button_mask & (1 << i)) == (m_button_mask & (1 << i)))
|
||||
continue;
|
||||
|
||||
auto& event = events[event_count++];
|
||||
MouseEvent event;
|
||||
event.type = MouseEventType::MouseButtonEvent;
|
||||
event.button_event.button = button_index_to_button(i);
|
||||
event.button_event.pressed = !!(new_button_mask & (1 << i));
|
||||
add_event(BAN::ConstByteSpan::from(event));
|
||||
}
|
||||
|
||||
m_button_mask = new_button_mask;
|
||||
|
@ -148,71 +143,20 @@ namespace Kernel::Input
|
|||
|
||||
if (rel_x || rel_y)
|
||||
{
|
||||
auto& event = events[event_count++];
|
||||
MouseEvent event;
|
||||
event.type = MouseEventType::MouseMoveEvent;
|
||||
event.move_event.rel_x = rel_x;
|
||||
event.move_event.rel_y = rel_y;
|
||||
add_event(BAN::ConstByteSpan::from(event));
|
||||
}
|
||||
|
||||
if (rel_z)
|
||||
{
|
||||
auto& event = events[event_count++];
|
||||
MouseEvent event;
|
||||
event.type = MouseEventType::MouseScrollEvent;
|
||||
event.scroll_event.scroll = rel_z;
|
||||
}
|
||||
|
||||
SpinLockGuard _(m_event_lock);
|
||||
|
||||
for (int i = 0; i < event_count; i++)
|
||||
{
|
||||
if (!m_event_queue.empty() && m_event_queue.back().type == events[i].type)
|
||||
{
|
||||
if (events[i].type == MouseEventType::MouseMoveEvent)
|
||||
{
|
||||
m_event_queue.back().move_event.rel_x += events[i].move_event.rel_x;
|
||||
m_event_queue.back().move_event.rel_y += events[i].move_event.rel_y;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (events[i].type == MouseEventType::MouseScrollEvent)
|
||||
{
|
||||
m_event_queue.back().scroll_event.scroll += events[i].scroll_event.scroll;
|
||||
continue;
|
||||
add_event(BAN::ConstByteSpan::from(event));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
using LibInput::MouseEvent;
|
||||
|
||||
if (buffer.size() < sizeof(MouseEvent))
|
||||
return BAN::Error::from_errno(ENOBUFS);
|
||||
|
||||
auto state = m_event_lock.lock();
|
||||
while (m_event_queue.empty())
|
||||
{
|
||||
m_event_lock.unlock(state);
|
||||
TRY(Thread::current().block_or_eintr_indefinite(m_semaphore));
|
||||
state = m_event_lock.lock();
|
||||
}
|
||||
|
||||
buffer.as<MouseEvent>() = m_event_queue.front();
|
||||
m_event_queue.pop();
|
||||
|
||||
m_event_lock.unlock(state);
|
||||
|
||||
return sizeof(MouseEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <kernel/Storage/ATA/AHCI/Controller.h>
|
||||
#include <kernel/Storage/ATA/ATAController.h>
|
||||
#include <kernel/Storage/NVMe/Controller.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
|
||||
#define INVALID_VENDOR 0xFFFF
|
||||
#define MULTI_FUNCTION 0x80
|
||||
|
@ -209,6 +210,20 @@ namespace Kernel::PCI
|
|||
dprintln("{}", res.error());
|
||||
break;
|
||||
}
|
||||
case 0x0C:
|
||||
{
|
||||
switch (pci_device.subclass())
|
||||
{
|
||||
case 0x03:
|
||||
if (auto res = USBManager::get().add_controller(pci_device); res.is_error())
|
||||
dprintln("{}", res.error());
|
||||
break;
|
||||
default:
|
||||
dprintln("unsupported serail bus controller (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace Kernel
|
|||
Process::create_kernel(
|
||||
[](void*)
|
||||
{
|
||||
auto file_or_error = VirtualFileSystem::get().file_from_absolute_path({ 0, 0, 0, 0 }, "/dev/input0"_sv, O_RDONLY);
|
||||
auto file_or_error = VirtualFileSystem::get().file_from_absolute_path({ 0, 0, 0, 0 }, "/dev/keyboard0"_sv, O_RDONLY);
|
||||
if (file_or_error.is_error())
|
||||
{
|
||||
dprintln("no input device found");
|
||||
|
|
|
@ -0,0 +1,327 @@
|
|||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/USB/Device.h>
|
||||
#include <kernel/USB/HID/HIDDriver.h>
|
||||
|
||||
#define DEBUG_USB 0
|
||||
#define USB_DUMP_DESCRIPTORS 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<void> USBDevice::initialize()
|
||||
{
|
||||
TRY(initialize_control_endpoint());
|
||||
|
||||
m_dma_buffer = TRY(DMARegion::create(1024));
|
||||
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0100;
|
||||
request.wIndex = 0;
|
||||
request.wLength = sizeof(USBDeviceDescriptor);
|
||||
auto transferred = TRY(send_request(request, m_dma_buffer->paddr()));
|
||||
|
||||
m_descriptor.descriptor = *reinterpret_cast<const USBDeviceDescriptor*>(m_dma_buffer->vaddr());
|
||||
if (transferred < sizeof(USBDeviceDescriptor) || transferred < m_descriptor.descriptor.bLength)
|
||||
{
|
||||
dprintln("invalid device descriptor response {}");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
dprintln_if(DEBUG_USB, "device has {} configurations", m_descriptor.descriptor.bNumConfigurations);
|
||||
|
||||
for (uint32_t i = 0; i < m_descriptor.descriptor.bNumConfigurations; i++)
|
||||
if (auto opt_configuration = parse_configuration(i); !opt_configuration.is_error())
|
||||
TRY(m_descriptor.configurations.push_back(opt_configuration.release_value()));
|
||||
|
||||
#if USB_DUMP_DESCRIPTORS
|
||||
const auto& descriptor = m_descriptor.descriptor;
|
||||
dprintln("device descriptor");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bcdUSB: {}", descriptor.bcdUSB);
|
||||
dprintln(" bDeviceClass: {}", descriptor.bDeviceClass);
|
||||
dprintln(" bDeviceSubClass: {}", descriptor.bDeviceSubClass);
|
||||
dprintln(" bDeviceProtocol: {}", descriptor.bDeviceProtocol);
|
||||
dprintln(" bMaxPacketSize0: {}", descriptor.bMaxPacketSize0);
|
||||
dprintln(" idVendor: {}", descriptor.idVendor);
|
||||
dprintln(" idProduct: {}", descriptor.idProduct);
|
||||
dprintln(" bcdDevice: {}", descriptor.bcdDevice);
|
||||
dprintln(" iManufacturer: {}", descriptor.iManufacturer);
|
||||
dprintln(" iProduct: {}", descriptor.iProduct);
|
||||
dprintln(" iSerialNumber: {}", descriptor.iSerialNumber);
|
||||
dprintln(" bNumConfigurations: {}", descriptor.bNumConfigurations);
|
||||
|
||||
for (const auto& configuration : m_descriptor.configurations)
|
||||
{
|
||||
const auto& descriptor = configuration.desciptor;
|
||||
dprintln(" configuration");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" wTotalLength: {}", descriptor.wTotalLength);
|
||||
dprintln(" bNumInterfaces: {}", descriptor.bNumInterfaces);
|
||||
dprintln(" bConfigurationValue: {}", descriptor.bConfigurationValue);
|
||||
dprintln(" iConfiguration: {}", descriptor.iConfiguration);
|
||||
dprintln(" bmAttributes: {}", descriptor.bmAttributes);
|
||||
dprintln(" bMaxPower: {}", descriptor.bMaxPower);
|
||||
|
||||
for (const auto& interface : configuration.interfaces)
|
||||
{
|
||||
const auto& descriptor = interface.descriptor;
|
||||
dprintln(" interface");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bInterfaceNumber: {}", descriptor.bInterfaceNumber);
|
||||
dprintln(" bAlternateSetting: {}", descriptor.bAlternateSetting);
|
||||
dprintln(" bNumEndpoints: {}", descriptor.bNumEndpoints);
|
||||
dprintln(" bInterfaceClass: {}", descriptor.bInterfaceClass);
|
||||
dprintln(" bInterfaceSubClass: {}", descriptor.bInterfaceSubClass);
|
||||
dprintln(" bInterfaceProtocol: {}", descriptor.bInterfaceProtocol);
|
||||
dprintln(" iInterface: {}", descriptor.iInterface);
|
||||
|
||||
for (const auto& endpoint : interface.endpoints)
|
||||
{
|
||||
const auto& descriptor = endpoint.descriptor;
|
||||
dprintln(" endpoint");
|
||||
dprintln(" bLength: {}", descriptor.bLength);
|
||||
dprintln(" bDescriptorType: {}", descriptor.bDescriptorType);
|
||||
dprintln(" bEndpointAddress: {}", descriptor.bEndpointAddress);
|
||||
dprintln(" bmAttributes: {}", descriptor.bmAttributes);
|
||||
dprintln(" wMaxPacketSize: {}", +descriptor.wMaxPacketSize);
|
||||
dprintln(" bInterval: {}", descriptor.bInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_descriptor.descriptor.bDeviceClass)
|
||||
{
|
||||
switch (static_cast<USB::DeviceBaseClass>(m_descriptor.descriptor.bDeviceClass))
|
||||
{
|
||||
case USB::DeviceBaseClass::CommunicationAndCDCControl:
|
||||
dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::Hub:
|
||||
dprintln_if(DEBUG_USB, "Found Hub device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::BillboardDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found BillboardDeviceClass device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::DiagnosticDevice:
|
||||
dprintln_if(DEBUG_USB, "Found DiagnosticDevice device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::Miscellaneous:
|
||||
dprintln_if(DEBUG_USB, "Found Miscellaneous device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::VendorSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found VendorSpecific device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
default:
|
||||
dprintln_if(DEBUG_USB, "Invalid device base class {2H}", m_descriptor.descriptor.bDeviceClass);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_descriptor.configurations.size(); i++)
|
||||
{
|
||||
const auto& configuration = m_descriptor.configurations[i];
|
||||
|
||||
{
|
||||
dprintln_if(DEBUG_USB, "Setting configuration 0x{2H}", configuration.desciptor.bConfigurationValue);
|
||||
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::SET_CONFIGURATION;
|
||||
request.wValue = configuration.desciptor.bConfigurationValue;
|
||||
request.wIndex = 0;
|
||||
request.wLength = 0;
|
||||
TRY(send_request(request, 0));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < configuration.interfaces.size(); j++)
|
||||
{
|
||||
const auto& interface = configuration.interfaces[j];
|
||||
|
||||
switch (static_cast<USB::InterfaceBaseClass>(interface.descriptor.bInterfaceClass))
|
||||
{
|
||||
case USB::InterfaceBaseClass::Audio:
|
||||
dprintln_if(DEBUG_USB, "Found Audio interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::CommunicationAndCDCControl:
|
||||
dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::HID:
|
||||
if (auto result = USBHIDDriver::create(*this, interface, j); !result.is_error())
|
||||
m_class_driver = result.release_value();
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Physical:
|
||||
dprintln_if(DEBUG_USB, "Found Physical interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Image:
|
||||
dprintln_if(DEBUG_USB, "Found Image interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Printer:
|
||||
dprintln_if(DEBUG_USB, "Found Printer interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::MassStorage:
|
||||
dprintln_if(DEBUG_USB, "Found MassStorage interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::CDCData:
|
||||
dprintln_if(DEBUG_USB, "Found CDCData interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::SmartCard:
|
||||
dprintln_if(DEBUG_USB, "Found SmartCard interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::ContentSecurity:
|
||||
dprintln_if(DEBUG_USB, "Found ContentSecurity interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Video:
|
||||
dprintln_if(DEBUG_USB, "Found Video interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::PersonalHealthcare:
|
||||
dprintln_if(DEBUG_USB, "Found PersonalHealthcare interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::AudioVideoDevice:
|
||||
dprintln_if(DEBUG_USB, "Found AudioVideoDevice interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::USBTypeCBridgeClass:
|
||||
dprintln_if(DEBUG_USB, "Found USBTypeCBridgeClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::USBBulkDisplayProtocolDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found USBBulkDisplayProtocolDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::MCTPOverUSBProtocolEndpointDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found MCTPOverUSBProtocolEndpointDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::I3CDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found I3CDeviceClass interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::DiagnosticDevice:
|
||||
dprintln_if(DEBUG_USB, "Found DiagnosticDevice interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::WirelessController:
|
||||
dprintln_if(DEBUG_USB, "Found WirelessController interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::Miscellaneous:
|
||||
dprintln_if(DEBUG_USB, "Found Miscellaneous interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::ApplicationSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found ApplicationSpecific interface");
|
||||
break;
|
||||
case USB::InterfaceBaseClass::VendorSpecific:
|
||||
dprintln_if(DEBUG_USB, "Found VendorSpecific interface");
|
||||
break;
|
||||
default:
|
||||
dprintln_if(DEBUG_USB, "Invalid interface base class {2H}", interface.descriptor.bInterfaceClass);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_class_driver)
|
||||
{
|
||||
dprintln("Successfully initialized USB interface");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<USBDevice::ConfigurationDescriptor> USBDevice::parse_configuration(size_t index)
|
||||
{
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0200 | index;
|
||||
request.wIndex = 0;
|
||||
request.wLength = m_dma_buffer->size();
|
||||
auto transferred = TRY(send_request(request, m_dma_buffer->paddr()));
|
||||
|
||||
auto configuration = *reinterpret_cast<const USBConfigurationDescriptor*>(m_dma_buffer->vaddr());
|
||||
|
||||
dprintln_if(DEBUG_USB, "configuration {} is {} bytes", index, +configuration.wTotalLength);
|
||||
if (configuration.bLength < sizeof(USBConfigurationDescriptor) || transferred < configuration.wTotalLength)
|
||||
{
|
||||
dwarnln("invalid configuration descriptor size: {} length, {} total", configuration.bLength, +configuration.wTotalLength);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurationDescriptor configuration;
|
||||
configuration.desciptor = *reinterpret_cast<const USBConfigurationDescriptor*>(m_dma_buffer->vaddr());
|
||||
|
||||
ptrdiff_t offset = configuration.desciptor.bLength;
|
||||
while (offset < configuration.desciptor.wTotalLength)
|
||||
{
|
||||
const uint8_t length = *reinterpret_cast<const uint8_t*>(m_dma_buffer->vaddr() + offset + 0);
|
||||
const uint8_t type = *reinterpret_cast<const uint8_t*>(m_dma_buffer->vaddr() + offset + 1);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case USB::DescriptorType::INTERFACE:
|
||||
if (length < sizeof(USBInterfaceDescriptor))
|
||||
{
|
||||
dwarnln("invalid interface descriptor size {}", length);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
TRY(configuration.interfaces.emplace_back(
|
||||
*reinterpret_cast<const USBInterfaceDescriptor*>(m_dma_buffer->vaddr() + offset),
|
||||
BAN::Vector<EndpointDescriptor>(),
|
||||
BAN::Vector<BAN::Vector<uint8_t>>()
|
||||
));
|
||||
break;
|
||||
case USB::DescriptorType::ENDPOINT:
|
||||
if (length < sizeof(USBEndpointDescriptor))
|
||||
{
|
||||
dwarnln("invalid interface descriptor size {}", length);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
if (configuration.interfaces.empty())
|
||||
{
|
||||
dwarnln("invalid endpoint descriptor before interface descriptor");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
TRY(configuration.interfaces.back().endpoints.emplace_back(
|
||||
*reinterpret_cast<const USBEndpointDescriptor*>(m_dma_buffer->vaddr() + offset)
|
||||
));
|
||||
break;
|
||||
default:
|
||||
if (configuration.interfaces.empty())
|
||||
dprintln_if(DEBUG_USB, "skipping descriptor type {}", type);
|
||||
else
|
||||
{
|
||||
BAN::Vector<uint8_t> descriptor;
|
||||
TRY(descriptor.resize(length));
|
||||
memcpy(descriptor.data(), reinterpret_cast<const void*>(m_dma_buffer->vaddr() + offset), length);
|
||||
TRY(configuration.interfaces.back().misc_descriptors.push_back(BAN::move(descriptor)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
offset += length;
|
||||
}
|
||||
|
||||
return BAN::move(configuration);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
USB::SpeedClass USBDevice::determine_speed_class(uint64_t bits_per_second)
|
||||
{
|
||||
if (bits_per_second <= 1'500'000)
|
||||
return USB::SpeedClass::LowSpeed;
|
||||
if (bits_per_second <= 12'000'000)
|
||||
return USB::SpeedClass::FullSpeed;
|
||||
else if (bits_per_second <= 480'000'000)
|
||||
return USB::SpeedClass::HighSpeed;
|
||||
return USB::SpeedClass::SuperSpeed;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,742 @@
|
|||
#include <BAN/ByteSpan.h>
|
||||
|
||||
#include <kernel/FS/DevFS/FileSystem.h>
|
||||
#include <kernel/USB/HID/HIDDriver.h>
|
||||
#include <kernel/USB/HID/Keyboard.h>
|
||||
#include <kernel/USB/HID/Mouse.h>
|
||||
|
||||
#define DEBUG_HID 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
enum class HIDDescriptorType : uint8_t
|
||||
{
|
||||
HID = 0x21,
|
||||
Report = 0x22,
|
||||
Physical = 0x23,
|
||||
};
|
||||
|
||||
struct HIDDescriptor
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdHID;
|
||||
uint8_t bCountryCode;
|
||||
uint8_t bNumDescriptors;
|
||||
struct
|
||||
{
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t wItemLength;
|
||||
} __attribute__((packed)) descriptors[];
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(HIDDescriptor) == 6);
|
||||
|
||||
|
||||
struct GlobalState
|
||||
{
|
||||
BAN::Optional<uint16_t> usage_page;
|
||||
BAN::Optional<int32_t> logical_minimum;
|
||||
BAN::Optional<int32_t> logical_maximum_signed;
|
||||
BAN::Optional<int32_t> logical_maximum_unsigned;
|
||||
BAN::Optional<int32_t> physical_minimum;
|
||||
BAN::Optional<int32_t> physical_maximum;
|
||||
// FIXME: support units
|
||||
BAN::Optional<uint32_t> report_size;
|
||||
// FIXME: support report id
|
||||
BAN::Optional<uint32_t> report_count;
|
||||
};
|
||||
|
||||
struct LocalState
|
||||
{
|
||||
BAN::Vector<uint32_t> usage_stack;
|
||||
BAN::Optional<uint32_t> usage_minimum;
|
||||
BAN::Optional<uint32_t> usage_maximum;
|
||||
// FIXME: support all local items
|
||||
};
|
||||
|
||||
using namespace USBHID;
|
||||
|
||||
#if DEBUG_HID
|
||||
static void dump_hid_collection(const Collection& collection, size_t indent);
|
||||
#endif
|
||||
|
||||
static BAN::ErrorOr<Collection> parse_report_descriptor(BAN::ConstByteSpan report_data);
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<USBHIDDriver>> USBHIDDriver::create(USBDevice& device, const USBDevice::InterfaceDescriptor& interface, uint8_t interface_index)
|
||||
{
|
||||
auto result = TRY(BAN::UniqPtr<USBHIDDriver>::create(device, interface, interface_index));
|
||||
TRY(result->initialize());
|
||||
return result;
|
||||
}
|
||||
|
||||
USBHIDDriver::USBHIDDriver(USBDevice& device, const USBDevice::InterfaceDescriptor& interface, uint8_t interface_index)
|
||||
: m_device(device)
|
||||
, m_interface(interface)
|
||||
, m_interface_index(interface_index)
|
||||
{}
|
||||
|
||||
USBHIDDriver::~USBHIDDriver()
|
||||
{}
|
||||
|
||||
BAN::ErrorOr<void> USBHIDDriver::initialize()
|
||||
{
|
||||
auto dma_buffer = TRY(DMARegion::create(1024));
|
||||
|
||||
ASSERT(static_cast<USB::InterfaceBaseClass>(m_interface.descriptor.bInterfaceClass) == USB::InterfaceBaseClass::HID);
|
||||
|
||||
size_t endpoint_index = static_cast<size_t>(-1);
|
||||
for (size_t i = 0; i < m_interface.endpoints.size(); i++)
|
||||
{
|
||||
const auto& endpoint = m_interface.endpoints[i];
|
||||
if (!(endpoint.descriptor.bEndpointAddress & 0x80))
|
||||
continue;
|
||||
if (endpoint.descriptor.bmAttributes != 0x03)
|
||||
continue;
|
||||
endpoint_index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (endpoint_index >= m_interface.endpoints.size())
|
||||
{
|
||||
dwarnln("HID device does not contain IN interrupt endpoint");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
bool hid_descriptor_invalid = false;
|
||||
size_t hid_descriptor_index = static_cast<size_t>(-1);
|
||||
for (size_t i = 0; i < m_interface.misc_descriptors.size(); i++)
|
||||
{
|
||||
if (static_cast<HIDDescriptorType>(m_interface.misc_descriptors[i][1]) != HIDDescriptorType::HID)
|
||||
continue;
|
||||
if (m_interface.misc_descriptors[i].size() < sizeof(HIDDescriptor))
|
||||
hid_descriptor_invalid = true;
|
||||
const auto& hid_descriptor = *reinterpret_cast<const HIDDescriptor*>(m_interface.misc_descriptors[i].data());
|
||||
if (hid_descriptor.bLength != m_interface.misc_descriptors[i].size())
|
||||
hid_descriptor_invalid = true;
|
||||
if (hid_descriptor.bLength != sizeof(HIDDescriptor) + hid_descriptor.bNumDescriptors * 3)
|
||||
hid_descriptor_invalid = true;
|
||||
hid_descriptor_index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hid_descriptor_index >= m_interface.misc_descriptors.size())
|
||||
{
|
||||
dwarnln("HID device does not contain HID descriptor");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
if (hid_descriptor_invalid)
|
||||
{
|
||||
dwarnln("HID device contains an invalid HID descriptor");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
// If this device supports boot protocol, make sure it is not used
|
||||
if (m_interface.endpoints.front().descriptor.bDescriptorType & 0x80)
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Interface;
|
||||
request.bRequest = USB::Request::SET_INTERFACE;
|
||||
request.wValue = 1; // report protocol
|
||||
request.wIndex = m_interface_index;
|
||||
request.wLength = 0;
|
||||
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());
|
||||
dprintln_if(DEBUG_HID, " bLength: {}", hid_descriptor.bLength);
|
||||
dprintln_if(DEBUG_HID, " bDescriptorType: {}", hid_descriptor.bDescriptorType);
|
||||
dprintln_if(DEBUG_HID, " bcdHID: {H}.{2H}", hid_descriptor.bcdHID >> 8, hid_descriptor.bcdHID & 0xFF);
|
||||
dprintln_if(DEBUG_HID, " bCountryCode: {}", hid_descriptor.bCountryCode);
|
||||
dprintln_if(DEBUG_HID, " bNumDescriptors: {}", hid_descriptor.bNumDescriptors);
|
||||
|
||||
bool report_descriptor_parsed = false;
|
||||
for (size_t i = 0; i < hid_descriptor.bNumDescriptors; i++)
|
||||
{
|
||||
auto descriptor = hid_descriptor.descriptors[i];
|
||||
|
||||
if (static_cast<HIDDescriptorType>(descriptor.bDescriptorType) != HIDDescriptorType::Report)
|
||||
{
|
||||
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())
|
||||
{
|
||||
dwarnln("Too big report descriptor size {} bytes ({} supported)", +descriptor.wItemLength, dma_buffer->size());
|
||||
return BAN::Error::from_errno(ENOBUFS);
|
||||
}
|
||||
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Interface;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = static_cast<uint16_t>(HIDDescriptorType::Report) << 8;
|
||||
request.wIndex = m_interface_index;
|
||||
request.wLength = descriptor.wItemLength;
|
||||
auto transferred = TRY(m_device.send_request(request, dma_buffer->paddr()));
|
||||
|
||||
if (transferred < descriptor.wItemLength)
|
||||
{
|
||||
dwarnln("HID device did not respond with full report descriptor");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!report_descriptor_parsed)
|
||||
{
|
||||
dwarnln("No report descriptors specified");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (collection.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)
|
||||
{
|
||||
case 0x02:
|
||||
m_hid_device = TRY(BAN::RefPtr<USBMouse>::create());
|
||||
break;
|
||||
case 0x06:
|
||||
m_hid_device = TRY(BAN::RefPtr<USBKeyboard>::create());
|
||||
break;
|
||||
default:
|
||||
dwarnln("Unsupported generic descript page usage 0x{2H}", collection.usage_id);
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
DevFileSystem::get().add_device(m_hid_device);
|
||||
|
||||
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);
|
||||
|
||||
TRY(m_device.initialize_endpoint(endpoint_descriptor));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void USBHIDDriver::forward_collection_inputs(const Collection& collection, 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
|
||||
{
|
||||
if (bit_offset >= data.size() * 8)
|
||||
return 0;
|
||||
if (bit_count + bit_offset > data.size() * 8)
|
||||
bit_count = data.size() * 8 - bit_offset;
|
||||
|
||||
uint32_t result = 0;
|
||||
uint32_t result_offset = 0;
|
||||
|
||||
while (result_offset < bit_count)
|
||||
{
|
||||
const uint32_t byte = bit_offset / 8;
|
||||
const uint32_t bit = bit_offset % 8;
|
||||
const uint32_t count = BAN::Math::min<uint32_t>(bit_count - result_offset, 8 - bit);
|
||||
const uint32_t mask = (1 << count) - 1;
|
||||
|
||||
result |= static_cast<uint32_t>((data[byte] >> bit) & mask) << result_offset;
|
||||
|
||||
bit_offset += count;
|
||||
result_offset += count;
|
||||
}
|
||||
|
||||
if (!as_unsigned && (result & (1u << (bit_count - 1))))
|
||||
{
|
||||
const uint32_t mask = (1u << bit_count) - 1;
|
||||
return -(static_cast<int64_t>(~result & mask) + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
for (const auto& entry : collection.entries)
|
||||
{
|
||||
if (entry.has<Collection>())
|
||||
{
|
||||
forward_collection_inputs(entry.get<Collection>(), data, bit_offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
ASSERT(entry.has<Report>());
|
||||
const auto& input = entry.get<Report>();
|
||||
if (input.type != Report::Type::Input)
|
||||
continue;
|
||||
|
||||
ASSERT(input.report_size <= 32);
|
||||
|
||||
if (input.usage_id == 0 && input.usage_minimum == 0 && input.usage_maximum == 0)
|
||||
{
|
||||
bit_offset += input.report_size * input.report_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < input.report_count; i++)
|
||||
{
|
||||
const int64_t logical = extract_bits(bit_offset, input.report_size, input.logical_minimum >= 0);
|
||||
if (logical < input.logical_minimum || logical > input.logical_maximum)
|
||||
{
|
||||
bit_offset += input.report_size;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int64_t physical =
|
||||
(input.physical_maximum - input.physical_minimum) *
|
||||
(logical - input.logical_minimum) /
|
||||
(input.logical_maximum - input.logical_minimum) +
|
||||
input.physical_minimum;
|
||||
|
||||
const uint32_t usage_base = input.usage_id ? input.usage_id : input.usage_minimum;
|
||||
if (input.flags & 0x02)
|
||||
m_hid_device->handle_variable(input.usage_page, usage_base + i, physical);
|
||||
else
|
||||
m_hid_device->handle_array(input.usage_page, usage_base + physical);
|
||||
|
||||
bit_offset += input.report_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void USBHIDDriver::handle_input_data(BAN::ConstByteSpan data, uint8_t endpoint_id)
|
||||
{
|
||||
ASSERT(m_endpoint_id == endpoint_id);
|
||||
|
||||
if constexpr(DEBUG_HID)
|
||||
{
|
||||
const auto nibble_to_hex = [](uint8_t x) -> char { return x + (x < 10 ? '0' : 'A' - 10); };
|
||||
|
||||
char buffer[512];
|
||||
char* ptr = buffer;
|
||||
for (size_t i = 0; i < BAN::Math::min<size_t>((sizeof(buffer) - 1) / 3, data.size()); i++)
|
||||
{
|
||||
*ptr++ = nibble_to_hex(data[i] >> 4);
|
||||
*ptr++ = nibble_to_hex(data[i] & 0xF);
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
*ptr = '\0';
|
||||
|
||||
dprintln_if(DEBUG_HID, "Received {} bytes from endpoint {}: {}", data.size(), endpoint_id, buffer);
|
||||
}
|
||||
|
||||
m_hid_device->start_report();
|
||||
forward_collection_inputs(m_collection, data, 0);
|
||||
m_hid_device->stop_report();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<Collection> parse_report_descriptor(BAN::ConstByteSpan report_data)
|
||||
{
|
||||
BAN::Vector<GlobalState> global_stack;
|
||||
GlobalState global_state;
|
||||
|
||||
LocalState local_state;
|
||||
|
||||
BAN::Optional<Collection> result;
|
||||
BAN::Vector<Collection> collection_stack;
|
||||
|
||||
const auto extract_report_item =
|
||||
[&](bool as_unsigned) -> int64_t
|
||||
{
|
||||
uint32_t value = 0;
|
||||
auto value_data = report_data.slice(1);
|
||||
switch (report_data[0] & 0x03)
|
||||
{
|
||||
case 1: value = as_unsigned ? value_data.as<const uint8_t>() : value_data.as<const int8_t>(); break;
|
||||
case 2: value = as_unsigned ? value_data.as<const uint16_t>() : value_data.as<const int16_t>(); break;
|
||||
case 3: value = as_unsigned ? value_data.as<const uint32_t>() : value_data.as<const int32_t>(); break;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
constexpr auto get_correct_sign =
|
||||
[](int64_t min, int64_t max_signed, int64_t max_unsigned) -> int64_t
|
||||
{
|
||||
if (min < 0 || max_signed >= 0)
|
||||
return max_signed;
|
||||
return max_unsigned;
|
||||
};
|
||||
|
||||
const auto add_data_item =
|
||||
[&](Report::Type type, uint32_t item_data, BAN::Vector<BAN::Variant<Collection, Report>>& container) -> BAN::ErrorOr<void>
|
||||
{
|
||||
if (!global_state.report_count.has_value() || !global_state.report_size.has_value())
|
||||
{
|
||||
dwarnln("Report count and/or report size is not defined");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
if (!global_state.usage_page.has_value())
|
||||
{
|
||||
dwarnln("Usage page is not defined");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
if (!global_state.logical_minimum.has_value() || !global_state.logical_maximum_signed.has_value())
|
||||
{
|
||||
dwarnln("Logical minimum and/or logical maximum is not defined");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
if (global_state.physical_minimum.has_value() != global_state.physical_minimum.has_value())
|
||||
{
|
||||
dwarnln("Only one of physical minimum and physical maximum is defined");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
if (local_state.usage_minimum.has_value() != local_state.usage_maximum.has_value())
|
||||
{
|
||||
dwarnln("Only one of logical minimum and logical maximum is defined");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
const int64_t logical_minimum = global_state.logical_minimum.value();
|
||||
const int64_t logical_maximum = get_correct_sign(
|
||||
global_state.logical_minimum.value(),
|
||||
global_state.logical_maximum_signed.value(),
|
||||
global_state.logical_maximum_unsigned.value()
|
||||
);
|
||||
|
||||
int64_t physical_minimum = logical_minimum;
|
||||
int64_t physical_maximum = logical_maximum;
|
||||
if (global_state.physical_minimum.has_value() && (global_state.physical_minimum.value() || global_state.physical_maximum.value()))
|
||||
{
|
||||
physical_minimum = global_state.physical_minimum.value();
|
||||
physical_maximum = global_state.physical_maximum.value();
|
||||
}
|
||||
|
||||
if (local_state.usage_stack.empty())
|
||||
{
|
||||
if (local_state.usage_minimum.has_value() && local_state.usage_maximum.has_value())
|
||||
{
|
||||
Report item;
|
||||
item.usage_page = global_state.usage_page.value();
|
||||
item.usage_id = 0;
|
||||
item.usage_minimum = local_state.usage_minimum.value();
|
||||
item.usage_maximum = local_state.usage_maximum.value();
|
||||
item.type = type;
|
||||
item.report_count = global_state.report_count.value();
|
||||
item.report_size = global_state.report_size.value();
|
||||
item.logical_minimum = logical_minimum;
|
||||
item.logical_maximum = logical_maximum;
|
||||
item.physical_minimum = physical_minimum;
|
||||
item.physical_maximum = physical_maximum;
|
||||
item.flags = item_data;
|
||||
TRY(container.push_back(item));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Report item;
|
||||
item.usage_page = global_state.usage_page.value();
|
||||
item.usage_id = 0;
|
||||
item.usage_minimum = 0;
|
||||
item.usage_maximum = 0;
|
||||
item.type = type;
|
||||
item.report_count = global_state.report_count.value();
|
||||
item.report_size = global_state.report_size.value();
|
||||
item.logical_minimum = 0;
|
||||
item.logical_maximum = 0;
|
||||
item.physical_minimum = 0;
|
||||
item.physical_maximum = 0;
|
||||
item.flags = item_data;
|
||||
TRY(container.push_back(item));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < global_state.report_count.value(); i++)
|
||||
{
|
||||
const uint32_t usage = local_state.usage_stack[BAN::Math::min<size_t>(i, local_state.usage_stack.size() - 1)];
|
||||
|
||||
Report item;
|
||||
item.usage_page = (usage >> 16) ? (usage >> 16) : global_state.usage_page.value();
|
||||
item.usage_id = usage & 0xFFFF;
|
||||
item.usage_minimum = 0;
|
||||
item.usage_maximum = 0;
|
||||
item.type = type;
|
||||
item.report_count = 1;
|
||||
item.report_size = global_state.report_size.value();
|
||||
item.logical_minimum = logical_minimum;
|
||||
item.logical_maximum = logical_maximum;
|
||||
item.physical_minimum = physical_minimum;
|
||||
item.physical_maximum = physical_maximum;
|
||||
item.flags = item_data;
|
||||
TRY(container.push_back(item));
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
while (report_data.size() > 0)
|
||||
{
|
||||
const uint8_t item_size = report_data[0] & 0x03;
|
||||
const uint8_t item_type = (report_data[0] >> 2) & 0x03;
|
||||
const uint8_t item_tag = (report_data[0] >> 4) & 0x0F;
|
||||
|
||||
if (item_type == 0)
|
||||
{
|
||||
switch (item_tag)
|
||||
{
|
||||
case 0b1000: // input
|
||||
if (collection_stack.empty())
|
||||
{
|
||||
dwarnln("Invalid input item outside of collection");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
TRY(add_data_item(Report::Type::Input, extract_report_item(true), collection_stack.back().entries));
|
||||
break;
|
||||
case 0b1001: // output
|
||||
if (collection_stack.empty())
|
||||
{
|
||||
dwarnln("Invalid input item outside of collection");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
TRY(add_data_item(Report::Type::Output, extract_report_item(true), collection_stack.back().entries));
|
||||
break;
|
||||
case 0b1011: // feature
|
||||
if (collection_stack.empty())
|
||||
{
|
||||
dwarnln("Invalid input item outside of collection");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
TRY(add_data_item(Report::Type::Feature, extract_report_item(true), collection_stack.back().entries));
|
||||
break;
|
||||
case 0b1010: // collection
|
||||
{
|
||||
if (local_state.usage_stack.size() != 1)
|
||||
{
|
||||
dwarnln("{} usages specified for collection", local_state.usage_stack.empty() ? "No" : "Multiple");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
uint16_t usage_page = 0;
|
||||
if (global_state.usage_page.has_value())
|
||||
usage_page = global_state.usage_page.value();
|
||||
if (local_state.usage_stack.front() >> 16)
|
||||
usage_page = local_state.usage_stack.front() >> 16;
|
||||
if (usage_page == 0)
|
||||
{
|
||||
dwarnln("Usage page not specified for a collection");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
TRY(collection_stack.emplace_back());
|
||||
collection_stack.back().usage_page = usage_page;
|
||||
collection_stack.back().usage_id = local_state.usage_stack.front();
|
||||
break;
|
||||
}
|
||||
case 0b1100: // end collection
|
||||
if (collection_stack.empty())
|
||||
{
|
||||
dwarnln("End collection outside of collection");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
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());
|
||||
collection_stack.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
TRY(collection_stack[collection_stack.size() - 2].entries.push_back(BAN::move(collection_stack.back())));
|
||||
collection_stack.pop_back();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dwarnln("Report has reserved main item tag 0b{4b}", item_tag);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
local_state = LocalState();
|
||||
}
|
||||
else if (item_type == 1)
|
||||
{
|
||||
switch (item_tag)
|
||||
{
|
||||
case 0b0000: // usage page
|
||||
global_state.usage_page = extract_report_item(true);
|
||||
break;
|
||||
case 0b0001: // logical minimum
|
||||
global_state.logical_minimum = extract_report_item(false);
|
||||
break;
|
||||
case 0b0010: // logical maximum
|
||||
global_state.logical_maximum_signed = extract_report_item(false);
|
||||
global_state.logical_maximum_unsigned = extract_report_item(true);
|
||||
break;
|
||||
case 0b0011: // physical minimum
|
||||
global_state.physical_minimum = extract_report_item(false);
|
||||
break;
|
||||
case 0b0100: // physical maximum
|
||||
global_state.physical_maximum = extract_report_item(false);
|
||||
break;
|
||||
case 0b0101: // unit exponent
|
||||
dwarnln("Report units are not supported");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0b0110: // unit
|
||||
dwarnln("Report units are not supported");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0b0111: // report size
|
||||
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);
|
||||
case 0b1001: // report count
|
||||
global_state.report_count = extract_report_item(true);
|
||||
break;
|
||||
case 0b1010: // push
|
||||
TRY(global_stack.push_back(global_state));
|
||||
break;
|
||||
case 0b1011: // pop
|
||||
if (global_stack.empty())
|
||||
{
|
||||
dwarnln("Report pop from empty stack");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
global_state = global_stack.back();
|
||||
global_stack.pop_back();
|
||||
break;
|
||||
default:
|
||||
dwarnln("Report has reserved global item tag 0b{4b}", item_tag);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
}
|
||||
else if (item_type == 2)
|
||||
{
|
||||
switch (item_tag)
|
||||
{
|
||||
case 0b0000: // usage
|
||||
TRY(local_state.usage_stack.emplace_back(extract_report_item(true)));
|
||||
break;
|
||||
case 0b0001: // usage minimum
|
||||
local_state.usage_minimum = extract_report_item(true);
|
||||
break;
|
||||
case 0b0010: // usage maximum
|
||||
local_state.usage_maximum = extract_report_item(true);
|
||||
break;
|
||||
case 0b0011: // designator index
|
||||
case 0b0100: // designator minimum
|
||||
case 0b0101: // designator maximum
|
||||
case 0b0111: // string index
|
||||
case 0b1000: // string minimum
|
||||
case 0b1001: // string maximum
|
||||
case 0b1010: // delimeter
|
||||
dwarnln("Unsupported local item tag 0b{4b}", item_tag);
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
default:
|
||||
dwarnln("Report has reserved local item tag 0b{4b}", item_tag);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dwarnln("Report has reserved item type 0b{2b}", item_type);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
report_data = report_data.slice(1 + item_size);
|
||||
}
|
||||
|
||||
if (!result.has_value())
|
||||
{
|
||||
dwarnln("No collection defined in report descriptor");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return result.release_value();
|
||||
}
|
||||
|
||||
#if DEBUG_HID
|
||||
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)
|
||||
{
|
||||
const char* report_type = "";
|
||||
switch (report.type)
|
||||
{
|
||||
case Report::Type::Input: report_type = "input"; break;
|
||||
case Report::Type::Output: report_type = "output"; break;
|
||||
case Report::Type::Feature: report_type = "feature"; break;
|
||||
}
|
||||
print_indent(indent);
|
||||
BAN::Formatter::println(Debug::putchar, "report {}", report_type);
|
||||
|
||||
print_indent(indent + 4);
|
||||
BAN::Formatter::println(Debug::putchar, "usage page: {2H}", report.usage_page);
|
||||
|
||||
if (report.usage_id || report.usage_minimum || report.usage_maximum)
|
||||
{
|
||||
print_indent(indent + 4);
|
||||
if (report.usage_id)
|
||||
BAN::Formatter::println(Debug::putchar, "usage: {2H}", report.usage_id);
|
||||
else
|
||||
BAN::Formatter::println(Debug::putchar, "usage: {2H}->{2H}", report.usage_minimum, report.usage_maximum);
|
||||
}
|
||||
|
||||
print_indent(indent + 4);
|
||||
BAN::Formatter::println(Debug::putchar, "flags: 0b{8b}", report.flags);
|
||||
|
||||
print_indent(indent + 4);
|
||||
BAN::Formatter::println(Debug::putchar, "size: {}", report.report_size);
|
||||
print_indent(indent + 4);
|
||||
BAN::Formatter::println(Debug::putchar, "count: {}", report.report_count);
|
||||
|
||||
print_indent(indent + 4);
|
||||
BAN::Formatter::println(Debug::putchar, "lminimum: {}", report.logical_minimum);
|
||||
print_indent(indent + 4);
|
||||
BAN::Formatter::println(Debug::putchar, "lmaximum: {}", report.logical_maximum);
|
||||
|
||||
print_indent(indent + 4);
|
||||
BAN::Formatter::println(Debug::putchar, "pminimum: {}", report.physical_minimum);
|
||||
print_indent(indent + 4);
|
||||
BAN::Formatter::println(Debug::putchar, "pmaximum: {}", report.physical_maximum);
|
||||
}
|
||||
|
||||
static void dump_hid_collection(const Collection& collection, size_t indent)
|
||||
{
|
||||
print_indent(indent);
|
||||
BAN::Formatter::println(Debug::putchar, "collection {}", collection.type);
|
||||
print_indent(indent);
|
||||
BAN::Formatter::println(Debug::putchar, "usage {H}:{H}", collection.usage_page, collection.usage_id);
|
||||
|
||||
for (const auto& entry : collection.entries)
|
||||
{
|
||||
if (entry.has<Collection>())
|
||||
dump_hid_collection(entry.get<Collection>(), indent + 4);
|
||||
if (entry.has<Report>())
|
||||
dump_hid_report(entry.get<Report>(), indent + 4);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
#include <kernel/USB/HID/Keyboard.h>
|
||||
#include <LibInput/KeyEvent.h>
|
||||
|
||||
#define DEBUG_KEYBOARD 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static BAN::Optional<uint8_t> s_scancode_to_keycode[0x100] {};
|
||||
static bool s_scancode_to_keycode_initialized = false;
|
||||
|
||||
static void initialize_scancode_to_keycode();
|
||||
|
||||
void USBKeyboard::start_report()
|
||||
{
|
||||
for (auto& val : m_keyboard_state_temp)
|
||||
val = false;
|
||||
}
|
||||
|
||||
void USBKeyboard::stop_report()
|
||||
{
|
||||
using KeyModifier = LibInput::KeyEvent::Modifier;
|
||||
|
||||
if (!s_scancode_to_keycode_initialized)
|
||||
initialize_scancode_to_keycode();
|
||||
|
||||
// FIXME: RawKeyEvent probably should only contain keycode.
|
||||
// Modifier should be determined when converting to KeyEvent.
|
||||
|
||||
uint8_t modifier = 0;
|
||||
if (m_keyboard_state_temp[0xE1])
|
||||
modifier |= KeyModifier::LShift;
|
||||
if (m_keyboard_state_temp[0xE5])
|
||||
modifier |= KeyModifier::RShift;
|
||||
if (m_keyboard_state_temp[0xE0])
|
||||
modifier |= KeyModifier::LCtrl;
|
||||
if (m_keyboard_state_temp[0xE4])
|
||||
modifier |= KeyModifier::RCtrl;
|
||||
if (m_keyboard_state_temp[0xE2])
|
||||
modifier |= KeyModifier::LAlt;
|
||||
if (m_keyboard_state_temp[0xE6])
|
||||
modifier |= KeyModifier::RAlt;
|
||||
|
||||
if (m_keyboard_state_temp[0x39] && !m_keyboard_state[0x39])
|
||||
m_toggle_mask ^= KeyModifier::CapsLock;
|
||||
if (m_keyboard_state_temp[0x47] && !m_keyboard_state[0x47])
|
||||
m_toggle_mask ^= KeyModifier::ScrollLock;
|
||||
if (m_keyboard_state_temp[0x53] && !m_keyboard_state[0x53])
|
||||
m_toggle_mask ^= KeyModifier::NumLock;
|
||||
|
||||
modifier |= m_toggle_mask;
|
||||
|
||||
for (size_t i = 0; i < m_keyboard_state.size(); i++)
|
||||
{
|
||||
if (m_keyboard_state[i] == m_keyboard_state_temp[i])
|
||||
continue;
|
||||
|
||||
const bool pressed = m_keyboard_state_temp[i];
|
||||
|
||||
if (pressed)
|
||||
dprintln_if(DEBUG_KEYBOARD, "{2H}", i);
|
||||
|
||||
auto opt_keycode = s_scancode_to_keycode[i];
|
||||
if (opt_keycode.has_value())
|
||||
{
|
||||
LibInput::RawKeyEvent event;
|
||||
event.keycode = opt_keycode.value();
|
||||
event.modifier = modifier | (pressed ? KeyModifier::Pressed : 0);
|
||||
add_event(BAN::ConstByteSpan::from(event));
|
||||
}
|
||||
|
||||
m_keyboard_state[i] = m_keyboard_state_temp[i];
|
||||
}
|
||||
}
|
||||
|
||||
void USBKeyboard::handle_variable(uint16_t usage_page, uint16_t usage, int64_t state)
|
||||
{
|
||||
if (usage_page != 0x07)
|
||||
{
|
||||
dprintln_if(DEBUG_KEYBOARD, "Unsupported keyboard usage page {2H}", usage_page);
|
||||
return;
|
||||
}
|
||||
if (usage >= 4 && usage < m_keyboard_state_temp.size())
|
||||
m_keyboard_state_temp[usage] = state;
|
||||
}
|
||||
|
||||
void USBKeyboard::handle_array(uint16_t usage_page, uint16_t usage)
|
||||
{
|
||||
if (usage_page != 0x07)
|
||||
{
|
||||
dprintln_if(DEBUG_KEYBOARD, "Unsupported keyboard usage page {2H}", usage_page);
|
||||
return;
|
||||
}
|
||||
if (usage >= 4 && usage < m_keyboard_state_temp.size())
|
||||
m_keyboard_state_temp[usage] = true;
|
||||
}
|
||||
|
||||
void initialize_scancode_to_keycode()
|
||||
{
|
||||
using LibInput::keycode_function;
|
||||
using LibInput::keycode_normal;
|
||||
using LibInput::keycode_numpad;
|
||||
|
||||
s_scancode_to_keycode[0x35] = keycode_normal(0, 0);
|
||||
s_scancode_to_keycode[0x1E] = keycode_normal(0, 1);
|
||||
s_scancode_to_keycode[0x1F] = keycode_normal(0, 2);
|
||||
s_scancode_to_keycode[0x20] = keycode_normal(0, 3);
|
||||
s_scancode_to_keycode[0x21] = keycode_normal(0, 4);
|
||||
s_scancode_to_keycode[0x22] = keycode_normal(0, 5);
|
||||
s_scancode_to_keycode[0x23] = keycode_normal(0, 6);
|
||||
s_scancode_to_keycode[0x24] = keycode_normal(0, 7);
|
||||
s_scancode_to_keycode[0x25] = keycode_normal(0, 8);
|
||||
s_scancode_to_keycode[0x26] = keycode_normal(0, 9);
|
||||
s_scancode_to_keycode[0x27] = keycode_normal(0, 10);
|
||||
s_scancode_to_keycode[0x2D] = keycode_normal(0, 11);
|
||||
s_scancode_to_keycode[0x2E] = keycode_normal(0, 12);
|
||||
s_scancode_to_keycode[0x2A] = keycode_normal(0, 13);
|
||||
s_scancode_to_keycode[0x2B] = keycode_normal(1, 0);
|
||||
s_scancode_to_keycode[0x14] = keycode_normal(1, 1);
|
||||
s_scancode_to_keycode[0x1A] = keycode_normal(1, 2);
|
||||
s_scancode_to_keycode[0x08] = keycode_normal(1, 3);
|
||||
s_scancode_to_keycode[0x15] = keycode_normal(1, 4);
|
||||
s_scancode_to_keycode[0x17] = keycode_normal(1, 5);
|
||||
s_scancode_to_keycode[0x1C] = keycode_normal(1, 6);
|
||||
s_scancode_to_keycode[0x18] = keycode_normal(1, 7);
|
||||
s_scancode_to_keycode[0x0C] = keycode_normal(1, 8);
|
||||
s_scancode_to_keycode[0x12] = keycode_normal(1, 9);
|
||||
s_scancode_to_keycode[0x13] = keycode_normal(1, 10);
|
||||
s_scancode_to_keycode[0x2F] = keycode_normal(1, 11);
|
||||
s_scancode_to_keycode[0x30] = keycode_normal(1, 12);
|
||||
s_scancode_to_keycode[0x39] = keycode_normal(2, 0);
|
||||
s_scancode_to_keycode[0x04] = keycode_normal(2, 1);
|
||||
s_scancode_to_keycode[0x16] = keycode_normal(2, 2);
|
||||
s_scancode_to_keycode[0x07] = keycode_normal(2, 3);
|
||||
s_scancode_to_keycode[0x09] = keycode_normal(2, 4);
|
||||
s_scancode_to_keycode[0x0A] = keycode_normal(2, 5);
|
||||
s_scancode_to_keycode[0x0B] = keycode_normal(2, 6);
|
||||
s_scancode_to_keycode[0x0D] = keycode_normal(2, 7);
|
||||
s_scancode_to_keycode[0x0E] = keycode_normal(2, 8);
|
||||
s_scancode_to_keycode[0x0F] = keycode_normal(2, 9);
|
||||
s_scancode_to_keycode[0x33] = keycode_normal(2, 10);
|
||||
s_scancode_to_keycode[0x34] = keycode_normal(2, 11);
|
||||
s_scancode_to_keycode[0x31] = keycode_normal(2, 12);
|
||||
s_scancode_to_keycode[0x28] = keycode_normal(2, 13);
|
||||
s_scancode_to_keycode[0xE1] = keycode_normal(3, 0);
|
||||
s_scancode_to_keycode[0x64] = keycode_normal(3, 1);
|
||||
s_scancode_to_keycode[0x1D] = keycode_normal(3, 2);
|
||||
s_scancode_to_keycode[0x1B] = keycode_normal(3, 3);
|
||||
s_scancode_to_keycode[0x06] = keycode_normal(3, 4);
|
||||
s_scancode_to_keycode[0x19] = keycode_normal(3, 5);
|
||||
s_scancode_to_keycode[0x05] = keycode_normal(3, 6);
|
||||
s_scancode_to_keycode[0x11] = keycode_normal(3, 7);
|
||||
s_scancode_to_keycode[0x10] = keycode_normal(3, 8);
|
||||
s_scancode_to_keycode[0x36] = keycode_normal(3, 9);
|
||||
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[0xE6] = keycode_normal(4, 5);
|
||||
s_scancode_to_keycode[0xE4] = keycode_normal(4, 6);
|
||||
|
||||
s_scancode_to_keycode[0x52] = keycode_normal(5, 0);
|
||||
s_scancode_to_keycode[0x50] = keycode_normal(5, 1);
|
||||
s_scancode_to_keycode[0x51] = keycode_normal(5, 2);
|
||||
s_scancode_to_keycode[0x4F] = keycode_normal(5, 3);
|
||||
|
||||
s_scancode_to_keycode[0x29] = keycode_function( 0);
|
||||
s_scancode_to_keycode[0x3A] = keycode_function( 1);
|
||||
s_scancode_to_keycode[0x3B] = keycode_function( 2);
|
||||
s_scancode_to_keycode[0x3C] = keycode_function( 3);
|
||||
s_scancode_to_keycode[0x3D] = keycode_function( 4);
|
||||
s_scancode_to_keycode[0x3E] = keycode_function( 5);
|
||||
s_scancode_to_keycode[0x3F] = keycode_function( 6);
|
||||
s_scancode_to_keycode[0x40] = keycode_function( 7);
|
||||
s_scancode_to_keycode[0x41] = keycode_function( 8);
|
||||
s_scancode_to_keycode[0x42] = keycode_function( 9);
|
||||
s_scancode_to_keycode[0x43] = keycode_function(10);
|
||||
s_scancode_to_keycode[0x44] = keycode_function(11);
|
||||
s_scancode_to_keycode[0x45] = keycode_function(12);
|
||||
s_scancode_to_keycode[0x49] = keycode_function(13);
|
||||
s_scancode_to_keycode[0x46] = keycode_function(14);
|
||||
s_scancode_to_keycode[0x4C] = keycode_function(15);
|
||||
s_scancode_to_keycode[0x4A] = keycode_function(16);
|
||||
s_scancode_to_keycode[0x4D] = keycode_function(17);
|
||||
s_scancode_to_keycode[0x4B] = keycode_function(18);
|
||||
s_scancode_to_keycode[0x4E] = keycode_function(19);
|
||||
|
||||
s_scancode_to_keycode[0x53] = keycode_numpad(0, 0);
|
||||
s_scancode_to_keycode[0x54] = keycode_numpad(0, 1);
|
||||
s_scancode_to_keycode[0x55] = keycode_numpad(0, 2);
|
||||
s_scancode_to_keycode[0x56] = keycode_numpad(0, 3);
|
||||
s_scancode_to_keycode[0x5F] = keycode_numpad(1, 0);
|
||||
s_scancode_to_keycode[0x60] = keycode_numpad(1, 1);
|
||||
s_scancode_to_keycode[0x61] = keycode_numpad(1, 2);
|
||||
s_scancode_to_keycode[0x57] = keycode_numpad(1, 3);
|
||||
s_scancode_to_keycode[0x5C] = keycode_numpad(2, 0);
|
||||
s_scancode_to_keycode[0x5D] = keycode_numpad(2, 1);
|
||||
s_scancode_to_keycode[0x5E] = keycode_numpad(2, 2);
|
||||
s_scancode_to_keycode[0x59] = keycode_numpad(3, 0);
|
||||
s_scancode_to_keycode[0x5A] = keycode_numpad(3, 1);
|
||||
s_scancode_to_keycode[0x5B] = keycode_numpad(3, 2);
|
||||
s_scancode_to_keycode[0x58] = keycode_numpad(3, 3);
|
||||
s_scancode_to_keycode[0x62] = keycode_numpad(4, 0);
|
||||
s_scancode_to_keycode[0x63] = keycode_numpad(4, 1);
|
||||
|
||||
s_scancode_to_keycode_initialized = true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
#include <kernel/USB/HID/Mouse.h>
|
||||
#include <LibInput/MouseEvent.h>
|
||||
|
||||
#define DEBUG_MOUSE 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
void USBMouse::start_report()
|
||||
{
|
||||
for (auto& val : m_button_state_temp)
|
||||
val = false;
|
||||
}
|
||||
|
||||
void USBMouse::stop_report()
|
||||
{
|
||||
if (m_pointer_x || m_pointer_y)
|
||||
{
|
||||
dprintln_if(DEBUG_MOUSE, "Mouse move event {}, {}", m_pointer_x, m_pointer_y);
|
||||
|
||||
LibInput::MouseEvent event;
|
||||
event.type = LibInput::MouseEventType::MouseMoveEvent;
|
||||
event.move_event.rel_x = m_pointer_x;
|
||||
event.move_event.rel_y = -m_pointer_y;
|
||||
add_event(BAN::ConstByteSpan::from(event));
|
||||
|
||||
m_pointer_x = 0;
|
||||
m_pointer_y = 0;
|
||||
}
|
||||
|
||||
if (m_wheel)
|
||||
{
|
||||
dprintln_if(DEBUG_MOUSE, "Mouse scroll event {}", m_wheel);
|
||||
|
||||
LibInput::MouseEvent event;
|
||||
event.type = LibInput::MouseEventType::MouseScrollEvent;
|
||||
event.scroll_event.scroll = m_wheel;
|
||||
add_event(BAN::ConstByteSpan::from(event));
|
||||
|
||||
m_wheel = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_button_state.size(); i++)
|
||||
{
|
||||
if (m_button_state[i] == m_button_state_temp[i])
|
||||
continue;
|
||||
|
||||
const bool pressed = m_button_state_temp[i];
|
||||
|
||||
dprintln_if(DEBUG_MOUSE, "Mouse button event {}: {}", i, pressed);
|
||||
|
||||
LibInput::MouseEvent event;
|
||||
event.type = LibInput::MouseEventType::MouseButtonEvent;
|
||||
event.button_event.pressed = pressed;
|
||||
event.button_event.button = static_cast<LibInput::MouseButton>(i);
|
||||
add_event(BAN::ConstByteSpan::from(event));
|
||||
|
||||
m_button_state[i] = m_button_state_temp[i];
|
||||
}
|
||||
}
|
||||
|
||||
void USBMouse::handle_variable(uint16_t usage_page, uint16_t usage, int64_t state)
|
||||
{
|
||||
switch (usage_page)
|
||||
{
|
||||
case 0x01: // pointer
|
||||
switch (usage)
|
||||
{
|
||||
case 0x30:
|
||||
m_pointer_x = state;
|
||||
break;
|
||||
case 0x31:
|
||||
m_pointer_y = state;
|
||||
break;
|
||||
case 0x38:
|
||||
m_wheel = state;
|
||||
break;
|
||||
default:
|
||||
dprintln_if(DEBUG_MOUSE, "Unsupported mouse usage {2H} on page {2H}", usage, usage_page);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x09: // button
|
||||
if (usage == 0 || usage >= m_button_state_temp.size())
|
||||
break;
|
||||
m_button_state_temp[usage - 1] = state;
|
||||
break;
|
||||
default:
|
||||
dprintln_if(DEBUG_MOUSE, "Unsupported mouse usage page {2H}", usage_page);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void USBMouse::handle_array(uint16_t usage_page, uint16_t usage)
|
||||
{
|
||||
dprintln_if(DEBUG_MOUSE, "Unhandled array report {2H}:{2H}", usage_page, usage);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
#include <BAN/UniqPtr.h>
|
||||
|
||||
#include <kernel/USB/USBManager.h>
|
||||
#include <kernel/USB/XHCI/Controller.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static BAN::UniqPtr<USBManager> s_instance;
|
||||
|
||||
BAN::ErrorOr<void> USBManager::initialize()
|
||||
{
|
||||
ASSERT(!s_instance);
|
||||
auto manager = TRY(BAN::UniqPtr<USBManager>::create());
|
||||
s_instance = BAN::move(manager);
|
||||
return {};
|
||||
}
|
||||
|
||||
USBManager& USBManager::get()
|
||||
{
|
||||
ASSERT(s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> USBManager::add_controller(PCI::Device& pci_device)
|
||||
{
|
||||
ASSERT(pci_device.class_code() == 0x0C);
|
||||
ASSERT(pci_device.subclass() == 0x03);
|
||||
|
||||
BAN::UniqPtr<USBController> controller;
|
||||
switch (pci_device.prog_if())
|
||||
{
|
||||
case 0x00:
|
||||
dprintln("Unsupported UHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x10:
|
||||
dprintln("Unsupported OHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x20:
|
||||
dprintln("Unsupported EHCI controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x30:
|
||||
if (auto ret = XHCIController::initialize(pci_device); ret.is_error())
|
||||
dprintln("Could not initialize XHCI controller: {}", ret.error());
|
||||
else
|
||||
controller = ret.release_value();
|
||||
break;
|
||||
default:
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
TRY(m_controllers.push_back(BAN::move(controller)));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,506 @@
|
|||
#include <BAN/Bitcast.h>
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Process.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/XHCI/Controller.h>
|
||||
#include <kernel/USB/XHCI/Device.h>
|
||||
|
||||
#define DEBUG_XHCI 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
XHCIController::XHCIController(PCI::Device& pci_device)
|
||||
: m_pci_device(pci_device)
|
||||
{ }
|
||||
|
||||
XHCIController::~XHCIController()
|
||||
{
|
||||
if (m_port_updater)
|
||||
m_port_updater->exit(0, SIGKILL);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<XHCIController>> XHCIController::initialize(PCI::Device& pci_device)
|
||||
{
|
||||
auto controller = TRY(BAN::UniqPtr<XHCIController>::create(pci_device));
|
||||
TRY(controller->initialize_impl());
|
||||
return controller;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_impl()
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "XHCI controller at PCI {2H}:{2H}:{2H}", m_pci_device.bus(), m_pci_device.dev(), m_pci_device.func());
|
||||
|
||||
m_pci_device.enable_bus_mastering();
|
||||
m_pci_device.enable_memory_space();
|
||||
|
||||
m_configuration_bar = TRY(m_pci_device.allocate_bar_region(0));
|
||||
if (m_configuration_bar->type() != PCI::BarType::MEM)
|
||||
{
|
||||
dwarnln("XHCI controller with non-memory configuration space");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (auto ret = reset_controller(); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not reset XHCI Controller: {}", ret.error());
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
auto& capabilities = capability_regs();
|
||||
dprintln_if(DEBUG_XHCI, " version {H}.{H}.{H}",
|
||||
+capabilities.major_revision,
|
||||
capabilities.minor_revision >> 4,
|
||||
capabilities.minor_revision & 0x0F
|
||||
);
|
||||
dprintln_if(DEBUG_XHCI, " max slots {}", +capabilities.hcsparams1.max_slots);
|
||||
dprintln_if(DEBUG_XHCI, " max intrs {}", +capabilities.hcsparams1.max_interrupters);
|
||||
dprintln_if(DEBUG_XHCI, " max ports {}", +capabilities.hcsparams1.max_ports);
|
||||
|
||||
TRY(m_slots.resize(capabilities.hcsparams1.max_slots));
|
||||
TRY(m_ports.resize(capabilities.hcsparams1.max_ports));
|
||||
|
||||
TRY(initialize_ports());
|
||||
|
||||
auto& operational = operational_regs();
|
||||
|
||||
// allocate and program dcbaa
|
||||
m_dcbaa_region = TRY(DMARegion::create(capabilities.hcsparams1.max_slots * 8));
|
||||
memset(reinterpret_cast<void*>(m_dcbaa_region->vaddr()), 0, m_dcbaa_region->size());
|
||||
operational.dcbaap_lo = m_dcbaa_region->paddr() & 0xFFFFFFFF;
|
||||
operational.dcbaap_hi = m_dcbaa_region->paddr() >> 32;
|
||||
|
||||
// allocate and program crcr
|
||||
TRY(m_command_completions.resize(m_command_ring_trb_count));
|
||||
m_command_ring_region = TRY(DMARegion::create(m_command_ring_trb_count * sizeof(XHCI::TRB)));
|
||||
memset(reinterpret_cast<void*>(m_command_ring_region->vaddr()), 0, m_command_ring_region->size());
|
||||
operational.crcr_lo = m_command_ring_region->paddr() | XHCI::CRCR::RingCycleState;
|
||||
operational.crcr_hi = m_command_ring_region->paddr() >> 32;
|
||||
|
||||
TRY(initialize_primary_interrupter());
|
||||
|
||||
// enable the controller
|
||||
operational.usbcmd.run_stop = 1;
|
||||
while (operational.usbsts & XHCI::USBSTS::HCHalted)
|
||||
continue;
|
||||
|
||||
m_port_updater = Process::create_kernel([](void* data) { reinterpret_cast<XHCIController*>(data)->port_updater_task(); }, this);
|
||||
if (m_port_updater == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_ports()
|
||||
{
|
||||
auto& capabilities = capability_regs();
|
||||
uint8_t max_ports = capabilities.hcsparams1.max_ports;
|
||||
|
||||
ASSERT(m_ports.size() == max_ports);
|
||||
|
||||
{
|
||||
uint16_t ext_offset = capabilities.hccparams1.xhci_extended_capabilities_pointer;
|
||||
if (ext_offset == 0)
|
||||
{
|
||||
dwarnln("XHCI controller does not have extended capabilities");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
vaddr_t ext_addr = m_configuration_bar->vaddr() + ext_offset * 4;
|
||||
while (true)
|
||||
{
|
||||
auto& ext_cap = *reinterpret_cast<volatile XHCI::ExtendedCap*>(ext_addr);
|
||||
|
||||
if (ext_cap.capability_id == XHCI::ExtendedCapabilityID::SupportedProtocol)
|
||||
{
|
||||
auto& protocol = reinterpret_cast<volatile XHCI::SupportedPrococolCap&>(ext_cap);
|
||||
|
||||
if (protocol.name_string != *reinterpret_cast<const uint32_t*>("USB "))
|
||||
{
|
||||
dwarnln("Invalid port protocol name string");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (protocol.compatible_port_offset == 0 || protocol.compatible_port_offset + protocol.compatible_port_count - 1 > max_ports)
|
||||
{
|
||||
dwarnln("Invalid port specified in SupportedProtocols");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < protocol.compatible_port_count; i++)
|
||||
{
|
||||
auto& port = m_ports[protocol.compatible_port_offset + i - 1];
|
||||
port.revision_major = protocol.major_revision;
|
||||
port.revision_minor = protocol.minor_revision;
|
||||
port.slot_type = protocol.protocol_slot_type;
|
||||
|
||||
for (size_t j = 0; j < protocol.protocol_speed_id_count; j++)
|
||||
{
|
||||
uint32_t speed_info = reinterpret_cast<const volatile uint32_t*>(ext_addr + sizeof(XHCI::SupportedPrococolCap))[j];
|
||||
uint32_t port_speed = speed_info >> 16;
|
||||
for (size_t exp = 0; exp < ((speed_info >> 4) & 0x03); exp++)
|
||||
port_speed *= 1000;
|
||||
port.speed_id_to_speed[speed_info & 0x0F] = port_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_cap.next_capability == 0)
|
||||
break;
|
||||
ext_addr += ext_cap.next_capability * 4;
|
||||
}
|
||||
}
|
||||
|
||||
// set max slots enabled
|
||||
auto& operational = operational_regs();
|
||||
operational.config.max_device_slots_enabled = capabilities.hcsparams1.max_slots;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_primary_interrupter()
|
||||
{
|
||||
TRY(m_pci_device.reserve_irqs(1));
|
||||
|
||||
auto& runtime = runtime_regs();
|
||||
|
||||
static constexpr size_t event_ring_table_offset = m_event_ring_trb_count * sizeof(XHCI::TRB);
|
||||
|
||||
m_event_ring_region = TRY(DMARegion::create(m_event_ring_trb_count * sizeof(XHCI::TRB) + sizeof(XHCI::EventRingTableEntry)));
|
||||
memset(reinterpret_cast<void*>(m_event_ring_region->vaddr()), 0, m_event_ring_region->size());
|
||||
|
||||
auto& event_ring_table_entry = *reinterpret_cast<XHCI::EventRingTableEntry*>(m_event_ring_region->vaddr() + event_ring_table_offset);
|
||||
event_ring_table_entry.rsba = m_event_ring_region->paddr();
|
||||
event_ring_table_entry.rsz = m_event_ring_trb_count;
|
||||
|
||||
auto& primary_interrupter = runtime.irs[0];
|
||||
primary_interrupter.erstsz = 1;
|
||||
primary_interrupter.erdp = m_event_ring_region->paddr();
|
||||
primary_interrupter.erstba = m_event_ring_region->paddr() + event_ring_table_offset;
|
||||
|
||||
auto& operational = operational_regs();
|
||||
operational.usbcmd.interrupter_enable = 1;
|
||||
|
||||
primary_interrupter.iman = primary_interrupter.iman | XHCI::IMAN::InterruptPending | XHCI::IMAN::InterruptEnable;
|
||||
|
||||
set_irq(m_pci_device.get_irq(0));
|
||||
enable_interrupt();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Mutex s_port_mutex;
|
||||
|
||||
void XHCIController::port_updater_task()
|
||||
{
|
||||
// allow initial pass of port iteration because controller
|
||||
// does not send Port Status Change event for already
|
||||
// attached ports
|
||||
m_port_changed = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
bool expected { true };
|
||||
while (!m_port_changed.compare_exchange(expected, false))
|
||||
{
|
||||
m_port_semaphore.block_with_timeout(100);
|
||||
expected = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_ports.size(); i++)
|
||||
{
|
||||
LockGuard _(s_port_mutex);
|
||||
|
||||
auto& my_port = m_ports[i];
|
||||
if (my_port.revision_major == 0)
|
||||
continue;
|
||||
|
||||
auto& op_port = operational_regs().ports[i];
|
||||
|
||||
if (!(op_port.portsc & XHCI::PORTSC::PP))
|
||||
continue;
|
||||
|
||||
// read and clear needed change flags
|
||||
const bool reset_change = op_port.portsc & XHCI::PORTSC::PRC;
|
||||
const bool connection_change = op_port.portsc & XHCI::PORTSC::CSC;
|
||||
op_port.portsc = XHCI::PORTSC::CSC | XHCI::PORTSC::PRC | XHCI::PORTSC::PP;
|
||||
|
||||
if (!(op_port.portsc & XHCI::PORTSC::CCS))
|
||||
{
|
||||
// if device detached, clear the port
|
||||
if (my_port.slot_id != 0)
|
||||
{
|
||||
m_slots[my_port.slot_id - 1].clear();
|
||||
my_port.slot_id = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (my_port.revision_major)
|
||||
{
|
||||
case 2:
|
||||
if (!reset_change)
|
||||
{
|
||||
if (connection_change)
|
||||
op_port.portsc = XHCI::PORTSC::PR | XHCI::PORTSC::PP;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!connection_change)
|
||||
continue;
|
||||
dprintln_if(DEBUG_XHCI, "USB 3 devices not supported");
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto ret = initialize_slot(i); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not initialize USB {H}.{H} device: {}",
|
||||
my_port.revision_major,
|
||||
my_port.revision_minor >> 4,
|
||||
ret.error()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::initialize_slot(int port_index)
|
||||
{
|
||||
auto& my_port = m_ports[port_index];
|
||||
|
||||
XHCI::TRB enable_slot { .enable_slot_command {} };
|
||||
enable_slot.enable_slot_command.trb_type = XHCI::TRBType::EnableSlotCommand;
|
||||
enable_slot.enable_slot_command.slot_type = my_port.slot_type;
|
||||
auto result = TRY(send_command(enable_slot));
|
||||
|
||||
uint8_t slot_id = result.command_completion_event.slot_id;
|
||||
if (slot_id == 0 || slot_id > capability_regs().hcsparams1.max_slots)
|
||||
{
|
||||
dwarnln("EnableSlot gave an invalid slot {}", slot_id);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
dprintln_if(DEBUG_XHCI, "allocated slot {} for port {}", slot_id, port_index + 1);
|
||||
|
||||
m_slots[slot_id - 1] = TRY(XHCIDevice::create(*this, port_index + 1, slot_id));
|
||||
if (auto ret = m_slots[slot_id - 1]->initialize(); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not initialize device on slot {}: {}", slot_id, ret.error());
|
||||
m_slots[slot_id - 1].clear();
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
my_port.slot_id = slot_id;
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "device on slot {} initialized", slot_id);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIController::reset_controller()
|
||||
{
|
||||
auto& operational = operational_regs();
|
||||
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 500;
|
||||
|
||||
// wait until controller not ready clears
|
||||
while (operational.usbsts & XHCI::USBSTS::ControllerNotReady)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
// issue software reset and wait for it to clear
|
||||
operational.usbcmd.host_controller_reset = 1;
|
||||
while (operational.usbcmd.host_controller_reset)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<XHCI::TRB> XHCIController::send_command(const XHCI::TRB& trb)
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
|
||||
auto& command_trb = reinterpret_cast<volatile XHCI::TRB*>(m_command_ring_region->vaddr())[m_command_enqueue];
|
||||
command_trb.raw.dword0 = trb.raw.dword0;
|
||||
command_trb.raw.dword1 = trb.raw.dword1;
|
||||
command_trb.raw.dword2 = trb.raw.dword2;
|
||||
command_trb.raw.dword3 = trb.raw.dword3;
|
||||
command_trb.cycle = m_command_cycle;
|
||||
|
||||
auto& completion_trb = const_cast<volatile XHCI::TRB&>(m_command_completions[m_command_enqueue]);
|
||||
completion_trb.raw.dword0 = 0;
|
||||
completion_trb.raw.dword1 = 0;
|
||||
completion_trb.raw.dword2 = 0;
|
||||
completion_trb.raw.dword3 = 0;
|
||||
|
||||
advance_command_enqueue();
|
||||
|
||||
doorbell_reg(0) = 0;
|
||||
|
||||
uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 1000;
|
||||
while ((__atomic_load_n(&completion_trb.raw.dword2, __ATOMIC_SEQ_CST) >> 24) == 0)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
if (completion_trb.command_completion_event.completion_code != 1)
|
||||
{
|
||||
dwarnln("Completion error: {}", +completion_trb.command_completion_event.completion_code);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return BAN::bit_cast<XHCI::TRB>(completion_trb);
|
||||
}
|
||||
|
||||
void XHCIController::advance_command_enqueue()
|
||||
{
|
||||
m_command_enqueue++;
|
||||
if (m_command_enqueue < m_command_ring_trb_count - 1)
|
||||
return;
|
||||
|
||||
auto& link_trb = reinterpret_cast<volatile XHCI::TRB*>(m_command_ring_region->vaddr())[m_command_enqueue].link_trb;
|
||||
link_trb.trb_type = XHCI::TRBType::Link;
|
||||
link_trb.ring_segment_ponter = m_command_ring_region->paddr();
|
||||
link_trb.interrupter_target = 0;
|
||||
link_trb.cycle_bit = m_command_cycle;
|
||||
link_trb.toggle_cycle = 1;
|
||||
link_trb.chain_bit = 0;
|
||||
link_trb.interrupt_on_completion = 0;
|
||||
|
||||
m_command_enqueue = 0;
|
||||
m_command_cycle = !m_command_cycle;
|
||||
}
|
||||
|
||||
void XHCIController::handle_irq()
|
||||
{
|
||||
auto& operational = operational_regs();
|
||||
if (!(operational.usbsts & XHCI::USBSTS::EventInterrupt))
|
||||
return;
|
||||
operational.usbsts = XHCI::USBSTS::EventInterrupt;
|
||||
|
||||
auto& primary_interrupter = runtime_regs().irs[0];
|
||||
primary_interrupter.iman = primary_interrupter.iman | XHCI::IMAN::InterruptPending | XHCI::IMAN::InterruptEnable;
|
||||
|
||||
if (current_event_trb().cycle == m_event_cycle)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
auto& trb = current_event_trb();
|
||||
if (trb.cycle != m_event_cycle)
|
||||
break;
|
||||
|
||||
switch (trb.trb_type)
|
||||
{
|
||||
case XHCI::TRBType::TransferEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "TransferEvent");
|
||||
|
||||
const uint32_t slot_id = trb.transfer_event.slot_id;
|
||||
if (slot_id == 0 || slot_id > m_slots.size() || !m_slots[slot_id - 1])
|
||||
{
|
||||
dwarnln("TransferEvent for invalid slot {}", slot_id);
|
||||
dwarnln("Completion error: {}", +trb.transfer_event.completion_code);
|
||||
break;
|
||||
}
|
||||
|
||||
m_slots[slot_id - 1]->on_transfer_event(trb);
|
||||
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::CommandCompletionEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "CommandCompletionEvent");
|
||||
|
||||
const uint32_t trb_index = (trb.command_completion_event.command_trb_pointer - m_command_ring_region->paddr()) / sizeof(XHCI::TRB);
|
||||
|
||||
// NOTE: dword2 is last (and atomic) as that is what send_command is waiting for
|
||||
auto& completion_trb = const_cast<volatile XHCI::TRB&>(m_command_completions[trb_index]);
|
||||
completion_trb.raw.dword0 = trb.raw.dword0;
|
||||
completion_trb.raw.dword1 = trb.raw.dword1;
|
||||
completion_trb.raw.dword3 = trb.raw.dword3;
|
||||
__atomic_store_n(&completion_trb.raw.dword2, trb.raw.dword2, __ATOMIC_SEQ_CST);
|
||||
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::PortStatusChangeEvent:
|
||||
{
|
||||
dprintln_if(DEBUG_XHCI, "PortStatusChangeEvent");
|
||||
uint8_t port_id = trb.port_status_chage_event.port_id;
|
||||
if (port_id > capability_regs().hcsparams1.max_ports)
|
||||
{
|
||||
dwarnln("PortStatusChangeEvent on non-existent port {}", port_id);
|
||||
break;
|
||||
}
|
||||
m_port_changed = true;
|
||||
m_port_semaphore.unblock();
|
||||
break;
|
||||
}
|
||||
case XHCI::TRBType::BandwidthRequestEvent:
|
||||
dwarnln("Unhandled BandwidthRequestEvent");
|
||||
break;
|
||||
case XHCI::TRBType::DoorbellEvent:
|
||||
dwarnln("Unhandled DoorbellEvent");
|
||||
break;
|
||||
case XHCI::TRBType::HostControllerEvent:
|
||||
dwarnln("Unhandled HostControllerEvent");
|
||||
break;
|
||||
case XHCI::TRBType::DeviceNotificationEvent:
|
||||
dwarnln("Unhandled DeviceNotificationEvent");
|
||||
break;
|
||||
case XHCI::TRBType::MFINDEXWrapEvent:
|
||||
dwarnln("Unhandled MFINDEXWrapEvent");
|
||||
break;
|
||||
default:
|
||||
dwarnln("Unrecognized event TRB type {}", +trb.trb_type);
|
||||
break;
|
||||
}
|
||||
|
||||
m_event_dequeue++;
|
||||
if (m_event_dequeue >= m_event_ring_trb_count)
|
||||
{
|
||||
m_event_dequeue = 0;
|
||||
m_event_cycle = !m_event_cycle;
|
||||
}
|
||||
}
|
||||
|
||||
primary_interrupter.erdp = (m_event_ring_region->paddr() + (m_event_dequeue * sizeof(XHCI::TRB))) | XHCI::ERDP::EventHandlerBusy;
|
||||
}
|
||||
}
|
||||
|
||||
volatile XHCI::CapabilityRegs& XHCIController::capability_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::CapabilityRegs*>(m_configuration_bar->vaddr());
|
||||
}
|
||||
|
||||
volatile XHCI::OperationalRegs& XHCIController::operational_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::OperationalRegs*>(m_configuration_bar->vaddr() + capability_regs().caplength);
|
||||
}
|
||||
|
||||
volatile XHCI::RuntimeRegs& XHCIController::runtime_regs()
|
||||
{
|
||||
return *reinterpret_cast<volatile XHCI::RuntimeRegs*>(m_configuration_bar->vaddr() + (capability_regs().rstoff & ~0x1Fu));
|
||||
}
|
||||
|
||||
volatile uint32_t& XHCIController::doorbell_reg(uint32_t slot_id)
|
||||
{
|
||||
return reinterpret_cast<volatile uint32_t*>(m_configuration_bar->vaddr() + capability_regs().dboff)[slot_id];
|
||||
}
|
||||
|
||||
const volatile XHCI::TRB& XHCIController::current_event_trb()
|
||||
{
|
||||
return reinterpret_cast<const volatile XHCI::TRB*>(m_event_ring_region->vaddr())[m_event_dequeue];;
|
||||
}
|
||||
|
||||
volatile uint64_t& XHCIController::dcbaa_reg(uint32_t slot_id)
|
||||
{
|
||||
return reinterpret_cast<volatile uint64_t*>(m_dcbaa_region->vaddr())[slot_id];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,447 @@
|
|||
#include <BAN/Bitcast.h>
|
||||
#include <BAN/ByteSpan.h>
|
||||
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/XHCI/Device.h>
|
||||
|
||||
#define DEBUG_XHCI 0
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<XHCIDevice>> XHCIDevice::create(XHCIController& controller, uint32_t port_id, uint32_t slot_id)
|
||||
{
|
||||
return TRY(BAN::UniqPtr<XHCIDevice>::create(controller, port_id, slot_id));
|
||||
}
|
||||
|
||||
XHCIDevice::~XHCIDevice()
|
||||
{
|
||||
XHCI::TRB disable_slot { .disable_slot_command {} };
|
||||
disable_slot.disable_slot_command.trb_type = XHCI::TRBType::DisableSlotCommand;
|
||||
disable_slot.disable_slot_command.slot_id = m_slot_id;
|
||||
if (auto ret = m_controller.send_command(disable_slot); ret.is_error())
|
||||
dwarnln("Could not disable slot {}: {}", m_slot_id, ret.error());
|
||||
else
|
||||
dprintln_if(DEBUG_XHCI, "Slot {} disabled", m_slot_id);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::initialize_control_endpoint()
|
||||
{
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
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)
|
||||
{
|
||||
case USB::SpeedClass::LowSpeed:
|
||||
case USB::SpeedClass::FullSpeed:
|
||||
m_endpoints[0].max_packet_size = 8;
|
||||
break;
|
||||
case USB::SpeedClass::HighSpeed:
|
||||
m_endpoints[0].max_packet_size = 64;
|
||||
break;
|
||||
case USB::SpeedClass::SuperSpeed:
|
||||
m_endpoints[0].max_packet_size = 512;
|
||||
break;
|
||||
default: ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
m_input_context = TRY(DMARegion::create(33 * context_size));
|
||||
memset(reinterpret_cast<void*>(m_input_context->vaddr()), 0, m_input_context->size());
|
||||
|
||||
m_output_context = TRY(DMARegion::create(32 * context_size));
|
||||
memset(reinterpret_cast<void*>(m_output_context->vaddr()), 0, m_output_context->size());
|
||||
|
||||
m_endpoints[0].transfer_ring = TRY(DMARegion::create(m_transfer_ring_trb_count * sizeof(XHCI::TRB)));
|
||||
memset(reinterpret_cast<void*>(m_endpoints[0].transfer_ring->vaddr()), 0, m_endpoints[0].transfer_ring->size());
|
||||
|
||||
{
|
||||
auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr() + 0 * context_size);
|
||||
auto& slot_context = *reinterpret_cast<XHCI::SlotContext*> (m_input_context->vaddr() + 1 * context_size);
|
||||
auto& endpoint0_context = *reinterpret_cast<XHCI::EndpointContext*> (m_input_context->vaddr() + 2 * context_size);
|
||||
|
||||
memset(&input_control_context, 0, context_size);
|
||||
input_control_context.add_context_flags = 0b11;
|
||||
|
||||
memset(&slot_context, 0, context_size);
|
||||
slot_context.route_string = 0;
|
||||
slot_context.root_hub_port_number = m_port_id;
|
||||
slot_context.context_entries = 1;
|
||||
slot_context.interrupter_target = 0;
|
||||
slot_context.speed = speed_id;
|
||||
// FIXME: 4.5.2 hub
|
||||
|
||||
memset(&endpoint0_context, 0, context_size);
|
||||
endpoint0_context.endpoint_type = XHCI::EndpointType::Control;
|
||||
endpoint0_context.max_packet_size = m_endpoints[0].max_packet_size;
|
||||
endpoint0_context.error_count = 3;
|
||||
endpoint0_context.tr_dequeue_pointer = m_endpoints[0].transfer_ring->paddr() | 1;
|
||||
}
|
||||
|
||||
m_controller.dcbaa_reg(m_slot_id) = m_output_context->paddr();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
XHCI::TRB address_device { .address_device_command = {} };
|
||||
address_device.address_device_command.trb_type = XHCI::TRBType::AddressDeviceCommand;
|
||||
address_device.address_device_command.input_context_pointer = m_input_context->paddr();
|
||||
// NOTE: some legacy devices require sending request with BSR=1 before actual BSR=0
|
||||
address_device.address_device_command.block_set_address_request = (i == 0);
|
||||
address_device.address_device_command.slot_id = m_slot_id;
|
||||
TRY(m_controller.send_command(address_device));
|
||||
}
|
||||
|
||||
// NOTE: Full speed devices can have other max packet sizes than 8
|
||||
if (speed_class == USB::SpeedClass::FullSpeed)
|
||||
TRY(update_actual_max_packet_size());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::update_actual_max_packet_size()
|
||||
{
|
||||
// FIXME: This is more or less generic USB code
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "Retrieving actual max packet size of full speed device");
|
||||
|
||||
BAN::Vector<uint8_t> buffer;
|
||||
TRY(buffer.resize(8, 0));
|
||||
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = 0x0100;
|
||||
request.wIndex = 0;
|
||||
request.wLength = 8;
|
||||
TRY(send_request(request, kmalloc_paddr_of((vaddr_t)buffer.data()).value()));
|
||||
|
||||
m_endpoints[0].max_packet_size = buffer.back();
|
||||
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
{
|
||||
auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr() + 0 * context_size);
|
||||
auto& slot_context = *reinterpret_cast<XHCI::SlotContext*> (m_input_context->vaddr() + 1 * context_size);
|
||||
auto& endpoint0_context = *reinterpret_cast<XHCI::EndpointContext*> (m_input_context->vaddr() + 2 * context_size);
|
||||
|
||||
memset(&input_control_context, 0, context_size);
|
||||
input_control_context.add_context_flags = 0b11;
|
||||
|
||||
memset(&slot_context, 0, context_size);
|
||||
slot_context.max_exit_latency = 0; // FIXME:
|
||||
slot_context.interrupter_target = 0;
|
||||
|
||||
memset(&endpoint0_context, 0, context_size);
|
||||
endpoint0_context.endpoint_type = XHCI::EndpointType::Control;
|
||||
endpoint0_context.max_packet_size = m_endpoints[0].max_packet_size;
|
||||
endpoint0_context.error_count = 3;
|
||||
endpoint0_context.tr_dequeue_pointer = m_endpoints[0].transfer_ring->paddr() | 1;
|
||||
}
|
||||
|
||||
XHCI::TRB evaluate_context { .address_device_command = {} };
|
||||
evaluate_context.address_device_command.trb_type = XHCI::TRBType::EvaluateContextCommand;
|
||||
evaluate_context.address_device_command.input_context_pointer = m_input_context->paddr();
|
||||
evaluate_context.address_device_command.block_set_address_request = 0;
|
||||
evaluate_context.address_device_command.slot_id = m_slot_id;
|
||||
TRY(m_controller.send_command(evaluate_context));
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "successfully updated max packet size to {}", m_endpoints[0].max_packet_size);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::initialize_endpoint(const USBEndpointDescriptor& endpoint_descriptor)
|
||||
{
|
||||
const uint32_t endpoint_id = (endpoint_descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint_descriptor.bEndpointAddress & 0x80);
|
||||
|
||||
auto& endpoint = m_endpoints[endpoint_id - 1];
|
||||
ASSERT(!endpoint.transfer_ring);
|
||||
|
||||
uint32_t last_valid_endpoint_id = endpoint_id;
|
||||
for (size_t i = endpoint_id; i < m_endpoints.size(); i++)
|
||||
if (m_endpoints[i].transfer_ring)
|
||||
last_valid_endpoint_id = i + 1;
|
||||
|
||||
endpoint.transfer_ring = TRY(DMARegion::create(m_transfer_ring_trb_count * sizeof(XHCI::TRB)));
|
||||
endpoint.max_packet_size = endpoint_descriptor.wMaxPacketSize & 0x07FF;
|
||||
endpoint.dequeue_index = 0;
|
||||
endpoint.enqueue_index = 0;
|
||||
endpoint.cycle_bit = 1;
|
||||
endpoint.callback = &XHCIDevice::on_interrupt_endpoint_event;
|
||||
endpoint.data_region = TRY(DMARegion::create(endpoint.max_packet_size));
|
||||
|
||||
memset(reinterpret_cast<void*>(endpoint.transfer_ring->vaddr()), 0, endpoint.transfer_ring->size());
|
||||
|
||||
{
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr());
|
||||
auto& slot_context = *reinterpret_cast<XHCI::SlotContext*> (m_input_context->vaddr() + context_size);
|
||||
auto& endpoint_context = *reinterpret_cast<XHCI::EndpointContext*> (m_input_context->vaddr() + (endpoint_id + 1) * context_size);
|
||||
|
||||
memset(&input_control_context, 0, context_size);
|
||||
input_control_context.add_context_flags = (1u << endpoint_id) | 1;
|
||||
|
||||
memset(&slot_context, 0, context_size);
|
||||
slot_context.context_entries = last_valid_endpoint_id;
|
||||
// FIXME: 4.5.2 hub
|
||||
|
||||
ASSERT(endpoint_descriptor.bEndpointAddress & 0x80);
|
||||
ASSERT((endpoint_descriptor.bmAttributes & 0x03) == 3);
|
||||
ASSERT(m_controller.port(m_port_id).revision_major == 2);
|
||||
|
||||
memset(&endpoint_context, 0, context_size);
|
||||
endpoint_context.endpoint_type = XHCI::EndpointType::InterruptIn;
|
||||
endpoint_context.max_packet_size = endpoint.max_packet_size;
|
||||
endpoint_context.max_burst_size = (endpoint_descriptor.wMaxPacketSize >> 11) & 0x0003;
|
||||
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;
|
||||
}
|
||||
|
||||
XHCI::TRB configure_endpoint { .configure_endpoint_command = {} };
|
||||
configure_endpoint.configure_endpoint_command.trb_type = XHCI::TRBType::ConfigureEndpointCommand;
|
||||
configure_endpoint.configure_endpoint_command.input_context_pointer = m_input_context->paddr();
|
||||
configure_endpoint.configure_endpoint_command.deconfigure = 0;
|
||||
configure_endpoint.configure_endpoint_command.slot_id = m_slot_id;
|
||||
TRY(m_controller.send_command(configure_endpoint));
|
||||
|
||||
auto& trb = *reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr());
|
||||
memset(const_cast<XHCI::TRB*>(&trb), 0, sizeof(XHCI::TRB));
|
||||
trb.normal.trb_type = XHCI::TRBType::Normal;
|
||||
trb.normal.data_buffer_pointer = endpoint.data_region->paddr();
|
||||
trb.normal.trb_transfer_length = endpoint.data_region->size();
|
||||
trb.normal.td_size = 0;
|
||||
trb.normal.interrupt_target = 0;
|
||||
trb.normal.cycle_bit = 1;
|
||||
trb.normal.interrupt_on_completion = 1;
|
||||
trb.normal.interrupt_on_short_packet = 1;
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
|
||||
m_controller.doorbell_reg(m_slot_id) = endpoint_id;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void XHCIDevice::on_interrupt_endpoint_event(XHCI::TRB trb)
|
||||
{
|
||||
ASSERT(trb.trb_type == XHCI::TRBType::TransferEvent);
|
||||
if (trb.transfer_event.completion_code != 1 && trb.transfer_event.completion_code != 13)
|
||||
{
|
||||
dwarnln("Interrupt endpoint got transfer event with completion code {}", +trb.transfer_event.completion_code);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t endpoint_id = trb.transfer_event.endpoint_id;
|
||||
auto& endpoint = m_endpoints[endpoint_id - 1];
|
||||
ASSERT(endpoint.transfer_ring && endpoint.data_region);
|
||||
|
||||
const uint32_t transfer_length = endpoint.max_packet_size - trb.transfer_event.trb_transfer_length;
|
||||
auto received_data = BAN::ConstByteSpan(reinterpret_cast<uint8_t*>(endpoint.data_region->vaddr()), transfer_length);
|
||||
handle_input_data(received_data, endpoint_id);
|
||||
|
||||
auto& new_trb = *reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr() + endpoint.enqueue_index * sizeof(XHCI::TRB));
|
||||
memset(const_cast<XHCI::TRB*>(&new_trb), 0, sizeof(XHCI::TRB));
|
||||
new_trb.normal.trb_type = XHCI::TRBType::Normal;
|
||||
new_trb.normal.data_buffer_pointer = endpoint.data_region->paddr();
|
||||
new_trb.normal.trb_transfer_length = endpoint.max_packet_size;
|
||||
new_trb.normal.td_size = 0;
|
||||
new_trb.normal.interrupt_target = 0;
|
||||
new_trb.normal.cycle_bit = endpoint.cycle_bit;
|
||||
new_trb.normal.interrupt_on_completion = 1;
|
||||
new_trb.normal.interrupt_on_short_packet = 1;
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
|
||||
m_controller.doorbell_reg(m_slot_id) = endpoint_id;
|
||||
}
|
||||
|
||||
void XHCIDevice::on_transfer_event(const volatile XHCI::TRB& trb)
|
||||
{
|
||||
ASSERT(trb.trb_type == XHCI::TRBType::TransferEvent);
|
||||
if (trb.transfer_event.endpoint_id == 0)
|
||||
{
|
||||
dwarnln("TransferEvent for endpoint id 0");
|
||||
return;
|
||||
}
|
||||
|
||||
auto& endpoint = m_endpoints[trb.transfer_event.endpoint_id - 1];
|
||||
|
||||
if (endpoint.callback)
|
||||
{
|
||||
XHCI::TRB copy;
|
||||
copy.raw.dword0 = trb.raw.dword0;
|
||||
copy.raw.dword1 = trb.raw.dword1;
|
||||
copy.raw.dword2 = trb.raw.dword2;
|
||||
copy.raw.dword3 = trb.raw.dword3;
|
||||
(this->*endpoint.callback)(copy);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get received bytes from short packet
|
||||
if (trb.transfer_event.completion_code == 13)
|
||||
{
|
||||
auto* transfer_trb_arr = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr());
|
||||
|
||||
const uint32_t trb_index = (trb.transfer_event.trb_pointer - endpoint.transfer_ring->paddr()) / sizeof(XHCI::TRB);
|
||||
|
||||
const uint32_t full_trbs_transferred = (trb_index >= endpoint.dequeue_index)
|
||||
? trb_index - 1 - endpoint.dequeue_index
|
||||
: trb_index + m_transfer_ring_trb_count - 2 - endpoint.dequeue_index;
|
||||
|
||||
const uint32_t full_trb_data = full_trbs_transferred * endpoint.max_packet_size;
|
||||
const uint32_t short_data = transfer_trb_arr[trb_index].data_stage.trb_transfer_length - trb.transfer_event.trb_transfer_length;
|
||||
|
||||
endpoint.transfer_count = full_trb_data + short_data;
|
||||
|
||||
ASSERT(trb_index >= endpoint.dequeue_index);
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: dword2 is last (and atomic) as that is what send_request is waiting for
|
||||
auto& completion_trb = endpoint.completion_trb;
|
||||
completion_trb.raw.dword0 = trb.raw.dword0;
|
||||
completion_trb.raw.dword1 = trb.raw.dword1;
|
||||
completion_trb.raw.dword3 = trb.raw.dword3;
|
||||
__atomic_store_n(&completion_trb.raw.dword2, trb.raw.dword2, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> XHCIDevice::send_request(const USBDeviceRequest& request, paddr_t buffer_paddr)
|
||||
{
|
||||
// FIXME: This is more or less generic USB code
|
||||
|
||||
auto& endpoint = m_endpoints[0];
|
||||
|
||||
// minus 3: Setup, Status, Link (this is probably too generous and will result in STALL)
|
||||
if (request.wLength > (m_transfer_ring_trb_count - 3) * endpoint.max_packet_size)
|
||||
return BAN::Error::from_errno((ENOBUFS));
|
||||
|
||||
LockGuard _(endpoint.mutex);
|
||||
|
||||
uint8_t transfer_type =
|
||||
[&request]() -> uint8_t
|
||||
{
|
||||
if (request.wLength == 0)
|
||||
return 0;
|
||||
if (request.bmRequestType & USB::RequestType::DeviceToHost)
|
||||
return 3;
|
||||
return 2;
|
||||
}();
|
||||
|
||||
auto* transfer_trb_arr = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr());
|
||||
|
||||
{
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset(const_cast<XHCI::TRB*>(&trb), 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.setup_stage.trb_type = XHCI::TRBType::SetupStage;
|
||||
trb.setup_stage.transfer_type = transfer_type;
|
||||
trb.setup_stage.trb_transfer_length = 8;
|
||||
trb.setup_stage.interrupt_on_completion = 0;
|
||||
trb.setup_stage.immediate_data = 1;
|
||||
trb.setup_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
trb.setup_stage.bmRequestType = request.bmRequestType;
|
||||
trb.setup_stage.bRequest = request.bRequest;
|
||||
trb.setup_stage.wValue = request.wValue;
|
||||
trb.setup_stage.wIndex = request.wIndex;
|
||||
trb.setup_stage.wLength = request.wLength;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
}
|
||||
|
||||
const uint32_t td_packet_count = BAN::Math::div_round_up<uint32_t>(request.wLength, endpoint.max_packet_size);
|
||||
uint32_t packets_transferred = 1;
|
||||
|
||||
uint32_t bytes_handled = 0;
|
||||
while (bytes_handled < request.wLength)
|
||||
{
|
||||
const uint32_t to_handle = BAN::Math::min<uint32_t>(endpoint.max_packet_size, request.wLength - bytes_handled);
|
||||
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset(const_cast<XHCI::TRB*>(&trb), 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.data_stage.trb_type = XHCI::TRBType::DataStage;
|
||||
trb.data_stage.direction = 1;
|
||||
trb.data_stage.trb_transfer_length = to_handle;
|
||||
trb.data_stage.td_size = BAN::Math::min<uint32_t>(td_packet_count - packets_transferred, 31);
|
||||
trb.data_stage.chain_bit = (bytes_handled + to_handle < request.wLength);
|
||||
trb.data_stage.interrupt_on_completion = 0;
|
||||
trb.data_stage.interrupt_on_short_packet = 1;
|
||||
trb.data_stage.immediate_data = 0;
|
||||
trb.data_stage.data_buffer_pointer = buffer_paddr + bytes_handled;
|
||||
trb.data_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
bytes_handled += to_handle;
|
||||
packets_transferred++;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, trb.data_stage.chain_bit);
|
||||
}
|
||||
|
||||
{
|
||||
auto& trb = transfer_trb_arr[endpoint.enqueue_index];
|
||||
memset(const_cast<XHCI::TRB*>(&trb), 0, sizeof(XHCI::TRB));
|
||||
|
||||
trb.status_stage.trb_type = XHCI::TRBType::StatusStage;
|
||||
trb.status_stage.direction = 0;
|
||||
trb.status_stage.chain_bit = 0;
|
||||
trb.status_stage.interrupt_on_completion = 1;
|
||||
trb.data_stage.cycle_bit = endpoint.cycle_bit;
|
||||
|
||||
advance_endpoint_enqueue(endpoint, false);
|
||||
}
|
||||
|
||||
auto& completion_trb = endpoint.completion_trb;
|
||||
completion_trb.raw.dword0 = 0;
|
||||
completion_trb.raw.dword1 = 0;
|
||||
completion_trb.raw.dword2 = 0;
|
||||
completion_trb.raw.dword3 = 0;
|
||||
|
||||
endpoint.transfer_count = request.wLength;
|
||||
|
||||
m_controller.doorbell_reg(m_slot_id) = 1;
|
||||
|
||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 1000;
|
||||
while ((__atomic_load_n(&completion_trb.raw.dword2, __ATOMIC_SEQ_CST) >> 24) == 0)
|
||||
if (SystemTimer::get().ms_since_boot() > timeout_ms)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
||||
endpoint.dequeue_index = endpoint.enqueue_index;
|
||||
|
||||
if (completion_trb.transfer_event.completion_code != 1)
|
||||
{
|
||||
dwarnln("Completion error: {}", +completion_trb.transfer_event.completion_code);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return endpoint.transfer_count;
|
||||
}
|
||||
|
||||
void XHCIDevice::advance_endpoint_enqueue(Endpoint& endpoint, bool chain)
|
||||
{
|
||||
endpoint.enqueue_index++;
|
||||
if (endpoint.enqueue_index < m_transfer_ring_trb_count - 1)
|
||||
return;
|
||||
|
||||
// This is the last TRB in transfer ring. Make it link to the beginning of the ring
|
||||
auto& link_trb = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr())[endpoint.enqueue_index].link_trb;
|
||||
link_trb.trb_type = XHCI::TRBType::Link;
|
||||
link_trb.ring_segment_ponter = endpoint.transfer_ring->paddr();
|
||||
link_trb.interrupter_target = 0;
|
||||
link_trb.cycle_bit = endpoint.cycle_bit;
|
||||
link_trb.toggle_cycle = 1;
|
||||
link_trb.chain_bit = chain;
|
||||
link_trb.interrupt_on_completion = 0;
|
||||
|
||||
endpoint.enqueue_index = 0;
|
||||
endpoint.cycle_bit = !endpoint.cycle_bit;
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
#include <kernel/Terminal/Serial.h>
|
||||
#include <kernel/Terminal/VirtualTTY.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <kernel/USB/USBManager.h>
|
||||
|
||||
#include <LibInput/KeyboardLayout.h>
|
||||
|
||||
|
@ -200,16 +201,23 @@ static void init2(void*)
|
|||
|
||||
// Initialize empty keymap
|
||||
MUST(LibInput::KeyboardLayout::initialize());
|
||||
if (auto res = PS2Controller::initialize(); res.is_error())
|
||||
dprintln("{}", res.error());
|
||||
|
||||
// FIXME: initialize PS/2 after USB
|
||||
//if (auto res = PS2Controller::initialize(); res.is_error())
|
||||
// dprintln("{}", res.error());
|
||||
|
||||
MUST(NetworkManager::initialize());
|
||||
|
||||
MUST(USBManager::initialize());
|
||||
|
||||
// NOTE: PCI devices are the last ones to be initialized
|
||||
// so other devices can reserve predefined interrupts
|
||||
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");
|
||||
|
||||
|
|
|
@ -31,5 +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 \
|
||||
$DISK_ARGS \
|
||||
$@ \
|
||||
|
|
|
@ -36,7 +36,7 @@ BAN::Optional<BAN::String> parse_dollar(BAN::StringView command, size_t& i)
|
|||
ASSERT(command[i] == '$');
|
||||
|
||||
if (++i >= command.size())
|
||||
return "$"_sv;
|
||||
return BAN::String("$"_sv);
|
||||
|
||||
if (command[i] == '?')
|
||||
{
|
||||
|
@ -53,8 +53,8 @@ BAN::Optional<BAN::String> parse_dollar(BAN::StringView command, size_t& i)
|
|||
i += len - 1;
|
||||
|
||||
if (const char* value = getenv(name.data()))
|
||||
return BAN::StringView(value);
|
||||
return ""_sv;
|
||||
return BAN::String(value);
|
||||
return BAN::String();
|
||||
}
|
||||
else if (command[i] == '{')
|
||||
{
|
||||
|
@ -74,8 +74,8 @@ BAN::Optional<BAN::String> parse_dollar(BAN::StringView command, size_t& i)
|
|||
i += len;
|
||||
|
||||
if (const char* value = getenv(name.data()))
|
||||
return BAN::StringView(value);
|
||||
return ""_sv;
|
||||
return BAN::String(value);
|
||||
return BAN::String();
|
||||
}
|
||||
else if (command[i] == '[')
|
||||
{
|
||||
|
|
|
@ -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/input0", O_RDONLY);
|
||||
int keyboard_fd = open("/dev/keyboard0", O_RDONLY);
|
||||
if (keyboard_fd == -1)
|
||||
perror("open");
|
||||
|
||||
int mouse_fd = open("/dev/input1", O_RDONLY);
|
||||
int mouse_fd = open("/dev/mouse0", O_RDONLY);
|
||||
if (mouse_fd == -1)
|
||||
perror("open");
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ void cleanup()
|
|||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* fb_path = "/dev/fb0";
|
||||
const char* mouse_path = "/dev/input1";
|
||||
const char* mouse_path = "/dev/mouse0";
|
||||
|
||||
if (argc == 1)
|
||||
;
|
||||
|
|
Loading…
Reference in New Issue