use crate::probe::{
DebugProbeError, DebugProbeInfo, DebugProbeSelector, Probe, ProbeCreationError,
};
use super::DRIVERS;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Accessibility {
#[default]
Accessible,
PermissionDenied,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProbeListItem {
pub info: DebugProbeInfo,
pub accessibility: Accessibility,
}
impl ProbeListItem {
pub fn accessible(info: DebugProbeInfo) -> Self {
Self {
info,
accessibility: Accessibility::Accessible,
}
}
}
pub fn usb_probe_accessibility(device: &nusb::DeviceInfo) -> Accessibility {
#[cfg(target_os = "linux")]
let accessibility = {
let path = linux::usbfs_node_path(device.busnum(), device.device_address());
device_node_accessibility(&path)
};
#[cfg(not(target_os = "linux"))]
let accessibility = {
let _ = device;
Accessibility::Accessible
};
accessibility
}
pub fn device_node_accessibility(path: &str) -> Accessibility {
#[cfg(target_os = "linux")]
{
if !linux::can_access(path) {
return Accessibility::PermissionDenied;
}
}
let _ = path;
Accessibility::Accessible
}
#[derive(Debug)]
pub struct Lister {
lister: Box<dyn ProbeLister>,
}
impl Lister {
pub fn new() -> Self {
Self {
lister: Box::new(AllProbesLister::new()),
}
}
pub fn with_lister(lister: Box<dyn ProbeLister>) -> Self {
Self { lister }
}
pub fn open(&self, selector: impl Into<DebugProbeSelector>) -> Result<Probe, DebugProbeError> {
self.lister.open(&selector.into())
}
pub fn list_all(&self) -> Vec<DebugProbeInfo> {
self.list(None)
}
pub fn list(&self, selector: Option<&DebugProbeSelector>) -> Vec<DebugProbeInfo> {
self.lister.list(selector)
}
pub fn list_all_with_access(&self) -> Vec<ProbeListItem> {
self.list_with_access(None)
}
pub fn list_with_access(&self, selector: Option<&DebugProbeSelector>) -> Vec<ProbeListItem> {
self.lister.list_with_access(selector)
}
}
impl Default for Lister {
fn default() -> Self {
Self::new()
}
}
pub trait ProbeLister: std::fmt::Debug {
fn open(&self, selector: &DebugProbeSelector) -> Result<Probe, DebugProbeError>;
fn list_with_access(&self, selector: Option<&DebugProbeSelector>) -> Vec<ProbeListItem>;
fn list(&self, selector: Option<&DebugProbeSelector>) -> Vec<DebugProbeInfo> {
self.list_with_access(selector)
.into_iter()
.map(|item| item.info)
.collect()
}
fn list_all(&self) -> Vec<DebugProbeInfo> {
self.list(None)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct AllProbesLister;
impl ProbeLister for AllProbesLister {
fn open(&self, selector: &DebugProbeSelector) -> Result<Probe, DebugProbeError> {
let mut open_error = None;
let mut fallback_error = ProbeCreationError::NotFound;
for probe_ctor in DRIVERS.read().iter() {
match probe_ctor.open(selector) {
Ok(link) => return Ok(Probe::from_specific_probe(link)),
Err(DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::NotFound)) => {}
Err(DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::CouldNotOpen)) => {
fallback_error = ProbeCreationError::CouldNotOpen;
}
Err(e) => open_error = Some(e),
};
}
Err(open_error.unwrap_or(DebugProbeError::ProbeCouldNotBeCreated(fallback_error)))
}
fn list_with_access(&self, selector: Option<&DebugProbeSelector>) -> Vec<ProbeListItem> {
let mut list = vec![];
for driver in DRIVERS.read().iter() {
list.extend(driver.list_probes_filtered(selector));
}
list
}
}
impl Default for AllProbesLister {
fn default() -> Self {
Self::new()
}
}
impl AllProbesLister {
pub const fn new() -> Self {
Self
}
}
#[cfg(target_os = "linux")]
mod linux {
pub(super) fn usbfs_node_path(busnum: u8, device_address: u8) -> String {
format!("/dev/bus/usb/{busnum:03}/{device_address:03}")
}
pub(super) fn can_access(path: &str) -> bool {
use nix::unistd::{AccessFlags, access};
access(path, AccessFlags::R_OK | AccessFlags::W_OK).is_ok()
}
#[cfg(test)]
mod tests {
use super::usbfs_node_path;
#[test]
fn node_path_is_zero_padded() {
assert_eq!(usbfs_node_path(1, 5), "/dev/bus/usb/001/005");
assert_eq!(usbfs_node_path(12, 127), "/dev/bus/usb/012/127");
}
}
}