Implement basic queue type for convenience

This commit is contained in:
Garen Tyler 2023-10-30 18:03:19 -06:00
parent 6a41a4e8b8
commit b3be1f8d47
Signed by: garentyler
GPG Key ID: D7A048C454CB7054
4 changed files with 125 additions and 25 deletions

View File

@ -1,7 +1,10 @@
//! Low-level driver routines for 16550a UART.
#![allow(non_upper_case_globals)]
use crate::{console::consoleintr, proc::wakeup, sync::spinlock::Spinlock, trap::InterruptBlocker};
use crate::{
console::consoleintr, proc::wakeup, queue::Queue, sync::spinlock::Spinlock,
trap::InterruptBlocker,
};
use core::ptr::addr_of;
// The UART control registers.
@ -22,8 +25,6 @@ const LSR_RX_READY: u8 = 1 << 0;
/// THR can accept another character to send
const LSR_TX_IDLE: u8 = 1 << 5;
const UART_TX_BUF_SIZE: usize = 32;
pub static UART0: Uart = Uart::new(crate::riscv::memlayout::UART0);
enum Register {
@ -61,18 +62,14 @@ impl Register {
pub struct Uart {
pub lock: Spinlock,
pub base_address: usize,
pub buffer: [u8; UART_TX_BUF_SIZE],
pub queue_start: usize,
pub queue_end: usize,
pub buffer: Queue<u8>,
}
impl Uart {
pub const fn new(base_address: usize) -> Uart {
Uart {
lock: Spinlock::new(),
base_address,
buffer: [0u8; UART_TX_BUF_SIZE],
queue_start: 0,
queue_end: 0,
buffer: Queue::new(),
}
}
/// Initialize the UART.
@ -146,7 +143,8 @@ impl Uart {
}
}
while self.queue_start == self.queue_end + 1 {
// Sleep until there is space in the buffer.
while self.buffer.space_remaining() == 0 {
unsafe {
guard.sleep(addr_of!(*self).cast_mut().cast());
}
@ -157,10 +155,7 @@ impl Uart {
let this: &mut Uart = unsafe { &mut *addr_of!(*self).cast_mut() };
// Add the byte onto the end of the queue.
this.buffer[this.queue_end] = b;
this.queue_end += 1;
this.queue_end %= this.buffer.len();
this.buffer.push_back(b).expect("space in the uart queue");
this.send_buffered_bytes();
}
pub fn write_slice_buffered(&self, bytes: &[u8]) {
@ -175,14 +170,6 @@ impl Uart {
let this: &mut Uart = unsafe { &mut *addr_of!(*self).cast_mut() };
loop {
// Ensure the indices are correct.
this.queue_start %= this.buffer.len();
this.queue_end %= this.buffer.len();
if this.queue_start == this.queue_end {
// The buffer is empty, we're finished sending bytes.
return;
}
if Register::LineStatus.read(this.base_address) & LSR_TX_IDLE == 0 {
// The UART transmit holding register is full,
// so we cannot give it another byte.
@ -191,9 +178,10 @@ impl Uart {
}
// Pop a byte from the front of the queue.
let b = this.buffer[this.queue_start];
this.queue_start += 1;
this.queue_start %= this.buffer.len();
let Some(b) = this.buffer.pop_front() else {
// The buffer is empty, we're finished sending bytes.
return;
};
// Maybe uartputc() is waiting for space in the buffer.
unsafe {

View File

@ -36,6 +36,11 @@ impl Pipe {
}
}
}
impl Default for Pipe {
fn default() -> Pipe {
Pipe::new()
}
}
extern "C" {
// pub fn pipealloc(a: *mut *mut File, b: *mut *mut File) -> i32;
@ -122,6 +127,7 @@ pub unsafe extern "C" fn pipewrite(pipe: *mut Pipe, addr: u64, n: i32) -> i32 {
}
#[no_mangle]
#[allow(clippy::while_immutable_condition)]
pub unsafe extern "C" fn piperead(pipe: *mut Pipe, addr: u64, n: i32) -> i32 {
let mut i = 0;
let p = myproc();

View File

@ -13,6 +13,7 @@ pub mod fs;
pub mod io;
pub mod mem;
pub mod proc;
pub mod queue;
pub(crate) mod riscv;
pub mod start;
pub mod string;

View File

@ -0,0 +1,105 @@
use core::iter::*;
pub const QUEUE_SIZE: usize = 64;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum QueueError {
NoSpace,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Queue<T> {
inner: [Option<T>; QUEUE_SIZE],
/// The index of the first item in the queue.
queue_start: usize,
/// The length of the queue.
queue_len: usize,
}
impl<T: Copy> Queue<T> {
pub const fn new() -> Queue<T> {
Queue {
inner: [None; QUEUE_SIZE],
queue_start: 0,
queue_len: 0,
}
}
}
impl<T> Queue<T> {
/// Accessor method for the length of the queue.
pub fn len(&self) -> usize {
self.queue_len
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns how many items can currently be added to the queue.
pub fn space_remaining(&self) -> usize {
self.inner.len() - self.len()
}
/// Returns the index of the last item in the queue.
fn queue_end(&self) -> usize {
(self.queue_start + self.queue_len - 1) % self.inner.len()
}
/// Removes an item from the front of the queue.
pub fn pop_front(&mut self) -> Option<T> {
let item = self.inner[self.queue_start].take();
if item.is_some() {
self.queue_start += 1;
self.queue_start %= self.inner.len();
self.queue_len -= 1;
}
item
}
/// Adds an item to the front of the queue.
pub fn push_front(&mut self, value: T) -> Result<(), QueueError> {
if self.space_remaining() == 0 {
return Err(QueueError::NoSpace);
}
if self.queue_start == 0 {
self.queue_start = self.inner.len() - 1;
} else {
self.queue_start -= 1;
}
self.inner[self.queue_start] = Some(value);
self.queue_len += 1;
Ok(())
}
/// Removes an item from the end of the queue.
pub fn pop_back(&mut self) -> Option<T> {
let item = self.inner[self.queue_start].take();
if item.is_some() {
self.queue_len -= 1;
}
item
}
/// Adds an item to the end of the queue.
pub fn push_back(&mut self, value: T) -> Result<(), QueueError> {
if self.space_remaining() == 0 {
return Err(QueueError::NoSpace);
}
self.queue_len += 1;
self.inner[self.queue_end()] = Some(value);
Ok(())
}
}
impl<T> Iterator for Queue<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.pop_front()
}
}
impl<T> DoubleEndedIterator for Queue<T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.pop_back()
}
}
impl<T> ExactSizeIterator for Queue<T> {
fn len(&self) -> usize {
self.len()
}
}