use core::cell::Cell;
use core::fmt::{write, Arguments, Result, Write};
use core::panic::PanicInfo;
use core::str;
use crate::common::cells::NumericCellExt;
use crate::common::cells::{MapCell, TakeCell};
use crate::common::queue::Queue;
use crate::common::ring_buffer::RingBuffer;
use crate::hil;
use crate::process::ProcessType;
use crate::Chip;
use crate::ReturnCode;
pub trait IoWrite {
fn write(&mut self, buf: &[u8]);
fn write_ring_buffer<'a>(&mut self, buf: &RingBuffer<'a, u8>) {
let (left, right) = buf.as_slices();
if let Some(slice) = left {
self.write(slice);
}
if let Some(slice) = right {
self.write(slice);
}
}
}
pub unsafe fn panic<L: hil::led::Led, W: Write + IoWrite, C: Chip>(
leds: &mut [&L],
writer: &mut W,
panic_info: &PanicInfo,
nop: &dyn Fn(),
processes: &'static [Option<&'static dyn ProcessType>],
chip: &'static Option<&'static C>,
) -> ! {
panic_begin(nop);
panic_banner(writer, panic_info);
flush(writer);
panic_cpu_state(chip, writer);
panic_process_info(processes, writer);
panic_blink_forever(leds)
}
pub unsafe fn panic_begin(nop: &dyn Fn()) {
for _ in 0..200000 {
nop();
}
}
pub unsafe fn panic_banner<W: Write>(writer: &mut W, panic_info: &PanicInfo) {
let _ = writer.write_fmt(format_args!("\r\n{}\r\n", panic_info));
let _ = writer.write_fmt(format_args!(
"\tKernel version {}\r\n",
option_env!("TOCK_KERNEL_VERSION").unwrap_or("unknown")
));
}
pub unsafe fn panic_cpu_state<W: Write, C: Chip>(
chip: &'static Option<&'static C>,
writer: &mut W,
) {
chip.map(|c| {
c.print_state(writer);
});
}
pub unsafe fn panic_process_info<W: Write>(
procs: &'static [Option<&'static dyn ProcessType>],
writer: &mut W,
) {
let _ = writer.write_fmt(format_args!("\r\n---| App Status |---\r\n"));
for idx in 0..procs.len() {
procs[idx].as_ref().map(|process| {
process.print_full_process(writer);
});
}
}
pub fn panic_blink_forever<L: hil::led::Led>(leds: &mut [&L]) -> ! {
leds.iter_mut().for_each(|led| led.init());
loop {
for _ in 0..1000000 {
leds.iter_mut().for_each(|led| led.on());
}
for _ in 0..100000 {
leds.iter_mut().for_each(|led| led.off());
}
for _ in 0..1000000 {
leds.iter_mut().for_each(|led| led.on());
}
for _ in 0..500000 {
leds.iter_mut().for_each(|led| led.off());
}
}
}
pub static mut DEBUG_GPIOS: (
Option<&'static dyn hil::gpio::Pin>,
Option<&'static dyn hil::gpio::Pin>,
Option<&'static dyn hil::gpio::Pin>,
) = (None, None, None);
pub unsafe fn assign_gpios(
gpio0: Option<&'static dyn hil::gpio::Pin>,
gpio1: Option<&'static dyn hil::gpio::Pin>,
gpio2: Option<&'static dyn hil::gpio::Pin>,
) {
DEBUG_GPIOS.0 = gpio0;
DEBUG_GPIOS.1 = gpio1;
DEBUG_GPIOS.2 = gpio2;
}
#[macro_export]
macro_rules! debug_gpio {
($i:tt, $method:ident $(,)?) => {{
#[allow(unused_unsafe)]
unsafe {
$crate::debug::DEBUG_GPIOS.$i.map(|g| g.$method());
}
}};
}
pub struct DebugQueueWrapper {
dw: MapCell<&'static DebugQueue>,
}
impl DebugQueueWrapper {
pub fn new(dw: &'static DebugQueue) -> Self {
Self {
dw: MapCell::new(dw),
}
}
}
pub struct DebugQueue {
ring_buffer: TakeCell<'static, RingBuffer<'static, u8>>,
}
impl DebugQueue {
pub fn new(ring_buffer: &'static mut RingBuffer<'static, u8>) -> Self {
Self {
ring_buffer: TakeCell::new(ring_buffer),
}
}
}
static mut DEBUG_QUEUE: Option<&'static mut DebugQueueWrapper> = None;
pub unsafe fn set_debug_queue(buffer: &'static mut DebugQueueWrapper) {
DEBUG_QUEUE = Some(buffer);
}
impl Write for DebugQueueWrapper {
fn write_str(&mut self, s: &str) -> Result {
self.dw.map(|dw| {
dw.ring_buffer.map(|ring_buffer| {
let bytes = s.as_bytes();
for &b in bytes {
ring_buffer.push(b);
}
});
});
Ok(())
}
}
pub fn debug_enqueue_fmt(args: Arguments) {
unsafe { DEBUG_QUEUE.as_deref_mut() }.map(|buffer| {
let _ = write(buffer, args);
let _ = buffer.write_str("\r\n");
});
}
pub fn debug_flush_queue_() {
let writer = unsafe { get_debug_writer() };
unsafe { DEBUG_QUEUE.as_deref_mut() }.map(|buffer| {
buffer.dw.map(|dw| {
dw.ring_buffer.map(|ring_buffer| {
writer.write_ring_buffer(ring_buffer);
ring_buffer.empty();
});
});
});
}
#[macro_export]
macro_rules! debug_enqueue {
() => ({
debug_enqueue!("")
});
($msg:expr $(,)?) => ({
$crate::debug::debug_enqueue_fmt(format_args!($msg))
});
($fmt:expr, $($arg:tt)+) => ({
$crate::debug::debug_enqueue_fmt(format_args!($fmt, $($arg)+))
});
}
#[macro_export]
macro_rules! debug_flush_queue {
() => {{
$crate::debug::debug_flush_queue_()
}};
}
pub struct DebugWriterWrapper {
dw: MapCell<&'static DebugWriter>,
}
pub struct DebugWriter {
uart: &'static dyn hil::uart::Transmit<'static>,
output_buffer: TakeCell<'static, [u8]>,
internal_buffer: TakeCell<'static, RingBuffer<'static, u8>>,
count: Cell<usize>,
}
static mut DEBUG_WRITER: Option<&'static mut DebugWriterWrapper> = None;
unsafe fn try_get_debug_writer() -> Option<&'static mut DebugWriterWrapper> {
DEBUG_WRITER.as_deref_mut()
}
unsafe fn get_debug_writer() -> &'static mut DebugWriterWrapper {
try_get_debug_writer().expect("Must call `set_debug_writer_wrapper` in board initialization.")
}
pub unsafe fn set_debug_writer_wrapper(debug_writer: &'static mut DebugWriterWrapper) {
DEBUG_WRITER = Some(debug_writer);
}
impl DebugWriterWrapper {
pub fn new(dw: &'static DebugWriter) -> DebugWriterWrapper {
DebugWriterWrapper {
dw: MapCell::new(dw),
}
}
}
impl DebugWriter {
pub fn new(
uart: &'static dyn hil::uart::Transmit,
out_buffer: &'static mut [u8],
internal_buffer: &'static mut RingBuffer<'static, u8>,
) -> DebugWriter {
DebugWriter {
uart: uart,
output_buffer: TakeCell::new(out_buffer),
internal_buffer: TakeCell::new(internal_buffer),
count: Cell::new(0),
}
}
fn increment_count(&self) {
self.count.increment();
}
fn get_count(&self) -> usize {
self.count.get()
}
fn publish_bytes(&self) {
self.internal_buffer.map(|ring_buffer| {
if let Some(out_buffer) = self.output_buffer.take() {
let mut count = 0;
for dst in out_buffer.iter_mut() {
match ring_buffer.dequeue() {
Some(src) => {
*dst = src;
count += 1;
}
None => {
break;
}
}
}
if count != 0 {
let (_rval, opt) = self.uart.transmit_buffer(out_buffer, count);
self.output_buffer.put(opt);
}
}
});
}
fn extract(&self) -> Option<&mut RingBuffer<'static, u8>> {
self.internal_buffer.take()
}
}
impl hil::uart::TransmitClient for DebugWriter {
fn transmitted_buffer(&self, buffer: &'static mut [u8], _tx_len: usize, _rcode: ReturnCode) {
self.output_buffer.replace(buffer);
if self.internal_buffer.map_or(false, |buf| buf.has_elements()) {
self.publish_bytes();
}
}
fn transmitted_word(&self, _rcode: ReturnCode) {}
}
impl DebugWriterWrapper {
fn increment_count(&self) {
self.dw.map(|dw| {
dw.increment_count();
});
}
fn get_count(&self) -> usize {
self.dw.map_or(0, |dw| dw.get_count())
}
fn publish_bytes(&self) {
self.dw.map(|dw| {
dw.publish_bytes();
});
}
fn extract(&self) -> Option<&mut RingBuffer<'static, u8>> {
self.dw.map_or(None, |dw| dw.extract())
}
}
impl IoWrite for DebugWriterWrapper {
fn write(&mut self, bytes: &[u8]) {
const FULL_MSG: &[u8] = b"\n*** DEBUG BUFFER FULL ***\n";
self.dw.map(|dw| {
dw.internal_buffer.map(|ring_buffer| {
let available_len_for_msg =
ring_buffer.available_len().saturating_sub(FULL_MSG.len());
if available_len_for_msg >= bytes.len() {
for &b in bytes {
ring_buffer.enqueue(b);
}
} else {
for &b in &bytes[..available_len_for_msg] {
ring_buffer.enqueue(b);
}
for &b in FULL_MSG {
ring_buffer.enqueue(b);
}
}
});
});
}
}
impl Write for DebugWriterWrapper {
fn write_str(&mut self, s: &str) -> Result {
self.write(s.as_bytes());
Ok(())
}
}
pub fn begin_debug_fmt(args: Arguments) {
let writer = unsafe { get_debug_writer() };
let _ = write(writer, args);
let _ = writer.write_str("\r\n");
writer.publish_bytes();
}
pub fn begin_debug_verbose_fmt(args: Arguments, file_line: &(&'static str, u32)) {
let writer = unsafe { get_debug_writer() };
writer.increment_count();
let count = writer.get_count();
let (file, line) = *file_line;
let _ = writer.write_fmt(format_args!("TOCK_DEBUG({}): {}:{}: ", count, file, line));
let _ = write(writer, args);
let _ = writer.write_str("\r\n");
writer.publish_bytes();
}
#[macro_export]
macro_rules! debug {
() => ({
debug!("")
});
($msg:expr $(,)?) => ({
$crate::debug::begin_debug_fmt(format_args!($msg))
});
($fmt:expr, $($arg:tt)+) => ({
$crate::debug::begin_debug_fmt(format_args!($fmt, $($arg)+))
});
}
#[macro_export]
macro_rules! debug_verbose {
() => ({
debug_verbose!("")
});
($msg:expr $(,)?) => ({
$crate::debug::begin_debug_verbose_fmt(format_args!($msg), {
static _FILE_LINE: (&'static str, u32) = (file!(), line!());
&_FILE_LINE
})
});
($fmt:expr, $($arg:tt)+) => ({
$crate::debug::begin_debug_verbose_fmt(format_args!($fmt, $($arg)+), {
static _FILE_LINE: (&'static str, u32) = (file!(), line!());
&_FILE_LINE
})
});
}
pub trait Debug {
fn write(&self, buf: &'static mut [u8], len: usize);
}
#[cfg(debug = "true")]
impl Default for Debug {
fn write(&self, buf: &'static mut [u8], len: usize) {
panic!(
"No registered kernel debug printer. Thrown printing {:?}",
buf
);
}
}
pub unsafe fn flush<W: Write + IoWrite>(writer: &mut W) {
if let Some(debug_writer) = try_get_debug_writer() {
if let Some(ring_buffer) = debug_writer.extract() {
if ring_buffer.has_elements() {
let _ = writer.write_str(
"\r\n---| Debug buffer not empty. Flushing. May repeat some of last message(s):\r\n",
);
writer.write_ring_buffer(ring_buffer);
}
}
match DEBUG_QUEUE.as_deref_mut() {
None => {
let _ = writer.write_str(
"\r\n---| No debug queue found. You can set it with the DebugQueue component.\r\n",
);
}
Some(buffer) => {
let _ = writer.write_str("\r\n---| Flushing debug queue:\r\n");
buffer.dw.map(|dw| {
dw.ring_buffer.map(|ring_buffer| {
writer.write_ring_buffer(ring_buffer);
});
});
}
}
} else {
let _ = writer.write_str(
"\r\n---| Global debug writer not registered.\
\r\n Call `set_debug_writer_wrapper` in board initialization.\r\n",
);
}
}