use crate::util::rtt::{
ChannelMode, RttActiveDownChannel, RttActiveUpChannel, RttConfig, RttConnection,
};
use probe_rs::{
Core, MemoryInterface, Target,
flashing::FlashLoader,
rtt::{Error, Rtt, ScanRegion},
};
pub struct RttClient {
pub scan_region: ScanRegion,
channel_modes: Vec<Option<ChannelMode>>,
need_configure: bool,
target: Option<RttConnection>,
last_control_block_address: Option<u64>,
disallow_clearing_rtt_header: bool,
try_attaching: bool,
polled_data: bool,
core_id: usize,
}
impl RttClient {
pub fn new(config: RttConfig, scan_region: ScanRegion, target: &Target) -> Self {
let core_id = if let ScanRegion::Exact(address) = scan_region {
target.core_index_by_address(address).unwrap_or(0)
} else {
0
};
Self {
scan_region,
channel_modes: config.channels.iter().map(|c| c.mode).collect(),
need_configure: true,
target: None,
last_control_block_address: None,
disallow_clearing_rtt_header: false,
try_attaching: true,
polled_data: false,
core_id,
}
}
pub fn disallow_clearing_rtt_header(&mut self) {
self.disallow_clearing_rtt_header = true;
}
pub fn configure_from_loader(&mut self, loader: &FlashLoader) {
if let ScanRegion::Exact(address) = self.scan_region
&& loader.has_data_for_address(address)
{
tracing::debug!(
"RTT control block is initialized by flash loader. Disabling clearing."
);
self.disallow_clearing_rtt_header()
}
}
pub fn is_attached(&self) -> bool {
self.target.is_some()
}
fn try_attach_impl(&mut self, core: &mut Core) -> Result<bool, Error> {
if self.is_attached() {
return Ok(true);
}
if !self.try_attaching {
return Ok(false);
}
let location = if let Some(location) = self.last_control_block_address {
location
} else {
let location = match Rtt::find_contol_block(core, &self.scan_region) {
Ok(location) => location,
Err(Error::ControlBlockNotFound) => {
tracing::debug!("Failed to attach - control block not found");
return Ok(false);
}
Err(Error::NoControlBlockLocation) => {
tracing::debug!("Failed to attach - control block location not specified");
self.try_attaching = false;
return Ok(false);
}
Err(error) => return Err(error),
};
self.last_control_block_address = Some(location);
location
};
let rtt = match Rtt::attach_at(core, location) {
Ok(rtt) => rtt,
Err(Error::ControlBlockNotFound) => {
self.last_control_block_address = None;
tracing::debug!("Failed to attach - control block not found");
return Ok(false);
}
Err(Error::ControlBlockCorrupted(error)) => {
tracing::debug!("Failed to attach - control block corrupted: {}", error);
return Ok(false);
}
Err(error) => return Err(error),
};
match RttConnection::new(rtt) {
Ok(rtt) => self.target = Some(rtt),
Err(Error::ControlBlockCorrupted(error)) => {
tracing::debug!("Failed to attach - control block corrupted: {}", error);
}
Err(error) => return Err(error),
};
self.need_configure = true;
Ok(self.target.is_some())
}
pub fn try_attach(&mut self, core: &mut Core) -> Result<bool, Error> {
let attached = self.try_attach_impl(core)?;
if attached && self.need_configure {
self.configure(core)?;
self.need_configure = false;
}
Ok(self.is_attached())
}
pub fn poll_channel(&mut self, core: &mut Core, channel: u32) -> Result<&[u8], Error> {
self.try_attach(core)?;
if let Some(ref mut target) = self.target {
match target.poll_channel(core, channel) {
Ok(()) => self.polled_data = true,
Err(Error::ControlBlockCorrupted(error)) => {
if self.polled_data {
tracing::warn!("RTT control block corrupted ({error}), re-attaching");
}
self.target = None;
self.polled_data = false;
}
Err(Error::ReadPointerChanged) => {
if self.polled_data {
tracing::warn!("RTT read pointer changed, re-attaching");
}
self.target = None;
self.polled_data = false;
}
Err(other) => return Err(other),
}
}
if let Some(ref target) = self.target {
return target.channel_data(channel);
}
Ok(&[])
}
pub(crate) fn write_down_channel(
&mut self,
core: &mut Core,
channel: u32,
input: impl AsRef<[u8]>,
) -> Result<(), Error> {
self.try_attach(core)?;
let Some(target) = self.target.as_mut() else {
return Ok(());
};
target.write_down_channel(core, channel, input)
}
pub fn clean_up(&mut self, core: &mut Core) -> Result<(), Error> {
self.need_configure = true;
if let Some(target) = self.target.as_mut() {
target.clean_up(core)?;
}
Ok(())
}
pub(crate) fn clear_control_block(&mut self, core: &mut Core) -> Result<(), Error> {
if self.disallow_clearing_rtt_header {
tracing::debug!("Not clearing RTT control block");
return Ok(());
}
self.try_attach(core)?;
tracing::debug!("Clearing RTT control block");
if let Some(mut target) = self.target.take() {
target.clear_control_block(core)?;
} else {
if let Some(location) = self.last_control_block_address.take() {
if let ScanRegion::Exact(scan_location) = self.scan_region {
if location == scan_location {
if core.is_64_bit() {
const SIZE_64B: usize = 16 + 2 * 8;
core.write_8(location, &[0; SIZE_64B])?;
} else {
const SIZE_32B: usize = 16 + 2 * 4;
core.write_8(location, &[0; SIZE_32B])?;
}
}
} else {
let mut magic = [0; Rtt::RTT_ID.len()];
core.read_8(location, &mut magic)?;
if magic == Rtt::RTT_ID {
core.write_8(location, &[0; 16])?;
}
}
}
}
Ok(())
}
pub(crate) fn up_channels(&self) -> &[RttActiveUpChannel] {
self.target
.as_ref()
.map(|t| t.active_up_channels.as_slice())
.unwrap_or_default()
}
pub(crate) fn down_channels(&self) -> &[RttActiveDownChannel] {
self.target
.as_ref()
.map(|t| t.active_down_channels.as_slice())
.unwrap_or_default()
}
pub(crate) fn core_id(&self) -> usize {
self.core_id
}
pub(crate) fn configure(&mut self, core: &mut Core<'_>) -> Result<(), Error> {
let Some(target) = self.target.as_mut() else {
return Ok(());
};
for channel in target.active_up_channels.as_mut_slice() {
let channel_mode = self
.channel_modes
.get(channel.up_channel.number())
.copied()
.unwrap_or_else(|| {
if channel.channel_name() == "defmt" {
Some(ChannelMode::BlockIfFull)
} else {
None
}
});
if let Some(mode) = channel_mode {
channel.change_mode(core, mode)?;
}
}
Ok(())
}
}