Skip to content

Instantly share code, notes, and snippets.

@diondokter
Created July 18, 2020 09:34
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diondokter/66cd38125178be78c40e8e08ee722d91 to your computer and use it in GitHub Desktop.
Save diondokter/66cd38125178be78c40e8e08ee722d91 to your computer and use it in GitHub Desktop.
Final rust code for Oxidize talk of Dion Dokter: How can we write the best device driver?
use std::marker::PhantomData;
// -------------------- HARDWARE INTERFACE --------------------
pub trait Interface {
fn enable_cs(&mut self);
fn disable_cs(&mut self);
fn transfer(&mut self, value: u8) -> u8;
}
// -------------------- LOW LEVEL DEVICE INTERFACE --------------------
pub struct Device<I: Interface> {
interface: I,
}
impl<I: Interface> Device<I> {
pub fn new(interface: I) -> Self {
Self { interface }
}
pub fn id<'a>(&'a mut self) -> RegAccessor<'a, I, id::R, ()> {
RegAccessor::new(&mut self.interface)
}
pub fn port<'a>(&'a mut self) -> RegAccessor<'a, I, port::R, port::W> {
RegAccessor::new(&mut self.interface)
}
}
// -------------------- REGISTER WRAPPER --------------------
pub struct RegAccessor<'a, I: Interface, R, W> {
interface: &'a mut I,
phantom: PhantomData<(R, W)>,
}
impl<'a, I: Interface, R, W> RegAccessor<'a, I, R, W> {
pub fn new(interface: &'a mut I) -> Self {
Self {
interface,
phantom: Default::default(),
}
}
}
// -------------------- REGISTER DEFINITIONS --------------------
pub mod id {
use super::{RegAccessor, Interface};
#[derive(Copy, Clone)]
pub struct R(u8);
impl R {
pub fn manufacturer(&self) -> u8 {
self.0 & 0xF0
}
pub fn version(&self) -> u8 {
self.0 & 0x0F
}
}
impl<'a, I: Interface> RegAccessor<'a, I, R, ()> {
pub fn read(&mut self) -> R {
self.interface.enable_cs();
self.interface.transfer(1);
let value = self.interface.transfer(0);
self.interface.disable_cs();
R(value)
}
}
}
pub mod port {
use super::{RegAccessor, Interface};
#[derive(Copy, Clone)]
pub struct W(u8);
impl W {
pub fn enable_0(mut self, value: bool) -> Self {
self.0 &= !(1 << 0);
self.0 |= (value as u8) << 0;
self
}
pub fn enable_1(mut self, value: bool) -> Self {
self.0 &= !(1 << 1);
self.0 |= (value as u8) << 1;
self
}
pub fn enable_2(mut self, value: bool) -> Self {
self.0 &= !(1 << 2);
self.0 |= (value as u8) << 2;
self
}
pub fn enable_3(mut self, value: bool) -> Self {
self.0 &= !(1 << 3);
self.0 |= (value as u8) << 3;
self
}
pub fn enable_4(mut self, value: bool) -> Self {
self.0 &= !(1 << 4);
self.0 |= (value as u8) << 4;
self
}
pub fn enable_5(mut self, value: bool) -> Self {
self.0 &= !(1 << 5);
self.0 |= (value as u8) << 5;
self
}
pub fn enable_6(mut self, value: bool) -> Self {
self.0 &= !(1 << 6);
self.0 |= (value as u8) << 6;
self
}
pub fn enable_7(mut self, value: bool) -> Self {
self.0 &= !(1 << 7);
self.0 |= (value as u8) << 7;
self
}
}
#[derive(Copy, Clone)]
pub struct R(u8);
impl R {
pub fn enable_0(&self) -> bool {
(self.0 & (1 << 0)) > 0
}
pub fn enable_1(&self) -> bool {
(self.0 & (1 << 1)) > 0
}
pub fn enable_2(&self) -> bool {
(self.0 & (1 << 2)) > 0
}
pub fn enable_3(&self) -> bool {
(self.0 & (1 << 3)) > 0
}
pub fn enable_4(&self) -> bool {
(self.0 & (1 << 4)) > 0
}
pub fn enable_5(&self) -> bool {
(self.0 & (1 << 5)) > 0
}
pub fn enable_6(&self) -> bool {
(self.0 & (1 << 6)) > 0
}
pub fn enable_7(&self) -> bool {
(self.0 & (1 << 7)) > 0
}
}
impl<'a, I: Interface> RegAccessor<'a, I, R, W> {
pub fn read(&mut self) -> R {
self.interface.enable_cs();
self.interface.transfer(6);
let value = self.interface.transfer(0);
self.interface.disable_cs();
R(value)
}
pub fn write<F: FnOnce(W) -> W>(&mut self, f: F) {
let value = f(W(0)).0;
self.interface.enable_cs();
self.interface.transfer(6);
self.interface.transfer(value);
self.interface.disable_cs();
}
pub fn modify<F: FnOnce(R, W) -> W>(&mut self, f: F) {
let read_value = self.read();
self.write(|_| f(read_value, W(read_value.0)));
}
}
}
// -------------------- EXAMPLE CODE --------------------
pub fn example() {
let mut device = Device::new(Spi);
let manufacturer = device.id().read().manufacturer();
if manufacturer != 0 {
device.port().modify(|_, w| w.enable_7(true));
}
}
// -------------------- HARDWARE INTERFACE IMPLEMENTATION --------------------
pub struct Spi;
impl Interface for Spi {
fn enable_cs(&mut self) {
unsafe { core::ptr::write_volatile(0x2000000 as *mut u8, 1); }
}
fn disable_cs(&mut self) {
unsafe { core::ptr::write_volatile(0x2000000 as *mut u8, 0); }
}
fn transfer(&mut self, value: u8) -> u8 {
unsafe { core::ptr::write_volatile(0x2000001 as *mut u8, value); }
unsafe { core::ptr::read_volatile(0x2000001 as *mut u8) }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment