use crate::{TerminalChannel, TerminalWriter, UpChannel};
use core::fmt::{self, Write as _};
use core::mem::MaybeUninit;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
static CRITICAL_SECTION: AtomicPtr<CriticalSectionFunc> = AtomicPtr::new(core::ptr::null_mut());
static mut PRINT_TERMINAL: MaybeUninit<TerminalChannel> = MaybeUninit::uninit();
pub type CriticalSectionFunc = fn(arg: *mut (), f: fn(arg: *mut ()) -> ()) -> ();
pub unsafe fn set_print_channel_cs(channel: UpChannel, cs: &'static CriticalSectionFunc) {
cs(channel.0 as *mut (), |channel_ptr| {
ptr::write(
PRINT_TERMINAL.as_mut_ptr(),
TerminalChannel::new(UpChannel(channel_ptr as *mut crate::rtt::RttChannel)),
);
});
CRITICAL_SECTION.store(cs as *const _ as *mut _, Ordering::SeqCst);
}
#[cfg(feature = "cortex-m")]
pub fn set_print_channel(channel: UpChannel) {
unsafe {
set_print_channel_cs(
channel,
&((|arg, f| cortex_m::interrupt::free(|_| f(arg))) as CriticalSectionFunc),
);
}
}
#[doc(hidden)]
pub mod print_impl {
use super::*;
fn with_writer<F: Fn(TerminalWriter) -> ()>(number: u8, f: F) {
let cs = CRITICAL_SECTION.load(Ordering::SeqCst);
if !cs.is_null() {
let args = (number, f);
unsafe {
(&*cs)(&args as *const _ as *mut (), |args_ptr| {
let args = &*(args_ptr as *const (u8, F));
let term = &mut *PRINT_TERMINAL.as_mut_ptr();
(args.1)(term.write(args.0));
});
}
}
}
#[doc(hidden)]
pub fn write_str(number: u8, s: &str) {
with_writer(number, |mut w| {
w.write_str(s).ok();
});
}
#[doc(hidden)]
pub fn write_fmt(number: u8, arg: fmt::Arguments) {
with_writer(number, |mut w| {
w.write_fmt(arg).ok();
});
}
}
#[macro_export]
macro_rules! rprint {
(=> $terminal:expr, $s:expr) => {
$crate::print_impl::write_str($terminal, $s);
};
(=> $terminal:expr, $($arg:tt)*) => {
$crate::print_impl::write_fmt($terminal, format_args!($($arg)*));
};
($s:expr) => {
$crate::print_impl::write_str(0, $s);
};
($($arg:tt)*) => {
$crate::print_impl::write_fmt(0, format_args!($($arg)*));
};
}
#[macro_export]
macro_rules! rprintln {
(=> $terminal:expr) => {
$crate::print_impl::write_str($terminal, "\n");
};
(=> $terminal:expr, $fmt:expr) => {
$crate::print_impl::write_str($terminal, concat!($fmt, "\n"));
};
(=> $terminal:expr, $fmt:expr, $($arg:tt)*) => {
$crate::print_impl::write_fmt($terminal, format_args!(concat!($fmt, "\n"), $($arg)*));
};
() => {
$crate::print_impl::write_str(0, "\n");
};
($fmt:expr) => {
$crate::print_impl::write_str(0, concat!($fmt, "\n"));
};
($fmt:expr, $($arg:tt)*) => {
$crate::print_impl::write_fmt(0, format_args!(concat!($fmt, "\n"), $($arg)*));
};
}
#[cfg(any(feature = "cortex-m"))]
#[macro_export]
macro_rules! rtt_init_print {
($mode:ident, $size:literal) => {
let channels = $crate::rtt_init! {
up: {
0: {
size: $size
mode: $mode
name: "Terminal"
}
}
};
$crate::set_print_channel(channels.up.0);
};
($mode:ident) => {
$crate::rtt_init_print!($mode, 1024);
};
() => {
$crate::rtt_init_print!(NoBlockSkip, 1024);
};
}
#[cfg(not(any(feature = "cortex-m")))]
#[macro_export]
macro_rules! rtt_init_print {
($($_:tt)*) => {
compile_error!(concat!(
"rtt_init_print! is only available if a platform support feature is enabled.\r\n",
"Solutions:\r\n",
"- Enable a platform support feature:\r\n",
" # Cargo.toml\r\n",
" rtt-target = { version = \"x.y.z\", features = [\"cortex-m\"] }\r\n",
"- OR use set_print_channel_cs() instead if you want to provide your own locking.\r\n"
))
};
}