#[cfg(any(docsrs, target_os = "windows"))]
use std::ffi::{OsStr, OsString};
use std::fmt::Debug;
#[cfg(target_os = "linux")]
use crate::platform::SysfsPath;
#[cfg(target_os = "windows")]
use crate::platform::DevInst;
#[cfg(all(docsrs, not(target_os = "windows")))]
struct DevInst();
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: 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",
target_arch = "wasm32"
))]
pub(crate) device_version: u16,
pub(crate) usb_version: u16,
pub(crate) class: u8,
pub(crate) subclass: u8,
pub(crate) protocol: u8,
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "windows",
target_os = "android",
))]
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>,
#[cfg(target_arch = "wasm32")]
pub(crate) device: crate::platform::UsbDevice,
}
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_arch = "wasm32")]
{
DeviceId(crate::platform::DeviceId::from_device(&self.device))
}
}
#[cfg(any(docsrs, target_os = "linux"))]
pub fn sysfs_path(&self) -> &std::path::Path {
&self.path.0
}
#[cfg(any(docsrs, target_os = "linux"))]
pub fn busnum(&self) -> u8 {
self.busnum
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn instance_id(&self) -> &OsStr {
&self.instance_id
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn location_paths(&self) -> &[OsString] {
&self.location_paths
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn parent_instance_id(&self) -> &OsStr {
&self.parent_instance_id
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn port_number(&self) -> u32 {
self.port_number
}
#[cfg(any(
docsrs,
target_os = "linux",
target_os = "macos",
target_os = "windows"
))]
pub fn port_chain(&self) -> &[u8] {
&self.port_chain
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn driver(&self) -> Option<&str> {
self.driver.as_deref()
}
#[cfg(any(docsrs, target_os = "macos"))]
pub fn location_id(&self) -> u32 {
self.location_id
}
#[cfg(any(docsrs, target_os = "macos"))]
pub fn registry_entry_id(&self) -> u64 {
self.registry_id
}
#[cfg(any(
docsrs,
target_os = "linux",
target_os = "macos",
target_os = "windows"
))]
pub fn bus_id(&self) -> &str {
&self.bus_id
}
#[cfg(any(
docsrs,
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(
docsrs,
target_os = "linux",
target_os = "macos",
target_os = "windows",
target_arch = "wasm32"
))]
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(
docsrs,
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 matches(&self, s: &DeviceSelector) -> bool {
s.vendor_id.is_none_or(|id| self.vendor_id == id)
&& s.product_id.is_none_or(|id| self.product_id == id)
&& s.serial_number
.as_ref()
.is_none_or(|s| self.serial_number.as_deref() == Some(s))
&& ((s.class.is_none_or(|c| self.class == c)
&& s.subclass.is_none_or(|s| self.subclass == s)
&& s.protocol.is_none_or(|p| self.protocol == p))
|| self.interfaces().any(|i| {
s.class.is_none_or(|c| i.class == c)
&& s.subclass.is_none_or(|s| i.subclass == s)
&& s.protocol.is_none_or(|p| i.protocol == p)
}))
}
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))
.field("speed", &self.speed);
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",
target_arch = "wasm32"
))]
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("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(
docsrs,
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: 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(
docsrs,
target_os = "linux",
target_os = "macos",
target_os = "windows"
))]
impl BusInfo {
#[cfg(any(docsrs, target_os = "linux"))]
pub fn sysfs_path(&self) -> &std::path::Path {
&self.path.0
}
#[cfg(any(docsrs, target_os = "linux"))]
pub fn busnum(&self) -> u8 {
self.busnum
}
#[cfg(any(docsrs, target_os = "linux"))]
pub fn root_hub(&self) -> &DeviceInfo {
&self.root_hub
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn instance_id(&self) -> &OsStr {
&self.instance_id
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn parent_instance_id(&self) -> &OsStr {
&self.parent_instance_id
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn location_paths(&self) -> &[OsString] {
&self.location_paths
}
#[cfg(any(docsrs, target_os = "windows"))]
pub fn devinst(&self) -> DevInst {
self.devinst
}
#[cfg(any(docsrs, target_os = "macos"))]
pub fn location_id(&self) -> u32 {
self.location_id
}
#[cfg(any(docsrs, target_os = "macos"))]
pub fn registry_entry_id(&self) -> u64 {
self.registry_id
}
#[cfg(any(docsrs, target_os = "macos"))]
pub fn provider_class_name(&self) -> &str {
&self.provider_class_name
}
#[cfg(any(docsrs, target_os = "macos"))]
pub fn class_name(&self) -> &str {
&self.class_name
}
#[cfg(any(docsrs, 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(target_arch = "wasm32")]
{
None
}
}
}
#[cfg(any(
docsrs,
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()
}
}
#[derive(Default, Clone)]
pub struct DeviceSelector {
pub(crate) vendor_id: Option<u16>,
pub(crate) product_id: Option<u16>,
pub(crate) class: Option<u8>,
pub(crate) subclass: Option<u8>,
pub(crate) protocol: Option<u8>,
pub(crate) serial_number: Option<String>,
}
impl DeviceSelector {
pub const fn all() -> DeviceSelector {
DeviceSelector {
vendor_id: None,
product_id: None,
class: None,
subclass: None,
protocol: None,
serial_number: None,
}
}
pub const fn with_vid(mut self, vendor_id: u16) -> DeviceSelector {
self.vendor_id = Some(vendor_id);
self
}
pub const fn with_vid_pid(mut self, vendor_id: u16, product_id: u16) -> DeviceSelector {
self.vendor_id = Some(vendor_id);
self.product_id = Some(product_id);
self
}
pub const fn with_class(mut self, class_code: u8) -> DeviceSelector {
self.class = Some(class_code);
self
}
pub const fn with_class_subclass(
mut self,
class_code: u8,
subclass_code: u8,
) -> DeviceSelector {
self.class = Some(class_code);
self.subclass = Some(subclass_code);
self
}
pub const fn with_class_subclass_protocol(
mut self,
class_code: u8,
subclass_code: u8,
protocol_code: u8,
) -> DeviceSelector {
self.class = Some(class_code);
self.subclass = Some(subclass_code);
self.protocol = Some(protocol_code);
self
}
pub fn with_serial_number(mut self, serial_number: String) -> DeviceSelector {
self.serial_number = Some(serial_number);
self
}
pub fn vendor_id(&self) -> Option<u16> {
self.vendor_id
}
pub fn product_id(&self) -> Option<u16> {
self.product_id
}
pub fn class(&self) -> Option<u8> {
self.class
}
pub fn subclass(&self) -> Option<u8> {
self.subclass
}
pub fn protocol(&self) -> Option<u8> {
self.protocol
}
pub fn serial_number(&self) -> Option<&str> {
self.serial_number.as_deref()
}
}
impl Debug for DeviceSelector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let DeviceSelector {
vendor_id,
product_id,
class,
subclass,
protocol,
ref serial_number,
} = *self;
let mut s = f.debug_struct("DeviceSelector");
if let Some(vendor_id) = vendor_id {
s.field("vendor_id", &format_args!("0x{vendor_id:04X}"));
}
if let Some(product_id) = product_id {
s.field("product_id", &format_args!("0x{product_id:04X}"));
}
if let Some(class) = class {
s.field("class", &format_args!("0x{class:02X}"));
}
if let Some(subclass) = subclass {
s.field("subclass", &format_args!("0x{subclass:02X}"));
}
if let Some(protocol) = protocol {
s.field("protocol", &format_args!("0x{protocol:02X}"));
}
if let Some(ref serial_number) = serial_number {
s.field("serial_number", serial_number);
}
s.finish()
}
}