#![cfg(feature = "logger")]
use core::ptr::addr_of_mut;
use defmt::{Encoder, Logger, global_logger};
use crate::config;
use crate::descriptor::{DESCRIPTOR_SIZE, Descriptor, FLAGS_OFFSET, Flags};
use crate::utils::wv_u16;
#[global_logger]
pub struct ConsortiumLogger;
static mut ENCODER: Encoder = Encoder::new();
static mut FRAME: [u8; FRAME_CAP] = [0; FRAME_CAP];
static mut FRAME_LEN: usize = 0;
static mut FRAME_OVERFLOW: bool = false;
const FRAME_CAP: usize = u8::MAX as usize;
fn stage(bytes: &[u8]) {
#[allow(clippy::multiple_unsafe_ops_per_block)]
unsafe {
let buf = &mut *addr_of_mut!(FRAME);
for &byte in bytes {
if FRAME_LEN < FRAME_CAP {
buf[FRAME_LEN] = byte;
FRAME_LEN += 1;
} else {
FRAME_OVERFLOW = true;
}
}
}
}
impl ConsortiumLogger {
pub unsafe fn force_init_shared_memory(base: *mut u8, size: u16) {
for offset in DESCRIPTOR_SIZE..size as usize {
#[allow(clippy::multiple_unsafe_ops_per_block)]
unsafe {
base.add(offset).write_volatile(0)
};
}
unsafe { Descriptor::new(size).write_to_raw_ptr(base) };
}
pub unsafe fn acquire_shared_memory(base: *mut u8, size: u16) {
let descriptor = unsafe { Descriptor::from_raw_ptr(base) };
if !descriptor.check() || descriptor.capacity != size {
unsafe { Self::force_init_shared_memory(base, size) };
}
let descriptor = unsafe { &mut *(base as *mut Descriptor) };
descriptor.flags.insert(Flags::Writing);
}
pub unsafe fn flush_shared_memory(base: *mut u8) {
let descriptor = unsafe { &mut *(base as *mut Descriptor) };
if descriptor.flags.contains(Flags::Error) {
return;
}
unsafe { descriptor.flush(base) };
}
pub unsafe fn release_shared_memory(base: *mut u8) {
let descriptor = unsafe { &mut *(base as *mut Descriptor) };
descriptor.flags.remove(Flags::Writing);
}
pub unsafe fn mark_panic(base: *mut u8) {
let descriptor = unsafe { &mut *(base as *mut Descriptor) };
descriptor.flags.insert(Flags::Panic);
unsafe { wv_u16(base, FLAGS_OFFSET, descriptor.flags.bits()) };
}
pub unsafe fn write_shared_memory(base: *mut u8, bytes: &[u8]) {
let descriptor = unsafe { &mut *(base as *mut Descriptor) };
if descriptor.flags.contains(Flags::Error) {
return;
}
unsafe {
if !descriptor.write_one_frame(base, bytes) {
descriptor.flags.insert(Flags::Overflow);
}
}
}
pub fn init() {
if config::BASE == 0 {
return;
}
unsafe {
Self::force_init_shared_memory(config::BASE_PTR, config::SIZE);
}
}
}
unsafe impl Logger for ConsortiumLogger {
fn acquire() {
if config::BASE == 0 {
return;
}
#[allow(clippy::multiple_unsafe_ops_per_block)]
unsafe {
Self::acquire_shared_memory(config::BASE_PTR, config::SIZE);
FRAME_LEN = 0;
FRAME_OVERFLOW = false;
(*addr_of_mut!(ENCODER)).start_frame(stage);
}
}
unsafe fn flush() {
if config::BASE == 0 {
return;
}
unsafe {
Self::flush_shared_memory(config::BASE_PTR);
}
}
unsafe fn release() {
if config::BASE == 0 {
return;
}
#[allow(clippy::multiple_unsafe_ops_per_block)]
unsafe {
(*addr_of_mut!(ENCODER)).end_frame(stage);
if FRAME_OVERFLOW {
let descriptor = &mut *(config::BASE_PTR as *mut Descriptor);
descriptor.flags.insert(Flags::Overflow);
wv_u16(config::BASE_PTR, FLAGS_OFFSET, descriptor.flags.bits());
} else {
let frame =
core::slice::from_raw_parts(addr_of_mut!(FRAME) as *const u8, FRAME_LEN);
Self::write_shared_memory(config::BASE_PTR, frame);
}
Self::release_shared_memory(config::BASE_PTR);
}
}
unsafe fn write(bytes: &[u8]) {
if config::BASE == 0 {
return;
}
#[allow(clippy::multiple_unsafe_ops_per_block)]
unsafe {
(*addr_of_mut!(ENCODER)).write(bytes, stage);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::descriptor::{DESCRIPTOR_SIZE, Descriptor};
const SIZE: u16 = 0x200;
fn region() -> Vec<u8> {
vec![0u8; SIZE as usize]
}
#[test]
fn force_init_writes_fresh_descriptor() {
let mut buf = region();
unsafe { ConsortiumLogger::force_init_shared_memory(buf.as_mut_ptr(), SIZE) };
let desc = unsafe { Descriptor::from_raw_ptr(buf.as_ptr()) };
assert!(desc.check());
assert_eq!(desc.capacity, SIZE);
assert!(desc.flags.is_empty());
assert_eq!(desc.read_ptr, desc.write_ptr);
}
#[test]
fn acquire_initializes_garbage_region() {
let mut buf = vec![0xABu8; SIZE as usize];
unsafe { ConsortiumLogger::acquire_shared_memory(buf.as_mut_ptr(), SIZE) };
let desc = unsafe { Descriptor::from_raw_ptr(buf.as_ptr()) };
assert!(desc.check());
assert_eq!(desc.capacity, SIZE);
assert!(desc.flags.contains(Flags::Writing));
assert_eq!(desc.read_ptr, desc.write_ptr);
}
#[test]
fn acquire_reinits_on_capacity_mismatch() {
let mut buf = region();
let mut stale = Descriptor::new(SIZE / 2);
stale.write_ptr = DESCRIPTOR_SIZE as u16 + 8;
unsafe { stale.write_to_raw_ptr(buf.as_mut_ptr()) };
unsafe { ConsortiumLogger::acquire_shared_memory(buf.as_mut_ptr(), SIZE) };
let desc = unsafe { Descriptor::from_raw_ptr(buf.as_ptr()) };
assert_eq!(desc.capacity, SIZE);
assert_eq!(desc.read_ptr, desc.write_ptr);
}
#[test]
fn force_init_clears_stale_ring_slots() {
let mut buf = vec![0xCDu8; SIZE as usize];
unsafe { ConsortiumLogger::force_init_shared_memory(buf.as_mut_ptr(), SIZE) };
assert!(buf[DESCRIPTOR_SIZE..].iter().all(|&byte| byte == 0));
let mut desc = unsafe { Descriptor::from_raw_ptr(buf.as_ptr()) };
let wrote = unsafe { desc.write_one_frame(buf.as_mut_ptr(), &[0xAA, 0xBB]) };
assert!(wrote);
assert!(!desc.flags.contains(Flags::Overflow));
}
#[test]
fn mark_panic_latches_flag_and_preserves_others() {
let mut buf = region();
let mut desc = Descriptor::new(SIZE);
desc.flags.insert(Flags::Overflow);
unsafe { desc.write_to_raw_ptr(buf.as_mut_ptr()) };
unsafe { ConsortiumLogger::mark_panic(buf.as_mut_ptr()) };
let desc = unsafe { Descriptor::from_raw_ptr(buf.as_ptr()) };
assert!(desc.flags.contains(Flags::Panic));
assert!(desc.flags.contains(Flags::Overflow));
}
#[test]
fn acquire_preserves_valid_same_capacity_ring() {
let mut buf = region();
let mut live = Descriptor::new(SIZE);
live.write_ptr = DESCRIPTOR_SIZE as u16 + 16;
unsafe { live.write_to_raw_ptr(buf.as_mut_ptr()) };
unsafe { ConsortiumLogger::acquire_shared_memory(buf.as_mut_ptr(), SIZE) };
let desc = unsafe { Descriptor::from_raw_ptr(buf.as_ptr()) };
assert_eq!(desc.write_ptr, DESCRIPTOR_SIZE as u16 + 16);
assert!(desc.flags.contains(Flags::Writing));
}
}