diff --git a/examples/descriptors.rs b/examples/descriptors.rs index 099578b..3955170 100644 --- a/examples/descriptors.rs +++ b/examples/descriptors.rs @@ -20,11 +20,16 @@ fn inspect_device(dev: DeviceInfo) { let dev = match dev.open() { Ok(dev) => dev, Err(e) => { - println!("\tFailed to open device: {}", e); + println!(" Failed to open device: {}", e); return; } }; + match dev.active_configuration() { + Ok(config) => println!(" Active configuration is {}", config.configuration_value()), + Err(e) => println!("Unknown active configuration: {e}"), + } + for config in dev.configurations() { println!(" Configuration {}", config.configuration_value()); for intf in config.interfaces() { diff --git a/src/device.rs b/src/device.rs index 45d4659..1d4873d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::{ - descriptors::Configuration, + descriptors::{ActiveConfigurationError, Configuration}, platform, transfer::{ControlIn, ControlOut, EndpointType, Queue, RequestBuffer, TransferFuture}, DeviceInfo, Error, @@ -44,7 +44,18 @@ impl Device { Ok(Interface { backend }) } - /// Iterate over the configurations of the device. + /// Get information about the active configuration. + pub fn active_configuration(&self) -> Result { + let active = self.backend.active_configuration_value(); + + self.configurations() + .find(|c| c.configuration_value() == active) + .ok_or_else(|| ActiveConfigurationError { + configuration_value: active, + }) + } + + /// Iterate over all configurations of the device. pub fn configurations(&self) -> impl Iterator { self.backend .configuration_descriptors() @@ -53,6 +64,10 @@ impl Device { /// Set the device configuration. /// + /// The argument is the desired configuration's `bConfigurationValue` + /// descriptor field from [`Configuration::configuration_value`] or `0` to + /// unconfigure the device. + /// /// ### Platform-specific notes /// * Not supported on Windows pub fn set_configuration(&self, configuration: u8) -> Result<(), Error> { diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 925bbac..9be49a6 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -1,4 +1,13 @@ -use std::{fs::File, io::Read, mem::ManuallyDrop, path::PathBuf, sync::Arc}; +use std::{ + fs::File, + io::Read, + mem::ManuallyDrop, + path::PathBuf, + sync::{ + atomic::{AtomicU8, Ordering}, + Arc, + }, +}; use log::{debug, error}; use rustix::{ @@ -10,6 +19,7 @@ use rustix::{ use super::{ events, usbfs::{self, Urb}, + SysfsPath, }; use crate::{ descriptors::{parse_concatenated_config_descriptors, DESCRIPTOR_LEN_DEVICE}, @@ -23,14 +33,17 @@ pub(crate) struct LinuxDevice { /// Read from the fd, consists of device descriptor followed by configuration descriptors descriptors: Vec, + + sysfs: Option, + active_config: AtomicU8, } impl LinuxDevice { pub(crate) fn from_device_info(d: &DeviceInfo) -> Result, Error> { - Self::open(d.bus_number(), d.device_address()) - } + let busnum = d.bus_number(); + let devnum = d.device_address(); + let active_config = d.path.read_attr("bConfigurationValue")?; - pub(crate) fn open(busnum: u8, devnum: u8) -> Result, Error> { let path = PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}")); debug!("Opening usbfs device {}", path.display()); let fd = rustix::fs::open(path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty())?; @@ -52,6 +65,8 @@ impl LinuxDevice { fd, events_id, descriptors, + sysfs: Some(d.path.clone()), + active_config: AtomicU8::new(active_config), } }); @@ -103,8 +118,24 @@ impl LinuxDevice { parse_concatenated_config_descriptors(&self.descriptors[DESCRIPTOR_LEN_DEVICE as usize..]) } + pub(crate) fn active_configuration_value(&self) -> u8 { + if let Some(sysfs) = self.sysfs.as_ref() { + match sysfs.read_attr("bConfigurationValue") { + Ok(v) => { + self.active_config.store(v, Ordering::SeqCst); + return v; + } + Err(e) => { + error!("Failed to read sysfs bConfigurationValue: {e}, using cached value"); + } + } + } + self.active_config.load(Ordering::SeqCst) + } + pub(crate) fn set_configuration(&self, configuration: u8) -> Result<(), Error> { usbfs::set_configuration(&self.fd, configuration)?; + self.active_config.store(configuration, Ordering::SeqCst); Ok(()) } diff --git a/src/platform/linux_usbfs/enumeration.rs b/src/platform/linux_usbfs/enumeration.rs index dfc4dbf..7244bad 100644 --- a/src/platform/linux_usbfs/enumeration.rs +++ b/src/platform/linux_usbfs/enumeration.rs @@ -14,7 +14,7 @@ use crate::Speed; pub struct SysfsPath(PathBuf); impl SysfsPath { - fn read_attr(&self, attr: &str) -> Result + pub(crate) fn read_attr(&self, attr: &str) -> Result where T: FromStr, T::Err: std::error::Error + Send + Sync + 'static, diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index df5f12b..feb719d 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -33,6 +33,10 @@ impl MacDevice { })) } + pub(crate) fn active_configuration_value(&self) -> u8 { + 1 + } + pub(crate) fn configuration_descriptors(&self) -> impl Iterator { let num_configs = self.device.get_number_of_configurations().unwrap_or(0); (0..num_configs).flat_map(|i| self.device.get_configuration_descriptor(i).ok()) diff --git a/src/platform/windows_winusb/device.rs b/src/platform/windows_winusb/device.rs index cd2ed10..2a36f20 100644 --- a/src/platform/windows_winusb/device.rs +++ b/src/platform/windows_winusb/device.rs @@ -80,6 +80,10 @@ impl WindowsDevice { })) } + pub(crate) fn active_configuration_value(&self) -> u8 { + 1 + } + pub(crate) fn configuration_descriptors(&self) -> impl Iterator { self.config_descriptors.iter().map(|d| &d[..]) }