1use crate::Format;
6use fuchsia_runtime::vmar_root_self;
7
8use thiserror::Error;
9
10pub struct VmoBuffer {
12 vmo: zx::Vmo,
14
15 vmo_size_bytes: u64,
17
18 num_frames: u64,
20
21 format: Format,
23
24 base_address: usize,
26}
27
28impl VmoBuffer {
29 pub fn new(vmo: zx::Vmo, num_frames: u64, format: Format) -> Result<Self, VmoBufferError> {
30 let data_size_bytes = num_frames * format.bytes_per_frame() as u64;
32 let vmo_size_bytes = vmo
33 .get_size()
34 .map_err(|status| VmoBufferError::VmoGetSize(zx::Status::from(status)))?;
35
36 if data_size_bytes > vmo_size_bytes {
37 return Err(VmoBufferError::VmoTooSmall { data_size_bytes, vmo_size_bytes });
38 }
39
40 let base_address = vmar_root_self()
41 .map(
42 0,
43 &vmo,
44 0,
45 vmo_size_bytes as usize,
46 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
48 )
49 .map_err(|status| VmoBufferError::VmoMap(zx::Status::from(status)))?;
50
51 log::debug!(
52 "format {:?} num frames {} data_size_bytes {}",
53 format,
54 num_frames,
55 data_size_bytes
56 );
57
58 Ok(Self { vmo, vmo_size_bytes, base_address, num_frames, format })
59 }
60
61 pub fn data_size_bytes(&self) -> u64 {
65 self.num_frames * self.format.bytes_per_frame() as u64
66 }
67
68 pub fn write_to_frame(&self, running_frame: u64, buf: &[u8]) -> Result<(), VmoBufferError> {
70 if buf.len() % self.format.bytes_per_frame() as usize != 0 {
71 return Err(VmoBufferError::BufferIncompleteFrames);
72 }
73 let frame_offset = running_frame % self.num_frames;
74 let byte_offset = frame_offset as usize * self.format.bytes_per_frame() as usize;
75 let num_frames_in_buf = buf.len() as u64 / self.format.bytes_per_frame() as u64;
76
77 if (frame_offset + num_frames_in_buf) <= self.num_frames {
80 self.vmo.write(&buf[..], byte_offset as u64).map_err(VmoBufferError::VmoWrite)?;
81 self.flush_cache(byte_offset, buf.len()).map_err(VmoBufferError::VmoFlushCache)?;
83 log::debug!(
84 "Wrote {} bytes ({} frames) from buf, to ring_buf frame {} (running frame {})",
85 buf.len(),
86 num_frames_in_buf,
87 frame_offset,
88 running_frame
89 );
90 fuchsia_trace::instant!(
91 c"audio-streaming",
92 c"AudioLib::VmoBuffer::write_to_frame",
93 fuchsia_trace::Scope::Process,
94 "source bytes to write" => buf.len(),
95 "source frames to write" => num_frames_in_buf,
96 "frame size" => self.format.bytes_per_frame(),
97 "dest RB size (frames)" => self.num_frames,
98 "dest RB running frame" => running_frame,
99 "dest RB frame position" => frame_offset,
100 "vmo write start" => byte_offset,
101 "vmo write len (bytes)" => buf.len()
102 );
103 } else {
104 let frames_to_write_until_end = self.num_frames - frame_offset;
105 let bytes_until_buffer_end =
106 frames_to_write_until_end as usize * self.format.bytes_per_frame() as usize;
107
108 self.vmo
109 .write(&buf[..bytes_until_buffer_end], byte_offset as u64)
110 .map_err(VmoBufferError::VmoWrite)?;
111 self.flush_cache(byte_offset, bytes_until_buffer_end)
113 .map_err(VmoBufferError::VmoFlushCache)?;
114 log::debug!(
115 "First wrote {} bytes ({} frames) from buf, to ring_buf frame {} (running frame {})",
116 bytes_until_buffer_end, frames_to_write_until_end,
117 frame_offset, running_frame
118 );
119
120 if buf[bytes_until_buffer_end..].len() > self.vmo_size_bytes as usize {
121 log::error!("Remainder of write buffer is too big for the vmo.");
122 }
123
124 self.vmo.write(&buf[bytes_until_buffer_end..], 0).map_err(VmoBufferError::VmoWrite)?;
126 self.flush_cache(0, buf.len() - bytes_until_buffer_end)
127 .map_err(VmoBufferError::VmoFlushCache)?;
128 fuchsia_trace::instant!(
129 c"audio-streaming",
130 c"AudioLib::VmoBuffer::write_to_frame",
131 fuchsia_trace::Scope::Process,
132 "source bytes to write" => buf.len(),
133 "source frames to write" => num_frames_in_buf,
134 "frame size" => self.format.bytes_per_frame(),
135 "dest RB size (frames)" => self.num_frames,
136 "dest RB running frame" => running_frame,
137 "dest RB frame position" => frame_offset,
138 "first vmo write start" => byte_offset,
139 "first vmo write len (bytes)" => bytes_until_buffer_end,
140 "second vmo write start" => 0,
141 "second vmo write len (bytes)" => buf.len() - bytes_until_buffer_end
142 );
143 log::debug!(
144 "Then wrote {} bytes ({} frames) from buf, to ring_buf frame 0 (running frame {})",
145 buf.len() - bytes_until_buffer_end,
146 num_frames_in_buf - frames_to_write_until_end,
147 running_frame + frames_to_write_until_end
148 );
149 }
150
151 Ok(())
152 }
153
154 pub fn read_from_frame(
156 &self,
157 running_frame: u64,
158 buf: &mut [u8],
159 ) -> Result<(), VmoBufferError> {
160 if buf.len() % self.format.bytes_per_frame() as usize != 0 {
161 return Err(VmoBufferError::BufferIncompleteFrames);
162 }
163 let frame_offset = running_frame % self.num_frames;
164 let byte_offset = frame_offset as usize * self.format.bytes_per_frame() as usize;
165 let num_frames_in_buf = buf.len() as u64 / self.format.bytes_per_frame() as u64;
166
167 if (frame_offset + num_frames_in_buf) <= self.num_frames {
170 log::debug!("frame {} reading starting from position {}", running_frame, byte_offset);
172 self.flush_invalidate_cache(byte_offset as usize, buf.len())
173 .map_err(VmoBufferError::VmoFlushCache)?;
174 self.vmo.read(buf, byte_offset as u64).map_err(VmoBufferError::VmoRead)?;
175 } else {
176 let frames_to_write_until_end = self.num_frames - frame_offset;
177 let bytes_until_buffer_end =
178 frames_to_write_until_end as usize * self.format.bytes_per_frame() as usize;
179
180 log::debug!(
181 "frame {} reading starting from position {} (with looparound)",
182 running_frame,
183 byte_offset
184 );
185 self.flush_invalidate_cache(byte_offset, bytes_until_buffer_end)
187 .map_err(VmoBufferError::VmoFlushCache)?;
188 self.vmo
189 .read(&mut buf[..bytes_until_buffer_end], byte_offset as u64)
190 .map_err(VmoBufferError::VmoRead)?;
191
192 if buf[bytes_until_buffer_end..].len() > self.vmo_size_bytes as usize {
193 log::error!("Remainder of read buffer is too big for the vmo.");
194 }
195
196 self.flush_invalidate_cache(0, buf.len() - bytes_until_buffer_end)
197 .map_err(VmoBufferError::VmoFlushCache)?;
198 self.vmo
199 .read(&mut buf[bytes_until_buffer_end..], 0)
200 .map_err(VmoBufferError::VmoRead)?;
201 }
202 Ok(())
203 }
204
205 fn flush_cache(&self, offset_bytes: usize, size_bytes: usize) -> Result<(), zx::Status> {
208 assert!(offset_bytes + size_bytes <= self.vmo_size_bytes as usize);
209 let status = unsafe {
210 zx::sys::zx_cache_flush(
212 (self.base_address + offset_bytes) as *mut u8,
213 size_bytes,
214 zx::sys::ZX_CACHE_FLUSH_DATA,
215 )
216 };
217 zx::Status::ok(status)
218 }
219
220 fn flush_invalidate_cache(
223 &self,
224 offset_bytes: usize,
225 size_bytes: usize,
226 ) -> Result<(), zx::Status> {
227 assert!(offset_bytes + size_bytes <= self.vmo_size_bytes as usize);
228 let status = unsafe {
229 zx::sys::zx_cache_flush(
231 (self.base_address + offset_bytes) as *mut u8,
232 size_bytes,
233 zx::sys::ZX_CACHE_FLUSH_DATA | zx::sys::ZX_CACHE_FLUSH_INVALIDATE,
234 )
235 };
236 zx::Status::ok(status)
237 }
238}
239
240impl Drop for VmoBuffer {
241 fn drop(&mut self) {
242 unsafe {
245 vmar_root_self().unmap(self.base_address, self.vmo_size_bytes as usize).unwrap();
246 }
247 }
248}
249
250#[derive(Error, Debug)]
251pub enum VmoBufferError {
252 #[error("VMO is too small ({vmo_size_bytes} bytes) to hold ring buffer data ({data_size_bytes} bytes)")]
253 VmoTooSmall { data_size_bytes: u64, vmo_size_bytes: u64 },
254
255 #[error("Buffer size is invalid; contains incomplete frames")]
256 BufferIncompleteFrames,
257
258 #[error("Failed to memory map VMO: {}", .0)]
259 VmoMap(#[source] zx::Status),
260
261 #[error("Failed to get VMO size: {}", .0)]
262 VmoGetSize(#[source] zx::Status),
263
264 #[error("Failed to flush VMO memory cache: {}", .0)]
265 VmoFlushCache(#[source] zx::Status),
266
267 #[error("Failed to read from VMO: {}", .0)]
268 VmoRead(#[source] zx::Status),
269
270 #[error("Failed to write to VMO: {}", .0)]
271 VmoWrite(#[source] zx::Status),
272}
273
274#[cfg(test)]
275mod test {
276 use super::*;
277 use crate::format::SampleType;
278 use assert_matches::assert_matches;
279
280 #[test]
281 fn vmobuffer_vmo_too_small() {
282 let format =
283 Format { frames_per_second: 48000, sample_type: SampleType::Uint8, channels: 2 };
284
285 let page_size = zx::system_get_page_size() as u64;
287 let num_frames = page_size + 1;
288 let vmo = zx::Vmo::create(page_size).unwrap();
289
290 assert_matches!(
291 VmoBuffer::new(vmo, num_frames, format).err(),
292 Some(VmoBufferError::VmoTooSmall { .. })
293 )
294 }
295
296 #[test]
297 fn vmobuffer_read_write() {
298 let format =
299 Format { frames_per_second: 48000, sample_type: SampleType::Uint8, channels: 2 };
300 const NUM_FRAMES_VMO: u64 = 10;
301 const NUM_FRAMES_BUF: u64 = 5;
302 const SAMPLE: u8 = 42;
303
304 let vmo_size = format.bytes_per_frame() as u64 * NUM_FRAMES_VMO;
305 let buf_size = format.bytes_per_frame() as u64 * NUM_FRAMES_BUF;
306
307 let vmo = zx::Vmo::create(vmo_size).unwrap();
308
309 let mut in_buf = vec![0; buf_size as usize];
311 let out_buf = vec![SAMPLE; buf_size as usize];
313
314 let vmo_buffer = VmoBuffer::new(vmo, NUM_FRAMES_VMO, format).unwrap();
315
316 vmo_buffer.write_to_frame(1, &out_buf).unwrap();
318
319 vmo_buffer.read_from_frame(1, &mut in_buf).unwrap();
321
322 assert_eq!(in_buf, out_buf);
323 }
324
325 #[test]
326 fn vmobuffer_read_write_wrapping() {
327 let format =
328 Format { frames_per_second: 48000, sample_type: SampleType::Uint8, channels: 2 };
329 let page_size = zx::system_get_page_size() as u64;
330 let num_frames_vmo: u64 = page_size;
331 let num_frames_buf: u64 = page_size / 2;
332 const SAMPLE: u8 = 42;
333
334 let vmo_size = format.bytes_per_frame() as u64 * num_frames_vmo;
335 let buf_size = format.bytes_per_frame() as u64 * num_frames_buf;
336
337 let vmo = zx::Vmo::create(vmo_size).unwrap();
338
339 let mut in_buf = vec![0; buf_size as usize];
341 let out_buf = vec![SAMPLE; buf_size as usize];
343
344 let vmo_buffer = VmoBuffer::new(vmo, num_frames_vmo, format).unwrap();
345
346 let frame = num_frames_vmo - 1;
348 assert!(frame + num_frames_buf > num_frames_vmo);
349
350 vmo_buffer.write_to_frame(frame, &out_buf).unwrap();
352
353 vmo_buffer.read_from_frame(frame, &mut in_buf).unwrap();
355
356 assert_eq!(in_buf, out_buf);
357 }
358}