Skip to content

Commit

Permalink
Linux: Support getting device speed from the kernel
Browse files Browse the repository at this point in the history
This is done through an ioctl `USBDEVFS_GET_SPEED`. Judging from
the kernel souce code, it just returns a cached value, and should
not make any request to the device.
  • Loading branch information
kirisauce committed Jan 13, 2025
1 parent 2c6a6d3 commit 5a7a4be
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 2 deletions.
15 changes: 14 additions & 1 deletion src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
Control, ControlIn, ControlOut, EndpointType, Queue, RequestBuffer, TransferError,
TransferFuture,
},
DeviceInfo, Error,
DeviceInfo, Error, Speed,
};
use log::error;
use std::{io::ErrorKind, sync::Arc, time::Duration};
Expand Down Expand Up @@ -358,6 +358,19 @@ impl Device {
let buf = self.backend.descriptors();
validate_device_descriptor(&buf).map(|len| DeviceDescriptor::new(&buf[0..len]))
}

/// Get device speed.
///
/// On most platforms, the speed is stored by the OS, and calling this method should not
/// make any request to the device.
///
/// ### Platform-specific notes
///
/// * This is only supported on Linux at present.
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn get_speed(&self) -> Result<Option<Speed>, Error> {
self.backend.get_speed()
}
}

/// An opened interface of a USB device.
Expand Down
16 changes: 15 additions & 1 deletion src/platform/linux_usbfs/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::{
transfer::{
notify_completion, Control, Direction, EndpointType, TransferError, TransferHandle,
},
DeviceInfo, Error,
DeviceInfo, Error, Speed,
};

pub(crate) struct LinuxDevice {
Expand Down Expand Up @@ -409,6 +409,20 @@ impl LinuxDevice {
pub(crate) fn descriptors(&self) -> &[u8] {
&self.descriptors
}

pub(crate) fn get_speed(&self) -> Result<Option<Speed>, Error> {
usbfs::get_speed(&self.fd)
.map_err(|errno| errno.into())
.map(|raw_speed| match raw_speed {
1 => Some(Speed::Low),
2 => Some(Speed::Full),
3 => Some(Speed::High),
// 4 is wireless USB, but we don't support it
5 => Some(Speed::Super),
6 => Some(Speed::SuperPlus),
_ => None,
})
}
}

impl Drop for LinuxDevice {
Expand Down
7 changes: 7 additions & 0 deletions src/platform/linux_usbfs/usbfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,10 @@ pub fn clear_halt<Fd: AsFd>(fd: Fd, endpoint: u8) -> io::Result<()> {
ioctl::ioctl(fd, ctl)
}
}

pub fn get_speed<Fd: AsFd>(fd: Fd) -> io::Result<usize> {
unsafe {
let ctl = Transfer::<ioctl::NoneOpcode<b'U', 31, ()>, ()>::new(());
ioctl::ioctl(fd, ctl)
}
}

0 comments on commit 5a7a4be

Please sign in to comment.