diff --git a/src/enumeration.rs b/src/enumeration.rs index 49c6e26..25f157a 100644 --- a/src/enumeration.rs +++ b/src/enumeration.rs @@ -60,6 +60,7 @@ pub struct DeviceInfo { pub(crate) product_id: u16, pub(crate) device_version: u16, + pub(crate) usb_version: u16, pub(crate) class: u8, pub(crate) subclass: u8, pub(crate) protocol: u8, @@ -200,6 +201,12 @@ impl DeviceInfo { self.device_version } + /// Encoded version of the USB specification, from the `bcdUSB` device descriptor field. + #[doc(alias = "bcdUSB")] + pub fn usb_version(&self) -> u16 { + self.usb_version + } + /// Code identifying the standard device class, from the `bDeviceClass` device descriptor field. /// /// `0x00`: specified at the interface level.\ @@ -296,6 +303,7 @@ impl std::fmt::Debug for DeviceInfo { "device_version", &format_args!("0x{:04X}", self.device_version), ) + .field("usb_version", &format_args!("0x{:04X}", self.usb_version)) .field("class", &format_args!("0x{:02X}", self.class)) .field("subclass", &format_args!("0x{:02X}", self.subclass)) .field("protocol", &format_args!("0x{:02X}", self.protocol)) diff --git a/src/platform/linux_usbfs/enumeration.rs b/src/platform/linux_usbfs/enumeration.rs index ec5fc91..4d18a87 100644 --- a/src/platform/linux_usbfs/enumeration.rs +++ b/src/platform/linux_usbfs/enumeration.rs @@ -1,12 +1,11 @@ +use log::debug; +use log::warn; use std::fs; use std::io; use std::num::ParseIntError; use std::path::PathBuf; use std::str::FromStr; -use log::debug; -use log::warn; - use crate::enumeration::InterfaceInfo; use crate::maybe_future::{MaybeFuture, Ready}; use crate::{BusInfo, DeviceInfo, Error, Speed, UsbControllerType}; @@ -74,6 +73,16 @@ impl SysfsPath { self.parse_attr(attr, |s| T::from_hex_str(s.strip_prefix("0x").unwrap_or(s))) } + fn read_version_attr(&self, attr: &str) -> Result { + // in sysfs bcdUSB is parsed to `x.yz`, so we have to parse it back. It should be coded in bcd, so we can treat parts as hex. + self.parse_attr(attr, |s| { + s.to_owned() + .split('.') + .map(|x| u16::from_hex_str(x)) + .fold(Ok::(0), |a, b| Ok((a? << 8) + b?)) + }) + } + pub(crate) fn readlink_attr_filename(&self, attr: &str) -> Result { self.readlink_attr(attr).map(|p| { p.file_name() @@ -214,6 +223,7 @@ pub fn probe_device(path: SysfsPath) -> Result { vendor_id: path.read_attr_hex("idVendor")?, product_id: path.read_attr_hex("idProduct")?, device_version: path.read_attr_hex("bcdDevice")?, + usb_version: path.read_version_attr("version")?, class: path.read_attr_hex("bDeviceClass")?, subclass: path.read_attr_hex("bDeviceSubClass")?, protocol: path.read_attr_hex("bDeviceProtocol")?, diff --git a/src/platform/macos_iokit/enumeration.rs b/src/platform/macos_iokit/enumeration.rs index 958fcc4..4e3616f 100644 --- a/src/platform/macos_iokit/enumeration.rs +++ b/src/platform/macos_iokit/enumeration.rs @@ -124,6 +124,7 @@ pub(crate) fn probe_device(device: IoService) -> Option { vendor_id: get_integer_property(&device, "idVendor")? as u16, product_id: get_integer_property(&device, "idProduct")? as u16, device_version: get_integer_property(&device, "bcdDevice")? as u16, + usb_version: get_integer_property(&device, "bcdUSB")? as u16, class: get_integer_property(&device, "bDeviceClass")? as u8, subclass: get_integer_property(&device, "bDeviceSubClass")? as u8, protocol: get_integer_property(&device, "bDeviceProtocol")? as u8, diff --git a/src/platform/windows_winusb/enumeration.rs b/src/platform/windows_winusb/enumeration.rs index fd7b092..20b5a7b 100644 --- a/src/platform/windows_winusb/enumeration.rs +++ b/src/platform/windows_winusb/enumeration.rs @@ -142,6 +142,7 @@ pub fn probe_device(devinst: DevInst) -> Option { vendor_id: info.device_desc.idVendor, product_id: info.device_desc.idProduct, device_version: info.device_desc.bcdDevice, + usb_version: info.device_desc.bcdUSB, class: info.device_desc.bDeviceClass, subclass: info.device_desc.bDeviceSubClass, protocol: info.device_desc.bDeviceProtocol,