From b526f6a6a3056340ad8c262995b90caf3a9fdcdf Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Sun, 16 Feb 2025 13:03:30 -0700 Subject: [PATCH 1/8] Use NonZeroU8 for string descriptor indexes --- src/descriptors.rs | 27 ++++++++++++++------------- src/device.rs | 17 ++++++++--------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/descriptors.rs b/src/descriptors.rs index fec314d..a505a5b 100644 --- a/src/descriptors.rs +++ b/src/descriptors.rs @@ -7,6 +7,7 @@ use std::{ fmt::{Debug, Display}, io::ErrorKind, iter, + num::NonZeroU8, ops::Deref, }; @@ -321,18 +322,18 @@ descriptor_fields! { impl DeviceDescriptor { /// `iManufacturer` descriptor field: Index for manufacturer description string. - pub fn manufacturer_string_index(&self) -> Option { - Some(self.manufacturer_string_index_raw()).filter(|&i| i != 0) + pub fn manufacturer_string_index(&self) -> Option { + NonZeroU8::new(self.manufacturer_string_index_raw()) } /// `iProduct` descriptor field: Index for product description string. - pub fn product_string_index(&self) -> Option { - Some(self.product_string_index_raw()).filter(|&i| i != 0) + pub fn product_string_index(&self) -> Option { + NonZeroU8::new(self.product_string_index_raw()) } /// `iSerialNumber` descriptor field: Index for serial number string. - pub fn serial_number_string_index(&self) -> Option { - Some(self.serial_number_string_index_raw()).filter(|&i| i != 0) + pub fn serial_number_string_index(&self) -> Option { + NonZeroU8::new(self.serial_number_string_index_raw()) } } impl Debug for DeviceDescriptor { @@ -484,8 +485,8 @@ descriptor_fields! { impl<'a> ConfigurationDescriptor<'a> { /// Index of the string descriptor describing this configuration. #[doc(alias = "iConfiguration")] - pub fn string_index(&self) -> Option { - Some(self.string_index_raw()).filter(|&i| i != 0) + pub fn string_index(&self) -> Option { + NonZeroU8::new(self.string_index_raw()) } } @@ -607,8 +608,8 @@ descriptor_fields! { impl<'a> InterfaceDescriptor<'a> { /// Index of the string descriptor describing this interface or alternate setting. #[doc(alias = "iInterface")] - pub fn string_index(&self) -> Option { - Some(self.string_index_raw()).filter(|&i| i != 0) + pub fn string_index(&self) -> Option { + NonZeroU8::new(self.string_index_raw()) } } @@ -856,9 +857,9 @@ fn test_linux_root_hub() { assert_eq!(dev.vendor_id(), 0x1d6b); assert_eq!(dev.product_id(), 0x0002); assert_eq!(dev.device_version(), 0x0510); - assert_eq!(dev.manufacturer_string_index(), Some(3)); - assert_eq!(dev.product_string_index(), Some(2)); - assert_eq!(dev.serial_number_string_index(), Some(1)); + assert_eq!(dev.manufacturer_string_index(), NonZeroU8::new(3)); + assert_eq!(dev.product_string_index(), NonZeroU8::new(2)); + assert_eq!(dev.serial_number_string_index(), NonZeroU8::new(1)); assert_eq!(dev.num_configurations(), 1); let c = ConfigurationDescriptor(&[ diff --git a/src/device.rs b/src/device.rs index 9d67f3b..4f31cd7 100644 --- a/src/device.rs +++ b/src/device.rs @@ -11,7 +11,7 @@ use crate::{ DeviceInfo, Error, MaybeFuture, Speed, }; use log::error; -use std::{io::ErrorKind, sync::Arc, time::Duration}; +use std::{io::ErrorKind, num::NonZeroU8, sync::Arc, time::Duration}; /// An opened USB device. /// @@ -237,17 +237,16 @@ impl Device { /// See notes on [`get_descriptor`][`Self::get_descriptor`]. pub fn get_string_descriptor( &self, - desc_index: u8, + desc_index: NonZeroU8, language_id: u16, timeout: Duration, ) -> Result { - if desc_index == 0 { - return Err(Error::new( - ErrorKind::InvalidInput, - "string descriptor index 0 is reserved for the language table", - )); - } - let data = self.get_descriptor(DESCRIPTOR_TYPE_STRING, desc_index, language_id, timeout)?; + let data = self.get_descriptor( + DESCRIPTOR_TYPE_STRING, + desc_index.get(), + language_id, + timeout, + )?; decode_string_descriptor(&data) .map_err(|_| Error::new(ErrorKind::InvalidData, "string descriptor data was invalid")) From e8b271d874998236215706dec7d2b302d4aee951 Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Wed, 19 Feb 2025 07:18:36 -0700 Subject: [PATCH 2/8] Return option instead of panic in descriptor constructors --- src/descriptors.rs | 188 ++++++++++----------- src/device.rs | 5 +- src/platform/linux_usbfs/device.rs | 14 +- src/platform/windows_winusb/device.rs | 30 ++-- src/platform/windows_winusb/enumeration.rs | 7 +- 5 files changed, 115 insertions(+), 129 deletions(-) diff --git a/src/descriptors.rs b/src/descriptors.rs index a505a5b..4239319 100644 --- a/src/descriptors.rs +++ b/src/descriptors.rs @@ -182,37 +182,6 @@ macro_rules! descriptor_fields { } } -/// Check whether the buffer contains a valid device descriptor. -/// On success, it will return length of the descriptor, or returns `None`. -#[allow(unused)] -pub(crate) fn validate_device_descriptor(buf: &[u8]) -> Option { - if buf.len() < DESCRIPTOR_LEN_DEVICE as usize { - if buf.len() != 0 { - warn!( - "device descriptor buffer is {} bytes, need {}", - buf.len(), - DESCRIPTOR_LEN_DEVICE - ); - } - return None; - } - - if buf[0] < DESCRIPTOR_LEN_DEVICE { - warn!("invalid device descriptor bLength"); - return None; - } - - if buf[1] != DESCRIPTOR_TYPE_DEVICE { - warn!( - "device bDescriptorType is {}, not a device descriptor", - buf[1] - ); - return None; - } - - return Some(buf[0] as usize); -} - /// Information about a USB device. #[derive(Clone)] pub struct DeviceDescriptor([u8; DESCRIPTOR_LEN_DEVICE as usize]); @@ -223,14 +192,31 @@ impl DeviceDescriptor { /// You normally obtain a `DeviceDescriptor` from a [`Device`][crate::Device], but this allows creating /// one from your own descriptor bytes for tests. /// - /// ### Panics - /// * when the buffer is too short for a device descriptor - /// * when the first descriptor is not a device descriptor - pub fn new(buf: &[u8]) -> Self { - assert!(buf.len() >= DESCRIPTOR_LEN_DEVICE as usize); - assert!(buf[0] as usize >= DESCRIPTOR_LEN_DEVICE as usize); - assert!(buf[1] == DESCRIPTOR_TYPE_DEVICE); - Self(buf[0..DESCRIPTOR_LEN_DEVICE as usize].try_into().unwrap()) + /// This ignores any trailing data after the `bLength` specified in the descriptor. + pub fn new(buf: &[u8]) -> Option { + let Some(buf) = buf.get(0..DESCRIPTOR_LEN_DEVICE as usize) else { + if buf.len() != 0 { + warn!( + "device descriptor buffer is {} bytes, need {}", + buf.len(), + DESCRIPTOR_LEN_DEVICE + ); + } + return None; + }; + let buf: [u8; DESCRIPTOR_LEN_DEVICE as usize] = buf.try_into().ok()?; + if buf[0] < DESCRIPTOR_LEN_DEVICE { + warn!("invalid device descriptor bLength"); + None + } else if buf[1] != DESCRIPTOR_TYPE_DEVICE { + warn!( + "device bDescriptorType is {}, not a device descriptor", + buf[1] + ); + None + } else { + Some(Self(buf)) + } } /// Get the bytes of the descriptor. @@ -364,44 +350,6 @@ impl Debug for DeviceDescriptor { } } -#[allow(unused)] -pub(crate) fn validate_config_descriptor(buf: &[u8]) -> Option { - if buf.len() < DESCRIPTOR_LEN_CONFIGURATION as usize { - if buf.len() != 0 { - warn!( - "config descriptor buffer is {} bytes, need {}", - buf.len(), - DESCRIPTOR_LEN_CONFIGURATION - ); - } - return None; - } - - if buf[0] < DESCRIPTOR_LEN_CONFIGURATION { - warn!("invalid config descriptor bLength"); - return None; - } - - if buf[1] != DESCRIPTOR_TYPE_CONFIGURATION { - warn!( - "config bDescriptorType is {}, not a configuration descriptor", - buf[0] - ); - return None; - } - - let total_len = u16::from_le_bytes(buf[2..4].try_into().unwrap()) as usize; - if total_len < buf[0] as usize || total_len > buf.len() { - warn!( - "invalid config descriptor wTotalLen of {total_len} (buffer size is {bufsize})", - bufsize = buf.len() - ); - return None; - } - - Some(total_len) -} - /// Information about a USB configuration with access to all associated interfaces, endpoints, and other descriptors. #[derive(Clone)] pub struct ConfigurationDescriptor<'a>(&'a [u8]); @@ -412,16 +360,47 @@ impl<'a> ConfigurationDescriptor<'a> { /// You normally obtain a `Configuration` from a [`Device`][crate::Device], but this allows creating /// one from your own descriptor bytes for tests. /// - /// ### Panics - /// * when the buffer is too short for a configuration descriptor - /// * when the bLength and wTotalLength fields are longer than the buffer - /// * when the first descriptor is not a configuration descriptor - pub fn new(buf: &[u8]) -> ConfigurationDescriptor { - assert!(buf.len() >= DESCRIPTOR_LEN_CONFIGURATION as usize); - assert!(buf[0] as usize >= DESCRIPTOR_LEN_CONFIGURATION as usize); - assert!(buf[1] == DESCRIPTOR_TYPE_CONFIGURATION); - assert!(buf.len() == u16::from_le_bytes(buf[2..4].try_into().unwrap()) as usize); - ConfigurationDescriptor(buf) + /// This ignores any trailing data after the length specified in `wTotalLen`. + pub fn new(buf: &[u8]) -> Option { + if buf.len() < DESCRIPTOR_LEN_CONFIGURATION as usize { + if buf.len() != 0 { + warn!( + "config descriptor buffer is {} bytes, need {}", + buf.len(), + DESCRIPTOR_LEN_CONFIGURATION + ); + } + return None; + } + + if buf[0] < DESCRIPTOR_LEN_CONFIGURATION { + warn!("invalid config descriptor bLength"); + return None; + } + + if buf[1] != DESCRIPTOR_TYPE_CONFIGURATION { + warn!( + "config bDescriptorType is {}, not a configuration descriptor", + buf[0] + ); + return None; + } + + let total_len = u16::from_le_bytes(buf[2..4].try_into().unwrap()) as usize; + if total_len < buf[0] as usize || total_len > buf.len() { + warn!( + "invalid config descriptor wTotalLen of {total_len} (buffer size is {bufsize})", + bufsize = buf.len() + ); + return None; + } + + Some(ConfigurationDescriptor(&buf[..total_len])) + } + + #[allow(unused)] + pub(crate) fn new_unchecked(d: &'a [u8]) -> Self { + Self(d) } /// Get the configuration descriptor followed by all trailing interface and other descriptors. @@ -735,12 +714,13 @@ impl From for Error { /// Split a chain of concatenated configuration descriptors by `wTotalLength` #[allow(unused)] -pub(crate) fn parse_concatenated_config_descriptors(mut buf: &[u8]) -> impl Iterator { +pub(crate) fn parse_concatenated_config_descriptors( + mut buf: &[u8], +) -> impl Iterator { iter::from_fn(move || { - let total_len = validate_config_descriptor(buf)?; - let descriptors = &buf[..total_len]; - buf = &buf[total_len..]; - Some(descriptors) + let desc = ConfigurationDescriptor::new(buf)?; + buf = &buf[desc.0.len()..]; + Some(desc) }) } @@ -775,15 +755,19 @@ mod test_concatenated { #[test] fn test_empty() { assert_eq!( - parse_concatenated_config_descriptors(&[]).collect::>(), - Vec::<&[u8]>::new() + parse_concatenated_config_descriptors(&[]) + .collect::>() + .len(), + 0 ); } #[test] fn test_short() { assert_eq!( - parse_concatenated_config_descriptors(&[0]).collect::>(), + parse_concatenated_config_descriptors(&[0]) + .map(|d| d.descriptors().as_bytes()) + .collect::>(), Vec::<&[u8]>::new() ); } @@ -792,7 +776,8 @@ mod test_concatenated { fn test_invalid_total_len() { assert_eq!( parse_concatenated_config_descriptors(&[9, 2, 0, 0, 0, 0, 0, 0, 0]) - .collect::>(), + .map(|d| d.descriptors().as_bytes()) + .collect::>(), Vec::<&[u8]>::new() ); } @@ -801,13 +786,15 @@ mod test_concatenated { fn test_one_config() { assert_eq!( parse_concatenated_config_descriptors(&[9, 2, 9, 0, 0, 0, 0, 0, 0]) - .collect::>(), + .map(|d| d.descriptors().as_bytes()) + .collect::>(), vec![&[9, 2, 9, 0, 0, 0, 0, 0, 0]] ); assert_eq!( parse_concatenated_config_descriptors(&[9, 2, 13, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0]) - .collect::>(), + .map(|d| d.descriptors().as_bytes()) + .collect::>(), vec![&[9, 2, 13, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0]] ); } @@ -818,7 +805,8 @@ mod test_concatenated { parse_concatenated_config_descriptors(&[ 9, 2, 13, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 9, 2, 9, 0, 0, 0, 0, 0, 0 ]) - .collect::>(), + .map(|d| d.descriptors().as_bytes()) + .collect::>(), vec![ [9, 2, 13, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0].as_slice(), [9, 2, 9, 0, 0, 0, 0, 0, 0].as_slice() @@ -848,7 +836,7 @@ fn test_linux_root_hub() { let dev = DeviceDescriptor::new(&[ 0x12, 0x01, 0x00, 0x02, 0x09, 0x00, 0x01, 0x40, 0x6b, 0x1d, 0x02, 0x00, 0x10, 0x05, 0x03, 0x02, 0x01, 0x01 - ]); + ]).unwrap(); assert_eq!(dev.usb_version(), 0x0200); assert_eq!(dev.class(), 0x09); assert_eq!(dev.subclass(), 0x00); diff --git a/src/device.rs b/src/device.rs index 4f31cd7..5bcfa97 100644 --- a/src/device.rs +++ b/src/device.rs @@ -134,9 +134,7 @@ impl Device { /// /// This returns cached data and does not perform IO. pub fn configurations(&self) -> impl Iterator { - self.backend - .configuration_descriptors() - .map(ConfigurationDescriptor::new) + self.backend.configuration_descriptors() } /// Set the device configuration. @@ -604,7 +602,6 @@ impl Interface { .backend .device .configuration_descriptors() - .map(ConfigurationDescriptor::new) .find(|c| c.configuration_value() == active); configuration diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 3aa1b8a..91eb101 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -25,7 +25,7 @@ use super::{ usbfs::{self, Urb}, SysfsPath, }; -use crate::descriptors::{validate_device_descriptor, ConfigurationDescriptor, DeviceDescriptor}; +use crate::descriptors::{ConfigurationDescriptor, DeviceDescriptor}; use crate::maybe_future::{blocking::Blocking, MaybeFuture}; use crate::platform::linux_usbfs::events::Watch; use crate::transfer::{ControlType, Recipient}; @@ -91,7 +91,7 @@ impl LinuxDevice { buf }; - let Some(_) = validate_device_descriptor(&descriptors) else { + let Some(_) = DeviceDescriptor::new(&descriptors) else { return Err(Error::new( ErrorKind::InvalidData, "invalid device descriptor", @@ -167,10 +167,12 @@ impl LinuxDevice { } pub(crate) fn device_descriptor(&self) -> DeviceDescriptor { - DeviceDescriptor::new(&self.descriptors) + DeviceDescriptor::new(&self.descriptors).unwrap() } - pub(crate) fn configuration_descriptors(&self) -> impl Iterator { + pub(crate) fn configuration_descriptors( + &self, + ) -> impl Iterator> { parse_concatenated_config_descriptors(&self.descriptors[DESCRIPTOR_LEN_DEVICE as usize..]) } @@ -421,9 +423,7 @@ impl LinuxDevice { // 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..]) - .map(ConfigurationDescriptor::new); - + parse_concatenated_config_descriptors(&descriptors[DESCRIPTOR_LEN_DEVICE as usize..]); if let Some(config) = descriptors.next() { return Ok(config.configuration_value()); } diff --git a/src/platform/windows_winusb/device.rs b/src/platform/windows_winusb/device.rs index 976e82e..2eb6510 100644 --- a/src/platform/windows_winusb/device.rs +++ b/src/platform/windows_winusb/device.rs @@ -24,7 +24,7 @@ use windows_sys::Win32::{ use crate::{ descriptors::{ - validate_config_descriptor, DeviceDescriptor, DESCRIPTOR_LEN_DEVICE, + ConfigurationDescriptor, DeviceDescriptor, DESCRIPTOR_LEN_DEVICE, DESCRIPTOR_TYPE_CONFIGURATION, }, maybe_future::{blocking::Blocking, MaybeFuture, Ready}, @@ -68,22 +68,20 @@ impl WindowsDevice { // Safety: Windows API struct is repr(C), packed, and we're assuming Windows is little-endian let device_descriptor = unsafe { - DeviceDescriptor::new(&transmute::<_, [u8; DESCRIPTOR_LEN_DEVICE as usize]>( - connection_info.device_desc, - )) + &transmute::<_, [u8; DESCRIPTOR_LEN_DEVICE as usize]>(connection_info.device_desc) }; + let device_descriptor = DeviceDescriptor::new(device_descriptor) + .ok_or_else(|| Error::new(ErrorKind::InvalidData, "invalid device descriptor"))?; let num_configurations = connection_info.device_desc.bNumConfigurations; let config_descriptors = (0..num_configurations) .flat_map(|i| { - let res = hub_port.get_descriptor(DESCRIPTOR_TYPE_CONFIGURATION, i, 0); - match res { - Ok(v) => validate_config_descriptor(&v[..]).map(|_| v), - Err(e) => { - error!("Failed to read config descriptor {}: {}", i, e); - None - } - } + let d = hub_port + .get_descriptor(DESCRIPTOR_TYPE_CONFIGURATION, i, 0) + .inspect_err(|e| error!("Failed to read config descriptor {}: {}", i, e)) + .ok()?; + + ConfigurationDescriptor::new(&d).is_some().then_some(d) }) .collect(); @@ -110,8 +108,12 @@ impl WindowsDevice { self.active_config } - pub(crate) fn configuration_descriptors(&self) -> impl Iterator { - self.config_descriptors.iter().map(|d| &d[..]) + pub(crate) fn configuration_descriptors( + &self, + ) -> impl Iterator { + self.config_descriptors + .iter() + .map(|d| ConfigurationDescriptor::new_unchecked(&d[..])) } pub(crate) fn set_configuration( diff --git a/src/platform/windows_winusb/enumeration.rs b/src/platform/windows_winusb/enumeration.rs index a714b76..fd7b092 100644 --- a/src/platform/windows_winusb/enumeration.rs +++ b/src/platform/windows_winusb/enumeration.rs @@ -15,8 +15,8 @@ use windows_sys::Win32::Devices::{ use crate::{ descriptors::{ - decode_string_descriptor, language_id::US_ENGLISH, validate_config_descriptor, - ConfigurationDescriptor, DESCRIPTOR_TYPE_CONFIGURATION, DESCRIPTOR_TYPE_STRING, + decode_string_descriptor, language_id::US_ENGLISH, ConfigurationDescriptor, + DESCRIPTOR_TYPE_CONFIGURATION, DESCRIPTOR_TYPE_STRING, }, maybe_future::{blocking::Blocking, MaybeFuture}, BusInfo, DeviceInfo, Error, InterfaceInfo, UsbControllerType, @@ -206,8 +206,7 @@ fn list_interfaces_from_desc(hub_port: &HubPort, active_config: u8) -> Option Date: Wed, 19 Feb 2025 09:22:38 -0700 Subject: [PATCH 3/8] Rename Descriptors -> DescriptorIter, don't include the parent descriptor --- src/descriptors.rs | 54 +++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/descriptors.rs b/src/descriptors.rs index 4239319..3e91fa7 100644 --- a/src/descriptors.rs +++ b/src/descriptors.rs @@ -86,9 +86,9 @@ impl<'a> Deref for Descriptor<'a> { /// An iterator over a sequence of USB descriptors. #[derive(Clone)] -pub struct Descriptors<'a>(&'a [u8]); +pub struct DescriptorIter<'a>(&'a [u8]); -impl<'a> Descriptors<'a> { +impl<'a> DescriptorIter<'a> { /// Get the concatenated bytes of the remaining descriptors. pub fn as_bytes(&self) -> &'a [u8] { self.0 @@ -152,7 +152,7 @@ impl<'a> Descriptors<'a> { } } -impl<'a> Iterator for Descriptors<'a> { +impl<'a> Iterator for DescriptorIter<'a> { type Item = Descriptor<'a>; fn next(&mut self) -> Option { @@ -355,9 +355,9 @@ impl Debug for DeviceDescriptor { pub struct ConfigurationDescriptor<'a>(&'a [u8]); impl<'a> ConfigurationDescriptor<'a> { - /// Create a `Configuration` from a buffer containing a series of descriptors. + /// Create a `ConfigurationDescriptor` from a buffer containing a series of descriptors. /// - /// You normally obtain a `Configuration` from a [`Device`][crate::Device], but this allows creating + /// You normally obtain a `ConfigurationDescriptor` from a [`Device`][crate::Device], but this allows creating /// one from your own descriptor bytes for tests. /// /// This ignores any trailing data after the length specified in `wTotalLen`. @@ -403,9 +403,14 @@ impl<'a> ConfigurationDescriptor<'a> { Self(d) } - /// Get the configuration descriptor followed by all trailing interface and other descriptors. - pub fn descriptors(&self) -> Descriptors<'a> { - Descriptors(self.0) + /// The bytes of the configuration descriptor and all trailing descriptors. + pub fn as_bytes(&self) -> &'a [u8] { + self.0 + } + + /// Iterate all trailing interface and other descriptors. + pub fn descriptors(&self) -> DescriptorIter<'a> { + DescriptorIter(&self.0[self.0[0] as usize..]) } /// Iterate all interfaces and alternate settings settings of this configuration. @@ -536,10 +541,15 @@ impl<'a> InterfaceDescriptors<'a> { pub struct InterfaceDescriptor<'a>(&'a [u8]); impl<'a> InterfaceDescriptor<'a> { - /// Get the interface descriptor followed by all trailing endpoint and other - /// descriptors up to the next interface descriptor. - pub fn descriptors(&self) -> Descriptors<'a> { - Descriptors(self.0) + /// The bytes of the interface descriptor and all trailing descriptors. + pub fn as_bytes(&self) -> &[u8] { + self.0 + } + + /// Iterate all trailing endpoint and other descriptors up to the next + /// interface descriptor. + pub fn descriptors(&self) -> DescriptorIter<'a> { + DescriptorIter(&self.0[self.0[0] as usize..]) } /// Get the endpoints of this interface. @@ -611,9 +621,14 @@ impl<'a> Debug for InterfaceDescriptor<'a> { pub struct EndpointDescriptor<'a>(&'a [u8]); impl<'a> EndpointDescriptor<'a> { - /// Get the endpoint descriptor followed by all trailing descriptors up to the next endpoint or interface descriptor. - pub fn descriptors(&self) -> impl Iterator> { - Descriptors(self.0) + /// The bytes of the endpoint descriptor and all trailing descriptors. + pub fn as_bytes(&self) -> &'a [u8] { + self.0 + } + + /// Iterate all trailing descriptors up to the next endpoint or interface descriptor. + pub fn descriptors(&self) -> DescriptorIter<'a> { + DescriptorIter(&self.0[self.0[0] as usize..]) } /// Get the endpoint's direction. @@ -786,14 +801,14 @@ mod test_concatenated { fn test_one_config() { assert_eq!( parse_concatenated_config_descriptors(&[9, 2, 9, 0, 0, 0, 0, 0, 0]) - .map(|d| d.descriptors().as_bytes()) + .map(|d| d.as_bytes()) .collect::>(), vec![&[9, 2, 9, 0, 0, 0, 0, 0, 0]] ); assert_eq!( parse_concatenated_config_descriptors(&[9, 2, 13, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0]) - .map(|d| d.descriptors().as_bytes()) + .map(|d| d.as_bytes()) .collect::>(), vec![&[9, 2, 13, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0]] ); @@ -805,7 +820,7 @@ mod test_concatenated { parse_concatenated_config_descriptors(&[ 9, 2, 13, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 9, 2, 9, 0, 0, 0, 0, 0, 0 ]) - .map(|d| d.descriptors().as_bytes()) + .map(|d| d.as_bytes()) .collect::>(), vec![ [9, 2, 13, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0].as_slice(), @@ -998,7 +1013,6 @@ fn test_dell_webcam() { assert_eq!(alt.protocol(), 0); let mut descriptors = alt.descriptors(); - assert_eq!(descriptors.next().unwrap().descriptor_type(), DESCRIPTOR_TYPE_INTERFACE); for _ in 0..6 { assert_eq!(descriptors.next().unwrap().descriptor_type(), 0x24); } @@ -1013,7 +1027,7 @@ fn test_dell_webcam() { assert_eq!(endpoint.transfer_type(), EndpointType::Interrupt); assert_eq!(endpoint.max_packet_size(), 16); - assert_eq!(endpoint.descriptors().nth(1).unwrap().descriptor_type(), 0x25); + assert_eq!(endpoint.descriptors().next().unwrap().descriptor_type(), 0x25); assert!(endpoints.next().is_none()); assert!(alts.next().is_none()); From 12c0b57d13d49a154ab2296c5f9af08d241e8fbb Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Sat, 1 Feb 2025 19:33:37 -0700 Subject: [PATCH 4/8] Rename EndpointType -> TransferType --- src/descriptors.rs | 28 ++++++++++++------------- src/device.rs | 24 ++++++++++----------- src/platform/linux_usbfs/device.rs | 6 +++--- src/platform/linux_usbfs/transfer.rs | 14 ++++++------- src/platform/macos_iokit/device.rs | 6 +++--- src/platform/windows_winusb/device.rs | 8 +++---- src/platform/windows_winusb/transfer.rs | 12 +++++------ src/transfer/mod.rs | 2 +- src/transfer/queue.rs | 6 +++--- 9 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/descriptors.rs b/src/descriptors.rs index 3e91fa7..f32a9d2 100644 --- a/src/descriptors.rs +++ b/src/descriptors.rs @@ -14,7 +14,7 @@ use std::{ use log::warn; use crate::{ - transfer::{Direction, EndpointType}, + transfer::{Direction, TransferType}, Error, }; @@ -640,12 +640,12 @@ impl<'a> EndpointDescriptor<'a> { } /// Get the endpoint's transfer type. - pub fn transfer_type(&self) -> EndpointType { + pub fn transfer_type(&self) -> TransferType { match self.attributes() & 0x03 { - 0 => EndpointType::Control, - 1 => EndpointType::Isochronous, - 2 => EndpointType::Bulk, - 3 => EndpointType::Interrupt, + 0 => TransferType::Control, + 1 => TransferType::Isochronous, + 2 => TransferType::Bulk, + 3 => TransferType::Interrupt, _ => unreachable!(), } } @@ -891,7 +891,7 @@ fn test_linux_root_hub() { let endpoint = alt.endpoints().next().unwrap(); assert_eq!(endpoint.address(), 0x81); - assert_eq!(endpoint.transfer_type(), EndpointType::Interrupt); + assert_eq!(endpoint.transfer_type(), TransferType::Interrupt); assert_eq!(endpoint.max_packet_size(), 4); assert_eq!(endpoint.interval(), 12); @@ -1024,7 +1024,7 @@ fn test_dell_webcam() { let endpoint = endpoints.next().unwrap(); assert_eq!(endpoint.address(), 0x83); - assert_eq!(endpoint.transfer_type(), EndpointType::Interrupt); + assert_eq!(endpoint.transfer_type(), TransferType::Interrupt); assert_eq!(endpoint.max_packet_size(), 16); assert_eq!(endpoint.descriptors().next().unwrap().descriptor_type(), 0x25); @@ -1057,7 +1057,7 @@ fn test_dell_webcam() { let endpoint = endpoints.next().unwrap(); assert_eq!(endpoint.address(), 0x81); - assert_eq!(endpoint.transfer_type(), EndpointType::Isochronous); + assert_eq!(endpoint.transfer_type(), TransferType::Isochronous); assert_eq!(endpoint.max_packet_size(), 128); assert!(endpoints.next().is_none()); @@ -1073,7 +1073,7 @@ fn test_dell_webcam() { let endpoint = endpoints.next().unwrap(); assert_eq!(endpoint.address(), 0x81); - assert_eq!(endpoint.transfer_type(), EndpointType::Isochronous); + assert_eq!(endpoint.transfer_type(), TransferType::Isochronous); assert_eq!(endpoint.max_packet_size(), 256); assert_eq!(endpoint.packets_per_microframe(), 1); @@ -1090,7 +1090,7 @@ fn test_dell_webcam() { let endpoint = endpoints.next().unwrap(); assert_eq!(endpoint.address(), 0x81); - assert_eq!(endpoint.transfer_type(), EndpointType::Isochronous); + assert_eq!(endpoint.transfer_type(), TransferType::Isochronous); assert_eq!(endpoint.max_packet_size(), 800); assert_eq!(endpoint.packets_per_microframe(), 1); @@ -1107,7 +1107,7 @@ fn test_dell_webcam() { let endpoint = endpoints.next().unwrap(); assert_eq!(endpoint.address(), 0x81); - assert_eq!(endpoint.transfer_type(), EndpointType::Isochronous); + assert_eq!(endpoint.transfer_type(), TransferType::Isochronous); assert_eq!(endpoint.max_packet_size(), 800); assert_eq!(endpoint.packets_per_microframe(), 2); @@ -1122,7 +1122,7 @@ fn test_dell_webcam() { let endpoint = endpoints.next().unwrap(); assert_eq!(endpoint.address(), 0x81); - assert_eq!(endpoint.transfer_type(), EndpointType::Isochronous); + assert_eq!(endpoint.transfer_type(), TransferType::Isochronous); assert_eq!(endpoint.max_packet_size(), 800); assert_eq!(endpoint.packets_per_microframe(), 3); @@ -1137,7 +1137,7 @@ fn test_dell_webcam() { let endpoint = endpoints.next().unwrap(); assert_eq!(endpoint.address(), 0x81); - assert_eq!(endpoint.transfer_type(), EndpointType::Isochronous); + assert_eq!(endpoint.transfer_type(), TransferType::Isochronous); assert_eq!(endpoint.max_packet_size(), 1024); assert_eq!(endpoint.packets_per_microframe(), 3); diff --git a/src/device.rs b/src/device.rs index 5bcfa97..ed5da15 100644 --- a/src/device.rs +++ b/src/device.rs @@ -5,8 +5,8 @@ use crate::{ }, platform, transfer::{ - Control, ControlIn, ControlOut, EndpointType, Queue, RequestBuffer, TransferError, - TransferFuture, + Control, ControlIn, ControlOut, Queue, RequestBuffer, TransferError, TransferFuture, + TransferType, }, DeviceInfo, Error, MaybeFuture, Speed, }; @@ -462,7 +462,7 @@ impl Interface { /// least significant byte differs from the interface number, and this may /// become an error in the future. pub fn control_in(&self, data: ControlIn) -> TransferFuture { - let mut t = self.backend.make_transfer(0, EndpointType::Control); + let mut t = self.backend.make_transfer(0, TransferType::Control); t.submit::(data); TransferFuture::new(t) } @@ -498,7 +498,7 @@ impl Interface { /// least significant byte differs from the interface number, and this may /// become an error in the future. pub fn control_out(&self, data: ControlOut) -> TransferFuture { - let mut t = self.backend.make_transfer(0, EndpointType::Control); + let mut t = self.backend.make_transfer(0, TransferType::Control); t.submit::(data); TransferFuture::new(t) } @@ -508,7 +508,7 @@ impl Interface { /// * The requested length must be a multiple of the endpoint's maximum packet size /// * An IN endpoint address must have the top (`0x80`) bit set. pub fn bulk_in(&self, endpoint: u8, buf: RequestBuffer) -> TransferFuture { - let mut t = self.backend.make_transfer(endpoint, EndpointType::Bulk); + let mut t = self.backend.make_transfer(endpoint, TransferType::Bulk); t.submit(buf); TransferFuture::new(t) } @@ -517,7 +517,7 @@ impl Interface { /// /// * An OUT endpoint address must have the top (`0x80`) bit clear. pub fn bulk_out(&self, endpoint: u8, buf: Vec) -> TransferFuture> { - let mut t = self.backend.make_transfer(endpoint, EndpointType::Bulk); + let mut t = self.backend.make_transfer(endpoint, TransferType::Bulk); t.submit(buf); TransferFuture::new(t) } @@ -526,14 +526,14 @@ impl Interface { /// /// * An IN endpoint address must have the top (`0x80`) bit set. pub fn bulk_in_queue(&self, endpoint: u8) -> Queue { - Queue::new(self.backend.clone(), endpoint, EndpointType::Bulk) + Queue::new(self.backend.clone(), endpoint, TransferType::Bulk) } /// Create a queue for managing multiple **OUT (host-to-device)** transfers on a **bulk** endpoint. /// /// * An OUT endpoint address must have the top (`0x80`) bit clear. pub fn bulk_out_queue(&self, endpoint: u8) -> Queue> { - Queue::new(self.backend.clone(), endpoint, EndpointType::Bulk) + Queue::new(self.backend.clone(), endpoint, TransferType::Bulk) } /// Submit a single **IN (device-to-host)** transfer on the specified **interrupt** endpoint. @@ -543,7 +543,7 @@ impl Interface { pub fn interrupt_in(&self, endpoint: u8, buf: RequestBuffer) -> TransferFuture { let mut t = self .backend - .make_transfer(endpoint, EndpointType::Interrupt); + .make_transfer(endpoint, TransferType::Interrupt); t.submit(buf); TransferFuture::new(t) } @@ -554,7 +554,7 @@ impl Interface { pub fn interrupt_out(&self, endpoint: u8, buf: Vec) -> TransferFuture> { let mut t = self .backend - .make_transfer(endpoint, EndpointType::Interrupt); + .make_transfer(endpoint, TransferType::Interrupt); t.submit(buf); TransferFuture::new(t) } @@ -563,14 +563,14 @@ impl Interface { /// /// * An IN endpoint address must have the top (`0x80`) bit set. pub fn interrupt_in_queue(&self, endpoint: u8) -> Queue { - Queue::new(self.backend.clone(), endpoint, EndpointType::Interrupt) + Queue::new(self.backend.clone(), endpoint, TransferType::Interrupt) } /// Create a queue for managing multiple **OUT (device-to-host)** transfers on an **interrupt** endpoint. /// /// * An OUT endpoint address must have the top (`0x80`) bit clear. pub fn interrupt_out_queue(&self, endpoint: u8) -> Queue> { - Queue::new(self.backend.clone(), endpoint, EndpointType::Interrupt) + Queue::new(self.backend.clone(), endpoint, TransferType::Interrupt) } /// Clear a bulk or interrupt endpoint's halt / stall condition. diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 91eb101..3edd0c1 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -32,7 +32,7 @@ use crate::transfer::{ControlType, Recipient}; use crate::{ descriptors::{parse_concatenated_config_descriptors, DESCRIPTOR_LEN_DEVICE}, transfer::{ - notify_completion, Control, Direction, EndpointType, TransferError, TransferHandle, + notify_completion, Control, Direction, TransferError, TransferHandle, TransferType, }, DeviceInfo, Error, Speed, }; @@ -276,7 +276,7 @@ impl LinuxDevice { self.clone(), None, 0, - EndpointType::Control, + TransferType::Control, )) } @@ -468,7 +468,7 @@ impl LinuxInterface { pub(crate) fn make_transfer( self: &Arc, endpoint: u8, - ep_type: EndpointType, + ep_type: TransferType, ) -> TransferHandle { TransferHandle::new(super::TransferData::new( self.device.clone(), diff --git a/src/platform/linux_usbfs/transfer.rs b/src/platform/linux_usbfs/transfer.rs index 8cc8510..106f766 100644 --- a/src/platform/linux_usbfs/transfer.rs +++ b/src/platform/linux_usbfs/transfer.rs @@ -8,8 +8,8 @@ use std::{ use rustix::io::Errno; use crate::transfer::{ - Completion, ControlIn, ControlOut, EndpointType, PlatformSubmit, PlatformTransfer, - RequestBuffer, ResponseBuffer, TransferError, SETUP_PACKET_SIZE, + Completion, ControlIn, ControlOut, PlatformSubmit, PlatformTransfer, RequestBuffer, + ResponseBuffer, TransferError, TransferType, SETUP_PACKET_SIZE, }; use super::{ @@ -43,13 +43,13 @@ impl TransferData { device: Arc, interface: Option>, endpoint: u8, - ep_type: EndpointType, + ep_type: TransferType, ) -> TransferData { let ep_type = match ep_type { - EndpointType::Control => USBDEVFS_URB_TYPE_CONTROL, - EndpointType::Interrupt => USBDEVFS_URB_TYPE_INTERRUPT, - EndpointType::Bulk => USBDEVFS_URB_TYPE_BULK, - EndpointType::Isochronous => USBDEVFS_URB_TYPE_ISO, + TransferType::Control => USBDEVFS_URB_TYPE_CONTROL, + TransferType::Interrupt => USBDEVFS_URB_TYPE_INTERRUPT, + TransferType::Bulk => USBDEVFS_URB_TYPE_BULK, + TransferType::Isochronous => USBDEVFS_URB_TYPE_ISO, }; TransferData { diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index 1a0ddf5..9100fcb 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -14,7 +14,7 @@ use log::{debug, error}; use crate::{ descriptors::DeviceDescriptor, maybe_future::blocking::Blocking, - transfer::{Control, Direction, EndpointType, TransferError, TransferHandle}, + transfer::{Control, Direction, TransferError, TransferHandle, TransferType}, DeviceInfo, Error, MaybeFuture, Speed, }; @@ -293,9 +293,9 @@ impl MacInterface { pub(crate) fn make_transfer( self: &Arc, endpoint: u8, - ep_type: EndpointType, + ep_type: TransferType, ) -> TransferHandle { - if ep_type == EndpointType::Control { + if ep_type == TransferType::Control { assert!(endpoint == 0); TransferHandle::new(super::TransferData::new_control(self.device.clone())) } else { diff --git a/src/platform/windows_winusb/device.rs b/src/platform/windows_winusb/device.rs index 2eb6510..54ce107 100644 --- a/src/platform/windows_winusb/device.rs +++ b/src/platform/windows_winusb/device.rs @@ -27,9 +27,9 @@ use crate::{ ConfigurationDescriptor, DeviceDescriptor, DESCRIPTOR_LEN_DEVICE, DESCRIPTOR_TYPE_CONFIGURATION, }, - maybe_future::{blocking::Blocking, MaybeFuture, Ready}, - transfer::{Control, Direction, EndpointType, Recipient, TransferError, TransferHandle}, - DeviceInfo, Error, Speed, + maybe_future::{blocking::Blocking, Ready}, + transfer::{Control, Direction, Recipient, TransferError, TransferHandle, TransferType}, + DeviceInfo, Error, MaybeFuture, Speed, }; use super::{ @@ -388,7 +388,7 @@ impl WindowsInterface { pub(crate) fn make_transfer( self: &Arc, endpoint: u8, - ep_type: EndpointType, + ep_type: TransferType, ) -> TransferHandle { TransferHandle::new(super::TransferData::new(self.clone(), endpoint, ep_type)) } diff --git a/src/platform/windows_winusb/transfer.rs b/src/platform/windows_winusb/transfer.rs index b6134ba..cf3f5f7 100644 --- a/src/platform/windows_winusb/transfer.rs +++ b/src/platform/windows_winusb/transfer.rs @@ -21,8 +21,8 @@ use windows_sys::Win32::{ }; use crate::transfer::{ - notify_completion, Completion, ControlIn, ControlOut, EndpointType, PlatformSubmit, - PlatformTransfer, Recipient, RequestBuffer, ResponseBuffer, TransferError, + notify_completion, Completion, ControlIn, ControlOut, PlatformSubmit, PlatformTransfer, + Recipient, RequestBuffer, ResponseBuffer, TransferError, TransferType, }; #[repr(C)] @@ -38,7 +38,7 @@ pub struct TransferData { buf: *mut u8, capacity: usize, endpoint: u8, - ep_type: EndpointType, + ep_type: TransferType, submit_error: Option, } @@ -48,7 +48,7 @@ impl TransferData { pub(crate) fn new( interface: std::sync::Arc, endpoint: u8, - ep_type: EndpointType, + ep_type: TransferType, ) -> TransferData { TransferData { interface, @@ -225,7 +225,7 @@ impl PlatformSubmit for TransferData { impl PlatformSubmit for TransferData { unsafe fn submit(&mut self, data: ControlIn, user_data: *mut c_void) { assert_eq!(self.endpoint, 0); - assert_eq!(self.ep_type, EndpointType::Control); + assert_eq!(self.ep_type, TransferType::Control); if data.recipient == Recipient::Interface && data.index as u8 != self.interface.interface_number @@ -274,7 +274,7 @@ impl PlatformSubmit for TransferData { impl PlatformSubmit> for TransferData { unsafe fn submit(&mut self, data: ControlOut, user_data: *mut c_void) { assert_eq!(self.endpoint, 0); - assert_eq!(self.ep_type, EndpointType::Control); + assert_eq!(self.ep_type, TransferType::Control); if data.recipient == Recipient::Interface && data.index as u8 != self.interface.interface_number diff --git a/src/transfer/mod.rs b/src/transfer/mod.rs index 9aa3d1b..14e100a 100644 --- a/src/transfer/mod.rs +++ b/src/transfer/mod.rs @@ -32,7 +32,7 @@ pub(crate) use internal::{ /// Endpoint type. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[allow(dead_code)] -pub enum EndpointType { +pub enum TransferType { /// Control endpoint. Control = 0, diff --git a/src/transfer/queue.rs b/src/transfer/queue.rs index ebdd013..8c0f22e 100644 --- a/src/transfer/queue.rs +++ b/src/transfer/queue.rs @@ -8,7 +8,7 @@ use std::{ use crate::{platform, Error, MaybeFuture}; -use super::{Completion, EndpointType, PlatformSubmit, TransferHandle, TransferRequest}; +use super::{Completion, PlatformSubmit, TransferHandle, TransferRequest, TransferType}; /// Manages a stream of transfers on an endpoint. /// @@ -113,7 +113,7 @@ use super::{Completion, EndpointType, PlatformSubmit, TransferHandle, TransferRe pub struct Queue { interface: Arc, endpoint: u8, - endpoint_type: EndpointType, + endpoint_type: TransferType, /// A queue of pending transfers, expected to complete in order pending: VecDeque>, @@ -132,7 +132,7 @@ where pub(crate) fn new( interface: Arc, endpoint: u8, - endpoint_type: EndpointType, + endpoint_type: TransferType, ) -> Queue { Queue { interface, From 1508051e1a3625bc1995610f7f569dbc3b5e01b5 Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Wed, 19 Feb 2025 16:39:16 -0700 Subject: [PATCH 5/8] Track interface alt setting --- src/device.rs | 11 +++++++++++ src/platform/linux_usbfs/device.rs | 24 ++++++++++++++++++------ src/platform/macos_iokit/device.rs | 24 ++++++++++++++++++++---- src/platform/windows_winusb/device.rs | 17 +++++++++++++++++ 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/device.rs b/src/device.rs index ed5da15..36fdcd6 100644 --- a/src/device.rs +++ b/src/device.rs @@ -391,6 +391,11 @@ impl Interface { self.backend.clone().set_alt_setting(alt_setting) } + /// Get the current alternate setting of this interface. + pub fn get_alt_setting(&self) -> u8 { + self.backend.get_alt_setting() + } + /// Synchronously perform a single **IN (device-to-host)** transfer on the default **control** endpoint. /// /// ### Platform-specific notes @@ -609,6 +614,12 @@ impl Interface { .flat_map(|i| i.interface_alt_settings()) .filter(|g| g.interface_number() == self.backend.interface_number) } + + /// Get the interface descriptor for the current alternate setting. + pub fn descriptor(&self) -> Option { + self.descriptors() + .find(|i| i.alternate_setting() == self.get_alt_setting()) + } } #[test] diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 3edd0c1..95ad0de 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -1,4 +1,5 @@ use std::io::{ErrorKind, Seek}; +use std::sync::Mutex; use std::{ffi::c_void, time::Duration}; use std::{ fs::File, @@ -299,6 +300,7 @@ impl LinuxDevice { device: self, interface_number, reattach: false, + state: Mutex::new(Default::default()), }))) }) } @@ -317,6 +319,7 @@ impl LinuxDevice { device: self, interface_number, reattach: true, + state: Mutex::new(Default::default()), }))) }) } @@ -461,7 +464,13 @@ impl Drop for LinuxDevice { pub(crate) struct LinuxInterface { pub(crate) interface_number: u8, pub(crate) device: Arc, - pub(crate) reattach: bool, + reattach: bool, + state: Mutex, +} + +#[derive(Default)] +struct InterfaceState { + alt_setting: u8, } impl LinuxInterface { @@ -496,20 +505,23 @@ impl LinuxInterface { self.device.control_out_blocking(control, data, timeout) } + pub fn get_alt_setting(&self) -> u8 { + self.state.lock().unwrap().alt_setting + } + pub fn set_alt_setting( self: Arc, alt_setting: u8, ) -> impl MaybeFuture> { Blocking::new(move || { + let mut state = self.state.lock().unwrap(); debug!( "Set interface {} alt setting to {alt_setting}", self.interface_number ); - Ok(usbfs::set_interface( - &self.device.fd, - self.interface_number, - alt_setting, - )?) + usbfs::set_interface(&self.device.fd, self.interface_number, alt_setting)?; + state.alt_setting = alt_setting; + Ok(()) }) } diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index 9100fcb..67df1f4 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -12,7 +12,7 @@ use std::{ use log::{debug, error}; use crate::{ - descriptors::DeviceDescriptor, + descriptors::{ConfigurationDescriptor, DeviceDescriptor}, maybe_future::blocking::Blocking, transfer::{Control, Direction, TransferError, TransferHandle, TransferType}, DeviceInfo, Error, MaybeFuture, Speed, @@ -112,9 +112,13 @@ impl MacDevice { self.active_config.load(Ordering::SeqCst) } - pub(crate) fn configuration_descriptors(&self) -> impl Iterator { + pub(crate) fn configuration_descriptors( + &self, + ) -> impl Iterator { let num_configs = self.device.get_number_of_configurations().unwrap_or(0); - (0..num_configs).flat_map(|i| self.device.get_configuration_descriptor(i).ok()) + (0..num_configs) + .flat_map(|i| self.device.get_configuration_descriptor(i).ok()) + .flat_map(ConfigurationDescriptor::new) } fn require_open_exclusive(&self) -> Result<(), Error> { @@ -255,6 +259,7 @@ impl MacDevice { interface_number, interface, endpoints: Mutex::new(endpoints), + state: Mutex::new(InterfaceState::default()), _event_registration, }))) }) @@ -284,9 +289,14 @@ pub(crate) struct MacInterface { _event_registration: EventRegistration, pub(crate) interface: IoKitInterface, pub(crate) device: Arc, - /// Map from address to a structure that contains the `pipe_ref` used by iokit pub(crate) endpoints: Mutex>, + state: Mutex, +} + +#[derive(Default)] +struct InterfaceState { + alt_setting: u8, } impl MacInterface { @@ -337,6 +347,7 @@ impl MacInterface { alt_setting: u8, ) -> impl MaybeFuture> { Blocking::new(move || { + let mut state = self.state.lock().unwrap(); debug!( "Set interface {} alt setting to {alt_setting}", self.interface_number @@ -353,11 +364,16 @@ impl MacInterface { *endpoints = self.interface.endpoints()?; debug!("Found endpoints: {endpoints:?}"); + state.alt_setting = alt_setting; Ok(()) }) } + pub fn get_alt_setting(&self) -> u8 { + self.state.lock().unwrap().alt_setting + } + pub fn clear_halt( self: Arc, endpoint: u8, diff --git a/src/platform/windows_winusb/device.rs b/src/platform/windows_winusb/device.rs index 54ce107..a3c7c1a 100644 --- a/src/platform/windows_winusb/device.rs +++ b/src/platform/windows_winusb/device.rs @@ -321,6 +321,7 @@ impl WinusbFileHandle { interface_number, first_interface_number: self.first_interface, winusb_handle, + state: Mutex::new(InterfaceState::default()), })) } } @@ -343,6 +344,12 @@ pub(crate) struct WindowsInterface { pub(crate) first_interface_number: u8, pub(crate) interface_number: u8, pub(crate) winusb_handle: WINUSB_INTERFACE_HANDLE, + state: Mutex, +} + +#[derive(Default)] +struct InterfaceState { + alt_setting: u8, } unsafe impl Send for WindowsInterface {} @@ -509,8 +516,14 @@ impl WindowsInterface { alt_setting: u8, ) -> impl MaybeFuture> { Blocking::new(move || unsafe { + let mut state = self.state.lock().unwrap(); let r = WinUsb_SetCurrentAlternateSetting(self.winusb_handle, alt_setting.into()); if r == TRUE { + debug!( + "Set interface {} alt setting to {alt_setting}", + self.interface_number + ); + state.alt_setting = alt_setting; Ok(()) } else { Err(io::Error::last_os_error()) @@ -518,6 +531,10 @@ impl WindowsInterface { }) } + pub fn get_alt_setting(&self) -> u8 { + self.state.lock().unwrap().alt_setting + } + pub fn clear_halt( self: Arc, endpoint: u8, From 99eaf768fd02d1e35a5b88cf4b5413432325b7e2 Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Wed, 19 Feb 2025 16:55:16 -0700 Subject: [PATCH 6/8] Refactor Direction --- src/descriptors.rs | 5 +---- src/transfer/control.rs | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/descriptors.rs b/src/descriptors.rs index f32a9d2..17dec44 100644 --- a/src/descriptors.rs +++ b/src/descriptors.rs @@ -633,10 +633,7 @@ impl<'a> EndpointDescriptor<'a> { /// Get the endpoint's direction. pub fn direction(&self) -> Direction { - match self.address() & 0x80 { - 0 => Direction::Out, - _ => Direction::In, - } + Direction::from_address(self.address()) } /// Get the endpoint's transfer type. diff --git a/src/transfer/control.rs b/src/transfer/control.rs index 710cc2d..feea377 100644 --- a/src/transfer/control.rs +++ b/src/transfer/control.rs @@ -8,7 +8,17 @@ pub enum Direction { Out = 0, /// Device to host - In = 1, + In = 0x80, +} + +impl Direction { + pub(crate) fn from_address(addr: u8) -> Direction { + match addr & Self::MASK { + 0 => Self::Out, + _ => Self::In, + } + } + pub(crate) const MASK: u8 = 0x80; } /// Specification defining the request. @@ -200,10 +210,14 @@ fn pack_setup( ] } -fn request_type(direction: Direction, control_type: ControlType, recipient: Recipient) -> u8 { - ((direction as u8) << 7) | ((control_type as u8) << 5) | (recipient as u8) -} - impl TransferRequest for ControlIn { type Response = Vec; } + +pub(crate) fn request_type( + direction: Direction, + control_type: ControlType, + recipient: Recipient, +) -> u8 { + (direction as u8) | ((control_type as u8) << 5) | (recipient as u8) +} From 8dfb3e5f1d8b6274d12aa4e02b4c8224cc9738c1 Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Sat, 1 Feb 2025 19:33:37 -0700 Subject: [PATCH 7/8] linux: Refactor events slab --- src/platform/linux_usbfs/device.rs | 55 ++++++------- src/platform/linux_usbfs/events.rs | 120 +++++++++++++++++------------ 2 files changed, 97 insertions(+), 78 deletions(-) diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 95ad0de..3a0585e 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -1,5 +1,5 @@ use std::io::{ErrorKind, Seek}; -use std::sync::Mutex; +use std::sync::{Mutex, Weak}; use std::{ffi::c_void, time::Duration}; use std::{ fs::File, @@ -20,6 +20,7 @@ use rustix::{ fs::{Mode, OFlags}, io::Errno, }; +use slab::Slab; use super::{ errno_to_transfer_error, events, @@ -28,7 +29,6 @@ use super::{ }; use crate::descriptors::{ConfigurationDescriptor, DeviceDescriptor}; use crate::maybe_future::{blocking::Blocking, MaybeFuture}; -use crate::platform::linux_usbfs::events::Watch; use crate::transfer::{ControlType, Recipient}; use crate::{ descriptors::{parse_concatenated_config_descriptors, DESCRIPTOR_LEN_DEVICE}, @@ -38,6 +38,8 @@ use crate::{ DeviceInfo, Error, Speed, }; +static DEVICES: Mutex>> = Mutex::new(Slab::new()); + pub(crate) struct LinuxDevice { fd: OwnedFd, events_id: usize, @@ -62,12 +64,7 @@ impl LinuxDevice { 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}"))?; - - let inner = Self::create_inner(fd, Some(sysfs_path), Some(active_config)); - if inner.is_ok() { - debug!("Opened device bus={busnum} addr={devnum}",); - } - inner + Self::create_inner(fd, Some(sysfs_path), Some(active_config)) }) } @@ -105,19 +102,8 @@ impl LinuxDevice { Self::get_config(&descriptors, &fd)? }; - // because there's no Arc::try_new_cyclic - let mut events_err = None; let arc = Arc::new_cyclic(|weak| { - let res = events::register( - fd.as_fd(), - Watch::Device(weak.clone()), - epoll::EventFlags::OUT, - ); - let events_id = *res.as_ref().unwrap_or(&usize::MAX); - events_err = res.err(); - if events_err.is_none() { - debug!("Opened device fd={} with id {}", fd.as_raw_fd(), events_id,); - } + let events_id = DEVICES.lock().unwrap().insert(weak.clone()); LinuxDevice { fd, events_id, @@ -127,15 +113,29 @@ impl LinuxDevice { } }); - if let Some(err) = events_err { - error!("Failed to initialize event loop: {err}"); - Err(err) - } else { - Ok(crate::Device::wrap(arc)) + debug!( + "Opened device fd={} with id {}", + arc.fd.as_raw_fd(), + arc.events_id + ); + + events::register_fd( + arc.fd.as_fd(), + events::Tag::Device(arc.events_id), + epoll::EventFlags::OUT, + )?; + + Ok(crate::Device::wrap(arc)) + } + + pub(crate) fn handle_usb_epoll(id: usize) { + let device = DEVICES.lock().unwrap().get(id).and_then(|w| w.upgrade()); + if let Some(device) = device { + device.handle_events(); } } - pub(crate) fn handle_events(&self) { + fn handle_events(&self) { debug!("Handling events for device {}", self.events_id); match usbfs::reap_urb_ndelay(&self.fd) { Ok(urb_ptr) => { @@ -457,7 +457,8 @@ impl LinuxDevice { impl Drop for LinuxDevice { fn drop(&mut self) { debug!("Closing device {}", self.events_id); - events::unregister(self.fd.as_fd(), self.events_id) + events::unregister_fd(self.fd.as_fd()); + DEVICES.lock().unwrap().remove(self.events_id); } } diff --git a/src/platform/linux_usbfs/events.rs b/src/platform/linux_usbfs/events.rs index c717970..ef9d1b0 100644 --- a/src/platform/linux_usbfs/events.rs +++ b/src/platform/linux_usbfs/events.rs @@ -1,16 +1,17 @@ -use atomic_waker::AtomicWaker; -/// Epoll based event loop for Linux. -/// -/// Launches a thread when opening the first device that polls -/// for events on usbfs devices and arbitrary file descriptors -/// (used for udev hotplug). -/// -/// ### Why not share an event loop with `tokio` or `async-io`? -/// -/// This event loop will call USBFS_REAP_URB on the event thread and -/// dispatch to the transfer's waker directly. Since all USB transfers -/// on a device use the same file descriptor, putting USB-specific -/// dispatch in the event loop avoids additonal synchronization. +//! Epoll based event loop for Linux. +//! +//! Launches a thread when opening the first device that polls +//! for events on usbfs devices and arbitrary file descriptors +//! (used for udev hotplug). +//! +//! ### Why not share an event loop with `tokio` or `async-io`? +//! +//! This event loop will call USBFS_REAP_URB on the event thread and +//! dispatch to the transfer's waker directly. Since all USB transfers +//! on a device use the same file descriptor, putting USB-specific +//! dispatch in the event loop avoids additonal synchronization. + +use crate::Error; use once_cell::sync::OnceCell; use rustix::{ event::epoll::{self, EventData, EventFlags}, @@ -20,42 +21,63 @@ use rustix::{ use slab::Slab; use std::{ io, - sync::{Arc, Mutex, Weak}, + sync::{Arc, Mutex}, task::Waker, thread, }; -use crate::Error; +use atomic_waker::AtomicWaker; use super::Device; static EPOLL_FD: OnceCell = OnceCell::new(); -static WATCHES: Mutex> = Mutex::new(Slab::new()); -pub(super) enum Watch { - Device(Weak), - Fd(Arc), +pub(crate) enum Tag { + Device(usize), + Waker(usize), } -pub(super) fn register(fd: BorrowedFd, watch: Watch, flags: EventFlags) -> Result { +impl Tag { + const DEVICE: u64 = 1; + const WAKER: u64 = 3; + + fn as_event_data(&self) -> EventData { + let (tag, id) = match *self { + Tag::Device(id) => (Self::DEVICE, id), + Tag::Waker(id) => (Self::WAKER, id), + }; + EventData::new_u64((id as u64) << 3 | tag) + } + + fn from_event_data(data: EventData) -> Self { + let id = (data.u64() >> 3) as usize; + let tag = data.u64() & 0b111; + match (tag, id as usize) { + (Self::DEVICE, id) => Tag::Device(id), + (Self::WAKER, id) => Tag::Waker(id), + _ => panic!("Invalid event data"), + } + } +} + +pub(super) fn register_fd(fd: BorrowedFd, tag: Tag, flags: EventFlags) -> Result<(), Error> { let mut start_thread = false; let epoll_fd = EPOLL_FD.get_or_try_init(|| { start_thread = true; - epoll::create(epoll::CreateFlags::CLOEXEC) + epoll::create(epoll::CreateFlags::CLOEXEC).inspect_err(|e| { + log::error!("Failed to initialize epoll: {e}"); + }) })?; - let id = { - let mut watches = WATCHES.lock().unwrap(); - watches.insert(watch) - }; - if start_thread { thread::spawn(event_loop); } - let data = EventData::new_u64(id as u64); - epoll::add(epoll_fd, fd, data, flags)?; - Ok(id) + epoll::add(epoll_fd, fd, tag.as_event_data(), flags).inspect_err(|e| { + log::error!("Failed to add epoll watch: {e}"); + })?; + + Ok(()) } pub(super) fn unregister_fd(fd: BorrowedFd) { @@ -63,39 +85,27 @@ pub(super) fn unregister_fd(fd: BorrowedFd) { epoll::delete(epoll_fd, fd).ok(); } -pub(super) fn unregister(fd: BorrowedFd, events_id: usize) { - let epoll_fd = EPOLL_FD.get().unwrap(); - epoll::delete(epoll_fd, fd).ok(); - WATCHES.lock().unwrap().remove(events_id); -} - fn event_loop() { let epoll_fd = EPOLL_FD.get().unwrap(); let mut event_list = epoll::EventVec::with_capacity(4); loop { retry_on_intr(|| epoll::wait(epoll_fd, &mut event_list, -1)).unwrap(); for event in &event_list { - let key = event.data.u64() as usize; - log::trace!("event on {key}"); - let lock = WATCHES.lock().unwrap(); - let Some(watch) = lock.get(key) else { continue }; - - match watch { - Watch::Device(w) => { - if let Some(device) = w.upgrade() { - drop(lock); - device.handle_events(); - // `device` gets dropped here. if it was the last reference, the LinuxDevice will be dropped. - // That will unregister its fd, so it's important that WATCHES is unlocked here, or we'd deadlock. + match Tag::from_event_data(event.data) { + Tag::Device(id) => Device::handle_usb_epoll(id), + Tag::Waker(id) => { + if let Some(waker) = WAKERS.lock().unwrap().get(id) { + waker.wake(); } } - Watch::Fd(waker) => waker.wake(), } } } } -pub(crate) struct Async { +static WAKERS: Mutex>> = Mutex::new(Slab::new()); + +pub(crate) struct Async { pub(crate) inner: T, waker: Arc, id: usize, @@ -104,7 +114,8 @@ pub(crate) struct Async { impl Async { pub fn new(inner: T) -> Result { let waker = Arc::new(AtomicWaker::new()); - let id = register(inner.as_fd(), Watch::Fd(waker.clone()), EventFlags::empty())?; + let id = WAKERS.lock().unwrap().insert(waker.clone()); + register_fd(inner.as_fd(), Tag::Waker(id), EventFlags::empty())?; Ok(Async { inner, id, waker }) } @@ -114,9 +125,16 @@ impl Async { epoll::modify( epoll_fd, self.inner.as_fd(), - EventData::new_u64(self.id as u64), + Tag::Waker(self.id).as_event_data(), EventFlags::ONESHOT | EventFlags::IN, )?; Ok(()) } } + +impl Drop for Async { + fn drop(&mut self) { + unregister_fd(self.inner.as_fd()); + WAKERS.lock().unwrap().remove(self.id); + } +} From 5e866af4e462f182dc1db9b88c92c08eb9c584a9 Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Sat, 22 Feb 2025 14:46:41 -0700 Subject: [PATCH 8/8] Add MaybeFuture::map --- src/device.rs | 14 ++-- src/maybe_future.rs | 61 ++++++++++++++++- src/platform/linux_usbfs/device.rs | 22 +++--- src/platform/macos_iokit/device.rs | 14 ++-- src/platform/windows_winusb/device.rs | 98 ++++++++++++--------------- 5 files changed, 134 insertions(+), 75 deletions(-) diff --git a/src/device.rs b/src/device.rs index 36fdcd6..9eff6ad 100644 --- a/src/device.rs +++ b/src/device.rs @@ -46,13 +46,13 @@ impl Device { pub(crate) fn open( d: &DeviceInfo, ) -> impl MaybeFuture> { - platform::Device::from_device_info(d) + platform::Device::from_device_info(d).map(|d| d.map(Device::wrap)) } /// Wraps a device that is already open. #[cfg(any(target_os = "android", target_os = "linux"))] pub fn from_fd(fd: std::os::fd::OwnedFd) -> impl MaybeFuture> { - platform::Device::from_fd(fd) + platform::Device::from_fd(fd).map(|d| d.map(Device::wrap)) } /// Open an interface of the device and claim it for exclusive use. @@ -60,7 +60,10 @@ impl Device { &self, interface: u8, ) -> impl MaybeFuture> { - self.backend.clone().claim_interface(interface) + self.backend + .clone() + .claim_interface(interface) + .map(|i| i.map(Interface::wrap)) } /// Detach kernel drivers and open an interface of the device and claim it for exclusive use. @@ -72,7 +75,10 @@ impl Device { &self, interface: u8, ) -> impl MaybeFuture> { - self.backend.clone().detach_and_claim_interface(interface) + self.backend + .clone() + .detach_and_claim_interface(interface) + .map(|i| i.map(Interface::wrap)) } /// Detach kernel drivers for the specified interface. diff --git a/src/maybe_future.rs b/src/maybe_future.rs index 75d645e..236bd7a 100644 --- a/src/maybe_future.rs +++ b/src/maybe_future.rs @@ -1,4 +1,8 @@ -use std::future::IntoFuture; +use std::{ + future::{Future, IntoFuture}, + pin::Pin, + task::{Context, Poll}, +}; /// IO that may be performed synchronously or asynchronously. /// @@ -8,6 +12,17 @@ pub trait MaybeFuture: IntoFuture { /// Block waiting for the action to complete #[cfg(not(target_arch = "wasm32"))] fn wait(self) -> Self::Output; + + /// Apply a function to the output. + fn map R + Unpin, R>(self, f: T) -> Map + where + Self: Sized, + { + Map { + wrapped: self, + func: f, + } + } } #[cfg(any( @@ -73,3 +88,47 @@ impl MaybeFuture for Ready { self.0 } } + +pub struct Map { + wrapped: F, + func: T, +} + +impl R, R> IntoFuture for Map { + type Output = R; + type IntoFuture = MapFut; + + fn into_future(self) -> Self::IntoFuture { + MapFut { + wrapped: self.wrapped.into_future(), + func: Some(self.func), + } + } +} + +impl R, R> MaybeFuture for Map { + fn wait(self) -> Self::Output { + (self.func)(self.wrapped.wait()) + } +} + +pub struct MapFut { + wrapped: F, + func: Option, +} + +impl R, R> Future for MapFut { + type Output = R; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: structural pin projection: `self.wrapped` is always pinned. + let wrapped = unsafe { self.as_mut().map_unchecked_mut(|s| &mut s.wrapped) }; + + Future::poll(wrapped, cx).map(|output| { + // SAFETY: `self.func` is never pinned. + let func = unsafe { &mut self.as_mut().get_unchecked_mut().func }; + + (func.take().expect("polled after completion"))(output) + }) + } +} diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 3a0585e..e55d2fc 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -54,7 +54,7 @@ pub(crate) struct LinuxDevice { impl LinuxDevice { pub(crate) fn from_device_info( d: &DeviceInfo, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { let busnum = d.busnum(); let devnum = d.device_address(); let sysfs_path = d.path.clone(); @@ -68,7 +68,9 @@ impl LinuxDevice { }) } - pub(crate) fn from_fd(fd: OwnedFd) -> impl MaybeFuture> { + pub(crate) fn from_fd( + fd: OwnedFd, + ) -> impl MaybeFuture, Error>> { Blocking::new(move || { debug!("Wrapping fd {} as usbfs device", fd.as_raw_fd()); Self::create_inner(fd, None, None) @@ -79,7 +81,7 @@ impl LinuxDevice { fd: OwnedFd, sysfs: Option, active_config: Option, - ) -> Result { + ) -> Result, Error> { let descriptors = { let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) }; // NOTE: Seek required on android @@ -125,7 +127,7 @@ impl LinuxDevice { epoll::EventFlags::OUT, )?; - Ok(crate::Device::wrap(arc)) + Ok(arc) } pub(crate) fn handle_usb_epoll(id: usize) { @@ -284,7 +286,7 @@ impl LinuxDevice { pub(crate) fn claim_interface( self: Arc, interface_number: u8, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { Blocking::new(move || { usbfs::claim_interface(&self.fd, interface_number).inspect_err(|e| { warn!( @@ -296,31 +298,31 @@ impl LinuxDevice { "Claimed interface {interface_number} on device id {dev}", dev = self.events_id ); - Ok(crate::Interface::wrap(Arc::new(LinuxInterface { + Ok(Arc::new(LinuxInterface { device: self, interface_number, reattach: false, state: Mutex::new(Default::default()), - }))) + })) }) } pub(crate) fn detach_and_claim_interface( self: Arc, interface_number: u8, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { Blocking::new(move || { usbfs::detach_and_claim_interface(&self.fd, interface_number)?; debug!( "Detached and claimed interface {interface_number} on device id {dev}", dev = self.events_id ); - Ok(crate::Interface::wrap(Arc::new(LinuxInterface { + Ok(Arc::new(LinuxInterface { device: self, interface_number, reattach: true, state: Mutex::new(Default::default()), - }))) + })) }) } diff --git a/src/platform/macos_iokit/device.rs b/src/platform/macos_iokit/device.rs index 67df1f4..0591bf4 100644 --- a/src/platform/macos_iokit/device.rs +++ b/src/platform/macos_iokit/device.rs @@ -53,7 +53,7 @@ fn guess_active_config(dev: &IoKitDevice) -> Option { impl MacDevice { pub(crate) fn from_device_info( d: &DeviceInfo, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { let registry_id = d.registry_id; let speed = d.speed; Blocking::new(move || { @@ -88,7 +88,7 @@ impl MacDevice { res.unwrap_or(0) }; - Ok(crate::Device::wrap(Arc::new(MacDevice { + Ok(Arc::new(MacDevice { _event_registration, device, device_descriptor, @@ -96,7 +96,7 @@ impl MacDevice { active_config: AtomicU8::new(active_config), is_open_exclusive: Mutex::new(opened), claimed_interfaces: AtomicUsize::new(0), - }))) + })) }) } @@ -236,7 +236,7 @@ impl MacDevice { pub(crate) fn claim_interface( self: Arc, interface_number: u8, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { Blocking::new(move || { let intf_service = self .device @@ -254,21 +254,21 @@ impl MacDevice { self.claimed_interfaces.fetch_add(1, Ordering::Acquire); - Ok(crate::Interface::wrap(Arc::new(MacInterface { + Ok(Arc::new(MacInterface { device: self.clone(), interface_number, interface, endpoints: Mutex::new(endpoints), state: Mutex::new(InterfaceState::default()), _event_registration, - }))) + })) }) } pub(crate) fn detach_and_claim_interface( self: Arc, interface: u8, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { self.claim_interface(interface) } } diff --git a/src/platform/windows_winusb/device.rs b/src/platform/windows_winusb/device.rs index a3c7c1a..15e466e 100644 --- a/src/platform/windows_winusb/device.rs +++ b/src/platform/windows_winusb/device.rs @@ -53,7 +53,7 @@ pub(crate) struct WindowsDevice { impl WindowsDevice { pub(crate) fn from_device_info( d: &DeviceInfo, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { let instance_id = d.instance_id.clone(); let devinst = d.devinst; Blocking::new(move || { @@ -85,14 +85,14 @@ impl WindowsDevice { }) .collect(); - Ok(crate::Device::wrap(Arc::new(WindowsDevice { + Ok(Arc::new(WindowsDevice { device_descriptor, config_descriptors, speed: connection_info.speed, active_config: connection_info.active_config, devinst: devinst, handles: Mutex::new(BTreeMap::new()), - }))) + })) }) } @@ -145,63 +145,55 @@ impl WindowsDevice { pub(crate) fn claim_interface( self: Arc, interface_number: u8, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { Blocking::new(move || { - self.claim_interface_blocking(interface_number) - .map(crate::Interface::wrap) + let driver = get_driver_name(self.devinst); + + let mut handles = self.handles.lock().unwrap(); + + if driver.eq_ignore_ascii_case("winusb") { + match handles.entry(0) { + Entry::Occupied(mut e) => e.get_mut().claim_interface(&self, interface_number), + Entry::Vacant(e) => { + let path = get_winusb_device_path(self.devinst)?; + let mut handle = WinusbFileHandle::new(&path, 0)?; + let intf = handle.claim_interface(&self, interface_number)?; + e.insert(handle); + Ok(intf) + } + } + } else if driver.eq_ignore_ascii_case("usbccgp") { + let (first_interface, child_dev) = + find_usbccgp_child(self.devinst, interface_number) + .ok_or_else(|| Error::new(ErrorKind::NotFound, "Interface not found"))?; + + if first_interface != interface_number { + debug!("Guessing that interface {interface_number} is an associated interface of {first_interface}"); + } + + match handles.entry(first_interface) { + Entry::Occupied(mut e) => e.get_mut().claim_interface(&self, interface_number), + Entry::Vacant(e) => { + let path = get_usbccgp_winusb_device_path(child_dev)?; + let mut handle = WinusbFileHandle::new(&path, first_interface)?; + let intf = handle.claim_interface(&self, interface_number)?; + e.insert(handle); + Ok(intf) + } + } + } else { + Err(Error::new( + ErrorKind::Unsupported, + format!("Device driver is {driver:?}, not WinUSB or USBCCGP"), + )) + } }) } - fn claim_interface_blocking( - self: &Arc, - interface_number: u8, - ) -> Result, Error> { - let driver = get_driver_name(self.devinst); - - let mut handles = self.handles.lock().unwrap(); - - if driver.eq_ignore_ascii_case("winusb") { - match handles.entry(0) { - Entry::Occupied(mut e) => e.get_mut().claim_interface(self, interface_number), - Entry::Vacant(e) => { - let path = get_winusb_device_path(self.devinst)?; - let mut handle = WinusbFileHandle::new(&path, 0)?; - let intf = handle.claim_interface(self, interface_number)?; - e.insert(handle); - Ok(intf) - } - } - } else if driver.eq_ignore_ascii_case("usbccgp") { - let (first_interface, child_dev) = - find_usbccgp_child(self.devinst, interface_number) - .ok_or_else(|| Error::new(ErrorKind::NotFound, "Interface not found"))?; - - if first_interface != interface_number { - debug!("Guessing that interface {interface_number} is an associated interface of {first_interface}"); - } - - match handles.entry(first_interface) { - Entry::Occupied(mut e) => e.get_mut().claim_interface(self, interface_number), - Entry::Vacant(e) => { - let path = get_usbccgp_winusb_device_path(child_dev)?; - let mut handle = WinusbFileHandle::new(&path, first_interface)?; - let intf = handle.claim_interface(self, interface_number)?; - e.insert(handle); - Ok(intf) - } - } - } else { - Err(Error::new( - ErrorKind::Unsupported, - format!("Device driver is {driver:?}, not WinUSB or USBCCGP"), - )) - } - } - pub(crate) fn detach_and_claim_interface( self: Arc, interface: u8, - ) -> impl MaybeFuture> { + ) -> impl MaybeFuture, Error>> { self.claim_interface(interface) } }