From 5262bad233caf9a5c37cdad3f0f7046bf3ae5229 Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Sat, 26 Jul 2025 13:52:05 -0600 Subject: [PATCH] Disable DeviceInfo fields and BusInfo for future compatibility with Android and WebUSB backends --- src/enumeration.rs | 75 ++++++++++++++++++------------ src/lib.rs | 12 +++-- src/platform/linux_usbfs/device.rs | 65 +++++++++++++++++++------- src/platform/linux_usbfs/mod.rs | 15 ++++-- 4 files changed, 109 insertions(+), 58 deletions(-) diff --git a/src/enumeration.rs b/src/enumeration.rs index a7e0f50..ab5cda8 100644 --- a/src/enumeration.rs +++ b/src/enumeration.rs @@ -1,7 +1,7 @@ #[cfg(target_os = "windows")] use std::ffi::{OsStr, OsString}; -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "linux"))] use crate::platform::SysfsPath; use crate::{Device, Error, MaybeFuture}; @@ -22,7 +22,7 @@ pub struct DeviceId(pub(crate) crate::platform::DeviceId); /// * macOS: `registry_id`, `location_id` #[derive(Clone)] pub struct DeviceInfo { - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(target_os = "linux")] pub(crate) path: SysfsPath, #[cfg(any(target_os = "linux", target_os = "android"))] @@ -52,12 +52,24 @@ pub struct DeviceInfo { #[cfg(target_os = "macos")] pub(crate) location_id: u32, + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub(crate) bus_id: String, + + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "windows", + target_os = "android" + ))] pub(crate) device_address: u8, + + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub(crate) port_chain: Vec, pub(crate) vendor_id: u16, pub(crate) product_id: u16, + + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub(crate) device_version: u16, pub(crate) usb_version: u16, @@ -96,14 +108,6 @@ impl DeviceInfo { } } - /// *(Linux-only)* Sysfs path for the device. - #[doc(hidden)] - #[deprecated = "use `sysfs_path()` instead"] - #[cfg(target_os = "linux")] - pub fn path(&self) -> &SysfsPath { - &self.path - } - /// *(Linux-only)* Sysfs path for the device. #[cfg(target_os = "linux")] pub fn sysfs_path(&self) -> &std::path::Path { @@ -113,7 +117,7 @@ impl DeviceInfo { /// *(Linux-only)* Bus number. /// /// On Linux, the `bus_id` is an integer and this provides the value as `u8`. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] pub fn busnum(&self) -> u8 { self.busnum } @@ -149,6 +153,7 @@ impl DeviceInfo { /// /// Since USB SuperSpeed is a separate topology from USB 2.0 speeds, a /// physical port may be identified differently depending on speed. + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub fn port_chain(&self) -> &[u8] { &self.port_chain } @@ -172,11 +177,13 @@ impl DeviceInfo { } /// Identifier for the bus / host controller where the device is connected. + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub fn bus_id(&self) -> &str { &self.bus_id } /// Number identifying the device within the bus. + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub fn device_address(&self) -> u8 { self.device_address } @@ -195,6 +202,7 @@ impl DeviceInfo { /// The device version, normally encoded as BCD, from the `bcdDevice` device descriptor field. #[doc(alias = "bcdDevice")] + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub fn device_version(&self) -> u16 { self.device_version } @@ -227,6 +235,7 @@ impl DeviceInfo { } /// Connection speed + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub fn speed(&self) -> Option { self.speed } @@ -286,16 +295,21 @@ impl std::fmt::Debug for DeviceInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut s = f.debug_struct("DeviceInfo"); + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] s.field("bus_id", &self.bus_id) .field("device_address", &self.device_address) - .field("port_chain", &format_args!("{:?}", self.port_chain)) - .field("vendor_id", &format_args!("0x{:04X}", self.vendor_id)) - .field("product_id", &format_args!("0x{:04X}", self.product_id)) - .field( - "device_version", - &format_args!("0x{:04X}", self.device_version), - ) - .field("usb_version", &format_args!("0x{:04X}", self.usb_version)) + .field("port_chain", &format_args!("{:?}", self.port_chain)); + + s.field("vendor_id", &format_args!("0x{:04X}", self.vendor_id)) + .field("product_id", &format_args!("0x{:04X}", self.product_id)); + + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] + s.field( + "device_version", + &format_args!("0x{:04X}", self.device_version), + ); + + s.field("usb_version", &format_args!("0x{:04X}", self.usb_version)) .field("class", &format_args!("0x{:02X}", self.class)) .field("subclass", &format_args!("0x{:02X}", self.subclass)) .field("protocol", &format_args!("0x{:02X}", self.protocol)) @@ -308,10 +322,6 @@ impl std::fmt::Debug for DeviceInfo { { s.field("sysfs_path", &self.path); } - #[cfg(any(target_os = "linux", target_os = "android"))] - { - s.field("busnum", &self.busnum); - } #[cfg(target_os = "windows")] { @@ -466,15 +476,16 @@ impl UsbControllerType { /// * Linux: `path`, `parent_path`, `busnum`, `root_hub` /// * Windows: `instance_id`, `parent_instance_id`, `location_paths`, `devinst`, `root_hub_description` /// * macOS: `registry_id`, `location_id`, `name`, `provider_class_name`, `class_name` +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub struct BusInfo { - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] pub(crate) path: SysfsPath, /// The phony root hub device - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] pub(crate) root_hub: DeviceInfo, - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] pub(crate) busnum: u8, #[cfg(target_os = "windows")] @@ -516,9 +527,10 @@ pub struct BusInfo { pub(crate) controller_type: Option, } +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] impl BusInfo { /// *(Linux-only)* Sysfs path for the bus. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] pub fn sysfs_path(&self) -> &std::path::Path { &self.path.0 } @@ -526,13 +538,13 @@ impl BusInfo { /// *(Linux-only)* Bus number. /// /// On Linux, the `bus_id` is an integer and this provides the value as `u8`. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] pub fn busnum(&self) -> u8 { self.busnum } /// *(Linux-only)* The root hub [`DeviceInfo`] representing the bus. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] pub fn root_hub(&self) -> &DeviceInfo { &self.root_hub } @@ -622,7 +634,7 @@ impl BusInfo { /// * macOS: The [IONameMatched](https://developer.apple.com/documentation/bundleresources/information_property_list/ionamematch) key of the IOService entry. /// * Windows: Description field of the root hub device. How the bus will appear in Device Manager. pub fn system_name(&self) -> Option<&str> { - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] { self.root_hub.product_string() } @@ -639,11 +651,12 @@ impl BusInfo { } } +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] impl std::fmt::Debug for BusInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut s = f.debug_struct("BusInfo"); - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux"))] { s.field("sysfs_path", &self.path); s.field("busnum", &self.busnum); diff --git a/src/lib.rs b/src/lib.rs index 9cbfb20..af3a67f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,13 +139,16 @@ mod platform; pub mod descriptors; mod enumeration; -pub use enumeration::{BusInfo, DeviceId, DeviceInfo, InterfaceInfo, Speed, UsbControllerType}; +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] +pub use enumeration::BusInfo; +pub use enumeration::{DeviceId, DeviceInfo, InterfaceInfo, Speed, UsbControllerType}; mod device; pub use device::{Device, Endpoint, Interface}; pub mod transfer; +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub mod hotplug; mod maybe_future; @@ -168,6 +171,7 @@ pub use error::{ActiveConfigurationError, Error, ErrorKind, GetDescriptorError}; /// .find(|dev| dev.vendor_id() == 0xAAAA && dev.product_id() == 0xBBBB) /// .expect("device not connected"); /// ``` +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] pub fn list_devices() -> impl MaybeFuture, Error>> { platform::list_devices() @@ -191,10 +195,7 @@ pub fn list_devices() -> impl MaybeFuture impl MaybeFuture, Error>> { platform::list_buses() } @@ -234,6 +235,7 @@ pub fn list_buses() -> impl MaybeFuture Result { Ok(hotplug::HotplugWatch(platform::HotplugWatch::new()?)) } diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 4360f90..69b05ae 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -4,7 +4,6 @@ use std::{ fs::File, io::{Read, Seek}, mem::ManuallyDrop, - path::PathBuf, sync::{ atomic::{AtomicU8, Ordering}, Arc, Mutex, MutexGuard, Weak, @@ -17,7 +16,7 @@ use log::{debug, error, warn}; use rustix::{ event::epoll::EventFlags, fd::{AsFd, AsRawFd, FromRawFd, OwnedFd}, - fs::{Mode, OFlags, Timespec}, + fs::Timespec, io::Errno, time::{timerfd_create, timerfd_settime, Itimerspec, TimerfdFlags, TimerfdTimerFlags}, }; @@ -26,8 +25,12 @@ use slab::Slab; use super::{ errno_to_transfer_error, events, usbfs::{self, Urb}, - SysfsPath, TransferData, + TransferData, }; + +#[cfg(not(target_os = "android"))] +use super::SysfsPath; + use crate::{ bitset::EndpointBitSet, descriptors::{ @@ -63,7 +66,9 @@ pub(crate) struct LinuxDevice { /// Read from the fd, consists of device descriptor followed by configuration descriptors descriptors: Vec, + #[cfg(not(target_os = "android"))] sysfs: Option, + active_config: AtomicU8, timerfd: OwnedFd, @@ -71,15 +76,18 @@ pub(crate) struct LinuxDevice { } impl LinuxDevice { + #[cfg(not(target_os = "android"))] pub(crate) fn from_device_info( d: &DeviceInfo, ) -> impl MaybeFuture, Error>> { + use rustix::fs::{Mode, OFlags}; + let busnum = d.busnum(); let devnum = d.device_address(); let sysfs_path = d.path.clone(); Blocking::new(move || { - let path = PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}")); + let path = std::path::PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}")); let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty()) .map_err(|e| { match e { @@ -98,18 +106,29 @@ impl LinuxDevice { }) } + #[cfg(target_os = "android")] + pub(crate) fn from_device_info( + _d: &DeviceInfo, + ) -> impl MaybeFuture, Error>> { + Blocking::new(move || unimplemented!()) + } + pub(crate) fn from_fd( fd: OwnedFd, ) -> impl MaybeFuture, Error>> { Blocking::new(move || { debug!("Wrapping fd {} as usbfs device", fd.as_raw_fd()); - Self::create_inner(fd, None) + Self::create_inner( + fd, + #[cfg(not(target_os = "android"))] + None, + ) }) } pub(crate) fn create_inner( fd: OwnedFd, - sysfs: Option, + #[cfg(not(target_os = "android"))] sysfs: Option, ) -> Result, Error> { let descriptors = read_all_from_fd(&fd).map_err(|e| { Error::new_io(ErrorKind::Other, "failed to read descriptors", e).log_error() @@ -119,26 +138,19 @@ impl LinuxDevice { return Err(Error::new(ErrorKind::Other, "invalid device descriptor")); }; + #[cfg(not(target_os = "android"))] let active_config: u8 = if let Some(sysfs) = sysfs.as_ref() { sysfs.read_attr("bConfigurationValue").map_err(|e| { warn!("failed to read sysfs bConfigurationValue: {e}"); Error::new(ErrorKind::Other, "failed to read sysfs bConfigurationValue") })? } else { - request_configuration(&fd).unwrap_or_else(|()| { - let mut config_descriptors = parse_concatenated_config_descriptors( - &descriptors[DESCRIPTOR_LEN_DEVICE as usize..], - ); - - if let Some(config) = config_descriptors.next() { - config.configuration_value() - } else { - error!("no configurations for device fd {}", fd.as_raw_fd()); - 0 - } - }) + guess_active_configuration(&fd, &descriptors) }; + #[cfg(target_os = "android")] + let active_config = guess_active_configuration(&fd, &descriptors); + let timerfd = timerfd_create( rustix::time::TimerfdClockId::Monotonic, TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK, @@ -151,6 +163,7 @@ impl LinuxDevice { fd, events_id, descriptors, + #[cfg(not(target_os = "android"))] sysfs, active_config: AtomicU8::new(active_config), timerfd, @@ -305,6 +318,7 @@ impl LinuxDevice { } pub(crate) fn active_configuration_value(&self) -> u8 { + #[cfg(not(target_os = "android"))] if let Some(sysfs) = self.sysfs.as_ref() { match sysfs.read_attr("bConfigurationValue") { Ok(v) => { @@ -529,6 +543,21 @@ fn read_all_from_fd(fd: &OwnedFd) -> Result, std::io::Error> { Ok(buf) } +/// Try a request to get the active configuration or fall back to a guess. +fn guess_active_configuration(fd: &OwnedFd, descriptors: &[u8]) -> u8 { + request_configuration(fd).unwrap_or_else(|()| { + let mut config_descriptors = + parse_concatenated_config_descriptors(&descriptors[DESCRIPTOR_LEN_DEVICE as usize..]); + + if let Some(config) = config_descriptors.next() { + config.configuration_value() + } else { + error!("no configurations for device fd {}", fd.as_raw_fd()); + 0 + } + }) +} + /// Get the active configuration with a blocking request to the device. fn request_configuration(fd: &OwnedFd) -> Result { const REQUEST_GET_CONFIGURATION: u8 = 0x08; diff --git a/src/platform/linux_usbfs/mod.rs b/src/platform/linux_usbfs/mod.rs index 20be16f..2e83c94 100644 --- a/src/platform/linux_usbfs/mod.rs +++ b/src/platform/linux_usbfs/mod.rs @@ -6,18 +6,25 @@ use rustix::io::Errno; pub(crate) use transfer::TransferData; mod usbfs; +#[cfg(not(target_os = "android"))] mod enumeration; -mod events; + +#[cfg(not(target_os = "android"))] pub use enumeration::{list_buses, list_devices, SysfsPath}; +#[cfg(not(target_os = "android"))] +mod hotplug; + +#[cfg(not(target_os = "android"))] +pub(crate) use hotplug::LinuxHotplugWatch as HotplugWatch; + +mod events; + mod device; pub(crate) use device::LinuxDevice as Device; pub(crate) use device::LinuxEndpoint as Endpoint; pub(crate) use device::LinuxInterface as Interface; -mod hotplug; -pub(crate) use hotplug::LinuxHotplugWatch as HotplugWatch; - use crate::transfer::TransferError; use crate::ErrorKind;