-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
…ple app for ELF loader.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,4 @@ pipe | |
poll | ||
rtc | ||
signal | ||
virtio-9p | ||
virtio-9p |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,4 +16,4 @@ default = [] | |
|
||
[dependencies] | ||
cfg-if = "1.0" | ||
kernel_guard = { path = "../kernel_guard" } | ||
kernel_guard = "0.1.0" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,9 @@ | ||
[package] | ||
name = "line_discipline" | ||
name = "tty" | ||
version = "0.0.1" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
spin = { version = "0.9", default-features = false, features = [] } | ||
spinlock = { path = "../spinlock" } | ||
lazy_init = { path = "../lazy_init" } | ||
log = "0.4" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
//! functions for tty buffer. | ||
//! Drivers should fill the buffer by functions below. | ||
//! then the data will be passed to line discipline for processing. | ||
/// tty buffer size. | ||
const TTY_BUF_SIZE: usize = 4096; | ||
|
||
/// ring buffer. | ||
#[derive(Debug)] | ||
struct RingBuffer { | ||
/// data. | ||
buf: [u8; TTY_BUF_SIZE], | ||
|
||
/// the first element or empty slot if buffer is empty. | ||
head: usize, | ||
|
||
/// the first empty slot. | ||
tail: usize, | ||
|
||
/// number of elements. | ||
len: usize, | ||
} | ||
|
||
/// tty buffer. | ||
/// TODO: use flip buffer. | ||
#[derive(Debug)] | ||
pub struct TtyBuffer { | ||
/// use ring buffer to save chars. | ||
buffer: spinlock::SpinNoIrq<RingBuffer>, | ||
} | ||
|
||
impl TtyBuffer { | ||
pub fn new() -> Self { | ||
Self { | ||
buffer: spinlock::SpinNoIrq::new(RingBuffer { | ||
buf: [0u8; TTY_BUF_SIZE], | ||
head: 0, | ||
tail: 0, | ||
len: 0, | ||
}), | ||
} | ||
} | ||
|
||
/// get `index`th element without changing buffer. | ||
pub fn see(&self, index: usize) -> u8 { | ||
let buf = self.buffer.lock(); | ||
if index < buf.len { | ||
buf.buf[(index + buf.head) % TTY_BUF_SIZE] | ||
} else { | ||
0 | ||
} | ||
} | ||
|
||
/// push a char to tail. | ||
pub fn push(&self, ch: u8) { | ||
let mut buf = self.buffer.lock(); | ||
if buf.len != TTY_BUF_SIZE { | ||
buf.len += 1; | ||
let idx = buf.tail; | ||
buf.buf[idx] = ch; | ||
buf.tail = (buf.tail + 1) % TTY_BUF_SIZE; | ||
} | ||
} | ||
|
||
/// delete and return the heading char. | ||
pub fn pop(&self) -> u8 { | ||
self.delete(0) | ||
} | ||
|
||
/// insert `ch` to `index`th position. | ||
pub fn insert(&self, ch: u8, index: usize) { | ||
let mut buf = self.buffer.lock(); | ||
// if not full and index is right | ||
if buf.len != TTY_BUF_SIZE && index <= buf.len { | ||
// shift buffer[index..move_len+index] one slot right. | ||
let move_len = buf.len - index; | ||
let mut i = buf.tail; | ||
for _ in 0..move_len { | ||
i -= 1; | ||
buf.buf[(i + 1) % TTY_BUF_SIZE] = buf.buf[i % TTY_BUF_SIZE]; | ||
} | ||
// insert | ||
let idx = (buf.head + index) % TTY_BUF_SIZE; | ||
buf.buf[idx] = ch; | ||
buf.len += 1; | ||
buf.tail = (buf.tail + 1) % TTY_BUF_SIZE; | ||
} | ||
} | ||
|
||
/// delete and return the `index`th element. | ||
pub fn delete(&self, index: usize) -> u8 { | ||
let mut buf = self.buffer.lock(); | ||
// if not empty and index is right | ||
if buf.len != 0 && index < buf.len { | ||
let move_len = buf.len - index; | ||
let mut i = index + buf.head; | ||
|
||
// save retval | ||
let ret = buf.buf[i % TTY_BUF_SIZE]; | ||
|
||
// copy move_len elements from buffer[index+head] to buffer[index+head-1]; | ||
for _ in 0..move_len { | ||
buf.buf[i % TTY_BUF_SIZE] = buf.buf[(i + 1) % TTY_BUF_SIZE]; | ||
i += 1; | ||
} | ||
|
||
// len -= 1 | ||
buf.len -= 1; | ||
buf.tail -= 1; | ||
ret | ||
} else { | ||
0 | ||
} | ||
} | ||
|
||
/// get current length of buffer. | ||
pub fn len(&self) -> usize { | ||
self.buffer.lock().len | ||
} | ||
} | ||
|
||
/// a buffer for echo of line discipline. | ||
/// additionally saving the cursor position. | ||
#[derive(Debug)] | ||
pub struct EchoBuffer { | ||
/// chars buffer. | ||
pub buffer: TtyBuffer, | ||
|
||
/// current column of cursor. | ||
pub col: usize, | ||
} | ||
|
||
impl EchoBuffer { | ||
pub fn new() -> Self { | ||
Self { | ||
buffer: TtyBuffer::new(), | ||
col: 0, | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
pub const LF: u8 = b'\n'; | ||
pub const CR: u8 = b'\r'; | ||
|
||
pub const DEL: u8 = b'\x7f'; | ||
pub const BS: u8 = b'\x08'; | ||
|
||
pub const SPACE: u8 = b' '; | ||
|
||
/// escape | ||
pub const ESC: u8 = 27; | ||
/// [ | ||
pub const LEFT_BRACKET: u8 = 91; | ||
|
||
/// an arrow char is `ARROW_PREFIX` + `UP/DOWN/RIGHT/LEFT` | ||
pub const ARROW_PREFIX: [u8; 2] = [ESC, LEFT_BRACKET]; | ||
|
||
// const UP: u8 = 65; | ||
// const DOWN: u8 = 66; | ||
pub const RIGHT: u8 = 67; | ||
pub const LEFT: u8 = 68; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
//! the first thing a driver should do is registering itself by `register_driver()`, | ||
//! which will allocate an index for this driver. | ||
//! | ||
//! then, driver should register every device it has by `register_device()`, | ||
//! which will allocate an index for this device. | ||
use crate::tty::TtyStruct; | ||
use alloc::string::String; | ||
use alloc::sync::Arc; | ||
use alloc::{vec, vec::Vec}; | ||
use lazy_init::LazyInit; | ||
use spinlock::SpinNoIrq; | ||
|
||
/// all tty drivers. | ||
/// only be written when registering a driver. | ||
pub(super) static ALL_DRIVERS: LazyInit<SpinNoIrq<Vec<Arc<TtyDriver>>>> = LazyInit::new(); | ||
|
||
/// the operations a tty driver must implement. | ||
/// passed by driver when registering itself. | ||
#[derive(Debug)] | ||
pub struct TtyDriverOps { | ||
/// push a char to device. | ||
pub putchar: fn(u8), | ||
} | ||
|
||
/// tty driver. | ||
#[derive(Debug)] | ||
pub struct TtyDriver { | ||
/// driver operations. | ||
pub ops: TtyDriverOps, | ||
|
||
/// driver's devices. | ||
/// TODO: maybe use rwlock for dynamicly adding devices is better. | ||
ttys: SpinNoIrq<Vec<Arc<TtyStruct>>>, | ||
|
||
/// index of driver. | ||
index: usize, | ||
|
||
/// name of driver. | ||
name: String, | ||
} | ||
|
||
impl TtyDriver { | ||
pub fn new(ops: TtyDriverOps, name: &str) -> Self { | ||
Self { | ||
ops, | ||
ttys: SpinNoIrq::new(vec![]), | ||
index: 0, | ||
name: String::from(name), | ||
} | ||
} | ||
|
||
/// add a device, return its index, -1 means failure. | ||
fn add_one_device(&self, tty: Arc<TtyStruct>) -> isize { | ||
let index = self.ttys.lock().len(); | ||
|
||
// set index of device | ||
tty.set_index(index); | ||
|
||
// set name of device | ||
let mut name = self.name.clone(); | ||
name.push(core::char::from_digit(index as _, 16).unwrap()); | ||
tty.set_name(&name); | ||
|
||
// save this device | ||
self.ttys.lock().push(tty); | ||
|
||
// return device's index | ||
index as _ | ||
} | ||
|
||
pub fn name(&self) -> String { | ||
self.name.clone() | ||
} | ||
|
||
pub fn index(&self) -> usize { | ||
self.index | ||
} | ||
|
||
/// get all devices' name | ||
pub fn get_all_device_names(&self) -> Vec<String> { | ||
let mut ret = vec![]; | ||
for dev in self.ttys.lock().iter() { | ||
let name = dev.name(); | ||
ret.push(name); | ||
} | ||
ret | ||
} | ||
|
||
/// get device | ||
pub fn get_device_by_name(&self, name: &str) -> Option<Arc<TtyStruct>> { | ||
for tty in self.ttys.lock().iter() { | ||
if tty.name() == name { | ||
return Some(tty.clone()); | ||
} | ||
} | ||
None | ||
} | ||
|
||
/// get device | ||
pub fn get_device_by_index(&self, index: usize) -> Option<Arc<TtyStruct>> { | ||
let lock = self.ttys.lock(); | ||
if let Some(dev) = lock.get(index) { | ||
return Some(dev.clone()); | ||
} | ||
None | ||
} | ||
} | ||
|
||
pub fn init() { | ||
ALL_DRIVERS.init_by(SpinNoIrq::new(vec![])); | ||
} | ||
|
||
/// get driver by index. | ||
pub fn get_driver_by_index(index: usize) -> Option<Arc<TtyDriver>> { | ||
let lock = ALL_DRIVERS.lock(); | ||
for driver in lock.iter() { | ||
if driver.index == index { | ||
return Some(driver.clone()); | ||
} | ||
} | ||
None | ||
} | ||
|
||
/// called by driver to register itself. | ||
/// return driver's index. | ||
pub fn register_driver(ops: TtyDriverOps, name: &str) -> usize { | ||
// create a tty driver structure | ||
let mut driver = TtyDriver::new(ops, name); | ||
|
||
// lock | ||
let mut lock = ALL_DRIVERS.lock(); | ||
|
||
// grant an index to the driver | ||
let index = lock.len(); | ||
driver.index = index; | ||
|
||
// push | ||
lock.push(Arc::new(driver)); | ||
|
||
// return index | ||
index | ||
} | ||
|
||
/// called by driver to register device. | ||
/// return device's index, or -1 on failure. | ||
pub fn register_device(driver_index: usize) -> isize { | ||
let mut index = -1; | ||
// if driver is found | ||
if let Some(driver) = get_driver_by_index(driver_index) { | ||
// create a tty structure | ||
let tty = Arc::new(TtyStruct::new(driver.clone())); | ||
|
||
// save this structure | ||
index = driver.add_one_device(tty.clone()); | ||
crate::tty::add_one_device(tty.clone()); | ||
} | ||
index | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
//! TTY line discipline process all incoming and outgoing chars from/to a tty device. | ||
//! the currently implemented line discipline is N_TTY. | ||
//! line disciplines are registered when a device is registered. | ||
use alloc::sync::Arc; | ||
use spinlock::SpinNoIrq; | ||
|
||
use crate::{ | ||
buffer::{EchoBuffer, TtyBuffer}, | ||
tty::TtyStruct, | ||
}; | ||
|
||
/// tty line discipline. | ||
#[derive(Debug)] | ||
pub struct TtyLdisc { | ||
/// chars that can be read by kernel. | ||
read_buf: TtyBuffer, | ||
|
||
/// chars being echoed on the screen. | ||
echo_buf: SpinNoIrq<EchoBuffer>, | ||
|
||
/// chars from driver, and not yet been processed. | ||
rec_buf: TtyBuffer, | ||
} | ||
|
||
/// implement N_TTY. | ||
impl TtyLdisc { | ||
pub fn new() -> Self { | ||
Self { | ||
read_buf: TtyBuffer::new(), | ||
echo_buf: SpinNoIrq::new(EchoBuffer::new()), | ||
rec_buf: TtyBuffer::new(), | ||
} | ||
} | ||
|
||
/// kernel reads data. | ||
pub fn read(&self, buf: &mut [u8]) -> usize { | ||
let read_buf = &self.read_buf; | ||
|
||
// len of this reading | ||
let len = buf.len().min(read_buf.len()); | ||
|
||
// return if nothing can be read | ||
if len == 0 { | ||
return 0; | ||
} | ||
|
||
// copy data from read_buf to `buf` | ||
for ch in buf.iter_mut().take(len) { | ||
*ch = read_buf.pop(); | ||
} | ||
|
||
len | ||
} | ||
|
||
/// driver sends data from device for processing and echoing. | ||
/// running in irq. | ||
pub fn receive_buf(&self, tty: Arc<TtyStruct>, buf: &[u8]) { | ||
use crate::constant::*; | ||
|
||
let rec_buf = &self.rec_buf; | ||
|
||
// save data to receive buffer | ||
for ch in buf { | ||
rec_buf.push(*ch); | ||
} | ||
|
||
// process chars in receive buffer | ||
while rec_buf.len() > 0 { | ||
let ch = rec_buf.see(0); | ||
|
||
// if char may be arrow char | ||
if ch == ARROW_PREFIX[0] { | ||
// no enough len, just break, waitting for next time | ||
if rec_buf.len() < 3 { | ||
break; | ||
} | ||
|
||
// enough len, but not a arrow char, just ignore | ||
if rec_buf.see(1) != ARROW_PREFIX[1] { | ||
rec_buf.pop(); | ||
rec_buf.pop(); | ||
break; | ||
} | ||
|
||
// it is an arrow char, get it | ||
rec_buf.pop(); | ||
rec_buf.pop(); | ||
let ch = rec_buf.pop(); | ||
|
||
// deal with arrow char | ||
match ch { | ||
LEFT => { | ||
let mut lock = self.echo_buf.lock(); | ||
// if can go left | ||
if lock.col > 0 { | ||
self.write(tty.clone(), &[ARROW_PREFIX[0], ARROW_PREFIX[1], ch]); | ||
lock.col -= 1; | ||
} | ||
} | ||
RIGHT => { | ||
let mut lock = self.echo_buf.lock(); | ||
// if can go right | ||
if lock.col < lock.buffer.len() { | ||
self.write(tty.clone(), &[ARROW_PREFIX[0], ARROW_PREFIX[1], ch]); | ||
lock.col += 1; | ||
} | ||
} | ||
_ => { | ||
// it is UP/DOWN, just ignore | ||
} | ||
} | ||
// not a arrow char, handle it as a normal char | ||
} else { | ||
let ch = rec_buf.pop(); | ||
match ch { | ||
CR | LF => { | ||
// always '\n' | ||
let ch = LF; | ||
|
||
// echo | ||
self.write(tty.clone(), &[ch]); | ||
|
||
// push this char to echo buffer | ||
let mut lock = self.echo_buf.lock(); | ||
lock.buffer.push(ch); | ||
|
||
// copy echo buffer to read buffer | ||
// FIXME: currently will push all data to read_buf | ||
let len = lock.buffer.len(); | ||
for _ in 0..len { | ||
self.read_buf.push(lock.buffer.pop()); | ||
} | ||
|
||
// echo buffer's column is set to 0 | ||
lock.col = 0; | ||
} | ||
BS | DEL => { | ||
let mut lock = self.echo_buf.lock(); | ||
let col = lock.col; | ||
let len = lock.buffer.len(); | ||
// if can delete | ||
if col > 0 { | ||
// perform a backspace | ||
self.write(tty.clone(), &[BS, SPACE, BS]); | ||
|
||
// if cursor is not on the rightmost | ||
if col != len { | ||
for i in col..len { | ||
let ch = lock.buffer.see(i); | ||
self.write(tty.clone(), &[ch]); | ||
} | ||
self.write(tty.clone(), &[SPACE]); | ||
for _ in 0..(len - col + 1) { | ||
self.write( | ||
tty.clone(), | ||
&[ARROW_PREFIX[0], ARROW_PREFIX[1], LEFT], | ||
); | ||
} | ||
} | ||
|
||
// modify echo buffer | ||
lock.buffer.delete(col - 1); | ||
lock.col -= 1; | ||
} | ||
} | ||
_ => { | ||
// process normal chars. | ||
let mut echo_buf = self.echo_buf.lock(); | ||
let col = echo_buf.col; | ||
let len = echo_buf.buffer.len(); | ||
|
||
// echo | ||
self.write(tty.clone(), &[ch]); | ||
|
||
// if cursor is not on the rightmost | ||
if col != len { | ||
for i in col..len { | ||
self.write(tty.clone(), &[echo_buf.buffer.see(i)]); | ||
} | ||
for _ in 0..(len - col) { | ||
self.write(tty.clone(), &[ARROW_PREFIX[0], ARROW_PREFIX[1], LEFT]); | ||
} | ||
} | ||
|
||
// modify echo buffer | ||
echo_buf.buffer.insert(ch, col); | ||
echo_buf.col += 1; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// kernel writes data to device. | ||
pub fn write(&self, tty: Arc<TtyStruct>, buf: &[u8]) -> usize { | ||
let mut len = 0; | ||
let driver = tty.driver(); | ||
for ch in buf { | ||
len += 1; | ||
// call driver's method | ||
(driver.ops.putchar)(*ch); | ||
} | ||
len | ||
} | ||
} | ||
|
||
impl Default for TtyLdisc { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
//! Init | ||
//! | ||
//! firstly, a driver registers itself to get its index. | ||
//! next, the driver registers all devices it found to get their indices. | ||
//! | ||
//! Read | ||
//! | ||
//! when a device receives data, it will cause a irq. | ||
//! then the driver sends the data to tty layer using their indices. | ||
//! finally, kernel will get the data using the device's name. | ||
//! | ||
//! Write | ||
//! | ||
//! kernel writes data to a device using its name. | ||
#![no_std] | ||
|
||
extern crate alloc; | ||
|
||
mod buffer; | ||
mod constant; | ||
mod driver; | ||
mod ldisc; | ||
mod tty; | ||
|
||
use driver::get_driver_by_index; | ||
|
||
pub use driver::{register_device, register_driver, TtyDriverOps}; | ||
pub use tty::{get_all_device_names, get_device_by_name}; | ||
|
||
/// called by driver when irq, to send data from hardware. | ||
pub fn tty_receive_buf(driver_index: usize, device_index: usize, buf: &[u8]) { | ||
// check the validation of index | ||
if let Some(driver) = get_driver_by_index(driver_index) { | ||
if let Some(tty) = driver.get_device_by_index(device_index) { | ||
tty.ldisc().receive_buf(tty.clone(), buf); | ||
} | ||
} | ||
} | ||
|
||
/// called by kernel to read a tty device. | ||
pub fn tty_read(buf: &mut [u8], dev_name: &str) -> usize { | ||
if let Some(tty) = get_device_by_name(dev_name) { | ||
tty.ldisc().read(buf) | ||
} else { | ||
0 | ||
} | ||
} | ||
|
||
/// called by kernel to write a tty device. | ||
pub fn tty_write(buf: &[u8], dev_name: &str) -> usize { | ||
if let Some(tty) = get_device_by_name(dev_name) { | ||
tty.ldisc().write(tty.clone(), buf) | ||
} else { | ||
0 | ||
} | ||
} | ||
|
||
/// init | ||
pub fn init() { | ||
driver::init(); | ||
tty::init(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
use core::sync::atomic::AtomicUsize; | ||
|
||
use alloc::{string::String, sync::Arc, vec, vec::Vec}; | ||
use lazy_init::LazyInit; | ||
use spinlock::SpinNoIrq; | ||
|
||
use crate::{driver::TtyDriver, ldisc::TtyLdisc}; | ||
|
||
/// all registered devices. | ||
pub(super) static ALL_DEVICES: LazyInit<SpinNoIrq<Vec<Arc<TtyStruct>>>> = LazyInit::new(); | ||
|
||
/// tty device. | ||
#[derive(Debug)] | ||
pub struct TtyStruct { | ||
/// driver of device. | ||
driver: Arc<TtyDriver>, | ||
|
||
/// device's line discipline. | ||
ldisc: Arc<TtyLdisc>, | ||
|
||
/// index of device. | ||
index: AtomicUsize, | ||
|
||
/// name of device. | ||
name: SpinNoIrq<String>, | ||
} | ||
|
||
impl TtyStruct { | ||
pub fn new(driver: Arc<TtyDriver>) -> Self { | ||
Self { | ||
driver: driver.clone(), | ||
ldisc: Arc::new(TtyLdisc::new()), | ||
index: AtomicUsize::new(0), | ||
name: SpinNoIrq::new(String::new()), | ||
} | ||
} | ||
|
||
/// get tty line discipline. | ||
pub fn ldisc(&self) -> Arc<TtyLdisc> { | ||
self.ldisc.clone() | ||
} | ||
|
||
/// set device index. | ||
pub fn set_index(&self, index: usize) { | ||
self.index | ||
.store(index, core::sync::atomic::Ordering::Relaxed); | ||
} | ||
|
||
/// set name of device | ||
pub fn set_name(&self, name: &str) { | ||
let mut lock = self.name.lock(); | ||
lock.clone_from(&String::from(name)); | ||
} | ||
|
||
/// Convert a tty structure into a name, reflecting the kernel naming policy. | ||
pub fn name(&self) -> String { | ||
self.name.lock().clone() | ||
} | ||
|
||
/// get device's driver. | ||
pub fn driver(&self) -> Arc<TtyDriver> { | ||
self.driver.clone() | ||
} | ||
} | ||
|
||
/// called by kernel to get a device. | ||
pub fn get_device_by_name(name: &str) -> Option<Arc<TtyStruct>> { | ||
let lock = ALL_DEVICES.lock(); | ||
for tty in lock.iter() { | ||
if tty.name() == name { | ||
return Some(tty.clone()); | ||
} | ||
} | ||
None | ||
} | ||
|
||
/// called by kernel to get all devices' names. | ||
/// usually used in init to get the view of tty. | ||
pub fn get_all_device_names() -> Vec<String> { | ||
let mut ret = vec![]; | ||
let alldev = ALL_DEVICES.lock(); | ||
for dev in alldev.iter() { | ||
ret.push(dev.name()); | ||
} | ||
ret | ||
} | ||
|
||
/// save a device when registered. | ||
pub fn add_one_device(tty: Arc<TtyStruct>) { | ||
ALL_DEVICES.lock().push(tty); | ||
} | ||
|
||
pub fn init() { | ||
ALL_DEVICES.init_by(SpinNoIrq::new(vec![])); | ||
} |