pub(crate) mod arm_debug_interface;
pub(crate) mod common;
pub(crate) mod usb_util;
pub mod blackmagic;
pub mod cmsisdap;
pub mod espusbjtag;
pub mod fake_probe;
pub mod ftdi;
pub mod jlink;
pub mod list;
pub mod stlink;
pub mod wlink;
use crate::architecture::arm::sequences::{ArmDebugSequence, DefaultArmSequence};
use crate::architecture::arm::ArmError;
use crate::architecture::arm::{
communication_interface::{DapProbe, UninitializedArmProbe},
PortType, SwoAccess,
};
use crate::architecture::riscv::communication_interface::{RiscvError, RiscvInterfaceBuilder};
use crate::architecture::xtensa::communication_interface::{
XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
};
use crate::config::TargetSelector;
use crate::probe::common::IdCode;
use crate::{Error, Permissions, Session};
use common::ScanChainError;
use nusb::DeviceInfo;
use probe_rs_target::ScanChainElement;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
const LOW_TARGET_VOLTAGE_WARNING_THRESHOLD: f32 = 1.4;
#[derive(Copy, Clone, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
pub enum WireProtocol {
Swd,
Jtag,
}
impl fmt::Display for WireProtocol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
WireProtocol::Swd => f.write_str("SWD"),
WireProtocol::Jtag => f.write_str("JTAG"),
}
}
}
impl std::str::FromStr for WireProtocol {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match &s.to_ascii_lowercase()[..] {
"swd" => Ok(WireProtocol::Swd),
"jtag" => Ok(WireProtocol::Jtag),
_ => Err(format!(
"'{s}' is not a valid protocol. Choose from [swd, jtag]."
)),
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum BatchCommand {
Read(PortType, u16),
Write(PortType, u16, u32),
}
impl fmt::Display for BatchCommand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BatchCommand::Read(port, addr) => {
write!(f, "Read(port={port:?}, addr={addr})")
}
BatchCommand::Write(port, addr, data) => {
write!(f, "Write(port={port:?}, addr={addr}, data={data:#010x})")
}
}
}
}
pub trait ProbeError: std::error::Error + Send + Sync + AnyShim {}
impl std::error::Error for Box<dyn ProbeError> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.as_ref().source()
}
}
#[doc(hidden)]
pub trait AnyShim: std::any::Any {
fn as_any(&self) -> &dyn std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}
impl<T> AnyShim for T
where
T: ProbeError,
{
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct BoxedProbeError(#[from] Box<dyn ProbeError>);
impl BoxedProbeError {
pub fn is<T: ProbeError>(&self) -> bool {
self.0.as_ref().as_any().is::<T>()
}
pub fn downcast_ref<T: ProbeError>(&self) -> Option<&T> {
self.0.as_ref().as_any().downcast_ref()
}
pub fn downcast_mut<T: ProbeError>(&mut self) -> Option<&mut T> {
self.0.as_mut().as_any_mut().downcast_mut()
}
}
impl<T> From<T> for BoxedProbeError
where
T: ProbeError,
{
fn from(e: T) -> Self {
BoxedProbeError(Box::new(e))
}
}
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum DebugProbeError {
Usb(#[source] std::io::Error),
ProbeSpecific(#[source] BoxedProbeError),
ProbeCouldNotBeCreated(#[from] ProbeCreationError),
UnsupportedProtocol(WireProtocol),
#[ignore_extra_doc_attributes]
InterfaceNotAvailable {
interface_name: &'static str,
},
UnsupportedSpeed(u32),
#[ignore_extra_doc_attributes]
NotAttached,
#[ignore_extra_doc_attributes]
Attached,
TargetNotFound,
BatchError(BatchCommand),
#[ignore_extra_doc_attributes]
NotImplemented {
function_name: &'static str,
},
CommandNotSupportedByProbe {
command_name: &'static str,
},
JtagScanChain(#[from] ScanChainError),
#[display("{0}")]
Other(String),
Timeout,
}
impl<T: ProbeError> From<T> for DebugProbeError {
fn from(e: T) -> Self {
Self::ProbeSpecific(BoxedProbeError::from(e))
}
}
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum ProbeCreationError {
NotFound,
CouldNotOpen,
HidApi(#[from] hidapi::HidError),
Usb(#[source] std::io::Error),
ProbeSpecific(#[source] BoxedProbeError),
#[display("{0}")]
Other(&'static str),
}
impl<T: ProbeError> From<T> for ProbeCreationError {
fn from(e: T) -> Self {
Self::ProbeSpecific(BoxedProbeError::from(e))
}
}
#[derive(Debug)]
pub struct Probe {
inner: Box<dyn DebugProbe>,
attached: bool,
}
impl Probe {
pub fn new(probe: impl DebugProbe + 'static) -> Self {
Self {
inner: Box::new(probe),
attached: false,
}
}
pub(crate) fn from_attached_probe(probe: Box<dyn DebugProbe>) -> Self {
Self {
inner: probe,
attached: true,
}
}
pub fn from_specific_probe(probe: Box<dyn DebugProbe>) -> Self {
Probe {
inner: probe,
attached: false,
}
}
pub fn get_name(&self) -> String {
self.inner.get_name().to_string()
}
pub fn attach(
self,
target: impl Into<TargetSelector>,
permissions: Permissions,
) -> Result<Session, Error> {
Session::new(self, target.into(), AttachMethod::Normal, permissions)
}
pub fn attach_to_unspecified(&mut self) -> Result<(), Error> {
self.inner.attach()?;
self.attached = true;
Ok(())
}
pub fn attach_to_unspecified_under_reset(&mut self) -> Result<(), Error> {
if let Some(dap_probe) = self.try_as_dap_probe() {
DefaultArmSequence(()).reset_hardware_assert(dap_probe)?;
} else {
tracing::info!(
"Custom reset sequences are not supported on {}.",
self.get_name()
);
tracing::info!("Falling back to standard probe reset.");
self.target_reset_assert()?;
}
self.attach_to_unspecified()?;
Ok(())
}
pub fn attach_under_reset(
self,
target: impl Into<TargetSelector>,
permissions: Permissions,
) -> Result<Session, Error> {
Session::new(self, target.into(), AttachMethod::UnderReset, permissions).map_err(
|e| match e {
Error::Arm(ArmError::Timeout)
| Error::Riscv(RiscvError::Timeout)
| Error::Xtensa(XtensaError::Timeout) => Error::Other(
"Timeout while attaching to target under reset. \
This can happen if the target is not responding to the reset sequence. \
Ensure the chip's reset pin is connected, or try attaching without reset \
(`connectUnderReset = false` for DAP Clients, or remove `connect-under-reset` \
option from CLI options.)."
.to_string(),
),
e => e,
},
)
}
pub fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
if !self.attached {
self.inner.select_protocol(protocol)
} else {
Err(DebugProbeError::Attached)
}
}
pub fn protocol(&self) -> Option<WireProtocol> {
self.inner.active_protocol()
}
pub fn detach(&mut self) -> Result<(), crate::Error> {
self.attached = false;
self.inner.detach()?;
Ok(())
}
pub fn target_reset(&mut self) -> Result<(), DebugProbeError> {
self.inner.target_reset()
}
pub fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
tracing::debug!("Asserting target reset");
self.inner.target_reset_assert()
}
pub fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
tracing::debug!("Deasserting target reset");
self.inner.target_reset_deassert()
}
pub fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError> {
if !self.attached {
self.inner.set_speed(speed_khz)
} else {
Err(DebugProbeError::Attached)
}
}
pub fn set_scan_chain(
&mut self,
scan_chain: Vec<ScanChainElement>,
) -> Result<(), DebugProbeError> {
if !self.attached {
self.inner.set_scan_chain(scan_chain)
} else {
Err(DebugProbeError::Attached)
}
}
pub fn scan_chain(&self) -> Result<&[ScanChainElement], DebugProbeError> {
self.inner.scan_chain()
}
pub fn select_jtag_tap(&mut self, index: usize) -> Result<(), DebugProbeError> {
self.inner.select_jtag_tap(index)
}
pub fn speed_khz(&self) -> u32 {
self.inner.speed_khz()
}
pub fn has_xtensa_interface(&self) -> bool {
self.inner.has_xtensa_interface()
}
pub fn try_get_xtensa_interface<'probe>(
&'probe mut self,
state: &'probe mut XtensaDebugInterfaceState,
) -> Result<XtensaCommunicationInterface<'probe>, DebugProbeError> {
if !self.attached {
Err(DebugProbeError::NotAttached)
} else {
Ok(self.inner.try_get_xtensa_interface(state)?)
}
}
pub fn has_arm_interface(&self) -> bool {
self.inner.has_arm_interface()
}
pub fn try_into_arm_interface<'probe>(
self,
) -> Result<Box<dyn UninitializedArmProbe + 'probe>, (Self, DebugProbeError)> {
if !self.attached {
Err((self, DebugProbeError::NotAttached))
} else {
self.inner
.try_get_arm_interface()
.map_err(|(probe, err)| (Probe::from_attached_probe(probe), err))
}
}
pub fn has_riscv_interface(&self) -> bool {
self.inner.has_riscv_interface()
}
pub fn try_get_riscv_interface_builder<'probe>(
&'probe mut self,
) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, DebugProbeError> {
if !self.attached {
Err(DebugProbeError::NotAttached)
} else {
self.inner.try_get_riscv_interface_builder()
}
}
pub fn get_swo_interface(&self) -> Option<&dyn SwoAccess> {
self.inner.get_swo_interface()
}
pub fn get_swo_interface_mut(&mut self) -> Option<&mut dyn SwoAccess> {
self.inner.get_swo_interface_mut()
}
pub fn try_as_dap_probe(&mut self) -> Option<&mut dyn DapProbe> {
self.inner.try_as_dap_probe()
}
pub fn get_target_voltage(&mut self) -> Result<Option<f32>, DebugProbeError> {
self.inner.get_target_voltage()
}
pub fn try_into_jlink(&mut self) -> Result<&mut jlink::JLink, DebugProbeError> {
self.inner.try_into_jlink()
}
}
pub trait ProbeFactory: std::any::Any + std::fmt::Display + std::fmt::Debug + Sync {
fn open(&self, selector: &DebugProbeSelector) -> Result<Box<dyn DebugProbe>, DebugProbeError>;
fn list_probes(&self) -> Vec<DebugProbeInfo>;
}
pub trait DebugProbe: Send + fmt::Debug {
fn get_name(&self) -> &str;
fn speed_khz(&self) -> u32;
fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError>;
fn set_scan_chain(&mut self, scan_chain: Vec<ScanChainElement>) -> Result<(), DebugProbeError>;
fn scan_chain(&self) -> Result<&[ScanChainElement], DebugProbeError>;
fn attach(&mut self) -> Result<(), DebugProbeError>;
fn select_jtag_tap(&mut self, index: usize) -> Result<(), DebugProbeError> {
if index != 0 {
return Err(DebugProbeError::NotImplemented {
function_name: "select_jtag_tap",
});
}
Ok(())
}
fn detach(&mut self) -> Result<(), crate::Error>;
fn target_reset(&mut self) -> Result<(), DebugProbeError>;
fn target_reset_assert(&mut self) -> Result<(), DebugProbeError>;
fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError>;
fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError>;
fn active_protocol(&self) -> Option<WireProtocol>;
fn has_arm_interface(&self) -> bool {
false
}
fn try_get_arm_interface<'probe>(
self: Box<Self>,
) -> Result<Box<dyn UninitializedArmProbe + 'probe>, (Box<dyn DebugProbe>, DebugProbeError)>
{
Err((
self.into_probe(),
DebugProbeError::InterfaceNotAvailable {
interface_name: "ARM",
},
))
}
fn try_get_riscv_interface_builder<'probe>(
&'probe mut self,
) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, DebugProbeError> {
Err(DebugProbeError::InterfaceNotAvailable {
interface_name: "RISC-V",
})
}
fn has_riscv_interface(&self) -> bool {
false
}
fn try_get_xtensa_interface<'probe>(
&'probe mut self,
_state: &'probe mut XtensaDebugInterfaceState,
) -> Result<XtensaCommunicationInterface<'probe>, DebugProbeError> {
Err(DebugProbeError::InterfaceNotAvailable {
interface_name: "Xtensa",
})
}
fn has_xtensa_interface(&self) -> bool {
false
}
fn get_swo_interface(&self) -> Option<&dyn SwoAccess> {
None
}
fn get_swo_interface_mut(&mut self) -> Option<&mut dyn SwoAccess> {
None
}
fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe>;
fn try_as_dap_probe(&mut self) -> Option<&mut dyn DapProbe> {
None
}
fn get_target_voltage(&mut self) -> Result<Option<f32>, DebugProbeError> {
Ok(None)
}
fn try_into_jlink(&mut self) -> Result<&mut jlink::JLink, DebugProbeError> {
Err(DebugProbeError::Other(
"This probe is not a J-Link.".to_string(),
))
}
}
impl PartialEq for dyn ProbeFactory {
fn eq(&self, other: &Self) -> bool {
self.type_id() == other.type_id()
&& std::ptr::eq(
self as *const _ as *const (),
other as *const _ as *const (),
)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DebugProbeInfo {
pub identifier: String,
pub vendor_id: u16,
pub product_id: u16,
pub serial_number: Option<String>,
pub hid_interface: Option<u8>,
probe_factory: &'static dyn ProbeFactory,
}
impl std::fmt::Display for DebugProbeInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{} -- {:04x}:{:04x}:{} ({})",
self.identifier,
self.vendor_id,
self.product_id,
self.serial_number.as_deref().unwrap_or(""),
self.probe_factory,
)
}
}
impl DebugProbeInfo {
pub fn new<S: Into<String>>(
identifier: S,
vendor_id: u16,
product_id: u16,
serial_number: Option<String>,
probe_factory: &'static dyn ProbeFactory,
hid_interface: Option<u8>,
) -> Self {
Self {
identifier: identifier.into(),
vendor_id,
product_id,
serial_number,
probe_factory,
hid_interface,
}
}
pub fn open(&self) -> Result<Probe, DebugProbeError> {
let selector = DebugProbeSelector::from(self);
self.probe_factory
.open(&selector)
.map(Probe::from_specific_probe)
}
pub fn is_probe_type<F: ProbeFactory>(&self) -> bool {
self.probe_factory.type_id() == std::any::TypeId::of::<F>()
}
pub fn probe_type(&self) -> String {
self.probe_factory.to_string()
}
}
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum DebugProbeSelectorParseError {
ParseInt(#[from] std::num::ParseIntError),
Format,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "String")]
pub struct DebugProbeSelector {
pub vendor_id: u16,
pub product_id: u16,
pub serial_number: Option<String>,
}
impl DebugProbeSelector {
pub(crate) fn matches(&self, info: &DeviceInfo) -> bool {
info.vendor_id() == self.vendor_id
&& info.product_id() == self.product_id
&& self
.serial_number
.as_ref()
.map(|s| info.serial_number() == Some(s))
.unwrap_or(true)
}
}
impl TryFrom<&str> for DebugProbeSelector {
type Error = DebugProbeSelectorParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut split = value.splitn(3, ':');
let vendor_id = split.next().unwrap(); let product_id = split.next().ok_or(DebugProbeSelectorParseError::Format)?;
let serial_number = split.next().map(|s| s.to_string());
Ok(DebugProbeSelector {
vendor_id: u16::from_str_radix(vendor_id, 16)?,
product_id: u16::from_str_radix(product_id, 16)?,
serial_number,
})
}
}
impl TryFrom<String> for DebugProbeSelector {
type Error = DebugProbeSelectorParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
TryFrom::<&str>::try_from(&value)
}
}
impl std::str::FromStr for DebugProbeSelector {
type Err = DebugProbeSelectorParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
impl From<DebugProbeInfo> for DebugProbeSelector {
fn from(selector: DebugProbeInfo) -> Self {
DebugProbeSelector {
vendor_id: selector.vendor_id,
product_id: selector.product_id,
serial_number: selector.serial_number,
}
}
}
impl From<&DebugProbeInfo> for DebugProbeSelector {
fn from(selector: &DebugProbeInfo) -> Self {
DebugProbeSelector {
vendor_id: selector.vendor_id,
product_id: selector.product_id,
serial_number: selector.serial_number.clone(),
}
}
}
impl From<&DebugProbeSelector> for DebugProbeSelector {
fn from(selector: &DebugProbeSelector) -> Self {
selector.clone()
}
}
impl fmt::Display for DebugProbeSelector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04x}:{:04x}", self.vendor_id, self.product_id)?;
if let Some(ref sn) = self.serial_number {
write!(f, ":{sn}")?;
}
Ok(())
}
}
pub trait JTAGAccess: DebugProbe {
fn scan_chain(&mut self) -> Result<(), DebugProbeError>;
fn tap_reset(&mut self) -> Result<(), DebugProbeError>;
fn read_register(&mut self, address: u32, len: u32) -> Result<Vec<u8>, DebugProbeError> {
let data = vec![0u8; len.div_ceil(8) as usize];
self.write_register(address, &data, len)
}
fn set_idle_cycles(&mut self, idle_cycles: u8);
fn idle_cycles(&self) -> u8;
fn write_register(
&mut self,
address: u32,
data: &[u8],
len: u32,
) -> Result<Vec<u8>, DebugProbeError>;
fn write_dr(&mut self, data: &[u8], len: u32) -> Result<Vec<u8>, DebugProbeError>;
fn write_register_batch(
&mut self,
writes: &JtagCommandQueue,
) -> Result<DeferredResultSet, BatchExecutionError> {
tracing::debug!("Using default `JTAGAccess::write_register_batch` this will hurt performance. Please implement proper batching for this probe.");
let mut results = DeferredResultSet::new();
for (idx, write) in writes.iter() {
match write {
JtagCommand::WriteRegister(write) => {
match self
.write_register(write.address, &write.data, write.len)
.map_err(crate::Error::Probe)
.and_then(|response| (write.transform)(write, response))
{
Ok(res) => results.push(idx, res),
Err(e) => return Err(BatchExecutionError::new(e, results)),
}
}
JtagCommand::ShiftDr(write) => {
match self
.write_dr(&write.data, write.len)
.map_err(crate::Error::Probe)
.and_then(|response| (write.transform)(write, response))
{
Ok(res) => results.push(idx, res),
Err(e) => return Err(BatchExecutionError::new(e, results)),
}
}
}
}
Ok(results)
}
}
#[derive(Debug, Clone)]
pub struct JtagWriteCommand {
pub address: u32,
pub data: Vec<u8>,
pub len: u32,
pub transform: fn(&JtagWriteCommand, Vec<u8>) -> Result<CommandResult, crate::Error>,
}
#[derive(Debug, Clone)]
pub struct ShiftDrCommand {
pub data: Vec<u8>,
pub len: u32,
pub transform: fn(&ShiftDrCommand, Vec<u8>) -> Result<CommandResult, crate::Error>,
}
#[derive(Debug, Clone)]
pub enum JtagCommand {
WriteRegister(JtagWriteCommand),
ShiftDr(ShiftDrCommand),
}
impl From<JtagWriteCommand> for JtagCommand {
fn from(cmd: JtagWriteCommand) -> Self {
JtagCommand::WriteRegister(cmd)
}
}
impl From<ShiftDrCommand> for JtagCommand {
fn from(cmd: ShiftDrCommand) -> Self {
JtagCommand::ShiftDr(cmd)
}
}
#[derive(Debug)]
pub struct JtagChainItem {
pub idcode: Option<IdCode>,
pub irlen: usize,
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ChainParams {
pub irpre: usize,
pub irpost: usize,
pub drpre: usize,
pub drpost: usize,
pub irlen: usize,
}
impl ChainParams {
fn from_jtag_chain(chain: &[JtagChainItem], selected: usize) -> Option<Self> {
let mut params = Self::default();
let mut found = false;
for (index, tap) in chain.iter().enumerate() {
if index == selected {
params.irlen = tap.irlen;
found = true;
} else if found {
params.irpost += tap.irlen;
params.drpost += 1;
} else {
params.irpre += tap.irlen;
params.drpre += 1;
}
}
found.then_some(params)
}
}
#[derive(thiserror::Error, Debug)]
pub struct BatchExecutionError {
#[source]
pub error: crate::Error,
pub results: DeferredResultSet,
}
impl BatchExecutionError {
pub(crate) fn new(error: crate::Error, results: DeferredResultSet) -> BatchExecutionError {
BatchExecutionError { error, results }
}
}
impl std::fmt::Display for BatchExecutionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Error cause was {}. Successful command count {}",
self.error,
self.results.len()
)
}
}
#[derive(Debug, Clone)]
pub enum CommandResult {
None,
U8(u8),
U16(u16),
U32(u32),
VecU8(Vec<u8>),
}
impl CommandResult {
pub fn into_u32(self) -> u32 {
match self {
CommandResult::U32(val) => val,
_ => panic!("CommandResult is not a u32"),
}
}
pub fn into_u8(self) -> u8 {
match self {
CommandResult::U8(val) => val,
_ => panic!("CommandResult is not a u8"),
}
}
}
#[derive(Default, Debug)]
pub struct JtagCommandQueue {
commands: Vec<(DeferredResultIndex, JtagCommand)>,
}
impl JtagCommandQueue {
pub fn new() -> Self {
Self::default()
}
pub fn schedule(&mut self, command: impl Into<JtagCommand>) -> DeferredResultIndex {
let index = DeferredResultIndex::new();
self.commands.push((index.clone(), command.into()));
index
}
pub fn len(&self) -> usize {
self.commands.len()
}
pub fn is_empty(&self) -> bool {
self.commands.is_empty()
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &(DeferredResultIndex, JtagCommand)> {
self.commands.iter()
}
pub(crate) fn consume(&mut self, len: usize) {
self.commands.drain(..len);
}
}
#[derive(Debug, Default)]
pub struct DeferredResultSet(HashMap<DeferredResultIndex, CommandResult>);
impl DeferredResultSet {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self(HashMap::with_capacity(capacity))
}
pub(crate) fn push(&mut self, idx: &DeferredResultIndex, result: CommandResult) {
self.0.insert(idx.clone(), result);
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn merge_from(&mut self, other: DeferredResultSet) {
self.0.extend(other.0);
self.0.retain(|k, _| k.should_capture());
}
pub fn take(
&mut self,
index: DeferredResultIndex,
) -> Result<CommandResult, DeferredResultIndex> {
self.0.remove(&index).ok_or(index)
}
}
#[derive(Eq)]
pub struct DeferredResultIndex(Arc<()>);
impl PartialEq for DeferredResultIndex {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl fmt::Debug for DeferredResultIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DeferredResultIndex")
.field(&self.id())
.finish()
}
}
impl DeferredResultIndex {
fn new() -> Self {
Self(Arc::new(()))
}
fn id(&self) -> usize {
Arc::as_ptr(&self.0) as usize
}
pub(crate) fn should_capture(&self) -> bool {
Arc::strong_count(&self.0) > 1
}
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl std::hash::Hash for DeferredResultIndex {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id().hash(state)
}
}
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum AttachMethod {
Normal,
UnderReset,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_is_probe_factory() {
let probe_info = DebugProbeInfo::new(
"Mock probe",
0x12,
0x23,
Some("mock_serial".to_owned()),
&ftdi::FtdiProbeFactory,
None,
);
assert!(probe_info.is_probe_type::<ftdi::FtdiProbeFactory>());
assert!(!probe_info.is_probe_type::<espusbjtag::EspUsbJtagFactory>());
}
#[test]
fn test_parsing_many_colons() {
let selector: DebugProbeSelector = "303a:1001:DC:DA:0C:D3:FE:D8".try_into().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(
selector.serial_number,
Some("DC:DA:0C:D3:FE:D8".to_string())
);
}
}