use std::path::PathBuf;
use color_eyre::eyre::Result;
use crate::bmp::BmpDevice;
use crate::serial::bmd_rsp::BmdRspInterface;
use crate::serial::gdb_rsp::GdbRspInterface;
pub struct ProbeInterface
{
serial_number: String,
}
impl ProbeInterface
{
pub fn from_device(probe: &BmpDevice) -> Result<Self>
{
Ok(Self {
serial_number: probe.serial_number()?.to_string(),
})
}
pub fn gdb_interface(&self) -> Result<GdbRspInterface>
{
GdbRspInterface::from_path(&self.probe_interface()?)
}
pub fn bmd_interface(&self) -> Result<BmdRspInterface>
{
BmdRspInterface::from_path(&self.probe_interface()?)
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl ProbeInterface
{
const BMD_IDSTRING_1BITSQUARED: &str = "usb-1BitSquared_Black_Magic_Probe";
const BMD_IDSTRING_BLACKMAGIC: &str = "usb-Black_Magic_Debug_Black_Magic_Probe";
const BMD_IDSTRING_BLACKSHERE: &str = "usb-Black_Sphere_Technologies_Black_Magic_Probe";
const DEVICE_BY_ID: &str = "/dev/serial/by-id";
fn probe_interface(&self) -> Result<PathBuf>
{
use std::fs::read_dir;
use color_eyre::eyre::eyre;
let dir = read_dir(Self::DEVICE_BY_ID)?;
for entry in dir {
let entry = entry?;
let file_name = entry.file_name();
let file_name = if let Some(path) = file_name.to_str() {
path
} else {
continue;
};
if !Self::device_is_bmd_gdb_port(file_name) {
continue;
}
if self.serial_matches(file_name) {
return Ok(entry.path());
}
}
Err(eyre!("Failed to locate a device matching serial number {}", self.serial_number))
}
fn device_is_bmd_gdb_port(file_name: &str) -> bool
{
(file_name.starts_with(Self::BMD_IDSTRING_BLACKSHERE) ||
file_name.starts_with(Self::BMD_IDSTRING_BLACKMAGIC) ||
file_name.starts_with(Self::BMD_IDSTRING_1BITSQUARED)) &&
file_name.ends_with("-if00")
}
fn serial_matches(&self, file_name: &str) -> bool
{
let last_underscore = if let Some(pos) = file_name.rfind('_') {
pos
} else {
return false;
};
let begin = last_underscore + 1;
let end = file_name.len() - 5;
file_name[begin..end] == self.serial_number
}
}
#[cfg(target_os = "windows")]
impl ProbeInterface
{
const PRODUCT_ID_BMP: u16 = 0x6018;
const VENDOR_ID_BMP: u16 = 0x1d50;
fn probe_interface(&self) -> Result<PathBuf>
{
let serial_path = format!("\\{}", self.serial_number);
let prefix = Self::read_key_from_path(&serial_path, "ParentIdPrefix")?;
let parameter_path = format!("&MI_00\\{}&0000\\Device Parameters", prefix);
let port_name = Self::read_key_from_path(¶meter_path, "PortName")?;
if port_name.starts_with("\\\\.\\") {
Ok(port_name.into())
} else {
Ok(format!("\\\\.\\{}", port_name).into())
}
}
fn read_key_from_path(subpath: &str, key_name: &str) -> Result<String>
{
use log::debug;
use windows_registry::LOCAL_MACHINE;
let key_path = format!(
"SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_{:04X}&PID_{:04X}{}",
Self::VENDOR_ID_BMP,
Self::PRODUCT_ID_BMP,
subpath,
);
debug!("Trying to open registry key {} to get value {}", key_path, key_name);
let key_handle = LOCAL_MACHINE.open(&key_path)?;
Ok(key_handle.get_string(key_name)?)
}
}
#[cfg(target_os = "macos")]
impl ProbeInterface
{
fn probe_interface(&self) -> Result<PathBuf>
{
Ok(format!("/dev/cu.usbmodem{}1", self.serial_number).into())
}
}