use crate::ChannelMode;
use core::cmp::min;
use core::fmt;
use core::ptr;
use portable_atomic::{AtomicUsize, Ordering::SeqCst};
#[repr(C)]
pub struct RttHeader {
id: [u8; 16],
max_up_channels: usize,
max_down_channels: usize,
}
impl RttHeader {
pub unsafe fn init(&mut self, max_up_channels: usize, max_down_channels: usize) {
ptr::write_volatile(&mut self.max_up_channels, max_up_channels);
ptr::write_volatile(&mut self.max_down_channels, max_down_channels);
const MAGIC_STR_BACKWARDS: &[u8; 16] = b"\0\0\0\0\0\0TTR REGGES";
for (idx, byte) in MAGIC_STR_BACKWARDS.into_iter().enumerate() {
ptr::write_volatile(&mut self.id[15 - idx], *byte);
}
}
pub fn max_up_channels(&self) -> usize {
self.max_up_channels
}
}
#[repr(C)]
pub struct RttChannel {
name: *const u8,
buffer: *mut u8,
size: usize,
write: AtomicUsize,
read: AtomicUsize,
flags: AtomicUsize,
}
impl RttChannel {
pub unsafe fn init(&mut self, name: *const u8, mode: ChannelMode, buffer: *mut [u8]) {
ptr::write_volatile(&mut self.name, name);
ptr::write_volatile(&mut self.size, (*buffer).len());
self.set_mode(mode);
ptr::write_volatile(&mut self.buffer, buffer as *mut u8);
}
pub fn is_initialized(&self) -> bool {
!self.buffer.is_null()
}
pub(crate) fn mode(&self) -> ChannelMode {
let mode = self.flags.load(SeqCst) & 3;
match mode {
0 => ChannelMode::NoBlockSkip,
1 => ChannelMode::NoBlockTrim,
2 => ChannelMode::BlockIfFull,
_ => ChannelMode::NoBlockSkip,
}
}
pub(crate) fn set_mode(&self, mode: ChannelMode) {
self.flags
.store((self.flags.load(SeqCst) & !3) | mode as usize, SeqCst);
}
pub(crate) fn read(&self, mut buf: &mut [u8]) -> usize {
let (write, mut read) = self.read_pointers();
let mut total = 0;
while !buf.is_empty() {
let count = min(self.readable_contiguous(write, read), buf.len());
if count == 0 {
break;
}
unsafe {
ptr::copy_nonoverlapping(self.buffer.add(read), buf.as_mut_ptr(), count);
}
total += count;
read += count;
if read >= self.size {
read = 0;
}
buf = &mut buf[count..];
}
self.read.store(read, SeqCst);
total
}
pub(crate) fn writer(&self) -> RttWriter<'_> {
RttWriter {
chan: self,
write: self.read_pointers().0,
total: 0,
state: WriteState::Writable,
}
}
fn readable_contiguous(&self, write: usize, read: usize) -> usize {
if read > write {
self.size - read
} else {
write - read
}
}
pub(crate) fn read_pointers(&self) -> (usize, usize) {
let write = self.write.load(SeqCst);
let read = self.read.load(SeqCst);
if write >= self.size || read >= self.size {
self.write.store(0, SeqCst);
self.read.store(0, SeqCst);
return (0, 0);
}
(write, read)
}
}
pub(crate) struct RttWriter<'c> {
chan: &'c RttChannel,
write: usize,
total: usize,
state: WriteState,
}
#[derive(Eq, PartialEq)]
enum WriteState {
Writable,
Full,
Finished,
}
impl RttWriter<'_> {
pub fn write(&mut self, buf: &[u8]) {
self.write_with_mode(self.chan.mode(), buf);
}
pub fn write_with_mode(&mut self, mode: ChannelMode, mut buf: &[u8]) {
while self.state == WriteState::Writable && !buf.is_empty() {
let count = min(self.writable_contiguous(), buf.len());
if count == 0 {
match mode {
ChannelMode::NoBlockSkip => {
self.state = WriteState::Finished;
return;
}
ChannelMode::NoBlockTrim => {
self.state = WriteState::Full;
}
ChannelMode::BlockIfFull => {
self.chan.write.store(self.write, SeqCst);
continue;
}
}
}
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr(), self.chan.buffer.add(self.write), count);
}
self.write += count;
self.total += count;
if self.write >= self.chan.size {
self.write = 0;
}
buf = &buf[count..];
}
}
fn writable_contiguous(&self) -> usize {
let read = self.chan.read_pointers().1;
if read > self.write {
read - self.write - 1
} else if read == 0 {
self.chan.size - self.write - 1
} else {
self.chan.size - self.write
}
}
pub fn is_failed(&self) -> bool {
self.state != WriteState::Finished
}
pub fn commit(mut self) -> usize {
self.commit_impl();
self.total
}
fn commit_impl(&mut self) {
match self.state {
WriteState::Finished => (),
WriteState::Full | WriteState::Writable => {
self.chan.write.store(self.write, SeqCst);
self.state = WriteState::Finished;
}
}
}
}
impl Drop for RttWriter<'_> {
fn drop(&mut self) {
self.commit_impl();
}
}
impl fmt::Write for RttWriter<'_> {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
self.write(s.as_bytes());
Ok(())
}
}