#[cfg(target_os = "windows")]
use std::ffi::{OsStr, OsString};
#[cfg(any(target_os = "linux"))]
use crate::platform::SysfsPath;
use crate::{Device, Error, MaybeFuture};
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct DeviceId(pub(crate) crate::platform::DeviceId);
#[derive(Clone)]
pub struct DeviceInfo {
#[cfg(target_os = "linux")]
pub(crate) path: SysfsPath,
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) busnum: u8,
#[cfg(target_os = "windows")]
pub(crate) instance_id: OsString,
#[cfg(target_os = "windows")]
pub(crate) location_paths: Vec<OsString>,
#[cfg(target_os = "windows")]
pub(crate) parent_instance_id: OsString,
#[cfg(target_os = "windows")]
pub(crate) port_number: u32,
#[cfg(target_os = "windows")]
pub(crate) devinst: crate::platform::DevInst,
#[cfg(target_os = "windows")]
pub(crate) driver: Option<String>,
#[cfg(target_os = "macos")]
pub(crate) registry_id: u64,
#[cfg(target_os = "macos")]
pub(crate) location_id: u32,
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub(crate) bus_id: String,
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "windows",
target_os = "android"
))]
pub(crate) device_address: u8,
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub(crate) port_chain: Vec<u8>,
pub(crate) vendor_id: u16,
pub(crate) product_id: u16,
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub(crate) device_version: u16,
pub(crate) usb_version: u16,
pub(crate) class: u8,
pub(crate) subclass: u8,
pub(crate) protocol: u8,
pub(crate) speed: Option<Speed>,
pub(crate) manufacturer_string: Option<String>,
pub(crate) product_string: Option<String>,
pub(crate) serial_number: Option<String>,
pub(crate) interfaces: Vec<InterfaceInfo>,
}
impl DeviceInfo {
pub fn id(&self) -> DeviceId {
#[cfg(target_os = "windows")]
{
DeviceId(self.devinst)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
{
DeviceId(crate::platform::DeviceId {
bus: self.busnum,
addr: self.device_address,
})
}
#[cfg(target_os = "macos")]
{
DeviceId(self.registry_id)
}
}
#[cfg(target_os = "linux")]
pub fn sysfs_path(&self) -> &std::path::Path {
&self.path.0
}
#[cfg(any(target_os = "linux"))]
pub fn busnum(&self) -> u8 {
self.busnum
}
#[cfg(target_os = "windows")]
pub fn instance_id(&self) -> &OsStr {
&self.instance_id
}
#[cfg(target_os = "windows")]
pub fn location_paths(&self) -> &[OsString] {
&self.location_paths
}
#[cfg(target_os = "windows")]
pub fn parent_instance_id(&self) -> &OsStr {
&self.parent_instance_id
}
#[cfg(target_os = "windows")]
pub fn port_number(&self) -> u32 {
self.port_number
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub fn port_chain(&self) -> &[u8] {
&self.port_chain
}
#[cfg(target_os = "windows")]
pub fn driver(&self) -> Option<&str> {
self.driver.as_deref()
}
#[cfg(target_os = "macos")]
pub fn location_id(&self) -> u32 {
self.location_id
}
#[cfg(target_os = "macos")]
pub fn registry_entry_id(&self) -> u64 {
self.registry_id
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub fn bus_id(&self) -> &str {
&self.bus_id
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub fn device_address(&self) -> u8 {
self.device_address
}
#[doc(alias = "idVendor")]
pub fn vendor_id(&self) -> u16 {
self.vendor_id
}
#[doc(alias = "idProduct")]
pub fn product_id(&self) -> u16 {
self.product_id
}
#[doc(alias = "bcdDevice")]
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub fn device_version(&self) -> u16 {
self.device_version
}
#[doc(alias = "bcdUSB")]
pub fn usb_version(&self) -> u16 {
self.usb_version
}
#[doc(alias = "bDeviceClass")]
pub fn class(&self) -> u8 {
self.class
}
#[doc(alias = "bDeviceSubClass")]
pub fn subclass(&self) -> u8 {
self.subclass
}
#[doc(alias = "bDeviceProtocol")]
pub fn protocol(&self) -> u8 {
self.protocol
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub fn speed(&self) -> Option<Speed> {
self.speed
}
#[doc(alias = "iManufacturer")]
pub fn manufacturer_string(&self) -> Option<&str> {
self.manufacturer_string.as_deref()
}
#[doc(alias = "iProduct")]
pub fn product_string(&self) -> Option<&str> {
self.product_string.as_deref()
}
#[doc(alias = "iSerial")]
pub fn serial_number(&self) -> Option<&str> {
self.serial_number.as_deref()
}
pub fn interfaces(&self) -> impl Iterator<Item = &InterfaceInfo> {
self.interfaces.iter()
}
pub fn open(&self) -> impl MaybeFuture<Output = Result<Device, Error>> {
Device::open(self)
}
}
impl std::fmt::Debug for DeviceInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct("DeviceInfo");
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
s.field("bus_id", &self.bus_id)
.field("device_address", &self.device_address)
.field("port_chain", &format_args!("{:?}", self.port_chain));
s.field("vendor_id", &format_args!("0x{:04X}", self.vendor_id))
.field("product_id", &format_args!("0x{:04X}", self.product_id));
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
s.field(
"device_version",
&format_args!("0x{:04X}", self.device_version),
);
s.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))
.field("speed", &self.speed)
.field("manufacturer_string", &self.manufacturer_string)
.field("product_string", &self.product_string)
.field("serial_number", &self.serial_number);
#[cfg(target_os = "linux")]
{
s.field("sysfs_path", &self.path);
}
#[cfg(target_os = "windows")]
{
s.field("instance_id", &self.instance_id);
s.field("parent_instance_id", &self.parent_instance_id);
s.field("location_paths", &self.location_paths);
s.field("port_number", &self.port_number);
s.field("driver", &self.driver);
}
#[cfg(target_os = "macos")]
{
s.field("location_id", &format_args!("0x{:08X}", self.location_id));
s.field(
"registry_entry_id",
&format_args!("0x{:08X}", self.registry_id),
);
}
s.field("interfaces", &self.interfaces);
s.finish()
}
}
#[derive(Copy, Clone, Eq, PartialOrd, Ord, PartialEq, Hash, Debug)]
#[non_exhaustive]
pub enum Speed {
Low,
Full,
High,
Super,
SuperPlus,
}
impl Speed {
#[allow(dead_code)] pub(crate) fn from_str(s: &str) -> Option<Self> {
match s {
"low" | "1.5" => Some(Speed::Low),
"full" | "12" => Some(Speed::Full),
"high" | "480" => Some(Speed::High),
"super" | "5000" => Some(Speed::Super),
"super+" | "10000" => Some(Speed::SuperPlus),
_ => None,
}
}
}
#[derive(Clone)]
pub struct InterfaceInfo {
pub(crate) interface_number: u8,
pub(crate) class: u8,
pub(crate) subclass: u8,
pub(crate) protocol: u8,
pub(crate) interface_string: Option<String>,
}
impl InterfaceInfo {
pub fn interface_number(&self) -> u8 {
self.interface_number
}
pub fn class(&self) -> u8 {
self.class
}
pub fn subclass(&self) -> u8 {
self.subclass
}
pub fn protocol(&self) -> u8 {
self.protocol
}
pub fn interface_string(&self) -> Option<&str> {
self.interface_string.as_deref()
}
}
impl std::fmt::Debug for InterfaceInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InterfaceInfo")
.field("interface_number", &self.interface_number)
.field("class", &format_args!("0x{:02X}", self.class))
.field("subclass", &format_args!("0x{:02X}", self.subclass))
.field("protocol", &format_args!("0x{:02X}", self.protocol))
.field("interface_string", &self.interface_string)
.finish()
}
}
#[derive(Copy, Clone, Eq, PartialOrd, Ord, PartialEq, Hash, Debug)]
#[non_exhaustive]
pub enum UsbControllerType {
XHCI,
EHCI,
OHCI,
UHCI,
VHCI,
}
impl UsbControllerType {
#[allow(dead_code)] pub(crate) fn from_str(s: &str) -> Option<Self> {
let lower_s = s.to_owned().to_ascii_lowercase();
match lower_s
.find("hci")
.filter(|i| *i > 0)
.and_then(|i| lower_s.as_bytes().get(i - 1))
{
Some(b'x') => Some(UsbControllerType::XHCI),
Some(b'e') => Some(UsbControllerType::EHCI),
Some(b'o') => Some(UsbControllerType::OHCI),
Some(b'v') => Some(UsbControllerType::VHCI),
Some(b'u') => Some(UsbControllerType::UHCI),
_ => None,
}
}
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub struct BusInfo {
#[cfg(any(target_os = "linux"))]
pub(crate) path: SysfsPath,
#[cfg(any(target_os = "linux"))]
pub(crate) root_hub: DeviceInfo,
#[cfg(any(target_os = "linux"))]
pub(crate) busnum: u8,
#[cfg(target_os = "windows")]
pub(crate) instance_id: OsString,
#[cfg(target_os = "windows")]
pub(crate) location_paths: Vec<OsString>,
#[cfg(target_os = "windows")]
pub(crate) devinst: crate::platform::DevInst,
#[cfg(target_os = "windows")]
pub(crate) root_hub_description: String,
#[cfg(target_os = "windows")]
pub(crate) parent_instance_id: OsString,
#[cfg(target_os = "macos")]
pub(crate) registry_id: u64,
#[cfg(target_os = "macos")]
pub(crate) location_id: u32,
#[cfg(target_os = "macos")]
pub(crate) provider_class_name: String,
#[cfg(target_os = "macos")]
pub(crate) class_name: String,
#[cfg(target_os = "macos")]
pub(crate) name: Option<String>,
pub(crate) driver: Option<String>,
pub(crate) bus_id: String,
pub(crate) controller_type: Option<UsbControllerType>,
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
impl BusInfo {
#[cfg(any(target_os = "linux"))]
pub fn sysfs_path(&self) -> &std::path::Path {
&self.path.0
}
#[cfg(any(target_os = "linux"))]
pub fn busnum(&self) -> u8 {
self.busnum
}
#[cfg(any(target_os = "linux"))]
pub fn root_hub(&self) -> &DeviceInfo {
&self.root_hub
}
#[cfg(target_os = "windows")]
pub fn instance_id(&self) -> &OsStr {
&self.instance_id
}
#[cfg(target_os = "windows")]
pub fn parent_instance_id(&self) -> &OsStr {
&self.parent_instance_id
}
#[cfg(target_os = "windows")]
pub fn location_paths(&self) -> &[OsString] {
&self.location_paths
}
#[cfg(target_os = "windows")]
pub fn devinst(&self) -> crate::platform::DevInst {
self.devinst
}
#[cfg(target_os = "macos")]
pub fn location_id(&self) -> u32 {
self.location_id
}
#[cfg(target_os = "macos")]
pub fn registry_entry_id(&self) -> u64 {
self.registry_id
}
#[cfg(target_os = "macos")]
pub fn provider_class_name(&self) -> &str {
&self.provider_class_name
}
#[cfg(target_os = "macos")]
pub fn class_name(&self) -> &str {
&self.class_name
}
#[cfg(target_os = "macos")]
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn driver(&self) -> Option<&str> {
self.driver.as_deref()
}
pub fn bus_id(&self) -> &str {
&self.bus_id
}
pub fn controller_type(&self) -> Option<UsbControllerType> {
self.controller_type
}
pub fn system_name(&self) -> Option<&str> {
#[cfg(any(target_os = "linux"))]
{
self.root_hub.product_string()
}
#[cfg(target_os = "windows")]
{
Some(&self.root_hub_description)
}
#[cfg(target_os = "macos")]
{
self.name.as_deref()
}
}
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
impl std::fmt::Debug for BusInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct("BusInfo");
#[cfg(any(target_os = "linux"))]
{
s.field("sysfs_path", &self.path);
s.field("busnum", &self.busnum);
}
#[cfg(target_os = "windows")]
{
s.field("instance_id", &self.instance_id);
s.field("parent_instance_id", &self.parent_instance_id);
s.field("location_paths", &self.location_paths);
}
#[cfg(target_os = "macos")]
{
s.field("location_id", &format_args!("0x{:08X}", self.location_id));
s.field(
"registry_entry_id",
&format_args!("0x{:08X}", self.registry_id),
);
s.field("class_name", &self.class_name);
s.field("provider_class_name", &self.provider_class_name);
}
s.field("bus_id", &self.bus_id)
.field("system_name", &self.system_name())
.field("controller_type", &self.controller_type)
.field("driver", &self.driver);
s.finish()
}
}