diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 922816a..e68e5a7 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -80,11 +80,10 @@ impl LinuxDevice { let sysfs_path = d.path.clone(); Blocking::new(move || { - let active_config = sysfs_path.read_attr("bConfigurationValue")?; let path = PathBuf::from(format!("/dev/bus/usb/{busnum:03}/{devnum:03}")); let fd = rustix::fs::open(&path, OFlags::RDWR | OFlags::CLOEXEC, Mode::empty()) .inspect_err(|e| warn!("Failed to open device {path:?}: {e}"))?; - Self::create_inner(fd, Some(sysfs_path), Some(active_config)) + Self::create_inner(fd, Some(sysfs_path)) }) } @@ -93,23 +92,15 @@ impl LinuxDevice { ) -> impl MaybeFuture, Error>> { Blocking::new(move || { debug!("Wrapping fd {} as usbfs device", fd.as_raw_fd()); - Self::create_inner(fd, None, None) + Self::create_inner(fd, None) }) } pub(crate) fn create_inner( fd: OwnedFd, sysfs: Option, - active_config: Option, ) -> Result, Error> { - let descriptors = { - let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) }; - // NOTE: Seek required on android - file.seek(std::io::SeekFrom::Start(0))?; - let mut buf = Vec::new(); - file.read_to_end(&mut buf)?; - buf - }; + let descriptors = read_all_from_fd(&fd)?; let Some(_) = DeviceDescriptor::new(&descriptors) else { return Err(Error::new( @@ -118,10 +109,23 @@ impl LinuxDevice { )); }; - let active_config = if let Some(active_config) = active_config { - active_config + let active_config: u8 = if let Some(sysfs) = sysfs.as_ref() { + sysfs.read_attr("bConfigurationValue").inspect_err(|e| { + warn!("failed to read sysfs bConfigurationValue: {e}"); + })? } else { - Self::get_config(&descriptors, &fd)? + request_configuration(&fd).unwrap_or_else(|()| { + let mut config_descriptors = parse_concatenated_config_descriptors( + &descriptors[DESCRIPTOR_LEN_DEVICE as usize..], + ); + + if let Some(config) = config_descriptors.next() { + config.configuration_value() + } else { + error!("no configurations for device fd {}", fd.as_raw_fd()); + 0 + } + }) }; let timerfd = timerfd_create( @@ -467,67 +471,6 @@ impl LinuxDevice { } } - fn get_config(descriptors: &[u8], fd: &OwnedFd) -> Result { - const REQUEST_GET_CONFIGURATION: u8 = 0x08; - - let mut dst = [0u8; 1]; - let r = usbfs::control( - fd, - usbfs::CtrlTransfer { - bRequestType: request_type(Direction::In, ControlType::Standard, Recipient::Device), - bRequest: REQUEST_GET_CONFIGURATION, - wValue: 0, - wIndex: 0, - wLength: dst.len() as u16, - timeout: Duration::from_millis(50) - .as_millis() - .try_into() - .expect("timeout must fit in u32 ms"), - data: &mut dst[0] as *mut u8 as *mut c_void, - }, - ); - - match r { - Ok(n) => { - if n == dst.len() { - let active_config = dst[0]; - debug!("Obtained active configuration for fd {} from GET_CONFIGURATION request: {active_config}", fd.as_raw_fd()); - return Ok(active_config); - } else { - warn!("GET_CONFIGURATION request returned incorrect length: {n}, expected 1",); - } - } - Err(e) => { - warn!( - "GET_CONFIGURATION request failed: {:?}", - errno_to_transfer_error(e) - ); - } - } - - if descriptors.len() < DESCRIPTOR_LEN_DEVICE as usize { - warn!( - "Descriptors for device fd {} too short to use fallback configuration", - fd.as_raw_fd() - ); - return Err(ErrorKind::Other.into()); - } - - // Assume the current configuration is the first one - // See: https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L865 - let mut descriptors = - parse_concatenated_config_descriptors(&descriptors[DESCRIPTOR_LEN_DEVICE as usize..]); - if let Some(config) = descriptors.next() { - return Ok(config.configuration_value()); - } - - error!( - "No available configurations for device fd {}", - fd.as_raw_fd() - ); - Err(ErrorKind::Other.into()) - } - pub(crate) fn speed(&self) -> Option { usbfs::get_speed(&self.fd) .inspect_err(|e| log::error!("USBDEVFS_GET_SPEED failed: {e}")) @@ -544,6 +487,52 @@ impl LinuxDevice { } } +fn read_all_from_fd(fd: &OwnedFd) -> Result, std::io::Error> { + let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) }; + file.seek(std::io::SeekFrom::Start(0))?; + let mut buf = Vec::new(); + file.read_to_end(&mut buf)?; + Ok(buf) +} + +/// Get the active configuration with a blocking request to the device. +fn request_configuration(fd: &OwnedFd) -> Result { + const REQUEST_GET_CONFIGURATION: u8 = 0x08; + + let mut dst = [0u8; 1]; + let r = usbfs::control( + fd, + usbfs::CtrlTransfer { + bRequestType: request_type(Direction::In, ControlType::Standard, Recipient::Device), + bRequest: REQUEST_GET_CONFIGURATION, + wValue: 0, + wIndex: 0, + wLength: dst.len() as u16, + timeout: 50, + data: &mut dst[0] as *mut u8 as *mut c_void, + }, + ); + + match r { + Ok(1) => { + let active_config = dst[0]; + debug!("Obtained active configuration for fd {} from GET_CONFIGURATION request: {active_config}", fd.as_raw_fd()); + return Ok(active_config); + } + Ok(n) => { + warn!("GET_CONFIGURATION request returned unexpected length: {n}, expected 1"); + Err(()) + } + Err(e) => { + warn!( + "GET_CONFIGURATION request failed: {:?}", + errno_to_transfer_error(e) + ); + Err(()) + } + } +} + impl Drop for LinuxDevice { fn drop(&mut self) { debug!("Closing device {}", self.events_id);