From 94e8a71964589536cca54beb6202f877b4d7608f Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Thu, 5 Sep 2024 06:40:02 +0000 Subject: [PATCH 01/11] add cfgs --- Cargo.toml | 2 +- src/device.rs | 12 ++++++------ src/enumeration.rs | 16 ++++++++-------- src/platform/linux_usbfs/usbfs.rs | 4 ++-- src/platform/mod.rs | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8195261..1ae34fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ slab = "0.4.9" env_logger = "0.10.0" futures-lite = "1.13.0" -[target.'cfg(target_os="linux")'.dependencies] +[target.'cfg(any(target_os="linux", target_os="android"))'.dependencies] rustix = { version = "0.38.17", features = ["fs", "event", "net"] } libc = "0.2.155" diff --git a/src/device.rs b/src/device.rs index 2f9198a..af47ea8 100644 --- a/src/device.rs +++ b/src/device.rs @@ -68,7 +68,7 @@ impl Device { /// This function can only detach kernel drivers on Linux. Calling on other platforms has /// no effect. pub fn detach_kernel_driver(&self, interface: u8) -> Result<(), Error> { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] self.backend.detach_kernel_driver(interface)?; let _ = interface; @@ -81,7 +81,7 @@ impl Device { /// This function can only attach kernel drivers on Linux. Calling on other platforms has /// no effect. pub fn attach_kernel_driver(&self, interface: u8) -> Result<(), Error> { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] self.backend.attach_kernel_driver(interface)?; let _ = interface; @@ -242,7 +242,7 @@ impl Device { /// and use the interface handle to submit transfers. /// * On Linux, this takes a device-wide lock, so if you have multiple threads, you /// are better off using the async methods. - #[cfg(any(target_os = "linux", target_os = "macos"))] + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] pub fn control_in_blocking( &self, control: Control, @@ -260,7 +260,7 @@ impl Device { /// and use the interface handle to submit transfers. /// * On Linux, this takes a device-wide lock, so if you have multiple threads, you /// are better off using the async methods. - #[cfg(any(target_os = "linux", target_os = "macos"))] + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] pub fn control_out_blocking( &self, control: Control, @@ -296,7 +296,7 @@ impl Device { /// /// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`] /// and use the interface handle to submit transfers. - #[cfg(any(target_os = "linux", target_os = "macos"))] + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] pub fn control_in(&self, data: ControlIn) -> TransferFuture { let mut t = self.backend.make_control_transfer(); t.submit::(data); @@ -329,7 +329,7 @@ impl Device { /// /// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`] /// and use the interface handle to submit transfers. - #[cfg(any(target_os = "linux", target_os = "macos"))] + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] pub fn control_out(&self, data: ControlOut) -> TransferFuture { let mut t = self.backend.make_control_transfer(); t.submit::(data); diff --git a/src/enumeration.rs b/src/enumeration.rs index 996094f..7f9a0ad 100644 --- a/src/enumeration.rs +++ b/src/enumeration.rs @@ -1,7 +1,7 @@ #[cfg(target_os = "windows")] use std::ffi::{OsStr, OsString}; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] use crate::platform::SysfsPath; use crate::{Device, Error}; @@ -22,10 +22,10 @@ pub struct DeviceId(pub(crate) crate::platform::DeviceId); /// * macOS: `registry_id`, `location_id` #[derive(Clone)] pub struct DeviceInfo { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] pub(crate) path: SysfsPath, - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] pub(crate) busnum: u8, #[cfg(target_os = "windows")] @@ -83,7 +83,7 @@ impl DeviceInfo { DeviceId(self.devinst) } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { DeviceId(crate::platform::DeviceId { bus: self.busnum, @@ -100,13 +100,13 @@ impl DeviceInfo { /// *(Linux-only)* Sysfs path for the device. #[doc(hidden)] #[deprecated = "use `sysfs_path()` instead"] - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] pub fn path(&self) -> &SysfsPath { &self.path } /// *(Linux-only)* Sysfs path for the device. - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] pub fn sysfs_path(&self) -> &std::path::Path { &self.path.0 } @@ -114,7 +114,7 @@ impl DeviceInfo { /// *(Linux-only)* Bus number. /// /// On Linux, the `bus_id` is an integer and this provides the value as `u8`. - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] pub fn busnum(&self) -> u8 { self.busnum } @@ -311,7 +311,7 @@ impl std::fmt::Debug for DeviceInfo { .field("product_string", &self.product_string) .field("serial_number", &self.serial_number); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "android"))] { s.field("sysfs_path", &self.path); s.field("busnum", &self.busnum); diff --git a/src/platform/linux_usbfs/usbfs.rs b/src/platform/linux_usbfs/usbfs.rs index 166bd54..47ee77b 100644 --- a/src/platform/linux_usbfs/usbfs.rs +++ b/src/platform/linux_usbfs/usbfs.rs @@ -95,7 +95,7 @@ mod opcodes { pub fn detach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { let command = UsbFsIoctl { interface: interface.into(), - ioctl_code: opcodes::nested::USBDEVFS_DISCONNECT::OPCODE.raw(), + ioctl_code: opcodes::nested::USBDEVFS_DISCONNECT::OPCODE.raw() as _, data: std::ptr::null_mut(), }; unsafe { @@ -107,7 +107,7 @@ pub fn detach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { pub fn attach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { let command = UsbFsIoctl { interface: interface.into(), - ioctl_code: opcodes::nested::USBDEVFS_CONNECT::OPCODE.raw(), + ioctl_code: opcodes::nested::USBDEVFS_CONNECT::OPCODE.raw() as _, data: std::ptr::null_mut(), }; unsafe { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 53a2f21..83ef39d 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,7 +1,7 @@ -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] mod linux_usbfs; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] pub use linux_usbfs::*; #[cfg(target_os = "windows")] From f745d3b074379fbbfdb652613dd447aaa38910f7 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Thu, 5 Sep 2024 07:12:55 +0000 Subject: [PATCH 02/11] android warp approach --- src/platform/linux_usbfs/device.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index dc130b9..49563dc 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -48,12 +48,36 @@ impl LinuxDevice { pub(crate) fn from_device_info(d: &DeviceInfo) -> Result, Error> { let busnum = d.busnum(); let devnum = d.device_address(); + // Can we also use this method to read devnum and busnum? + // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L626-L631 + // + // Yes I think it is the same! + // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L584-L585 + // + // + // But how do we get the path (as in d.path)? + // + // Looks like /proc/self/fd/ is always a simlink to something with the busnum! + // Its then possible to parse `/dev/bus/usb/%hhu/%hhu` into busnum and devnum + // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L616-L617 let active_config = d.path.read_attr("bConfigurationValue")?; let path = PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}")); let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty()) .inspect_err(|e| warn!("Failed to open device {path:?}: {e}"))?; + Self::from_fd(fd, d.path.clone(), active_config, busnum, devnum) + } + + // On android, we have the fd, but are missing active config, busnum, devnum, and sysfs path + // See libusb's linux_get_device_address. Used by wrap sys device on android + pub(crate) fn from_fd( + fd: OwnedFd, + sysfs: SysfsPath, + active_config: u8, + busnum: u8, + devnum: u8, + ) -> Result, Error> { let descriptors = { let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) }; let mut buf = Vec::new(); @@ -75,7 +99,7 @@ impl LinuxDevice { fd, events_id, descriptors, - sysfs: Some(d.path.clone()), + sysfs: Some(sysfs), active_config: AtomicU8::new(active_config), } }); From 6b5f2deb42b333c8dae4eb83942f15d4ed5446bd Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Thu, 5 Sep 2024 15:32:56 +0000 Subject: [PATCH 03/11] debugging hackrf on android --- src/device.rs | 15 +++++++++--- src/platform/linux_usbfs/device.rs | 39 +++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/device.rs b/src/device.rs index af47ea8..6eefb47 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,7 +1,3 @@ -use std::{io::ErrorKind, sync::Arc, time::Duration}; - -use log::error; - use crate::{ descriptors::{ decode_string_descriptor, validate_string_descriptor, ActiveConfigurationError, @@ -14,6 +10,9 @@ use crate::{ }, DeviceInfo, Error, }; +use log::error; +use rustix::fd::OwnedFd; +use std::{io::ErrorKind, sync::Arc, time::Duration}; /// An opened USB device. /// @@ -46,6 +45,14 @@ impl Device { Ok(Device { backend }) } + /// Wraps a device that is already open. + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn from_fd(fd: OwnedFd) -> Result { + Ok(Device { + backend: platform::Device::from_fd(fd)?, + }) + } + /// Open an interface of the device and claim it for exclusive use. pub fn claim_interface(&self, interface: u8) -> Result { let backend = self.backend.claim_interface(interface)?; diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 49563dc..c30c483 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -1,3 +1,6 @@ +use std::ffi::OsString; +use std::io::ErrorKind; +use std::os::unix::ffi::OsStringExt; use std::{ffi::c_void, time::Duration}; use std::{ fs::File, @@ -13,6 +16,7 @@ use std::{ use log::{debug, error, warn}; use rustix::event::epoll; use rustix::fd::AsFd; +use rustix::path::Arg; use rustix::{ fd::{AsRawFd, FromRawFd, OwnedFd}, fs::{Mode, OFlags}, @@ -54,7 +58,7 @@ impl LinuxDevice { // Yes I think it is the same! // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L584-L585 // - // + // // But how do we get the path (as in d.path)? // // Looks like /proc/self/fd/ is always a simlink to something with the busnum! @@ -66,18 +70,46 @@ impl LinuxDevice { let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty()) .inspect_err(|e| warn!("Failed to open device {path:?}: {e}"))?; - Self::from_fd(fd, d.path.clone(), active_config, busnum, devnum) + Self::create_inner(fd, d.path.clone(), active_config, busnum, devnum) + } + + pub(crate) fn from_fd(fd: OwnedFd) -> Result, Error> { + log::info!("from_fd called with: {}", fd.as_raw_fd()); + + // Fails an android here with: No such file or directory + let proc_path = PathBuf::from(format!("/proc/self/fd/{}", fd.as_raw_fd())); + log::info!("reading link: {}", proc_path.to_string_lossy()); + + let fd_path = rustix::fs::readlink(&proc_path, vec![]).expect("failed to read fd link"); + if fd_path.is_empty() { + return Err(ErrorKind::Other.into()); + } + log::info!("sysfs path: {:?}", fd_path.to_string_lossy()); + let sysfs = SysfsPath(PathBuf::from(OsString::from_vec(fd_path.into_bytes()))); + // TODO: should we parse from path or just read directly? + + let active_config = sysfs.read_attr("bConfigurationValue")?; + log::info!("read active_config: {active_config:?}"); + + let busnum = sysfs.read_attr("denvum")?; + log::info!("read busnum: {busnum:?}"); + + let devnum = sysfs.read_attr("busnum")?; + log::info!("read devnum: {busnum:?}"); + + Self::create_inner(fd, sysfs, active_config, busnum, devnum) } // On android, we have the fd, but are missing active config, busnum, devnum, and sysfs path // See libusb's linux_get_device_address. Used by wrap sys device on android - pub(crate) fn from_fd( + pub(crate) fn create_inner( fd: OwnedFd, sysfs: SysfsPath, active_config: u8, busnum: u8, devnum: u8, ) -> Result, Error> { + log::info!("reading descriptors"); let descriptors = { let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) }; let mut buf = Vec::new(); @@ -103,6 +135,7 @@ impl LinuxDevice { active_config: AtomicU8::new(active_config), } }); + log::info!("Created LinuxDevice struct!"); if let Some(err) = events_err { error!("Failed to initialize event loop: {err}"); From 76fc011d1c3ff05e36e1aa83d649fd1355e2b82b Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Sat, 7 Sep 2024 12:49:05 -0700 Subject: [PATCH 04/11] wip --- Cargo.toml | 1 + src/platform/linux_usbfs/device.rs | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ae34fd..0e65268 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ rust-version = "1.76" # keep in sync with .github/workflows/rust.yml [dependencies] atomic-waker = "1.1.2" futures-core = "0.3.29" +libc = "0.2.155" log = "0.4.20" once_cell = "1.18.0" slab = "0.4.9" diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index c30c483..9d669b3 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -1,4 +1,4 @@ -use std::ffi::OsString; +use std::ffi::{CStr, OsString}; use std::io::ErrorKind; use std::os::unix::ffi::OsStringExt; use std::{ffi::c_void, time::Duration}; @@ -80,12 +80,19 @@ impl LinuxDevice { let proc_path = PathBuf::from(format!("/proc/self/fd/{}", fd.as_raw_fd())); log::info!("reading link: {}", proc_path.to_string_lossy()); - let fd_path = rustix::fs::readlink(&proc_path, vec![]).expect("failed to read fd link"); - if fd_path.is_empty() { + //let mut dst = [0u8; 256]; + //let path = libc::readlink(CStr::from(proc_Path).unwrap(), dst.as_mut_ptr(), dst.len()); + let fd_path = std::fs::read_link(&proc_path).expect("Failed to read link"); + log::info!("sysfs path: {:?}", fd_path.to_string_lossy()); + + //let fd_path = rustix::fs::readlink(&proc_path, vec![]).expect("failed to read fd link"); + if fd_path.as_path().to_str().unwrap().is_empty() { + log::error!("empty path??"); + return Err(ErrorKind::Other.into()); } - log::info!("sysfs path: {:?}", fd_path.to_string_lossy()); - let sysfs = SysfsPath(PathBuf::from(OsString::from_vec(fd_path.into_bytes()))); + + let sysfs = SysfsPath(fd_path); // TODO: should we parse from path or just read directly? let active_config = sysfs.read_attr("bConfigurationValue")?; From d569d530f4f98a854d0c00d25ac8d8a7136cbd58 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Sat, 7 Sep 2024 19:03:17 -0700 Subject: [PATCH 05/11] RADIO ON ANDROID WORKS --- src/device.rs | 3 +- src/platform/linux_usbfs/device.rs | 57 +++++++++++++++++------------- src/platform/mod.rs | 14 ++++---- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/device.rs b/src/device.rs index 6eefb47..a35af50 100644 --- a/src/device.rs +++ b/src/device.rs @@ -11,7 +11,6 @@ use crate::{ DeviceInfo, Error, }; use log::error; -use rustix::fd::OwnedFd; use std::{io::ErrorKind, sync::Arc, time::Duration}; /// An opened USB device. @@ -47,7 +46,7 @@ impl Device { /// Wraps a device that is already open. #[cfg(any(target_os = "android", target_os = "linux"))] - pub fn from_fd(fd: OwnedFd) -> Result { + pub fn from_fd(fd: std::os::fd::OwnedFd) -> Result { Ok(Device { backend: platform::Device::from_fd(fd)?, }) diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 9d669b3..9165b49 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -1,6 +1,4 @@ -use std::ffi::{CStr, OsString}; use std::io::ErrorKind; -use std::os::unix::ffi::OsStringExt; use std::{ffi::c_void, time::Duration}; use std::{ fs::File, @@ -52,6 +50,7 @@ impl LinuxDevice { pub(crate) fn from_device_info(d: &DeviceInfo) -> Result, Error> { let busnum = d.busnum(); let devnum = d.device_address(); + // NOTE: a bit wrong since these files dont exist on android // Can we also use this method to read devnum and busnum? // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L626-L631 // @@ -59,6 +58,7 @@ impl LinuxDevice { // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L584-L585 // // + // NOTE: Yes this path does exist and we can use it to get devnum // But how do we get the path (as in d.path)? // // Looks like /proc/self/fd/ is always a simlink to something with the busnum! @@ -70,48 +70,57 @@ impl LinuxDevice { let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty()) .inspect_err(|e| warn!("Failed to open device {path:?}: {e}"))?; - Self::create_inner(fd, d.path.clone(), active_config, busnum, devnum) + Self::create_inner(fd, Some(d.path.clone()), active_config, busnum, devnum) } pub(crate) fn from_fd(fd: OwnedFd) -> Result, Error> { log::info!("from_fd called with: {}", fd.as_raw_fd()); - // Fails an android here with: No such file or directory + // NOTE: must be called from the same thread that gave us the FD on android, otherwise this fails let proc_path = PathBuf::from(format!("/proc/self/fd/{}", fd.as_raw_fd())); log::info!("reading link: {}", proc_path.to_string_lossy()); - //let mut dst = [0u8; 256]; - //let path = libc::readlink(CStr::from(proc_Path).unwrap(), dst.as_mut_ptr(), dst.len()); let fd_path = std::fs::read_link(&proc_path).expect("Failed to read link"); log::info!("sysfs path: {:?}", fd_path.to_string_lossy()); - //let fd_path = rustix::fs::readlink(&proc_path, vec![]).expect("failed to read fd link"); - if fd_path.as_path().to_str().unwrap().is_empty() { - log::error!("empty path??"); - + let prefix = "/dev/bus/usb/"; + let Ok(dev_num_bus_num) = fd_path.strip_prefix(prefix) else { + log::error!( + "Usb device path does not start with required prefix `{prefix}`: {}", + fd_path.to_string_lossy() + ); return Err(ErrorKind::Other.into()); - } - - let sysfs = SysfsPath(fd_path); - // TODO: should we parse from path or just read directly? - - let active_config = sysfs.read_attr("bConfigurationValue")?; - log::info!("read active_config: {active_config:?}"); + }; + let Some(dev_num_bus_num) = dev_num_bus_num.to_str() else { + log::error!("End of usb device path is not UTF-8!: {dev_num_bus_num:?}",); + return Err(ErrorKind::Other.into()); + }; + log::info!("Got dev_num_bus_num: {dev_num_bus_num}"); - let busnum = sysfs.read_attr("denvum")?; - log::info!("read busnum: {busnum:?}"); + let mut split = dev_num_bus_num.split("/"); + let bus_str = split.next().unwrap(); + let dev_str = split.next().unwrap(); - let devnum = sysfs.read_attr("busnum")?; - log::info!("read devnum: {busnum:?}"); + let Ok(busnum) = bus_str.parse::() else { + log::error!("Usb bus number failed to parse: {bus_str}",); + return Err(ErrorKind::Other.into()); + }; + let Ok(devnum) = dev_str.parse::() else { + log::error!("Usb device number failed to parse: {dev_str}",); + return Err(ErrorKind::Other.into()); + }; - Self::create_inner(fd, sysfs, active_config, busnum, devnum) + // TODO: actually read active config + // Looks like we have to send a in ctrl. See: https://github.com/libusb/libusb/blob/master/libusb/os/linux_usbfs.c#L850-L858 + let active_config = 1; + Self::create_inner(fd, None, active_config, busnum, devnum) } // On android, we have the fd, but are missing active config, busnum, devnum, and sysfs path // See libusb's linux_get_device_address. Used by wrap sys device on android pub(crate) fn create_inner( fd: OwnedFd, - sysfs: SysfsPath, + sysfs: Option, active_config: u8, busnum: u8, devnum: u8, @@ -138,7 +147,7 @@ impl LinuxDevice { fd, events_id, descriptors, - sysfs: Some(sysfs), + sysfs, active_config: AtomicU8::new(active_config), } }); diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 83ef39d..955d50e 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,7 +1,7 @@ -#[cfg(any(target_os = "linux", target_os = "android"))] +//#[cfg(any(target_os = "linux", target_os = "android"))] mod linux_usbfs; -#[cfg(any(target_os = "linux", target_os = "android"))] +//#[cfg(any(target_os = "linux", target_os = "android"))] pub use linux_usbfs::*; #[cfg(target_os = "windows")] @@ -10,8 +10,8 @@ mod windows_winusb; #[cfg(target_os = "windows")] pub use windows_winusb::*; -#[cfg(target_os = "macos")] -mod macos_iokit; - -#[cfg(target_os = "macos")] -pub use macos_iokit::*; +// #[cfg(target_os = "macos")] +// mod macos_iokit; +// +// #[cfg(target_os = "macos")] +// pub use macos_iokit::*; From 81d6650e0af400e4c20f0f6aa10d1fa21930abc4 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Sat, 7 Sep 2024 20:54:45 -0700 Subject: [PATCH 06/11] cleanup --- Cargo.toml | 1 - src/device.rs | 4 ++-- src/enumeration.rs | 9 +++++--- src/platform/linux_usbfs/device.rs | 34 +++++++++--------------------- src/platform/linux_usbfs/usbfs.rs | 4 ++-- src/platform/mod.rs | 14 ++++++------ 6 files changed, 27 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e65268..1ae34fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ rust-version = "1.76" # keep in sync with .github/workflows/rust.yml [dependencies] atomic-waker = "1.1.2" futures-core = "0.3.29" -libc = "0.2.155" log = "0.4.20" once_cell = "1.18.0" slab = "0.4.9" diff --git a/src/device.rs b/src/device.rs index a35af50..b3d6227 100644 --- a/src/device.rs +++ b/src/device.rs @@ -74,7 +74,7 @@ impl Device { /// This function can only detach kernel drivers on Linux. Calling on other platforms has /// no effect. pub fn detach_kernel_driver(&self, interface: u8) -> Result<(), Error> { - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(target_os = "linux")] self.backend.detach_kernel_driver(interface)?; let _ = interface; @@ -87,7 +87,7 @@ impl Device { /// This function can only attach kernel drivers on Linux. Calling on other platforms has /// no effect. pub fn attach_kernel_driver(&self, interface: u8) -> Result<(), Error> { - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(target_os = "linux")] self.backend.attach_kernel_driver(interface)?; let _ = interface; diff --git a/src/enumeration.rs b/src/enumeration.rs index 7f9a0ad..f5fecfd 100644 --- a/src/enumeration.rs +++ b/src/enumeration.rs @@ -100,13 +100,13 @@ impl DeviceInfo { /// *(Linux-only)* Sysfs path for the device. #[doc(hidden)] #[deprecated = "use `sysfs_path()` instead"] - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(target_os = "linux")] pub fn path(&self) -> &SysfsPath { &self.path } /// *(Linux-only)* Sysfs path for the device. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(target_os = "linux")] pub fn sysfs_path(&self) -> &std::path::Path { &self.path.0 } @@ -311,9 +311,12 @@ impl std::fmt::Debug for DeviceInfo { .field("product_string", &self.product_string) .field("serial_number", &self.serial_number); - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(target_os = "linux")] { s.field("sysfs_path", &self.path); + } + #[cfg(any(target_os = "linux", target_os = "android"))] + { s.field("busnum", &self.busnum); } diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 9165b49..d01fd76 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -14,7 +14,6 @@ use std::{ use log::{debug, error, warn}; use rustix::event::epoll; use rustix::fd::AsFd; -use rustix::path::Arg; use rustix::{ fd::{AsRawFd, FromRawFd, OwnedFd}, fs::{Mode, OFlags}, @@ -50,20 +49,6 @@ impl LinuxDevice { pub(crate) fn from_device_info(d: &DeviceInfo) -> Result, Error> { let busnum = d.busnum(); let devnum = d.device_address(); - // NOTE: a bit wrong since these files dont exist on android - // Can we also use this method to read devnum and busnum? - // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L626-L631 - // - // Yes I think it is the same! - // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L584-L585 - // - // - // NOTE: Yes this path does exist and we can use it to get devnum - // But how do we get the path (as in d.path)? - // - // Looks like /proc/self/fd/ is always a simlink to something with the busnum! - // Its then possible to parse `/dev/bus/usb/%hhu/%hhu` into busnum and devnum - // https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L616-L617 let active_config = d.path.read_attr("bConfigurationValue")?; let path = PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}")); @@ -74,14 +59,12 @@ impl LinuxDevice { } pub(crate) fn from_fd(fd: OwnedFd) -> Result, Error> { - log::info!("from_fd called with: {}", fd.as_raw_fd()); + log::debug!("Wrapping fd {} as usbfs device", fd.as_raw_fd()); // NOTE: must be called from the same thread that gave us the FD on android, otherwise this fails let proc_path = PathBuf::from(format!("/proc/self/fd/{}", fd.as_raw_fd())); - log::info!("reading link: {}", proc_path.to_string_lossy()); let fd_path = std::fs::read_link(&proc_path).expect("Failed to read link"); - log::info!("sysfs path: {:?}", fd_path.to_string_lossy()); let prefix = "/dev/bus/usb/"; let Ok(dev_num_bus_num) = fd_path.strip_prefix(prefix) else { @@ -95,11 +78,16 @@ impl LinuxDevice { log::error!("End of usb device path is not UTF-8!: {dev_num_bus_num:?}",); return Err(ErrorKind::Other.into()); }; - log::info!("Got dev_num_bus_num: {dev_num_bus_num}"); let mut split = dev_num_bus_num.split("/"); - let bus_str = split.next().unwrap(); - let dev_str = split.next().unwrap(); + let Some(bus_str) = split.next() else { + log::error!("Failed to split usbfs device path: {}", dev_num_bus_num); + return Err(ErrorKind::Other.into()); + }; + let Some(dev_str) = split.next() else { + log::error!("Failed to split usbfs device path: {}", dev_num_bus_num); + return Err(ErrorKind::Other.into()); + }; let Ok(busnum) = bus_str.parse::() else { log::error!("Usb bus number failed to parse: {bus_str}",); @@ -112,12 +100,11 @@ impl LinuxDevice { // TODO: actually read active config // Looks like we have to send a in ctrl. See: https://github.com/libusb/libusb/blob/master/libusb/os/linux_usbfs.c#L850-L858 + // How to handle wait time? Make async? let active_config = 1; Self::create_inner(fd, None, active_config, busnum, devnum) } - // On android, we have the fd, but are missing active config, busnum, devnum, and sysfs path - // See libusb's linux_get_device_address. Used by wrap sys device on android pub(crate) fn create_inner( fd: OwnedFd, sysfs: Option, @@ -151,7 +138,6 @@ impl LinuxDevice { active_config: AtomicU8::new(active_config), } }); - log::info!("Created LinuxDevice struct!"); if let Some(err) = events_err { error!("Failed to initialize event loop: {err}"); diff --git a/src/platform/linux_usbfs/usbfs.rs b/src/platform/linux_usbfs/usbfs.rs index 47ee77b..166bd54 100644 --- a/src/platform/linux_usbfs/usbfs.rs +++ b/src/platform/linux_usbfs/usbfs.rs @@ -95,7 +95,7 @@ mod opcodes { pub fn detach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { let command = UsbFsIoctl { interface: interface.into(), - ioctl_code: opcodes::nested::USBDEVFS_DISCONNECT::OPCODE.raw() as _, + ioctl_code: opcodes::nested::USBDEVFS_DISCONNECT::OPCODE.raw(), data: std::ptr::null_mut(), }; unsafe { @@ -107,7 +107,7 @@ pub fn detach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { pub fn attach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { let command = UsbFsIoctl { interface: interface.into(), - ioctl_code: opcodes::nested::USBDEVFS_CONNECT::OPCODE.raw() as _, + ioctl_code: opcodes::nested::USBDEVFS_CONNECT::OPCODE.raw(), data: std::ptr::null_mut(), }; unsafe { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 955d50e..83ef39d 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,7 +1,7 @@ -//#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] mod linux_usbfs; -//#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] pub use linux_usbfs::*; #[cfg(target_os = "windows")] @@ -10,8 +10,8 @@ mod windows_winusb; #[cfg(target_os = "windows")] pub use windows_winusb::*; -// #[cfg(target_os = "macos")] -// mod macos_iokit; -// -// #[cfg(target_os = "macos")] -// pub use macos_iokit::*; +#[cfg(target_os = "macos")] +mod macos_iokit; + +#[cfg(target_os = "macos")] +pub use macos_iokit::*; From 08c4b8715d2f0134d793e9a98dcdf663c89d4220 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Tue, 10 Sep 2024 20:56:33 -0700 Subject: [PATCH 07/11] try get configuration from ioctl --- src/platform/linux_usbfs/device.rs | 44 +++++++++++++++++++++++++++--- src/platform/linux_usbfs/usbfs.rs | 5 ++-- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index d01fd76..c0c13b3 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -26,6 +26,7 @@ use super::{ SysfsPath, }; use crate::platform::linux_usbfs::events::Watch; +use crate::transfer::{ControlType, Recipient}; use crate::{ descriptors::{parse_concatenated_config_descriptors, DESCRIPTOR_LEN_DEVICE}, transfer::{ @@ -98,10 +99,43 @@ impl LinuxDevice { return Err(ErrorKind::Other.into()); }; - // TODO: actually read active config - // Looks like we have to send a in ctrl. See: https://github.com/libusb/libusb/blob/master/libusb/os/linux_usbfs.c#L850-L858 - // How to handle wait time? Make async? - let active_config = 1; + const LIBUSB_REQUEST_GET_CONFIGURATION: u8 = 0x08; + + let mut dst = [0u8; 1]; + + let control = Control { + control_type: ControlType::Standard, + recipient: Recipient::Device, + request: LIBUSB_REQUEST_GET_CONFIGURATION, + value: 0, + index: 0, + }; + let r = usbfs::control( + &fd, + usbfs::CtrlTransfer { + bRequestType: control.request_type(Direction::In), + bRequest: control.request, + wValue: control.value, + wIndex: control.index, + wLength: dst.len() as u16, + timeout: Duration::from_millis(50) + .as_millis() + .try_into() + .expect("timeout must fit in u32 ms"), + data: &mut dst[0] as *mut u8 as *mut c_void, + }, + ); + + // Could use some more fullproof logic here to support buggy devices + // See: https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L865 + let n = r.map_err(errno_to_transfer_error)?; + if n != dst.len() { + log::error!("Failed to read descriptor"); + return Err(ErrorKind::Other.into()); + } + + let active_config = dst[0]; + log::info!("Got active config: {active_config}"); Self::create_inner(fd, None, active_config, busnum, devnum) } @@ -321,6 +355,7 @@ impl LinuxDevice { })) } + #[cfg(target_os = "linux")] pub(crate) fn detach_kernel_driver( self: &Arc, interface_number: u8, @@ -328,6 +363,7 @@ impl LinuxDevice { usbfs::detach_kernel_driver(&self.fd, interface_number).map_err(|e| e.into()) } + #[cfg(target_os = "linux")] pub(crate) fn attach_kernel_driver( self: &Arc, interface_number: u8, diff --git a/src/platform/linux_usbfs/usbfs.rs b/src/platform/linux_usbfs/usbfs.rs index 166bd54..22725db 100644 --- a/src/platform/linux_usbfs/usbfs.rs +++ b/src/platform/linux_usbfs/usbfs.rs @@ -95,7 +95,8 @@ mod opcodes { pub fn detach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { let command = UsbFsIoctl { interface: interface.into(), - ioctl_code: opcodes::nested::USBDEVFS_DISCONNECT::OPCODE.raw(), + // NOTE: Cast needed since on android this type is i32 vs u32 on linux + ioctl_code: opcodes::nested::USBDEVFS_DISCONNECT::OPCODE.raw() as _, data: std::ptr::null_mut(), }; unsafe { @@ -107,7 +108,7 @@ pub fn detach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { pub fn attach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { let command = UsbFsIoctl { interface: interface.into(), - ioctl_code: opcodes::nested::USBDEVFS_CONNECT::OPCODE.raw(), + ioctl_code: opcodes::nested::USBDEVFS_CONNECT::OPCODE.raw() as _, data: std::ptr::null_mut(), }; unsafe { From 926ca33a99768f42427170f1cafedd315c7022e6 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Wed, 11 Sep 2024 00:21:08 -0700 Subject: [PATCH 08/11] better error handling --- src/platform/linux_usbfs/device.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index c0c13b3..3fe6500 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -65,8 +65,10 @@ impl LinuxDevice { // NOTE: must be called from the same thread that gave us the FD on android, otherwise this fails let proc_path = PathBuf::from(format!("/proc/self/fd/{}", fd.as_raw_fd())); - let fd_path = std::fs::read_link(&proc_path).expect("Failed to read link"); + log::debug!("Reading link: {proc_path:?}"); + let fd_path = std::fs::read_link(&proc_path)?; + // TODO: should we switch to regex or regex-lite for this? let prefix = "/dev/bus/usb/"; let Ok(dev_num_bus_num) = fd_path.strip_prefix(prefix) else { log::error!( @@ -110,6 +112,8 @@ impl LinuxDevice { value: 0, index: 0, }; + + // TODO: what to do about blocking here? let r = usbfs::control( &fd, usbfs::CtrlTransfer { @@ -126,7 +130,7 @@ impl LinuxDevice { }, ); - // Could use some more fullproof logic here to support buggy devices + // TODO: Could add some more logic here to support buggy devices, similar to what libusb does // See: https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L865 let n = r.map_err(errno_to_transfer_error)?; if n != dst.len() { From 0585b543bdc307bfbbfa7b6e0bca709e7992e3e7 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Wed, 11 Sep 2024 08:23:53 -0700 Subject: [PATCH 09/11] cleanup logging --- src/platform/linux_usbfs/device.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 3fe6500..ea35118 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -1,4 +1,4 @@ -use std::io::ErrorKind; +use std::io::{ErrorKind, Seek}; use std::{ffi::c_void, time::Duration}; use std::{ fs::File, @@ -60,44 +60,44 @@ impl LinuxDevice { } pub(crate) fn from_fd(fd: OwnedFd) -> Result, Error> { - log::debug!("Wrapping fd {} as usbfs device", fd.as_raw_fd()); + debug!("Wrapping fd {} as usbfs device", fd.as_raw_fd()); // NOTE: must be called from the same thread that gave us the FD on android, otherwise this fails let proc_path = PathBuf::from(format!("/proc/self/fd/{}", fd.as_raw_fd())); - log::debug!("Reading link: {proc_path:?}"); + debug!("Reading link: {proc_path:?}"); let fd_path = std::fs::read_link(&proc_path)?; // TODO: should we switch to regex or regex-lite for this? let prefix = "/dev/bus/usb/"; let Ok(dev_num_bus_num) = fd_path.strip_prefix(prefix) else { - log::error!( + error!( "Usb device path does not start with required prefix `{prefix}`: {}", fd_path.to_string_lossy() ); return Err(ErrorKind::Other.into()); }; let Some(dev_num_bus_num) = dev_num_bus_num.to_str() else { - log::error!("End of usb device path is not UTF-8!: {dev_num_bus_num:?}",); + error!("End of usb device path is not UTF-8!: {dev_num_bus_num:?}",); return Err(ErrorKind::Other.into()); }; let mut split = dev_num_bus_num.split("/"); let Some(bus_str) = split.next() else { - log::error!("Failed to split usbfs device path: {}", dev_num_bus_num); + error!("Failed to split usbfs device path: {}", dev_num_bus_num); return Err(ErrorKind::Other.into()); }; let Some(dev_str) = split.next() else { - log::error!("Failed to split usbfs device path: {}", dev_num_bus_num); + error!("Failed to split usbfs device path: {}", dev_num_bus_num); return Err(ErrorKind::Other.into()); }; let Ok(busnum) = bus_str.parse::() else { - log::error!("Usb bus number failed to parse: {bus_str}",); + error!("Usb bus number failed to parse: {bus_str}",); return Err(ErrorKind::Other.into()); }; let Ok(devnum) = dev_str.parse::() else { - log::error!("Usb device number failed to parse: {dev_str}",); + error!("Usb device number failed to parse: {dev_str}",); return Err(ErrorKind::Other.into()); }; @@ -134,12 +134,13 @@ impl LinuxDevice { // See: https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L865 let n = r.map_err(errno_to_transfer_error)?; if n != dst.len() { - log::error!("Failed to read descriptor"); + error!("Failed to read descriptor"); return Err(ErrorKind::Other.into()); } let active_config = dst[0]; - log::info!("Got active config: {active_config}"); + debug!("Active config for fd {}: {active_config}", fd.as_raw_fd()); + Self::create_inner(fd, None, active_config, busnum, devnum) } @@ -150,9 +151,10 @@ impl LinuxDevice { busnum: u8, devnum: u8, ) -> Result, Error> { - log::info!("reading descriptors"); let descriptors = { let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) }; + // NOTE: Seek required on android + file.seek(std::io::SeekFrom::Start(0))?; let mut buf = Vec::new(); file.read_to_end(&mut buf)?; buf From 7d556a4cc1366ae6571aa2220f4f7547cdd0ada7 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Thu, 12 Sep 2024 08:47:19 -0700 Subject: [PATCH 10/11] implement feedback --- src/platform/linux_usbfs/device.rs | 177 +++++++++++++++-------------- 1 file changed, 89 insertions(+), 88 deletions(-) diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index ea35118..0db566f 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -25,6 +25,7 @@ use super::{ usbfs::{self, Urb}, SysfsPath, }; +use crate::descriptors::Configuration; use crate::platform::linux_usbfs::events::Watch; use crate::transfer::{ControlType, Recipient}; use crate::{ @@ -56,100 +57,23 @@ impl LinuxDevice { let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty()) .inspect_err(|e| warn!("Failed to open device {path:?}: {e}"))?; - Self::create_inner(fd, Some(d.path.clone()), active_config, busnum, devnum) + let inner = Self::create_inner(fd, Some(d.path.clone()), Some(active_config)); + if inner.is_ok() { + debug!("Opened device bus={busnum} addr={devnum}",); + } + inner } pub(crate) fn from_fd(fd: OwnedFd) -> Result, Error> { debug!("Wrapping fd {} as usbfs device", fd.as_raw_fd()); - // NOTE: must be called from the same thread that gave us the FD on android, otherwise this fails - let proc_path = PathBuf::from(format!("/proc/self/fd/{}", fd.as_raw_fd())); - - debug!("Reading link: {proc_path:?}"); - let fd_path = std::fs::read_link(&proc_path)?; - - // TODO: should we switch to regex or regex-lite for this? - let prefix = "/dev/bus/usb/"; - let Ok(dev_num_bus_num) = fd_path.strip_prefix(prefix) else { - error!( - "Usb device path does not start with required prefix `{prefix}`: {}", - fd_path.to_string_lossy() - ); - return Err(ErrorKind::Other.into()); - }; - let Some(dev_num_bus_num) = dev_num_bus_num.to_str() else { - error!("End of usb device path is not UTF-8!: {dev_num_bus_num:?}",); - return Err(ErrorKind::Other.into()); - }; - - let mut split = dev_num_bus_num.split("/"); - let Some(bus_str) = split.next() else { - error!("Failed to split usbfs device path: {}", dev_num_bus_num); - return Err(ErrorKind::Other.into()); - }; - let Some(dev_str) = split.next() else { - error!("Failed to split usbfs device path: {}", dev_num_bus_num); - return Err(ErrorKind::Other.into()); - }; - - let Ok(busnum) = bus_str.parse::() else { - error!("Usb bus number failed to parse: {bus_str}",); - return Err(ErrorKind::Other.into()); - }; - let Ok(devnum) = dev_str.parse::() else { - error!("Usb device number failed to parse: {dev_str}",); - return Err(ErrorKind::Other.into()); - }; - - const LIBUSB_REQUEST_GET_CONFIGURATION: u8 = 0x08; - - let mut dst = [0u8; 1]; - - let control = Control { - control_type: ControlType::Standard, - recipient: Recipient::Device, - request: LIBUSB_REQUEST_GET_CONFIGURATION, - value: 0, - index: 0, - }; - - // TODO: what to do about blocking here? - let r = usbfs::control( - &fd, - usbfs::CtrlTransfer { - bRequestType: control.request_type(Direction::In), - bRequest: control.request, - wValue: control.value, - wIndex: control.index, - wLength: dst.len() as u16, - timeout: Duration::from_millis(50) - .as_millis() - .try_into() - .expect("timeout must fit in u32 ms"), - data: &mut dst[0] as *mut u8 as *mut c_void, - }, - ); - - // TODO: Could add some more logic here to support buggy devices, similar to what libusb does - // See: https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L865 - let n = r.map_err(errno_to_transfer_error)?; - if n != dst.len() { - error!("Failed to read descriptor"); - return Err(ErrorKind::Other.into()); - } - - let active_config = dst[0]; - debug!("Active config for fd {}: {active_config}", fd.as_raw_fd()); - - Self::create_inner(fd, None, active_config, busnum, devnum) + Self::create_inner(fd, None, None) } pub(crate) fn create_inner( fd: OwnedFd, sysfs: Option, - active_config: u8, - busnum: u8, - devnum: u8, + active_config: Option, ) -> Result, Error> { let descriptors = { let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) }; @@ -160,6 +84,12 @@ impl LinuxDevice { buf }; + let active_config = if let Some(active_config) = active_config { + active_config + } else { + Self::get_config(&descriptors, &fd)? + }; + // because there's no Arc::try_new_cyclic let mut events_err = None; let arc = Arc::new_cyclic(|weak| { @@ -170,6 +100,9 @@ impl LinuxDevice { ); let events_id = *res.as_ref().unwrap_or(&usize::MAX); events_err = res.err(); + if events_err.is_none() { + debug!("Opened device fd={} with id {}", fd.as_raw_fd(), events_id,); + } LinuxDevice { fd, events_id, @@ -183,10 +116,6 @@ impl LinuxDevice { error!("Failed to initialize event loop: {err}"); Err(err) } else { - debug!( - "Opened device bus={busnum} addr={devnum} with id {}", - arc.events_id - ); Ok(arc) } } @@ -404,6 +333,78 @@ impl LinuxDevice { } } } + + fn get_config(descriptors: &[u8], fd: &OwnedFd) -> Result { + const REQUEST_GET_CONFIGURATION: u8 = 0x08; + + let mut dst = [0u8; 1]; + + let control = Control { + control_type: ControlType::Standard, + recipient: Recipient::Device, + request: REQUEST_GET_CONFIGURATION, + value: 0, + index: 0, + }; + + let r = usbfs::control( + &fd, + usbfs::CtrlTransfer { + bRequestType: control.request_type(Direction::In), + bRequest: control.request, + wValue: control.value, + wIndex: control.index, + wLength: dst.len() as u16, + timeout: Duration::from_millis(50) + .as_millis() + .try_into() + .expect("timeout must fit in u32 ms"), + data: &mut dst[0] as *mut u8 as *mut c_void, + }, + ); + + match r { + Ok(n) => { + if n == dst.len() { + let active_config = dst[0]; + debug!("Obtained active configuration for fd {} from GET_CONFIGURATION request: {active_config}", fd.as_raw_fd()); + return Ok(active_config); + } else { + warn!("GET_CONFIGURATION request returned incorrect length: {n}, expected 1",); + } + } + Err(e) => { + warn!( + "GET_CONFIGURATION request failed: {:?}", + errno_to_transfer_error(e) + ); + } + } + + if descriptors.len() < DESCRIPTOR_LEN_DEVICE as usize { + warn!( + "Descriptors for device fd {} too short to use fallback configuration", + fd.as_raw_fd() + ); + return Err(ErrorKind::Other.into()); + } + + // Assume the current configuration is the first one + // See: https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L865 + let mut descriptors = + parse_concatenated_config_descriptors(&descriptors[DESCRIPTOR_LEN_DEVICE as usize..]) + .map(Configuration::new); + + if let Some(config) = descriptors.next() { + return Ok(config.configuration_value()); + } + + error!( + "No available configurations for device fd {}", + fd.as_raw_fd() + ); + return Err(ErrorKind::Other.into()); + } } impl Drop for LinuxDevice { From fd62469b545b4e757940f9fefaeb414929d596da Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Thu, 12 Sep 2024 09:06:29 -0700 Subject: [PATCH 11/11] add ci --- .github/workflows/rust.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8406b5a..66d35b8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -28,7 +28,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.rust }} override: true @@ -36,3 +36,16 @@ jobs: run: cargo build --verbose - name: Run tests run: cargo test --verbose + + build_android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: 'aarch64-linux-android, armv7-linux-androideabi' + - name: build + run: | + cargo build --target aarch64-linux-android --all-features + cargo build --target armv7-linux-androideabi --all-features