From 5a7a4be0cf6772518c4d804923782789105eb152 Mon Sep 17 00:00:00 2001 From: kirisauce Date: Mon, 13 Jan 2025 22:58:12 +0800 Subject: [PATCH] Linux: Support getting device speed from the kernel 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. --- src/device.rs | 15 ++++++++++++++- src/platform/linux_usbfs/device.rs | 16 +++++++++++++++- src/platform/linux_usbfs/usbfs.rs | 7 +++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/device.rs b/src/device.rs index 5c706f8..63f86c6 100644 --- a/src/device.rs +++ b/src/device.rs @@ -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}; @@ -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, Error> { + self.backend.get_speed() + } } /// An opened interface of a USB device. diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index a0c9cfd..6adeeb5 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -33,7 +33,7 @@ use crate::{ transfer::{ notify_completion, Control, Direction, EndpointType, TransferError, TransferHandle, }, - DeviceInfo, Error, + DeviceInfo, Error, Speed, }; pub(crate) struct LinuxDevice { @@ -409,6 +409,20 @@ impl LinuxDevice { pub(crate) fn descriptors(&self) -> &[u8] { &self.descriptors } + + pub(crate) fn get_speed(&self) -> Result, 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 { diff --git a/src/platform/linux_usbfs/usbfs.rs b/src/platform/linux_usbfs/usbfs.rs index 22725db..20485d5 100644 --- a/src/platform/linux_usbfs/usbfs.rs +++ b/src/platform/linux_usbfs/usbfs.rs @@ -286,3 +286,10 @@ pub fn clear_halt(fd: Fd, endpoint: u8) -> io::Result<()> { ioctl::ioctl(fd, ctl) } } + +pub fn get_speed(fd: Fd) -> io::Result { + unsafe { + let ctl = Transfer::, ()>::new(()); + ioctl::ioctl(fd, ctl) + } +}