From 706f7e4e9ade8f749198a4e5d1bbfdf96ded48b1 Mon Sep 17 00:00:00 2001 From: Harry Chen Date: Sun, 7 Apr 2019 18:52:10 +0800 Subject: [PATCH] Fix UART controller for malta, now input works! Signed-off-by: Harry Chen --- kernel/src/arch/mipsel/board/malta/mod.rs | 2 +- kernel/src/arch/mipsel/interrupt.rs | 6 +- kernel/src/drivers/serial/ti_16c550c.rs | 110 ++++++++++++++++++++++ 3 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 kernel/src/drivers/serial/ti_16c550c.rs diff --git a/kernel/src/arch/mipsel/board/malta/mod.rs b/kernel/src/arch/mipsel/board/malta/mod.rs index 67d5a8f..1e8251d 100644 --- a/kernel/src/arch/mipsel/board/malta/mod.rs +++ b/kernel/src/arch/mipsel/board/malta/mod.rs @@ -2,7 +2,7 @@ use once::*; use alloc::string::String; use mips::registers::cp0; -#[path = "../../../../drivers/serial/16550_reg.rs"] +#[path = "../../../../drivers/serial/ti_16c550c.rs"] pub mod serial; #[path = "../../../../drivers/gpu/fb.rs"] pub mod fb; diff --git a/kernel/src/arch/mipsel/interrupt.rs b/kernel/src/arch/mipsel/interrupt.rs index 19c0aad..36176b9 100644 --- a/kernel/src/arch/mipsel/interrupt.rs +++ b/kernel/src/arch/mipsel/interrupt.rs @@ -132,13 +132,15 @@ fn syscall(tf: &mut TrapFrame) { let arguments = [tf.a0, tf.a1, tf.a2, tf.a3, tf.t0, tf.t1]; trace!("MIPS syscall {} invoked with {:?}", tf.v0, arguments); - let ret = crate::syscall::syscall(tf.v0, arguments, tf); - let ret = tf.v0 as isize; + let ret = crate::syscall::syscall(tf.v0, arguments, tf) as isize; // comply with mips n32 abi, always return a positive value // https://git.musl-libc.org/cgit/musl/tree/arch/mipsn32/syscall_arch.h if (ret < 0) { tf.v0 = (-ret) as usize; tf.a3 = 1; + } else { + tf.v0 = ret as usize; + tf.a3 = 0; } } diff --git a/kernel/src/drivers/serial/ti_16c550c.rs b/kernel/src/drivers/serial/ti_16c550c.rs new file mode 100644 index 0000000..0ee544e --- /dev/null +++ b/kernel/src/drivers/serial/ti_16c550c.rs @@ -0,0 +1,110 @@ +//! TI 16c550c serial adapter driver for malta board + +#![allow(dead_code)] + +use core::fmt::{Write, Result, Arguments}; +use core::ptr::{read_volatile, write_volatile}; +use spin::Mutex; + +pub struct SerialPort { + base: usize +} + +impl SerialPort { + fn new() -> SerialPort { + SerialPort { + base: 0 + } + } + + pub fn init(&mut self, base: usize) { + self.base = base; + // Turn off the FIFO + // write(self.base + COM_FCR, 0 as u8); + // Set speed; requires DLAB latch + // write(self.base + COM_LCR, COM_LCR_DLAB); + // write(self.base + COM_DLL, (115200 / 9600) as u8); + // write(self.base + COM_DLM, 0 as u8); + + // 8 data bits, 1 stop bit, parity off; turn off DLAB latch + // write(self.base + COM_LCR, COM_LCR_WLEN8 & !COM_LCR_DLAB); + + // No modem controls + // write(self.base + COM_MCR, 0 as u8); + // Enable rcv interrupts + write(self.base + COM_INT_EN, 0x1); + } + + /// non-blocking version of putchar() + pub fn putchar(&mut self, c: u8) { + write(self.base + COM_TX, c); + } + + /// blocking version of getchar() + pub fn getchar(&mut self) -> char { + loop { + if (read::(self.base + COM_LSR) & 0x01) == 0 { + break; + } + } + let c = read::(self.base + COM_RX); + match c { + 255 => '\0', // null + c => c as char, + } + } + + /// non-blocking version of getchar() + pub fn getchar_option(&mut self) -> Option { + match read::(self.base + COM_LSR) & 0x01 { + 0 => None, + _ => Some(read::(self.base + COM_RX) as u8 as char), + } + } + + pub fn putfmt(&mut self, fmt: Arguments) { + self.write_fmt(fmt).unwrap(); + } + +} + +impl Write for SerialPort { + fn write_str(&mut self, s: &str) -> Result { + for c in s.bytes() { + if c == 127 { + self.putchar(8); + self.putchar(b' '); + self.putchar(8); + } else { + self.putchar(c); + } + } + Ok(()) + } +} + +fn write(addr: usize, content: T) { + let cell = (addr) as *mut T; + unsafe { write_volatile(cell, content); } +} + +fn read(addr: usize) -> T { + let cell = (addr) as *const T; + unsafe { read_volatile(cell) } +} + + +const COM_RX :usize = 0x00; // In: Receive buffer (DLAB=0) +const COM_TX :usize = 0x00; // Out: Transmit buffer (DLAB=0) +const COM_INT_EN :usize = 0x08; // In: Interrupt enable +const COM_INT_ID :usize = 0x10; // Out: Interrupt identification +const COM_LSR :usize = 0x28; // In: Line status register + + +lazy_static! { + pub static ref SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); +} + +pub fn init(base: usize) { + SERIAL_PORT.lock().init(base); +}