From 77441079dd2178a030189e8da1239122e738ecb2 Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 5 May 2023 09:05:04 +0300 Subject: [PATCH] Kernel/HID: Introduce initial USB mouse support --- Kernel/Bus/USB/Drivers/HID/Codes.h | 44 ++++++++++++ Kernel/Bus/USB/Drivers/HID/MouseDriver.cpp | 83 ++++++++++++++++++++++ Kernel/Bus/USB/Drivers/HID/MouseDriver.h | 38 ++++++++++ Kernel/CMakeLists.txt | 2 + Kernel/Devices/HID/USB/MouseDevice.cpp | 55 ++++++++++++++ Kernel/Devices/HID/USB/MouseDevice.h | 44 ++++++++++++ 6 files changed, 266 insertions(+) create mode 100644 Kernel/Bus/USB/Drivers/HID/Codes.h create mode 100644 Kernel/Bus/USB/Drivers/HID/MouseDriver.cpp create mode 100644 Kernel/Bus/USB/Drivers/HID/MouseDriver.h create mode 100644 Kernel/Devices/HID/USB/MouseDevice.cpp create mode 100644 Kernel/Devices/HID/USB/MouseDevice.h diff --git a/Kernel/Bus/USB/Drivers/HID/Codes.h b/Kernel/Bus/USB/Drivers/HID/Codes.h new file mode 100644 index 0000000000..6d7e96a4c0 --- /dev/null +++ b/Kernel/Bus/USB/Drivers/HID/Codes.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Kernel::USB::HID { + +enum class SubclassCode : u8 { + BootProtocol = 0x01, +}; + +struct [[gnu::packed]] MouseBootProtocolPacket { + u8 buttons; + i8 x; + i8 y; + i8 z; + i8 reserved1; + i8 reserved2; +}; + +static_assert(AssertSize()); + +constexpr StringView subclass_string(SubclassCode code) +{ + switch (code) { + case SubclassCode::BootProtocol: + return "Boot Protocol"sv; + } + + return "Reserved"sv; +} + +enum class InterfaceProtocol : u8 { + Mouse = 0x02, +}; + +} diff --git a/Kernel/Bus/USB/Drivers/HID/MouseDriver.cpp b/Kernel/Bus/USB/Drivers/HID/MouseDriver.cpp new file mode 100644 index 0000000000..d7c411bb56 --- /dev/null +++ b/Kernel/Bus/USB/Drivers/HID/MouseDriver.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel::USB { + +USB_DEVICE_DRIVER(MouseDriver); + +void MouseDriver::init() +{ + auto driver = MUST(adopt_nonnull_lock_ref_or_enomem(new MouseDriver())); + USBManagement::the().register_driver(driver); +} + +ErrorOr MouseDriver::checkout_interface(USB::Device& device, USBInterface const& interface) +{ + auto const& descriptor = interface.descriptor(); + if (descriptor.interface_class_code == USB_CLASS_HID + && descriptor.interface_sub_class_code == to_underlying(HID::SubclassCode::BootProtocol) + && descriptor.interface_protocol == to_underlying(HID::InterfaceProtocol::Mouse)) { + dmesgln("USB HID Mouse Interface for device {:#04x}:{:#04x} found", device.device_descriptor().vendor_id, device.device_descriptor().product_id); + return initialize_device(device, interface); + } + return ENOTSUP; +} + +ErrorOr MouseDriver::probe(USB::Device& device) +{ + if (device.device_descriptor().device_class != USB_CLASS_DEVICE + || device.device_descriptor().device_sub_class != 0x00 + || device.device_descriptor().device_protocol != 0x00) + return ENOTSUP; + // FIXME: Are we guaranteed to have one USB configuration for a mouse device? + if (device.configurations().size() != 1) + return ENOTSUP; + // FIXME: If we have multiple USB configurations for a mouse device, find the appropriate one + // and handle multiple interfaces for it. + if (device.configurations()[0].interfaces().size() != 1) + return ENOTSUP; + + TRY(checkout_interface(device, device.configurations()[0].interfaces()[0])); + + return ENOTSUP; +} + +ErrorOr MouseDriver::initialize_device(USB::Device& device, USBInterface const& interface) +{ + if (interface.endpoints().size() != 1) + return ENOTSUP; + auto const& configuration = interface.configuration(); + // FIXME: Should we check other configurations? + TRY(device.control_transfer( + USB_REQUEST_RECIPIENT_DEVICE | USB_REQUEST_TYPE_STANDARD | USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE, + USB_REQUEST_SET_CONFIGURATION, configuration.configuration_id(), 0, 0, nullptr)); + + auto const& endpoint_descriptor = interface.endpoints()[0]; + auto interrupt_in_pipe = TRY(USB::InterruptInPipe::create(device.controller(), endpoint_descriptor.endpoint_address, endpoint_descriptor.max_packet_size, device.address(), 10)); + auto mouse_device = TRY(USBMouseDevice::try_create_instance(device, endpoint_descriptor.max_packet_size, move(interrupt_in_pipe))); + HIDManagement::the().attach_standalone_hid_device(*mouse_device); + m_interfaces.append(mouse_device); + return {}; +} + +void MouseDriver::detach(USB::Device& device) +{ + auto&& mouse_device = AK::find_if(m_interfaces.begin(), m_interfaces.end(), [&device](auto& interface) { return &interface.device() == &device; }); + + HIDManagement::the().detach_standalone_hid_device(*mouse_device); + m_interfaces.remove(*mouse_device); +} + +} diff --git a/Kernel/Bus/USB/Drivers/HID/MouseDriver.h b/Kernel/Bus/USB/Drivers/HID/MouseDriver.h new file mode 100644 index 0000000000..b57f3a863c --- /dev/null +++ b/Kernel/Bus/USB/Drivers/HID/MouseDriver.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::USB { + +class MouseDriver final : public Driver { +public: + MouseDriver() + : Driver("USB Mouse"sv) + { + } + + static void init(); + + virtual ~MouseDriver() override = default; + + virtual ErrorOr probe(USB::Device&) override; + virtual void detach(USB::Device&) override; + +private: + USBMouseDevice::List m_interfaces; + + ErrorOr checkout_interface(USB::Device&, USBInterface const&); + + ErrorOr initialize_device(USB::Device&, USBInterface const&); +}; + +} diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 22a47e601f..53d0dcdbb0 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -33,6 +33,7 @@ set(KERNEL_SOURCES Bus/PCI/DeviceIdentifier.cpp Bus/USB/UHCI/UHCIController.cpp Bus/USB/UHCI/UHCIRootHub.cpp + Bus/USB/Drivers/HID/MouseDriver.cpp Bus/USB/Drivers/MassStorage/MassStorageDriver.cpp Bus/USB/USBConfiguration.cpp Bus/USB/USBController.cpp @@ -73,6 +74,7 @@ set(KERNEL_SOURCES Devices/HID/MouseDevice.cpp Devices/HID/PS2/KeyboardDevice.cpp Devices/HID/PS2/MouseDevice.cpp + Devices/HID/USB/MouseDevice.cpp Devices/Generic/ConsoleDevice.cpp Devices/Generic/DeviceControlDevice.cpp Devices/Generic/FullDevice.cpp diff --git a/Kernel/Devices/HID/USB/MouseDevice.cpp b/Kernel/Devices/HID/USB/MouseDevice.cpp new file mode 100644 index 0000000000..e7f93490ba --- /dev/null +++ b/Kernel/Devices/HID/USB/MouseDevice.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +ErrorOr> USBMouseDevice::try_create_instance(USB::Device const& usb_device, size_t max_packet_size, NonnullOwnPtr pipe) +{ + if (max_packet_size < 4) + return Error::from_errno(ENOTSUP); + auto device = TRY(DeviceManagement::try_create_device(usb_device, move(pipe))); + TRY(device->create_and_start_polling_process(max_packet_size)); + return *device; +} + +ErrorOr USBMouseDevice::create_and_start_polling_process(size_t max_packet_size) +{ + VERIFY(max_packet_size >= 4); + [[maybe_unused]] auto interrupt_in_transfer = TRY(m_interrupt_in_pipe->submit_interrupt_in_transfer(max_packet_size, 10, [this](auto* transfer) { + USB::HID::MouseBootProtocolPacket packet_raw; + memcpy(&packet_raw, transfer->buffer().as_ptr(), 4); + MousePacket packet; + packet.buttons = packet_raw.buttons & 0x07; + packet.x = packet_raw.x; + packet.y = -packet_raw.y; + packet.z = -packet_raw.z; + packet.w = 0; + packet.is_relative = true; + + handle_mouse_packet_input_event(packet); + })); + return {}; +} + +USBMouseDevice::USBMouseDevice(USB::Device const& usb_device, NonnullOwnPtr pipe) + : MouseDevice() + , m_interrupt_in_pipe(move(pipe)) + , m_attached_usb_device(usb_device) +{ +} + +} diff --git a/Kernel/Devices/HID/USB/MouseDevice.h b/Kernel/Devices/HID/USB/MouseDevice.h new file mode 100644 index 0000000000..dad964f677 --- /dev/null +++ b/Kernel/Devices/HID/USB/MouseDevice.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022-2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class USBMouseDevice final : public MouseDevice { + friend class DeviceManagement; + +public: + static ErrorOr> try_create_instance(USB::Device const&, size_t max_packet_size, NonnullOwnPtr pipe); + virtual ~USBMouseDevice() override {}; + + USB::Device const& device() const { return *m_attached_usb_device; } + +private: + ErrorOr create_and_start_polling_process(size_t max_packet_size); + + USBMouseDevice(USB::Device const&, NonnullOwnPtr pipe); + NonnullOwnPtr m_interrupt_in_pipe; + NonnullRefPtr m_attached_usb_device; + + IntrusiveListNode> m_list_node; + +public: + using List = IntrusiveList<&USBMouseDevice::m_list_node>; +}; + +}