Merge pull request #35 from bugadani/ioctl
Implement `detach_and_claim_interface`
This commit is contained in:
commit
fc693a9233
6 changed files with 109 additions and 0 deletions
14
examples/detach_claim.rs
Normal file
14
examples/detach_claim.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() == 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue