base/
terminal.rs

1/// The global writer implementation.
2pub static GLOBAL_WRITER: OnceCell<LockedWriter> = OnceCell::uninit();
3
4/// Initialise a global writer using the framebuffer set up by the bootloader.
5pub fn init_writer(
6   buffer: &'static mut [u8],
7   info: FrameBufferInfo,
8   with_framebuffer: bool,
9   with_serial: bool,
10) {
11   let writer = GLOBAL_WRITER.get_or_init(move || {
12      LockedWriter::new(buffer, info, with_framebuffer, with_serial)
13   });
14
15   log::set_logger(writer).expect("logger already exists");
16   log::set_max_level(LevelFilter::Trace);
17   log::info!("Global writer/logger successfully initialised: {:?}", info);
18}
19
20pub struct LockedWriter {
21   pub writer: Option<Spinlock<TerminalWriter>>,
22   pub serial: Option<Spinlock<SerialPort>>,
23}
24
25impl LockedWriter {
26   pub fn new(
27      buffer: &'static mut [u8],
28      info: FrameBufferInfo,
29      writer_log_status: bool,
30      serial_log_status: bool,
31   ) -> Self {
32      let port = unsafe {
33         let mut serial = SerialPort::new(0x3F8);
34         serial.init();
35         serial
36      };
37
38      let writer = match writer_log_status {
39         true => Some(Spinlock::new(TerminalWriter::new(buffer, info))),
40         false => None,
41      };
42
43      let serial = match serial_log_status {
44         true => Some(Spinlock::new(port)),
45         false => None,
46      };
47
48      return LockedWriter {
49         writer,
50         serial,
51      };
52   }
53
54   /// Force-unlocks the logger to prevent a deadlock.
55   ///
56   /// ## Safety
57   /// This method is not memory safe and should be only used when absolutely necessary.
58   pub unsafe fn force_unlock(&self) {
59      if let Some(framebuffer) = &self.writer {
60         unsafe { framebuffer.force_unlock() };
61      }
62
63      if let Some(serial) = &self.serial {
64         unsafe { serial.force_unlock() };
65      }
66   }
67}
68
69impl log::Log for LockedWriter {
70   fn enabled(&self, _metadata: &log::Metadata) -> bool {
71      true
72   }
73
74   fn log(&self, record: &log::Record) {
75      if let Some(writer) = &self.writer {
76         let mut writer = writer.lock();
77         writeln!(writer, "{:5}: {}", record.level(), record.args()).unwrap();
78      }
79
80      if let Some(serial) = &self.serial {
81         let mut serial = serial.lock();
82         writeln!(serial, "{:5}: {}", record.level(), record.args()).unwrap();
83      }
84   }
85
86   fn flush(&self) {}
87}
88
89// MACROS //
90
91#[macro_export]
92macro_rules! print {
93   ($($args:tt)+) => ({
94      use core::fmt::Write;
95
96      if let Some(writer) = &$crate::terminal::GLOBAL_WRITER.get().unwrap().writer {
97         let mut writer = writer.lock();
98         let _ = write!(writer, $($args)+).unwrap();
99      }
100
101      if let Some(serial) = &$crate::terminal::GLOBAL_WRITER.get().unwrap().serial {
102         let mut serial = serial.lock();
103         let _ = write!(serial, $($args)+).unwrap();
104      }
105   });
106}
107
108#[macro_export]
109macro_rules! println {
110   () => ({
111      print!("\n");
112   });
113
114   ($fmt:expr) => ({
115      print!(concat!($fmt, "\r\n"))
116   });
117
118   ($fmt:expr, $($args:tt)+) => ({
119      print!(concat!($fmt, "\r\n"), $($args)+)
120   });
121}
122
123#[macro_export]
124pub macro clear_screen {
125   () => {
126      if let Some(writer) = &$crate::terminal::GLOBAL_WRITER.get().unwrap().writer {
127         let mut writer = writer.lock();
128         writer.clear();
129      }
130   }
131}
132
133#[doc(hidden)]
134pub fn _print(args: fmt::Arguments) {
135   use core::fmt::Write;
136
137   if let Some(writer) = &GLOBAL_WRITER.get().unwrap().writer {
138      let mut writer = writer.lock();
139      writer.write_fmt(args).unwrap();
140   }
141
142   if let Some(serial) = &GLOBAL_WRITER.get().unwrap().serial {
143      let mut serial = serial.lock();
144      serial.write_fmt(args).unwrap();
145   }
146}
147
148// MODULES //
149
150/// Font-related constants.
151pub mod font;
152
153/// A framebuffer-based writer implementation that piggy-backs off the buffer
154/// set up by the bootloader.
155pub mod framebuffer;
156
157// IMPORTS //
158
159use {
160   crate::uart::SerialPort,
161   self::framebuffer::TerminalWriter,
162   conquer_once::spin::OnceCell,
163   core::fmt::{self, Write},
164   log::LevelFilter,
165   spinning_top::Spinlock,
166   springboard_api::info::{FrameBufferInfo, PixelFormat},
167};