Merge pull request #35 from bugadani/ioctl

Implement `detach_and_claim_interface`
This commit is contained in:
Kevin Mehall 2024-01-30 09:06:02 -07:00 committed by GitHub
commit fc693a9233
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 109 additions and 0 deletions

14
examples/detach_claim.rs Normal file
View 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() == 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);
}

View file

@ -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<Interface, Error> {
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

View file

@ -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<Self>,
interface: u8,
) -> Result<Arc<LinuxInterface>, 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<LinuxDevice>,
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
);
}
}
}

View file

@ -39,6 +39,51 @@ pub fn release_interface<Fd: AsFd>(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: AsFd>(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::<ioctl::ReadOpcode<b'U', 27, DetachAndClaim>, 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: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
unsafe {
let command = UsbFsIoctl {
interface: interface.into(),
ioctl_code: ioctl::NoneOpcode::<b'U', 23, ()>::OPCODE.raw(), // IOCTL_USBFS_CONNECT
data: std::ptr::null_mut(),
};
let ctl =
ioctl::Setter::<ioctl::ReadWriteOpcode<b'U', 18, UsbFsIoctl>, UsbFsIoctl>::new(command);
ioctl::ioctl(fd, ctl)
}
}
#[repr(C)]
struct SetAltSetting {
interface: c_int,

View file

@ -188,6 +188,13 @@ impl MacDevice {
_event_registration,
}))
}
pub(crate) fn detach_and_claim_interface(
self: &Arc<Self>,
interface: u8,
) -> Result<Arc<MacInterface>, Error> {
self.claim_interface(interface)
}
}
pub(crate) struct MacInterface {

View file

@ -130,6 +130,13 @@ impl WindowsDevice {
winusb_handle,
}))
}
pub(crate) fn detach_and_claim_interface(
self: &Arc<Self>,
interface: u8,
) -> Result<Arc<WindowsInterface>, Error> {
self.claim_interface(interface)
}
}
pub(crate) struct WindowsInterface {