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, SerialParts, SplitUart, UartInfo, 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 discard_rx(&mut self) {
580        UartPort::discard_rx(&mut self.serial)
581    }
582
583    fn write_tx(&mut self, bytes: &[u8]) -> usize {
584        UartPort::write_tx(&mut self.serial, bytes)
585    }
586
587    fn discard_tx(&mut self) -> bool {
588        UartPort::discard_tx(&mut self.serial)
589    }
590
591    fn tx_idle(&mut self) -> bool {
592        self.serial.tx_idle()
593    }
594
595    fn mask(&mut self, sources: SerialEventSet) {
596        UartPort::mask(&mut self.serial, sources);
597    }
598
599    fn mask_all(&mut self) {
600        UartPort::mask_all(&mut self.serial);
601    }
602
603    fn rearm(&mut self, sources: SerialEventSet) -> SerialEventSet {
604        UartPort::rearm(&mut self.serial, sources)
605    }
606}
607
608impl SplitUart for RockchipFiqSerial {
609    type Control = Self;
610    type Irq = Ns16550Irq<RockchipFiqPort>;
611    type EmergencyTx = super::Ns16550EmergencyTx<RockchipFiqPort>;
612
613    fn runtime_info(&self) -> UartInfo {
614        UartInfo {
615            name: "Rockchip FIQ Debugger UART",
616            register_base: self.serial.base.get_base(),
617            initial_baudrate: self.config.baudrate,
618        }
619    }
620
621    fn split(self) -> SerialParts<Self::Control, Self::Irq, Self::EmergencyTx> {
622        let irq = Ns16550Irq {
623            base: self.serial.base,
624            saved_lsr: LineStatusFlags::empty(),
625        };
626        let emergency_tx = super::Ns16550EmergencyTx {
627            base: self.serial.base,
628        };
629        SerialParts::new(self, irq, emergency_tx)
630    }
631}
632
633impl PollingUart for RockchipFiqSerial {
634    fn poll_status(&mut self) -> SerialEvent {
635        self.serial.poll_status()
636    }
637
638    fn write_byte(&mut self, byte: u8) {
639        self.serial.write_byte(byte);
640    }
641
642    fn read_byte(&mut self, status: SerialEvent) -> Option<Result<u8, TransferError>> {
643        self.serial.read_byte(status)
644    }
645}
646
647const COMMANDS: &[&str] = &[
648    "pc", "regs", "allregs", "bt", "reboot", "pcsr", "sleep", "nosleep", "console", "cpu", "reset",
649    "irqs", "kmsg", "version", "ps", "sysrq", "kgdb",
650];
651
652fn command_arg(cmd: &str, prefix: &str) -> Option<CommandString> {
653    let arg = cmd[prefix.len()..].trim();
654    if arg.is_empty() {
655        None
656    } else {
657        Some(command_string(arg))
658    }
659}
660
661fn command_string(value: &str) -> CommandString {
662    let mut out = CommandString::new();
663    let _ = out.push_str(value);
664    out
665}
666
667fn normalise_baudrate(baudrate: u32) -> u32 {
668    match baudrate {
669        115_200 | 1_500_000 => baudrate,
670        _ => 115_200,
671    }
672}
673
674#[cfg(test)]
675mod tests {
676    use super::*;
677
678    fn feed(debugger: &mut FiqDebugger, bytes: &[u8]) -> heapless::Vec<FiqDebuggerEvent, 64> {
679        let mut out = heapless::Vec::new();
680        for &byte in bytes {
681            debugger.handle_byte(byte, &mut |event| {
682                let _ = out.push(event);
683            });
684        }
685        out
686    }
687
688    #[test]
689    fn fiq_word_enters_debugger_unless_prefixed_by_space_or_underscore() {
690        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
691            debug_enable: true,
692            console_enable: true,
693            ..RockchipFiqConfig::default()
694        });
695
696        let events = feed(&mut debugger, b"fiq");
697        assert!(events.contains(&FiqDebuggerEvent::EnterDebugger));
698        assert!(!debugger.console_enabled());
699
700        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
701            debug_enable: true,
702            console_enable: true,
703            ..RockchipFiqConfig::default()
704        });
705        let events = feed(&mut debugger, b" fiq");
706        assert!(!events.contains(&FiqDebuggerEvent::EnterDebugger));
707        assert!(debugger.console_enabled());
708
709        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
710            debug_enable: true,
711            console_enable: true,
712            ..RockchipFiqConfig::default()
713        });
714        let events = feed(&mut debugger, b"_fiq");
715        assert!(!events.contains(&FiqDebuggerEvent::EnterDebugger));
716        assert!(debugger.console_enabled());
717    }
718
719    #[test]
720    fn newline_enables_debugger_when_debugging_is_disabled() {
721        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
722            debug_enable: false,
723            console_enable: false,
724            ..RockchipFiqConfig::default()
725        });
726
727        let events = feed(&mut debugger, b"\r");
728        assert!(debugger.debug_enabled());
729        assert!(
730            events
731                .iter()
732                .any(|event| matches!(event, FiqDebuggerEvent::OutputByte(b'>')))
733        );
734    }
735
736    #[test]
737    fn command_line_parses_console_and_sleep_modes() {
738        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
739            debug_enable: true,
740            console_enable: false,
741            ..RockchipFiqConfig::default()
742        });
743
744        let events = feed(&mut debugger, b"nosleep\r");
745        assert!(debugger.no_sleep());
746        assert!(events.contains(&FiqDebuggerEvent::Command(FiqCommand::NoSleep)));
747
748        let events = feed(&mut debugger, b"sleep\r");
749        assert!(!debugger.no_sleep());
750        assert!(events.contains(&FiqDebuggerEvent::Command(FiqCommand::Sleep)));
751
752        let events = feed(&mut debugger, b"console\r");
753        assert!(debugger.console_enabled());
754        assert!(events.contains(&FiqDebuggerEvent::ExitToConsole));
755        assert!(events.contains(&FiqDebuggerEvent::Command(FiqCommand::Console)));
756    }
757
758    #[test]
759    fn command_line_supports_backspace_history_and_tab_completion() {
760        let mut debugger = FiqDebugger::new(RockchipFiqConfig {
761            debug_enable: true,
762            console_enable: false,
763            ..RockchipFiqConfig::default()
764        });
765
766        let _ = feed(&mut debugger, b"nosleex\x08p\r");
767        assert!(debugger.no_sleep());
768
769        let _ = feed(&mut debugger, b"\x1b[A");
770        assert_eq!(debugger.current_line(), "nosleep");
771
772        let _ = feed(&mut debugger, b"\x1b[B");
773        assert_eq!(debugger.current_line(), "");
774
775        let _ = feed(&mut debugger, b"con\t");
776        assert_eq!(debugger.current_line(), "console");
777    }
778
779    #[test]
780    fn parser_covers_deferred_os_commands() {
781        assert_eq!(FiqCommand::parse("ps"), FiqCommand::Ps);
782        assert_eq!(FiqCommand::parse("sysrq"), FiqCommand::SysRq(None));
783        assert_eq!(FiqCommand::parse("sysrq g"), FiqCommand::SysRq(Some(b'g')));
784        assert_eq!(FiqCommand::parse("kgdb"), FiqCommand::Kgdb);
785
786        let mut arg = CommandString::new();
787        arg.push_str("bootloader").unwrap();
788        assert_eq!(
789            FiqCommand::parse("reboot bootloader"),
790            FiqCommand::Reboot(Some(arg))
791        );
792
793        let mut unknown = CommandString::new();
794        unknown.push_str("wat").unwrap();
795        assert_eq!(FiqCommand::parse("wat"), FiqCommand::Unknown(unknown));
796    }
797}