#![no_std]
#[cfg(all(feature = "drop-on-contention", not(target_arch = "arm"), not(doc)))]
compile_error!("the `drop-on-contention` feature is only supported on ARM targets");
mod channel;
mod consts;
use core::{
cell::UnsafeCell,
sync::atomic::{AtomicU32, Ordering},
};
#[cfg(not(feature = "drop-on-contention"))]
use core::sync::atomic::AtomicBool;
use crate::{channel::Channel, consts::BUF_SIZE};
const MODE_MASK: u32 = 0b11;
const MODE_BLOCK_IF_FULL: u32 = 2;
const MODE_NON_BLOCKING_TRIM: u32 = 1;
#[defmt::global_logger]
struct Logger;
#[cfg(not(feature = "drop-on-contention"))]
static RTT_ENCODER: RttEncoder = RttEncoder::new();
#[cfg(feature = "drop-on-contention")]
static RTT_ENCODER: AtomicRttEncoder = AtomicRttEncoder::new();
#[no_mangle]
static _SEGGER_RTT: Header = Header {
id: *b"SEGGER RTT\0\0\0\0\0\0",
max_up_channels: 1,
max_down_channels: 0,
up_channel: Channel {
name: NAME.as_ptr(),
buffer: BUFFER.get(),
size: BUF_SIZE as u32,
write: AtomicU32::new(0),
read: AtomicU32::new(0),
flags: AtomicU32::new(MODE_NON_BLOCKING_TRIM),
},
};
pub fn in_blocking_mode() -> bool {
(_SEGGER_RTT.up_channel.flags.load(Ordering::Relaxed) & MODE_MASK) == MODE_BLOCK_IF_FULL
}
#[cfg_attr(target_os = "macos", link_section = ".uninit,defmt-rtt.BUFFER")]
#[cfg_attr(not(target_os = "macos"), link_section = ".uninit.defmt-rtt.BUFFER")]
static BUFFER: Buffer = Buffer::new();
#[cfg_attr(target_os = "macos", link_section = ".data,defmt-rtt.NAME")]
#[cfg_attr(not(target_os = "macos"), link_section = ".data.defmt-rtt.NAME")]
static NAME: [u8; 6] = *b"defmt\0";
#[cfg(not(feature = "drop-on-contention"))]
struct RttEncoder {
taken: AtomicBool,
cs_restore: UnsafeCell<critical_section::RestoreState>,
encoder: UnsafeCell<defmt::Encoder>,
}
#[cfg(not(feature = "drop-on-contention"))]
impl RttEncoder {
const fn new() -> RttEncoder {
RttEncoder {
taken: AtomicBool::new(false),
cs_restore: UnsafeCell::new(critical_section::RestoreState::invalid()),
encoder: UnsafeCell::new(defmt::Encoder::new()),
}
}
fn acquire(&self) {
let restore = unsafe { critical_section::acquire() };
if self.taken.load(Ordering::Relaxed) {
panic!("defmt logger taken reentrantly")
}
self.taken.store(true, Ordering::Relaxed);
unsafe {
self.cs_restore.get().write(restore);
let encoder: &mut defmt::Encoder = &mut *self.encoder.get();
encoder.start_frame(|b| {
_SEGGER_RTT.up_channel.write_all(b);
});
}
}
unsafe fn write(&self, bytes: &[u8]) {
unsafe {
let encoder: &mut defmt::Encoder = &mut *self.encoder.get();
encoder.write(bytes, |b| {
_SEGGER_RTT.up_channel.write_all(b);
});
}
}
unsafe fn flush(&self) {
_SEGGER_RTT.up_channel.flush();
}
unsafe fn release(&self) {
if !self.taken.load(Ordering::Relaxed) {
panic!("defmt release out of context")
}
unsafe {
let encoder: &mut defmt::Encoder = &mut *self.encoder.get();
encoder.end_frame(|b| {
_SEGGER_RTT.up_channel.write_all(b);
});
let restore = self.cs_restore.get().read();
self.taken.store(false, Ordering::Relaxed);
critical_section::release(restore);
}
}
}
#[cfg(feature = "drop-on-contention")]
const NO_OWNER: u32 = u32::MAX;
#[cfg(feature = "drop-on-contention")]
struct AtomicRttEncoder {
encoder: UnsafeCell<defmt::Encoder>,
owner: AtomicU32,
overflowed: UnsafeCell<bool>,
start: UnsafeCell<usize>,
cursor: UnsafeCell<usize>,
}
#[cfg(feature = "drop-on-contention")]
impl AtomicRttEncoder {
const fn new() -> AtomicRttEncoder {
AtomicRttEncoder {
encoder: UnsafeCell::new(defmt::Encoder::new()),
owner: AtomicU32::new(NO_OWNER),
overflowed: UnsafeCell::new(false),
start: UnsafeCell::new(0),
cursor: UnsafeCell::new(0),
}
}
#[cfg(target_arch = "arm")]
fn current_context() -> u32 {
unsafe {
let ipsr: u32;
core::arch::asm!(
"mrs {}, ipsr",
out(reg) ipsr,
options(nomem, nostack, preserves_flags)
);
ipsr
}
}
#[cfg(all(not(target_arch = "arm"), doc))]
fn current_context() -> u32 {
NO_OWNER
}
#[cfg(all(not(target_arch = "arm"), not(doc)))]
fn current_context() -> u32 {
loop {}
}
fn is_owner(&self) -> bool {
self.owner.load(Ordering::Relaxed) == Self::current_context()
}
fn acquire(&self) {
let context = Self::current_context();
if self.owner.load(Ordering::Relaxed) == context {
panic!("defmt logger taken reentrantly");
}
if self
.owner
.compare_exchange(NO_OWNER, context, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
return;
}
unsafe {
*self.overflowed.get() = false;
let cursor = _SEGGER_RTT.up_channel.write.load(Ordering::Acquire) as usize;
*self.start.get() = cursor;
*self.cursor.get() = cursor;
let encoder: &mut defmt::Encoder = &mut *self.encoder.get();
encoder.start_frame(|b| RTT_ENCODER.stage(b));
}
}
fn write(&self, bytes: &[u8]) {
if !self.is_owner() {
return;
}
unsafe {
let encoder: &mut defmt::Encoder = &mut *self.encoder.get();
encoder.write(bytes, |b| RTT_ENCODER.stage(b));
}
}
fn flush(&self) {
if !self.is_owner() {
return;
}
_SEGGER_RTT.up_channel.flush();
}
fn release(&self) {
if !self.is_owner() {
return;
}
unsafe {
let encoder: &mut defmt::Encoder = &mut *self.encoder.get();
encoder.end_frame(|b| RTT_ENCODER.stage(b));
if !*self.overflowed.get() {
_SEGGER_RTT.up_channel.commit(*self.cursor.get());
}
}
self.owner.store(NO_OWNER, Ordering::Release);
}
unsafe fn stage(&self, bytes: &[u8]) {
unsafe {
if *self.overflowed.get() {
return;
}
if crate::channel::available_buffer_size(*self.start.get(), *self.cursor.get())
< bytes.len()
{
*self.overflowed.get() = true;
return;
}
if !_SEGGER_RTT
.up_channel
.stage_bytes(&mut *self.cursor.get(), bytes)
{
*self.overflowed.get() = true;
}
}
}
}
#[cfg(not(feature = "drop-on-contention"))]
unsafe impl Sync for RttEncoder {}
#[cfg(feature = "drop-on-contention")]
unsafe impl Sync for AtomicRttEncoder {}
#[cfg_attr(feature = "drop-on-contention", allow(unused_unsafe))]
unsafe impl defmt::Logger for Logger {
fn acquire() {
RTT_ENCODER.acquire();
}
unsafe fn write(bytes: &[u8]) {
unsafe {
RTT_ENCODER.write(bytes);
}
}
unsafe fn flush() {
unsafe {
RTT_ENCODER.flush();
}
}
unsafe fn release() {
unsafe {
RTT_ENCODER.release();
}
}
}
#[repr(C)]
struct Header {
id: [u8; 16],
max_up_channels: u32,
max_down_channels: u32,
up_channel: Channel,
}
unsafe impl Sync for Header {}
struct Buffer {
inner: UnsafeCell<[u8; BUF_SIZE]>,
}
impl Buffer {
const fn new() -> Buffer {
Buffer {
inner: UnsafeCell::new([0; BUF_SIZE]),
}
}
const fn get(&self) -> *mut u8 {
self.inner.get() as _
}
}
unsafe impl Sync for Buffer {}