1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
//! Userspace driver for the rxbuffer device of the maia-sdr kernel module.
//!
//! This implements memory mapping and cache invalidation control, as provided
//! by the rxbuffer device implemented in `maia-sdr.ko`. The rxbuffer device
//! gives access to a DMA buffer formed by a ring of buffers of equal size. The
//! cache of each of the buffers in the ring can be invalidated independently.
use anyhow::Result;
use std::os::unix::io::{AsRawFd, RawFd};
use tokio::fs;
/// Receive DMA buffer.
///
/// This struct corresponds to a rxbuffer device.
#[derive(Debug)]
pub struct RxBuffer {
_file: fs::File,
fd: RawFd,
buffer: *mut libc::c_void,
buffer_size: usize,
num_buffers: usize,
}
unsafe impl Send for RxBuffer {}
impl RxBuffer {
/// Opens an rxbuffer device.
///
/// The name of the device corresponds to the filename of the character
/// device in `/dev`.
pub async fn new(name: &str) -> Result<RxBuffer> {
let file = fs::File::open(format!("/dev/{name}")).await?;
let fd = file.as_raw_fd();
let buffer_size = usize::from_str_radix(
fs::read_to_string(format!("/sys/class/maia-sdr/{name}/device/buffer_size"))
.await?
.trim_end()
.trim_start_matches("0x"),
16,
)?;
let num_buffers =
fs::read_to_string(format!("/sys/class/maia-sdr/{name}/device/num_buffers"))
.await?
.trim_end()
.parse::<usize>()?;
let buffer = unsafe {
match libc::mmap(
std::ptr::null_mut::<libc::c_void>(),
buffer_size * num_buffers,
libc::PROT_READ,
libc::MAP_SHARED,
fd,
0,
) {
libc::MAP_FAILED => anyhow::bail!("mmap rxbuffer failed"),
x => x,
}
};
Ok(RxBuffer {
_file: file,
fd,
buffer,
buffer_size,
num_buffers,
})
}
/// Returns the size in bytes of each of the buffers in the ring.
pub fn buffer_size(&self) -> usize {
self.buffer_size
}
/// Returns the number of buffers in the ring.
pub fn num_buffers(&self) -> usize {
self.num_buffers
}
/// Returns a slice that contains one of the buffers in the ring.
///
/// # Panics
///
/// This function panics if `num_buffer` is greater or equal to the number
/// of buffers in the ring.
pub fn buffer_as_slice(&self, num_buffer: usize) -> &[u8] {
assert!(num_buffer < self.num_buffers);
unsafe {
std::slice::from_raw_parts(
self.buffer.add(num_buffer * self.buffer_size) as *const u8,
self.buffer_size,
)
}
}
/// Invalidates the cache of one of the buffers in the ring.
///
/// After calling this function, the contents of the CPU caches
/// corresponding to the buffer have been invalidated, so changes in the
/// buffer produced by non-coherent writes done by the FPGA can be observed
/// by the CPU.
///
/// This function should be called before reading data from the buffer,
/// unless we know that the FPGA has not written to that buffer since the
/// last time that we invalidated its corresponding caches.
pub fn cache_invalidate(&self, num_buffer: usize) -> Result<()> {
assert!(num_buffer < self.num_buffers);
unsafe { ioctl::maia_kmod_cacheinv(self.fd, num_buffer as _) }?;
Ok(())
}
}
mod ioctl {
use nix::ioctl_write_int;
const MAIA_SDR_IOC_MAGIC: u8 = b'M';
const MAIA_SDR_CACHEINV: u8 = 0;
ioctl_write_int!(maia_kmod_cacheinv, MAIA_SDR_IOC_MAGIC, MAIA_SDR_CACHEINV);
}
impl Drop for RxBuffer {
fn drop(&mut self) {
unsafe {
libc::munmap(self.buffer, self.buffer_size * self.num_buffers);
}
}
}