I'm still doing my first steps and for now it is like
Module platform::peripheral
. It contains sub-modues with only basic functions to access and modify peripheral mapped registers.
// platform::peripheral::gpio
pub mod function {
pub struct Unknown;
pub struct Input;
pub struct Output;
pub struct Alt0;
pub struct Alt1;
pub struct Alt2;
pub struct Alt3;
pub struct Alt4;
pub struct Alt5;
#[repr(u8)]
pub enum Fsel {
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011,
Alt5 = 0b010
}
pub trait GpioFn { fn fsel(&self) -> u32; }
impl GpioFn for Input { fn fsel(&self) -> u32 { Fsel::Input as u32 } }
impl GpioFn for Output { fn fsel(&self) -> u32 { Fsel::Output as u32 } }
impl GpioFn for Alt0 { fn fsel(&self) -> u32 { Fsel::Alt0 as u32 } }
impl GpioFn for Alt1 { fn fsel(&self) -> u32 { Fsel::Alt1 as u32 } }
impl GpioFn for Alt2 { fn fsel(&self) -> u32 { Fsel::Alt2 as u32 } }
impl GpioFn for Alt3 { fn fsel(&self) -> u32 { Fsel::Alt3 as u32 } }
impl GpioFn for Alt4 { fn fsel(&self) -> u32 { Fsel::Alt4 as u32 } }
impl GpioFn for Alt5 { fn fsel(&self) -> u32 { Fsel::Alt5 as u32 } }
}
static TAKEN: AtomicU64 = AtomicU64::new(0);
pub fn take_pin(number: u8) -> Option<Pin<function::Unknown>> {
let b: u64 = 1 << number as u64;
if number > 53 || TAKEN.fetch_or(b, Ordering::AcqRel) & b == b {
None
} else {
Some(Pin { number, function: function::Unknown })
}
}
pub struct Pin<FN> {
number: u8,
function: FN
}
impl<FN> Pin<FN> {
fn into<F: function::GpioFn>(self, function: F) -> Pin<F> {
unsafe {
self.fmodify(register::GPFSEL0::as_ptr(), 10, 3, function.fsel());
}
Pin { number: self.number, function }
}
pub fn into_input(self) -> Pin<function::Input> {
self.into(function::Input)
}
pub fn into_output(self) -> Pin<function::Output> {
self.into(function::Output)
}
pub fn into_alt0(self) -> Pin<function::Alt0> {
self.into(function::Alt0)
}
pub fn into_alt1(self) -> Pin<function::Alt1> {
self.into(function::Alt1)
}
pub fn into_alt2(self) -> Pin<function::Alt2> {
self.into(function::Alt2)
}
pub fn into_alt3(self) -> Pin<function::Alt3> {
self.into(function::Alt3)
}
pub fn into_alt4(self) -> Pin<function::Alt4> {
self.into(function::Alt4)
}
pub fn into_alt5(self) -> Pin<function::Alt5> {
self.into(function::Alt5)
}
pub fn level(&self) -> u32 {
unsafe {
let p = register::GPLEV0::as_ptr();
let a = p.add((self.number / 32) as usize);
let r = ReadOnly::<u32>::new(a as usize);
let f = RegisterField::<u32>::new(self.number % 32, 1);
r.read_field(f)
}
}
unsafe fn fmodify(&self, ptr: *mut u32, fcnt: u8, fsize: u8, val: u32) {
let a = ptr.add((self.number / fcnt) as usize);
let r = ReadWrite::<u32>::new(a as usize);
let f = RegisterField::<u32>::new((self.number % fcnt) * fsize, fsize);
r.modify_field(f, val);
}
}
impl Pin<function::Output> {
pub fn set(&self) {
unsafe {
self.fmodify(register::GPSET0::as_ptr(), 32, 1, 1);
}
}
pub fn clear(&self) {
unsafe {
self.fmodify(register::GPCLR0::as_ptr(), 32, 1, 1)
}
}
}
mod register {
// register difinitions
// I struggled a lot a couple of months ago to implement register abstractions the way I'll like
// and finally got across your idea of modules + constants, but adapted it, so register definitions
// is something similar to what you have in ruspiro-registers
}
Then I have module device
. device drivers accessed by kernel to manage devices. Those drivers have access to peripherals they need:
// device::ackled
use crate::platform::peripheral::gpio;
pub struct AckLED {
pin: gpio::Pin<gpio::function::Output>
}
impl AckLED {
pub fn init() -> Self {
Self { pin: gpio::take_pin(42).unwrap().into_output() }
}
pub fn on(&self) {
self.pin.set();
}
pub fn off(&self) {
self.pin.clear();
}
pub fn level(&self) -> u32 {
self.pin.level()
}
}
Here I will also have shared state of the device.
And finally when something needs a device, it can access it:
//kernel
#[inline(never)]
pub fn main() -> ! {
use crate::device::ackled;
let ackled = ackled::AckLED::init();
ackled.on();
loop {}
}
Now I'm working on enabling interrupts, then will come shared access to resources. I think I want to implement something as described here.