Skip to main content

some_serial/ns16550/
rockchip_fiq.rs

1//! Rockchip RK3588 FIQ debugger UART support.
2
3extern crate alloc;
4
5use alloc::boxed::Box;
6use core::{any::Any, num::NonZeroU32, ptr::NonNull};
7
8use heapless::{String, Vec};
9use rdif_serial::{
10    BSerial, Config, ConfigError, DataBits, DriverGeneric, Interface, InterruptMask, Parity,
11    SerialDyn, StopBits, TIrqHandler, TReciever, TSender, TransferError,
12};
13
14use super::{
15    Kind, Ns16550, Ns16550IrqHandler,
16    registers::{
17        UART_DLH, UART_DLL, UART_FCR, UART_IER, UART_IER_RDI, UART_IIR, UART_IIR_CTI, UART_LCR,
18        UART_LCR_DLAB, UART_LCR_WLEN8, UART_LSR, UART_LSR_BI, UART_LSR_DR, UART_LSR_TEMT, UART_MCR,
19        UART_RBR, UART_THR,
20    },
21};
22use crate::{RawReciever, RawSender};
23
24pub const ROCKCHIP_FIQ_RK3588_UART_CLOCK: u32 = 24_000_000;
25pub const ROCKCHIP_FIQ_DEFAULT_BAUDRATE: u32 = 1_500_000;
26
27const REG_SHIFT: usize = 2;
28const DEBUG_MAX: usize = 64;
29const HISTORY_MAX: usize = 16;
30const UART_USR: u8 = 0x1f;
31const RK_UART_RFL: u8 = 0x21;
32const UART_SRR: u8 = 0x22;
33const UART_USR_TX_FIFO_NOT_FULL: u32 = 0x02;
34const UART_USR_BUSY: u32 = 0x01;
35
36pub type CommandString = String<DEBUG_MAX>;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct RockchipFiqConfig {
40    pub serial_id: u32,
41    pub baudrate: u32,
42    pub clock_hz: u32,
43    pub irq_mode_enabled: bool,
44    pub debug_enable: bool,
45    pub console_enable: bool,
46}
47
48impl Default for RockchipFiqConfig {
49    fn default() -> Self {
50        Self {
51            serial_id: 0,
52            baudrate: ROCKCHIP_FIQ_DEFAULT_BAUDRATE,
53            clock_hz: ROCKCHIP_FIQ_RK3588_UART_CLOCK,
54            irq_mode_enabled: false,
55            debug_enable: true,
56            console_enable: true,
57        }
58    }
59}
60
61impl RockchipFiqConfig {
62    pub fn normalised(mut self) -> Self {
63        self.baudrate = normalise_baudrate(self.baudrate);
64        if self.clock_hz == 0 {
65            self.clock_hz = ROCKCHIP_FIQ_RK3588_UART_CLOCK;
66        }
67        self
68    }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub enum FiqCommand {
73    Pc,
74    Regs,
75    AllRegs,
76    Bt,
77    Pcsr,
78    Irqs,
79    Kmsg,
80    Version,
81    Ps,
82    SysRq(Option<u8>),
83    Reboot(Option<CommandString>),
84    Reset(Option<CommandString>),
85    Kgdb,
86    Cpu,
87    CpuSwitch(u32),
88    Sleep,
89    NoSleep,
90    Console,
91    Help,
92    Unknown(CommandString),
93}
94
95impl FiqCommand {
96    pub fn parse(cmd: &str) -> Self {
97        let cmd = cmd.trim();
98        match cmd {
99            "help" | "?" => Self::Help,
100            "pc" => Self::Pc,
101            "regs" => Self::Regs,
102            "allregs" => Self::AllRegs,
103            "bt" => Self::Bt,
104            "pcsr" => Self::Pcsr,
105            "irqs" => Self::Irqs,
106            "kmsg" => Self::Kmsg,
107            "version" => Self::Version,
108            "ps" => Self::Ps,
109            "sysrq" => Self::SysRq(None),
110            "kgdb" => Self::Kgdb,
111            "cpu" => Self::Cpu,
112            "sleep" => Self::Sleep,
113            "nosleep" => Self::NoSleep,
114            "console" => Self::Console,
115            _ if cmd.starts_with("sysrq ") => Self::SysRq(cmd.as_bytes().get(6).copied()),
116            _ if cmd.starts_with("reboot") => Self::Reboot(command_arg(cmd, "reboot")),
117            _ if cmd.starts_with("reset") => Self::Reset(command_arg(cmd, "reset")),
118            _ if cmd.starts_with("cpu ") => cmd[4..]
119                .trim()
120                .parse::<u32>()
121                .map(Self::CpuSwitch)
122                .unwrap_or_else(|_| Self::Unknown(command_string(cmd))),
123            _ => Self::Unknown(command_string(cmd)),
124        }
125    }
126
127    pub fn needs_irq_helper(&self) -> bool {
128        matches!(
129            self,
130            Self::Ps | Self::SysRq(_) | Self::Reboot(_) | Self::Kgdb | Self::Unknown(_)
131        )
132    }
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
136pub enum FiqDebuggerEvent {
137    ConsoleByte(u8),
138    OutputByte(u8),
139    EnterDebugger,
140    ExitToConsole,
141    Command(FiqCommand),
142    NeedIrqHelper,
143}
144
145pub struct FiqDebugger {
146    debug_enable: bool,
147    console_enable: bool,
148    no_sleep: bool,
149    line: CommandString,
150    history: Vec<CommandString, HISTORY_MAX>,
151    history_cursor: Option<usize>,
152    prev3: u8,
153    prev2: u8,
154    prev1: u8,
155    escape_state: u8,
156    last_newline: u8,
157}
158
159impl FiqDebugger {
160    pub fn new(config: RockchipFiqConfig) -> Self {
161        Self {
162            debug_enable: config.debug_enable,
163            console_enable: config.console_enable,
164            no_sleep: false,
165            line: CommandString::new(),
166            history: Vec::new(),
167            history_cursor: None,
168            prev3: 0,
169            prev2: 0,
170            prev1: 0,
171            escape_state: 0,
172            last_newline: 0,
173        }
174    }
175
176    pub fn debug_enabled(&self) -> bool {
177        self.debug_enable
178    }
179
180    pub fn console_enabled(&self) -> bool {
181        self.console_enable
182    }
183
184    pub fn no_sleep(&self) -> bool {
185        self.no_sleep
186    }
187
188    pub fn current_line(&self) -> &str {
189        self.line.as_str()
190    }
191
192    pub fn handle_byte(&mut self, byte: u8, emit: &mut impl FnMut(FiqDebuggerEvent)) {
193        let is_break = self.update_break_detector(byte);
194
195        if !self.debug_enable {
196            if byte == b'\r' || byte == b'\n' {
197                self.debug_enable = true;
198                self.line.clear();
199                self.prompt(emit);
200            }
201            return;
202        }
203
204        if is_break {
205            self.enter_debugger(emit);
206            return;
207        }
208
209        if self.console_enable {
210            emit(FiqDebuggerEvent::ConsoleByte(byte));
211            emit(FiqDebuggerEvent::NeedIrqHelper);
212            return;
213        }
214
215        if self.handle_escape(byte, emit) {
216            return;
217        }
218
219        match byte {
220            9 => self.complete_unique_prefix(emit),
221            8 | 127 => self.backspace(emit),
222            b'\r' | b'\n' => self.submit_newline(byte, emit),
223            b' '..=126 if self.line.len() < DEBUG_MAX - 1 => {
224                let _ = self.line.push(byte as char);
225                emit(FiqDebuggerEvent::OutputByte(byte));
226            }
227            _ => {}
228        }
229    }
230
231    fn update_break_detector(&mut self, byte: u8) -> bool {
232        let is_break = byte == b'q'
233            && self.prev1 == b'i'
234            && self.prev2 == b'f'
235            && self.prev3 != b'_'
236            && self.prev3 != b' ';
237        self.prev3 = self.prev2;
238        self.prev2 = self.prev1;
239        self.prev1 = byte;
240        is_break
241    }
242
243    fn enter_debugger(&mut self, emit: &mut impl FnMut(FiqDebuggerEvent)) {
244        self.debug_enable = true;
245        self.console_enable = false;
246        self.line.clear();
247        self.history_cursor = None;
248        emit(FiqDebuggerEvent::EnterDebugger);
249        self.emit_str("\nWelcome to fiq debugger mode\n", emit);
250        self.emit_str("Enter ? to get command help\n", emit);
251        self.prompt(emit);
252    }
253
254    fn handle_escape(&mut self, byte: u8, emit: &mut impl FnMut(FiqDebuggerEvent)) -> bool {
255        match (self.escape_state, byte) {
256            (0, 0x1b) => {
257                self.escape_state = 1;
258                true
259            }
260            (1, b'[') => {
261                self.escape_state = 2;
262                true
263            }
264            (2, b'A') => {
265                self.escape_state = 0;
266                self.history_up(emit);
267                true
268            }
269            (2, b'B') => {
270                self.escape_state = 0;
271                self.history_down(emit);
272                true
273            }
274            (2, b'C' | b'D') => {
275                self.escape_state = 0;
276                true
277            }
278            (1 | 2, _) => {
279                self.escape_state = 0;
280                false
281            }
282            _ => false,
283        }
284    }
285
286    fn submit_newline(&mut self, byte: u8, emit: &mut impl FnMut(FiqDebuggerEvent)) {
287        if byte == b'\r' || (byte == b'\n' && self.last_newline != b'\r') {
288            emit(FiqDebuggerEvent::OutputByte(b'\r'));
289            emit(FiqDebuggerEvent::OutputByte(b'\n'));
290        }
291        self.last_newline = byte;
292
293        if self.line.is_empty() {
294            self.prompt(emit);
295            return;
296        }
297
298        let line = self.line.clone();
299        self.line.clear();
300        self.history_cursor = None;
301        self.push_history(line.clone());
302
303        let command = FiqCommand::parse(line.as_str());
304        match command {
305            FiqCommand::Sleep => {
306                self.no_sleep = false;
307                self.emit_str("enabling sleep\n", emit);
308            }
309            FiqCommand::NoSleep => {
310                self.no_sleep = true;
311                self.emit_str("disabling sleep\n", emit);
312            }
313            FiqCommand::Console => {
314                self.emit_str("console mode\n", emit);
315                self.console_enable = true;
316                emit(FiqDebuggerEvent::ExitToConsole);
317            }
318            FiqCommand::Help => self.emit_help(emit),
319            _ => {}
320        }
321
322        let needs_irq_helper = command.needs_irq_helper();
323        emit(FiqDebuggerEvent::Command(command));
324        if needs_irq_helper || (self.debug_enable && !self.no_sleep) {
325            emit(FiqDebuggerEvent::NeedIrqHelper);
326        }
327        if !self.console_enable {
328            self.prompt(emit);
329        }
330    }
331
332    fn push_history(&mut self, line: CommandString) {
333        if self.history.last() == Some(&line) {
334            return;
335        }
336        if self.history.len() == HISTORY_MAX {
337            self.history.remove(0);
338        }
339        let _ = self.history.push(line);
340    }
341
342    fn history_up(&mut self, emit: &mut impl FnMut(FiqDebuggerEvent)) {
343        if self.history.is_empty() {
344            return;
345        }
346        let next = self
347            .history_cursor
348            .map(|idx| idx.saturating_sub(1))
349            .unwrap_or(self.history.len() - 1);
350        self.history_cursor = Some(next);
351        let line = self.history[next].clone();
352        self.replace_line(line, emit);
353    }
354
355    fn history_down(&mut self, emit: &mut impl FnMut(FiqDebuggerEvent)) {
356        let Some(idx) = self.history_cursor else {
357            return;
358        };
359        if idx + 1 < self.history.len() {
360            self.history_cursor = Some(idx + 1);
361            let line = self.history[idx + 1].clone();
362            self.replace_line(line, emit);
363        } else {
364            self.history_cursor = None;
365            self.replace_line(CommandString::new(), emit);
366        }
367    }
368
369    fn replace_line(&mut self, line: CommandString, emit: &mut impl FnMut(FiqDebuggerEvent)) {
370        while !self.line.is_empty() {
371            self.backspace(emit);
372        }
373        self.line = line;
374        let bytes: Vec<u8, DEBUG_MAX> = self.line.as_bytes().iter().copied().collect();
375        for byte in bytes {
376            emit(FiqDebuggerEvent::OutputByte(byte));
377        }
378    }
379
380    fn backspace(&mut self, emit: &mut impl FnMut(FiqDebuggerEvent)) {
381        if self.line.pop().is_some() {
382            emit(FiqDebuggerEvent::OutputByte(8));
383            emit(FiqDebuggerEvent::OutputByte(b' '));
384            emit(FiqDebuggerEvent::OutputByte(8));
385        }
386    }
387
388    fn complete_unique_prefix(&mut self, emit: &mut impl FnMut(FiqDebuggerEvent)) {
389        let mut found = None;
390        for cmd in COMMANDS {
391            if cmd.starts_with(self.line.as_str()) {
392                if found.is_some() {
393                    return;
394                }
395                found = Some(*cmd);
396            }
397        }
398
399        let Some(cmd) = found else {
400            return;
401        };
402        if cmd.len() <= self.line.len() {
403            return;
404        }
405        for &byte in &cmd.as_bytes()[self.line.len()..] {
406            if self.line.push(byte as char).is_ok() {
407                emit(FiqDebuggerEvent::OutputByte(byte));
408            }
409        }
410    }
411
412    fn prompt(&self, emit: &mut impl FnMut(FiqDebuggerEvent)) {
413        self.emit_str("> ", emit);
414    }
415
416    fn emit_help(&self, emit: &mut impl FnMut(FiqDebuggerEvent)) {
417        self.emit_str(
418            "pc regs allregs bt reboot sleep nosleep console cpu reset irqs kmsg version ps sysrq \
419             kgdb\n",
420            emit,
421        );
422    }
423
424    fn emit_str(&self, s: &str, emit: &mut impl FnMut(FiqDebuggerEvent)) {
425        for byte in s.bytes() {
426            emit(FiqDebuggerEvent::OutputByte(byte));
427        }
428    }
429}
430
431#[derive(Clone, Copy, Debug)]
432pub struct RockchipFiqPort {
433    base: usize,
434}
435
436impl RockchipFiqPort {
437    pub const fn new(base: usize) -> Self {
438        Self { base }
439    }
440
441    pub fn base_addr(&self) -> usize {
442        self.base
443    }
444
445    fn reg_addr(&self, reg: u8) -> usize {
446        self.base + ((reg as usize) << REG_SHIFT)
447    }
448
449    fn read_u32(&self, reg: u8) -> u32 {
450        unsafe { (self.reg_addr(reg) as *const u32).read_volatile() }
451    }
452
453    fn write_u32(&self, reg: u8, value: u32) {
454        unsafe { (self.reg_addr(reg) as *mut u32).write_volatile(value) }
455    }
456
457    fn init_debug_port(&self, baudrate: u32) {
458        if self.read_reg(UART_LSR) & UART_LSR_DR != 0 {
459            let _ = self.read_reg(UART_RBR);
460        }
461
462        let dll = match normalise_baudrate(baudrate) {
463            1_500_000 => 0x01,
464            _ => 0x0d,
465        };
466
467        self.write_reg(UART_SRR, 0x07);
468        for _ in 0..1024 {
469            core::hint::spin_loop();
470        }
471        self.write_reg(UART_MCR, 0x10);
472        self.write_reg(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
473        self.write_reg(UART_DLL, dll);
474        self.write_reg(UART_DLH, 0);
475        self.write_reg(UART_LCR, UART_LCR_WLEN8);
476        self.write_reg(UART_IER, UART_IER_RDI);
477        self.write_reg(UART_FCR, 0x01);
478        self.write_reg(UART_MCR, 0);
479    }
480
481    fn read_debug_byte(&self) -> Option<u8> {
482        let iir = self.read_u32(UART_IIR);
483        let usr = self.read_u32(UART_USR);
484        let lsr = self.read_reg(UART_LSR);
485
486        if (iir & 0x3f) == UART_IIR_CTI as u32 {
487            let rfl = self.read_u32(RK_UART_RFL);
488            if lsr & (UART_LSR_DR | UART_LSR_BI) == 0 && usr & UART_USR_BUSY == 0 && rfl == 0 {
489                let _ = self.read_reg(UART_RBR);
490            }
491        }
492
493        if lsr & UART_LSR_DR != 0 {
494            Some(self.read_reg(UART_RBR))
495        } else {
496            None
497        }
498    }
499
500    fn write_debug_byte(&self, byte: u8) -> bool {
501        let mut count = 10_000;
502        while self.read_u32(UART_USR) & UART_USR_TX_FIFO_NOT_FULL == 0 {
503            if count == 0 {
504                return false;
505            }
506            count -= 1;
507            core::hint::spin_loop();
508        }
509        self.write_reg(UART_THR, byte);
510        true
511    }
512
513    fn flush(&self) {
514        while self.read_reg(UART_LSR) & UART_LSR_TEMT == 0 {
515            core::hint::spin_loop();
516        }
517    }
518}
519
520impl Kind for RockchipFiqPort {
521    fn read_reg(&self, reg: u8) -> u8 {
522        (self.read_u32(reg) & 0xff) as u8
523    }
524
525    fn write_reg(&self, reg: u8, val: u8) {
526        self.write_u32(reg, val as u32);
527    }
528
529    fn get_base(&self) -> usize {
530        self.base
531    }
532
533    fn set_baudrate(&self, _clock_freq: u32, baudrate: u32) -> Result<(), ConfigError> {
534        if !matches!(baudrate, 115_200 | 1_500_000) {
535            return Err(ConfigError::InvalidBaudrate);
536        }
537        self.init_debug_port(baudrate);
538        Ok(())
539    }
540
541    fn baudrate(&self, _clock_freq: u32) -> u32 {
542        let lcr = self.read_reg(UART_LCR);
543        self.write_reg(UART_LCR, lcr | UART_LCR_DLAB);
544        let dll = self.read_reg(UART_DLL);
545        let dlh = self.read_reg(UART_DLH);
546        self.write_reg(UART_LCR, lcr);
547
548        match (dll, dlh) {
549            (0x01, 0) => 1_500_000,
550            (0x0d, 0) => 115_200,
551            _ => 0,
552        }
553    }
554}
555
556pub struct RockchipFiqSender {
557    pub(crate) base: RockchipFiqPort,
558}
559
560impl RockchipFiqSender {
561    pub fn base_addr(&self) -> usize {
562        self.base.base_addr()
563    }
564}
565
566impl RawSender for RockchipFiqSender {
567    fn write_byte(&mut self, byte: u8) -> bool {
568        self.base.write_debug_byte(byte)
569    }
570}
571
572pub struct RockchipFiqReceiver {
573    pub(crate) base: RockchipFiqPort,
574}
575
576impl RockchipFiqReceiver {
577    pub fn base_addr(&self) -> usize {
578        self.base.base_addr()
579    }
580}
581
582impl RawReciever for RockchipFiqReceiver {
583    fn read_byte(&mut self) -> Option<Result<u8, TransferError>> {
584        self.base.read_debug_byte().map(Ok)
585    }
586}
587
588impl Ns16550<RockchipFiqPort> {
589    pub fn new_rockchip_fiq(base: NonNull<u8>, clock_freq: u32) -> Self {
590        let base = RockchipFiqPort::new(base.as_ptr() as usize);
591        Self {
592            base,
593            clock_freq,
594            irq: Some(Ns16550IrqHandler { base }),
595            tx: Some(crate::Sender::Ns16550RockchipFiqSender(RockchipFiqSender {
596                base,
597            })),
598            rx: Some(crate::Reciever::Ns16550RockchipFiqReciever(
599                RockchipFiqReceiver { base },
600            )),
601        }
602    }
603}
604
605pub struct RockchipFiqSerial {
606    serial: BSerial,
607    port: RockchipFiqPort,
608    debugger: FiqDebugger,
609    config: RockchipFiqConfig,
610}
611
612impl RockchipFiqSerial {
613    pub fn new(base: NonNull<u8>, config: RockchipFiqConfig) -> Self {
614        let config = config.normalised();
615        let port = RockchipFiqPort::new(base.as_ptr() as usize);
616        port.init_debug_port(config.baudrate);
617        let serial = SerialDyn::new_boxed(Ns16550::new_rockchip_fiq(base, config.clock_hz));
618        Self {
619            serial,
620            port,
621            debugger: FiqDebugger::new(config),
622            config,
623        }
624    }
625
626    pub fn new_boxed(base: NonNull<u8>, config: RockchipFiqConfig) -> BSerial {
627        Box::new(Self::new(base, config))
628    }
629
630    pub fn config(&self) -> RockchipFiqConfig {
631        self.config
632    }
633
634    pub fn handle_fiq_byte(&mut self, byte: u8, emit: &mut impl FnMut(FiqDebuggerEvent)) {
635        self.debugger.handle_byte(byte, emit);
636    }
637
638    pub fn poll_fiq_events(&mut self, emit: &mut impl FnMut(FiqDebuggerEvent)) -> usize {
639        let mut count = 0;
640        while let Some(byte) = self.port.read_debug_byte() {
641            count += 1;
642            self.debugger.handle_byte(byte, emit);
643        }
644        if !self.debugger.console_enabled() {
645            self.port.flush();
646        }
647        count
648    }
649
650    pub fn debugger(&self) -> &FiqDebugger {
651        &self.debugger
652    }
653
654    pub fn debugger_mut(&mut self) -> &mut FiqDebugger {
655        &mut self.debugger
656    }
657}
658
659impl DriverGeneric for RockchipFiqSerial {
660    fn name(&self) -> &str {
661        "Rockchip FIQ Debugger UART"
662    }
663
664    fn raw_any(&self) -> Option<&dyn Any> {
665        Some(self)
666    }
667
668    fn raw_any_mut(&mut self) -> Option<&mut dyn Any> {
669        Some(self)
670    }
671}
672
673impl Interface for RockchipFiqSerial {
674    fn irq_handler(&mut self) -> Option<Box<dyn TIrqHandler>> {
675        self.serial.irq_handler()
676    }
677
678    fn take_tx(&mut self) -> Option<Box<dyn TSender>> {
679        self.serial.take_tx()
680    }
681
682    fn take_rx(&mut self) -> Option<Box<dyn TReciever>> {
683        self.serial.take_rx()
684    }
685
686    fn base_addr(&self) -> usize {
687        self.serial.base_addr()
688    }
689
690    fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
691        self.serial.set_config(config)
692    }
693
694    fn baudrate(&self) -> u32 {
695        self.serial.baudrate()
696    }
697
698    fn data_bits(&self) -> DataBits {
699        self.serial.data_bits()
700    }
701
702    fn stop_bits(&self) -> StopBits {
703        self.serial.stop_bits()
704    }
705
706    fn parity(&self) -> Parity {
707        self.serial.parity()
708    }
709
710    fn clock_freq(&self) -> Option<NonZeroU32> {
711        self.serial.clock_freq()
712    }
713
714    fn enable_loopback(&mut self) {
715        self.serial.enable_loopback()
716    }
717
718    fn disable_loopback(&mut self) {
719        self.serial.disable_loopback()
720    }
721
722    fn is_loopback_enabled(&self) -> bool {
723        self.serial.is_loopback_enabled()
724    }
725
726    fn enable_interrupts(&mut self, mask: InterruptMask) {
727        self.serial.enable_interrupts(mask)
728    }
729
730    fn disable_interrupts(&mut self, mask: InterruptMask) {
731        self.serial.disable_interrupts(mask)
732    }
733
734    fn get_enabled_interrupts(&self) -> InterruptMask {
735        self.serial.get_enabled_interrupts()
736    }
737}
738
739const COMMANDS: &[&str] = &[
740    "pc", "regs", "allregs", "bt", "reboot", "pcsr", "sleep", "nosleep", "console", "cpu", "reset",
741    "irqs", "kmsg", "version", "ps", "sysrq", "kgdb",
742];
743
744fn command_arg(cmd: &str, prefix: &str) -> Option<CommandString> {
745    let arg = cmd[prefix.len()..].trim();
746    if arg.is_empty() {
747        None
748    } else {
749        Some(command_string(arg))
750    }
751}
752
753fn command_string(value: &str) -> CommandString {
754    let mut out = CommandString::new();
755    let _ = out.push_str(value);
756    out
757}
758
759fn normalise_baudrate(baudrate: u32) -> u32 {
760    match baudrate {
761        115_200 | 1_500_000 => baudrate,
762        _ => 115_200,
763    }
764}
765
766#[cfg(test)]
767mod tests {
768    use super::*;
769
770    fn feed(debugger: &mut FiqDebugger, bytes: &[u8]) -> heapless::Vec<FiqDebuggerEvent, 64> {
771        let mut out = heapless::Vec::new();
772        for &byte in bytes {
773            debugger.handle_byte(byte, &mut |event| {
774                let _ = out.push(event);
775            });
776        }
777        out
778    }
779
780    #[test]
781    fn fiq_word_enters_debugger_unless_prefixed_by_space_or_underscore() {
782        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
783            debug_enable: true,
784            console_enable: true,
785            ..RockchipFiqConfig::default()
786        });
787
788        let events = feed(&mut debugger, b"fiq");
789        assert!(events.contains(&FiqDebuggerEvent::EnterDebugger));
790        assert!(!debugger.console_enabled());
791
792        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
793            debug_enable: true,
794            console_enable: true,
795            ..RockchipFiqConfig::default()
796        });
797        let events = feed(&mut debugger, b" fiq");
798        assert!(!events.contains(&FiqDebuggerEvent::EnterDebugger));
799        assert!(debugger.console_enabled());
800
801        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
802            debug_enable: true,
803            console_enable: true,
804            ..RockchipFiqConfig::default()
805        });
806        let events = feed(&mut debugger, b"_fiq");
807        assert!(!events.contains(&FiqDebuggerEvent::EnterDebugger));
808        assert!(debugger.console_enabled());
809    }
810
811    #[test]
812    fn newline_enables_debugger_when_debugging_is_disabled() {
813        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
814            debug_enable: false,
815            console_enable: false,
816            ..RockchipFiqConfig::default()
817        });
818
819        let events = feed(&mut debugger, b"\r");
820        assert!(debugger.debug_enabled());
821        assert!(
822            events
823                .iter()
824                .any(|event| matches!(event, FiqDebuggerEvent::OutputByte(b'>')))
825        );
826    }
827
828    #[test]
829    fn command_line_parses_console_and_sleep_modes() {
830        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
831            debug_enable: true,
832            console_enable: false,
833            ..RockchipFiqConfig::default()
834        });
835
836        let events = feed(&mut debugger, b"nosleep\r");
837        assert!(debugger.no_sleep());
838        assert!(events.contains(&FiqDebuggerEvent::Command(FiqCommand::NoSleep)));
839
840        let events = feed(&mut debugger, b"sleep\r");
841        assert!(!debugger.no_sleep());
842        assert!(events.contains(&FiqDebuggerEvent::Command(FiqCommand::Sleep)));
843
844        let events = feed(&mut debugger, b"console\r");
845        assert!(debugger.console_enabled());
846        assert!(events.contains(&FiqDebuggerEvent::ExitToConsole));
847        assert!(events.contains(&FiqDebuggerEvent::Command(FiqCommand::Console)));
848    }
849
850    #[test]
851    fn command_line_supports_backspace_history_and_tab_completion() {
852        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
853            debug_enable: true,
854            console_enable: false,
855            ..RockchipFiqConfig::default()
856        });
857
858        let _ = feed(&mut debugger, b"nosleex\x08p\r");
859        assert!(debugger.no_sleep());
860
861        let _ = feed(&mut debugger, b"\x1b[A");
862        assert_eq!(debugger.current_line(), "nosleep");
863
864        let _ = feed(&mut debugger, b"\x1b[B");
865        assert_eq!(debugger.current_line(), "");
866
867        let _ = feed(&mut debugger, b"con\t");
868        assert_eq!(debugger.current_line(), "console");
869    }
870
871    #[test]
872    fn parser_covers_deferred_os_commands() {
873        assert_eq!(FiqCommand::parse("ps"), FiqCommand::Ps);
874        assert_eq!(FiqCommand::parse("sysrq"), FiqCommand::SysRq(None));
875        assert_eq!(FiqCommand::parse("sysrq g"), FiqCommand::SysRq(Some(b'g')));
876        assert_eq!(FiqCommand::parse("kgdb"), FiqCommand::Kgdb);
877
878        let mut arg = CommandString::new();
879        arg.push_str("bootloader").unwrap();
880        assert_eq!(
881            FiqCommand::parse("reboot bootloader"),
882            FiqCommand::Reboot(Some(arg))
883        );
884
885        let mut unknown = CommandString::new();
886        unknown.push_str("wat").unwrap();
887        assert_eq!(FiqCommand::parse("wat"), FiqCommand::Unknown(unknown));
888    }
889}