diff --git a/kernel/src/arch/aarch64/board/raspi3/fb.rs b/kernel/src/arch/aarch64/board/raspi3/fb.rs index 5d6d8f3..19f9887 100644 --- a/kernel/src/arch/aarch64/board/raspi3/fb.rs +++ b/kernel/src/arch/aarch64/board/raspi3/fb.rs @@ -175,6 +175,16 @@ impl Framebuffer { RGBA8888 => self.buf.write32(y * self.fb_info.xres + x, pixel), } } + + /// Clean screen + pub fn clear(&mut self) { + let mut start = self.base_addr(); + let end = start + self.fb_info.screen_size as usize; + while start < end { + unsafe { *(start as *mut usize) = 0 } + start += core::mem::size_of::(); + } + } } lazy_static! { @@ -185,7 +195,6 @@ lazy_static! { pub fn init() { match Framebuffer::new(0, 0, 0) { Ok(fb) => { - let info = fb.fb_info; info!("framebuffer: init end\n{:#x?}", fb); *FRAME_BUFFER.lock() = Some(fb); } diff --git a/kernel/src/arch/aarch64/board/raspi3/serial.rs b/kernel/src/arch/aarch64/board/raspi3/serial.rs index d74432a..abb4ff6 100644 --- a/kernel/src/arch/aarch64/board/raspi3/serial.rs +++ b/kernel/src/arch/aarch64/board/raspi3/serial.rs @@ -22,7 +22,7 @@ impl SerialPort { } /// Init a newly created SerialPort, can only be called once. - pub fn init(&mut self) { + fn init(&mut self) { assert_has_not_been_called!("SerialPort::init must be called only once"); self.mu.init(); diff --git a/kernel/src/arch/aarch64/driver/console.rs b/kernel/src/arch/aarch64/driver/console.rs new file mode 100644 index 0000000..f0b01e5 --- /dev/null +++ b/kernel/src/arch/aarch64/driver/console.rs @@ -0,0 +1,198 @@ +//! Framebuffer console display driver for ARM64 + +use super::board::fb::{FramebufferInfo, FRAME_BUFFER}; +use alloc::vec::Vec; +use core::fmt::{self, Write}; +use lazy_static::lazy_static; +use spin::Mutex; + +#[derive(Debug, Clone, Copy)] +struct Color(u8); + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ConsoleChar { + ascii_char: u8, + color: Color, +} + +impl Default for ConsoleChar { + fn default() -> Self { + ConsoleChar { + ascii_char: b' ', + color: Color(0), + } + } +} + +const CHAR_WIDTH: u16 = 8; +const CHAR_HEIGHT: u16 = 16; + +/// Character buffer +struct ConsoleBuffer { + num_row: u16, + num_col: u16, + buf: Vec>, +} + +impl ConsoleBuffer { + fn new(num_row: u16, num_col: u16) -> ConsoleBuffer { + ConsoleBuffer { + num_row, + num_col, + buf: vec![vec![ConsoleChar::default(); num_col as usize]; num_row as usize], + } + } + + /// Read one character at `(row, col)`. + fn read(&self, row: u16, col: u16) -> ConsoleChar { + self.buf[row as usize][col as usize] + } + + /// Write one character at `(row, col)`. + /// TODO: font & color + fn write(&mut self, row: u16, col: u16, ch: ConsoleChar) { + self.buf[row as usize][col as usize] = ch; + + let off_x = col * CHAR_WIDTH; + let off_y = row * CHAR_HEIGHT; + if let Some(fb) = FRAME_BUFFER.lock().as_mut() { + for y in 0..CHAR_HEIGHT { + for x in 0..CHAR_WIDTH { + let pixel = if ch.color.0 == 0 { 0 } else { !0 }; + fb.write((off_x + x) as u32, (off_y + y) as u32, pixel); + } + } + } + } + + /// Delete one character at `(row, col)`. + fn delete(&mut self, row: u16, col: u16) { + self.write(row, col, ConsoleChar::default()); + } + + /// Insert one blank line at the bottom and remove the top line. + /// TODO: improve performance + fn new_line(&mut self) { + for i in 1..self.num_row { + for j in 0..self.num_col { + self.write(i - 1, j, self.read(i, j)); + } + } + for j in 0..self.num_col { + self.write(self.num_row - 1, j, ConsoleChar::default()); + } + } + + /// Clear the entire buffer and screen. + fn clear(&mut self) { + for i in 0..self.num_row as usize { + for j in 0..self.num_col as usize { + self.buf[i][j] = ConsoleChar::default() + } + } + if let Some(fb) = FRAME_BUFFER.lock().as_mut() { + fb.clear(); + } + } +} + +/// Console structure +pub struct Console { + /// current color + color: Color, + /// cursor row + row: u16, + /// cursor column + col: u16, + /// number of rows + num_row: u16, + /// number of columns + num_col: u16, + /// character buffer + buf: ConsoleBuffer, +} + +impl Console { + fn new(fb: &FramebufferInfo) -> Console { + let num_row = fb.yres as u16 / CHAR_HEIGHT; + let num_col = fb.xres as u16 / CHAR_WIDTH; + Console { + color: Color(1), + row: 0, + col: 0, + num_row, + num_col, + buf: ConsoleBuffer::new(num_row, num_col), + } + } + + fn new_line(&mut self) { + self.col = 0; + if self.row < self.num_row - 1 { + self.row += 1; + } else { + self.buf.new_line(); + } + } + + fn write_byte(&mut self, byte: u8) { + match byte { + b'\x7f' => { + if self.col > 0 { + self.col -= 1; + self.buf.delete(self.row, self.col); + } else if self.row > 0 { + self.row -= 1; + self.col = self.num_col - 1; + self.buf.delete(self.row, self.col); + } + } + b'\n' => self.new_line(), + b'\r' => self.col = 0, + byte => { + if self.col >= self.num_col { + self.new_line(); + } + + let ch = ConsoleChar { + ascii_char: byte, + color: self.color, + }; + self.buf.write(self.row, self.col, ch); + self.col += 1; + } + } + } + + pub fn clear(&mut self) { + self.color = Color(1); + self.row = 0; + self.col = 0; + self.buf.clear(); + } +} + +impl fmt::Write for Console { + fn write_str(&mut self, s: &str) -> fmt::Result { + for byte in s.bytes() { + self.write_byte(byte) + } + Ok(()) + } +} + +lazy_static! { + pub static ref CONSOLE: Mutex> = Mutex::new(None); +} + +/// Initialize console driver +pub fn init() { + if let Some(fb) = FRAME_BUFFER.lock().as_ref() { + *CONSOLE.lock() = Some(Console::new(&fb.fb_info)); + } + + if let Some(console) = CONSOLE.lock().as_mut() { + console.write_str("Hello Raspberry Pi!\n").unwrap(); + } +} diff --git a/kernel/src/arch/aarch64/driver/mod.rs b/kernel/src/arch/aarch64/driver/mod.rs index 43d8c43..78e3ba5 100644 --- a/kernel/src/arch/aarch64/driver/mod.rs +++ b/kernel/src/arch/aarch64/driver/mod.rs @@ -1,12 +1,14 @@ -/// ARM64 drivers +//! ARM64 drivers +use super::board; use once::*; -use super::board; +pub mod console; /// Initialize ARM64 common drivers pub fn init() { - assert_has_not_been_called!(); + assert_has_not_been_called!("driver::init must be called only once"); board::init_driver(); + console::init(); } diff --git a/kernel/src/arch/aarch64/io.rs b/kernel/src/arch/aarch64/io.rs index bb3c986..b9815d3 100644 --- a/kernel/src/arch/aarch64/io.rs +++ b/kernel/src/arch/aarch64/io.rs @@ -1,14 +1,20 @@ //! Serial driver for aarch64. -use core::fmt::{Arguments, Write}; use super::board::serial::*; +use super::driver::console::CONSOLE; +use core::fmt::{Arguments, Write}; pub fn getchar() -> char { - unsafe { SERIAL_PORT.force_unlock(); } + unsafe { SERIAL_PORT.force_unlock() } SERIAL_PORT.lock().receive() as char } pub fn putfmt(fmt: Arguments) { - unsafe { SERIAL_PORT.force_unlock(); } - SERIAL_PORT.lock().write_fmt(fmt).unwrap() + unsafe { SERIAL_PORT.force_unlock() } + SERIAL_PORT.lock().write_fmt(fmt).unwrap(); + + unsafe { CONSOLE.force_unlock() } + if let Some(console) = CONSOLE.lock().as_mut() { + console.write_fmt(fmt).unwrap(); + } }