Disable DeviceInfo fields and BusInfo for future compatibility with Android and WebUSB backends
This commit is contained in:
parent
84616c749e
commit
5262bad233
4 changed files with 109 additions and 58 deletions
|
|
@ -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<u8>,
|
||||
|
||||
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<Speed> {
|
||||
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<UsbControllerType>,
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
|
|
|||
12
src/lib.rs
12
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<Output = Result<impl Iterator<Item = DeviceInfo>, Error>>
|
||||
{
|
||||
platform::list_devices()
|
||||
|
|
@ -191,10 +195,7 @@ pub fn list_devices() -> impl MaybeFuture<Output = Result<impl Iterator<Item = D
|
|||
/// })
|
||||
/// .collect();
|
||||
/// ```
|
||||
///
|
||||
/// ### Platform-specific notes
|
||||
/// * On Linux, the abstraction of the "bus" is a phony device known as the root hub. This device is available at bus.root_hub()
|
||||
/// * On Android, this will only work on rooted devices due to sysfs path usage
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
|
||||
pub fn list_buses() -> impl MaybeFuture<Output = Result<impl Iterator<Item = BusInfo>, Error>> {
|
||||
platform::list_buses()
|
||||
}
|
||||
|
|
@ -234,6 +235,7 @@ pub fn list_buses() -> impl MaybeFuture<Output = Result<impl Iterator<Item = Bus
|
|||
/// when the `Connected` event is emitted. If you are immediately opening the device
|
||||
/// and claiming an interface when receiving a `Connected` event,
|
||||
/// you should retry after a short delay if opening or claiming fails.
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
|
||||
pub fn watch_devices() -> Result<hotplug::HotplugWatch, Error> {
|
||||
Ok(hotplug::HotplugWatch(platform::HotplugWatch::new()?))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<u8>,
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
sysfs: Option<SysfsPath>,
|
||||
|
||||
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<Output = Result<Arc<LinuxDevice>, 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<Output = Result<Arc<LinuxDevice>, Error>> {
|
||||
Blocking::new(move || unimplemented!())
|
||||
}
|
||||
|
||||
pub(crate) fn from_fd(
|
||||
fd: OwnedFd,
|
||||
) -> impl MaybeFuture<Output = Result<Arc<LinuxDevice>, 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<SysfsPath>,
|
||||
#[cfg(not(target_os = "android"))] sysfs: Option<SysfsPath>,
|
||||
) -> Result<Arc<LinuxDevice>, 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<Vec<u8>, 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<u8, ()> {
|
||||
const REQUEST_GET_CONFIGURATION: u8 = 0x08;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue