pub(crate) mod cmsisdap;
pub(crate) mod espusbjtag;
pub(crate) mod fake_probe;
#[cfg(feature = "ftdi")]
pub(crate) mod ftdi;
pub(crate) mod jlink;
pub(crate) mod stlink;
use self::espusbjtag::list_espjtag_devices;
use crate::architecture::riscv::communication_interface::RiscvError;
use crate::error::Error;
use crate::Session;
use crate::{
architecture::arm::communication_interface::UninitializedArmProbe,
config::{RegistryError, TargetSelector},
};
use crate::{
architecture::{
arm::{
communication_interface::DapProbe,
sequences::{ArmDebugSequence, DefaultArmSequence},
PortType, SwoAccess,
},
riscv::communication_interface::RiscvCommunicationInterface,
},
Permissions,
};
use jlink::list_jlink_devices;
use std::{convert::TryFrom, fmt};
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 => write!(f, "SWD"),
WireProtocol::Jtag => write!(f, "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=0x{data:08x}")
}
}
}
}
#[derive(thiserror::Error, Debug)]
pub enum DebugProbeError {
#[error("USB Communication Error")]
Usb(#[source] Option<Box<dyn std::error::Error + Send + Sync>>),
#[error("The firmware on the probe is outdated")]
ProbeFirmwareOutdated,
#[error("An error specific to a probe type occurred")]
ProbeSpecific(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("Probe could not be created")]
ProbeCouldNotBeCreated(#[from] ProbeCreationError),
#[error("Probe does not support {0}")]
UnsupportedProtocol(WireProtocol),
#[error("The connected probe does not support the interface '{0}'")]
InterfaceNotAvailable(&'static str),
#[error("An error occurred while working with the registry")]
Registry(#[from] RegistryError),
#[error("The requested speed setting ({0} kHz) is not supported by the probe")]
UnsupportedSpeed(u32),
#[error("You need to be attached to the target to perform this action")]
NotAttached,
#[error("You need to be detached from the target to perform this action")]
Attached,
#[error("Failed to find the target or attach to the target")]
TargetNotFound,
#[error("Some functionality was not implemented yet: {0}")]
NotImplemented(&'static str),
#[error("This debug sequence is not supported on the used probe: {0}")]
DebugSequenceNotSupported(&'static str),
#[error("Error in previous batched command")]
BatchError(BatchCommand),
#[error("Command not supported by probe: {0}")]
CommandNotSupportedByProbe(&'static str),
#[error(transparent)]
Other(#[from] anyhow::Error),
#[error("Timeout occured during probe operation.")]
Timeout,
}
#[derive(thiserror::Error, Debug)]
pub enum ProbeCreationError {
#[error("Probe was not found.")]
NotFound,
#[error("USB device could not be opened. Please check the permissions.")]
CouldNotOpen,
#[error("{0}")]
HidApi(#[from] hidapi::HidError),
#[error("{0}")]
Rusb(#[from] rusb::Error),
#[error("An error specific to a probe type occurred: {0}")]
ProbeSpecific(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("{0}")]
Other(&'static str),
}
#[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,
}
}
#[tracing::instrument]
pub fn list_all() -> Vec<DebugProbeInfo> {
let mut list = cmsisdap::tools::list_cmsisdap_devices();
#[cfg(feature = "ftdi")]
{
list.extend(ftdi::list_ftdi_devices());
}
list.extend(stlink::tools::list_stlink_devices());
list.extend(list_jlink_devices());
list.extend(list_espjtag_devices());
list
}
#[tracing::instrument(skip_all)]
pub fn open(selector: impl Into<DebugProbeSelector> + Clone) -> Result<Self, DebugProbeError> {
match cmsisdap::CmsisDap::new_from_selector(selector.clone()) {
Ok(link) => return Ok(Probe::from_specific_probe(link)),
Err(DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::NotFound)) => {}
Err(e) => return Err(e),
};
#[cfg(feature = "ftdi")]
match ftdi::FtdiProbe::new_from_selector(selector.clone()) {
Ok(link) => return Ok(Probe::from_specific_probe(link)),
Err(DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::NotFound)) => {}
Err(e) => return Err(e),
};
match stlink::StLink::new_from_selector(selector.clone()) {
Ok(link) => return Ok(Probe::from_specific_probe(link)),
Err(DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::NotFound)) => {}
Err(e) => return Err(e),
};
match jlink::JLink::new_from_selector(selector.clone()) {
Ok(link) => return Ok(Probe::from_specific_probe(link)),
Err(DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::NotFound)) => {}
Err(e) => return Err(e),
};
match espusbjtag::EspUsbJtag::new_from_selector(selector) {
Ok(link) => return Ok(Probe::from_specific_probe(link)),
Err(DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::NotFound)) => {}
Err(e) => return Err(e),
};
Err(DebugProbeError::ProbeCouldNotBeCreated(
ProbeCreationError::NotFound,
))
}
pub fn get_name(&self) -> String {
self.inner.get_name().to_string()
}
pub fn attach(
mut self,
target: impl Into<TargetSelector>,
permissions: Permissions,
) -> Result<Session, Error> {
self.attached = true;
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.inner_attach()?;
Ok(())
}
pub fn attach_under_reset(
mut self,
target: impl Into<TargetSelector>,
permissions: Permissions,
) -> Result<Session, Error> {
self.attached = true;
Session::new(self, target.into(), AttachMethod::UnderReset, permissions).map_err(|e| {
if matches!(e, Error::Timeout) {
Error::Other(
anyhow::anyhow!("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."))
} else {
e
}
})
}
pub(crate) fn inner_attach(&mut self) -> Result<(), DebugProbeError> {
self.inner.attach()
}
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 speed_khz(&self) -> u32 {
self.inner.speed_khz()
}
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_into_riscv_interface(
self,
) -> Result<RiscvCommunicationInterface, (Self, RiscvError)> {
if !self.attached {
Err((self, DebugProbeError::NotAttached.into()))
} else {
self.inner
.try_get_riscv_interface()
.map_err(|(probe, err)| (Probe::from_attached_probe(probe), err))
}
}
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 trait DebugProbe: Send + fmt::Debug {
fn new_from_selector(
selector: impl Into<DebugProbeSelector>,
) -> Result<Box<Self>, DebugProbeError>
where
Self: Sized;
fn get_name(&self) -> &str;
fn speed_khz(&self) -> u32;
fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError>;
fn attach(&mut self) -> Result<(), DebugProbeError>;
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("ARM"),
))
}
fn try_get_riscv_interface(
self: Box<Self>,
) -> Result<RiscvCommunicationInterface, (Box<dyn DebugProbe>, RiscvError)> {
Err((
self.into_probe(),
DebugProbeError::InterfaceNotAvailable("RISCV").into(),
))
}
fn has_riscv_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)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DebugProbeType {
CmsisDap,
Ftdi,
StLink,
JLink,
EspJtag,
}
#[derive(Clone, PartialEq, Eq)]
pub struct DebugProbeInfo {
pub identifier: String,
pub vendor_id: u16,
pub product_id: u16,
pub serial_number: Option<String>,
pub probe_type: DebugProbeType,
pub hid_interface: Option<u8>,
}
impl std::fmt::Debug for DebugProbeInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{} (VID: {:04x}, PID: {:04x}, {}{:?})",
self.identifier,
self.vendor_id,
self.product_id,
self.serial_number
.clone()
.map_or("".to_owned(), |v| format!("Serial: {v}, ")),
self.probe_type
)
}
}
impl DebugProbeInfo {
pub fn new<S: Into<String>>(
identifier: S,
vendor_id: u16,
product_id: u16,
serial_number: Option<String>,
probe_type: DebugProbeType,
usb_hid_interface: Option<u8>,
) -> Self {
Self {
identifier: identifier.into(),
vendor_id,
product_id,
serial_number,
probe_type,
hid_interface: usb_hid_interface,
}
}
pub fn open(&self) -> Result<Probe, DebugProbeError> {
Probe::open(self)
}
}
#[derive(thiserror::Error, Debug)]
pub enum DebugProbeSelectorParseError {
#[error("The VID or PID could not be parsed: {0}")]
ParseInt(#[from] std::num::ParseIntError),
#[error("Please use a string in the form `VID:PID:<Serial>` where Serial is optional.")]
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 TryFrom<&str> for DebugProbeSelector {
type Error = DebugProbeSelectorParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let split = value.split(':').collect::<Vec<_>>();
let mut selector = if split.len() > 1 {
DebugProbeSelector {
vendor_id: u16::from_str_radix(split[0], 16)?,
product_id: u16::from_str_radix(split[1], 16)?,
serial_number: None,
}
} else {
return Err(DebugProbeSelectorParseError::Format);
};
if split.len() == 3 {
selector.serial_number = Some(split[2].to_string());
}
Ok(selector)
}
}
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 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 read_register(&mut self, address: u32, len: u32) -> Result<Vec<u8>, DebugProbeError>;
fn set_idle_cycles(&mut self, idle_cycles: u8);
fn get_idle_cycles(&self) -> u8;
fn set_ir_len(&mut self, len: u32);
fn write_register(
&mut self,
address: u32,
data: &[u8],
len: u32,
) -> Result<Vec<u8>, DebugProbeError>;
fn write_register_batch(
&mut self,
writes: &[JtagWriteCommand],
) -> Result<Vec<CommandResult>, BatchExecutionError> {
let mut results = Vec::new();
for write in writes {
match self
.write_register(write.address, &write.data, write.len)
.map_err(crate::Error::Probe)
.and_then(|response| (write.transform)(response))
{
Ok(res) => results.push(res),
Err(e) => return Err(BatchExecutionError::new(e, results.clone())),
}
}
Ok(results)
}
}
pub type DeferredResultIndex = usize;
#[derive(Debug, Clone)]
pub struct JtagWriteCommand {
pub address: u32,
pub data: Vec<u8>,
pub len: u32,
pub transform: fn(Vec<u8>) -> Result<CommandResult, crate::Error>,
}
#[derive(thiserror::Error, Debug)]
pub struct BatchExecutionError {
#[source]
pub error: crate::Error,
pub results: Vec<CommandResult>,
}
impl BatchExecutionError {
pub fn new(error: crate::Error, results: Vec<CommandResult>) -> 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>),
}
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum AttachMethod {
Normal,
UnderReset,
}