From b9a4abce0e0e74bb6deb6e220f1efb11619d5b06 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 11 Feb 2021 22:13:21 +0100 Subject: [PATCH] DS4: inherit from hid_pad_handler --- rpcs3/CMakeLists.txt | 1 + rpcs3/Emu/Io/PadHandler.h | 3 +- rpcs3/Input/ds4_pad_handler.cpp | 295 +++++++------------------------- rpcs3/Input/ds4_pad_handler.h | 107 +++--------- rpcs3/Input/hid_pad_handler.cpp | 200 ++++++++++++++++++++++ rpcs3/Input/hid_pad_handler.h | 103 +++++++++++ rpcs3/rpcs3.vcxproj | 2 + rpcs3/rpcs3.vcxproj.filters | 6 + 8 files changed, 393 insertions(+), 324 deletions(-) create mode 100644 rpcs3/Input/hid_pad_handler.cpp create mode 100644 rpcs3/Input/hid_pad_handler.h diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index d165e935ac..f8742d4f13 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -60,6 +60,7 @@ set(RPCS3_SRC Input/ds4_pad_handler.cpp Input/dualsense_pad_handler.cpp Input/evdev_joystick_handler.cpp + Input/hid_pad_handler.cpp Input/keyboard_pad_handler.cpp Input/mm_joystick_handler.cpp Input/pad_thread.cpp diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 081960287a..4cf95af00c 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -12,8 +12,9 @@ #include #include -struct PadDevice +class PadDevice { +public: pad_config* config{ nullptr }; }; diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index bc17964f37..fad938739d 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -63,19 +63,10 @@ namespace return std::tuple(Clamp0To255((outX + 1) * 127.f), Clamp0To255(((outY * -1) + 1) * 127.f)); }*/ - - inline s16 read_s16(const void* buf) - { - return *reinterpret_cast(buf); - } - - inline u32 read_u32(const void* buf) - { - return *reinterpret_cast(buf); - } } -ds4_pad_handler::ds4_pad_handler() : PadHandlerBase(pad_handler::ds4) +ds4_pad_handler::ds4_pad_handler() + : hid_pad_handler(pad_handler::ds4, 0x054C, {0xBA0, 0x5C4, 0x09CC}) { // Unique names for the config files and our pad settings dialog button_list = @@ -189,7 +180,7 @@ void ds4_pad_handler::init_config(pad_config* cfg, const std::string& name) u32 ds4_pad_handler::get_battery_level(const std::string& padId) { - std::shared_ptr device = GetDS4Device(padId); + std::shared_ptr device = get_hid_device(padId); if (device == nullptr || device->hidDevice == nullptr) { return 0; @@ -199,7 +190,7 @@ u32 ds4_pad_handler::get_battery_level(const std::string& padId) void ds4_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b, bool battery_led, u32 battery_led_brightness) { - std::shared_ptr device = GetDS4Device(padId); + std::shared_ptr device = get_hid_device(padId); if (device == nullptr || device->hidDevice == nullptr) return; @@ -238,33 +229,13 @@ void ds4_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 s } // Start/Stop the engines :) - send_output_report(device); -} - -std::shared_ptr ds4_pad_handler::GetDS4Device(const std::string& padId) -{ - if (!Init()) - return nullptr; - - std::shared_ptr device = nullptr; - - // Controllers 1-n in GUI - for (auto& cur_control : controllers) - { - if (padId == cur_control.first) - { - device = cur_control.second; - break; - } - } - - return device; + send_output_report(device.get()); } std::unordered_map ds4_pad_handler::get_button_values(const std::shared_ptr& device) { std::unordered_map keyBuffer; - auto ds4_dev = std::static_pointer_cast(device); + DS4Device* ds4_dev = static_cast(device.get()); if (!ds4_dev) return keyBuffer; @@ -387,7 +358,7 @@ pad_preview_values ds4_pad_handler::get_preview_values(const std::unordered_map< }; } -bool ds4_pad_handler::GetCalibrationData(const std::shared_ptr& ds4Dev) +bool ds4_pad_handler::GetCalibrationData(DS4Device* ds4Dev) { if (!ds4Dev || !ds4Dev->hidDevice) { @@ -435,9 +406,9 @@ bool ds4_pad_handler::GetCalibrationData(const std::shared_ptr& ds4De } } - ds4Dev->calibData[DS4CalibIndex::PITCH].bias = read_s16(&buf[1]); - ds4Dev->calibData[DS4CalibIndex::YAW].bias = read_s16(&buf[3]); - ds4Dev->calibData[DS4CalibIndex::ROLL].bias = read_s16(&buf[5]); + ds4Dev->calibData[CalibIndex::PITCH].bias = read_s16(&buf[1]); + ds4Dev->calibData[CalibIndex::YAW].bias = read_s16(&buf[3]); + ds4Dev->calibData[CalibIndex::ROLL].bias = read_s16(&buf[5]); s16 pitchPlus, pitchNeg, rollPlus, rollNeg, yawPlus, yawNeg; @@ -474,14 +445,14 @@ bool ds4_pad_handler::GetCalibrationData(const std::shared_ptr& ds4De const s32 gyroSpeedScale = read_s16(&buf[19]) + read_s16(&buf[21]); - ds4Dev->calibData[DS4CalibIndex::PITCH].sensNumer = gyroSpeedScale * DS4_GYRO_RES_PER_DEG_S; - ds4Dev->calibData[DS4CalibIndex::PITCH].sensDenom = pitchPlus - pitchNeg; + ds4Dev->calibData[CalibIndex::PITCH].sens_numer = gyroSpeedScale * DS4_GYRO_RES_PER_DEG_S; + ds4Dev->calibData[CalibIndex::PITCH].sens_denom = pitchPlus - pitchNeg; - ds4Dev->calibData[DS4CalibIndex::YAW].sensNumer = gyroSpeedScale * DS4_GYRO_RES_PER_DEG_S; - ds4Dev->calibData[DS4CalibIndex::YAW].sensDenom = yawPlus - yawNeg; + ds4Dev->calibData[CalibIndex::YAW].sens_numer = gyroSpeedScale * DS4_GYRO_RES_PER_DEG_S; + ds4Dev->calibData[CalibIndex::YAW].sens_denom = yawPlus - yawNeg; - ds4Dev->calibData[DS4CalibIndex::ROLL].sensNumer = gyroSpeedScale * DS4_GYRO_RES_PER_DEG_S; - ds4Dev->calibData[DS4CalibIndex::ROLL].sensDenom = rollPlus - rollNeg; + ds4Dev->calibData[CalibIndex::ROLL].sens_numer = gyroSpeedScale * DS4_GYRO_RES_PER_DEG_S; + ds4Dev->calibData[CalibIndex::ROLL].sens_denom = rollPlus - rollNeg; const s16 accelXPlus = read_s16(&buf[23]); const s16 accelXNeg = read_s16(&buf[25]); @@ -491,25 +462,25 @@ bool ds4_pad_handler::GetCalibrationData(const std::shared_ptr& ds4De const s16 accelZNeg = read_s16(&buf[33]); const s32 accelXRange = accelXPlus - accelXNeg; - ds4Dev->calibData[DS4CalibIndex::X].bias = accelXPlus - accelXRange / 2; - ds4Dev->calibData[DS4CalibIndex::X].sensNumer = 2 * DS4_ACC_RES_PER_G; - ds4Dev->calibData[DS4CalibIndex::X].sensDenom = accelXRange; + ds4Dev->calibData[CalibIndex::X].bias = accelXPlus - accelXRange / 2; + ds4Dev->calibData[CalibIndex::X].sens_numer = 2 * DS4_ACC_RES_PER_G; + ds4Dev->calibData[CalibIndex::X].sens_denom = accelXRange; const s32 accelYRange = accelYPlus - accelYNeg; - ds4Dev->calibData[DS4CalibIndex::Y].bias = accelYPlus - accelYRange / 2; - ds4Dev->calibData[DS4CalibIndex::Y].sensNumer = 2 * DS4_ACC_RES_PER_G; - ds4Dev->calibData[DS4CalibIndex::Y].sensDenom = accelYRange; + ds4Dev->calibData[CalibIndex::Y].bias = accelYPlus - accelYRange / 2; + ds4Dev->calibData[CalibIndex::Y].sens_numer = 2 * DS4_ACC_RES_PER_G; + ds4Dev->calibData[CalibIndex::Y].sens_denom = accelYRange; const s32 accelZRange = accelZPlus - accelZNeg; - ds4Dev->calibData[DS4CalibIndex::Z].bias = accelZPlus - accelZRange / 2; - ds4Dev->calibData[DS4CalibIndex::Z].sensNumer = 2 * DS4_ACC_RES_PER_G; - ds4Dev->calibData[DS4CalibIndex::Z].sensDenom = accelZRange; + ds4Dev->calibData[CalibIndex::Z].bias = accelZPlus - accelZRange / 2; + ds4Dev->calibData[CalibIndex::Z].sens_numer = 2 * DS4_ACC_RES_PER_G; + ds4Dev->calibData[CalibIndex::Z].sens_denom = accelZRange; // Make sure data 'looks' valid, dongle will report invalid calibration data with no controller connected for (const auto& data : ds4Dev->calibData) { - if (data.sensDenom == 0) + if (data.sens_denom == 0) { ds4_log.error("GetCalibrationData: Failure: sensDenom == 0"); return false; @@ -519,15 +490,20 @@ bool ds4_pad_handler::GetCalibrationData(const std::shared_ptr& ds4De return true; } -void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, std::string_view path, std::wstring_view wide_serial) +void ds4_pad_handler::check_add_device(hid_device* hidDevice, std::string_view path, std::wstring_view wide_serial) { - std::shared_ptr ds4_dev; + if (!hidDevice) + { + return; + } - for (auto& controller : controllers) + DS4Device* ds4_dev = nullptr; + + for (auto& controller : m_controllers) { if (!controller.second || !controller.second->hidDevice) { - ds4_dev = controller.second; + ds4_dev = controller.second.get(); break; } } @@ -539,6 +515,7 @@ void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, std::string_view pat std::string serial; ds4_dev->hidDevice = hidDevice; + // There isnt a nice 'portable' way with hidapi to detect bt vs wired as the pid/vid's are the same // Let's try getting 0x81 feature report, which should will return mac address on wired, and should error on bluetooth std::array buf{}; @@ -548,14 +525,14 @@ void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, std::string_view pat if (bytes_read != DS4_FEATURE_REPORT_0x81_SIZE) { // Controller may not be genuine. These controllers do not have feature 0x81 implemented and calibration data is in bluetooth format even in USB mode! - ds4_log.warning("CheckAddDevice: DS4 controller may not be genuine. Workaround enabled."); + ds4_log.warning("check_add_device: DS4 controller may not be genuine. Workaround enabled."); // Read feature report 0x12 instead which is what the console uses. buf[0] = 0x12; buf[1] = 0; if (hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0x12_SIZE) == -1) { - ds4_log.error("CheckAddDevice: hid_get_feature_report 0x12 failed! Reason: %s", hid_error(hidDevice)); + ds4_log.error("check_add_device: hid_get_feature_report 0x12 failed! Reason: %s", hid_error(hidDevice)); } } @@ -570,14 +547,14 @@ void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, std::string_view pat if (!GetCalibrationData(ds4_dev)) { - ds4_log.error("CheckAddDevice: GetCalibrationData failed!"); + ds4_log.error("check_add_device: GetCalibrationData failed!"); hid_close(hidDevice); return; } if (hid_set_nonblocking(hidDevice, 1) == -1) { - ds4_log.error("CheckAddDevice: hid_set_nonblocking failed! Reason: %s", hid_error(hidDevice)); + ds4_log.error("check_add_device: hid_set_nonblocking failed! Reason: %s", hid_error(hidDevice)); hid_close(hidDevice); return; } @@ -590,7 +567,7 @@ void ds4_pad_handler::CheckAddDevice(hid_device* hidDevice, std::string_view pat ds4_pad_handler::~ds4_pad_handler() { - for (auto& controller : controllers) + for (auto& controller : m_controllers) { if (controller.second && controller.second->hidDevice) { @@ -599,18 +576,12 @@ ds4_pad_handler::~ds4_pad_handler() controller.second->largeVibrate = 0; controller.second->led_delay_on = 0; controller.second->led_delay_off = 0; - send_output_report(controller.second); - - hid_close(controller.second->hidDevice); + send_output_report(controller.second.get()); } } - if (hid_exit() != 0) - { - ds4_log.error("hid_exit failed!"); - } } -int ds4_pad_handler::send_output_report(const std::shared_ptr& device) +int ds4_pad_handler::send_output_report(DS4Device* device) { if (!device || !device->hidDevice) return -2; @@ -665,147 +636,10 @@ int ds4_pad_handler::send_output_report(const std::shared_ptr& device } } -void ds4_pad_handler::enumerate_devices() +ds4_pad_handler::DataStatus ds4_pad_handler::GetRawData(DS4Device* device) { - std::set device_paths; - std::map serials; - - for (auto pid : ds4Pids) - { - hid_device_info* dev_info = hid_enumerate(DS4_VID, pid); - hid_device_info* head = dev_info; - while (dev_info) - { - ensure(dev_info->path != nullptr); - device_paths.insert(dev_info->path); - serials[dev_info->path] = dev_info->serial_number ? std::wstring_view(dev_info->serial_number) : std::wstring_view{}; - dev_info = dev_info->next; - } - hid_free_enumeration(head); - } - - if (m_last_enumerated_devices == device_paths) - { - return; - } - - m_last_enumerated_devices = device_paths; - - // Scrap devices that are not in the new list - for (auto it = controllers.begin(); it != controllers.end(); ++it) - { - if (it->second && !it->second->path.empty() && !device_paths.contains(it->second->path)) - { - hid_close(it->second->hidDevice); - pad_config* config = it->second->config; - it->second.reset(new DS4Device()); - it->second->config = config; - } - } - - bool warn_about_drivers = false; - - // Find and add new devices - for (const auto& path : device_paths) - { - // Check if we already have this controller - const auto it_found = std::find_if(controllers.cbegin(), controllers.cend(), [path](const auto& c) { return c.second && c.second->path == path; }); - - if (it_found == controllers.cend()) - { - // Check if we have at least one virtual controller left - const auto it_free = std::find_if(controllers.cbegin(), controllers.cend(), [](const auto& c) { return !c.second || !c.second->hidDevice; }); - if (it_free == controllers.cend()) - { - break; - } - - hid_device* dev = hid_open_path(path.c_str()); - if (dev) - { - CheckAddDevice(dev, path, serials[path]); - } - else - { - ds4_log.error("hid_open_path failed! Reason: %s", hid_error(dev)); - warn_about_drivers = true; - } - } - } - - if (warn_about_drivers) - { - ds4_log.error("One or more DS4 pads were detected but couldn't be interacted with directly"); -#if defined(_WIN32) || defined(__linux__) - ds4_log.error("Check https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration for intructions on how to solve this issue"); -#endif - } - else - { - const size_t count = std::count_if(controllers.cbegin(), controllers.cend(), [](const auto& c) { return c.second && c.second->hidDevice; }); - if (count > 0) - { - ds4_log.success("Controllers found: %d", count); - } - else - { - ds4_log.warning("No controllers found!"); - } - } -} - -bool ds4_pad_handler::Init() -{ - if (is_init) - return true; - - const int res = hid_init(); - if (res != 0) - fmt::throw_exception("hidapi-init error.threadproc"); - - for (size_t i = 1; i <= MAX_GAMEPADS; i++) // Controllers 1-n in GUI - { - controllers.emplace(m_name_string + std::to_string(i), std::make_shared()); - } - - enumerate_devices(); - - is_init = true; - return true; -} - -void ds4_pad_handler::ThreadProc() -{ - const auto now = std::chrono::system_clock::now(); - const auto elapsed = std::chrono::duration_cast(now - m_last_enumeration).count(); - if (elapsed > 2000) - { - m_last_enumeration = now; - enumerate_devices(); - } - - PadHandlerBase::ThreadProc(); -} - -std::vector ds4_pad_handler::ListDevices() -{ - std::vector ds4_pads_list; - - if (!Init()) - return ds4_pads_list; - - for (const auto& controller : controllers) // Controllers 1-n in GUI - { - ds4_pads_list.emplace_back(controller.first); - } - - return ds4_pads_list; -} - -ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr& device) -{ - if (!device) - return DS4DataStatus::ReadError; + if (!device || !device->hidDevice) + return DataStatus::ReadError; std::array buf{}; @@ -813,12 +647,12 @@ ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr if (res == -1) { // looks like controller disconnected or read error - return DS4DataStatus::ReadError; + return DataStatus::ReadError; } // no data? keep going if (res == 0) - return DS4DataStatus::NoNewData; + return DataStatus::NoNewData; // bt controller sends this until 0x02 feature report is sent back (happens on controller init/restart) if (device->btCon && buf[0] == 0x1) @@ -830,7 +664,7 @@ ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr { ds4_log.error("GetRawData: hid_get_feature_report 0x2 failed! Reason: %s", hid_error(device->hidDevice)); } - return DS4DataStatus::NoNewData; + return DataStatus::NoNewData; } int offset = 0; @@ -846,7 +680,7 @@ ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr if (crcCalc != crcReported) { ds4_log.warning("Data packet CRC check failed, ignoring! Received 0x%x, Expected 0x%x", crcReported, crcCalc); - return DS4DataStatus::NoNewData; + return DataStatus::NoNewData; } } else if (!device->btCon && buf[0] == 0x01 && res == 64) @@ -859,7 +693,7 @@ ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr offset = 0; } else - return DS4DataStatus::NoNewData; + return DataStatus::NoNewData; const int battery_offset = offset + DS4_INPUT_REPORT_BATTERY_OFFSET; device->is_initialized = true; @@ -869,26 +703,17 @@ ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr if (device->hasCalibData) { int calibOffset = offset + DS4_INPUT_REPORT_GYRO_X_OFFSET; - for (int i = 0; i < DS4CalibIndex::COUNT; ++i) + for (int i = 0; i < CalibIndex::COUNT; ++i) { const s16 rawValue = read_s16(&buf[calibOffset]); - const s16 calValue = ApplyCalibration(rawValue, device->calibData[i]); + const s16 calValue = apply_calibration(rawValue, device->calibData[i]); buf[calibOffset++] = (static_cast(calValue) >> 0) & 0xFF; buf[calibOffset++] = (static_cast(calValue) >> 8) & 0xFF; } } memcpy(device->padData.data(), &buf[offset], 64); - return DS4DataStatus::NewData; -} - -std::shared_ptr ds4_pad_handler::get_device(const std::string& device) -{ - std::shared_ptr ds4device = GetDS4Device(device); - if (ds4device == nullptr) - return nullptr; - - return ds4device; + return DataStatus::NewData; } bool ds4_pad_handler::get_is_left_trigger(u64 keyCode) @@ -945,7 +770,7 @@ u32 ds4_pad_handler::get_battery_color(u8 battery_level, int brightness) PadHandlerBase::connection ds4_pad_handler::update_connection(const std::shared_ptr& device) { - auto ds4_dev = std::static_pointer_cast(device); + DS4Device* ds4_dev = static_cast(device.get()); if (!ds4_dev || ds4_dev->path.empty()) return connection::disconnected; @@ -970,9 +795,7 @@ PadHandlerBase::connection ds4_pad_handler::update_connection(const std::shared_ } } - status = GetRawData(ds4_dev); - - if (status == DS4DataStatus::ReadError) + if (GetRawData(ds4_dev) == DataStatus::ReadError) { // this also can mean disconnected, either way deal with it on next loop and reconnect hid_close(ds4_dev->hidDevice); @@ -986,7 +809,7 @@ PadHandlerBase::connection ds4_pad_handler::update_connection(const std::shared_ void ds4_pad_handler::get_extended_info(const std::shared_ptr& device, const std::shared_ptr& pad) { - auto ds4_device = std::static_pointer_cast(device); + DS4Device* ds4_device = static_cast(device.get()); if (!ds4_device || !pad) return; @@ -1024,8 +847,8 @@ void ds4_pad_handler::get_extended_info(const std::shared_ptr& device void ds4_pad_handler::apply_pad_data(const std::shared_ptr& device, const std::shared_ptr& pad) { - auto ds4_dev = std::static_pointer_cast(device); - if (!ds4_dev || !pad) + DS4Device* ds4_dev = static_cast(device.get()); + if (!ds4_dev || !ds4_dev->hidDevice || !pad) return; auto config = ds4_dev->config; diff --git a/rpcs3/Input/ds4_pad_handler.h b/rpcs3/Input/ds4_pad_handler.h index 6b28b060cc..8ff9632f42 100644 --- a/rpcs3/Input/ds4_pad_handler.h +++ b/rpcs3/Input/ds4_pad_handler.h @@ -1,12 +1,25 @@ #pragma once -#include "Emu/Io/PadHandler.h" +#include "hid_pad_handler.h" #include "Utilities/CRC.h" -#include "hidapi.h" #include -class ds4_pad_handler final : public PadHandlerBase +class DS4Device : public HidDevice +{ +public: + bool btCon{false}; + bool hasCalibData{false}; + std::array calibData{}; + bool newVibrateData{true}; + std::array padData{}; + u8 batteryLevel{0}; + u8 last_battery_level{0}; + u8 cableState{0}; + bool is_initialized{false}; +}; + +class ds4_pad_handler final : public hid_pad_handler { // These are all the possible buttons on a standard DS4 controller // The touchpad is restricted to its button for now (or forever?) @@ -46,105 +59,25 @@ class ds4_pad_handler final : public PadHandlerBase KeyCodeCount }; - enum DS4CalibIndex - { - // gyro - PITCH = 0, - YAW, - ROLL, - - // accel - X, - Y, - Z, - COUNT - }; - - struct DS4CalibData - { - s16 bias; - s32 sensNumer; - s32 sensDenom; - }; - - enum class DS4DataStatus - { - NewData, - NoNewData, - ReadError, - }; - - struct DS4Device : public PadDevice - { - hid_device* hidDevice{ nullptr }; - std::string path{ "" }; - bool btCon{ false }; - bool hasCalibData{ false }; - std::array calibData{}; - bool newVibrateData{ true }; - u8 largeVibrate{ 0 }; - u8 smallVibrate{ 0 }; - std::array padData{}; - u8 batteryLevel{ 0 }; - u8 last_battery_level { 0 }; - u8 cableState{ 0 }; - u8 led_delay_on{ 0 }; - u8 led_delay_off{ 0 }; - bool is_initialized{ false }; - }; - - const u16 DS4_VID = 0x054C; - - // pid's of connected ds4 - const std::array ds4Pids = { { 0xBA0, 0x5C4, 0x09CC } }; - - // pseudo 'controller id' to keep track of unique controllers - std::map> controllers; - CRCPP::CRC::Table crcTable{ CRCPP::CRC::CRC_32() }; - public: ds4_pad_handler(); ~ds4_pad_handler(); - bool Init() override; - void ThreadProc() override; - - std::vector ListDevices() override; void SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b, bool battery_led, u32 battery_led_brightness) override; u32 get_battery_level(const std::string& padId) override; void init_config(pad_config* cfg, const std::string& name) override; private: - bool is_init = false; - DS4DataStatus status; - std::chrono::system_clock::time_point m_last_enumeration; - std::set m_last_enumerated_devices; - void enumerate_devices(); u32 get_battery_color(u8 battery_level, int brightness); -private: - std::shared_ptr GetDS4Device(const std::string& padId); // Copies data into padData if status is NewData, otherwise buffer is untouched - DS4DataStatus GetRawData(const std::shared_ptr& ds4Device); + DataStatus GetRawData(DS4Device* ds4Device); // This function gets us usuable buffer from the rawbuffer of padData - bool GetCalibrationData(const std::shared_ptr& ds4Device); - void CheckAddDevice(hid_device* hidDevice, std::string_view path, std::wstring_view serial); - int send_output_report(const std::shared_ptr& device); - inline s16 ApplyCalibration(s32 rawValue, const DS4CalibData& calibData) - { - const s32 biased = rawValue - calibData.bias; - const s32 quot = calibData.sensNumer / calibData.sensDenom; - const s32 rem = calibData.sensNumer % calibData.sensDenom; - const s32 output = (quot * biased) + ((rem * biased) / calibData.sensDenom); + bool GetCalibrationData(DS4Device* ds4Device); + int send_output_report(DS4Device* device); - if (output > INT16_MAX) - return INT16_MAX; - else if (output < INT16_MIN) - return INT16_MIN; - else return static_cast(output); - } + void check_add_device(hid_device* hidDevice, std::string_view path, std::wstring_view serial); - std::shared_ptr get_device(const std::string& device) override; bool get_is_left_trigger(u64 keyCode) override; bool get_is_right_trigger(u64 keyCode) override; bool get_is_left_stick(u64 keyCode) override; diff --git a/rpcs3/Input/hid_pad_handler.cpp b/rpcs3/Input/hid_pad_handler.cpp new file mode 100644 index 0000000000..f624a3e413 --- /dev/null +++ b/rpcs3/Input/hid_pad_handler.cpp @@ -0,0 +1,200 @@ +#include "hid_pad_handler.h" +#include "ds4_pad_handler.h" +#include "util/logs.hpp" + +#include +#include + +LOG_CHANNEL(hid_log, "HID"); + +template +hid_pad_handler::hid_pad_handler(pad_handler type, u16 vid, std::vector pids) + : PadHandlerBase(type), m_vid(vid), m_pids(std::move(pids)) +{ +}; + +template +hid_pad_handler::~hid_pad_handler() +{ + for (auto& controller : m_controllers) + { + if (controller.second && controller.second->hidDevice) + { + hid_close(controller.second->hidDevice); + } + } + + if (hid_exit() != 0) + { + hid_log.error("hid_exit failed!"); + } +} + +template +bool hid_pad_handler::Init() +{ + if (m_is_init) + return true; + + const int res = hid_init(); + if (res != 0) + fmt::throw_exception("hidapi-init error.threadproc"); + + for (size_t i = 1; i <= MAX_GAMEPADS; i++) // Controllers 1-n in GUI + { + m_controllers.emplace(m_name_string + std::to_string(i), std::make_shared()); + } + + enumerate_devices(); + + m_is_init = true; + return true; +} + +template +void hid_pad_handler::ThreadProc() +{ + const auto now = std::chrono::system_clock::now(); + const auto elapsed = std::chrono::duration_cast(now - m_last_enumeration).count(); + if (elapsed > 2000) + { + m_last_enumeration = now; + enumerate_devices(); + } + + PadHandlerBase::ThreadProc(); +} + +template +std::vector hid_pad_handler::ListDevices() +{ + std::vector pads_list; + + if (!Init()) + return pads_list; + + for (const auto& controller : m_controllers) // Controllers 1-n in GUI + { + pads_list.emplace_back(controller.first); + } + + return pads_list; +} + +template +void hid_pad_handler::enumerate_devices() +{ + std::set device_paths; + std::map serials; + + for (const auto& pid : m_pids) + { + hid_device_info* dev_info = hid_enumerate(m_vid, pid); + hid_device_info* head = dev_info; + while (dev_info) + { + ensure(dev_info->path != nullptr); + device_paths.insert(dev_info->path); + serials[dev_info->path] = dev_info->serial_number ? std::wstring_view(dev_info->serial_number) : std::wstring_view{}; + dev_info = dev_info->next; + } + hid_free_enumeration(head); + } + + if (m_last_enumerated_devices == device_paths) + { + return; + } + + m_last_enumerated_devices = device_paths; + + // Scrap devices that are not in the new list + for (auto& controller : m_controllers) + { + if (controller.second && !controller.second->path.empty() && !device_paths.contains(controller.second->path)) + { + hid_close(controller.second->hidDevice); + pad_config* config = controller.second->config; + controller.second.reset(new Device()); + controller.second->config = config; + } + } + + bool warn_about_drivers = false; + + // Find and add new devices + for (const auto& path : device_paths) + { + // Check if we already have this controller + const auto it_found = std::find_if(m_controllers.cbegin(), m_controllers.cend(), [path](const auto& c) { return c.second && c.second->path == path; }); + + if (it_found == m_controllers.cend()) + { + // Check if we have at least one virtual controller left + const auto it_free = std::find_if(m_controllers.cbegin(), m_controllers.cend(), [](const auto& c) { return !c.second || !c.second->hidDevice; }); + if (it_free == m_controllers.cend()) + { + break; + } + + hid_device* dev = hid_open_path(path.c_str()); + if (dev) + { + check_add_device(dev, path, serials[path]); + } + else + { + hid_log.error("hid_open_path failed! Reason: %s", hid_error(dev)); + warn_about_drivers = true; + } + } + } + + if (warn_about_drivers) + { + hid_log.error("One or more pads were detected but couldn't be interacted with directly"); +#if defined(_WIN32) || defined(__linux__) + hid_log.error("Check https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration for intructions on how to solve this issue"); +#endif + } + else + { + const size_t count = std::count_if(m_controllers.cbegin(), m_controllers.cend(), [](const auto& c) { return c.second && c.second->hidDevice; }); + if (count > 0) + { + hid_log.success("Controllers found: %d", count); + } + else + { + hid_log.warning("No controllers found!"); + } + } +} + +template +std::shared_ptr hid_pad_handler::get_hid_device(const std::string& padId) +{ + if (!Init()) + return nullptr; + + std::shared_ptr device = nullptr; + + // Controllers 1-n in GUI + for (auto& cur_control : m_controllers) + { + if (padId == cur_control.first) + { + return cur_control.second; + } + } + + return nullptr; +} + +template +std::shared_ptr hid_pad_handler::get_device(const std::string& device) +{ + return get_hid_device(device); +} + +template class hid_pad_handler; diff --git a/rpcs3/Input/hid_pad_handler.h b/rpcs3/Input/hid_pad_handler.h new file mode 100644 index 0000000000..6af2535c38 --- /dev/null +++ b/rpcs3/Input/hid_pad_handler.h @@ -0,0 +1,103 @@ +#pragma once + +#include "Emu/Io/PadHandler.h" +#include "Utilities/CRC.h" + +#include "hidapi.h" + +struct CalibData +{ + s16 bias; + s32 sens_numer; + s32 sens_denom; +}; + +enum CalibIndex +{ + // gyro + PITCH = 0, + YAW, + ROLL, + + // accel + X, + Y, + Z, + COUNT +}; + +class HidDevice : public PadDevice +{ +public: + hid_device* hidDevice{nullptr}; + std::string path{""}; + u8 largeVibrate{0}; + u8 smallVibrate{0}; + u8 led_delay_on{0}; + u8 led_delay_off{0}; +}; + +template +class hid_pad_handler : public PadHandlerBase +{ +public: + hid_pad_handler(pad_handler type, u16 vid, std::vector pids); + ~hid_pad_handler(); + + bool Init() override; + void ThreadProc() override; + std::vector ListDevices() override; + +protected: + enum class DataStatus + { + NewData, + NoNewData, + ReadError, + }; + + CRCPP::CRC::Table crcTable{CRCPP::CRC::CRC_32()}; + + u16 m_vid; + std::vector m_pids; + + // pseudo 'controller id' to keep track of unique controllers + std::map> m_controllers; + + bool m_is_init = false; + std::chrono::system_clock::time_point m_last_enumeration; + std::set m_last_enumerated_devices; + + void enumerate_devices(); + std::shared_ptr get_hid_device(const std::string& padId); + + virtual void check_add_device(hid_device* hidDevice, std::string_view path, std::wstring_view serial) = 0; + + inline s16 apply_calibration(s32 rawValue, const CalibData& calibData) + { + const s32 biased = rawValue - calibData.bias; + const s32 quot = calibData.sens_numer / calibData.sens_denom; + const s32 rem = calibData.sens_numer % calibData.sens_denom; + const s32 output = (quot * biased) + ((rem * biased) / calibData.sens_denom); + + if (output > INT16_MAX) + return INT16_MAX; + else if (output < INT16_MIN) + return INT16_MIN; + else + return static_cast(output); + } + + inline s16 read_s16(const void* buf) + { + return *reinterpret_cast(buf); + } + + inline u32 read_u32(const void* buf) + { + return *reinterpret_cast(buf); + } + +private: + std::shared_ptr get_device(const std::string& device) override; +}; diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 461d958711..97ee05b4af 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -332,6 +332,7 @@ + @@ -1641,6 +1642,7 @@ + Moc%27ing gs_frame.h... diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 1c72125b83..a8c176877c 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1105,6 +1105,9 @@ Generated Files\Debug - LLVM + + Io + @@ -1230,6 +1233,9 @@ Gui\misc dialogs + + Io +