//! K405: bounded BS/DEL line editing through actual PL011 receive data. use super::*; use turkos_kernel::serial_rx::{EditResult, LineEditor, LineEndings, LineEvent}; const RXFE: u32 = 1 << 4; const DATA_ERROR: u32 = 0x0f00; const INPUT_COUNT: usize = 28; fn fail(reason: &str) -> ! { write_str("TURKOS/SERIAL-EDIT-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 } 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 editor = LineEditor::new(); let mut endings = LineEndings::new(); let mut lines = 0usize; let mut erased = 0usize; let mut empty = 0usize; let mut full = 0usize; let mut suppressed = 0usize; write_line("TURKOS/SERIAL-EDIT: READY EXPECTED=0x1c"); for index in 0..INPUT_COUNT { 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; let label = match endings.feed(byte) { LineEvent::Data(value) => match editor.feed(value) { EditResult::Inserted => "INSERT", EditResult::Full => { full += 1; "FULL" } EditResult::Erased => { erased += 1; "ERASE" } EditResult::Empty => { empty += 1; "EMPTY" } }, LineEvent::End => { let expected = match lines { 0 => b"AC".as_slice(), 1 => b"XXXXXXXXXXXXXXXZ".as_slice(), _ => fail("too many lines"), }; if editor.as_bytes() != expected { fail("line content"); } lines += 1; write_str("TURKOS/SERIAL-EDIT-LINE: NUMBER=0x"); write_hex(lines); write_str(" TEXT="); for value in editor.as_bytes() { write_byte(*value); } write_str("\r\n"); editor.clear(); "END" } LineEvent::SuppressedLf => { suppressed += 1; "SUPPRESS" } }; write_str("TURKOS/SERIAL-EDIT-BYTE: INDEX=0x"); write_hex(index); write_str(" VALUE=0x"); write_hex(byte as usize); write_str(" EVENT="); write_str(label); write_str(" LEN=0x"); write_hex(editor.as_bytes().len()); write_str(" LINES=0x"); write_hex(lines); write_str(" ERASED=0x"); write_hex(erased); write_str(" EMPTY=0x"); write_hex(empty); write_str(" FULL=0x"); write_hex(full); write_str(" SUPPRESSED=0x"); write_hex(suppressed); write_str("\r\n"); } if lines != 2 || erased != 2 || empty != 2 || full != 1 || suppressed != 1 || !editor.as_bytes().is_empty() { fail("final edit accounting"); } write_line( "TURKOS/SERIAL-EDIT: PASS LINES=0x2 ERASED=0x2 EMPTY=0x2 FULL=0x1 SUPPRESSED=0x1 BYTES=0x1c", ); park() }