From e3ddd1459542e4c2457faf5e4e9ce482050be410 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Sat, 8 Feb 2025 15:46:49 -0800 Subject: [PATCH] fix(Device Hiding): Use static udev rule when ManageAllDevices is set. --- .../usr/lib/udev/hwdb.d/59-inputplumber.hwdb | 25 ++++++++++ .../devices/60-horipad_steam.yaml | 10 ++-- .../devices/60-ps5_ds_gamepad.yaml | 6 +-- src/input/manager.rs | 18 ++++++- src/input/target/xb360.rs | 2 +- src/input/target/xbox_elite.rs | 2 +- src/input/target/xbox_series.rs | 2 +- src/main.rs | 4 ++ src/udev/mod.rs | 49 ++++++++++++++++++- 9 files changed, 103 insertions(+), 15 deletions(-) diff --git a/rootfs/usr/lib/udev/hwdb.d/59-inputplumber.hwdb b/rootfs/usr/lib/udev/hwdb.d/59-inputplumber.hwdb index 75169472..352a3e90 100644 --- a/rootfs/usr/lib/udev/hwdb.d/59-inputplumber.hwdb +++ b/rootfs/usr/lib/udev/hwdb.d/59-inputplumber.hwdb @@ -10,3 +10,28 @@ evdev:name:AT Translated Set 2 keyboard:dmi:*:svnOrangePi:pnNEO-01:* KEYBOARD_KEY_66=f15 KEYBOARD_KEY_67=f16 +# List of virtual devices provided by inputplumber +# Sony Dualsense Edge +hid:b0003g0001v0000054Cp00000DF2 + INPUTPLUMBER_VIRT=1 + +# Valve Steam Deck +hid:b0003g0103v000028DEp00001205 + INPUTPLUMBER_VIRT=1 + +# Horipad Steam Controller +hid:b0003g0001v00000F0Dp000001AB + INPUTPLUMBER_VIRT=1 + +# XBox 360 +input:b0003v045Ep028EeACED-e0,1,3,15,k130,131,133,134,136,137,13A,13B,13C,13D,13E,2C0,2C1,2C2,2C3,ra0,1,2,3,4,5,10,11,mlsf50,51,58,59,5A,60,w + INPUTPLUMBER_VIRT=1 + +# Xbox Series +input:b0003v045Ep0B12eACED-e0,1,3,15,kA7,130,131,133,134,136,137,13A,13B,13C,13D,13E,2C0,2C1,2C2,2C3,ra0,1,2,3,4,5,10,11,mlsf50,51,58,59,5A,60,w + INPUTPLUMBER_VIRT=1 + +# Xbox Elite 2 +input:b0003v045Ep0B00eACED-e0,1,3,15,kA7,130,131,133,134,136,137,13A,13B,13C,13D,13E,2C0,2C1,2C2,2C3,2C4,2C5,2C6,2C7,ra0,1,2,3,4,5,10,11,mlsf50,51,58,59,5A,60,w + INPUTPLUMBER_VIRT=1 + diff --git a/rootfs/usr/share/inputplumber/devices/60-horipad_steam.yaml b/rootfs/usr/share/inputplumber/devices/60-horipad_steam.yaml index 44a77834..98f45b6b 100644 --- a/rootfs/usr/share/inputplumber/devices/60-horipad_steam.yaml +++ b/rootfs/usr/share/inputplumber/devices/60-horipad_steam.yaml @@ -8,16 +8,16 @@ kind: CompositeDevice # Name of the composite device mapping name: Horipad Steam -# Only use this profile if *any* of the given matches matches. If this list is -# empty,then the source devices will *always* be checked. -# /sys/class/dmi/id/product_name -matches: [] - # Only allow a CompositeDevice to manage at most the given number of # source devices. When this limit is reached, a new CompositeDevice will be # created for any new matching devices. maximum_sources: 2 +# Only use this profile if *any* of the given matches matches. If this list is +# empty,then the source devices will *always* be checked. +# /sys/class/dmi/id/product_name +matches: [] + # One or more source devices to combine into a single virtual device. The events # from these devices will be watched and translated according to the key map. source_devices: diff --git a/rootfs/usr/share/inputplumber/devices/60-ps5_ds_gamepad.yaml b/rootfs/usr/share/inputplumber/devices/60-ps5_ds_gamepad.yaml index d91faf2f..e066b7f2 100644 --- a/rootfs/usr/share/inputplumber/devices/60-ps5_ds_gamepad.yaml +++ b/rootfs/usr/share/inputplumber/devices/60-ps5_ds_gamepad.yaml @@ -26,21 +26,21 @@ source_devices: - group: gamepad blocked: true evdev: - name: "{Sony Interactive Entertainment DualSense Wireless Controller,DualSense Wireless Controller}" + name: "*DualSense Wireless Controller" vendor_id: 054c product_id: 0ce6 handler: event* - group: gamepad blocked: true evdev: - name: "{Sony Interactive Entertainment DualSense Wireless Controller Touchpad,DualSense Wireless Controller Touchpad}" + name: "*DualSense Wireless Controller Touchpad" vendor_id: 054c product_id: 0ce6 handler: event* - group: gamepad blocked: true evdev: - name: "{Sony Interactive Entertainment DualSense Wireless Controller Motion Sensors,DualSense Wireless Controller Motion Sensors}" + name: "*DualSense Wireless Controller Motion Sensors" vendor_id: 054c product_id: 0ce6 handler: event* diff --git a/src/input/manager.rs b/src/input/manager.rs index dd505810..78954adf 100644 --- a/src/input/manager.rs +++ b/src/input/manager.rs @@ -40,8 +40,10 @@ use crate::input::source::iio; use crate::input::target::TargetDevice; use crate::input::target::TargetDeviceTypeId; use crate::udev; +use crate::udev::block_joysticks; use crate::udev::device::AttributeGetter; use crate::udev::device::UdevDevice; +use crate::udev::unblock_joysticks; use super::composite_device::client::CompositeDeviceClient; use super::target::client::TargetDeviceClient; @@ -350,8 +352,13 @@ impl Manager { } self.manage_all_devices = manage_all_devices; - // If management of all devices was enabled, trigger device discovery + // If management of all devices was enabled, add the needed udev rules and trigger + // device discovery if manage_all_devices { + if let Err(e) = block_joysticks().await { + log::error!("Failed to set udev rules to block source devices. Double input is possible. {e:?}"); + } + let cmd_tx = self.tx.clone(); tokio::task::spawn(async move { if let Err(e) = Manager::discover_all_devices(&cmd_tx).await { @@ -363,6 +370,10 @@ impl Manager { // If management was disabled, stop any composite devices that // are not auto-managed. + if let Err(e) = unblock_joysticks().await { + log::error!("Failed to unset udev rules blocking source devices. Input devices may be unavailable. {e:?}"); + } + for (dbus_path, config) in self.used_configs.iter() { if let Some(options) = config.options.as_ref() { let auto_managed = options.auto_manage.unwrap_or(false); @@ -1014,12 +1025,15 @@ impl Manager { // Check if the virtual device is using the bluetooth bus // TODO: Can we get properties from UdevDevice::get_attribute_from_tree? + let bus_id = device.get_attribute_from_tree("id/bustype"); let id_bus = device_info.properties.get("ID_BUS"); - log::debug!("Bus ID for {dev_path}: {id_bus:?}"); + log::debug!("Bus ID for {dev_path}: udev: {id_bus:?}, UdevDevice: {bus_id:?}"); let is_bluetooth = { if let Some(bus) = id_bus { bus == "bluetooth" + } else if let Some(bus) = bus_id { + bus == "0005" } else { false } diff --git a/src/input/target/xb360.rs b/src/input/target/xb360.rs index 11e762b5..343a2cbf 100644 --- a/src/input/target/xb360.rs +++ b/src/input/target/xb360.rs @@ -110,7 +110,7 @@ impl XBox360Controller { ff.insert(FFEffectCode::FF_GAIN); // Identify to the kernel as an Xbox One Elite - let id = InputId::new(BusType(3), 0x045e, 0x028e, 0x0001); + let id = InputId::new(BusType(3), 0x045e, 0x028e, 0xaced); // Build the device let device = VirtualDeviceBuilder::new()? diff --git a/src/input/target/xbox_elite.rs b/src/input/target/xbox_elite.rs index 409a0fd5..2f340bdc 100644 --- a/src/input/target/xbox_elite.rs +++ b/src/input/target/xbox_elite.rs @@ -115,7 +115,7 @@ impl XboxEliteController { ff.insert(FFEffectCode::FF_GAIN); // Identify to the kernel as an Xbox One Elite - let id = InputId::new(BusType(3), 0x045e, 0x0b00, 0x0001); + let id = InputId::new(BusType(3), 0x045e, 0x0b00, 0xaced); // Build the device let device = VirtualDeviceBuilder::new()? diff --git a/src/input/target/xbox_series.rs b/src/input/target/xbox_series.rs index c55dd2ae..ecc1f3ef 100644 --- a/src/input/target/xbox_series.rs +++ b/src/input/target/xbox_series.rs @@ -111,7 +111,7 @@ impl XboxSeriesController { ff.insert(FFEffectCode::FF_GAIN); // Identify to the kernel as an Xbox One Elite - let id = InputId::new(BusType(3), 0x045e, 0x0b12, 0x0001); + let id = InputId::new(BusType(3), 0x045e, 0x0b12, 0xaced); // Build the device let device = VirtualDeviceBuilder::new()? diff --git a/src/main.rs b/src/main.rs index e502931a..b05cd44c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; +use inputplumber::udev::unblock_joysticks; use std::env; use std::error::Error; use std::process; @@ -62,6 +63,9 @@ async fn main() -> Result<(), Box> { tokio::spawn(async move { tokio::signal::ctrl_c().await.unwrap(); log::info!("Un-hiding all devices"); + if let Err(e) = unblock_joysticks().await { + log::error!("Unable to unblock normal joysticks: {e:?}"); + } if let Err(e) = unhide_all().await { log::error!("Unable to un-hide devices: {:?}", e); } diff --git a/src/udev/mod.rs b/src/udev/mod.rs index a5967f3d..b0f71105 100644 --- a/src/udev/mod.rs +++ b/src/udev/mod.rs @@ -13,9 +13,54 @@ use udev::Enumerator; use self::device::Device; -const RULE_PRIORITY: &str = "73"; +const RULE_PRIORITY: &str = "59"; const RULES_PREFIX: &str = "/run/udev/rules.d"; +/// Hide all removable input devices from regular users. +pub async fn block_joysticks() -> Result<(), Box> { + // Find the chmod command to use for hiding + let chmod_cmd = if Path::new("/bin/chmod").exists() { + "/bin/chmod" + } else { + "/usr/bin/chmod" + }; + + let rule = format!( + r#"# Hide all evdev devices that are not InputPlumber virtual devices +ACTION=="add|change", SUBSYSTEM=="input", KERNEL=="js[0-9]*|event[0-9]*", ENV{{ID_INPUT_JOYSTICK}}=="1", ENV{{INPUTPLUMBER_VIRT}}!="1", MODE:="0000", GROUP:="root", RUN:="{chmod_cmd} 000 %p" + +# Hide all Horipad Steam Controller hidraw devices +ACTION=="add|change", SUBSYSTEM=="hidraw", KERNEL=="hidraw[0-9]*", ATTR{{idVendor}}=="0F0D", ATTR{{idProduct}}=="0196", ENV{{INPUTPLUMBER_VIRT}}!="1", MODE:="0000", GROUP:="root", RUN:="{chmod_cmd} 000 %p" +ACTION=="add|change", SUBSYSTEM=="hidraw", KERNEL=="hidraw[0-9]*", ATTR{{idVendor}}=="0F0D", ATTR{{idProduct}}=="01AB", ENV{{INPUTPLUMBER_VIRT}}!="1", MODE:="0000", GROUP:="root", RUN:="{chmod_cmd} 000 %p" + +# Hide all PlayStation hidraw devices +ACTION=="add|change", SUBSYSTEMS=="hid", DRIVERS=="playstation", GOTO="playstation_start" +GOTO="playstation_end" +LABEL="playstation_start" +ACTION=="add|change", SUBSYSTEM=="hidraw", KERNEL=="hidraw[0-9]*", ENV{{INPUTPLUMBER_VIRT}}!="1", MODE:="0000", GROUP:="root", RUN:="{chmod_cmd} 000 %p" +LABEL="playstation_end" +"# + ); + + // Write the udev rule + fs::create_dir_all(RULES_PREFIX)?; + let rule_path = format!("{RULES_PREFIX}/51-inputplumber-hide-joysticks.rules"); + fs::write(rule_path, rule)?; + + reload_all().await?; + + Ok(()) +} + +/// Unhide all removable input devices from regular users. +pub async fn unblock_joysticks() -> Result<(), Box> { + let rule_path = format!("{RULES_PREFIX}/51-inputplumber-hide-joysticks.rules"); + fs::remove_file(rule_path)?; + reload_all().await?; + + Ok(()) +} + /// Hide the given input device from regular users. pub async fn hide_device(path: String) -> Result<(), Box> { // Get the device to hide @@ -43,7 +88,7 @@ pub async fn hide_device(path: String) -> Result<(), Box> { {match_rule}, GOTO="inputplumber_valid" GOTO="inputplumber_end" LABEL="inputplumber_valid" -ACTION=="add", KERNEL=="hidraw[0-9]*|js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE="000", GROUP="root", SYMLINK+="inputplumber/%k", TAG-="uaccess", RUN:="{chmod_cmd} 000 {path}" +ACTION=="add|change", KERNEL=="hidraw[0-9]*|js[0-9]*|event[0-9]*", SUBSYSTEM=="{subsystem}", MODE:="0000", GROUP:="root", RUN:="{chmod_cmd} 000 {path}", SYMLINK+="inputplumber/%k" LABEL="inputplumber_end" "# );