diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ee4687cf..c1115b23 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -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 diff --git a/kernel/include/kernel/Device/DeviceNumbers.h b/kernel/include/kernel/Device/DeviceNumbers.h index 3a96cb62..be591b05 100644 --- a/kernel/include/kernel/Device/DeviceNumbers.h +++ b/kernel/include/kernel/Device/DeviceNumbers.h @@ -16,6 +16,7 @@ namespace Kernel Debug, Keyboard, Mouse, + Joystick, SCSI, NVMeController, NVMeNamespace, diff --git a/kernel/include/kernel/Input/InputDevice.h b/kernel/include/kernel/Input/InputDevice.h index 97e2cc4c..3ea03305 100644 --- a/kernel/include/kernel/Input/InputDevice.h +++ b/kernel/include/kernel/Input/InputDevice.h @@ -15,6 +15,7 @@ namespace Kernel { Mouse, Keyboard, + Joystick, }; public: diff --git a/kernel/include/kernel/USB/HID/HIDDriver.h b/kernel/include/kernel/USB/HID/HIDDriver.h index f2d8bae4..0a581d0c 100644 --- a/kernel/include/kernel/USB/HID/HIDDriver.h +++ b/kernel/include/kernel/USB/HID/HIDDriver.h @@ -55,6 +55,8 @@ namespace Kernel {} virtual ~USBHIDDevice() = default; + virtual BAN::ErrorOr initialize() { return {}; } + virtual void start_report() = 0; virtual void stop_report() = 0; diff --git a/kernel/include/kernel/USB/HID/Joystick.h b/kernel/include/kernel/USB/HID/Joystick.h new file mode 100644 index 00000000..755addc6 --- /dev/null +++ b/kernel/include/kernel/USB/HID/Joystick.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +namespace Kernel +{ + + class USBJoystick final : public USBHIDDevice + { + BAN_NON_COPYABLE(USBJoystick); + BAN_NON_MOVABLE(USBJoystick); + + public: + BAN::ErrorOr 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 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; + }; + +} diff --git a/kernel/kernel/Input/InputDevice.cpp b/kernel/kernel/Input/InputDevice.cpp index 85adc5ca..74fd90a8 100644 --- a/kernel/kernel/Input/InputDevice.cpp +++ b/kernel/kernel/Input/InputDevice.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -18,6 +19,8 @@ namespace Kernel static BAN::Vector> s_mice; static BAN::RefPtr s_mouse_device; + static BAN::Vector> 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; } } diff --git a/kernel/kernel/USB/HID/HIDDriver.cpp b/kernel/kernel/USB/HID/HIDDriver.cpp index db06feef..b9596214 100644 --- a/kernel/kernel/USB/HID/HIDDriver.cpp +++ b/kernel/kernel/USB/HID/HIDDriver.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -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::create()); dprintln("Initialized an USB Mouse"); break; + case 0x04: + report.device = TRY(BAN::RefPtr::create(*this)); + dprintln("Initialized an USB Joystick"); + break; case 0x06: report.device = TRY(BAN::RefPtr::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 diff --git a/kernel/kernel/USB/HID/Joystick.cpp b/kernel/kernel/USB/HID/Joystick.cpp new file mode 100644 index 00000000..f8dd3f0e --- /dev/null +++ b/kernel/kernel/USB/HID/Joystick.cpp @@ -0,0 +1,114 @@ +#include +#include + +namespace Kernel +{ + + USBJoystick::USBJoystick(USBHIDDriver& driver) + : USBHIDDevice(InputDevice::Type::Joystick) + , m_driver(driver) + { + } + + BAN::ErrorOr 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 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; + } + +} diff --git a/userspace/libraries/LibInput/include/LibInput/Joystick.h b/userspace/libraries/LibInput/include/LibInput/Joystick.h new file mode 100644 index 00000000..13ddfced --- /dev/null +++ b/userspace/libraries/LibInput/include/LibInput/Joystick.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +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]; + }; + +}