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:
2026-01-03 19:48:28 +02:00
parent 08bfa0971e
commit 65664b0d65
9 changed files with 240 additions and 20 deletions

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;
}
}