use alloc::sync::Arc;
use core::ptr::NonNull;
use axpoll::PollSet;
use spin::LazyLock;
use super::{
Tty,
terminal::{
WindowSize,
ldisc::{ProcessMode, TtyConfig, TtyRead, TtyWrite},
},
};
pub type NTtyDriver = Tty<Console, Console>;
#[derive(Clone, Copy)]
pub struct Console;
impl TtyRead for Console {
fn read(&mut self, buf: &mut [u8]) -> usize {
ax_runtime::hal::console::read_bytes(buf)
}
}
impl TtyWrite for Console {
fn write(&self, buf: &[u8]) {
ax_runtime::hal::console::write_bytes(buf);
}
}
pub static N_TTY: LazyLock<Arc<NTtyDriver>> = LazyLock::new(new_n_tty);
static CONSOLE_INPUT_SOURCE: LazyLock<Arc<PollSet>> = LazyLock::new(|| Arc::new(PollSet::new()));
fn handle_console_input_irq(_irq_num: usize) {
let events = ax_runtime::hal::console::handle_irq();
if events.intersects(
ax_runtime::hal::console::ConsoleIrqEvent::RX_READY
| ax_runtime::hal::console::ConsoleIrqEvent::RX_ERROR
| ax_runtime::hal::console::ConsoleIrqEvent::OVERRUN,
) {
CONSOLE_INPUT_SOURCE.wake();
}
}
unsafe fn handle_console_input_raw_irq(
ctx: ax_runtime::hal::irq::IrqContext,
_data: NonNull<()>,
) -> ax_runtime::hal::irq::IrqReturn {
handle_console_input_irq(ctx.irq.0);
ax_runtime::hal::irq::IrqReturn::Handled
}
fn new_n_tty() -> Arc<NTtyDriver> {
let terminal = {
let t = super::terminal::Terminal::default();
if let Some((rows, cols)) = query_console_size() {
*t.window_size.lock() = WindowSize {
ws_row: rows,
ws_col: cols,
ws_xpixel: 0,
ws_ypixel: 0,
};
}
Arc::new(t)
};
Tty::new(
terminal,
TtyConfig {
reader: Console,
writer: Console,
process_mode: console_irq_mode().unwrap_or(ProcessMode::Manual),
},
)
}
fn query_console_size() -> Option<(u16, u16)> {
ax_runtime::hal::console::write_bytes(b"\x1b7\x1b[9999;9999H\x1b[6n\x1b8");
let mut buf = [0u8; 32];
let mut len = 0usize;
let deadline = ax_runtime::hal::time::wall_time() + core::time::Duration::from_millis(100);
'collect: while ax_runtime::hal::time::wall_time() < deadline {
let mut tmp = [0u8; 1];
if ax_runtime::hal::console::read_bytes(&mut tmp) > 0 {
if len < buf.len() {
buf[len] = tmp[0];
len += 1;
} else {
break 'collect;
}
if tmp[0] == b'R' {
break 'collect;
}
}
core::hint::spin_loop();
}
if len == 0 {
return None;
}
let r_pos = buf[..len].iter().rposition(|&b| b == b'R')?;
let escape_pos = buf[..r_pos].windows(2).rposition(|w| w == b"\x1b[")?;
let inner = core::str::from_utf8(&buf[escape_pos + 2..r_pos]).ok()?;
let mut parts = inner.splitn(2, ';');
let rows: u16 = parts.next()?.parse().ok()?;
let cols: u16 = parts.next()?.parse().ok()?;
if rows == 0 || cols == 0 {
return None;
}
Some((rows, cols))
}
fn console_irq_mode() -> Option<ProcessMode> {
let irq = ax_runtime::hal::console::irq_num()?;
if ax_runtime::hal::irq::request_shared_irq(
irq,
handle_console_input_raw_irq,
NonNull::dangling(),
)
.is_err()
{
warn!("Failed to register console IRQ handler for irq {irq}, falling back to polling mode");
return None;
}
ax_runtime::hal::console::set_input_irq_enabled(true);
Some(ProcessMode::InterruptDriven(CONSOLE_INPUT_SOURCE.clone()))
}