#![cfg(feature = "host")]
use consortium_shared_memory::descriptor::DescriptorHeaderError;
use crate::descriptor::{DESCRIPTOR_SIZE, Descriptor, MAGIC, VERSION};
#[derive(thiserror::Error, Debug)]
pub enum HostError {
#[error("CDBG magic mismatch")]
MagicMismatch,
#[error("CDBG version mismatch")]
VersionMismatch,
#[error("descriptor capacity {descriptor} exceeds mapped length {mapped}")]
CapacityMismatch {
descriptor: u16,
mapped: usize,
},
#[error("ring pointer {ptr} is out of bounds (capacity {capacity})")]
PointerOutOfBounds {
ptr: u16,
capacity: u16,
},
#[cfg(feature = "decode")]
#[error("ELF parsing failed or missing defmt table")]
ElfParse,
#[cfg(feature = "decode")]
#[error("defmt decode error: {0:?}")]
Decode(defmt_decoder::DecodeError),
}
pub struct HostRing {
base: *mut u8,
len: usize,
}
unsafe impl Send for HostRing {}
impl HostRing {
pub unsafe fn from_raw_parts(base: *mut u8, len: usize) -> Result<Self, HostError> {
consortium_log::trace!(
"HostRing::from_raw_parts(base = {:#x}, len = {:#x})",
base as usize,
len
);
if len < DESCRIPTOR_SIZE {
return Err(HostError::CapacityMismatch {
descriptor: 0,
mapped: len,
});
}
let desc = unsafe { Descriptor::from_raw_ptr(base) };
desc.header.check(MAGIC, VERSION).map_err(|e| match e {
DescriptorHeaderError::BadVersion(..) => HostError::VersionMismatch,
_ => HostError::MagicMismatch,
})?;
consortium_log::trace!(
"HostRing::from_raw_parts: descriptor capacity = {:#x}, read_ptr = {:#x}, write_ptr = {:#x}",
desc.capacity,
desc.read_ptr,
desc.write_ptr
);
if desc.capacity as usize > len {
return Err(HostError::CapacityMismatch {
descriptor: desc.capacity,
mapped: len,
});
}
let data_start = DESCRIPTOR_SIZE as u16;
if desc.read_ptr < data_start || desc.read_ptr >= desc.capacity {
return Err(HostError::PointerOutOfBounds {
ptr: desc.read_ptr,
capacity: desc.capacity,
});
}
if desc.write_ptr < data_start || desc.write_ptr >= desc.capacity {
return Err(HostError::PointerOutOfBounds {
ptr: desc.write_ptr,
capacity: desc.capacity,
});
}
Ok(Self { base, len })
}
pub fn mapped_len(&self) -> usize {
self.len
}
pub fn snapshot(&self) -> Descriptor {
unsafe { Descriptor::from_raw_ptr(self.base) }
}
pub fn read_packet(&mut self) -> Result<Option<Vec<u8>>, HostError> {
let mut desc = unsafe { Descriptor::from_raw_ptr(self.base) };
let payload = unsafe { desc.read_one_frame(self.base) };
Ok(payload)
}
pub fn drain_packets(&mut self) -> Result<Vec<Vec<u8>>, HostError> {
let mut desc = unsafe { Descriptor::from_raw_ptr(self.base) };
let payloads = unsafe { desc.read_bulk_frames(self.base) };
Ok(payloads)
}
}
#[cfg(feature = "decode")]
pub struct DefmtContext {
table: defmt_decoder::Table,
}
#[cfg(feature = "decode")]
impl DefmtContext {
pub fn from_elf_bytes(bytes: &[u8]) -> Result<Self, HostError> {
consortium_log::trace!("parsing defmt table from ELF bytes ({} bytes)", bytes.len());
let table = defmt_decoder::Table::parse(bytes)
.map_err(|e| {
consortium_log::error!("defmt table parse failed: {:?}", e);
HostError::ElfParse
})?
.ok_or_else(|| {
consortium_log::error!("defmt table missing from ELF");
HostError::ElfParse
})?;
consortium_log::info!(
"defmt table parsed: encoding = {:?}, has_timestamp = {}",
table.encoding(),
table.has_timestamp()
);
Ok(Self { table })
}
}
#[cfg(feature = "decode")]
pub struct DefmtSession<'ctx> {
decoder: Box<dyn defmt_decoder::StreamDecoder + 'ctx>,
}
#[cfg(feature = "decode")]
impl<'ctx> DefmtSession<'ctx> {
pub fn new(ctx: &'ctx DefmtContext) -> Self {
Self {
decoder: ctx.table.new_stream_decoder(),
}
}
pub fn push_packet(
&mut self,
bytes: &[u8],
mut on_frame: impl FnMut(&defmt_decoder::Frame<'_>),
) -> Result<(), HostError> {
self.decoder.received(bytes);
loop {
match self.decoder.decode() {
Ok(frame) => on_frame(&frame),
Err(defmt_decoder::DecodeError::UnexpectedEof) => return Ok(()),
Err(e) => return Err(HostError::Decode(e)),
}
}
}
}