1use 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}