Skip to content

Commit

Permalink
Kernel/HID: Introduce initial USB keyboard support
Browse files Browse the repository at this point in the history
  • Loading branch information
Olekoop committed May 4, 2024
1 parent 1678f43 commit 06a577e
Show file tree
Hide file tree
Showing 8 changed files with 1,261 additions and 6 deletions.
10 changes: 10 additions & 0 deletions Kernel/Bus/USB/Drivers/HID/Codes.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2024, Olekoop <mlglol360xd@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
Expand Down Expand Up @@ -27,6 +28,14 @@ struct [[gnu::packed]] MouseBootProtocolPacket {

static_assert(AssertSize<MouseBootProtocolPacket, 6>());

struct [[gnu::packed]] KeyboardBootProtocolPacket {
u8 modifiers;
u8 reverved;
u8 keypress[6];
};

static_assert(AssertSize<KeyboardBootProtocolPacket, 8>());

constexpr StringView subclass_string(SubclassCode code)
{
switch (code) {
Expand All @@ -39,6 +48,7 @@ constexpr StringView subclass_string(SubclassCode code)

enum class InterfaceProtocol : u8 {
Mouse = 0x02,
Keyboard = 0x01,
};

}
87 changes: 87 additions & 0 deletions Kernel/Bus/USB/Drivers/HID/KeyboardDriver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2024, Olekoop <mlglol360xd@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <AK/Find.h>
#include <Kernel/Bus/USB/Drivers/HID/Codes.h>
#include <Kernel/Bus/USB/Drivers/HID/KeyboardDriver.h>
#include <Kernel/Bus/USB/USBClasses.h>
#include <Kernel/Bus/USB/USBEndpoint.h>
#include <Kernel/Bus/USB/USBRequest.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/HID/KeyboardDevice.h>
#include <Kernel/Devices/HID/Management.h>
#include <Kernel/Devices/HID/USB/KeyboardDevice.h>

namespace Kernel::USB {

USB_DEVICE_DRIVER(KeyboardDriver);

void KeyboardDriver::init()
{
auto driver = MUST(adopt_nonnull_lock_ref_or_enomem(new KeyboardDriver()));
USBManagement::register_driver(driver);
}

ErrorOr<void> KeyboardDriver::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::Keyboard)) {
dmesgln("USB HID Keyboard Interface for device {:#04x}:{:#04x} found", device.device_descriptor().vendor_id, device.device_descriptor().product_id);
return initialize_device(device, interface);
}
return ENOTSUP;
}

ErrorOr<void> KeyboardDriver::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 keyboard device?
if (device.configurations().size() != 1)
return ENOTSUP;
// FIXME: If we have multiple USB configurations for a keyboard 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<void> KeyboardDriver::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 keyboard_device = TRY(KeyboardDevice::try_to_initialize());
auto usb_keyboard_device = TRY(USBKeyboardDevice::try_create_instance(device, endpoint_descriptor.max_packet_size, move(interrupt_in_pipe), keyboard_device));
HIDManagement::the().attach_standalone_hid_device(*usb_keyboard_device);
m_interfaces.append(usb_keyboard_device);
return {};
}

void KeyboardDriver::detach(USB::Device& device)
{
auto&& keyboard_device = AK::find_if(m_interfaces.begin(), m_interfaces.end(), [&device](auto& interface) { return &interface.device() == &device; });

HIDManagement::the().detach_standalone_hid_device(*keyboard_device);
m_interfaces.remove(*keyboard_device);
}

}
39 changes: 39 additions & 0 deletions Kernel/Bus/USB/Drivers/HID/KeyboardDriver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2024, Olekoop <mlglol360xd@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <Kernel/Bus/USB/Drivers/USBDriver.h>
#include <Kernel/Bus/USB/USBInterface.h>
#include <Kernel/Bus/USB/USBManagement.h>
#include <Kernel/Devices/HID/USB/KeyboardDevice.h>

namespace Kernel::USB {

class KeyboardDriver final : public Driver {
public:
KeyboardDriver()
: Driver("USB Keyboard"sv)
{
}

static void init();

virtual ~KeyboardDriver() override = default;

virtual ErrorOr<void> probe(USB::Device&) override;
virtual void detach(USB::Device&) override;

private:
USBKeyboardDevice::List m_interfaces;

ErrorOr<void> checkout_interface(USB::Device&, USBInterface const&);

ErrorOr<void> initialize_device(USB::Device&, USBInterface const&);
};

}
2 changes: 2 additions & 0 deletions Kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ set(KERNEL_SOURCES
Bus/USB/UHCI/UHCIController.cpp
Bus/USB/UHCI/UHCIRootHub.cpp
Bus/USB/Drivers/HID/MouseDriver.cpp
Bus/USB/Drivers/HID/KeyboardDriver.cpp
Bus/USB/Drivers/MassStorage/MassStorageDriver.cpp
Bus/USB/USBConfiguration.cpp
Bus/USB/USBController.cpp
Expand Down Expand Up @@ -75,6 +76,7 @@ set(KERNEL_SOURCES
Devices/HID/PS2/KeyboardDevice.cpp
Devices/HID/PS2/MouseDevice.cpp
Devices/HID/USB/MouseDevice.cpp
Devices/HID/USB/KeyboardDevice.cpp
Devices/Generic/ConsoleDevice.cpp
Devices/Generic/DeviceControlDevice.cpp
Devices/Generic/FullDevice.cpp
Expand Down
8 changes: 2 additions & 6 deletions Kernel/Devices/HID/KeyboardDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,12 @@ ErrorOr<NonnullRefPtr<KeyboardDevice>> KeyboardDevice::try_to_initialize()
return *TRY(DeviceManagement::try_create_device<KeyboardDevice>());
}

// FIXME: UNMAP_AFTER_INIT is fine for now, but for hot-pluggable devices
// like USB keyboards, we need to remove this
UNMAP_AFTER_INIT KeyboardDevice::KeyboardDevice()
KeyboardDevice::KeyboardDevice()
: HIDDevice(85, HIDManagement::the().generate_minor_device_number_for_keyboard())
{
}

// FIXME: UNMAP_AFTER_INIT is fine for now, but for hot-pluggable devices
// like USB keyboards, we need to remove this
UNMAP_AFTER_INIT KeyboardDevice::~KeyboardDevice() = default;
KeyboardDevice::~KeyboardDevice() = default;

bool KeyboardDevice::can_read(OpenFileDescription const&, u64) const
{
Expand Down
1 change: 1 addition & 0 deletions Kernel/Devices/HID/Management.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class HIDManagement {
friend class KeyboardDevice;
friend class MouseDevice;
friend class AllMiceDevice;
friend class USBKeyboardDevice;

public:
HIDManagement();
Expand Down

0 comments on commit 06a577e

Please sign in to comment.