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}