diff --git a/src/dap.rs b/src/dap.rs index 5bffa2c..ed7aaa2 100644 --- a/src/dap.rs +++ b/src/dap.rs @@ -9,6 +9,7 @@ mod request; mod response; mod state; +use bitflags::bitflags; pub use command::*; pub use request::*; pub use response::*; @@ -35,13 +36,26 @@ pub struct TransferConfig { pub match_mask: u32, } +bitflags! { + struct Capabilities: u8 { + const SWD = 0x01; + const JTAG = 0x02; + const SWO_UART = 0x04; + const SWO_MANCHESTER = 0x08; + const ATOMIC_COMMANDS = 0x10; + const TEST_DOMAIN_TIMER = 0x20; + const SWO_STREAMING = 0x40; + } +} + /// DAP handler. pub struct Dap<'a, DEPS, LEDS, WAIT, JTAG, SWD, SWO> { state: State, - swo: Option, + swo: SWO, swo_streaming: bool, version_string: &'a str, transfer_config: TransferConfig, + capabilities: Capabilities, // mode: Option, leds: LEDS, wait: WAIT, @@ -61,11 +75,38 @@ where dependencies: DEPS, leds: LEDS, wait: WAIT, - swo: Option, + swo: SWO, version_string: &'a str, ) -> Self { - // TODO: Replace with const assert - assert!(SWD::AVAILABLE || JTAG::AVAILABLE); + assert!(SWD::available(&dependencies) || JTAG::available(&dependencies)); + + let capabilities = { + let mut caps = Capabilities::empty(); + + // Bit 0: SWD optional + if SWD::available(&dependencies) { + caps.insert(Capabilities::SWD); + } + // Bit 1: JTAG optional + if JTAG::available(&dependencies) { + caps.insert(Capabilities::JTAG); + } + // Bit 2: SWO UART optional + // Bit 3: SWO Manchester optional + if swo.support().uart { + caps.insert(Capabilities::SWO_UART); + } + if swo.support().manchester { + caps.insert(Capabilities::SWO_MANCHESTER); + } + // Bit 4: Atomic commands not supported + // Bit 5: Test Domain Timer not supported + // Bit 6: SWO Streaming Trace optional + if swo.support().streaming { + caps.insert(Capabilities::SWO_STREAMING); + } + caps + }; Dap { state: State::new(dependencies), @@ -81,6 +122,7 @@ where // mode: None, leds, wait, + capabilities, } } @@ -152,7 +194,7 @@ where } } - fn process_info(&mut self, mut req: Request, resp: &mut ResponseWriter, version: DapVersion) { + fn process_info(&self, mut req: Request, resp: &mut ResponseWriter, version: DapVersion) { match DapInfoID::try_from(req.next_u8()) { // Return 0-length string for VendorID, ProductID, SerialNumber // to indicate they should be read from USB descriptor instead @@ -170,33 +212,11 @@ where Ok(DapInfoID::TargetName) => resp.write_u8(0), Ok(DapInfoID::Capabilities) => { resp.write_u8(1); - // Bit 0: SWD supported - // Bit 1: JTAG supported - // Bit 2: SWO UART supported - // Bit 3: SWO Manchester not supported - // Bit 4: Atomic commands not supported - // Bit 5: Test Domain Timer not supported - // Bit 6: SWO Streaming Trace supported - let swd = (SWD::AVAILABLE as u8) << 0; - let jtag = (JTAG::AVAILABLE as u8) << 1; - let swo = match &self.swo { - Some(swo) => { - let support = swo.support(); - (support.uart as u8) << 2 | (support.manchester as u8) << 3 - } - None => 0, - }; - let atomic = 0 << 4; - let swo_streaming = 1 << 6; - resp.write_u8(swd | jtag | swo | atomic | swo_streaming); + resp.write_u8(self.capabilities.bits); } Ok(DapInfoID::SWOTraceBufferSize) => { resp.write_u8(4); - let size = match &self.swo { - Some(swo) => swo.buffer_size(), - None => 0, - }; - resp.write_u32(size as u32); + resp.write_u32(self.swo.buffer_size()); } Ok(DapInfoID::MaxPacketCount) => { resp.write_u8(1); @@ -249,11 +269,15 @@ where info!( "DAP connect: {}, SWD: {}, JTAG: {}", port, - SWD::AVAILABLE, - JTAG::AVAILABLE + self.capabilities.contains(Capabilities::SWD), + self.capabilities.contains(Capabilities::JTAG), ); - match (SWD::AVAILABLE, JTAG::AVAILABLE, port) { + match ( + self.capabilities.contains(Capabilities::SWD), + self.capabilities.contains(Capabilities::JTAG), + port, + ) { // SWD (true, true, ConnectPort::Default) | (true, true, ConnectPort::SWD) @@ -298,8 +322,8 @@ where let idx = req.next_u8(); let word = req.next_u32(); - match (SWD::AVAILABLE, JTAG::AVAILABLE, &mut self.state) { - (_, true, State::Jtag(jtag)) => { + match &mut self.state { + State::Jtag(jtag) => { let config = jtag.config(); if !config.select_index(idx) { resp.write_err(); @@ -311,16 +335,14 @@ where resp.write_ok(); } - (true, _, State::Swd(swd)) => { + State::Swd(swd) => { let wait_retries = self.transfer_config.wait_retries; match swd.write_dp(wait_retries, swd::DPRegister::DPIDR, word) { Ok(_) => resp.write_ok(), Err(_) => resp.write_err(), } } - _ => { - resp.write_err(); - } + _ => resp.write_err(), } } @@ -470,48 +492,34 @@ where } fn process_swo_mode(&mut self, mut req: Request, resp: &mut ResponseWriter) { - let mode = req.next_u8(); + let success = match swo::SwoMode::try_from(req.next_u8()) { + Ok(mode) => self.swo.set_mode(mode), + _ => false, + }; - let swo = if let Some(swo) = &mut self.swo { - swo + if success { + resp.write_ok(); } else { resp.write_err(); - return; - }; - - match swo::SwoMode::try_from(mode) { - Ok(mode) => { - swo.set_mode(mode); - resp.write_ok(); - } - _ => resp.write_err(), } } fn process_swo_baudrate(&mut self, mut req: Request, resp: &mut ResponseWriter) { let target = req.next_u32(); - let actual = if let Some(swo) = &mut self.swo { - swo.set_baudrate(target) - } else { - 0 - }; + let actual = self.swo.set_baudrate(target); resp.write_u32(actual); } fn process_swo_control(&mut self, mut req: Request, resp: &mut ResponseWriter) { - let swo = if let Some(swo) = &mut self.swo { - swo - } else { - resp.write_err(); - return; + let success = match swo::SwoControl::try_from(req.next_u8()) { + Ok(control) => self.swo.set_control(control), + _ => false, }; - match swo::SwoControl::try_from(req.next_u8()) { - Ok(control) => { - swo.set_control(control); - resp.write_ok(); - } - _ => resp.write_err(), + if success { + resp.write_ok(); + } else { + resp.write_err(); } } @@ -520,11 +528,7 @@ where // Bit 0: trace capture active // Bit 6: trace stream error (always written as 0) // Bit 7: trace buffer overflow (always written as 0) - let (active, len) = if let Some(swo) = &self.swo { - (swo.is_active(), swo.bytes_available()) - } else { - (false, 0) - }; + let (active, len) = (self.swo.is_active(), self.swo.bytes_available()); resp.write_u8(active as u8); // Trace count: remaining bytes in buffer @@ -536,11 +540,8 @@ where // Bit 0: trace capture active // Bit 6: trace stream error (always written as 0) // Bit 7: trace buffer overflow (always written as 0) - let (active, len) = if let Some(swo) = &self.swo { - (swo.is_active(), swo.bytes_available()) - } else { - (false, 0) - }; + let (active, len) = (self.swo.is_active(), self.swo.bytes_available()); + resp.write_u8(active as u8); // Trace count: remaining bytes in buffer. resp.write_u32(len); @@ -551,11 +552,7 @@ where } fn process_swo_data(&mut self, mut req: Request, resp: &mut ResponseWriter) { - let active = if let Some(swo) = &mut self.swo { - swo.is_active() - } else { - false - }; + let active = self.swo.is_active(); // Write status byte to response resp.write_u8(active as u8); @@ -572,11 +569,7 @@ where } // Read data from UART - let len = if let Some(swo) = &mut self.swo { - swo.polling_data(&mut buf) - } else { - 0 - }; + let len = self.swo.polling_data(&mut buf); resp.skip(len as _); // Go back and write length @@ -1261,7 +1254,7 @@ fn transfer_with_retry( #[cfg(test)] mod test { use super::*; - use crate::mock_device::*; + use crate::{mock_device::*, swo::NoSwo}; use mockall::predicate::*; struct FakeLEDs {} @@ -1283,7 +1276,7 @@ mod test { StdDelayUs, MockSwdJtagDevice, MockSwdJtagDevice, - swo::MockSwo, + NoSwo, >; #[test] @@ -1292,7 +1285,7 @@ mod test { MockSwdJtagDevice::new(), FakeLEDs {}, StdDelayUs {}, - None, + NoSwo, "test_dap", ); @@ -1319,7 +1312,7 @@ mod test { MockSwdJtagDevice::new(), FakeLEDs {}, StdDelayUs {}, - None, + NoSwo, "test_dap", ); @@ -1349,7 +1342,7 @@ mod test { MockSwdJtagDevice::new(), FakeLEDs {}, StdDelayUs {}, - None, + NoSwo, "test_dap", ); @@ -1382,7 +1375,7 @@ mod test { MockSwdJtagDevice::new(), FakeLEDs {}, StdDelayUs {}, - None, + NoSwo, "test_dap", ); @@ -1416,7 +1409,7 @@ mod test { MockSwdJtagDevice::new(), FakeLEDs {}, StdDelayUs {}, - None, + NoSwo, "test_dap", ); diff --git a/src/driver/bitbang.rs b/src/driver/bitbang.rs new file mode 100644 index 0000000..de4d3b3 --- /dev/null +++ b/src/driver/bitbang.rs @@ -0,0 +1,447 @@ +//! Bitbanging (GPIO-driven) SWD and JTAG implementation. + +use core::num::NonZeroU32; + +use crate::{ + dap::DelayNs, + jtag::{self, TapConfig}, + swd::{self, APnDP, DPRegister}, + swj::{Dependencies, Pins}, +}; + +/// A trait for a pin that can be used as an input or output. +pub trait InputOutputPin { + fn set_as_output(&mut self); + fn set_high(&mut self, high: bool); + + fn set_as_input(&mut self); + fn is_high(&mut self) -> bool; +} + +/// A trait for a delay implementation that can be used to delay for a number of CPU cycles. +pub trait DelayCycles: DelayNs { + fn cpu_clock(&self) -> u32; + fn delay_cycles(&mut self, cycles: u32); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Dir { + Write = 0, + Read = 1, +} + +pub struct BitbangAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + nreset: IO, + tdi: IO, + tms_swdio: IO, + tck_swclk: IO, + tdo: IO, + delay: D, + bit_cycles: u32, + target_clock: Option, + swd_config: swd::Config, + jtag_config: jtag::Config, +} + +impl BitbangAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + pub fn new( + nreset: IO, + tdi: IO, + tms_swdio: IO, + tck_swclk: IO, + tdo: IO, + delay: D, + scan_chain: &'static mut [TapConfig], + ) -> Self { + let mut this = Self { + nreset, + tdi, + tms_swdio, + tck_swclk, + tdo, + delay, + bit_cycles: 0, + target_clock: None, + swd_config: swd::Config::default(), + jtag_config: jtag::Config::new(scan_chain), + }; + + // Start with pins configured to SWD - JTAG pins are a superset, and SWJ sequences need this. + this.pins_to_swd(); + this + } + + fn pins_to_swd(&mut self) { + self.tms_swdio.set_high(true); + self.tms_swdio.set_as_output(); + + self.tck_swclk.set_as_output(); + + self.nreset.set_as_input(); + self.tdi.set_as_input(); + self.tdo.set_as_input(); + } + + fn pins_to_jtag(&mut self) { + self.nreset.set_high(true); + self.nreset.set_as_output(); + + self.tms_swdio.set_high(true); + self.tms_swdio.set_as_output(); + + self.tck_swclk.set_as_output(); + + self.tdi.set_as_output(); + self.tdo.set_as_input(); + } + + fn req(&mut self, port: APnDP, dir: Dir, addr: DPRegister) { + let req = (port as u32) | (dir as u32) << 1 | (addr as u32) << 2; + let parity = req.count_ones() % 2; + self.shift_out(0b10000001 | req << 1 | parity << 5, 8); + } + + pub fn read(&mut self, port: APnDP, addr: DPRegister) -> swd::Result { + self.req(port, Dir::Read, addr); + + let ack = self.shift_in(4); // turnaround + ack + match (ack >> 1) & 0b111 { + 0b001 => {} // ok + 0b010 => { + self.shift_in(1); // turnaround + return Err(swd::Error::AckWait); + } + 0b100 => { + self.shift_in(1); // turnaround + return Err(swd::Error::AckFault); + } + _ => { + self.shift_in(1); // turnaround + return Err(swd::Error::AckUnknown(ack as u8)); + } + } + + let data = self.shift_in(32); + let parity = self.shift_in(1); + if parity != data.count_ones() % 2 { + return Err(swd::Error::BadParity); + } + + self.shift_in(1); // turnaround + + Ok(data) + } + + pub fn write(&mut self, port: APnDP, addr: DPRegister, data: u32) -> swd::Result<()> { + self.req(port, Dir::Write, addr); + + let ack = self.shift_in(5); // turnaround + ack + turnaround + match (ack >> 1) & 0b111 { + 0b001 => {} // ok + 0b010 => return Err(swd::Error::AckWait), + 0b100 => return Err(swd::Error::AckFault), + _ => return Err(swd::Error::AckUnknown(ack as _)), + } + + self.shift_out(data, 32); + self.shift_out(data.count_ones() % 2, 1); + + Ok(()) + } + + fn shift_out(&mut self, val: u32, num_bits: usize) { + self.tms_swdio.set_as_output(); + for i in 0..num_bits { + self.tms_swdio.set_high(val & (1 << i) != 0); + self.tck_swclk.set_high(false); + self.wait(); + self.tck_swclk.set_high(true); + self.wait(); + } + } + + fn shift_in(&mut self, num_bits: usize) -> u32 { + self.tms_swdio.set_as_input(); + let mut val = 0; + for i in 0..num_bits { + self.tck_swclk.set_high(false); + self.wait(); + val |= (self.tms_swdio.is_high() as u32) << i; + self.tck_swclk.set_high(true); + self.wait(); + } + val + } + + fn shift_jtag(&mut self, tms: bool, tdi: u32, num_bits: usize) -> u32 { + self.tms_swdio.set_as_output(); + self.tms_swdio.set_high(tms); + let mut tdo = 0; + for i in 0..num_bits { + self.tdi.set_high(tdi & (1 << i) != 0); + self.tck_swclk.set_high(false); + self.wait(); + tdo |= (self.tdo.is_high() as u32) << i; + self.tck_swclk.set_high(true); + self.wait(); + } + tdo + } + + fn wait(&mut self) { + self.delay.delay_cycles(self.bit_cycles); + } + + fn apply_clock(&mut self) { + if let Some(target_clock) = self.target_clock { + self.bit_cycles = (self.delay.cpu_clock() / 2) + .div_ceil(target_clock.get()) + .max(1); + + self.target_clock = None; + } + // TODO implement proper setup when converting into swd/jtag + self.tck_swclk.set_as_output(); + self.tdi.set_as_output(); + } + + fn set_target_clock(&mut self, max_frequency: u32) -> bool { + debug!("set frequency({})", max_frequency); + match NonZeroU32::new(max_frequency) { + Some(frequency) => { + self.target_clock = Some(frequency); + true + } + _ => false, + } + } +} + +impl Dependencies, JtagAdapter> + for BitbangAdapter +{ + fn high_impedance_mode(&mut self) { + self.tms_swdio.set_as_input(); + self.tck_swclk.set_as_input(); + self.nreset.set_as_input(); + self.tdi.set_as_input(); + self.tdo.set_as_input(); + } + + fn process_swj_clock(&mut self, max_frequency: u32) -> bool { + self.set_target_clock(max_frequency) + } + + fn process_swj_pins(&mut self, output: Pins, mask: Pins, wait_us: u32) -> Pins { + if mask.contains(Pins::SWCLK) { + self.tck_swclk.set_high(output.contains(Pins::SWCLK)); + } + if mask.contains(Pins::SWDIO) { + self.tms_swdio.set_high(output.contains(Pins::SWDIO)); + } + if mask.contains(Pins::NRESET) { + self.nreset.set_high(output.contains(Pins::NRESET)); + } + if mask.contains(Pins::TDO) { + self.tdo.set_high(output.contains(Pins::TDO)); + } + + if wait_us != 0 { + self.delay.delay_us(wait_us); + } + + let mut read = Pins::empty(); + + read.set(Pins::SWCLK, self.tck_swclk.is_high()); + read.set(Pins::SWDIO, self.tms_swdio.is_high()); + read.set(Pins::NRESET, self.nreset.is_high()); + read.set(Pins::TDO, self.tdo.is_high()); + read.set(Pins::TDI, self.tdi.is_high()); + read.set(Pins::NTRST, true); + + read + } + + fn process_swj_sequence(&mut self, data: &[u8], mut num_bits: usize) { + self.apply_clock(); + debug!("write_sequence({})", num_bits); + for b in data.iter().copied() { + if num_bits == 0 { + break; + } + let bits = num_bits.min(8); + self.shift_out(b as u32, bits); + num_bits -= bits; + } + } + + fn swd_config(&mut self) -> &mut swd::Config { + &mut self.swd_config + } + + fn jtag_config(&mut self) -> &mut jtag::Config { + &mut self.jtag_config + } +} + +pub struct SwdAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + inner: BitbangAdapter, +} + +impl From> for SwdAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + fn from(mut inner: BitbangAdapter) -> Self { + inner.pins_to_swd(); + + Self { inner } + } +} +impl From> for BitbangAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + fn from(inner: SwdAdapter) -> Self { + inner.inner + } +} + +impl swd::Swd> for SwdAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + fn available(deps: &BitbangAdapter) -> bool { + // TODO: Check if the SWD pins are available + true + } + + fn read_inner(&mut self, port: APnDP, addr: DPRegister) -> swd::Result { + debug!("read_inner({:?}, {:?})", port, addr); + self.inner.read(port, addr) + } + + fn write_inner(&mut self, port: APnDP, addr: DPRegister, data: u32) -> swd::Result<()> { + debug!("write_inner({:?}, {:?}, {:x})", port, addr, data); + self.inner.write(port, addr, data) + } + + fn set_clock(&mut self, max_frequency: u32) -> bool { + self.inner.set_target_clock(max_frequency) + } + + /// Write a sequence of bits using SWDIO and the clock line running at the configured freq. + fn write_sequence(&mut self, num_bits: usize, data: &[u8]) -> swd::Result<()> { + self.inner.process_swj_sequence(data, num_bits); + Ok(()) + } + + /// Read a sequence of bits using SWDIO and the clock line running at the configured freq. + fn read_sequence(&mut self, mut num_bits: usize, data: &mut [u8]) -> swd::Result<()> { + self.inner.apply_clock(); + debug!("read_sequence({})", num_bits); + for b in data.iter_mut() { + if num_bits == 0 { + break; + } + let bits = num_bits.min(8); + *b = self.inner.shift_in(bits) as u8; + num_bits -= bits; + } + Ok(()) + } + + fn config(&mut self) -> &mut swd::Config { + &mut self.inner.swd_config + } +} + +pub struct JtagAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + inner: BitbangAdapter, +} + +impl From> for JtagAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + fn from(mut inner: BitbangAdapter) -> Self { + inner.pins_to_jtag(); + + Self { inner } + } +} + +impl From> for BitbangAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + fn from(mut inner: JtagAdapter) -> Self { + // Reset pin configuration to default. + inner.inner.pins_to_swd(); + inner.inner + } +} + +impl jtag::Jtag> for JtagAdapter +where + IO: InputOutputPin, + D: DelayCycles, +{ + fn available(deps: &BitbangAdapter) -> bool { + // TODO: Check if the JTAG pins are actually available + true + } + + fn set_clock(&mut self, max_frequency: u32) -> bool { + self.inner.set_target_clock(max_frequency) + } + + fn config(&mut self) -> &mut jtag::Config { + &mut self.inner.jtag_config + } + + fn sequence(&mut self, info: jtag::SequenceInfo, tdi: &[u8], mut rxbuf: &mut [u8]) { + let mut n_bits = info.n_bits; + for &byte in tdi { + let bits = n_bits.min(8) as usize; + n_bits -= bits as u8; + let tdo = self.inner.shift_jtag(info.tms, byte as u32, bits); + if info.capture && !rxbuf.is_empty() { + rxbuf[0] = tdo as u8; + rxbuf = &mut rxbuf[1..]; + } + } + } + + fn tms_sequence(&mut self, tms: &[bool]) { + for &bit in tms.iter() { + self.inner.tms_swdio.set_high(bit); + self.inner.tck_swclk.set_high(false); + self.inner.wait(); + self.inner.tck_swclk.set_high(true); + self.inner.wait(); + } + } +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs new file mode 100644 index 0000000..c0c84ac --- /dev/null +++ b/src/driver/mod.rs @@ -0,0 +1,3 @@ +//! Implementations for parts of dap-rs, built on top of various platforms. + +pub mod bitbang; diff --git a/src/jtag.rs b/src/jtag.rs index 157407e..31c1a7d 100644 --- a/src/jtag.rs +++ b/src/jtag.rs @@ -183,8 +183,8 @@ const DAP_TRANSFER_OK_FAULT: u32 = 0x02; /// JTAG interface. pub trait Jtag: From { - /// If JTAG is available or not. - const AVAILABLE: bool; + /// Returns whether JTAG is available or not. + fn available(deps: &DEPS) -> bool; /// Returns a mutable reference to the JTAG interface configuration. fn config(&mut self) -> &mut Config; diff --git a/src/lib.rs b/src/lib.rs index a27fd72..296188a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub(crate) mod fmt; /// TODO: Dap docs pub mod dap; +pub mod driver; pub mod jtag; pub mod swd; pub mod swj; diff --git a/src/mock_device.rs b/src/mock_device.rs index cd8a3e3..3ffe130 100644 --- a/src/mock_device.rs +++ b/src/mock_device.rs @@ -51,7 +51,9 @@ impl swj::Dependencies for MockSwdJtagDevice { } impl swd::Swd for MockSwdJtagDevice { - const AVAILABLE: bool = true; + fn available(_: &Self) -> bool { + true + } fn set_clock(&mut self, max_frequency: u32) -> bool { SwdJtagDevice::set_clock(self, max_frequency) @@ -79,7 +81,9 @@ impl swd::Swd for MockSwdJtagDevice { } impl jtag::Jtag for MockSwdJtagDevice { - const AVAILABLE: bool = true; + fn available(_: &Self) -> bool { + true + } fn set_clock(&mut self, max_frequency: u32) -> bool { SwdJtagDevice::set_clock(self, max_frequency) diff --git a/src/swd.rs b/src/swd.rs index 602c7da..02f71c1 100644 --- a/src/swd.rs +++ b/src/swd.rs @@ -111,8 +111,8 @@ impl Default for Config { /// Definition of SWD communication. pub trait Swd: From { - /// If SWD is available or not. - const AVAILABLE: bool; + /// Returns whether SWD is available or not. + fn available(deps: &DEPS) -> bool; /// Returns a mutable reference to the SWD interface configuration. fn config(&mut self) -> &mut Config; diff --git a/src/swo.rs b/src/swo.rs index 2fd5a6f..c0f650b 100644 --- a/src/swo.rs +++ b/src/swo.rs @@ -29,8 +29,12 @@ pub enum SwoControl { #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SwoSupport { + /// `true` if the UART encoding is supported. pub uart: bool, + /// `true` if the Manchester encoding is supported. pub manchester: bool, + /// `true` if the USB interface has a trace endpoint. + pub streaming: bool, } #[derive(Copy, Clone, Debug)] @@ -42,12 +46,11 @@ pub struct SwoStatus { pub bytes_available: u32, } -#[cfg_attr(test, mockall::automock)] pub trait Swo { - fn set_transport(&mut self, transport: SwoTransport); - fn set_mode(&mut self, mode: SwoMode); + fn set_transport(&mut self, transport: SwoTransport) -> bool; + fn set_mode(&mut self, mode: SwoMode) -> bool; fn set_baudrate(&mut self, baudrate: u32) -> u32; - fn set_control(&mut self, control: SwoControl); + fn set_control(&mut self, control: SwoControl) -> bool; fn polling_data(&mut self, buf: &mut [u8]) -> u32; fn streaming_data(&mut self); // -> SomeBufferFromStreaming; // TODO: What is a good interface? fn is_active(&self) -> bool; @@ -56,3 +59,49 @@ pub trait Swo { fn support(&self) -> SwoSupport; fn status(&mut self) -> SwoStatus; } + +/// Marker struct for no SWO support. +pub struct NoSwo; + +impl Swo for NoSwo { + fn set_transport(&mut self, _transport: SwoTransport) -> bool { + false + } + fn set_mode(&mut self, _mode: SwoMode) -> bool { + false + } + fn set_baudrate(&mut self, _baudrate: u32) -> u32 { + 0 + } + fn set_control(&mut self, _control: SwoControl) -> bool { + false + } + fn polling_data(&mut self, _buf: &mut [u8]) -> u32 { + 0 + } + fn streaming_data(&mut self) {} + fn is_active(&self) -> bool { + false + } + fn bytes_available(&self) -> u32 { + 0 + } + fn buffer_size(&self) -> u32 { + 0 + } + fn support(&self) -> SwoSupport { + SwoSupport { + uart: false, + manchester: false, + streaming: false, + } + } + fn status(&mut self) -> SwoStatus { + SwoStatus { + active: false, + trace_error: false, + trace_overrun: false, + bytes_available: 0, + } + } +}