use core::fmt::{self, Write};
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{Channel, Once, Receiver, Sender};
const RX_CAPACITY: usize = 64;
const TX_CAPACITY: usize = 256;
#[cfg(not(loom))]
static RX_CHANNEL: Channel<u8, RX_CAPACITY> = Channel::new();
#[cfg(loom)]
loom::lazy_static! {
static ref RX_CHANNEL: Channel<u8, RX_CAPACITY> = Channel::new();
}
#[cfg(not(loom))]
static RX_SENDER: Once<Sender<'static, u8, RX_CAPACITY>> = Once::new();
#[cfg(loom)]
loom::lazy_static! {
static ref RX_SENDER: Once<Sender<'static, u8, RX_CAPACITY>> = Once::new();
}
#[cfg(not(loom))]
static RX_RECEIVER: Once<Receiver<'static, u8, RX_CAPACITY>> = Once::new();
#[cfg(loom)]
loom::lazy_static! {
static ref RX_RECEIVER: Once<Receiver<'static, u8, RX_CAPACITY>> = Once::new();
}
#[cfg(not(loom))]
static TX_CHANNEL: Channel<u8, TX_CAPACITY> = Channel::new();
#[cfg(loom)]
loom::lazy_static! {
static ref TX_CHANNEL: Channel<u8, TX_CAPACITY> = Channel::new();
}
#[cfg(not(loom))]
static TX_SENDER: Once<Sender<'static, u8, TX_CAPACITY>> = Once::new();
#[cfg(loom)]
loom::lazy_static! {
static ref TX_SENDER: Once<Sender<'static, u8, TX_CAPACITY>> = Once::new();
}
#[cfg(not(loom))]
static TX_RECEIVER: Once<Receiver<'static, u8, TX_CAPACITY>> = Once::new();
#[cfg(loom)]
loom::lazy_static! {
static ref TX_RECEIVER: Once<Receiver<'static, u8, TX_CAPACITY>> = Once::new();
}
#[cfg(not(loom))]
static IRQ_TX_ACTIVE: AtomicBool = AtomicBool::new(false);
#[cfg(loom)]
loom::lazy_static! {
static ref IRQ_TX_ACTIVE: AtomicBool = AtomicBool::new(false);
}
pub(crate) fn init() {
if let Some((tx, rx)) = RX_CHANNEL.split() {
let _ = RX_SENDER.set(tx);
let _ = RX_RECEIVER.set(rx);
}
if let Some((tx, rx)) = TX_CHANNEL.split() {
let _ = TX_SENDER.set(tx);
let _ = TX_RECEIVER.set(rx);
}
}
pub fn enable_irq_tx() {
IRQ_TX_ACTIVE.store(true, Ordering::Release);
}
pub fn tx_irq_next_byte() -> Option<u8> {
TX_RECEIVER.get().and_then(|rx| rx.try_recv())
}
pub fn on_rx_byte(b: u8) {
if let Some(tx) = RX_SENDER.get() {
let _ = tx.try_send(b);
}
}
pub fn try_read_byte() -> Option<u8> {
RX_RECEIVER.get().and_then(|rx| rx.try_recv())
}
fn write_bytes_irq(bytes: &[u8]) -> bool {
let Some(tx) = TX_SENDER.get() else {
return false;
};
crate::critical::enter(|| {
for &b in bytes {
while tx.try_send(b).is_err() {
if let Some(old) = tx_irq_next_byte() {
crate::port::board::console_write(&[old]);
} else {
break; }
}
}
if let Some(b) = tx_irq_next_byte() {
crate::port::board::console_write(&[b]);
}
crate::port::board::console_kick_tx();
});
true
}
pub fn write_str(s: &str) {
write_bytes(s.as_bytes());
}
pub fn write_bytes(bytes: &[u8]) {
if IRQ_TX_ACTIVE.load(Ordering::Acquire) && write_bytes_irq(bytes) {
return;
}
let mut spins: u32 = 0;
while CONSOLE_WRITE_LOCK
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
spins += 1;
if spins >= LOCK_SPIN_LIMIT {
crate::port::board::console_write(bytes);
return;
}
core::hint::spin_loop();
}
crate::port::board::console_write(bytes);
CONSOLE_WRITE_LOCK.store(false, Ordering::Release);
}
#[cfg(not(loom))]
static CONSOLE_WRITE_LOCK: AtomicBool = AtomicBool::new(false);
#[cfg(loom)]
loom::lazy_static! {
static ref CONSOLE_WRITE_LOCK: AtomicBool = AtomicBool::new(false);
}
const LOCK_SPIN_LIMIT: u32 = 100_000;
pub fn flush_sync() {
crate::critical::enter(|| {
while let Some(b) = tx_irq_next_byte() {
crate::port::board::console_write(&[b]);
}
});
}
struct Console;
impl Write for Console {
fn write_str(&mut self, s: &str) -> fmt::Result {
write_str(s);
Ok(())
}
}
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
let _ = Console.write_fmt(args);
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {{
$crate::console::_print(core::format_args!($($arg)*));
}};
}
#[macro_export]
macro_rules! println {
() => { $crate::print!("\n") };
($($arg:tt)*) => {{
$crate::console::_print(core::format_args!($($arg)*));
$crate::print!("\n");
}};
}