Skip to main content

some_serial/ns16550/
rockchip_fiq.rs

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