pub mod general;
pub mod jtag;
pub mod swd;
pub mod swj;
pub mod swo;
pub mod transfer;
use crate::probe::cmsisdap::commands::general::info::PacketSizeCommand;
use crate::probe::usb_util::InterfaceExt;
use crate::probe::{ProbeError, WireProtocol};
use std::io::ErrorKind;
use std::str::Utf8Error;
use std::time::Duration;
use self::general::host_status::HostStatusRequest;
use self::swj::clock::SWJClockRequest;
use self::transfer::InnerTransferBlockRequest;
pub(crate) const DEFAULT_USB_TIMEOUT: Duration = Duration::from_millis(1000);
#[derive(Debug, thiserror::Error, docsplay::Display)]
pub enum CmsisDapError {
Send {
command_id: CommandId,
source: SendError,
},
ErrorResponse(#[source] RequestError),
TooMuchData,
SwoBaudrateNotConfigured,
SwoTraceStreamError,
SwoModeNotAvailable,
SwoReadError(#[source] std::io::Error),
NoPacketSize,
InvalidIdCode,
InvalidIR,
ProbeFirmwareOutdated(&'static str),
}
impl ProbeError for CmsisDapError {}
#[derive(Debug, thiserror::Error, docsplay::Display)]
pub enum SendError {
#[cfg(feature = "cmsisdap_v1")]
HidApi(#[from] hidapi::HidError),
UsbError(std::io::Error),
NotEnoughData,
InvalidResponseStatus,
ConnectResponseError(u8),
CommandIdMismatch(u8, CommandId),
#[ignore_extra_doc_attributes]
InvalidString(#[from] Utf8Error),
UnexpectedAnswer,
Timeout,
}
impl From<std::io::Error> for SendError {
fn from(error: std::io::Error) -> Self {
match error.kind() {
ErrorKind::TimedOut => SendError::Timeout,
_ => SendError::UsbError(error),
}
}
}
#[derive(Debug, thiserror::Error, docsplay::Display)]
pub enum RequestError {
SWJClock { request: SWJClockRequest },
SwdConfigure {
request: swd::configure::ConfigureRequest,
},
JtagConfigure {
request: jtag::configure::ConfigureRequest,
},
TransferConfigure {
request: transfer::configure::ConfigureRequest,
},
SwjSequence {
request: swj::sequence::SequenceRequest,
},
JtagSequence {
request: jtag::sequence::SequenceRequest,
},
BrokenScanChain {
name: &'static str,
expected_bit: u8,
},
EmptyScanChain { name: &'static str },
SwoTransport { transport: swo::TransportRequest },
SwoMode { mode: swo::ModeRequest },
SwoControl { command: swo::ControlRequest },
InitFailed { protocol: Option<WireProtocol> },
HostStatus { request: HostStatusRequest },
BlockTransfer {
dap_index: u8,
transfer_count: u16,
transfer_request: InnerTransferBlockRequest,
},
}
pub enum CmsisDapDevice {
#[cfg(feature = "cmsisdap_v1")]
V1 {
handle: hidapi::HidDevice,
report_size: usize,
usb_timeout: Duration,
},
V2 {
handle: nusb::Interface,
out_ep: u8,
in_ep: u8,
max_packet_size: usize,
swo_ep: Option<(u8, usize)>,
usb_timeout: Duration,
},
}
impl CmsisDapDevice {
fn usb_timeout(&self) -> Duration {
match self {
#[cfg(feature = "cmsisdap_v1")]
Self::V1 { usb_timeout, .. } => *usb_timeout,
Self::V2 { usb_timeout, .. } => *usb_timeout,
}
}
fn set_usb_timeout(&mut self, timeout: Duration) {
match self {
#[cfg(feature = "cmsisdap_v1")]
Self::V1 { usb_timeout, .. } => *usb_timeout = timeout,
Self::V2 { usb_timeout, .. } => *usb_timeout = timeout,
}
}
fn read(&self, buf: &mut [u8]) -> Result<usize, SendError> {
match self {
#[cfg(feature = "cmsisdap_v1")]
CmsisDapDevice::V1 { handle, .. } => {
match handle.read_timeout(buf, self.usb_timeout().as_millis() as i32)? {
0 => Err(SendError::Timeout),
n => Ok(n),
}
}
CmsisDapDevice::V2 { handle, in_ep, .. } => {
Ok(handle.read_bulk(*in_ep, buf, self.usb_timeout())?)
}
}
}
fn write(&self, buf: &[u8]) -> Result<usize, SendError> {
match self {
#[cfg(feature = "cmsisdap_v1")]
CmsisDapDevice::V1 { handle, .. } => Ok(handle.write(buf)?),
CmsisDapDevice::V2 { handle, out_ep, .. } => {
Ok(handle.write_bulk(*out_ep, &buf[1..], self.usb_timeout())?)
}
}
}
pub(super) fn drain(&self) {
tracing::debug!("Draining probe of any pending data.");
match self {
#[cfg(feature = "cmsisdap_v1")]
CmsisDapDevice::V1 {
handle,
report_size,
..
} => loop {
let mut discard = vec![0u8; report_size + 1];
match handle.read_timeout(&mut discard, 1) {
Ok(n) if n != 0 => continue,
_ => break,
}
},
CmsisDapDevice::V2 {
handle,
in_ep,
max_packet_size,
..
} => {
let timeout = Duration::from_millis(1);
let mut discard = vec![0u8; *max_packet_size];
loop {
match handle.read_bulk(*in_ep, &mut discard, timeout) {
Ok(n) if n != 0 => continue,
_ => break,
}
}
}
}
}
pub(super) fn set_packet_size(&mut self, packet_size: usize) {
tracing::debug!("Configuring probe to use packet size {}", packet_size);
match self {
#[cfg(feature = "cmsisdap_v1")]
CmsisDapDevice::V1 { report_size, .. } => {
*report_size = packet_size;
}
CmsisDapDevice::V2 {
max_packet_size, ..
} => {
*max_packet_size = packet_size;
}
}
}
pub(super) fn find_packet_size(&mut self) -> Result<usize, CmsisDapError> {
for repeat in 0..16 {
tracing::debug!("Attempt {} to find packet size", repeat + 1);
let old_timeout = self.usb_timeout();
self.set_usb_timeout(Duration::from_millis(50));
match send_command(self, &PacketSizeCommand {}) {
Ok(size) => {
tracing::debug!("Success: packet size is {}", size);
self.set_usb_timeout(old_timeout);
self.set_packet_size(size as usize);
return Ok(size as usize);
}
Err(CmsisDapError::Send {
source: SendError::Timeout,
..
}) => (),
Err(e) => return Err(e),
}
}
Err(CmsisDapError::NoPacketSize)
}
pub(super) fn swo_streaming_supported(&self) -> bool {
match self {
#[cfg(feature = "cmsisdap_v1")]
CmsisDapDevice::V1 { .. } => false,
CmsisDapDevice::V2 { swo_ep, .. } => swo_ep.is_some(),
}
}
pub(super) fn read_swo_stream(&self, timeout: Duration) -> Result<Vec<u8>, CmsisDapError> {
match self {
#[cfg(feature = "cmsisdap_v1")]
CmsisDapDevice::V1 { .. } => Err(CmsisDapError::SwoModeNotAvailable),
CmsisDapDevice::V2 { handle, swo_ep, .. } => match swo_ep {
Some((ep, len)) => {
let mut buf = vec![0u8; *len];
match handle.read_bulk(*ep, &mut buf, timeout) {
Ok(n) => {
buf.truncate(n);
Ok(buf)
}
Err(e) if e.kind() == ErrorKind::TimedOut => {
buf.truncate(0);
Ok(buf)
}
Err(e) => Err(CmsisDapError::SwoReadError(e)),
}
}
None => Err(CmsisDapError::SwoModeNotAvailable),
},
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum Status {
DapOk = 0x00,
DapError = 0xFF,
}
impl Status {
pub fn from_byte(value: u8) -> Result<Self, SendError> {
match value {
0x00 => Ok(Status::DapOk),
0xFF => Ok(Status::DapError),
_ => Err(SendError::InvalidResponseStatus),
}
}
}
#[derive(Debug, Clone, Copy)]
#[expect(unused)]
pub enum CommandId {
Info = 0x00,
HostStatus = 0x01,
Connect = 0x02,
Disconnect = 0x03,
WriteAbort = 0x08,
Delay = 0x09,
ResetTarget = 0x0A,
SwjPins = 0x10,
SwjClock = 0x11,
SwjSequence = 0x12,
SwdConfigure = 0x13,
SwdSequence = 0x1D,
SwoTransport = 0x17,
SwoMode = 0x18,
SwoBaudrate = 0x19,
SwoControl = 0x1A,
SwoStatus = 0x1B,
SwoExtendedStatus = 0x1E,
SwoData = 0x1C,
JtagSequence = 0x14,
JtagConfigure = 0x15,
JtagIdcode = 0x16,
TransferConfigure = 0x04,
Transfer = 0x05,
TransferBlock = 0x06,
TransferAbort = 0x07,
ExecuteCommands = 0x7F,
QueueCommands = 0x7E,
UartTransport = 0x1F,
UartConfigure = 0x20,
UartControl = 0x22,
UartStatus = 0x23,
UartTransfer = 0x21,
}
pub(crate) trait Request {
const COMMAND_ID: CommandId;
type Response;
fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, SendError>;
fn parse_response(&self, buffer: &[u8]) -> Result<Self::Response, SendError>;
}
pub(crate) fn send_command<Req: Request>(
device: &mut CmsisDapDevice,
request: &Req,
) -> Result<Req::Response, CmsisDapError> {
send_command_inner(device, request).map_err(|e| CmsisDapError::Send {
command_id: Req::COMMAND_ID,
source: e,
})
}
fn send_command_inner<Req: Request>(
device: &mut CmsisDapDevice,
request: &Req,
) -> Result<Req::Response, SendError> {
let buffer_len: usize = match device {
#[cfg(feature = "cmsisdap_v1")]
CmsisDapDevice::V1 { report_size, .. } => *report_size + 1,
CmsisDapDevice::V2 {
max_packet_size, ..
} => *max_packet_size + 1,
};
let mut buffer = vec![0; buffer_len];
buffer[1] = Req::COMMAND_ID as u8;
#[cfg_attr(not(feature = "cmsisdap_v1"), allow(unused_mut))]
let mut size = request.to_bytes(&mut buffer[2..])? + 2;
#[cfg(feature = "cmsisdap_v1")]
if let CmsisDapDevice::V1 { report_size, .. } = device {
size = *report_size + 1;
}
let _ = device.write(&buffer[..size])?;
trace_buffer("Transmit buffer", &buffer[..size]);
let bytes_read = device.read(&mut buffer)?;
let response_data = &buffer[..bytes_read];
trace_buffer("Receive buffer", response_data);
if response_data.is_empty() {
return Err(SendError::NotEnoughData);
}
if response_data[0] == Req::COMMAND_ID as u8 {
request.parse_response(&response_data[1..])
} else {
Err(SendError::CommandIdMismatch(
response_data[0],
Req::COMMAND_ID,
))
}
}
fn trace_buffer(name: &str, buf: &[u8]) {
if tracing::enabled!(tracing::Level::TRACE) {
let len = buf.len();
let cut = len + 1 - buf.iter().rev().position(|&x| x != 0).unwrap_or(len);
let end = cut.clamp(1, len);
tracing::trace!("{}: {:02X?}...", name, &buf[..end]);
}
}