mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-05 15:56:49 +00:00
DS4: inherit from hid_pad_handler
This commit is contained in:
parent
d0cc5c0fc7
commit
b9a4abce0e
@ -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
|
||||
|
@ -12,8 +12,9 @@
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
struct PadDevice
|
||||
class PadDevice
|
||||
{
|
||||
public:
|
||||
pad_config* config{ nullptr };
|
||||
};
|
||||
|
||||
|
@ -63,19 +63,10 @@ namespace
|
||||
|
||||
return std::tuple<u16, u16>(Clamp0To255((outX + 1) * 127.f), Clamp0To255(((outY * -1) + 1) * 127.f));
|
||||
}*/
|
||||
|
||||
inline s16 read_s16(const void* buf)
|
||||
{
|
||||
return *reinterpret_cast<const s16*>(buf);
|
||||
}
|
||||
|
||||
inline u32 read_u32(const void* buf)
|
||||
{
|
||||
return *reinterpret_cast<const u32*>(buf);
|
||||
}
|
||||
}
|
||||
|
||||
ds4_pad_handler::ds4_pad_handler() : PadHandlerBase(pad_handler::ds4)
|
||||
ds4_pad_handler::ds4_pad_handler()
|
||||
: hid_pad_handler<DS4Device>(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<DS4Device> device = GetDS4Device(padId);
|
||||
std::shared_ptr<DS4Device> 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<DS4Device> device = GetDS4Device(padId);
|
||||
std::shared_ptr<DS4Device> 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::DS4Device> ds4_pad_handler::GetDS4Device(const std::string& padId)
|
||||
{
|
||||
if (!Init())
|
||||
return nullptr;
|
||||
|
||||
std::shared_ptr<DS4Device> 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<u64, u16> ds4_pad_handler::get_button_values(const std::shared_ptr<PadDevice>& device)
|
||||
{
|
||||
std::unordered_map<u64, u16> keyBuffer;
|
||||
auto ds4_dev = std::static_pointer_cast<DS4Device>(device);
|
||||
DS4Device* ds4_dev = static_cast<DS4Device*>(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<DS4Device>& 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<DS4Device>& 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<DS4Device>& 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<DS4Device>& 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<DS4Device>& 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<DS4Device> 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<u8, 64> 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<DS4Device>& 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<DS4Device>& device
|
||||
}
|
||||
}
|
||||
|
||||
void ds4_pad_handler::enumerate_devices()
|
||||
ds4_pad_handler::DataStatus ds4_pad_handler::GetRawData(DS4Device* device)
|
||||
{
|
||||
std::set<std::string> device_paths;
|
||||
std::map<std::string, std::wstring_view> 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<DS4Device>());
|
||||
}
|
||||
|
||||
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<std::chrono::milliseconds>(now - m_last_enumeration).count();
|
||||
if (elapsed > 2000)
|
||||
{
|
||||
m_last_enumeration = now;
|
||||
enumerate_devices();
|
||||
}
|
||||
|
||||
PadHandlerBase::ThreadProc();
|
||||
}
|
||||
|
||||
std::vector<std::string> ds4_pad_handler::ListDevices()
|
||||
{
|
||||
std::vector<std::string> 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<DS4Device>& device)
|
||||
{
|
||||
if (!device)
|
||||
return DS4DataStatus::ReadError;
|
||||
if (!device || !device->hidDevice)
|
||||
return DataStatus::ReadError;
|
||||
|
||||
std::array<u8, 78> 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<u16>(calValue) >> 0) & 0xFF;
|
||||
buf[calibOffset++] = (static_cast<u16>(calValue) >> 8) & 0xFF;
|
||||
}
|
||||
}
|
||||
memcpy(device->padData.data(), &buf[offset], 64);
|
||||
|
||||
return DS4DataStatus::NewData;
|
||||
}
|
||||
|
||||
std::shared_ptr<PadDevice> ds4_pad_handler::get_device(const std::string& device)
|
||||
{
|
||||
std::shared_ptr<DS4Device> 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<PadDevice>& device)
|
||||
{
|
||||
auto ds4_dev = std::static_pointer_cast<DS4Device>(device);
|
||||
DS4Device* ds4_dev = static_cast<DS4Device*>(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<PadDevice>& device, const std::shared_ptr<Pad>& pad)
|
||||
{
|
||||
auto ds4_device = std::static_pointer_cast<DS4Device>(device);
|
||||
DS4Device* ds4_device = static_cast<DS4Device*>(device.get());
|
||||
if (!ds4_device || !pad)
|
||||
return;
|
||||
|
||||
@ -1024,8 +847,8 @@ void ds4_pad_handler::get_extended_info(const std::shared_ptr<PadDevice>& device
|
||||
|
||||
void ds4_pad_handler::apply_pad_data(const std::shared_ptr<PadDevice>& device, const std::shared_ptr<Pad>& pad)
|
||||
{
|
||||
auto ds4_dev = std::static_pointer_cast<DS4Device>(device);
|
||||
if (!ds4_dev || !pad)
|
||||
DS4Device* ds4_dev = static_cast<DS4Device*>(device.get());
|
||||
if (!ds4_dev || !ds4_dev->hidDevice || !pad)
|
||||
return;
|
||||
|
||||
auto config = ds4_dev->config;
|
||||
|
@ -1,12 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#include "hid_pad_handler.h"
|
||||
#include "Utilities/CRC.h"
|
||||
#include "hidapi.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class ds4_pad_handler final : public PadHandlerBase
|
||||
class DS4Device : public HidDevice
|
||||
{
|
||||
public:
|
||||
bool btCon{false};
|
||||
bool hasCalibData{false};
|
||||
std::array<CalibData, CalibIndex::COUNT> calibData{};
|
||||
bool newVibrateData{true};
|
||||
std::array<u8, 64> 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<DS4Device>
|
||||
{
|
||||
// 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<DS4CalibData, DS4CalibIndex::COUNT> calibData{};
|
||||
bool newVibrateData{ true };
|
||||
u8 largeVibrate{ 0 };
|
||||
u8 smallVibrate{ 0 };
|
||||
std::array<u8, 64> 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<u16, 3> ds4Pids = { { 0xBA0, 0x5C4, 0x09CC } };
|
||||
|
||||
// pseudo 'controller id' to keep track of unique controllers
|
||||
std::map<std::string, std::shared_ptr<DS4Device>> controllers;
|
||||
CRCPP::CRC::Table<u32, 32> crcTable{ CRCPP::CRC::CRC_32() };
|
||||
|
||||
public:
|
||||
ds4_pad_handler();
|
||||
~ds4_pad_handler();
|
||||
|
||||
bool Init() override;
|
||||
void ThreadProc() override;
|
||||
|
||||
std::vector<std::string> 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<std::string> m_last_enumerated_devices;
|
||||
void enumerate_devices();
|
||||
u32 get_battery_color(u8 battery_level, int brightness);
|
||||
|
||||
private:
|
||||
std::shared_ptr<DS4Device> GetDS4Device(const std::string& padId);
|
||||
// Copies data into padData if status is NewData, otherwise buffer is untouched
|
||||
DS4DataStatus GetRawData(const std::shared_ptr<DS4Device>& ds4Device);
|
||||
DataStatus GetRawData(DS4Device* ds4Device);
|
||||
// This function gets us usuable buffer from the rawbuffer of padData
|
||||
bool GetCalibrationData(const std::shared_ptr<DS4Device>& ds4Device);
|
||||
void CheckAddDevice(hid_device* hidDevice, std::string_view path, std::wstring_view serial);
|
||||
int send_output_report(const std::shared_ptr<DS4Device>& 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<s16>(output);
|
||||
}
|
||||
void check_add_device(hid_device* hidDevice, std::string_view path, std::wstring_view serial);
|
||||
|
||||
std::shared_ptr<PadDevice> 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;
|
||||
|
200
rpcs3/Input/hid_pad_handler.cpp
Normal file
200
rpcs3/Input/hid_pad_handler.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
#include "hid_pad_handler.h"
|
||||
#include "ds4_pad_handler.h"
|
||||
#include "util/logs.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
LOG_CHANNEL(hid_log, "HID");
|
||||
|
||||
template <class Device>
|
||||
hid_pad_handler<Device>::hid_pad_handler(pad_handler type, u16 vid, std::vector<u16> pids)
|
||||
: PadHandlerBase(type), m_vid(vid), m_pids(std::move(pids))
|
||||
{
|
||||
};
|
||||
|
||||
template <class Device>
|
||||
hid_pad_handler<Device>::~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 <class Device>
|
||||
bool hid_pad_handler<Device>::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<Device>());
|
||||
}
|
||||
|
||||
enumerate_devices();
|
||||
|
||||
m_is_init = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Device>
|
||||
void hid_pad_handler<Device>::ThreadProc()
|
||||
{
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_last_enumeration).count();
|
||||
if (elapsed > 2000)
|
||||
{
|
||||
m_last_enumeration = now;
|
||||
enumerate_devices();
|
||||
}
|
||||
|
||||
PadHandlerBase::ThreadProc();
|
||||
}
|
||||
|
||||
template <class Device>
|
||||
std::vector<std::string> hid_pad_handler<Device>::ListDevices()
|
||||
{
|
||||
std::vector<std::string> 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 <class Device>
|
||||
void hid_pad_handler<Device>::enumerate_devices()
|
||||
{
|
||||
std::set<std::string> device_paths;
|
||||
std::map<std::string, std::wstring_view> 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 <class Device>
|
||||
std::shared_ptr<Device> hid_pad_handler<Device>::get_hid_device(const std::string& padId)
|
||||
{
|
||||
if (!Init())
|
||||
return nullptr;
|
||||
|
||||
std::shared_ptr<Device> 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 <class Device>
|
||||
std::shared_ptr<PadDevice> hid_pad_handler<Device>::get_device(const std::string& device)
|
||||
{
|
||||
return get_hid_device(device);
|
||||
}
|
||||
|
||||
template class hid_pad_handler<DS4Device>;
|
103
rpcs3/Input/hid_pad_handler.h
Normal file
103
rpcs3/Input/hid_pad_handler.h
Normal file
@ -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 Device>
|
||||
class hid_pad_handler : public PadHandlerBase
|
||||
{
|
||||
public:
|
||||
hid_pad_handler(pad_handler type, u16 vid, std::vector<u16> pids);
|
||||
~hid_pad_handler();
|
||||
|
||||
bool Init() override;
|
||||
void ThreadProc() override;
|
||||
std::vector<std::string> ListDevices() override;
|
||||
|
||||
protected:
|
||||
enum class DataStatus
|
||||
{
|
||||
NewData,
|
||||
NoNewData,
|
||||
ReadError,
|
||||
};
|
||||
|
||||
CRCPP::CRC::Table<u32, 32> crcTable{CRCPP::CRC::CRC_32()};
|
||||
|
||||
u16 m_vid;
|
||||
std::vector<u16> m_pids;
|
||||
|
||||
// pseudo 'controller id' to keep track of unique controllers
|
||||
std::map<std::string, std::shared_ptr<Device>> m_controllers;
|
||||
|
||||
bool m_is_init = false;
|
||||
std::chrono::system_clock::time_point m_last_enumeration;
|
||||
std::set<std::string> m_last_enumerated_devices;
|
||||
|
||||
void enumerate_devices();
|
||||
std::shared_ptr<Device> 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<s16>(output);
|
||||
}
|
||||
|
||||
inline s16 read_s16(const void* buf)
|
||||
{
|
||||
return *reinterpret_cast<const s16*>(buf);
|
||||
}
|
||||
|
||||
inline u32 read_u32(const void* buf)
|
||||
{
|
||||
return *reinterpret_cast<const u32*>(buf);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<PadDevice> get_device(const std::string& device) override;
|
||||
};
|
@ -332,6 +332,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="display_sleep_control.cpp" />
|
||||
<ClCompile Include="Input\dualsense_pad_handler.cpp" />
|
||||
<ClCompile Include="Input\hid_pad_handler.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="main_application.cpp" />
|
||||
<ClCompile Include="Input\basic_keyboard_handler.cpp" />
|
||||
@ -1641,6 +1642,7 @@
|
||||
<ClInclude Include="Input\ds4_pad_handler.h" />
|
||||
<ClInclude Include="Input\dualsense_pad_handler.h" />
|
||||
<ClInclude Include="Input\evdev_joystick_handler.h" />
|
||||
<ClInclude Include="Input\hid_pad_handler.h" />
|
||||
<ClInclude Include="Input\keyboard_pad_handler.h" />
|
||||
<CustomBuild Include="rpcs3qt\gs_frame.h">
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing gs_frame.h...</Message>
|
||||
|
@ -1105,6 +1105,9 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_log_viewer.cpp">
|
||||
<Filter>Generated Files\Debug - LLVM</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Input\hid_pad_handler.cpp">
|
||||
<Filter>Io</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
@ -1230,6 +1233,9 @@
|
||||
<ClInclude Include="rpcs3qt\progress_dialog.h">
|
||||
<Filter>Gui\misc dialogs</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Input\hid_pad_handler.h">
|
||||
<Filter>Io</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
Loading…
Reference in New Issue
Block a user