//! K407/K408: real PL011 empty-command prompt and exact `help` dispatch. use super::*; use turkos_kernel::serial_console::{ConsoleEvent, HELP_COMMANDS, SerialConsole}; const RXFE: u32 = 1 << 4; const DATA_ERROR: u32 = 0x0f00; fn fail(reason: &str) -> ! { write_str("TURKOS/CONSOLE-ERROR: "); write_line(reason); park() } fn ticks() -> u64 { let value: u64; unsafe { asm!("mrs {value}, CNTPCT_EL0", value = out(reg) value, options(nomem, nostack, preserves_flags)) }; value } fn prompt(number: u64) { write_str("TURKOS/CONSOLE-PROMPT: NUMBER=0x"); write_hex(number as usize); write_str("\r\n"); } pub(super) fn smoke_test() -> ! { let base = UART_BASE.load(Ordering::Relaxed); let frequency: u64; unsafe { asm!("mrs {value}, CNTFRQ_EL0", value = out(reg) frequency, options(nomem, nostack, preserves_flags)) }; if frequency == 0 { fail("zero counter frequency"); } let mut console = SerialConsole::help_only(); let mut suppressed = 0usize; prompt(console.prompts()); write_line("TURKOS/CONSOLE: READY BLANK=0x5 HELP=0xb"); let mut index = 0usize; let mut target = 0usize; loop { let deadline = ticks() .checked_add(frequency.saturating_mul(5)) .unwrap_or(u64::MAX); while unsafe { read_volatile((base + 0x18) as *const u32) } & RXFE != 0 { if ticks() >= deadline { fail("input timeout"); } core::hint::spin_loop(); } let data = unsafe { read_volatile(base as *const u32) }; if data & DATA_ERROR != 0 { fail("PL011 data error"); } let byte = data as u8; if index == 0 { target = match byte { b'\n' => 5, b'h' => 11, _ => fail("unknown fixture"), }; } let label = match console.feed(byte) { ConsoleEvent::Input => "INPUT", ConsoleEvent::SuppressedLf => { suppressed += 1; "SUPPRESS" } ConsoleEvent::Blank { prompt: number } => { write_str("TURKOS/CONSOLE-BLANK: COUNT=0x"); write_hex(console.blanks() as usize); write_str("\r\n"); prompt(number); "BLANK" } ConsoleEvent::Help { prompt: number } => { write_str("TURKOS/CONSOLE-HELP: COUNT=0x"); write_hex(console.helps() as usize); write_str(" COMMANDS="); write_str(HELP_COMMANDS); write_str("\r\n"); write_line("TURKOS/CONSOLE-HELP-LINE: help - lists available commands"); prompt(number); "HELP" } ConsoleEvent::Echo { .. } => fail("unexpected echo in historical fixture"), ConsoleEvent::List { .. } => fail("unexpected ls in historical fixture"), ConsoleEvent::Cat { .. } => fail("unexpected cat in historical fixture"), ConsoleEvent::Run { .. } => fail("unexpected run in historical fixture"), ConsoleEvent::Wait { .. } => fail("unexpected wait in historical fixture"), ConsoleEvent::InvalidUtf8 { .. } => { fail("unexpected UTF-8 error in historical fixture") } ConsoleEvent::MalformedArguments { .. } => { fail("unexpected argument error in historical fixture") } ConsoleEvent::Cancelled { .. } => fail("unexpected Ctrl-C in historical fixture"), ConsoleEvent::Unhandled(_, _) => fail("unhandled command"), ConsoleEvent::TooLong => fail("overlong command"), }; write_str("TURKOS/CONSOLE-BYTE: INDEX=0x"); write_hex(index); write_str(" VALUE=0x"); write_hex(byte as usize); write_str(" EVENT="); write_str(label); write_str(" PROMPTS=0x"); write_hex(console.prompts() as usize); write_str(" BLANKS=0x"); write_hex(console.blanks() as usize); write_str(" HELPS=0x"); write_hex(console.helps() as usize); write_str(" SUPPRESSED=0x"); write_hex(suppressed); write_str("\r\n"); index += 1; if index == target { break; } } if target == 5 { if console.prompts() != 4 || console.blanks() != 3 || console.helps() != 0 || suppressed != 1 { fail("blank accounting"); } write_line( "TURKOS/CONSOLE: PASS MODE=BLANK PROMPTS=0x4 BLANKS=0x3 HELPS=0x0 SUPPRESSED=0x1 BYTES=0x5", ); } else { if console.prompts() != 3 || console.blanks() != 0 || console.helps() != 2 || suppressed != 1 { fail("help accounting"); } write_line( "TURKOS/CONSOLE: PASS MODE=HELP PROMPTS=0x3 BLANKS=0x0 HELPS=0x2 SUPPRESSED=0x1 BYTES=0xb", ); } park() }