implement equivalent to libusb_detach_kernel_driver
This commit is contained in:
parent
bcab2875f0
commit
984c3b1bc7
8 changed files with 112 additions and 11 deletions
14
examples/detach.rs
Normal file
14
examples/detach.rs
Normal file
|
|
@ -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() == 0x6001)
|
||||
.expect("device should be connected");
|
||||
|
||||
let device = di.open().unwrap();
|
||||
let interface = device.detach_kernel_driver(0).unwrap();
|
||||
sleep(Duration::from_secs(1));
|
||||
drop(interface);
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
//! Detach the kernel driver for an FTDI device and then reattach it.
|
||||
//! Detach the kernel driver for an FTDI device, claim the USB interface, and
|
||||
//! then reattach it.
|
||||
use std::{thread::sleep, time::Duration};
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
|
|
|||
|
|
@ -62,6 +62,24 @@ impl Device {
|
|||
Ok(Interface { backend })
|
||||
}
|
||||
|
||||
/// Detach kernel drivers for the specified interface. The drivers will be
|
||||
/// reattached on Drop.
|
||||
///
|
||||
/// ### Platform notes
|
||||
/// 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<DetachedInterface, Error> {
|
||||
#[cfg_attr(not(target_os = "linux"), allow(unused_variables))]
|
||||
let interface = interface;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let backend = self.backend.detach_kernel_driver(interface)?;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let backend = Arc::new(());
|
||||
|
||||
Ok(DetachedInterface { _backend: backend })
|
||||
}
|
||||
|
||||
/// Get information about the active configuration.
|
||||
///
|
||||
/// This returns cached data and does not perform IO. However, it can fail if the
|
||||
|
|
@ -561,6 +579,18 @@ impl Interface {
|
|||
}
|
||||
}
|
||||
|
||||
/// A detached interface of a USB device.
|
||||
///
|
||||
/// Obtain a `DetachedInterface` with the [`Device::detach_kernel_driver`] method.
|
||||
///
|
||||
/// This type is reference-counted with an [`Arc`] internally, and can be cloned cheaply for
|
||||
/// use in multiple places in your program. The interface is released when all clones
|
||||
/// are dropped.
|
||||
#[derive(Clone)]
|
||||
pub struct DetachedInterface {
|
||||
_backend: Arc<platform::DetachedInterface>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_send_sync() {
|
||||
fn require_send_sync<T: Send + Sync>() {}
|
||||
|
|
|
|||
|
|
@ -250,6 +250,17 @@ impl LinuxDevice {
|
|||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn detach_kernel_driver(
|
||||
self: &Arc<Self>,
|
||||
interface_number: u8,
|
||||
) -> Result<Arc<LinuxDetachedInterface>, Error> {
|
||||
usbfs::detach_kernel_driver(&self.fd, interface_number)?;
|
||||
Ok(Arc::new(LinuxDetachedInterface {
|
||||
device: self.clone(),
|
||||
interface_number,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn submit_urb(&self, urb: *mut Urb) {
|
||||
let ep = unsafe { (*urb).endpoint };
|
||||
if let Err(e) = usbfs::submit_urb(&self.fd, urb) {
|
||||
|
|
@ -359,3 +370,18 @@ impl Drop for LinuxInterface {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LinuxDetachedInterface {
|
||||
pub(crate) interface_number: u8,
|
||||
pub(crate) device: Arc<LinuxDevice>,
|
||||
}
|
||||
|
||||
impl Drop for LinuxDetachedInterface {
|
||||
fn drop(&mut self) {
|
||||
let res = usbfs::attach_kernel_driver(&self.device.fd, self.interface_number);
|
||||
debug!(
|
||||
"Reattached kernel drivers for interface {} on device {}: {res:?}",
|
||||
self.interface_number, self.device.events_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ mod events;
|
|||
pub use enumeration::{list_devices, SysfsPath};
|
||||
|
||||
mod device;
|
||||
pub(crate) use device::LinuxDetachedInterface as DetachedInterface;
|
||||
pub(crate) use device::LinuxDevice as Device;
|
||||
pub(crate) use device::LinuxInterface as Interface;
|
||||
|
||||
|
|
|
|||
|
|
@ -57,8 +57,7 @@ pub fn detach_and_claim_interface<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result
|
|||
|
||||
dc.driver[0..6].copy_from_slice(b"usbfs\0");
|
||||
|
||||
let ctl =
|
||||
ioctl::Setter::<ioctl::ReadOpcode<b'U', 27, DetachAndClaim>, DetachAndClaim>::new(dc);
|
||||
let ctl = ioctl::Setter::<opcodes::USBDEVFS_DISCONNECT_CLAIM, DetachAndClaim>::new(dc);
|
||||
|
||||
ioctl::ioctl(&fd, ctl)
|
||||
}
|
||||
|
|
@ -71,15 +70,43 @@ struct UsbFsIoctl {
|
|||
data: *mut c_void,
|
||||
}
|
||||
|
||||
pub fn attach_kernel_driver<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
||||
/// Opcodes used in ioctl with the usb device fs.
|
||||
///
|
||||
/// Taken from https://github.com/torvalds/linux/blob/e9680017b2dc8686a908ea1b51941a91b6da9f1d/include/uapi/linux/usbdevice_fs.h#L187
|
||||
// TODO: Move the rest of the opcodes into here?
|
||||
#[allow(non_camel_case_types)]
|
||||
mod opcodes {
|
||||
use super::*;
|
||||
|
||||
// We repeat the USBDEVFS_ prefix to help keep the same names as what linux uses.
|
||||
// This makes the code more searchable.
|
||||
|
||||
pub type USBDEVFS_IOCTL = ioctl::ReadWriteOpcode<b'U', 18, UsbFsIoctl>;
|
||||
pub type USBDEVFS_DISCONNECT = ioctl::NoneOpcode<b'U', 22, ()>;
|
||||
pub type USBDEVFS_CONNECT = ioctl::NoneOpcode<b'U', 23, ()>;
|
||||
pub type USBDEVFS_DISCONNECT_CLAIM = ioctl::ReadOpcode<b'U', 27, DetachAndClaim>;
|
||||
}
|
||||
|
||||
pub fn detach_kernel_driver<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
||||
let command = UsbFsIoctl {
|
||||
interface: interface.into(),
|
||||
ioctl_code: opcodes::USBDEVFS_DISCONNECT::OPCODE.raw(),
|
||||
data: std::ptr::null_mut(),
|
||||
};
|
||||
unsafe {
|
||||
let command = UsbFsIoctl {
|
||||
interface: interface.into(),
|
||||
ioctl_code: ioctl::NoneOpcode::<b'U', 23, ()>::OPCODE.raw() as c_uint, // IOCTL_USBFS_CONNECT
|
||||
data: std::ptr::null_mut(),
|
||||
};
|
||||
let ctl =
|
||||
ioctl::Setter::<ioctl::ReadWriteOpcode<b'U', 18, UsbFsIoctl>, UsbFsIoctl>::new(command);
|
||||
let ctl = ioctl::Setter::<opcodes::USBDEVFS_IOCTL, UsbFsIoctl>::new(command);
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach_kernel_driver<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
|
||||
let command = UsbFsIoctl {
|
||||
interface: interface.into(),
|
||||
ioctl_code: opcodes::USBDEVFS_CONNECT::OPCODE.raw(),
|
||||
data: std::ptr::null_mut(),
|
||||
};
|
||||
unsafe {
|
||||
let ctl = ioctl::Setter::<opcodes::USBDEVFS_IOCTL, UsbFsIoctl>::new(command);
|
||||
ioctl::ioctl(fd, ctl)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ pub use enumeration::list_devices;
|
|||
mod device;
|
||||
pub(crate) use device::MacDevice as Device;
|
||||
pub(crate) use device::MacInterface as Interface;
|
||||
pub(crate) type DetachedInterface = ();
|
||||
|
||||
use crate::transfer::TransferError;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ mod events;
|
|||
mod device;
|
||||
pub(crate) use device::WindowsDevice as Device;
|
||||
pub(crate) use device::WindowsInterface as Interface;
|
||||
pub(crate) type DetachedInterface = ();
|
||||
|
||||
mod transfer;
|
||||
pub(crate) use transfer::TransferData;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue