Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement detach_and_claim_interface #35

Merged
merged 7 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions src/platform/linux_usbfs/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
"Claimed interface {interface} on device id {dev}",
dev = self.events_id
);
Ok(Arc::new(LinuxInterface {
device: self.clone(),
interface,
reattach: true,
}))
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
);
}
}
}
92 changes: 92 additions & 0 deletions src/platform/linux_usbfs/usbfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,98 @@ 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);
match ioctl::ioctl(&fd, ctl) {
Err(e) if e == io::Errno::NOTTY => {}
other => return other,
}

// disconnect-and-claim failed, fall back to the racy detach-and-claim
let detach_result = detach_kernel_driver(&fd, interface);

if let Err(e) = claim_interface(fd, interface) {
if let Err(detach_err) = detach_result {
return Err(detach_err);
}

return Err(e);
}

Ok(())
}
}

#[repr(C)]
struct UsbFsIoctl {
interface: c_uint,
ioctl_code: c_uint,
data: *mut c_void,
}

#[repr(C)]
struct GetDriver {
interface: c_int,
driver: [u8; 256],
}

pub fn detach_kernel_driver<Fd: AsFd>(fd: Fd, interface: u8) -> io::Result<()> {
unsafe {
// Only detach usbfs
let mut driver = GetDriver {
interface: interface.into(),
driver: [0; 256],
};
let ctl =
ioctl::Updater::<ioctl::ReadOpcode<b'U', 8, GetDriver>, GetDriver>::new(&mut driver);
ioctl::ioctl(&fd, ctl)?;

if driver.driver[0..6] != *b"usbfs\0" {
return Err(io::Errno::NOENT);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems backwards -- you want to detach a kernel driver (e.g. ftdi_sio), but don't want to detach another instance of nusb / libusb using usbfs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Monkey see this, monkey do !=. Monkey no think source is C and that strcmp returns 0 if equal :)


let command = UsbFsIoctl {
interface: interface.into(),
ioctl_code: ioctl::NoneOpcode::<b'U', 22, ()>::OPCODE.raw(), // IOCTL_USBFS_DISCONNECT
data: std::ptr::null_mut(),
};
let ctl =
ioctl::Setter::<ioctl::WriteOpcode<b'U', 18, UsbFsIoctl>, UsbFsIoctl>::new(command);
ioctl::ioctl(fd, ctl)
}
}

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::WriteOpcode<b'U', 18, UsbFsIoctl>, UsbFsIoctl>::new(command);
ioctl::ioctl(fd, ctl)
}
}

#[repr(C)]
struct SetAltSetting {
interface: c_int,
Expand Down
7 changes: 7 additions & 0 deletions src/platform/macos_iokit/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions src/platform/windows_winusb/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading