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