1#![allow(unsafe_code)]
6#![cfg_attr(
7 feature = "nightly",
8 feature(
9 type_privacy_lints,
10 non_exhaustive_omitted_patterns_lint,
11 strict_provenance
12 )
13)]
14#![cfg_attr(
15 feature = "nightly",
16 warn(
17 fuzzy_provenance_casts,
18 lossy_provenance_casts,
19 unnameable_types,
20 non_exhaustive_omitted_patterns,
21 clippy::empty_enum_variants_with_brackets
22 )
23)]
24#![doc = include_str!("../README.md")]
25
26use core::{error, ffi::c_void, fmt, ptr, slice};
27use std::{
28 io,
29 os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
30};
31
32use rustix::{
33 fs::fstat,
34 mm::{mmap, munmap, MapFlags, ProtFlags},
35 param::page_size,
36};
37use tracing::{debug, debug_span, trace, trace_span, warn};
38
39mod ioctl;
40use ioctl::{
41 dma_buf_begin_cpu_read_access, dma_buf_begin_cpu_readwrite_access,
42 dma_buf_begin_cpu_write_access, dma_buf_end_cpu_read_access, dma_buf_end_cpu_readwrite_access,
43 dma_buf_end_cpu_write_access,
44};
45
46#[derive(Debug)]
48pub struct DmaBuf(OwnedFd);
49
50impl DmaBuf {
51 #[expect(
62 clippy::unwrap_in_result,
63 reason = "The kernel doesn't use the same types between stat and munmap, but it's not something one can recover from."
64 )]
65 pub fn memory_map(self) -> io::Result<MappedDmaBuf> {
66 debug!("Mapping DMA-Buf buffer with File Descriptor {:#?}", self.0);
67
68 let len = usize::try_from(fstat(&self.0)?.st_size)
69 .expect("Buffer size can't fit into mmap argument length")
70 .next_multiple_of(page_size());
71 trace!("Valid buffer, size {len}");
72
73 let mapping_ptr = unsafe {
76 mmap(
77 ptr::null_mut(),
78 len,
79 ProtFlags::READ | ProtFlags::WRITE,
80 MapFlags::SHARED,
81 &self.0,
82 0,
83 )
84 }
85 .map(<*mut c_void>::cast::<u8>)?;
86
87 trace!("Memory Mapping Done");
88
89 Ok(MappedDmaBuf {
90 buf: self,
91 len,
92 mmap: mapping_ptr,
93 })
94 }
95}
96
97pub struct MappedDmaBuf {
99 buf: DmaBuf,
100 len: usize,
101 mmap: *mut u8,
102}
103
104#[derive(Debug)]
106pub enum BufferError {
107 FdAccess {
109 reason: String,
111
112 source: io::Error,
114 },
115
116 Closure(Box<dyn error::Error>),
118}
119
120impl fmt::Display for BufferError {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self {
123 BufferError::FdAccess { reason, .. } => {
124 f.write_fmt(format_args!("Could not access the buffer: {reason}"))
125 }
126 BufferError::Closure(error) => {
127 f.write_fmt(format_args!("The closure returned an error: {error}"))
128 }
129 }
130 }
131}
132
133impl error::Error for BufferError {
134 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
135 match self {
136 BufferError::FdAccess { source, .. } => Some(source),
137 BufferError::Closure(error) => Some(error.as_ref()),
138 }
139 }
140}
141
142impl MappedDmaBuf {
143 fn as_slice(&self) -> &[u8] {
144 unsafe { slice::from_raw_parts(self.mmap, self.len) }
148 }
149
150 fn as_slice_mut(&mut self) -> &mut [u8] {
151 unsafe { slice::from_raw_parts_mut(self.mmap, self.len) }
155 }
156
157 pub fn read<A, F, R>(&self, f: F, arg: Option<A>) -> Result<R, BufferError>
169 where
170 F: Fn(&[u8], Option<A>) -> Result<R, Box<dyn error::Error>>,
171 {
172 trace_span!("Buffer Read Access").in_scope(|| {
173 trace_span!("dma-buf begin access ioctl")
174 .in_scope(|| dma_buf_begin_cpu_read_access(self.buf.as_fd()))?;
175
176 let ret = debug_span!("Closure Execution").in_scope(|| {
177 let bytes = self.as_slice();
178
179 f(bytes, arg)
180 .inspect(|_| debug!("Closure done without error"))
181 .map_err(|e| {
182 warn!("Closure encountered an error {}", e);
183 BufferError::Closure(e)
184 })
185 });
186
187 trace_span!("dma-buf end access ioctl")
188 .in_scope(|| dma_buf_end_cpu_read_access(self.buf.as_fd()))?;
189
190 ret
191 })
192 }
193
194 pub fn readwrite<A, F, R>(&mut self, f: F, arg: Option<A>) -> Result<R, BufferError>
207 where
208 F: Fn(&mut [u8], Option<A>) -> Result<R, Box<dyn error::Error>>,
209 {
210 trace_span!("Buffer Read / Write Access").in_scope(|| {
211 trace_span!("dma-buf begin access ioctl")
212 .in_scope(|| dma_buf_begin_cpu_readwrite_access(self.buf.as_fd()))?;
213
214 let ret = debug_span!("Closure Execution").in_scope(|| {
215 let bytes = self.as_slice_mut();
216
217 f(bytes, arg)
218 .inspect(|_| debug!("Closure done without error"))
219 .map_err(|e| {
220 warn!("Closure encountered an error {}", e);
221 BufferError::Closure(e)
222 })
223 });
224
225 trace_span!("dma-buf end access ioctl")
226 .in_scope(|| dma_buf_end_cpu_readwrite_access(self.buf.as_fd()))?;
227
228 ret
229 })
230 }
231
232 pub fn write<A, F>(&mut self, f: F, arg: Option<A>) -> Result<(), BufferError>
244 where
245 F: Fn(&mut [u8], Option<A>) -> Result<(), Box<dyn error::Error>>,
246 {
247 trace_span!("Buffer Write Access").in_scope(|| {
248 trace_span!("dma-buf begin access ioctl")
249 .in_scope(|| dma_buf_begin_cpu_write_access(self.buf.as_fd()))?;
250
251 let ret = debug_span!("Closure Execution").in_scope(|| {
252 let bytes = self.as_slice_mut();
253
254 f(bytes, arg)
255 .inspect(|()| debug!("Closure done without error"))
256 .map_err(|e| {
257 warn!("Closure encountered an error {}", e);
258 BufferError::Closure(e)
259 })
260 });
261
262 trace_span!("dma-buf end access ioctl")
263 .in_scope(|| dma_buf_end_cpu_write_access(self.buf.as_fd()))?;
264
265 ret
266 })
267 }
268}
269
270impl From<OwnedFd> for DmaBuf {
271 fn from(owned: OwnedFd) -> Self {
272 Self(owned)
273 }
274}
275
276impl AsFd for DmaBuf {
277 fn as_fd(&self) -> BorrowedFd<'_> {
278 self.0.as_fd()
279 }
280}
281
282impl AsRawFd for DmaBuf {
283 fn as_raw_fd(&self) -> RawFd {
284 self.0.as_raw_fd()
285 }
286}
287
288impl AsFd for MappedDmaBuf {
289 fn as_fd(&self) -> BorrowedFd<'_> {
290 self.buf.as_fd()
291 }
292}
293
294impl AsRawFd for MappedDmaBuf {
295 fn as_raw_fd(&self) -> RawFd {
296 self.buf.as_raw_fd()
297 }
298}
299
300impl FromRawFd for DmaBuf {
301 unsafe fn from_raw_fd(fd: RawFd) -> Self {
302 debug!("Importing DMABuf from File Descriptor {}", fd);
303
304 Self(unsafe { OwnedFd::from_raw_fd(fd) })
307 }
308}
309
310impl fmt::Debug for MappedDmaBuf {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 f.debug_struct("MappedDmaBuf")
313 .field("DmaBuf", &self.buf)
314 .field("len", &self.len)
315 .field("address", &self.mmap)
316 .finish()
317 }
318}
319
320impl Drop for MappedDmaBuf {
321 fn drop(&mut self) {
322 if unsafe { munmap(self.mmap.cast::<c_void>(), self.len) }.is_err() {
325 warn!("unmap failed!");
326 }
327 }
328}