hardware 0.0.9

A no_std bare-metal hardware abstraction layer — all port I/O, memory and swap allocations are guarded at runtime. Do not consider this dependency stable before x.1.x
Documentation
use crate::common::once::Once;
use crate::dma::buffer::DmaBuffer;
use crate::dma::engine;
use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

pub struct LpuDeviceInner {
    base: usize,
    initialized: AtomicBool,
    mode: AtomicUsize,
}

pub struct LpuDevice {
    inner: UnsafeCell<LpuDeviceInner>,
}

unsafe impl Sync for LpuDevice {}

impl LpuDevice {
    pub fn new_with_base(base: usize) -> Self {
        LpuDevice {
            inner: UnsafeCell::new(LpuDeviceInner {
                base,
                initialized: AtomicBool::new(false),
                mode: AtomicUsize::new(0),
            }),
        }
    }

    pub fn init(&self) -> bool {
        unsafe {
            (*self.inner.get())
                .initialized
                .store(true, Ordering::Release);
        }
        true
    }

    pub fn is_initialized(&self) -> bool {
        unsafe { (*self.inner.get()).initialized.load(Ordering::Acquire) }
    }

    pub fn base_addr(&self) -> usize {
        unsafe { (*self.inner.get()).base }
    }

    pub fn set_mode(&self, m: usize) {
        unsafe {
            (*self.inner.get()).mode.store(m, Ordering::Release);
        }
    }

    pub fn get_mode(&self) -> usize {
        unsafe { (*self.inner.get()).mode.load(Ordering::Acquire) }
    }

    pub fn submit_task(
        &self,
        payload: &[u8],
        flags: u32,
        align: usize,
    ) -> Result<usize, &'static str> {
        let buf = DmaBuffer::new(payload.len(), align).ok_or("alloc failed")?;
        unsafe {
            core::ptr::copy_nonoverlapping(payload.as_ptr(), buf.as_ptr(), payload.len());
        }
        let engine = engine::get().ok_or("no dma engine")?;
        let enq = engine.submit_buffer(&buf, flags, align)?;
        if enq == 0 {
            return Err("submit failed");
        }
        let mut out = [crate::dma::engine::Descriptor {
            phys: 0,
            len: 0,
            flags: 0,
        }; 8];
        let mut waited = 0;
        loop {
            let drained = engine.drain(&mut out);
            for d in out.iter().take(drained) {
                if d.len == payload.len() {
                    let ok = crate::dma::engine::DmaEngine::unmap_iova(d.phys);
                    debug_assert!(ok);
                    return Ok(d.len);
                }
            }
            waited += 1;
            if waited > 1000 {
                break;
            }
        }
        Err("timeout")
    }
}

static LPU_DEVICE: Once<LpuDevice> = Once::new();

pub fn init_with_base(base: usize) {
    let dev = LpuDevice::new_with_base(base);
    if LPU_DEVICE.set(dev) {
        if let Some(d) = LPU_DEVICE.get() {
            d.init();
        }
    }
}

pub fn init() {
    init_with_base(0);
}

pub fn get() -> Option<&'static LpuDevice> {
    LPU_DEVICE.get()
}

static LPU_IRQ_COUNT: AtomicUsize = AtomicUsize::new(0);

pub fn lpu_irq_shim() {
    LPU_IRQ_COUNT.fetch_add(1, Ordering::AcqRel);
}

pub fn lpu_irq_count() -> usize {
    LPU_IRQ_COUNT.load(Ordering::Acquire)
}

pub fn register_irq_vector(vec: usize) -> bool {
    crate::interrupt::register(vec, lpu_irq_shim)
}