Skip to content

Commit

Permalink
Merge pull request #65 from TheButlah/thebutlah/implement-detach
Browse files Browse the repository at this point in the history
implement equivalent to libusb_detach_kernel_driver
  • Loading branch information
kevinmehall authored Aug 3, 2024
2 parents db281eb + f1dd76f commit 7218b38
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 10 deletions.
14 changes: 14 additions & 0 deletions examples/detach.rs
Original file line number Diff line number Diff line change
@@ -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();
device.detach_kernel_driver(0).unwrap();
sleep(Duration::from_secs(10));
device.attach_kernel_driver(0).unwrap();
}
3 changes: 2 additions & 1 deletion examples/detach_claim.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand Down
26 changes: 26 additions & 0 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,32 @@ impl Device {
Ok(Interface { backend })
}

/// Detach kernel drivers for the specified interface.
///
/// ### 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<(), Error> {
#[cfg(target_os = "linux")]
self.backend.detach_kernel_driver(interface)?;
let _ = interface;

Ok(())
}

/// Attach kernel drivers for the specified interface.
///
/// ### Platform notes
/// 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")]
self.backend.attach_kernel_driver(interface)?;
let _ = interface;

Ok(())
}

/// Get information about the active configuration.
///
/// This returns cached data and does not perform IO. However, it can fail if the
Expand Down
14 changes: 14 additions & 0 deletions src/platform/linux_usbfs/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,20 @@ impl LinuxDevice {
}))
}

pub(crate) fn detach_kernel_driver(
self: &Arc<Self>,
interface_number: u8,
) -> Result<(), Error> {
usbfs::detach_kernel_driver(&self.fd, interface_number).map_err(|e| e.into())
}

pub(crate) fn attach_kernel_driver(
self: &Arc<Self>,
interface_number: u8,
) -> Result<(), Error> {
usbfs::attach_kernel_driver(&self.fd, interface_number).map_err(|e| e.into())
}

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) {
Expand Down
50 changes: 41 additions & 9 deletions src/platform/linux_usbfs/usbfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -71,15 +70,48 @@ struct UsbFsIoctl {
data: *mut c_void,
}

/// 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
// We repeat the USBDEVFS_ prefix to help keep the same names as what linux uses.
// This makes the code more searchable.
// TODO: Move the rest of the opcodes into here.
#[allow(non_camel_case_types)]
mod opcodes {
use super::*;

pub type USBDEVFS_IOCTL = ioctl::ReadWriteOpcode<b'U', 18, UsbFsIoctl>;
pub type USBDEVFS_DISCONNECT_CLAIM = ioctl::ReadOpcode<b'U', 27, DetachAndClaim>;

/// These opcodes are nested inside a [`USBDEVFS_IOCTL`] operation.
pub mod nested {
use super::*;

pub type USBDEVFS_DISCONNECT = ioctl::NoneOpcode<b'U', 22, ()>;
pub type USBDEVFS_CONNECT = ioctl::NoneOpcode<b'U', 23, ()>;
}
}

pub fn detach_kernel_driver<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
let command = UsbFsIoctl {
interface: interface.into(),
ioctl_code: opcodes::nested::USBDEVFS_DISCONNECT::OPCODE.raw(),
data: std::ptr::null_mut(),
};
unsafe {
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::nested::USBDEVFS_CONNECT::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)
}
}
Expand Down

0 comments on commit 7218b38

Please sign in to comment.