Skip to content

Commit 1ac2fd6

Browse files
committed
fix(TargetDevice): add method for target devices to query composite device config
1 parent b399900 commit 1ac2fd6

File tree

5 files changed

+134
-25
lines changed

5 files changed

+134
-25
lines changed

src/input/composite_device/client.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet};
22
use thiserror::Error;
33
use tokio::sync::mpsc::{channel, error::SendError, Sender};
44

5+
use crate::config::CompositeDeviceConfig;
56
use crate::input::event::native::NativeEvent;
67
use crate::input::target::client::TargetDeviceClient;
78
use crate::input::{capability::Capability, event::Event, output_event::OutputEvent};
@@ -97,6 +98,26 @@ impl CompositeDeviceClient {
9798
Err(ClientError::ChannelClosed)
9899
}
99100

101+
/// Get the [CompositeDeviceConfig] from the [CompositeDevice]
102+
pub async fn get_config(&self) -> Result<CompositeDeviceConfig, ClientError> {
103+
let (tx, mut rx) = channel(1);
104+
self.tx.send(CompositeCommand::GetConfig(tx)).await?;
105+
if let Some(config) = rx.recv().await {
106+
return Ok(config);
107+
}
108+
Err(ClientError::ChannelClosed)
109+
}
110+
111+
/// Get the [CompositeDeviceConfig] from the [CompositeDevice] (blocking)
112+
pub fn blocking_get_config(&self) -> Result<CompositeDeviceConfig, ClientError> {
113+
let (tx, mut rx) = channel(1);
114+
self.tx.blocking_send(CompositeCommand::GetConfig(tx))?;
115+
if let Some(config) = rx.blocking_recv() {
116+
return Ok(config);
117+
}
118+
Err(ClientError::ChannelClosed)
119+
}
120+
100121
/// Get capabilities from all target devices
101122
pub async fn get_target_capabilities(&self) -> Result<HashSet<Capability>, ClientError> {
102123
let (tx, mut rx) = channel(1);
@@ -139,6 +160,17 @@ impl CompositeDeviceClient {
139160
Err(ClientError::ChannelClosed)
140161
}
141162

163+
/// Get the source device paths of the composite device (blocking)
164+
pub fn blocking_get_source_device_paths(&self) -> Result<Vec<String>, ClientError> {
165+
let (tx, mut rx) = channel(1);
166+
self.tx
167+
.blocking_send(CompositeCommand::GetSourceDevicePaths(tx))?;
168+
if let Some(paths) = rx.blocking_recv() {
169+
return Ok(paths);
170+
}
171+
Err(ClientError::ChannelClosed)
172+
}
173+
142174
/// Get the target device paths of the composite device
143175
pub async fn get_target_device_paths(&self) -> Result<Vec<String>, ClientError> {
144176
let (tx, mut rx) = channel(1);

src/input/composite_device/command.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet};
33
use tokio::sync::mpsc;
44

55
use crate::{
6+
config::CompositeDeviceConfig,
67
input::{
78
capability::Capability,
89
event::{native::NativeEvent, Event},
@@ -20,6 +21,7 @@ use super::InterceptMode;
2021
#[derive(Debug, Clone)]
2122
pub enum CompositeCommand {
2223
AttachTargetDevices(HashMap<String, TargetDeviceClient>),
24+
GetConfig(mpsc::Sender<CompositeDeviceConfig>),
2325
GetCapabilities(mpsc::Sender<HashSet<Capability>>),
2426
GetDBusDevicePaths(mpsc::Sender<Vec<String>>),
2527
GetInterceptMode(mpsc::Sender<InterceptMode>),

src/input/composite_device/mod.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,12 @@ impl CompositeDevice {
342342
log::error!("Failed to send intercept mode: {:?}", e);
343343
}
344344
}
345+
CompositeCommand::GetConfig(sender) => {
346+
log::error!("sendin config");
347+
if let Err(e) = sender.send(self.config.clone()).await {
348+
log::error!("Failed to send config: {e:?}");
349+
}
350+
}
345351
CompositeCommand::GetSourceDevicePaths(sender) => {
346352
if let Err(e) = sender.send(self.get_source_device_paths()).await {
347353
log::error!("Failed to send source device paths: {:?}", e);
@@ -1900,6 +1906,14 @@ impl CompositeDevice {
19001906
) -> Result<(), Box<dyn Error>> {
19011907
// Keep track of all target devices
19021908
for (path, target) in targets.into_iter() {
1909+
// Query the target device for its capabilities
1910+
let caps = match target.get_capabilities().await {
1911+
Ok(caps) => caps,
1912+
Err(e) => {
1913+
return Err(format!("Failed to get target capabilities: {e:?}").into());
1914+
}
1915+
};
1916+
19031917
log::debug!("Attaching target device: {path}");
19041918
if let Err(e) = target.set_composite_device(self.client()).await {
19051919
return Err(
@@ -1911,14 +1925,6 @@ impl CompositeDevice {
19111925
self.dbus_path.as_ref().unwrap_or(&"".to_string())
19121926
);
19131927

1914-
// Query the target device for its capabilities
1915-
let caps = match target.get_capabilities().await {
1916-
Ok(caps) => caps,
1917-
Err(e) => {
1918-
return Err(format!("Failed to get target capabilities: {e:?}").into());
1919-
}
1920-
};
1921-
19221928
// Add the target device
19231929
self.target_devices_queued.remove(&path);
19241930
self.target_devices.insert(path.clone(), target);

src/input/target/mod.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::dbus::interface::target::gamepad::TargetGamepadInterface;
1313

1414
use super::{
1515
capability::Capability,
16-
composite_device::client::CompositeDeviceClient,
16+
composite_device::client::{ClientError, CompositeDeviceClient},
1717
event::native::{NativeEvent, ScheduledNativeEvent},
1818
output_capability::OutputCapability,
1919
output_event::OutputEvent,
@@ -87,6 +87,12 @@ impl From<io::Error> for InputError {
8787
}
8888
}
8989

90+
impl From<ClientError> for InputError {
91+
fn from(value: ClientError) -> Self {
92+
InputError::DeviceError(value.to_string())
93+
}
94+
}
95+
9096
/// Possible errors for a target device client
9197
#[derive(Error, Debug)]
9298
pub enum OutputError {
@@ -285,6 +291,14 @@ pub trait TargetInputDevice {
285291
/// that the target device should stop sending input.
286292
fn clear_state(&mut self) {}
287293

294+
/// Called when the target device has been attached to a composite device.
295+
fn on_composite_device_attached(
296+
&mut self,
297+
_device: CompositeDeviceClient,
298+
) -> Result<(), InputError> {
299+
Ok(())
300+
}
301+
288302
/// Stop the target device
289303
fn stop(&mut self) -> Result<(), InputError> {
290304
Ok(())
@@ -486,7 +500,8 @@ impl<T: TargetInputDevice + TargetOutputDevice + Send + 'static> TargetDriver<T>
486500
implementation.write_event(event)?;
487501
}
488502
TargetCommand::SetCompositeDevice(device) => {
489-
*composite_device = Some(device);
503+
*composite_device = Some(device.clone());
504+
implementation.on_composite_device_attached(device)?;
490505
}
491506
TargetCommand::GetCapabilities(sender) => {
492507
let capabilities = implementation.get_capabilities().unwrap_or_default();

src/input/target/steam_deck.rs

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use super::{InputError, OutputError, TargetInputDevice, TargetOutputDevice};
5151
const MIN_FRAME_TIME: Duration = Duration::from_millis(80);
5252

5353
pub struct SteamDeckDevice {
54-
device: VirtualUSBDevice,
54+
device: Option<VirtualUSBDevice>,
5555
state: PackedInputDataReport,
5656
/// Steam will send 'SetReport' commands with a report type, so it can fetch
5757
/// a particular result with 'GetReport'
@@ -70,12 +70,8 @@ impl SteamDeckDevice {
7070
return Err(e.to_string().into());
7171
}
7272

73-
// Create and start the virtual USB device
74-
let mut device = SteamDeckDevice::create_virtual_device()?;
75-
device.start()?;
76-
7773
Ok(Self {
78-
device,
74+
device: None,
7975
state: PackedInputDataReport::default(),
8076
current_report: ReportType::InputData,
8177
lizard_mode_enabled: false,
@@ -86,9 +82,9 @@ impl SteamDeckDevice {
8682
}
8783

8884
/// Create the virtual device to emulate
89-
fn create_virtual_device() -> Result<VirtualUSBDevice, Box<dyn Error>> {
85+
fn create_virtual_device(product_id: u16) -> Result<VirtualUSBDevice, Box<dyn Error>> {
9086
// Configuration values can be obtained from a real device with "sudo lsusb -v"
91-
let virtual_device = VirtualUSBDeviceBuilder::new(VID, PID)
87+
let virtual_device = VirtualUSBDeviceBuilder::new(VID, product_id)
9288
.class(DeviceClass::UseInterface)
9389
.supported_langs(vec![LangId::EnglishUnitedStates])
9490
.manufacturer("Valve Software")
@@ -673,6 +669,44 @@ impl SteamDeckDevice {
673669
}
674670

675671
impl TargetInputDevice for SteamDeckDevice {
672+
/// Start the driver when attached to a composite device.
673+
fn on_composite_device_attached(
674+
&mut self,
675+
composite_device: CompositeDeviceClient,
676+
) -> Result<(), InputError> {
677+
// Get the configuration from the composite device
678+
let config = composite_device.blocking_get_config()?;
679+
680+
// Set the product id based on the composite device configuration
681+
let pid = match config.name.as_str() {
682+
"Lenovo Legion Go" => PID,
683+
"ASUS ROG Ally" => PID,
684+
"ASUS ROG Ally X" => PID,
685+
_ => PID,
686+
};
687+
688+
// TODO:
689+
// Get the device node paths to the source devices to query for additional
690+
// information like serial number, etc.
691+
//let source_device_paths = composite_device.blocking_get_source_device_paths()?;
692+
//let mut source_device_info = Vec::with_capacity(source_device_paths.len());
693+
//for source_path in source_device_paths {
694+
// let path = Path::new(source_path.as_str());
695+
// let name = path.file_name().unwrap_or_default();
696+
// let base_path = path.parent().unwrap().as_os_str();
697+
// let device =
698+
// UdevDevice::from_devnode(base_path.to_str().unwrap(), name.to_str().unwrap());
699+
// source_device_info.push(device);
700+
//}
701+
702+
// Create and start the virtual USB device
703+
let mut device = SteamDeckDevice::create_virtual_device(pid)?;
704+
device.start()?;
705+
self.device = Some(device);
706+
707+
Ok(())
708+
}
709+
676710
fn write_event(&mut self, event: NativeEvent) -> Result<(), InputError> {
677711
log::trace!("Received event: {event:?}");
678712

@@ -768,18 +802,28 @@ impl TargetInputDevice for SteamDeckDevice {
768802
/// Stop the virtual USB read/write threads
769803
fn stop(&mut self) -> Result<(), InputError> {
770804
log::debug!("Stopping virtual Deck controller");
771-
self.device.stop();
772-
773-
// Read from the device
774-
let xfer = self.device.blocking_read()?;
805+
let xfer = {
806+
let Some(device) = self.device.as_mut() else {
807+
log::trace!("Deck controller was never started");
808+
return Ok(());
809+
};
810+
device.stop();
811+
812+
// Read from the device
813+
device.blocking_read()?
814+
};
775815

776816
// Handle any non-standard transfers
777817
if let Some(xfer) = xfer {
778818
let reply = self.handle_xfer(xfer);
779819

780820
// Write to the device if a reply is necessary
781821
if let Some(reply) = reply {
782-
self.device.write(reply)?;
822+
let Some(device) = self.device.as_mut() else {
823+
log::trace!("Deck controller was never started");
824+
return Ok(());
825+
};
826+
device.write(reply)?;
783827
}
784828
}
785829

@@ -797,15 +841,25 @@ impl TargetOutputDevice for SteamDeckDevice {
797841
self.state.frame = Integer::from_primitive(frame.wrapping_add(1));
798842

799843
// Read from the device
800-
let xfer = self.device.blocking_read()?;
844+
let xfer = {
845+
let Some(device) = self.device.as_mut() else {
846+
log::trace!("Device not started yet");
847+
return Ok(vec![]);
848+
};
849+
device.blocking_read()?
850+
};
801851

802852
// Handle any non-standard transfers
803853
if let Some(xfer) = xfer {
804854
let reply = self.handle_xfer(xfer);
805855

806856
// Write to the device if a reply is necessary
807857
if let Some(reply) = reply {
808-
self.device.write(reply)?;
858+
let Some(device) = self.device.as_mut() else {
859+
log::trace!("Device not started yet");
860+
return Ok(vec![]);
861+
};
862+
device.write(reply)?;
809863
}
810864
}
811865

0 commit comments

Comments
 (0)