libcamera/
framebuffer_map.rs1use std::{collections::HashMap, mem::MaybeUninit};
2
3use thiserror::Error;
4
5use crate::framebuffer::AsFrameBuffer;
6
7#[derive(Debug, Error)]
8pub enum MemoryMappedFrameBufferError {
9 #[error("Plane {index} with offset {offset} and size {len} exceeds file descriptor size of {fd_len}")]
10 PlaneOutOfBounds {
11 index: usize,
12 offset: usize,
13 len: usize,
14 fd_len: usize,
15 },
16 #[error("Plane {index} has an invalid offset")]
17 InvalidOffset { index: usize },
18 #[error("mmap failed with {0:?}")]
19 MemoryMapError(std::io::Error),
20 #[error("mapping was created read-only; write access requested")]
21 NotWritable,
22}
23
24struct MappedPlane {
25 fd: i32,
26 offset: usize,
27 len: usize,
28}
29
30pub struct MemoryMappedFrameBuffer<T: AsFrameBuffer> {
32 fb: T,
33 writable: bool,
34 mmaps: HashMap<i32, (*mut core::ffi::c_void, usize, usize)>,
36 planes: Vec<MappedPlane>,
37}
38
39impl<T: AsFrameBuffer> MemoryMappedFrameBuffer<T> {
40 pub fn new(fb: T) -> Result<Self, MemoryMappedFrameBufferError> {
44 Self::with_access(fb, false)
45 }
46
47 pub fn new_writable(fb: T) -> Result<Self, MemoryMappedFrameBufferError> {
49 Self::with_access(fb, true)
50 }
51
52 fn with_access(fb: T, writable: bool) -> Result<Self, MemoryMappedFrameBufferError> {
53 struct MapInfo {
54 start: usize,
56 end: usize,
58 total_len: usize,
60 }
61
62 let mut planes = Vec::new();
63 let mut map_info: HashMap<i32, MapInfo> = HashMap::new();
64 let page_size = {
65 let ps = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
66 if ps > 0 {
67 ps as usize
68 } else {
69 4096
70 }
71 };
72
73 for (index, plane) in fb.planes().into_iter().enumerate() {
74 let fd = plane.fd();
75 let offset = plane
76 .offset()
77 .ok_or(MemoryMappedFrameBufferError::InvalidOffset { index })?;
78 let len = plane.len();
79
80 planes.push(MappedPlane { fd, offset, len });
81
82 map_info.entry(fd).or_insert_with(|| {
84 let mut st = MaybeUninit::<libc::stat>::uninit();
85 let ret = unsafe { libc::fstat(fd, st.as_mut_ptr()) };
86 let total_len = if ret != 0 {
87 0
88 } else {
89 let st = unsafe { st.assume_init() };
90 st.st_size as usize
91 };
92 MapInfo {
93 start: offset,
94 end: offset,
95 total_len,
96 }
97 });
98
99 let info = map_info.get_mut(&fd).unwrap();
100
101 if info.total_len > 0 && offset + len > info.total_len {
103 return Err(MemoryMappedFrameBufferError::PlaneOutOfBounds {
104 index,
105 offset,
106 len,
107 fd_len: info.total_len,
108 });
109 }
110
111 let aligned_start = offset - (offset % page_size);
112 info.start = info.start.min(aligned_start);
113 info.end = info.end.max(offset + len);
114 }
115
116 let mmaps = map_info
117 .iter()
118 .map(|(fd, info)| {
119 let map_len = info.end.saturating_sub(info.start);
120 let addr = unsafe {
121 libc::mmap64(
122 core::ptr::null_mut(),
123 map_len,
124 libc::PROT_READ | if writable { libc::PROT_WRITE } else { 0 },
125 libc::MAP_SHARED,
126 *fd,
127 info.start as _,
128 )
129 };
130
131 if addr == libc::MAP_FAILED {
132 Err(MemoryMappedFrameBufferError::MemoryMapError(
133 std::io::Error::last_os_error(),
134 ))
135 } else {
136 Ok((*fd, (addr, map_len, info.start)))
137 }
138 })
139 .collect::<Result<HashMap<i32, (*mut core::ffi::c_void, usize, usize)>, MemoryMappedFrameBufferError>>()
140 .unwrap();
141
142 Ok(Self {
143 fb,
144 writable,
145 mmaps,
146 planes,
147 })
148 }
149
150 pub fn data(&self) -> Vec<&[u8]> {
152 self.planes
153 .iter()
154 .map(|plane| {
155 let (mmap_ptr, _, map_offset) = self.mmaps[&plane.fd];
156 let mmap_ptr: *const u8 = mmap_ptr.cast();
157 let offset = plane.offset - map_offset;
158 unsafe { core::slice::from_raw_parts(mmap_ptr.add(offset), plane.len) }
159 })
160 .collect()
161 }
162
163 pub fn data_mut(&mut self) -> Result<Vec<&mut [u8]>, MemoryMappedFrameBufferError> {
165 if !self.writable {
166 return Err(MemoryMappedFrameBufferError::NotWritable);
167 }
168
169 Ok(self
170 .planes
171 .iter()
172 .map(|plane| {
173 let (mmap_ptr, _, map_offset) = self.mmaps[&plane.fd];
174 let mmap_ptr: *mut u8 = mmap_ptr.cast();
175 let offset = plane.offset - map_offset;
176 unsafe { core::slice::from_raw_parts_mut(mmap_ptr.add(offset), plane.len) }
177 })
178 .collect())
179 }
180
181 pub fn is_writable(&self) -> bool {
183 self.writable
184 }
185
186 pub fn mapped_len(&self, fd: i32) -> Option<usize> {
188 self.mmaps.get(&fd).map(|(_, len, _)| *len)
189 }
190}
191
192impl<T: AsFrameBuffer> AsFrameBuffer for MemoryMappedFrameBuffer<T> {
193 unsafe fn ptr(&self) -> std::ptr::NonNull<libcamera_sys::libcamera_framebuffer_t> {
194 self.fb.ptr()
195 }
196}
197
198unsafe impl<T: AsFrameBuffer> Send for MemoryMappedFrameBuffer<T> {}
199
200impl<T: AsFrameBuffer> Drop for MemoryMappedFrameBuffer<T> {
201 fn drop(&mut self) {
202 for (_fd, (ptr, size, _map_offset)) in self.mmaps.drain() {
204 unsafe {
205 libc::munmap(ptr, size);
206 }
207 }
208 }
209}