Initial DualSense Support (#9308)

* Initial DualSense Support

* Add Vibration Support

* Add CRC32 Validation to Incoming Bluetooth Packets
Cleanup report sizes

* Consistency, remove button comments, add two buttons.

Co-authored-by: Ani <ani-leo@outlook.com>
This commit is contained in:
Luke Barr 2020-12-12 17:00:45 -06:00 committed by GitHub
parent ee814cfd0c
commit cb8ef46ec7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 854 additions and 0 deletions

View file

@ -58,6 +58,7 @@ set(RPCS3_SRC
Input/basic_mouse_handler.cpp
Input/ds3_pad_handler.cpp
Input/ds4_pad_handler.cpp
Input/dualsense_pad_handler.cpp
Input/evdev_joystick_handler.cpp
Input/keyboard_pad_handler.cpp
Input/mm_joystick_handler.cpp

View file

@ -12,6 +12,7 @@ void fmt_class_string<pad_handler>::format(std::string& out, u64 arg)
case pad_handler::keyboard: return "Keyboard";
case pad_handler::ds3: return "DualShock 3";
case pad_handler::ds4: return "DualShock 4";
case pad_handler::dualsense: return "DualSense";
#ifdef _WIN32
case pad_handler::xinput: return "XInput";
case pad_handler::mm: return "MMJoystick";

View file

@ -8,6 +8,7 @@ enum class pad_handler
keyboard,
ds3,
ds4,
dualsense,
#ifdef _WIN32
xinput,
mm,

View file

@ -0,0 +1,714 @@
#include "stdafx.h"
#include "dualsense_pad_handler.h"
#include "Emu/Io/pad_config.h"
LOG_CHANNEL(dualsense_log, "DualSense");
namespace
{
const auto THREAD_SLEEP = 1ms;
const auto THREAD_SLEEP_INACTIVE = 100ms;
const u32 DUALSENSE_ACC_RES_PER_G = 8192;
const u32 DUALSENSE_GYRO_RES_PER_DEG_S = 1024;
const u32 DUALSENSE_BLUETOOTH_REPORT_SIZE = 78;
const u32 DUALSENSE_USB_REPORT_SIZE = 48;
inline u32 read_u32(const void* buf)
{
return *reinterpret_cast<const u32*>(buf);
}
}
dualsense_pad_handler::dualsense_pad_handler()
: PadHandlerBase(pad_handler::dualsense)
{
// Unique names for the config files and our pad settings dialog
button_list =
{
{ DualSenseKeyCodes::Triangle, "Triangle" },
{ DualSenseKeyCodes::Circle, "Circle" },
{ DualSenseKeyCodes::Cross, "Cross" },
{ DualSenseKeyCodes::Square, "Square" },
{ DualSenseKeyCodes::Left, "Left" },
{ DualSenseKeyCodes::Right, "Right" },
{ DualSenseKeyCodes::Up, "Up" },
{ DualSenseKeyCodes::Down, "Down" },
{ DualSenseKeyCodes::R1, "R1" },
{ DualSenseKeyCodes::R2, "R2" },
{ DualSenseKeyCodes::R3, "R3" },
{ DualSenseKeyCodes::Options, "Options" },
{ DualSenseKeyCodes::Share, "Share" },
{ DualSenseKeyCodes::PSButton, "PS Button" },
{ DualSenseKeyCodes::TouchPad, "Touch Pad" },
{ DualSenseKeyCodes::L1, "L1" },
{ DualSenseKeyCodes::L2, "L2" },
{ DualSenseKeyCodes::L3, "L3" },
{ DualSenseKeyCodes::LSXNeg, "LS X-" },
{ DualSenseKeyCodes::LSXPos, "LS X+" },
{ DualSenseKeyCodes::LSYPos, "LS Y+" },
{ DualSenseKeyCodes::LSYNeg, "LS Y-" },
{ DualSenseKeyCodes::RSXNeg, "RS X-" },
{ DualSenseKeyCodes::RSXPos, "RS X+" },
{ DualSenseKeyCodes::RSYPos, "RS Y+" },
{ DualSenseKeyCodes::RSYNeg, "RS Y-" }
};
init_configs();
// Define border values
thumb_max = 255;
trigger_min = 0;
trigger_max = 255;
vibration_min = 0;
vibration_max = 255;
// Set capabilities
b_has_config = true;
b_has_rumble = true;
b_has_deadzones = true;
b_has_led = false;
b_has_battery = false;
m_name_string = "DualSense Pad #";
m_max_devices = CELL_PAD_MAX_PORT_NUM;
m_trigger_threshold = trigger_max / 2;
m_thumb_threshold = thumb_max / 2;
}
void dualsense_pad_handler::CheckAddDevice(hid_device * hidDevice, hid_device_info* hidDevInfo)
{
std::string serial;
std::shared_ptr<DualSenseDevice> dualsenseDev = std::make_shared<DualSenseDevice>();
dualsenseDev->hidDevice = hidDevice;
std::array<u8, 64> buf{};
buf[0] = 0x09;
// This will give us the bluetooth mac address of the device, regardless if we are on wired or bluetooth.
// So we can't use this to determine if it is a bluetooth device or not.
// Will also enable enhanced feature reports for bluetooth.
if (hid_get_feature_report(hidDevice, buf.data(), 64) == 21)
{
serial = fmt::format("%x%x%x%x%x%x", buf[6], buf[5], buf[4], buf[3], buf[2], buf[1]);
dualsenseDev->dataMode = DualSenseDataMode::Enhanced;
}
else
{
// We're probably on Bluetooth in this case, but for whatever reason the feature report failed.
// This will give us a less capable fallback.
dualsenseDev->dataMode = DualSenseDataMode::Simple;
std::wstring_view wideSerial(hidDevInfo->serial_number);
for (wchar_t ch : wideSerial)
serial += static_cast<uchar>(ch);
}
if (hid_set_nonblocking(hidDevice, 1) == -1)
{
dualsense_log.error("CheckAddDevice: hid_set_nonblocking failed! Reason: %s", hid_error(hidDevice));
hid_close(hidDevice);
return;
}
dualsenseDev->path = hidDevInfo->path;
controllers.emplace(serial, dualsenseDev);
}
bool dualsense_pad_handler::Init()
{
if (is_init)
return true;
const int res = hid_init();
if (res != 0)
fmt::throw_exception("hidapi-init error.threadproc");
hid_device_info* devInfo = hid_enumerate(DUALSENSE_VID, DUALSENSE_PID);
hid_device_info* head = devInfo;
while (devInfo)
{
if (controllers.size() >= MAX_GAMEPADS)
break;
hid_device* dev = hid_open_path(devInfo->path);
if (dev)
{
CheckAddDevice(dev, devInfo);
}
else
{
dualsense_log.error("hid_open_path failed! Reason: %s", hid_error(dev));
}
devInfo = devInfo->next;
}
hid_free_enumeration(head);
if (controllers.empty())
{
dualsense_log.warning("No controllers found!");
}
else
{
dualsense_log.success("Controllers found: %d", controllers.size());
}
is_init = true;
return true;
}
void dualsense_pad_handler::init_config(pad_config* cfg, const std::string& name)
{
if (!cfg) return;
// Set this profile's save location
cfg->cfg_name = name;
// Set default button mapping
cfg->ls_left.def = button_list.at(DualSenseKeyCodes::LSXNeg);
cfg->ls_down.def = button_list.at(DualSenseKeyCodes::LSYNeg);
cfg->ls_right.def = button_list.at(DualSenseKeyCodes::LSXPos);
cfg->ls_up.def = button_list.at(DualSenseKeyCodes::LSYPos);
cfg->rs_left.def = button_list.at(DualSenseKeyCodes::RSXNeg);
cfg->rs_down.def = button_list.at(DualSenseKeyCodes::RSYNeg);
cfg->rs_right.def = button_list.at(DualSenseKeyCodes::RSXPos);
cfg->rs_up.def = button_list.at(DualSenseKeyCodes::RSYPos);
cfg->start.def = button_list.at(DualSenseKeyCodes::Options);
cfg->select.def = button_list.at(DualSenseKeyCodes::Share);
cfg->ps.def = button_list.at(DualSenseKeyCodes::PSButton);
cfg->square.def = button_list.at(DualSenseKeyCodes::Square);
cfg->cross.def = button_list.at(DualSenseKeyCodes::Cross);
cfg->circle.def = button_list.at(DualSenseKeyCodes::Circle);
cfg->triangle.def = button_list.at(DualSenseKeyCodes::Triangle);
cfg->left.def = button_list.at(DualSenseKeyCodes::Left);
cfg->down.def = button_list.at(DualSenseKeyCodes::Down);
cfg->right.def = button_list.at(DualSenseKeyCodes::Right);
cfg->up.def = button_list.at(DualSenseKeyCodes::Up);
cfg->r1.def = button_list.at(DualSenseKeyCodes::R1);
cfg->r2.def = button_list.at(DualSenseKeyCodes::R2);
cfg->r3.def = button_list.at(DualSenseKeyCodes::R3);
cfg->l1.def = button_list.at(DualSenseKeyCodes::L1);
cfg->l2.def = button_list.at(DualSenseKeyCodes::L2);
cfg->l3.def = button_list.at(DualSenseKeyCodes::L3);
// Set default misc variables
cfg->lstickdeadzone.def = 40; // between 0 and 255
cfg->rstickdeadzone.def = 40; // between 0 and 255
cfg->ltriggerthreshold.def = 0; // between 0 and 255
cfg->rtriggerthreshold.def = 0; // between 0 and 255
cfg->lpadsquircling.def = 8000;
cfg->rpadsquircling.def = 8000;
// Set default color value
cfg->colorR.def = 0;
cfg->colorG.def = 0;
cfg->colorB.def = 20;
// Set default LED options
cfg->led_battery_indicator.def = false;
cfg->led_battery_indicator_brightness.def = 10;
cfg->led_low_battery_blink.def = true;
// apply defaults
cfg->from_default();
}
std::vector<std::string> dualsense_pad_handler::ListDevices()
{
std::vector<std::string> dualsense_pads_list;
if (!Init())
return dualsense_pads_list;
for (size_t i = 1; i < controllers.size(); ++i)
{
dualsense_pads_list.emplace_back(m_name_string + std::to_string(i));
}
for (auto& pad : dualsense_pads_list)
{
dualsense_log.success("%s", pad);
}
return dualsense_pads_list;
}
dualsense_pad_handler::DualSenseDataStatus dualsense_pad_handler::GetRawData(const std::shared_ptr<DualSenseDevice>& device)
{
if (!device)
return DualSenseDataStatus::ReadError;
std::array<u8, 128> buf{};
const int res = hid_read(device->hidDevice, buf.data(), 128);
// looks like controller disconnected or read error
if (res == -1)
return DualSenseDataStatus::ReadError;
if (res == 0)
return DualSenseDataStatus::NoNewData;
u8 offset = 0;
switch (buf[0])
{
case 0x01:
if (res == DUALSENSE_BLUETOOTH_REPORT_SIZE)
{
device->dataMode = DualSenseDataMode::Simple;
device->btCon = true;
offset = 1;
}
else
{
device->dataMode = DualSenseDataMode::Enhanced;
device->btCon = false;
offset = 1;
}
break;
case 0x31:
{
device->dataMode = DualSenseDataMode::Enhanced;
device->btCon = true;
offset = 2;
const u8 btHdr = 0xA1;
const u32 crcHdr = CRCPP::CRC::Calculate(&btHdr, 1, crcTable);
const u32 crcCalc = CRCPP::CRC::Calculate(buf.data(), (DUALSENSE_BLUETOOTH_REPORT_SIZE - 4), crcTable, crcHdr);
const u32 crcReported = read_u32(&buf[DUALSENSE_BLUETOOTH_REPORT_SIZE - 4]);
if (crcCalc != crcReported)
{
dualsense_log.warning("Data packet CRC check failed, ignoring! Received 0x%x, Expected 0x%x", crcReported, crcCalc);
return DualSenseDataStatus::NoNewData;
}
break;
}
default:
return DualSenseDataStatus::NoNewData;
}
memcpy(device->padData.data(), &buf[offset], 64);
return DualSenseDataStatus::NewData;
}
bool dualsense_pad_handler::get_is_left_trigger(u64 keyCode)
{
return keyCode == DualSenseKeyCodes::L2;
}
bool dualsense_pad_handler::get_is_right_trigger(u64 keyCode)
{
return keyCode == DualSenseKeyCodes::R2;
}
bool dualsense_pad_handler::get_is_left_stick(u64 keyCode)
{
switch (keyCode)
{
case DualSenseKeyCodes::LSXNeg:
case DualSenseKeyCodes::LSXPos:
case DualSenseKeyCodes::LSYPos:
case DualSenseKeyCodes::LSYNeg:
return true;
default:
return false;
}
}
bool dualsense_pad_handler::get_is_right_stick(u64 keyCode)
{
switch (keyCode)
{
case DualSenseKeyCodes::RSXNeg:
case DualSenseKeyCodes::RSXPos:
case DualSenseKeyCodes::RSYPos:
case DualSenseKeyCodes::RSYNeg:
return true;
default:
return false;
}
}
PadHandlerBase::connection dualsense_pad_handler::update_connection(const std::shared_ptr<PadDevice>& device)
{
auto dualsense_dev = std::static_pointer_cast<DualSenseDevice>(device);
if (!dualsense_dev)
return connection::disconnected;
if (dualsense_dev->hidDevice == nullptr)
{
// try to reconnect
hid_device* dev = hid_open_path(dualsense_dev->path.c_str());
if (dev)
{
if (hid_set_nonblocking(dev, 1) == -1)
{
dualsense_log.error("Reconnecting Device %s: hid_set_nonblocking failed with error %s", dualsense_dev->path, hid_error(dev));
}
dualsense_dev->hidDevice = dev;
}
else
{
// nope, not there
return connection::disconnected;
}
}
status = GetRawData(dualsense_dev);
if (status == DualSenseDataStatus::ReadError)
{
// this also can mean disconnected, either way deal with it on next loop and reconnect
hid_close(dualsense_dev->hidDevice);
dualsense_dev->hidDevice = nullptr;
return connection::no_data;
}
return connection::connected;
}
std::unordered_map<u64, u16> dualsense_pad_handler::get_button_values(const std::shared_ptr<PadDevice>& device)
{
std::unordered_map<u64, u16> keyBuffer;
auto dualsense_dev = std::static_pointer_cast<DualSenseDevice>(device);
if (!dualsense_dev)
return keyBuffer;
auto buf = dualsense_dev->padData;
if (dualsense_dev->dataMode == DualSenseDataMode::Simple)
{
// Left Stick X Axis
keyBuffer[DualSenseKeyCodes::LSXNeg] = Clamp0To255((127.5f - buf[0]) * 2.0f);
keyBuffer[DualSenseKeyCodes::LSXPos] = Clamp0To255((buf[0] - 127.5f) * 2.0f);
// Left Stick Y Axis (Up is the negative for some reason)
keyBuffer[DualSenseKeyCodes::LSYNeg] = Clamp0To255((buf[1] - 127.5f) * 2.0f);
keyBuffer[DualSenseKeyCodes::LSYPos] = Clamp0To255((127.5f - buf[1]) * 2.0f);
// Right Stick X Axis
keyBuffer[DualSenseKeyCodes::RSXNeg] = Clamp0To255((127.5f - buf[2]) * 2.0f);
keyBuffer[DualSenseKeyCodes::RSXPos] = Clamp0To255((buf[2] - 127.5f) * 2.0f);
// Right Stick Y Axis (Up is the negative for some reason)
keyBuffer[DualSenseKeyCodes::RSYNeg] = Clamp0To255((buf[3] - 127.5f) * 2.0f);
keyBuffer[DualSenseKeyCodes::RSYPos] = Clamp0To255((127.5f - buf[3]) * 2.0f);
// bleh, dpad in buffer is stored in a different state
u8 data = buf[4] & 0xf;
switch (data)
{
case 0x08: // none pressed
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x07: // NW...left and up
keyBuffer[DualSenseKeyCodes::Up] = 255;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 255;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x06: // W..left
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 255;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x05: // SW..left down
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 255;
keyBuffer[DualSenseKeyCodes::Left] = 255;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x04: // S..down
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 255;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x03: // SE..down and right
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 255;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 255;
break;
case 0x02: // E... right
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 255;
break;
case 0x01: // NE.. up right
keyBuffer[DualSenseKeyCodes::Up] = 255;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 255;
break;
case 0x00: // n.. up
keyBuffer[DualSenseKeyCodes::Up] = 255;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
default:
fmt::throw_exception("dualsense dpad state encountered unexpected input");
}
data = buf[4] >> 4;
keyBuffer[DualSenseKeyCodes::Square] = ((data & 0x01) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Cross] = ((data & 0x02) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Circle] = ((data & 0x04) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Triangle] = ((data & 0x08) != 0) ? 255 : 0;
data = buf[5];
keyBuffer[DualSenseKeyCodes::L1] = ((data & 0x01) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::R1] = ((data & 0x02) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Share] = ((data & 0x10) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Options] = ((data & 0x20) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::L3] = ((data & 0x40) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::R3] = ((data & 0x80) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::L2] = buf[7];
keyBuffer[DualSenseKeyCodes::R2] = buf[8];
data = buf[6] & 0x03;
keyBuffer[DualSenseKeyCodes::PSButton] = ((data & 0x01) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::TouchPad] = ((data & 0x02) != 0) ? 255 : 0;
return keyBuffer;
}
// Left Stick X Axis
keyBuffer[DualSenseKeyCodes::LSXNeg] = Clamp0To255((127.5f - buf[0]) * 2.0f);
keyBuffer[DualSenseKeyCodes::LSXPos] = Clamp0To255((buf[0] - 127.5f) * 2.0f);
// Left Stick Y Axis (Up is the negative for some reason)
keyBuffer[DualSenseKeyCodes::LSYNeg] = Clamp0To255((buf[1] - 127.5f) * 2.0f);
keyBuffer[DualSenseKeyCodes::LSYPos] = Clamp0To255((127.5f - buf[1]) * 2.0f);
// Right Stick X Axis
keyBuffer[DualSenseKeyCodes::RSXNeg] = Clamp0To255((127.5f - buf[2]) * 2.0f);
keyBuffer[DualSenseKeyCodes::RSXPos] = Clamp0To255((buf[2] - 127.5f) * 2.0f);
// Right Stick Y Axis (Up is the negative for some reason)
keyBuffer[DualSenseKeyCodes::RSYNeg] = Clamp0To255((buf[3] - 127.5f) * 2.0f);
keyBuffer[DualSenseKeyCodes::RSYPos] = Clamp0To255((127.5f - buf[3]) * 2.0f);
u8 data = buf[7] & 0xf;
switch (data)
{
case 0x08: // none pressed
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x07: // NW...left and up
keyBuffer[DualSenseKeyCodes::Up] = 255;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 255;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x06: // W..left
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 255;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x05: // SW..left down
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 255;
keyBuffer[DualSenseKeyCodes::Left] = 255;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x04: // S..down
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 255;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
case 0x03: // SE..down and right
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 255;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 255;
break;
case 0x02: // E... right
keyBuffer[DualSenseKeyCodes::Up] = 0;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 255;
break;
case 0x01: // NE.. up right
keyBuffer[DualSenseKeyCodes::Up] = 255;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 255;
break;
case 0x00: // n.. up
keyBuffer[DualSenseKeyCodes::Up] = 255;
keyBuffer[DualSenseKeyCodes::Down] = 0;
keyBuffer[DualSenseKeyCodes::Left] = 0;
keyBuffer[DualSenseKeyCodes::Right] = 0;
break;
default:
fmt::throw_exception("dualsense dpad state encountered unexpected input");
}
data = buf[7] >> 4;
keyBuffer[DualSenseKeyCodes::Square] = ((data & 0x01) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Cross] = ((data & 0x02) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Circle] = ((data & 0x04) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Triangle] = ((data & 0x08) != 0) ? 255 : 0;
data = buf[8];
keyBuffer[DualSenseKeyCodes::L1] = ((data & 0x01) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::R1] = ((data & 0x02) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Share] = ((data & 0x10) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::Options] = ((data & 0x20) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::L3] = ((data & 0x40) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::R3] = ((data & 0x80) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::L2] = buf[4];
keyBuffer[DualSenseKeyCodes::R2] = buf[5];
data = buf[9] & 0x03;
keyBuffer[DualSenseKeyCodes::PSButton] = ((data & 0x01) != 0) ? 255 : 0;
keyBuffer[DualSenseKeyCodes::TouchPad] = ((data & 0x02) != 0) ? 255 : 0;
return keyBuffer;
}
pad_preview_values dualsense_pad_handler::get_preview_values(std::unordered_map<u64, u16> data)
{
return {data[L2], data[R2], data[LSXPos] - data[LSXNeg], data[LSYPos] - data[LSYNeg], data[RSXPos] - data[RSXNeg], data[RSYPos] - data[RSYNeg]};
}
std::shared_ptr<dualsense_pad_handler::DualSenseDevice> dualsense_pad_handler::GetDualSenseDevice(const std::string& padId)
{
if (!Init())
return nullptr;
size_t pos = padId.find(m_name_string);
if (pos == umax)
return nullptr;
std::string pad_serial = padId.substr(pos + 15);
std::shared_ptr<DualSenseDevice> device = nullptr;
int i = 0; // Controllers 1-n in GUI
for (auto& cur_control : controllers)
{
if (pad_serial == std::to_string(++i) || pad_serial == cur_control.first)
{
device = cur_control.second;
break;
}
}
return device;
}
std::shared_ptr<PadDevice> dualsense_pad_handler::get_device(const std::string& device)
{
std::shared_ptr<DualSenseDevice> dualsense_dev = GetDualSenseDevice(device);
if (dualsense_dev == nullptr || dualsense_dev->hidDevice == nullptr)
return nullptr;
return dualsense_dev;
}
dualsense_pad_handler::~dualsense_pad_handler()
{
for (auto& controller : controllers)
{
if (controller.second->hidDevice)
{
// Disable vibration
controller.second->smallVibrate = 0;
controller.second->largeVibrate = 0;
SendVibrateData(controller.second);
hid_close(controller.second->hidDevice);
}
}
if (hid_exit() != 0)
{
dualsense_log.error("hid_exit failed!");
}
}
int dualsense_pad_handler::SendVibrateData(const std::shared_ptr<DualSenseDevice>& device)
{
if (!device)
return -2;
auto p_profile = device->config;
if (p_profile == nullptr)
return -2; // hid_write and hid_write_control return -1 on error
if (device->btCon)
{
std::array<u8, DUALSENSE_BLUETOOTH_REPORT_SIZE> outputBuf{};
outputBuf[0] = 0x31;
outputBuf[1] = 0x02;
outputBuf[2] |= 0x03;
outputBuf[4] = device->smallVibrate;
outputBuf[5] = device->largeVibrate;
const u8 btHdr = 0xA2;
const u32 crcHdr = CRCPP::CRC::Calculate(&btHdr, 1, crcTable);
const u32 crcCalc = CRCPP::CRC::Calculate(outputBuf.data(), (DUALSENSE_BLUETOOTH_REPORT_SIZE - 4), crcTable, crcHdr);
outputBuf[74] = (crcCalc >> 0) & 0xFF;
outputBuf[75] = (crcCalc >> 8) & 0xFF;
outputBuf[76] = (crcCalc >> 16) & 0xFF;
outputBuf[77] = (crcCalc >> 24) & 0xFF;
return hid_write(device->hidDevice, outputBuf.data(), DUALSENSE_BLUETOOTH_REPORT_SIZE);
}
else
{
std::array<u8, DUALSENSE_USB_REPORT_SIZE> outputBuf{};
outputBuf[0] = 0x02;
outputBuf[1] |= 0x03;
outputBuf[3] = device->smallVibrate;
outputBuf[4] = device->largeVibrate;
return hid_write(device->hidDevice, outputBuf.data(), DUALSENSE_USB_REPORT_SIZE);
}
}
void dualsense_pad_handler::apply_pad_data(const std::shared_ptr<PadDevice>& device, const std::shared_ptr<Pad>& pad)
{
auto dualsense_dev = std::static_pointer_cast<DualSenseDevice>(device);
if (!dualsense_dev || !pad)
return;
auto profile = dualsense_dev->config;
// Attempt to send rumble no matter what
int idx_l = profile->switch_vibration_motors ? 1 : 0;
int idx_s = profile->switch_vibration_motors ? 0 : 1;
int speed_large = profile->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : vibration_min;
int speed_small = profile->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value : vibration_min;
dualsense_dev->newVibrateData |= dualsense_dev->largeVibrate != speed_large || dualsense_dev->smallVibrate != speed_small;
dualsense_dev->largeVibrate = speed_large;
dualsense_dev->smallVibrate = speed_small;
if (dualsense_dev->newVibrateData)
{
if (SendVibrateData(dualsense_dev) >= 0)
{
dualsense_dev->newVibrateData = false;
}
}
}

View file

@ -0,0 +1,104 @@
#pragma once
#include "Emu/Io/PadHandler.h"
#include "Utilities/CRC.h"
#include "hidapi.h"
class dualsense_pad_handler final : public PadHandlerBase
{
enum DualSenseKeyCodes
{
Triangle = 0,
Circle,
Cross,
Square,
Left,
Right,
Up,
Down,
R1,
R3,
L1,
L3,
Share,
Options,
PSButton,
TouchPad,
L2,
R2,
LSXNeg,
LSXPos,
LSYNeg,
LSYPos,
RSXNeg,
RSXPos,
RSYNeg,
RSYPos,
KeyCodeCount
};
enum class DualSenseDataStatus
{
NewData,
NoNewData,
ReadError,
};
enum class DualSenseDataMode
{
Simple,
Enhanced
};
struct DualSenseDevice : public PadDevice
{
hid_device* hidDevice{ nullptr };
std::string path{ "" };
bool btCon{ false };
DualSenseDataMode dataMode{ DualSenseDataMode::Simple };
std::array<u8, 64> padData{};
bool newVibrateData{true};
u8 largeVibrate{0};
u8 smallVibrate{0};
};
const u16 DUALSENSE_VID = 0x054C;
const u16 DUALSENSE_PID = 0x0CE6;
std::unordered_map<std::string, std::shared_ptr<DualSenseDevice>> controllers;
CRCPP::CRC::Table<u32, 32> crcTable{CRCPP::CRC::CRC_32()};
public:
dualsense_pad_handler();
~dualsense_pad_handler();
bool Init() override;
std::vector<std::string> ListDevices() override;
void init_config(pad_config* cfg, const std::string& name) override;
private:
bool is_init = false;
DualSenseDataStatus status;
private:
std::shared_ptr<DualSenseDevice> GetDualSenseDevice(const std::string& padId);
DualSenseDataStatus GetRawData(const std::shared_ptr<DualSenseDevice>& dualsenseDevice);
void CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevInfo);
int SendVibrateData(const std::shared_ptr<DualSenseDevice>& device);
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;
bool get_is_right_stick(u64 keyCode) override;
PadHandlerBase::connection update_connection(const std::shared_ptr<PadDevice>& device) override;
std::unordered_map<u64, u16> get_button_values(const std::shared_ptr<PadDevice>& device) override;
pad_preview_values get_preview_values(std::unordered_map<u64, u16> data) override;
void apply_pad_data(const std::shared_ptr<PadDevice>& device, const std::shared_ptr<Pad>& pad) override;
};

View file

@ -3,6 +3,7 @@
#include "product_info.h"
#include "ds3_pad_handler.h"
#include "ds4_pad_handler.h"
#include "dualsense_pad_handler.h"
#ifdef _WIN32
#include "xinput_pad_handler.h"
#include "mm_joystick_handler.h"
@ -123,6 +124,9 @@ void pad_thread::Init()
case pad_handler::ds4:
cur_pad_handler = std::make_shared<ds4_pad_handler>();
break;
case pad_handler::dualsense:
cur_pad_handler = std::make_shared<dualsense_pad_handler>();
break;
#ifdef _WIN32
case pad_handler::xinput:
cur_pad_handler = std::make_shared<xinput_pad_handler>();

View file

@ -331,6 +331,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="display_sleep_control.cpp" />
<ClCompile Include="Input\dualsense_pad_handler.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="main_application.cpp" />
<ClCompile Include="Input\basic_keyboard_handler.cpp" />
@ -1690,6 +1691,7 @@
<ClInclude Include="display_sleep_control.h" />
<ClInclude Include="Input\ds3_pad_handler.h" />
<ClInclude Include="Input\ds4_pad_handler.h" />
<ClInclude Include="Input\dualsense_pad_handler.h" />
<ClInclude Include="Input\evdev_joystick_handler.h" />
<ClInclude Include="Input\keyboard_pad_handler.h" />
<CustomBuild Include="rpcs3qt\gs_frame.h">

View file

@ -134,6 +134,9 @@
<Filter Include="Gui\network">
<UniqueIdentifier>{bad5498c-a915-4a96-b0cc-f754c02d8e65}</UniqueIdentifier>
</Filter>
<Filter Include="Io\DualSense">
<UniqueIdentifier>{ff7eb8a7-5545-41af-bab1-348032b9a430}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
@ -1102,6 +1105,9 @@
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_localized_emu.cpp">
<Filter>Generated Files\Debug - LLVM</Filter>
</ClCompile>
<ClCompile Include="Input\dualsense_pad_handler.cpp">
<Filter>Io\DualSense</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h">
@ -1203,6 +1209,9 @@
<ClInclude Include="rpcs3qt\curl_handle.h">
<Filter>Gui\network</Filter>
</ClInclude>
<ClInclude Include="Input\dualsense_pad_handler.h">
<Filter>Io\DualSense</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">

View file

@ -23,6 +23,7 @@
#include "Input/keyboard_pad_handler.h"
#include "Input/ds3_pad_handler.h"
#include "Input/ds4_pad_handler.h"
#include "Input/dualsense_pad_handler.h"
#ifdef _WIN32
#include "Input/xinput_pad_handler.h"
#include "Input/mm_joystick_handler.h"
@ -1137,6 +1138,9 @@ std::shared_ptr<PadHandlerBase> pad_settings_dialog::GetHandler(pad_handler type
case pad_handler::ds4:
ret_handler = std::make_unique<ds4_pad_handler>();
break;
case pad_handler::dualsense:
ret_handler = std::make_unique<dualsense_pad_handler>();
break;
#ifdef _WIN32
case pad_handler::xinput:
ret_handler = std::make_unique<xinput_pad_handler>();
@ -1214,16 +1218,22 @@ void pad_settings_dialog::ChangeInputType()
m_description = tooltips.gamepad_settings.ds3_windows; break;
case pad_handler::ds4:
m_description = tooltips.gamepad_settings.ds4_windows; break;
case pad_handler::dualsense:
m_description = tooltips.gamepad_settings.dualsense_windows; break;
#elif __linux__
case pad_handler::ds3:
m_description = tooltips.gamepad_settings.ds3_linux; break;
case pad_handler::ds4:
m_description = tooltips.gamepad_settings.ds4_linux; break;
case pad_handler::dualsense:
m_description = tooltips.gamepad_settings.dualsense_linux; break;
#else
case pad_handler::ds3:
m_description = tooltips.gamepad_settings.ds3_other; break;
case pad_handler::ds4:
m_description = tooltips.gamepad_settings.ds4_other; break;
case pad_handler::dualsense:
m_description = tooltips.gamepad_settings.dualsense_other; break;
#endif
#ifdef HAVE_LIBEVDEV
case pad_handler::evdev:
@ -1246,6 +1256,7 @@ void pad_settings_dialog::ChangeInputType()
#endif
case pad_handler::ds3:
case pad_handler::ds4:
case pad_handler::dualsense:
{
const QString name_string = qstr(m_handler->name_string());
for (size_t i = 1; i <= m_handler->max_devices(); i++) // Controllers 1-n in GUI
@ -1383,6 +1394,9 @@ void pad_settings_dialog::ChangeProfile()
case pad_handler::ds4:
static_cast<ds4_pad_handler*>(m_handler.get())->init_config(&m_handler_cfg, cfg_name);
break;
case pad_handler::dualsense:
static_cast<dualsense_pad_handler*>(m_handler.get())->init_config(&m_handler_cfg, cfg_name);
break;
#ifdef _WIN32
case pad_handler::xinput:
static_cast<xinput_pad_handler*>(m_handler.get())->init_config(&m_handler_cfg, cfg_name);
@ -1595,6 +1609,7 @@ QString pad_settings_dialog::GetLocalizedPadHandler(const QString& original, pad
case pad_handler::keyboard: return tr("Keyboard");
case pad_handler::ds3: return tr("DualShock 3");
case pad_handler::ds4: return tr("DualShock 4");
case pad_handler::dualsense: return tr("DualSense");
#ifdef _WIN32
case pad_handler::xinput: return tr("XInput");
case pad_handler::mm: return tr("MMJoystick");

View file

@ -212,6 +212,9 @@ public:
const QString ds4_windows = tr("If you have any issues with the DualShock 4 handler, it might be caused by third-party tools such as DS4Windows. It's recommended that you disable them while using this handler.");
const QString ds4_linux = tr("In order to use the DualShock 4 handler, you might need to add udev rules to let RPCS3 access the controller.\nSee the <a href=\"https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration\">RPCS3 Wiki</a> for instructions.");
const QString ds4_other = tr("The DualShock 4 handler is recommended for official DualShock 4 controllers.");
const QString dualsense_windows = tr("The DualSense handler is recommended for official DualSense controllers.");
const QString dualsense_linux = tr("The DualSense handler is recommended for official DualSense controllers.");
const QString dualsense_other = tr("The DualSense handler is recommended for official DualSense controllers.");
const QString xinput = tr("The XInput handler will work with Xbox controllers and many third-party PC-compatible controllers. Pressure sensitive buttons from SCP are supported when SCP's XInput1_3.dll is placed in the main RPCS3 directory. For more details, see the <a href=\"https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration\">RPCS3 Wiki</a>.");
const QString evdev = tr("The evdev handler should work with any controller that has linux support.<br>If your joystick is not being centered properly, read the <a href=\"https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration\">RPCS3 Wiki</a> for instructions.");
const QString mmjoy = tr("The MMJoystick handler should work with almost any controller recognized by Windows. However, it is recommended that you use the more specific handlers if you have a controller that supports them.");