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