Kernel: Add support for DualShock 3 controllers

This driver accepts any HID joystick devices but button and axis
mappings will only work on a PS3 controller
This commit is contained in:
Bananymous 2026-01-03 19:48:28 +02:00
parent 08bfa0971e
commit 65664b0d65
9 changed files with 240 additions and 20 deletions

View File

@ -108,6 +108,7 @@ set(KERNEL_SOURCES
kernel/USB/Controller.cpp
kernel/USB/Device.cpp
kernel/USB/HID/HIDDriver.cpp
kernel/USB/HID/Joystick.cpp
kernel/USB/HID/Keyboard.cpp
kernel/USB/HID/Mouse.cpp
kernel/USB/Hub/HubDriver.cpp

View File

@ -16,6 +16,7 @@ namespace Kernel
Debug,
Keyboard,
Mouse,
Joystick,
SCSI,
NVMeController,
NVMeNamespace,

View File

@ -15,6 +15,7 @@ namespace Kernel
{
Mouse,
Keyboard,
Joystick,
};
public:

View File

@ -55,6 +55,8 @@ namespace Kernel
{}
virtual ~USBHIDDevice() = default;
virtual BAN::ErrorOr<void> initialize() { return {}; }
virtual void start_report() = 0;
virtual void stop_report() = 0;

View File

@ -0,0 +1,43 @@
#pragma once
#include <kernel/USB/HID/HIDDriver.h>
#include <LibInput/Joystick.h>
namespace Kernel
{
class USBJoystick final : public USBHIDDevice
{
BAN_NON_COPYABLE(USBJoystick);
BAN_NON_MOVABLE(USBJoystick);
public:
BAN::ErrorOr<void> initialize() override;
void start_report() override;
void stop_report() override;
void handle_array(uint16_t usage_page, uint16_t usage) override;
void handle_variable(uint16_t usage_page, uint16_t usage, int64_t state) override;
void handle_variable_absolute(uint16_t usage_page, uint16_t usage, int64_t state, int64_t min, int64_t max) override;
protected:
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
bool can_read_impl() const override { return true; }
private:
USBJoystick(USBHIDDriver&);
~USBJoystick() = default;
private:
USBHIDDriver& m_driver;
SpinLock m_state_lock;
InterruptState m_interrupt_state;
LibInput::JoystickState m_state {};
friend class BAN::RefPtr<USBJoystick>;
};
}

View File

@ -3,6 +3,7 @@
#include <kernel/Input/InputDevice.h>
#include <kernel/Lock/SpinLockAsMutex.h>
#include <LibInput/Joystick.h>
#include <LibInput/KeyEvent.h>
#include <LibInput/MouseEvent.h>
@ -18,6 +19,8 @@ namespace Kernel
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_mice;
static BAN::RefPtr<MouseDevice> s_mouse_device;
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_joysticks;
static const char* get_name_format(InputDevice::Type type)
{
switch (type)
@ -26,6 +29,8 @@ namespace Kernel
return "keyboard{}";
case InputDevice::Type::Mouse:
return "mouse{}";
case InputDevice::Type::Joystick:
return "joystick{}";
}
ASSERT_NOT_REACHED();
}
@ -44,6 +49,11 @@ namespace Kernel
if (!s_mice[i].valid())
return makedev(DeviceNumber::Mouse, i + 1);
return makedev(DeviceNumber::Mouse, s_mice.size() + 1);
case InputDevice::Type::Joystick:
for (size_t i = 0; i < s_joysticks.size(); i++)
if (!s_joysticks[i].valid())
return makedev(DeviceNumber::Joystick, i + 1);
return makedev(DeviceNumber::Joystick, s_joysticks.size() + 1);
}
ASSERT_NOT_REACHED();
}
@ -56,6 +66,8 @@ namespace Kernel
return sizeof(LibInput::RawKeyEvent);
case InputDevice::Type::Mouse:
return sizeof(LibInput::MouseEvent);
case InputDevice::Type::Joystick:
return sizeof(LibInput::JoystickEvent);
}
ASSERT_NOT_REACHED();
}
@ -69,18 +81,23 @@ namespace Kernel
{
MUST(m_event_buffer.resize(m_event_size * m_max_event_count, 0));
if (m_type == Type::Keyboard)
switch (m_type)
{
if (s_keyboards.size() < minor(m_rdev))
MUST(s_keyboards.resize(minor(m_rdev)));
s_keyboards[minor(m_rdev) - 1] = MUST(get_weak_ptr());
}
if (m_type == Type::Mouse)
{
if (s_mice.size() < minor(m_rdev))
MUST(s_mice.resize(minor(m_rdev)));
s_mice[minor(m_rdev) - 1] = MUST(get_weak_ptr());
case Type::Keyboard:
if (s_keyboards.size() < minor(m_rdev))
MUST(s_keyboards.resize(minor(m_rdev)));
s_keyboards[minor(m_rdev) - 1] = MUST(get_weak_ptr());
break;
case Type::Mouse:
if (s_mice.size() < minor(m_rdev))
MUST(s_mice.resize(minor(m_rdev)));
s_mice[minor(m_rdev) - 1] = MUST(get_weak_ptr());
break;
case Type::Joystick:
if (s_joysticks.size() < minor(m_rdev))
MUST(s_joysticks.resize(minor(m_rdev)));
s_joysticks[minor(m_rdev) - 1] = MUST(get_weak_ptr());
break;
}
}

View File

@ -3,6 +3,7 @@
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/USB/HID/HIDDriver.h>
#include <kernel/USB/HID/Joystick.h>
#include <kernel/USB/HID/Keyboard.h>
#include <kernel/USB/HID/Mouse.h>
@ -208,6 +209,10 @@ namespace Kernel
return BAN::Error::from_errno(EINVAL);
}
for (auto& report : m_device_inputs)
if (report.device && report.device->initialize().is_error())
report.device.clear();
m_device.send_data_buffer(m_data_endpoint_id, m_data_buffer->paddr(), m_data_buffer->size());
return {};
@ -256,6 +261,10 @@ namespace Kernel
report.device = TRY(BAN::RefPtr<USBMouse>::create());
dprintln("Initialized an USB Mouse");
break;
case 0x04:
report.device = TRY(BAN::RefPtr<USBJoystick>::create(*this));
dprintln("Initialized an USB Joystick");
break;
case 0x06:
report.device = TRY(BAN::RefPtr<USBKeyboard>::create(*this, BAN::move(outputs)));
dprintln("Initialized an USB Keyboard");
@ -376,13 +385,13 @@ namespace Kernel
continue;
}
const uint32_t usage_base = input.usage_id ? input.usage_id : input.usage_minimum;
const bool relative = !!(input.flags & 0x04);
const bool variable = !!(input.flags & 0x02);
const auto usage = input.usage_id ? input.usage_id : (input.usage_minimum + (variable ? i : logical));
if (!variable)
device_input.device->handle_array(input.usage_page, usage_base + logical);
device_input.device->handle_array(input.usage_page, usage);
else
{
const int64_t physical =
@ -392,9 +401,9 @@ namespace Kernel
input.physical_minimum;
if (relative)
device_input.device->handle_variable(input.usage_page, usage_base + i, physical);
device_input.device->handle_variable(input.usage_page, usage, physical);
else
device_input.device->handle_variable_absolute(input.usage_page, usage_base + i, physical, input.physical_minimum, input.physical_maximum);
device_input.device->handle_variable_absolute(input.usage_page, usage, physical, input.physical_minimum, input.physical_maximum);
}
bit_offset += input.report_size;
@ -595,15 +604,16 @@ namespace Kernel
break;
case 0b1010: // collection
{
if (local_state.usage_stack.size() != 1)
if (local_state.usage_stack.size() > 1)
{
dwarnln("{} usages specified for collection", local_state.usage_stack.empty() ? "No" : "Multiple");
dwarnln("Multiple usages specified for a collection");
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)
if (!local_state.usage_stack.empty() && local_state.usage_stack.front() >> 16)
usage_page = local_state.usage_stack.front() >> 16;
if (usage_page == 0)
{
@ -612,8 +622,11 @@ namespace Kernel
}
TRY(collection_stack.emplace_back());
collection_stack.back().type = report_data[1];
collection_stack.back().usage_page = usage_page;
collection_stack.back().usage_id = local_state.usage_stack.front();
if (!local_state.usage_stack.empty())
collection_stack.back().usage_id = local_state.usage_stack.front();
break;
}
case 0b1100: // end collection

View File

@ -0,0 +1,114 @@
#include <kernel/Input/InputDevice.h>
#include <kernel/USB/HID/Joystick.h>
namespace Kernel
{
USBJoystick::USBJoystick(USBHIDDriver& driver)
: USBHIDDevice(InputDevice::Type::Joystick)
, m_driver(driver)
{
}
BAN::ErrorOr<void> USBJoystick::initialize()
{
// TODO: this is not a generic USB HID joystick driver but one for PS3 controller.
// this may still work with other HID joysticks so i won't limit this only
// based on the device.
// linux hid-sony.c
auto temp_region = TRY(DMARegion::create(17));
USBDeviceRequest request;
// move ps3 controller to "operational" state
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Interface;
request.bRequest = 0x01;
request.wValue = 0x03F2;
request.wIndex = m_driver.interface().descriptor.bInterfaceNumber;
request.wLength = 17;
TRY(m_driver.device().send_request(request, temp_region->paddr()));
// some compatible controllers need this too
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Interface;
request.bRequest = 0x01;
request.wValue = 0x03F5;
request.wIndex = m_driver.interface().descriptor.bInterfaceNumber;
request.wLength = 8;
TRY(m_driver.device().send_request(request, temp_region->paddr()));
return {};
}
void USBJoystick::start_report()
{
m_interrupt_state = m_state_lock.lock();
for (auto& axis : m_state.axis)
axis = {};
for (auto& button : m_state.buttons)
button = false;
}
void USBJoystick::stop_report()
{
m_state_lock.unlock(m_interrupt_state);
}
void USBJoystick::handle_array(uint16_t usage_page, uint16_t usage)
{
(void)usage;
dprintln("Unsupported array {2H}", usage_page);
}
void USBJoystick::handle_variable(uint16_t usage_page, uint16_t usage, int64_t state)
{
(void)usage;
(void)state;
dprintln("Unsupported relative usage page {2H}", usage_page);
}
void USBJoystick::handle_variable_absolute(uint16_t usage_page, uint16_t usage, int64_t state, int64_t min, int64_t max)
{
switch (usage_page)
{
case 0x01:
switch (usage)
{
case 0x01:
// TODO: PS3 controller sends some extra data with this usage
break;
case 0x30:
m_state.axis[0] = { state, min, max };
break;
case 0x31:
m_state.axis[1] = { state, min, max };
break;
case 0x32:
m_state.axis[2] = { state, min, max };
break;
case 0x35:
m_state.axis[3] = { state, min, max };
break;
}
break;
case 0x09:
if (usage > 0 && usage <= sizeof(m_state.buttons))
m_state.buttons[usage - 1] = state;
break;
default:
dprintln("Unsupported absolute usage page {2H}", usage_page);
break;
}
}
BAN::ErrorOr<size_t> USBJoystick::read_impl(off_t, BAN::ByteSpan buffer)
{
SpinLockGuard _(m_state_lock);
const size_t to_copy = BAN::Math::min(buffer.size(), sizeof(m_state));
memcpy(buffer.data(), &m_state, to_copy);
return to_copy;
}
}

View File

@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
namespace LibInput
{
// TODO: not used but here if we ever make controller
// support generating events instead of being polled
struct JoystickEvent
{
};
struct JoystickState
{
struct Axis
{
int64_t value;
int64_t min;
int64_t max;
};
Axis axis[4];
bool buttons[32];
};
}