Skip to content

Commit

Permalink
feat(DualSense): add rumble support
Browse files Browse the repository at this point in the history
  • Loading branch information
ShadowApex committed Apr 14, 2024
1 parent c1d8bc7 commit 6bbfbeb
Show file tree
Hide file tree
Showing 8 changed files with 1,194 additions and 685 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ CACHE_DIR := .cache

# Build variables
BUILD_TYPE ?= release
LOG_LEVEL ?= debug

# Docker image variables
IMAGE_NAME ?= inputplumber-builder
Expand Down
1 change: 1 addition & 0 deletions src/drivers/dualsense/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub const INPUT_REPORT_BT: u8 = 0x31;
pub const INPUT_REPORT_BT_SIZE: usize = 78;
pub const OUTPUT_REPORT_USB: u8 = 0x02;
pub const OUTPUT_REPORT_USB_SIZE: usize = 63;
pub const OUTPUT_REPORT_USB_SHORT_SIZE: usize = 48;
pub const OUTPUT_REPORT_BT: u8 = 0x31;
pub const OUTPUT_REPORT_BT_SIZE: usize = 78;

Expand Down
41 changes: 23 additions & 18 deletions src/drivers/dualsense/hid_report.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Structures derived from the great work of the community of the Game Controller
//! Collective Wiki.
//! Source: https://controllers.fandom.com/wiki/Sony_DualSense
use packed_struct::prelude::*;

use super::driver::*;
Expand Down Expand Up @@ -55,7 +58,6 @@ pub struct TouchData {
pub timestamp: u8,
}

// Source: https://controllers.fandom.com/wiki/Sony_DualSense#HID_Report_0x01_Input_USB
#[derive(PackedStruct, Debug, Copy, Clone, PartialEq)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "64")]
pub struct USBPackedInputDataReport {
Expand Down Expand Up @@ -327,21 +329,15 @@ pub struct BluetoothPackedInputDataReport {

// byte 5
#[packed_field(bits = "40")]
pub down: bool, // Directional buttons
pub triangle: bool, // Button cluster, x, ◯, □, ∆
#[packed_field(bits = "41")]
pub left: bool,
pub circle: bool,
#[packed_field(bits = "42")]
pub right: bool,
#[packed_field(bits = "43")]
pub up: bool,
#[packed_field(bits = "44")]
pub square: bool, // Button cluster, x, ◯, □, ∆
#[packed_field(bits = "45")]
pub cross: bool,
#[packed_field(bits = "46")]
pub circle: bool,
#[packed_field(bits = "47")]
pub triangle: bool,
#[packed_field(bits = "43")]
pub square: bool,
#[packed_field(bits = "44..=47", ty = "enum")]
pub dpad: Direction, // Directional buttons

// byte 6
#[packed_field(bits = "48")]
Expand Down Expand Up @@ -388,10 +384,7 @@ impl BluetoothPackedInputDataReport {
l2_trigger: 0,
r2_trigger: 0,
counter: [0, 0, 0, 0, 0, 0],
down: false,
left: false,
right: false,
up: false,
dpad: Direction::None,
square: false,
cross: false,
circle: false,
Expand Down Expand Up @@ -608,7 +601,7 @@ pub struct SetStatePackedOutputData {
}

#[derive(PackedStruct, Debug, Copy, Clone, PartialEq)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "48")]
#[packed_struct(bit_numbering = "msb0", size_bytes = "63")]
pub struct UsbPackedOutputReport {
// byte 0
#[packed_field(bytes = "0")]
Expand All @@ -618,3 +611,15 @@ pub struct UsbPackedOutputReport {
#[packed_field(bytes = "1..=47")]
pub state: SetStatePackedOutputData,
}

#[derive(PackedStruct, Debug, Copy, Clone, PartialEq)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "48")]
pub struct UsbPackedOutputReportShort {
// byte 0
#[packed_field(bytes = "0")]
pub report_id: u8, // Report ID (always 0x02)

// byte 1-47
#[packed_field(bytes = "1..=47")]
pub state: SetStatePackedOutputData,
}
3 changes: 2 additions & 1 deletion src/input/composite_device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,11 @@ pub struct CompositeDevice {
/// E.g. {"/org/shadowblip/InputPlumber/devices/target/dbus0": <Sender>}
target_dbus_devices: HashMap<String, mpsc::Sender<TargetCommand>>,
/// Set of available Force Feedback effect IDs that are not in use
/// TODO: Just use the keys from ff_effect_id_source_map to determine next id
ff_effect_ids: BTreeSet<i16>,
/// Source devices use their own IDs for uploaded force feedback effects.
/// This mapping maps the composite device effect ids to source device effect ids.
/// E.g. {3: {"evdev://event0": 6}}
/// E.g. {3: {"evdev://event0": 6, "evdev://event1": 2}}
ff_effect_id_source_map: HashMap<i16, HashMap<String, i16>>,
}

Expand Down
3 changes: 3 additions & 0 deletions src/input/output_capability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
/// is capable of handling. E.g. Force Feedback, LED control, etc.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum OutputCapability {
NotImplemented,
ForceFeedback,
ForceFeedbackUpload,
ForceFeedbackErase,
#[allow(clippy::upper_case_acronyms)]
LED(LED),
}
Expand Down
42 changes: 42 additions & 0 deletions src/input/output_event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,53 @@ use std::sync::mpsc::Sender;

use ::evdev::{FFEffectData, InputEvent};

use crate::drivers::dualsense::hid_report::SetStatePackedOutputData;

use super::output_capability::OutputCapability;

/// Output events are events that flow from target devices back to source devices
#[derive(Debug, Clone)]
pub enum OutputEvent {
Evdev(InputEvent),
Uinput(UinputOutputEvent),
DualSense(SetStatePackedOutputData),
}

impl OutputEvent {
/// Returns the capability of the output event
fn as_capability(&self) -> OutputCapability {
match self {
OutputEvent::Evdev(event) => match event.destructure() {
evdev::EventSummary::Synchronization(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::Key(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::RelativeAxis(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::AbsoluteAxis(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::Misc(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::Switch(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::Led(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::Sound(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::Repeat(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::ForceFeedback(_, _, _) => OutputCapability::ForceFeedback,
evdev::EventSummary::Power(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::ForceFeedbackStatus(_, _, _) => {
OutputCapability::NotImplemented
}
evdev::EventSummary::UInput(_, _, _) => OutputCapability::NotImplemented,
evdev::EventSummary::Other(_, _, _) => OutputCapability::NotImplemented,
},
OutputEvent::Uinput(uinput) => match uinput {
UinputOutputEvent::FFUpload(_, _) => OutputCapability::ForceFeedbackUpload,
UinputOutputEvent::FFErase(_) => OutputCapability::ForceFeedbackErase,
},
OutputEvent::DualSense(report) => {
if report.use_rumble_not_haptics {
OutputCapability::ForceFeedback
} else {
OutputCapability::NotImplemented
}
}
}
}
}

#[derive(Debug, Clone)]
Expand Down
Loading

0 comments on commit 6bbfbeb

Please sign in to comment.