#![cfg(feature = "host")]
use consortium_core::{TryError, TryRecvTransport, UncachedRegion};
use crate::{
descriptor::{Descriptor, MAX_FRAME_LEN},
ring::{RingError, RingReader},
};
#[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),
#[error("{0}")]
Ring(RingError),
}
impl From<RingError> for HostError {
fn from(error: RingError) -> Self {
match error {
RingError::MagicMismatch => Self::MagicMismatch,
RingError::VersionMismatch => Self::VersionMismatch,
RingError::CapacityMismatch { descriptor, mapped } => {
Self::CapacityMismatch { descriptor, mapped }
}
RingError::PointerOutOfBounds { ptr, capacity } => {
Self::PointerOutOfBounds { ptr, capacity }
}
other => Self::Ring(other),
}
}
}
pub struct HostRing {
reader: RingReader<UncachedRegion>,
}
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
);
let region = unsafe { UncachedRegion::new(base, len) };
let reader = unsafe { RingReader::new(region) }?;
let desc = reader.snapshot();
consortium_log::trace!(
"HostRing::from_raw_parts: descriptor capacity = {:#x}, read_ptr = {:#x}, write_ptr = {:#x}",
desc.capacity,
desc.read_ptr,
desc.write_ptr
);
Ok(Self { reader })
}
pub fn mapped_len(&self) -> usize {
use consortium_core::Region;
self.reader.region().len()
}
pub fn snapshot(&self) -> Descriptor {
self.reader.snapshot()
}
pub fn read_packet(&mut self) -> Result<Option<Vec<u8>>, HostError> {
let mut buf = [0u8; MAX_FRAME_LEN];
match self.reader.try_recv(&mut buf) {
Ok(len) => Ok(Some(buf[..len].to_vec())),
Err(TryError::WouldBlock) => Ok(None),
Err(TryError::Fail(error)) => Err(error.into()),
}
}
pub fn drain_packets(&mut self) -> Result<Vec<Vec<u8>>, HostError> {
let mut packets = Vec::new();
self.reader.drain(|packet| packets.push(packet.to_vec()))?;
Ok(packets)
}
}
#[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)),
}
}
}
}