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