1use crate::Format;
6use fuchsia_runtime::vmar_root_self;
7
8use thiserror::Error;
9
10pub fn apply_affine_function(
20 a_value: i64,
21 numerator: u32,
22 denominator: u32,
23 a_offset: i64,
24 b_offset: i64,
25) -> i64 {
26 let numerator_i128 = numerator as i128;
27 let denominator_i128 = denominator as i128;
28
29 let mut intermediate = (a_value - a_offset) as i128 * numerator_i128;
36 if intermediate < 0 {
38 intermediate = intermediate - denominator_i128 + 1;
39 }
40 (intermediate / denominator_i128) as i64 + b_offset
41}
42
43pub struct VmoBuffer {
45 vmo: zx::Vmo,
47
48 vmo_size_bytes: u64,
50
51 num_frames: u64,
53
54 format: Format,
56
57 base_address: usize,
59}
60
61impl VmoBuffer {
62 pub fn new(vmo: zx::Vmo, num_frames: u64, format: Format) -> Result<Self, VmoBufferError> {
63 let data_size_bytes = num_frames * format.bytes_per_frame() as u64;
65 let vmo_size_bytes = vmo.get_size().map_err(VmoBufferError::VmoGetSize)?;
66
67 if data_size_bytes > vmo_size_bytes {
68 return Err(VmoBufferError::VmoTooSmall { data_size_bytes, vmo_size_bytes });
69 }
70
71 let base_address = vmar_root_self()
72 .map(
73 0,
74 &vmo,
75 0,
76 vmo_size_bytes as usize,
77 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
79 )
80 .map_err(VmoBufferError::VmoMap)?;
81
82 log::debug!(
83 "format {:?} num frames {} data_size_bytes {}",
84 format,
85 num_frames,
86 data_size_bytes
87 );
88
89 Ok(Self { vmo, vmo_size_bytes, num_frames, format, base_address })
90 }
91
92 pub fn data_size_bytes(&self) -> u64 {
96 self.num_frames * self.format.bytes_per_frame() as u64
97 }
98
99 pub fn write_to_frame(&self, running_frame: u64, buf: &[u8]) -> Result<(), VmoBufferError> {
101 if !buf.len().is_multiple_of(self.format.bytes_per_frame() as usize) {
102 return Err(VmoBufferError::BufferIncompleteFrames);
103 }
104 let frame_offset = running_frame % self.num_frames;
105 let byte_offset = frame_offset as usize * self.format.bytes_per_frame() as usize;
106 let num_frames_in_buf = buf.len() as u64 / self.format.bytes_per_frame() as u64;
107
108 if (frame_offset + num_frames_in_buf) <= self.num_frames {
111 self.vmo.write(buf, byte_offset as u64).map_err(VmoBufferError::VmoWrite)?;
112 self.flush_cache(byte_offset, buf.len()).map_err(VmoBufferError::VmoFlushCache)?;
114 log::debug!(
115 "At {} wrote {} frames at ring_buf pos {} (running frame {}-{})",
116 zx::MonotonicInstant::get().into_nanos(),
117 num_frames_in_buf,
118 frame_offset,
119 running_frame,
120 running_frame + num_frames_in_buf
121 );
122 fuchsia_trace::instant!(
123 c"audio-streaming",
124 c"AudioLib::VmoBuffer::write_to_frame",
125 fuchsia_trace::Scope::Process,
126 "source bytes to write" => buf.len(),
127 "source frames to write" => num_frames_in_buf,
128 "frame size" => self.format.bytes_per_frame(),
129 "dest RB size (frames)" => self.num_frames,
130 "dest RB running frame" => running_frame,
131 "dest RB frame position" => frame_offset,
132 "vmo write start" => byte_offset,
133 "vmo write len (bytes)" => buf.len()
134 );
135 } else {
136 let frames_to_write_until_end = self.num_frames - frame_offset;
137 let bytes_until_buffer_end =
138 frames_to_write_until_end as usize * self.format.bytes_per_frame() as usize;
139
140 self.vmo
141 .write(&buf[..bytes_until_buffer_end], byte_offset as u64)
142 .map_err(VmoBufferError::VmoWrite)?;
143 self.flush_cache(byte_offset, bytes_until_buffer_end)
145 .map_err(VmoBufferError::VmoFlushCache)?;
146 log::debug!(
147 "At {} first {} frames at ring_buf pos {} (running frame {}-{})",
148 zx::MonotonicInstant::get().into_nanos(),
149 frames_to_write_until_end,
150 frame_offset,
151 running_frame,
152 running_frame + frames_to_write_until_end
153 );
154
155 if buf[bytes_until_buffer_end..].len() > self.vmo_size_bytes as usize {
156 log::error!(
157 "Remainder of write buffer (size {}) is too big for the vmo (size {})",
158 buf[bytes_until_buffer_end..].len(),
159 self.vmo_size_bytes
160 );
161 }
162
163 self.vmo.write(&buf[bytes_until_buffer_end..], 0).map_err(VmoBufferError::VmoWrite)?;
165 self.flush_cache(0, buf.len() - bytes_until_buffer_end)
166 .map_err(VmoBufferError::VmoFlushCache)?;
167 fuchsia_trace::instant!(
168 c"audio-streaming",
169 c"AudioLib::VmoBuffer::write_to_frame",
170 fuchsia_trace::Scope::Process,
171 "source bytes to write" => buf.len(),
172 "source frames to write" => num_frames_in_buf,
173 "frame size" => self.format.bytes_per_frame(),
174 "dest RB size (frames)" => self.num_frames,
175 "dest RB running frame" => running_frame,
176 "dest RB frame position" => frame_offset,
177 "first vmo write start" => byte_offset,
178 "first vmo write len (bytes)" => bytes_until_buffer_end,
179 "second vmo write start" => 0,
180 "second vmo write len (bytes)" => buf.len() - bytes_until_buffer_end
181 );
182 log::debug!(
183 "At {}, then {} frames at ring_buf frame 0 (running frame {}-{})",
184 zx::MonotonicInstant::get().into_nanos(),
185 num_frames_in_buf - frames_to_write_until_end,
186 running_frame + frames_to_write_until_end,
187 running_frame + num_frames_in_buf
188 );
189 }
190
191 Ok(())
192 }
193
194 pub fn read_from_frame(
196 &self,
197 running_frame: u64,
198 buf: &mut [u8],
199 ) -> Result<(), VmoBufferError> {
200 if !buf.len().is_multiple_of(self.format.bytes_per_frame() as usize) {
201 return Err(VmoBufferError::BufferIncompleteFrames);
202 }
203 let frame_offset = running_frame % self.num_frames;
204 let byte_offset = frame_offset as usize * self.format.bytes_per_frame() as usize;
205 let num_frames_in_buf = buf.len() as u64 / self.format.bytes_per_frame() as u64;
206
207 if (frame_offset + num_frames_in_buf) <= self.num_frames {
210 log::debug!("frame {} reading starting from position {}", running_frame, byte_offset);
212 self.flush_invalidate_cache(byte_offset, buf.len())
213 .map_err(VmoBufferError::VmoFlushCache)?;
214 self.vmo.read(buf, byte_offset as u64).map_err(VmoBufferError::VmoRead)?;
215 } else {
216 let frames_to_write_until_end = self.num_frames - frame_offset;
217 let bytes_until_buffer_end =
218 frames_to_write_until_end as usize * self.format.bytes_per_frame() as usize;
219
220 log::debug!(
221 "frame {} reading starting from position {} (with looparound)",
222 running_frame,
223 byte_offset
224 );
225 self.flush_invalidate_cache(byte_offset, bytes_until_buffer_end)
227 .map_err(VmoBufferError::VmoFlushCache)?;
228 self.vmo
229 .read(&mut buf[..bytes_until_buffer_end], byte_offset as u64)
230 .map_err(VmoBufferError::VmoRead)?;
231
232 if buf[bytes_until_buffer_end..].len() > self.vmo_size_bytes as usize {
233 log::error!("Remainder of read buffer is too big for the vmo.");
234 }
235
236 self.flush_invalidate_cache(0, buf.len() - bytes_until_buffer_end)
237 .map_err(VmoBufferError::VmoFlushCache)?;
238 self.vmo
239 .read(&mut buf[bytes_until_buffer_end..], 0)
240 .map_err(VmoBufferError::VmoRead)?;
241 }
242 Ok(())
243 }
244
245 fn flush_cache(&self, offset_bytes: usize, size_bytes: usize) -> Result<(), zx::Status> {
248 assert!(offset_bytes + size_bytes <= self.vmo_size_bytes as usize);
249 let status = unsafe {
250 zx::sys::zx_cache_flush(
252 (self.base_address + offset_bytes) as *mut u8,
253 size_bytes,
254 zx::sys::ZX_CACHE_FLUSH_DATA,
255 )
256 };
257 zx::Status::ok(status)
258 }
259
260 fn flush_invalidate_cache(
263 &self,
264 offset_bytes: usize,
265 size_bytes: usize,
266 ) -> Result<(), zx::Status> {
267 assert!(offset_bytes + size_bytes <= self.vmo_size_bytes as usize);
268 let status = unsafe {
269 zx::sys::zx_cache_flush(
271 (self.base_address + offset_bytes) as *mut u8,
272 size_bytes,
273 zx::sys::ZX_CACHE_FLUSH_DATA | zx::sys::ZX_CACHE_FLUSH_INVALIDATE,
274 )
275 };
276 zx::Status::ok(status)
277 }
278}
279
280impl Drop for VmoBuffer {
281 fn drop(&mut self) {
282 unsafe {
285 vmar_root_self().unmap(self.base_address, self.vmo_size_bytes as usize).unwrap();
286 }
287 }
288}
289
290#[derive(Error, Debug)]
291pub enum VmoBufferError {
292 #[error(
293 "VMO is too small ({vmo_size_bytes} bytes) to hold ring buffer data ({data_size_bytes} bytes)"
294 )]
295 VmoTooSmall { data_size_bytes: u64, vmo_size_bytes: u64 },
296
297 #[error("Buffer size is invalid; contains incomplete frames")]
298 BufferIncompleteFrames,
299
300 #[error("Failed to memory map VMO: {}", .0)]
301 VmoMap(#[source] zx::Status),
302
303 #[error("Failed to get VMO size: {}", .0)]
304 VmoGetSize(#[source] zx::Status),
305
306 #[error("Failed to flush VMO memory cache: {}", .0)]
307 VmoFlushCache(#[source] zx::Status),
308
309 #[error("Failed to read from VMO: {}", .0)]
310 VmoRead(#[source] zx::Status),
311
312 #[error("Failed to write to VMO: {}", .0)]
313 VmoWrite(#[source] zx::Status),
314}
315
316#[cfg(test)]
317mod test {
318 use super::*;
319 use crate::format::SampleType;
320 use assert_matches::assert_matches;
321
322 #[test]
324 fn affine_function_time_to_frames() {
325 let start_time = zx::MonotonicInstant::from_nanos(1_234_567_890);
326 let current_time = start_time + zx::MonotonicDuration::from_seconds(1);
327
328 let frames_per_second: u32 = 48000;
329 let nanos_per_second: u32 = 1_000_000_000;
330
331 let start_frame: i64 = 1_234_567;
332 let expected_frame = start_frame + (frames_per_second as i64);
333
334 let current_frame = apply_affine_function(
335 current_time.into_nanos(),
336 frames_per_second,
337 nanos_per_second,
338 start_time.into_nanos(),
339 start_frame,
340 );
341
342 assert_eq!(current_frame, expected_frame);
343 }
344
345 #[test]
347 fn affine_function_frames_to_time() {
348 let nanos_per_second: u32 = 1_000_000_000;
349 let frames_per_second: u32 = 48000;
350
351 let start_frame: i64 = 1_234_567;
352 let current_frame: i64 = start_frame + (frames_per_second as i64);
353
354 let start_time = zx::MonotonicInstant::from_nanos(1_234_567_890);
355 let expected_time = start_time + zx::MonotonicDuration::from_seconds(1);
356
357 let time_for_frame = apply_affine_function(
358 current_frame,
359 nanos_per_second,
360 frames_per_second,
361 start_frame,
362 start_time.into_nanos(),
363 );
364
365 assert_eq!(time_for_frame, expected_time.into_nanos());
366 }
367
368 #[test]
371 fn affine_function_rounds_down() {
372 let start_time = zx::MonotonicInstant::from_nanos(1_234_567_890);
373 let current_time = start_time + zx::MonotonicDuration::from_seconds(1)
374 - zx::MonotonicDuration::from_nanos(1);
375
376 let frames_per_second: u32 = 48000;
377 let nanos_per_second: u32 = 1_000_000_000;
378
379 let start_frame: i64 = 1_234_567;
380 let expected_frame = start_frame + (frames_per_second as i64) - 1;
381
382 let current_frame = apply_affine_function(
383 current_time.into_nanos(),
384 frames_per_second,
385 nanos_per_second,
386 start_time.into_nanos(),
387 start_frame,
388 );
389
390 assert_eq!(current_frame, expected_frame);
391 }
392
393 #[test]
398 fn affine_function_rounds_negatively() {
399 let start_time = zx::MonotonicInstant::from_nanos(1_234_567_890);
400 let current_time = start_time
401 - zx::MonotonicDuration::from_seconds(1)
402 - zx::MonotonicDuration::from_nanos(1);
403
404 let frames_per_second: u32 = 48000;
405 let nanos_per_second: u32 = 1_000_000_000;
406
407 let start_frame: i64 = 1_234_567;
408 let expected_frame = start_frame - (frames_per_second as i64) - 1;
409
410 let current_frame = apply_affine_function(
411 current_time.into_nanos(),
412 frames_per_second,
413 nanos_per_second,
414 start_time.into_nanos(),
415 start_frame,
416 );
417
418 assert_eq!(current_frame, expected_frame);
419 }
420
421 #[test]
422 fn vmobuffer_vmo_too_small() {
423 let format =
424 Format { frames_per_second: 48000, sample_type: SampleType::Uint8, channels: 1 };
425
426 let page_size = zx::system_get_page_size() as u64;
428 let num_frames = page_size + 1;
429 let vmo = zx::Vmo::create(page_size).unwrap();
430
431 assert_matches!(
432 VmoBuffer::new(vmo, num_frames, format).err(),
433 Some(VmoBufferError::VmoTooSmall { .. })
434 )
435 }
436
437 #[test]
438 fn vmobuffer_read_write() {
439 let format =
440 Format { frames_per_second: 48000, sample_type: SampleType::Uint8, channels: 2 };
441 const NUM_FRAMES_VMO: u64 = 10;
442 const NUM_FRAMES_BUF: u64 = 5;
443 const SAMPLE: u8 = 42;
444
445 let vmo_size = format.bytes_per_frame() as u64 * NUM_FRAMES_VMO;
446 let buf_size = format.bytes_per_frame() as u64 * NUM_FRAMES_BUF;
447
448 let vmo = zx::Vmo::create(vmo_size).unwrap();
449
450 let mut in_buf = vec![0; buf_size as usize];
452 let out_buf = vec![SAMPLE; buf_size as usize];
454
455 let vmo_buffer = VmoBuffer::new(vmo, NUM_FRAMES_VMO, format).unwrap();
456
457 vmo_buffer.write_to_frame(1, &out_buf).unwrap();
459
460 vmo_buffer.read_from_frame(1, &mut in_buf).unwrap();
462
463 assert_eq!(in_buf, out_buf);
464 }
465
466 #[test]
467 fn vmobuffer_read_write_wrapping() {
468 let format =
469 Format { frames_per_second: 48000, sample_type: SampleType::Uint8, channels: 2 };
470 let page_size = zx::system_get_page_size() as u64;
471 let num_frames_vmo: u64 = page_size;
472 let num_frames_buf: u64 = page_size / 2;
473 const SAMPLE: u8 = 42;
474
475 let vmo_size = format.bytes_per_frame() as u64 * num_frames_vmo;
476 let buf_size = format.bytes_per_frame() as u64 * num_frames_buf;
477
478 let vmo = zx::Vmo::create(vmo_size).unwrap();
479
480 let mut in_buf = vec![0; buf_size as usize];
482 let out_buf = vec![SAMPLE; buf_size as usize];
484
485 let vmo_buffer = VmoBuffer::new(vmo, num_frames_vmo, format).unwrap();
486
487 let frame = num_frames_vmo - 1;
489 assert!(frame + num_frames_buf > num_frames_vmo);
490
491 vmo_buffer.write_to_frame(frame, &out_buf).unwrap();
493
494 vmo_buffer.read_from_frame(frame, &mut in_buf).unwrap();
496
497 assert_eq!(in_buf, out_buf);
498 }
499}