maia_httpd/
rxbuffer.rs

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