diff --git a/examples/detach_claim.rs b/examples/detach_claim.rs new file mode 100644 index 0000000..cb3fa3d --- /dev/null +++ b/examples/detach_claim.rs @@ -0,0 +1,14 @@ +//! Detach the kernel driver for an FTDI device and then reattach it. +use std::{thread::sleep, time::Duration}; +fn main() { + env_logger::init(); + let di = nusb::list_devices() + .unwrap() + .find(|d| d.vendor_id() == 0x0403 && d.product_id() == 0x6010) + .expect("device should be connected"); + + let device = di.open().unwrap(); + let interface = device.detach_and_claim_interface(0).unwrap(); + sleep(Duration::from_secs(1)); + drop(interface); +} diff --git a/src/device.rs b/src/device.rs index 80b1cfa..2e0bb0c 100644 --- a/src/device.rs +++ b/src/device.rs @@ -52,6 +52,16 @@ impl Device { Ok(Interface { backend }) } + /// Detach kernel drivers and open an interface of the device and claim it for exclusive use. + /// + /// ### Platform notes + /// This function can only detach kernel drivers on Linux. Calling on other platforms has + /// the same effect as [`claim_interface`][`Device::claim_interface`]. + pub fn detach_and_claim_interface(&self, interface: u8) -> Result { + let backend = self.backend.detach_and_claim_interface(interface)?; + Ok(Interface { backend }) + } + /// Get information about the active configuration. /// /// This returns cached data and does not perform IO. However, it can fail if the diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 590a49d..c5f78f0 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -230,6 +230,23 @@ impl LinuxDevice { Ok(Arc::new(LinuxInterface { device: self.clone(), interface, + reattach: false, + })) + } + + pub(crate) fn detach_and_claim_interface( + self: &Arc, + interface: u8, + ) -> Result, Error> { + usbfs::detach_and_claim_interface(&self.fd, interface)?; + debug!( + "Detached and claimed interface {interface} on device id {dev}", + dev = self.events_id + ); + Ok(Arc::new(LinuxInterface { + device: self.clone(), + interface, + reattach: true, })) } @@ -272,6 +289,7 @@ impl Drop for LinuxDevice { pub(crate) struct LinuxInterface { pub(crate) interface: u8, pub(crate) device: Arc, + pub(crate) reattach: bool, } impl LinuxInterface { @@ -326,5 +344,13 @@ impl Drop for LinuxInterface { "Released interface {} on device {}: {res:?}", self.interface, self.device.events_id ); + + if res.is_ok() && self.reattach { + let res = usbfs::attach_kernel_driver(&self.device.fd, self.interface); + debug!( + "Reattached kernel drivers for interface {} on device {}: {res:?}", + self.interface, self.device.events_id + ); + } } } diff --git a/src/platform/linux_usbfs/usbfs.rs b/src/platform/linux_usbfs/usbfs.rs index 8588095..8eca168 100644 --- a/src/platform/linux_usbfs/usbfs.rs +++ b/src/platform/linux_usbfs/usbfs.rs @@ -39,6 +39,51 @@ pub fn release_interface(fd: Fd, interface: u8) -> io::Result<()> { } } +#[repr(C)] +struct DetachAndClaim { + interface: c_uint, + flags: c_uint, + driver: [c_uchar; 255 + 1], +} + +pub fn detach_and_claim_interface(fd: Fd, interface: u8) -> io::Result<()> { + const USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER: c_uint = 0x02; + unsafe { + let mut dc = DetachAndClaim { + interface: interface.into(), + flags: USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER, + driver: [0; 256], + }; + + dc.driver[0..6].copy_from_slice(b"usbfs\0"); + + let ctl = + ioctl::Setter::, DetachAndClaim>::new(dc); + + ioctl::ioctl(&fd, ctl) + } +} + +#[repr(C)] +struct UsbFsIoctl { + interface: c_uint, + ioctl_code: c_uint, + data: *mut c_void, +} + +pub fn attach_kernel_driver(fd: Fd, interface: u8) -> io::Result<()> { + unsafe { + let command = UsbFsIoctl { + interface: interface.into(), + ioctl_code: ioctl::NoneOpcode::::OPCODE.raw(), // IOCTL_USBFS_CONNECT + data: std::ptr::null_mut(), + }; + let ctl = + ioctl::Setter::, UsbFsIoctl>::new(command); + ioctl::ioctl(fd, ctl) + } +} + #[repr(C)] struct SetAltSetting { interface: c_int, diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index 8f849c2..a306a7e 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -188,6 +188,13 @@ impl MacDevice { _event_registration, })) } + + pub(crate) fn detach_and_claim_interface( + self: &Arc, + interface: u8, + ) -> Result, Error> { + self.claim_interface(interface) + } } pub(crate) struct MacInterface { diff --git a/src/platform/windows_winusb/device.rs b/src/platform/windows_winusb/device.rs index 61360a5..6d3a0a6 100644 --- a/src/platform/windows_winusb/device.rs +++ b/src/platform/windows_winusb/device.rs @@ -130,6 +130,13 @@ impl WindowsDevice { winusb_handle, })) } + + pub(crate) fn detach_and_claim_interface( + self: &Arc, + interface: u8, + ) -> Result, Error> { + self.claim_interface(interface) + } } pub(crate) struct WindowsInterface {