std1.0.0-beta-7 SafeC docs
// SafeC Standard Library — DMA Implementation
#pragma once
#include <std/dma.h>
#include <std/mem.h>

// ── DmaBuffer ─────────────────────────────────────────────────────────────────

namespace std {

inline struct DmaBuffer DmaBuffer::wrap([]unsigned char virt, unsigned long phys) {
    struct DmaBuffer buf;
    unsafe { buf.virt = (&unsigned char)virt.ptr; }
    unsigned long va;
    unsafe { va = (unsigned long)virt.ptr; }
    buf.phys  = (phys != (unsigned long)0) ? phys : va;
    buf.size  = virt.len;
    buf.owner = 0;  // CPU owns initially
    return buf;
}

void DmaBuffer::to_device() {
    // Flush all cache lines in the buffer range.
    unsafe {
        // Start at the line holding the first byte: stepping from an
        // unaligned address would skip the line holding the last one.
        unsigned long p = mem_align_down((unsigned long)self.virt, (unsigned long)MEM_CACHE_LINE_SIZE);
        unsigned long end = (unsigned long)self.virt + self.size;
        while (p < end) {
            mem_clflush_raw_((const unsigned char*)p);
            p = p + (unsigned long)MEM_CACHE_LINE_SIZE;
        }
        // Memory barrier to ensure writes are visible before DMA starts.
#ifdef __x86_64__
        asm volatile ("mfence" : : : "memory");
#elif defined(__aarch64__)
        asm volatile ("dsb st" : : : "memory");
#elif defined(__riscv)
        asm volatile ("fence w,o" : : : "memory");
#else
        asm volatile ("" : : : "memory");
#endif
    }
    self.owner = 1;
}

void DmaBuffer::to_cpu() {
    // Invalidate cache lines so CPU sees DMA-written data.
    unsafe {
#ifdef __x86_64__
        asm volatile ("mfence" : : : "memory");
#elif defined(__aarch64__)
        asm volatile ("dsb ld" : : : "memory");
#elif defined(__riscv)
        asm volatile ("fence i,r" : : : "memory");
#else
        asm volatile ("" : : : "memory");
#endif
        // Start at the line holding the first byte: stepping from an
        // unaligned address would skip the line holding the last one.
        unsigned long p = mem_align_down((unsigned long)self.virt, (unsigned long)MEM_CACHE_LINE_SIZE);
        unsigned long end = (unsigned long)self.virt + self.size;
        while (p < end) {
            mem_clflush_raw_((const unsigned char*)p);
            p = p + (unsigned long)MEM_CACHE_LINE_SIZE;
        }
        asm volatile ("" : : : "memory");
    }
    self.owner = 0;
}

inline int DmaBuffer::cpu_accessible() const {
    if (self.owner == 0) { return 1; }
    return 0;
}

inline unsigned long DmaBuffer::phys_at(unsigned long offset) const {
    return self.phys + offset;
}


// ── DmaSgList ─────────────────────────────────────────────────────────────────

inline void DmaSgList::add(unsigned long phys, unsigned long len) {
    if (self.count >= DMA_SG_MAX) { return; }
    self.entries[self.count].phys = phys;
    self.entries[self.count].len  = len;
    self.count = self.count + 1;
}

inline void DmaSgList::clear() {
    self.count = 0;
}

inline unsigned long DmaSgList::total_len() const {
    unsigned long total = (unsigned long)0;
    int i = 0;
    while (i < self.count) {
        total = total + self.entries[i].len;
        i = i + 1;
    }
    return total;
}

} // namespace std