Created
July 18, 2020 09:34
-
-
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?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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