use super::*;
use crate::error::{Error, ErrorKind};
use crate::lsusb::names;
use crate::types::NumericalUnit;
use ::nusb::{self, MaybeFuture};
use usb_ids::{self, FromId};
#[derive(Debug)]
pub(crate) struct NusbProfiler {
#[cfg(target_os = "windows")]
bus_id_map: HashMap<String, u8>,
}
pub(crate) struct UsbDevice {
handle: nusb::Device,
language: u16,
vidpid: (u16, u16),
location: DeviceLocation,
timeout: std::time::Duration,
}
impl std::fmt::Debug for UsbDevice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"UsbDevice {{ vidpid: {:#04x}:{:#04x}, location: {:?} }}",
self.vidpid.0, self.vidpid.1, self.location
)
}
}
impl From<ControlRequest> for nusb::transfer::ControlIn {
fn from(request: ControlRequest) -> Self {
nusb::transfer::ControlIn {
control_type: request.control_type.into(),
request: request.request,
value: request.value,
index: request.index,
recipient: request.recipient.into(),
length: request.length as u16,
}
}
}
impl From<ControlType> for nusb::transfer::ControlType {
fn from(control: ControlType) -> Self {
match control {
ControlType::Standard => nusb::transfer::ControlType::Standard,
ControlType::Class => nusb::transfer::ControlType::Class,
ControlType::Vendor => nusb::transfer::ControlType::Vendor,
}
}
}
impl From<Recipient> for nusb::transfer::Recipient {
fn from(recipient: Recipient) -> Self {
match recipient {
Recipient::Device => nusb::transfer::Recipient::Device,
Recipient::Interface => nusb::transfer::Recipient::Interface,
Recipient::Endpoint => nusb::transfer::Recipient::Endpoint,
Recipient::Other => nusb::transfer::Recipient::Other,
}
}
}
impl From<nusb::Speed> for usb::Speed {
fn from(nusb: nusb::Speed) -> Self {
match nusb {
nusb::Speed::SuperPlus => usb::Speed::SuperSpeedPlus,
nusb::Speed::Super => usb::Speed::SuperSpeed,
nusb::Speed::High => usb::Speed::HighSpeed,
nusb::Speed::Full => usb::Speed::FullSpeed,
nusb::Speed::Low => usb::Speed::LowSpeed,
_ => usb::Speed::Unknown,
}
}
}
impl From<&nusb::BusInfo> for Device {
fn from(bus: &nusb::BusInfo) -> Self {
#[cfg(any(target_os = "linux", target_os = "android"))]
{
bus.root_hub().into()
}
#[cfg(target_os = "windows")]
{
let (bcd_device, protocol) = match bus.controller_type() {
Some(nusb::UsbControllerType::XHCI) => (Some(0x300), Some(0x03)),
Some(nusb::UsbControllerType::EHCI) => (Some(0x200), Some(0x01)),
Some(nusb::UsbControllerType::OHCI) => (Some(0x110), Some(0x00)),
Some(nusb::UsbControllerType::VHCI) => (Some(0x000), Some(0x00)),
_ => (None, None),
};
let (vendor_id, product_id) = if let Some(pci_info) = platform::pci_info_from_bus(bus) {
(Some(pci_info.vendor_id), Some(pci_info.product_id))
} else {
(None, None)
};
Device {
vendor_id,
product_id,
device_speed: None,
location_id: DeviceLocation {
bus: 0,
number: 0,
tree_positions: vec![],
},
bcd_device: bcd_device.map(usb::Version::from_bcd),
bcd_usb: None,
class: Some(usb::BaseClass::Hub),
sub_class: Some(0),
protocol,
name: bus.system_name().map(|s| s.to_string()).unwrap_or_default(),
manufacturer: None,
serial_num: Some(bus.parent_instance_id().to_string_lossy().to_string()),
last_event: Some(Default::default()),
..Default::default()
}
}
#[cfg(target_os = "macos")]
{
let (bcd_device, protocol) = match bus.controller_type() {
Some(nusb::UsbControllerType::XHCI) => (Some(0x300), Some(0x03)),
Some(nusb::UsbControllerType::EHCI) => (Some(0x200), Some(0x01)),
Some(nusb::UsbControllerType::OHCI) => (Some(0x110), Some(0x00)),
Some(nusb::UsbControllerType::VHCI) => (Some(0x000), Some(0x00)),
_ => (None, None),
};
Device {
vendor_id: None,
product_id: None,
device_speed: None,
location_id: DeviceLocation {
bus: u8::from_str_radix(bus.bus_id(), 16).expect(
"Failed to parse bus_id: macOS bus_id should be a hex string and not None",
),
number: 0,
tree_positions: vec![],
},
bcd_device: bcd_device.map(usb::Version::from_bcd),
bcd_usb: None,
class: Some(usb::BaseClass::Hub),
sub_class: Some(0),
protocol,
name: bus.class_name().to_string(),
manufacturer: Some(bus.provider_class_name().to_string()),
serial_num: bus.name().map(|s| s.to_string()),
last_event: Some(Default::default()),
..Default::default()
}
}
}
}
impl From<&nusb::BusInfo> for Bus {
fn from(bus: &nusb::BusInfo) -> Self {
platform::from(bus)
}
}
impl From<&nusb::DeviceInfo> for Device {
fn from(device_info: &nusb::DeviceInfo) -> Self {
let device_speed = device_info.speed().map(|s| {
let s = usb::Speed::from(s);
DeviceSpeed::SpeedValue(s)
});
let manufacturer = if cfg!(target_os = "windows") {
device_info.manufacturer_string().map(|s| s.to_string())
} else {
device_info
.manufacturer_string()
.map(|s| s.to_string())
.or_else(|| names::vendor(device_info.vendor_id()))
.or_else(|| {
usb_ids::Vendor::from_id(device_info.vendor_id()).map(|v| v.name().to_string())
})
};
let name = device_info
.product_string()
.map(|s| s.to_string())
.or_else(|| names::product(device_info.vendor_id(), device_info.product_id()))
.or_else(|| {
usb_ids::Device::from_vid_pid(device_info.vendor_id(), device_info.product_id())
.map(|d| d.name().to_string())
})
.unwrap_or_default();
let serial_num = device_info.serial_number().map(|s| s.to_string());
let bus_no = if cfg!(target_os = "macos") {
u8::from_str_radix(device_info.bus_id(), 16)
.expect("Failed to parse bus_id: macOS bus_id should be a hex string and not None")
} else if cfg!(target_os = "linux") || cfg!(target_os = "android") {
device_info.bus_id().parse::<u8>().expect(
"Failed to parse bus_id: Linux bus_id should be a decimal string and not None",
)
} else {
0
};
Device {
vendor_id: Some(device_info.vendor_id()),
product_id: Some(device_info.product_id()),
device_speed,
location_id: DeviceLocation {
bus: bus_no,
number: device_info.device_address(),
tree_positions: device_info.port_chain().to_vec(),
},
bcd_device: Some(usb::Version::from_bcd(device_info.device_version())),
bcd_usb: None,
class: Some(usb::BaseClass::from(device_info.class())),
sub_class: Some(device_info.subclass()),
protocol: Some(device_info.protocol()),
id: Some(device_info.id()),
name,
manufacturer,
serial_num,
last_event: Some(DeviceEvent::default()),
..Default::default()
}
}
}
impl UsbDevice {
#[allow(unused_variables)]
fn control_in(&self, control_request: &ControlRequest, force_claim: bool) -> Result<Vec<u8>> {
let nusb_control: nusb::transfer::ControlIn = (*control_request).into();
#[cfg(target_os = "windows")]
let ret = {
let interface = self
.handle
.claim_interface(control_request.index as u8)
.wait()?;
interface.control_in(nusb_control, self.timeout).wait()
};
#[cfg(not(target_os = "windows"))]
let ret = {
if control_request.claim_interface | force_claim {
let interface: nusb::Interface = self
.handle
.claim_interface(control_request.index as u8)
.wait()?;
interface.control_in(nusb_control, self.timeout).wait()
} else {
self.handle.control_in(nusb_control, self.timeout).wait()
}
};
ret.map_err(|e| match e {
nusb::transfer::TransferError::Stall => Error {
kind: ErrorKind::TransferStall,
message: "Endpoint in a STALL condition".to_string(),
},
_ => Error {
kind: ErrorKind::Nusb,
message: format!("Failed to get control message: {e}"),
},
})
}
fn control_in_retry(&self, control_request: &ControlRequest) -> Result<Vec<u8>> {
match self.control_in(control_request, false) {
Ok(n) => Ok(n),
Err(Error {
kind: ErrorKind::TransferStall,
..
}) => self.control_in(control_request, true).map_err(|e| Error {
kind: ErrorKind::Nusb,
message: format!("Failed to get control message: {e}"),
}),
Err(e) => Err(Error {
kind: ErrorKind::Nusb,
message: format!("Failed to get control message: {e}"),
}),
}
}
pub(crate) fn get_active_alt_setting(&self, interface_number: u8) -> Option<u8> {
self.handle
.claim_interface(interface_number)
.wait()
.ok()
.map(|interface| interface.get_alt_setting())
}
}
impl UsbOperations for UsbDevice {
fn get_descriptor_string(&self, string_index: u8) -> Option<String> {
if string_index == 0 {
return None;
}
self.handle
.get_string_descriptor(
std::num::NonZeroU8::new(string_index).expect("string_index should not be 0"),
self.language,
self.timeout,
)
.wait()
.map(|s| s.chars().filter(|c| !c.is_control()).collect())
.ok()
}
fn get_control_msg(&self, control_request: ControlRequest) -> Result<Vec<u8>> {
let data = self.control_in_retry(&control_request)?;
let n = data.len();
if n < control_request.length {
log::warn!(
"{:?} Failed to get full control message: read {} of {} bytes",
self,
n,
control_request.length
);
return Err(Error {
kind: ErrorKind::Nusb,
message: format!(
"{:?} Failed to get full control message: read {} of {} bytes",
self, n, control_request.length
),
});
}
Ok(data)
}
}
impl NusbProfiler {
pub fn new() -> Self {
Self {
#[cfg(target_os = "windows")]
bus_id_map: HashMap::new(),
}
}
fn build_endpoints(
&self,
device: &UsbDevice,
interface_path: &usb::DevicePath,
interface_desc: &nusb::descriptors::InterfaceDescriptor,
) -> Vec<usb::Endpoint> {
let mut ret: Vec<usb::Endpoint> = Vec::new();
for endpoint in interface_desc.endpoints() {
let endpoint_desc = endpoint.as_bytes();
let endpoint_extra = endpoint
.descriptors()
.flat_map(|d| d.to_vec())
.collect::<Vec<u8>>();
let endpoint_path = usb::EndpointPath::new_with_device_path(
interface_path.to_owned(),
endpoint.address(),
);
ret.push(usb::Endpoint {
address: usb::EndpointAddress::from(endpoint.address()),
transfer_type: usb::TransferType::from(endpoint.attributes()),
sync_type: usb::SyncType::from(endpoint.attributes()),
usage_type: usb::UsageType::from(endpoint.attributes()),
max_packet_size: endpoint.max_packet_size_raw(),
interval: endpoint.interval(),
length: endpoint_desc[0],
extra: self
.build_endpoint_descriptor_extra(
device,
(
interface_desc.class(),
interface_desc.subclass(),
interface_desc.protocol(),
),
interface_desc.interface_number(),
endpoint_extra,
)
.ok()
.flatten(),
internal: InternalData::default(),
endpoint_path: Some(endpoint_path),
});
}
ret
}
fn build_interfaces(
&self,
device: &UsbDevice,
config: &nusb::descriptors::ConfigurationDescriptor,
config_active: bool,
) -> Result<Vec<usb::Interface>> {
let mut ret: Vec<usb::Interface> = Vec::new();
for interface in config.interfaces() {
let sample_path = usb::DevicePath::new_with_port_path(
device.location.to_owned().into(),
Some(config.configuration_value()),
Some(interface.interface_number()),
None, )
.to_string();
let mut active_alt = get_sysfs_active_alternate_setting(&sample_path);
if active_alt.is_none() && config_active {
let mut alt_settings = interface.alt_settings();
if let (Some(first), None) = (alt_settings.next(), alt_settings.next()) {
active_alt = Some(first.alternate_setting());
} else {
active_alt = device.get_active_alt_setting(interface.interface_number());
}
}
for interface_alt in interface.alt_settings() {
let device_path = usb::DevicePath::new_with_port_path(
device.location.to_owned().into(),
Some(config.configuration_value()),
Some(interface_alt.interface_number()),
Some(interface_alt.alternate_setting()),
);
let path = device_path.to_string();
let syspath = get_syspath(&path).or_else(|| get_udev_syspath(&path).ok().flatten());
let interface_desc = interface_alt.as_bytes();
let interface_extra = interface_alt
.descriptors()
.take_while(|d| d.descriptor_type() != 0x05)
.flat_map(|d| d.to_vec())
.collect::<Vec<u8>>();
let mut interface = usb::Interface {
name: get_sysfs_string(&path, "interface").or_else(|| {
interface_alt
.string_index()
.and_then(|i| device.get_descriptor_string(i.into()))
}),
string_index: interface_alt.string_index().map(|i| i.into()).unwrap_or(0),
number: interface_alt.interface_number(),
class: usb::BaseClass::from(interface_alt.class()),
sub_class: interface_alt.subclass(),
protocol: interface_alt.protocol(),
alt_setting: interface_alt.alternate_setting(),
active: active_alt.is_some_and(|a| a == interface_alt.alternate_setting()),
driver: get_sysfs_readlink(&path, "driver")
.or_else(|| get_udev_driver_name(&path).ok().flatten()),
syspath,
devpaths: None,
mount_paths: None,
length: interface_desc[0],
endpoints: self.build_endpoints(device, &device_path, &interface_alt),
extra: self
.build_interface_descriptor_extra(
device,
(
interface_alt.class(),
interface_alt.subclass(),
interface_alt.protocol(),
),
interface_alt.interface_number(),
interface_extra,
)
.ok(),
path: path.to_string(),
device_path: Some(device_path),
internal: InternalData::default(),
};
interface.devpaths = interface.dev_paths();
interface.mount_paths = interface.block_mount_paths();
ret.push(interface);
}
}
Ok(ret)
}
fn build_configurations(
&self,
device: &UsbDevice,
device_desc: &usb::DeviceDescriptor,
sp_device: &Device,
) -> Result<Vec<usb::Configuration>> {
let mut ret: Vec<usb::Configuration> = Vec::new();
let active_config = device
.handle
.active_configuration()
.map(|c| Some(c.configuration_value()))
.unwrap_or_else(|_| get_sysfs_configuration_value(&sp_device.sysfs_name()));
for c in device.handle.configurations() {
let mut attributes = Vec::new();
if c.attributes() & 0x10 != 0 {
attributes.push(usb::ConfigAttributes::BatteryPowered);
}
if c.attributes() & 0x20 != 0 {
attributes.push(usb::ConfigAttributes::RemoteWakeup);
}
if c.attributes() & 0x40 != 0 {
attributes.push(usb::ConfigAttributes::SelfPowered);
} else {
attributes.push(usb::ConfigAttributes::BusPowered);
}
let config_desc = c.as_bytes();
let config_extra = c
.descriptors()
.take_while(|d| d.descriptor_type() != 0x04)
.flat_map(|d| d.to_vec())
.collect::<Vec<u8>>();
let total_length = u16::from_le_bytes(config_desc[2..4].try_into().unwrap());
let power_mult = if device_desc.usb_version.major() >= 3 {
8
} else {
2
};
let active = active_config.is_some_and(|a| a == c.configuration_value());
ret.push(usb::Configuration {
name: c
.string_index()
.and_then(|i| device.get_descriptor_string(i.into()))
.unwrap_or_default(),
string_index: c.string_index().map(|i| i.into()).unwrap_or(0),
number: c.configuration_value(),
active,
attributes,
max_power: NumericalUnit {
value: (c.max_power() as u32 * power_mult),
unit: String::from("mA"),
description: None,
},
length: config_desc[0],
total_length,
num_interfaces: Some(c.num_interfaces()),
interfaces: self.build_interfaces(device, &c, active)?,
extra: self
.build_config_descriptor_extra(device, config_extra)
.ok(),
internal: InternalData::default(),
});
}
Ok(ret)
}
fn build_spdevice_extra(
&self,
device: &UsbDevice,
sp_device: &mut Device,
more_extra: bool,
) -> Result<usb::DeviceExtra> {
let device_desc: usb::DeviceDescriptor =
usb::DeviceDescriptor::try_from(device.handle.device_descriptor().as_bytes())?;
sp_device.bcd_usb = Some(device_desc.usb_version);
if sp_device.name.is_empty() {
if let Some(name) = device.get_descriptor_string(device_desc.product_string_index) {
sp_device.name = name;
}
}
if sp_device.manufacturer.is_none() {
if let Some(manufacturer) =
device.get_descriptor_string(device_desc.manufacturer_string_index)
{
sp_device.manufacturer = Some(manufacturer);
}
}
if sp_device.serial_num.is_none() {
if let Some(serial) =
device.get_descriptor_string(device_desc.serial_number_string_index)
{
sp_device.serial_num = Some(serial);
}
}
let sysfs_name = sp_device.sysfs_name();
let mut extra = usb::DeviceExtra {
max_packet_size: device_desc.max_packet_size,
string_indexes: (
device_desc.product_string_index,
device_desc.manufacturer_string_index,
device_desc.serial_number_string_index,
),
driver: get_sysfs_readlink(&sysfs_name, "driver")
.or_else(|| get_udev_driver_name(&sysfs_name).ok().flatten()),
syspath: get_syspath(&sysfs_name)
.or_else(|| get_udev_syspath(&sysfs_name).ok().flatten()),
vendor: names::vendor(device_desc.vendor_id).or_else(|| {
usb_ids::Vendor::from_id(device_desc.vendor_id).map(|v| v.name().to_owned())
}),
product_name: names::product(device_desc.vendor_id, device_desc.product_id).or_else(
|| {
usb_ids::Device::from_vid_pid(device_desc.vendor_id, device_desc.product_id)
.map(|v| v.name().to_owned())
},
),
configurations: self.build_configurations(device, &device_desc, sp_device)?,
status: None,
debug: None,
binary_object_store: None,
qualifier: None,
hub: None,
negotiated_speed: device.handle.speed().map(usb::Speed::from),
};
if more_extra {
extra.status = Self::get_device_status(device).ok();
extra.debug = Self::get_debug_descriptor(device).ok();
if device_desc.usb_version >= usb::Version::from_bcd(0x0201) {
extra.binary_object_store = Self::get_bos_descriptor(device).ok();
}
if device_desc.usb_version >= usb::Version::from_bcd(0x0200) {
extra.qualifier = Self::get_device_qualifier(device).ok();
}
if device_desc.device_class == usb::BaseClass::Hub as u8 {
let has_ssp = if let Some(bos) = &extra.binary_object_store {
bos.capabilities.iter().any(|c| {
matches!(c, usb::descriptors::bos::BosCapability::SuperSpeedPlus(_))
})
} else {
false
};
let bcd = sp_device.bcd_usb.map_or(0x0100, |v| v.into());
extra.hub =
Self::get_hub_descriptor(device, device_desc.device_protocol, bcd, has_ssp)
.ok();
}
}
if extra.hub.is_none()
&& (device_desc.device_class == usb::BaseClass::Hub as u8 || sp_device.is_root_hub())
{
if let Some(ports) = get_sysfs_hub_ports(&sysfs_name) {
extra.hub = Some(usb::HubDescriptor {
num_ports: ports,
..Default::default()
});
}
}
Ok(extra)
}
pub(crate) fn build_spdevice(
&mut self,
device_info: &nusb::DeviceInfo,
options: &ProfilerOptions,
) -> Result<Device> {
let mut sp_device: Device = device_info.into();
let generic_extra = |sysfs_name: &str, is_root_hub: bool| {
let mut extra = usb::DeviceExtra {
max_packet_size: 0, string_indexes: (0, 0, 0),
driver: get_sysfs_readlink(sysfs_name, "driver")
.or_else(|| get_udev_driver_name(sysfs_name).ok().flatten()),
syspath: get_syspath(sysfs_name)
.or_else(|| get_udev_syspath(sysfs_name).ok().flatten()),
vendor: names::vendor(device_info.vendor_id()).or_else(|| {
usb_ids::Vendor::from_id(device_info.vendor_id()).map(|v| v.name().to_owned())
}),
product_name: names::product(device_info.vendor_id(), device_info.product_id())
.or_else(|| {
usb_ids::Device::from_vid_pid(
device_info.vendor_id(),
device_info.product_id(),
)
.map(|v| v.name().to_owned())
}),
configurations: vec![],
status: None,
debug: None,
binary_object_store: None,
qualifier: None,
hub: None,
negotiated_speed: None,
};
if extra.hub.is_none()
&& (device_info.class() == usb::BaseClass::Hub as u8 || is_root_hub)
{
if let Some(ports) = get_sysfs_hub_ports(sysfs_name) {
extra.hub = Some(usb::HubDescriptor {
num_ports: ports,
..Default::default()
});
}
}
extra
};
let is_match = options
.filter
.as_ref()
.is_none_or(|f| f.is_potential_match(&sp_device));
if options.depth.includes_extra() && is_match {
if let Ok(device) = device_info.open().wait() {
let languages: Vec<u16> = device
.get_string_descriptor_supported_languages(std::time::Duration::from_secs(1))
.wait()
.map(|i| i.collect())
.unwrap_or_default();
let language = languages
.first()
.copied()
.unwrap_or(nusb::descriptors::language_id::US_ENGLISH);
sp_device.profiler_error = {
let usb_device = UsbDevice {
handle: device,
language,
vidpid: (device_info.vendor_id(), device_info.product_id()),
location: sp_device.location_id.to_owned(),
timeout: std::time::Duration::from_millis(
std::env::var("CYME_USB_TIMEOUT_MS")
.ok()
.and_then(|s| s.parse::<u64>().ok())
.unwrap_or(200),
),
};
match self.build_spdevice_extra(
&usb_device,
&mut sp_device,
options.depth.includes_more_extra(),
) {
Ok(extra) => {
sp_device.extra = Some(extra);
None
}
Err(e) => {
sp_device.extra = Some(generic_extra(
&sp_device.sysfs_name(),
sp_device.is_root_hub(),
));
Some(format!("Failed to get some extra data for {sp_device}, probably requires elevated permissions: {e}"))
}
}
};
} else {
log::warn!("Failed to open device for extra data: {:04x}:{:04x}. Ensure user has USB access permissions: https://docs.rs/nusb/latest/nusb", device_info.vendor_id(), device_info.product_id());
sp_device.profiler_error = Some(
"Failed to open device, extra data incomplete and possibly inaccurate"
.to_string(),
);
sp_device.extra = Some(generic_extra(
&sp_device.sysfs_name(),
sp_device.is_root_hub(),
));
}
} else {
sp_device.extra = Some(generic_extra(
&sp_device.sysfs_name(),
sp_device.is_root_hub(),
));
}
Ok(sp_device)
}
}
impl Profiler<UsbDevice> for NusbProfiler {
fn get_devices(&mut self, options: &ProfilerOptions) -> Result<Vec<Device>> {
let mut devices = Vec::new();
let raw_devices: Vec<nusb::DeviceInfo> = nusb::list_devices().wait()?.collect();
let filter_set: Option<std::collections::HashSet<(u8, Vec<u8>)>> =
if let Some(filter) = &options.filter {
let mut set = std::collections::HashSet::new();
for device_info in &raw_devices {
let sp_device: Device = device_info.into();
if filter.is_potential_match(&sp_device) {
let bus = sp_device.location_id.bus;
let ports = sp_device.location_id.tree_positions;
set.insert((bus, ports.clone()));
if options.tree {
for i in 1..ports.len() {
set.insert((bus, ports[0..i].to_vec()));
}
}
}
}
Some(set)
} else {
None
};
for device_info in raw_devices {
let bus_no = if cfg!(target_os = "macos") {
u8::from_str_radix(device_info.bus_id(), 16).unwrap_or(0)
} else if cfg!(target_os = "linux") || cfg!(target_os = "android") {
device_info.bus_id().parse::<u8>().unwrap_or(0)
} else {
0
};
if let Some(set) = &filter_set {
if !set.contains(&(bus_no, device_info.port_chain().to_vec())) {
continue;
}
}
let mut device_options = options.clone();
if let Some(filter) = &options.filter {
let sp_device: Device = (&device_info).into();
if !filter.is_potential_match(&sp_device) {
device_options.depth = ProfileDepth::Identity;
}
}
match self.build_spdevice(&device_info, &device_options) {
#[allow(unused_mut)]
Ok(mut sp_device) => {
#[cfg(target_os = "windows")]
{
if let Some(existing_no) = self.bus_id_map.get(device_info.bus_id()) {
sp_device.location_id.bus = *existing_no;
} else {
let bus = self.bus_id_map.len() as u8;
self.bus_id_map.insert(device_info.bus_id().to_owned(), bus);
sp_device.location_id.bus = bus;
}
}
devices.push(sp_device.to_owned());
let print_stderr =
std::env::var_os("CYME_PRINT_NON_CRITICAL_PROFILER_STDERR").is_some();
sp_device.profiler_error.iter().for_each(|e| {
if print_stderr {
eprintln!("{e}");
} else {
log::warn!("Non-critical error during profile of {device_info:?}: {e}");
}
});
}
Err(e) => eprintln!("Failed to get data for {device_info:?}: {e}"),
}
}
Ok(devices)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn get_root_hubs(&mut self, options: &ProfilerOptions) -> Result<HashMap<u8, Device>> {
let mut root_hubs = HashMap::new();
for bus in nusb::list_buses().wait()? {
let device = bus.root_hub();
match self.build_spdevice(device, options) {
#[allow(unused_mut)]
Ok(mut sp_device) => {
if !sp_device.is_root_hub() {
return Err(Error::new(
ErrorKind::InvalidDevice,
&format!(
"Device {sp_device} returned by nusb::list_root_hubs is not a root hub!"
),
));
}
let print_stderr =
std::env::var_os("CYME_PRINT_NON_CRITICAL_PROFILER_STDERR").is_some();
sp_device.profiler_error.iter().for_each(|e| {
if print_stderr {
eprintln!("{e}");
} else {
log::warn!("Non-critical error during profile of {sp_device}: {e}");
}
});
root_hubs.insert(sp_device.location_id.bus, sp_device);
}
Err(e) => eprintln!("Failed to get data for {device:?}: {e}"),
}
}
Ok(root_hubs)
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn get_root_hubs(&mut self, _options: &ProfilerOptions) -> Result<HashMap<u8, Device>> {
let mut root_hubs = HashMap::new();
for bus in nusb::list_buses().wait()? {
#[allow(unused_mut)]
let mut device: Device = Device::from(&bus);
#[cfg(target_os = "windows")]
{
if let Some(existing_no) = self.bus_id_map.get(bus.bus_id()) {
device.location_id.bus = *existing_no;
} else {
let bus_no = self.bus_id_map.len() as u8;
self.bus_id_map.insert(bus.bus_id().to_owned(), bus_no);
device.location_id.bus = bus_no;
}
}
root_hubs.insert(device.location_id.bus, device);
}
Ok(root_hubs)
}
#[allow(unused_variables)]
fn get_buses(&mut self, options: &ProfilerOptions) -> Result<HashMap<u8, Bus>> {
let mut buses = HashMap::new();
for nusb_bus in nusb::list_buses().wait()? {
#[allow(unused_mut)]
let mut bus: Bus = Bus::from(&nusb_bus);
#[cfg(target_os = "windows")]
{
if let Some(existing_no) = self.bus_id_map.get(nusb_bus.bus_id()) {
bus.usb_bus_number = Some(*existing_no);
} else {
let bus_no = self.bus_id_map.len() as u8;
self.bus_id_map.insert(nusb_bus.bus_id().to_owned(), bus_no);
bus.usb_bus_number = Some(bus_no);
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
{
let sp_device = self.build_spdevice(nusb_bus.root_hub(), options)?;
bus.devices = Some(vec![sp_device]);
}
buses.insert(
bus.usb_bus_number
.expect("Bus has no usb_bus_number, unable to use as key"),
bus,
);
}
Ok(buses)
}
}
pub(crate) fn fill_spusb(spusb: &mut SystemProfile, options: &ProfilerOptions) -> Result<()> {
let mut profiler = NusbProfiler::new();
profiler.fill_spusb(spusb, options)
}