Skip to main content

some_serial/ns16550/
rockchip_fiq.rs

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