Skip to content

Commit

Permalink
Merge pull request #198 from Orycterope/mmio
Browse files Browse the repository at this point in the history
libutils: Mmio + refactoring
  • Loading branch information
Orycterope authored Mar 9, 2019
2 parents 71a4eb3 + c0c377d commit 32729a6
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 187 deletions.
187 changes: 0 additions & 187 deletions libutils/src/io.rs

This file was deleted.

93 changes: 93 additions & 0 deletions libutils/src/io/mmio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//! Wrapper around a mmio value
//!
//! Defines a pointer that should always be accessed by volatile reads/writes.
//!
//! Stolen from [Redox OS](https://gitlab.redox-os.org/redox-os/syscall/blob/master/src/io/mmio.rs).

use core::ptr::{read_volatile, write_volatile};
use core::mem::uninitialized;
use core::fmt::{Debug, Formatter, Error};

use super::Io;

/// A value that can only be accessed volatilely.
///
/// Generally used behind a pointer, as such:
///
/// ```
/// /// Layout of Mmio registers of a random device.
/// ///
/// /// This struct is repr packed so its field are not re-ordered,
/// /// and no undesired padding is added.
/// ///
/// /// Be careful though, in rust reading an unaligned field is undefined behaviour,
/// /// so you must make sure it is correctly aligned.
/// #[repr(packed)]
/// struct DeviceFooRegisters {
/// register_control: Mmio<u16>,
/// register_command: Mmio<u16>,
/// }
///
/// let device_address = 0xabcdef00 as *mut DeviceFooRegisters;
///
/// let device: &mut DeviceFooRegisters = unsafe {
/// // safety: make sure that mmio_address is valid and we're not violating
/// // rust's aliasing rules.
/// mmio_address.as_mut().unwrap()
/// };
///
/// let status = device.register_control.read();
/// device.register_command.write(0xFOOD);
/// ```
// todo: Mmio<T> UnsafeCell
// body: Figure out if Mmio<T> should implement UnsafeCell.
// body: Does this mean that, just like atomic, write can take self by const reference only ?
// body: But is a Mmio<T> actually atomic ?
// body:
// body: Forward all these questions to @roblabla.
// body:
// body: Note:
// body:
// body: see [volatile cell](https://docs.rs/vcell/0.1.0/src/vcell/lib.rs.html#18-20)
// body: and [this discussion](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/33)
#[repr(packed)]
pub struct Mmio<T> {
/// The value. Can only be accessed through .read()
value: T,
}

impl<T> Mmio<T> {
/// Create a new Mmio without initializing.
///
/// Mostly unused, you would almost always get a Mmio
/// by casting a raw pointer to a &mut Mmio.
#[allow(clippy::new_without_default)] // because of Redox.
pub fn new() -> Self {
Mmio {
value: unsafe { uninitialized() }
}
}
}

impl<T> Io for Mmio<T> where T: Copy {
type Value = T;

/// Performs a volatile read of the value.
fn read(&self) -> T {
unsafe { read_volatile(&self.value) }
}

/// Performs a volatile write of the value.
fn write(&mut self, value: T) {
unsafe { write_volatile(&mut self.value, value) };
}
}

impl<T> Debug for Mmio<T> where T: Copy + Debug {
/// Debug volatilely reads `value`.
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
fmt.debug_struct("Mmio")
.field("value", &self.read())
.finish()
}
}
116 changes: 116 additions & 0 deletions libutils/src/io/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//! The IO interface
//!
//! Stolen from [Redox Io](https://gitlab.redox-os.org/redox-os/syscall/blob/master/src/io/io.rs)

mod pio;
mod mmio;
pub use self::pio::Pio;
pub use self::mmio::Mmio;

use core::cmp::PartialEq;
use core::ops::{BitAnd, BitOr, Not};
use core::fmt::{Debug, Formatter, Error};

/// The Io trait allows for accessing device IO in a generic way, abstracting
/// over different IO accesses (Port IO and Memory Mapped IO).
pub trait Io {
/// The width of the IO access.
/// Should be a primitive type like u8, u16, u32...
type Value: Copy;

/// Reads from this Io.
fn read(&self) -> Self::Value;

/// Writes `value` to this Io.
fn write(&mut self, value: Self::Value);

/// Read from this Io, and mask the value with `flags`.
#[inline(always)]
fn readf(&self, flags: Self::Value) -> bool
where
Self::Value: PartialEq + BitAnd<Output = Self::Value>
{
(self.read() & flags) as Self::Value == flags
}

/// Mask `value` with `flags`, and write it to this device address. Note that
/// this causes a read!
#[inline(always)]
fn writef(&mut self, flags: Self::Value, value: bool)
where
Self::Value: PartialEq + BitAnd<Output = Self::Value> + BitOr<Output = Self::Value> + Not<Output = Self::Value>
{
let tmp: Self::Value = if value {
self.read() | flags
} else {
self.read() & !flags
};
self.write(tmp);
}
}

/// A read-only wrapper around an IO device.
#[derive(Debug)]
#[allow(clippy::missing_docs_in_private_items)]
pub struct ReadOnly<I> {
inner: I
}

impl<I> ReadOnly<I> {
/// Create a read-only wrapper around the IO device address.
pub const fn new(inner: I) -> ReadOnly<I> {
ReadOnly {
inner: inner
}
}
}

impl<I: Io> ReadOnly<I> {
/// Reads from this Io.
#[inline(always)]
pub fn read(&self) -> I::Value {
self.inner.read()
}

/// Read from this Io, and mask the value with `flags`.
#[inline(always)]
pub fn readf(&self, flags: I::Value) -> bool
where
<I as Io>::Value: PartialEq + BitAnd<Output = <I as Io>::Value>
{
self.inner.readf(flags)
}
}

/// An Io that we can only write to.
#[allow(clippy::missing_docs_in_private_items)]
pub struct WriteOnly<I> {
inner: I
}

impl<I> WriteOnly<I> {
/// Creates a WriteOnly Io.
pub const fn new(inner: I) -> WriteOnly<I> {
WriteOnly {
inner: inner
}
}
}

impl<I: Io> WriteOnly<I> {
/// Writes `value` to this Io.
#[inline(always)]
pub fn write(&mut self, value: I::Value) {
self.inner.write(value)
}

// writef() not exposed as it requires a read.
}

impl<I> Debug for WriteOnly<I> {
/// Debug does not access the **write only** value.
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.debug_struct("WriteOnly")
.finish()
}
}
Loading

0 comments on commit 32729a6

Please sign in to comment.