1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 use std::io;
5 use std::mem;
6 use std::os::unix::io::{AsRawFd, RawFd};
7 use std::ptr;
8 use std::ptr::NonNull;
9 use std::slice;
10
11 use libc;
12
13 use cras_sys::gen::{
14 cras_audio_shm_header, cras_server_state, CRAS_NUM_SHM_BUFFERS, CRAS_SHM_BUFFERS_MASK,
15 };
16 use data_model::VolatileRef;
17
18 /// A structure wrapping a fd which contains a shared `cras_audio_shm_header`.
19 /// * `shm_fd` - A shared memory fd contains a `cras_audio_shm_header`
20 pub struct CrasAudioShmHeaderFd {
21 fd: CrasShmFd,
22 }
23
24 impl CrasAudioShmHeaderFd {
25 /// Creates a `CrasAudioShmHeaderFd` by shared memory fd
26 /// # Arguments
27 /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and
28 /// the fd will be closed on drop.
29 ///
30 /// # Returns
31 /// A structure wrapping a `CrasShmFd` with the input fd and `size` which equals to
32 /// the size of `cras_audio_shm_header`.
33 ///
34 /// To use this function safely, we need to make sure
35 /// - The input fd is a valid shared memory fd.
36 /// - The input shared memory fd won't be used by others.
37 /// - The shared memory area in the input fd contains a `cras_audio_shm_header`.
new(fd: libc::c_int) -> Self38 pub unsafe fn new(fd: libc::c_int) -> Self {
39 Self {
40 fd: CrasShmFd::new(fd, mem::size_of::<cras_audio_shm_header>()),
41 }
42 }
43 }
44
45 /// A wrapper for the raw structure `cras_audio_shm_header` with
46 /// size information for the separate audio samples shm area and several
47 /// `VolatileRef` to sub fields for safe access to the header.
48 #[allow(dead_code)]
49 pub struct CrasAudioHeader<'a> {
50 addr: *mut libc::c_void,
51 /// Size of the buffer for samples in CrasAudioBuffer
52 samples_len: usize,
53 used_size: VolatileRef<'a, u32>,
54 frame_size: VolatileRef<'a, u32>,
55 read_buf_idx: VolatileRef<'a, u32>,
56 write_buf_idx: VolatileRef<'a, u32>,
57 read_offset: [VolatileRef<'a, u32>; CRAS_NUM_SHM_BUFFERS as usize],
58 write_offset: [VolatileRef<'a, u32>; CRAS_NUM_SHM_BUFFERS as usize],
59 buffer_offset: [VolatileRef<'a, u32>; CRAS_NUM_SHM_BUFFERS as usize],
60 }
61
62 // It is safe to send audio buffers between threads as this struct has exclusive ownership of the
63 // pointers contained in it.
64 unsafe impl<'a> Send for CrasAudioHeader<'a> {}
65
66 /// An unsafe macro for getting `VolatileRef` for a field from a given NonNull pointer.
67 /// It Supports
68 /// - Nested sub-field
69 /// - Element of an array field
70 ///
71 /// To use this macro safely, we need to
72 /// - Make sure the pointer address is readable and writable for its structure.
73 /// - Make sure all `VolatileRef`s generated from this macro have exclusive ownership for the same
74 /// pointer.
75 #[macro_export]
76 macro_rules! vref_from_addr {
77 ($addr:ident, $($field:ident).*) => {
78 VolatileRef::new(&mut $addr.as_mut().$($field).* as *mut _)
79 };
80
81 ($addr:ident, $field:ident[$idx:tt]) => {
82 VolatileRef::new(&mut $addr.as_mut().$field[$idx] as *mut _)
83 };
84 }
85
86 // Generates error when an index is out of range.
index_out_of_range() -> io::Error87 fn index_out_of_range() -> io::Error {
88 io::Error::new(io::ErrorKind::InvalidInput, "Index out of range.")
89 }
90
91 impl<'a> CrasAudioHeader<'a> {
92 // Creates a `CrasAudioHeader` with given `CrasAudioShmHeaderFd` and `samples_len`
new(header_fd: CrasAudioShmHeaderFd, samples_len: usize) -> io::Result<Self>93 fn new(header_fd: CrasAudioShmHeaderFd, samples_len: usize) -> io::Result<Self> {
94 // Safe because the creator of CrasAudioShmHeaderFd already
95 // ensured that header_fd contains a cras_audio_shm_header.
96 let mmap_addr = unsafe {
97 cras_mmap(
98 header_fd.fd.size,
99 libc::PROT_READ | libc::PROT_WRITE,
100 header_fd.fd.as_raw_fd(),
101 )?
102 };
103
104 let mut addr = NonNull::new(mmap_addr as *mut cras_audio_shm_header)
105 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to create header."))?;
106
107 // Safe because we know that mmap_addr (contained in addr) contains a
108 // cras_audio_shm_header, and the mapped area will be exclusively
109 // owned by this struct.
110 unsafe {
111 Ok(CrasAudioHeader {
112 addr: addr.as_ptr() as *mut libc::c_void,
113 samples_len,
114 used_size: vref_from_addr!(addr, config.used_size),
115 frame_size: vref_from_addr!(addr, config.frame_bytes),
116 read_buf_idx: vref_from_addr!(addr, read_buf_idx),
117 write_buf_idx: vref_from_addr!(addr, write_buf_idx),
118 read_offset: [
119 vref_from_addr!(addr, read_offset[0]),
120 vref_from_addr!(addr, read_offset[1]),
121 ],
122 write_offset: [
123 vref_from_addr!(addr, write_offset[0]),
124 vref_from_addr!(addr, write_offset[1]),
125 ],
126 buffer_offset: [
127 vref_from_addr!(addr, buffer_offset[0]),
128 vref_from_addr!(addr, buffer_offset[1]),
129 ],
130 })
131 }
132 }
133
134 /// Calculates the length of a buffer with the given offset. This length will
135 /// be `used_size`, unless the offset is closer than `used_size` to the end
136 /// of samples, in which case the length will be as long as possible.
137 ///
138 /// If that buffer length is invalid (too small to hold a frame of audio data),
139 /// then returns an error.
140 /// The returned buffer length will be rounded down to a multiple of `frame_size`.
buffer_len_from_offset(&self, offset: usize) -> io::Result<usize>141 fn buffer_len_from_offset(&self, offset: usize) -> io::Result<usize> {
142 if offset > self.samples_len {
143 return Err(io::Error::new(
144 io::ErrorKind::InvalidInput,
145 format!(
146 "Buffer offset {} exceeds the length of samples area ({}).",
147 offset, self.samples_len
148 ),
149 ));
150 }
151
152 let used_size = self.get_used_size();
153 let frame_size = self.get_frame_size();
154
155 // We explicitly allow a buffer shorter than used_size, but only
156 // at the end of the samples area.
157 // This is useful if we're playing a file where the number of samples is
158 // not a multiple of used_size (meaning the length of the samples area
159 // won't be either). Then, the last buffer played will be smaller than
160 // used_size.
161 let mut buffer_length = used_size.min(self.samples_len - offset);
162 if buffer_length < frame_size {
163 return Err(io::Error::new(
164 io::ErrorKind::InvalidInput,
165 format!(
166 "Buffer offset {} gives buffer length {} smaller than frame size {}.",
167 offset, buffer_length, frame_size
168 ),
169 ));
170 }
171
172 // Round buffer_length down to a multiple of frame size
173 buffer_length = buffer_length / frame_size * frame_size;
174 Ok(buffer_length)
175 }
176
177 /// Gets the base of the write buffer and the writable length (rounded to `frame_size`).
178 /// Does not take into account the write offset.
179 ///
180 /// # Returns
181 ///
182 /// * (`usize`, `usize`) - write buffer base as an offset from the start of
183 /// the samples area and buffer length in bytes.
get_write_offset_and_len(&self) -> io::Result<(usize, usize)>184 pub fn get_write_offset_and_len(&self) -> io::Result<(usize, usize)> {
185 let idx = self.get_write_buf_idx() as usize;
186 let offset = self.get_buffer_offset(idx)?;
187 let len = self.buffer_len_from_offset(offset)?;
188
189 Ok((offset, len))
190 }
191
192 /// Gets the buffer offset of the read buffer.
193 ///
194 /// # Returns
195 ///
196 /// * `usize` - read offset in bytes
get_read_buffer_offset(&self) -> io::Result<usize>197 pub fn get_read_buffer_offset(&self) -> io::Result<usize> {
198 let idx = self.get_read_buf_idx() as usize;
199 self.get_buffer_offset(idx)
200 }
201
202 /// Gets the offset of a buffer from the start of samples.
203 ///
204 /// # Arguments
205 /// `index` - 0 <= `index` < `CRAS_NUM_SHM_BUFFERS`. The index of the buffer
206 /// for which we want the `buffer_offset`.
207 ///
208 /// # Returns
209 /// * `usize` - buffer offset in bytes
get_buffer_offset(&self, idx: usize) -> io::Result<usize>210 fn get_buffer_offset(&self, idx: usize) -> io::Result<usize> {
211 let buffer_offset = self
212 .buffer_offset
213 .get(idx)
214 .ok_or_else(index_out_of_range)?
215 .load() as usize;
216 self.check_buffer_offset(idx, buffer_offset)?;
217 Ok(buffer_offset)
218 }
219
220 /// Gets the number of bytes per frame from the shared memory structure.
221 ///
222 /// # Returns
223 ///
224 /// * `usize` - Number of bytes per frame
get_frame_size(&self) -> usize225 pub fn get_frame_size(&self) -> usize {
226 self.frame_size.load() as usize
227 }
228
229 /// Gets the size in bytes of the shared memory buffer.
get_used_size(&self) -> usize230 fn get_used_size(&self) -> usize {
231 self.used_size.load() as usize
232 }
233
234 /// Gets the index of the current written buffer.
235 ///
236 /// # Returns
237 /// `u32` - the returned index is less than `CRAS_NUM_SHM_BUFFERS`.
get_write_buf_idx(&self) -> u32238 fn get_write_buf_idx(&self) -> u32 {
239 self.write_buf_idx.load() & CRAS_SHM_BUFFERS_MASK
240 }
241
get_read_buf_idx(&self) -> u32242 fn get_read_buf_idx(&self) -> u32 {
243 self.read_buf_idx.load() & CRAS_SHM_BUFFERS_MASK
244 }
245
246 /// Switches the written buffer.
switch_write_buf_idx(&mut self)247 fn switch_write_buf_idx(&mut self) {
248 self.write_buf_idx
249 .store(self.get_write_buf_idx() as u32 ^ 1u32)
250 }
251
252 /// Switches the buffer to read.
switch_read_buf_idx(&mut self)253 fn switch_read_buf_idx(&mut self) {
254 self.read_buf_idx
255 .store(self.get_read_buf_idx() as u32 ^ 1u32)
256 }
257
258 /// Checks if the offset value for setting write_offset or read_offset is
259 /// out of range or not.
260 ///
261 /// # Arguments
262 /// `idx` - The index of the buffer for which we're checking the offset.
263 /// `offset` - 0 <= `offset` <= `used_size` && `buffer_offset[idx]` + `offset` <=
264 /// `samples_len`. Writable or readable size equals to 0 when offset equals
265 /// to `used_size`.
266 ///
267 /// # Errors
268 /// Returns an error if `offset` is out of range or if idx is not a valid
269 /// buffer idx.
check_rw_offset(&self, idx: usize, offset: u32) -> io::Result<()>270 fn check_rw_offset(&self, idx: usize, offset: u32) -> io::Result<()> {
271 let buffer_len = self.buffer_len_from_offset(self.get_buffer_offset(idx)?)?;
272 if offset as usize > buffer_len {
273 return Err(io::Error::new(
274 io::ErrorKind::InvalidInput,
275 format!(
276 "Offset {} is larger than buffer size {}.",
277 offset, buffer_len
278 ),
279 ));
280 }
281
282 Ok(())
283 }
284
285 /// Sets `write_offset[idx]` to the count of written bytes.
286 ///
287 /// # Arguments
288 /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS`
289 /// `offset` - 0 <= `offset` <= `used_size` && `offset` + `used_size` <=
290 /// `samples_len`. Writable size equals to 0 when offset equals to
291 /// `used_size`.
292 ///
293 /// # Errors
294 /// Returns an error if `offset` is out of range.
set_write_offset(&mut self, idx: usize, offset: u32) -> io::Result<()>295 fn set_write_offset(&mut self, idx: usize, offset: u32) -> io::Result<()> {
296 self.check_rw_offset(idx, offset)?;
297 let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?;
298 write_offset.store(offset);
299 Ok(())
300 }
301
302 /// Sets `read_offset[idx]` of to count of written bytes.
303 ///
304 /// # Arguments
305 /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS`
306 /// `offset` - 0 <= `offset` <= `used_size` && `offset` + `used_size` <=
307 /// `samples_len`. Readable size equals to 0 when offset equals to
308 /// `used_size`.
309 ///
310 /// # Errors
311 /// Returns error if index out of range.
set_read_offset(&mut self, idx: usize, offset: u32) -> io::Result<()>312 fn set_read_offset(&mut self, idx: usize, offset: u32) -> io::Result<()> {
313 self.check_rw_offset(idx, offset)?;
314 let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?;
315 read_offset.store(offset);
316 Ok(())
317 }
318
319 /// Check that `offset` is a valid buffer offset for the buffer at `idx`
320 /// An offset is not valid if it is
321 /// * outside of the samples area
322 /// * overlaps some other buffer `[other_offset, other_offset + used_size)`
323 /// * is close enough to the end of the samples area that the buffer would
324 /// be shorter than `frame_size`.
check_buffer_offset(&self, idx: usize, offset: usize) -> io::Result<()>325 fn check_buffer_offset(&self, idx: usize, offset: usize) -> io::Result<()> {
326 let start = offset;
327 let end = start + self.buffer_len_from_offset(start)?;
328
329 let other_idx = (idx ^ 1) as usize;
330 let other_start = self
331 .buffer_offset
332 .get(other_idx)
333 .ok_or_else(index_out_of_range)?
334 .load() as usize;
335 let other_end = other_start + self.buffer_len_from_offset(other_start)?;
336 if start < other_end && other_start < end {
337 return Err(io::Error::new(
338 io::ErrorKind::InvalidInput,
339 format!(
340 "Setting buffer {} to [{}, {}) overlaps buffer {} at [{}, {})",
341 idx, start, end, other_idx, other_start, other_end,
342 ),
343 ));
344 }
345 Ok(())
346 }
347
348 /// Sets the location of the audio buffer `idx` within the samples area to
349 /// `offset`, so that CRAS will read/write samples for that buffer from that
350 /// offset.
351 ///
352 /// # Arguments
353 /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS`
354 /// `offset` - 0 <= `offset` && `offset` + `frame_size` <= `samples_len`
355 ///
356 /// # Errors
357 /// If `idx` is out of range
358 /// If the offset is invalid, which can happen if `offset` is
359 /// * outside of the samples area
360 /// * overlaps some other buffer `[other_offset, other_offset + used_size)`
361 /// * is close enough to the end of the samples area that the buffer would
362 /// be shorter than `frame_size`.
set_buffer_offset(&mut self, idx: usize, offset: usize) -> io::Result<()>363 fn set_buffer_offset(&mut self, idx: usize, offset: usize) -> io::Result<()> {
364 self.check_buffer_offset(idx, offset)?;
365
366 let buffer_offset = self.buffer_offset.get(idx).ok_or_else(index_out_of_range)?;
367 buffer_offset.store(offset as u32);
368 Ok(())
369 }
370
371 /// Commits written frames by switching the current buffer to the other one
372 /// after samples are ready and indexes of current buffer are all set.
373 /// - Sets `write_offset` of current buffer to `frame_count * frame_size`
374 /// - Sets `read_offset` of current buffer to `0`.
375 ///
376 /// # Arguments
377 ///
378 /// * `frame_count` - Number of frames written to the current buffer
379 ///
380 /// # Errors
381 ///
382 /// * Returns error if `frame_count` is larger than buffer size
383 ///
384 /// This function is safe because we switch `write_buf_idx` after letting
385 /// `write_offset` and `read_offset` ready and we read / write shared memory
386 /// variables with volatile operations.
commit_written_frames(&mut self, frame_count: u32) -> io::Result<()>387 pub fn commit_written_frames(&mut self, frame_count: u32) -> io::Result<()> {
388 // Uses `u64` to prevent possible overflow
389 let byte_count = frame_count as u64 * self.get_frame_size() as u64;
390 if byte_count > self.get_used_size() as u64 {
391 Err(io::Error::new(
392 io::ErrorKind::InvalidInput,
393 "frame_count * frame_size is larger than used_size",
394 ))
395 } else {
396 let idx = self.get_write_buf_idx() as usize;
397 // Sets `write_offset` of current buffer to frame_count * frame_size
398 self.set_write_offset(idx, byte_count as u32)?;
399 // Sets `read_offset` of current buffer to `0`.
400 self.set_read_offset(idx, 0)?;
401 // Switch to the other buffer
402 self.switch_write_buf_idx();
403 Ok(())
404 }
405 }
406
407 /// Get readable frames in current buffer.
408 ///
409 /// # Returns
410 ///
411 /// * `usize` - number of readable frames.
412 ///
413 /// # Errors
414 ///
415 /// Returns error if index out of range.
get_readable_frames(&self) -> io::Result<usize>416 pub fn get_readable_frames(&self) -> io::Result<usize> {
417 let idx = self.get_read_buf_idx() as usize;
418 let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?;
419 let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?;
420 let nframes =
421 (write_offset.load() as i32 - read_offset.load() as i32) / self.get_frame_size() as i32;
422 if nframes < 0 {
423 Ok(0)
424 } else {
425 Ok(nframes as usize)
426 }
427 }
428
429 /// Commit read frames from reader, .
430 /// - Sets `read_offset` of current buffer to `read_offset + frame_count * frame_size`.
431 /// If `read_offset` is larger than or equal to `write_offset`, then
432 /// - Sets `read_offset` and `write_offset` to `0` and switch `read_buf_idx`.
433 ///
434 /// # Arguments
435 ///
436 /// * `frame_count` - Read frames in current read buffer.
437 ///
438 /// # Errors
439 ///
440 /// Returns error if index out of range.
commit_read_frames(&mut self, frame_count: u32) -> io::Result<()>441 pub fn commit_read_frames(&mut self, frame_count: u32) -> io::Result<()> {
442 let idx = self.get_read_buf_idx() as usize;
443 let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?;
444 let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?;
445 read_offset.store(read_offset.load() + frame_count * self.get_frame_size() as u32);
446 if read_offset.load() >= write_offset.load() {
447 read_offset.store(0);
448 write_offset.store(0);
449 self.switch_read_buf_idx();
450 }
451 Ok(())
452 }
453 }
454
455 impl<'a> Drop for CrasAudioHeader<'a> {
drop(&mut self)456 fn drop(&mut self) {
457 // Safe because all references must be gone by the time drop is called.
458 unsafe {
459 libc::munmap(self.addr as *mut _, mem::size_of::<cras_audio_shm_header>());
460 }
461 }
462 }
463
464 // To use this safely, we need to make sure
465 // - The given fd contains valid space which is larger than `len` + `offset`
cras_mmap_offset( len: usize, prot: libc::c_int, fd: libc::c_int, offset: usize, ) -> io::Result<*mut libc::c_void>466 unsafe fn cras_mmap_offset(
467 len: usize,
468 prot: libc::c_int,
469 fd: libc::c_int,
470 offset: usize,
471 ) -> io::Result<*mut libc::c_void> {
472 if offset > libc::off_t::max_value() as usize {
473 return Err(io::Error::new(
474 io::ErrorKind::InvalidInput,
475 "Requested offset is out of range of `libc::off_t`.",
476 ));
477 }
478 // It's safe because we handle its returned results.
479 match libc::mmap(
480 ptr::null_mut(),
481 len,
482 prot,
483 libc::MAP_SHARED,
484 fd,
485 offset as libc::off_t,
486 ) {
487 libc::MAP_FAILED => Err(io::Error::last_os_error()),
488 shm_ptr => Ok(shm_ptr),
489 }
490 }
491
492 // To use this safely, we need to make sure
493 // - The given fd contains valid space which is larger than `len`
cras_mmap( len: usize, prot: libc::c_int, fd: libc::c_int, ) -> io::Result<*mut libc::c_void>494 unsafe fn cras_mmap(
495 len: usize,
496 prot: libc::c_int,
497 fd: libc::c_int,
498 ) -> io::Result<*mut libc::c_void> {
499 cras_mmap_offset(len, prot, fd, 0)
500 }
501
502 /// A structure that points to RO shared memory area - `cras_server_state`
503 /// The structure is created from a shared memory fd which contains the structure.
504 #[allow(dead_code)]
505 pub struct CrasServerState {
506 addr: *mut libc::c_void,
507 size: usize,
508 }
509
510 impl CrasServerState {
511 /// An unsafe function for creating `CrasServerState`. To use this function safely, we need to
512 /// - Make sure that the `shm_fd` must come from the server's message that provides the shared
513 /// memory region. The Id for the message is `CRAS_CLIENT_MESSAGE_ID::CRAS_CLIENT_CONNECTED`.
514 #[allow(dead_code)]
new(shm_fd: CrasShmFd) -> io::Result<Self>515 pub unsafe fn new(shm_fd: CrasShmFd) -> io::Result<Self> {
516 let size = mem::size_of::<cras_server_state>();
517 if size > shm_fd.size {
518 Err(io::Error::new(
519 io::ErrorKind::InvalidInput,
520 "Invalid shared memory size.",
521 ))
522 } else {
523 let addr = cras_mmap(size, libc::PROT_READ, shm_fd.as_raw_fd())?;
524 Ok(CrasServerState { addr, size })
525 }
526 }
527
528 // Gets `cras_server_state` reference from the structure.
529 #[allow(dead_code)]
get_ref(&self) -> VolatileRef<cras_server_state>530 fn get_ref(&self) -> VolatileRef<cras_server_state> {
531 unsafe { VolatileRef::new(self.addr as *mut _) }
532 }
533 }
534
535 impl Drop for CrasServerState {
536 /// Call `munmap` for `addr`.
drop(&mut self)537 fn drop(&mut self) {
538 unsafe {
539 // Safe because all references must be gone by the time drop is called.
540 libc::munmap(self.addr, self.size);
541 }
542 }
543 }
544
545 /// A structure holding the mapped shared memory area used to exchange
546 /// samples with CRAS. The shared memory is owned exclusively by this structure,
547 /// and will be cleaned up on drop.
548 /// * `addr` - The address of the mapped shared memory.
549 /// * `len` - Length of the mapped shared memory in bytes.
550 pub struct CrasAudioBuffer {
551 addr: *mut u8,
552 len: usize,
553 }
554
555 // It is safe to send audio buffers between threads as this struct has exclusive ownership of the
556 // shared memory area contained in it.
557 unsafe impl Send for CrasAudioBuffer {}
558
559 impl CrasAudioBuffer {
new(samples_fd: CrasShmFd) -> io::Result<Self>560 fn new(samples_fd: CrasShmFd) -> io::Result<Self> {
561 // This is safe because we checked that the size of the shm in samples_fd
562 // was at least samples_fd.size when it was created.
563 let addr = unsafe {
564 cras_mmap(
565 samples_fd.size,
566 libc::PROT_READ | libc::PROT_WRITE,
567 samples_fd.as_raw_fd(),
568 )? as *mut u8
569 };
570 Ok(Self {
571 addr,
572 len: samples_fd.size,
573 })
574 }
575
576 /// Provides a mutable slice to be filled with audio samples.
get_buffer(&mut self) -> &mut [u8]577 pub fn get_buffer(&mut self) -> &mut [u8] {
578 // This is safe because it takes a mutable reference to self, and there can only be one
579 // taken at a time. Although this is shared memory, the reader side must have it mapped as
580 // read only.
581 unsafe { slice::from_raw_parts_mut(self.addr, self.len) }
582 }
583 }
584
585 impl Drop for CrasAudioBuffer {
drop(&mut self)586 fn drop(&mut self) {
587 // Safe because all references must be gone by the time drop is called.
588 unsafe {
589 libc::munmap(self.addr as *mut _, self.len);
590 }
591 }
592 }
593
594 /// Creates header and buffer from given shared memory fds.
create_header_and_buffers<'a>( header_fd: CrasAudioShmHeaderFd, samples_fd: CrasShmFd, ) -> io::Result<(CrasAudioHeader<'a>, CrasAudioBuffer)>595 pub fn create_header_and_buffers<'a>(
596 header_fd: CrasAudioShmHeaderFd,
597 samples_fd: CrasShmFd,
598 ) -> io::Result<(CrasAudioHeader<'a>, CrasAudioBuffer)> {
599 let header = CrasAudioHeader::new(header_fd, samples_fd.size)?;
600 let buffer = CrasAudioBuffer::new(samples_fd)?;
601
602 Ok((header, buffer))
603 }
604
605 /// A structure wrapping a fd which contains a shared memory area and its size.
606 /// * `fd` - The shared memory file descriptor, a `libc::c_int`.
607 /// * `size` - Size of the shared memory area.
608 pub struct CrasShmFd {
609 fd: libc::c_int,
610 size: usize,
611 }
612
613 impl CrasShmFd {
614 /// Creates a `CrasShmFd` by shared memory fd and size
615 /// # Arguments
616 /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and
617 /// the fd will be closed on drop.
618 /// * `size` - Size of the shared memory.
619 ///
620 /// # Returns
621 /// * `CrasShmFd` - Wrap the input arguments without doing anything.
622 ///
623 /// To use this function safely, we need to make sure
624 /// - The input fd is a valid shared memory fd.
625 /// - The input shared memory fd won't be used by others.
626 /// - The input fd contains memory size larger than `size`.
new(fd: libc::c_int, size: usize) -> CrasShmFd627 pub unsafe fn new(fd: libc::c_int, size: usize) -> CrasShmFd {
628 CrasShmFd { fd, size }
629 }
630 }
631
632 impl AsRawFd for CrasShmFd {
as_raw_fd(&self) -> RawFd633 fn as_raw_fd(&self) -> RawFd {
634 self.fd
635 }
636 }
637
638 impl Drop for CrasShmFd {
drop(&mut self)639 fn drop(&mut self) {
640 // It's safe here if we make sure
641 // - the input fd is valid and
642 // - `CrasShmFd` is the only owner
643 // in `new` function
644 unsafe {
645 libc::close(self.fd);
646 }
647 }
648 }
649
650 /// A structure wrapping a fd which contains a shared `cras_server_state`.
651 /// * `shm_fd` - A shared memory fd contains a `cras_server_state`
652 pub struct CrasServerStateShmFd {
653 #[allow(dead_code)]
654 shm_fd: CrasShmFd,
655 }
656
657 impl CrasServerStateShmFd {
658 /// Creates a `CrasServerStateShmFd` by shared memory fd
659 /// # Arguments
660 /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and
661 /// the fd will be closed on drop.
662 ///
663 /// # Returns
664 /// A structure wrapping a `CrasShmFd` with the input fd and `size` which equals to
665 /// the size of `cras_server_sate`.
666 ///
667 /// To use this function safely, we need to make sure
668 /// - The input fd is a valid shared memory fd.
669 /// - The input shared memory fd won't be used by others.
670 /// - The shared memory area in the input fd contains a `cras_server_state`.
new(fd: libc::c_int) -> Self671 pub unsafe fn new(fd: libc::c_int) -> Self {
672 Self {
673 shm_fd: CrasShmFd::new(fd, mem::size_of::<cras_server_state>()),
674 }
675 }
676 }
677
678 #[cfg(test)]
679 mod tests {
680 use super::*;
681 use std::fs::File;
682 use std::os::unix::io::IntoRawFd;
683 use sys_util::{kernel_has_memfd, SharedMemory};
684
685 #[test]
cras_audio_header_switch_test()686 fn cras_audio_header_switch_test() {
687 if !kernel_has_memfd() {
688 return;
689 }
690 let mut header = create_cras_audio_header(20);
691 assert_eq!(0, header.get_write_buf_idx());
692 header.switch_write_buf_idx();
693 assert_eq!(1, header.get_write_buf_idx());
694 }
695
696 #[test]
cras_audio_header_write_offset_test()697 fn cras_audio_header_write_offset_test() {
698 if !kernel_has_memfd() {
699 return;
700 }
701 let mut header = create_cras_audio_header(20);
702 header.frame_size.store(2);
703 header.used_size.store(5);
704 header.set_buffer_offset(0, 12).unwrap();
705
706 assert_eq!(0, header.write_offset[0].load());
707 // Index out of bound
708 assert!(header.set_write_offset(2, 5).is_err());
709 // Offset out of bound
710 // Buffer length is 4, since that's the largest multiple of frame_size
711 // less than used_size.
712 assert!(header.set_write_offset(0, 6).is_err());
713 assert_eq!(0, header.write_offset[0].load());
714 assert!(header.set_write_offset(0, 5).is_err());
715 assert_eq!(0, header.write_offset[0].load());
716 assert!(header.set_write_offset(0, 4).is_ok());
717 assert_eq!(4, header.write_offset[0].load());
718 }
719
720 #[test]
cras_audio_header_read_offset_test()721 fn cras_audio_header_read_offset_test() {
722 if !kernel_has_memfd() {
723 return;
724 }
725 let mut header = create_cras_audio_header(20);
726 header.frame_size.store(2);
727 header.used_size.store(5);
728 header.set_buffer_offset(0, 12).unwrap();
729
730 assert_eq!(0, header.read_offset[0].load());
731 // Index out of bound
732 assert!(header.set_read_offset(2, 5).is_err());
733 // Offset out of bound
734 // Buffer length is 4, since that's the largest multiple of frame_size
735 // less than used_size.
736 assert!(header.set_read_offset(0, 6).is_err());
737 assert_eq!(0, header.read_offset[0].load());
738 assert!(header.set_read_offset(0, 5).is_err());
739 assert_eq!(0, header.read_offset[0].load());
740 assert!(header.set_read_offset(0, 4).is_ok());
741 assert_eq!(4, header.read_offset[0].load());
742 }
743
744 #[test]
cras_audio_header_commit_written_frame_test()745 fn cras_audio_header_commit_written_frame_test() {
746 if !kernel_has_memfd() {
747 return;
748 }
749 let mut header = create_cras_audio_header(20);
750 header.frame_size.store(2);
751 header.used_size.store(10);
752 header.read_offset[0].store(10);
753 header.set_buffer_offset(0, 10).unwrap();
754
755 assert!(header.commit_written_frames(5).is_ok());
756 assert_eq!(header.write_offset[0].load(), 10);
757 assert_eq!(header.read_offset[0].load(), 0);
758 assert_eq!(header.write_buf_idx.load(), 1);
759 }
760
761 #[test]
cras_audio_header_get_readable_frames_test()762 fn cras_audio_header_get_readable_frames_test() {
763 if !kernel_has_memfd() {
764 return;
765 }
766 let header = create_cras_audio_header(20);
767 header.frame_size.store(2);
768 header.used_size.store(10);
769 header.read_offset[0].store(2);
770 header.write_offset[0].store(10);
771 let frames = header
772 .get_readable_frames()
773 .expect("Failed to get readable frames.");
774 assert_eq!(frames, 4);
775 }
776
777 #[test]
cras_audio_header_commit_read_frames_test()778 fn cras_audio_header_commit_read_frames_test() {
779 if !kernel_has_memfd() {
780 return;
781 }
782 let mut header = create_cras_audio_header(20);
783 header.frame_size.store(2);
784 header.used_size.store(10);
785 header.read_offset[0].store(2);
786 header.write_offset[0].store(10);
787 header
788 .commit_read_frames(3)
789 .expect("Failed to commit read frames.");
790 assert_eq!(header.get_read_buf_idx(), 0);
791 assert_eq!(header.read_offset[0].load(), 8);
792
793 header
794 .commit_read_frames(1)
795 .expect("Failed to commit read frames.");
796 // Read buffer should be switched
797 assert_eq!(header.get_read_buf_idx(), 1);
798 assert_eq!(header.read_offset[0].load(), 0);
799 assert_eq!(header.read_offset[0].load(), 0);
800 }
801
802 #[test]
cras_audio_header_get_write_offset_and_len()803 fn cras_audio_header_get_write_offset_and_len() {
804 if !kernel_has_memfd() {
805 return;
806 }
807 let header = create_cras_audio_header(30);
808 header.frame_size.store(2);
809 header.used_size.store(10);
810 header.write_buf_idx.store(0);
811 header.read_offset[0].store(0);
812 header.write_offset[0].store(0);
813 header.buffer_offset[0].store(0);
814
815 header.read_buf_idx.store(1);
816 header.read_offset[1].store(0);
817 header.write_offset[1].store(0);
818 header.buffer_offset[1].store(10);
819
820 // standard offsets and lens
821 let (offset, len) = header.get_write_offset_and_len().unwrap();
822 assert_eq!(offset, 0);
823 assert_eq!(len, 10);
824
825 header.write_buf_idx.store(1);
826 header.read_buf_idx.store(0);
827 let (offset, len) = header.get_write_offset_and_len().unwrap();
828 assert_eq!(offset, 10);
829 assert_eq!(len, 10);
830
831 // relocate buffer offsets
832 header.buffer_offset[1].store(16);
833 let (offset, len) = header.get_write_offset_and_len().unwrap();
834 assert_eq!(offset, 16);
835 assert_eq!(len, 10);
836
837 header.buffer_offset[0].store(5);
838 header.write_buf_idx.store(0);
839 let (offset, len) = header.get_write_offset_and_len().unwrap();
840 assert_eq!(offset, 5);
841 assert_eq!(len, 10);
842
843 header.write_buf_idx.store(0);
844 header.buffer_offset[0].store(2);
845 header.read_buf_idx.store(1);
846 header.buffer_offset[1].store(10);
847 let result = header.get_write_offset_and_len();
848 // Should be an error as write buffer would overrun into other buffer.
849 assert!(result.is_err());
850
851 header.buffer_offset[0].store(24);
852 header.buffer_offset[1].store(10);
853 let (offset, len) = header.get_write_offset_and_len().unwrap();
854 // Should be ok since we're only running up against the end of samples.
855 assert_eq!(offset, 24);
856 assert_eq!(len, 6);
857
858 header.buffer_offset[0].store(25);
859 let (offset, len) = header.get_write_offset_and_len().unwrap();
860 // Should be ok, but we'll truncate len to frame_size.
861 assert_eq!(offset, 25);
862 assert_eq!(len, 4);
863
864 header.buffer_offset[0].store(29);
865 let result = header.get_write_offset_and_len();
866 // Should be an error as buffer is smaller than frame_size.
867 assert!(result.is_err());
868 }
869
870 #[test]
cras_audio_header_set_buffer_offset()871 fn cras_audio_header_set_buffer_offset() {
872 if !kernel_has_memfd() {
873 return;
874 }
875 let mut header = create_cras_audio_header(30);
876 header.frame_size.store(2);
877 header.used_size.store(10);
878 header.write_buf_idx.store(0);
879 header.read_offset[0].store(0);
880 header.write_offset[0].store(0);
881 header.buffer_offset[0].store(0);
882
883 header.read_buf_idx.store(1);
884 header.read_offset[1].store(0);
885 header.write_offset[1].store(0);
886 header.buffer_offset[1].store(10);
887
888 // Setting buffer_offset to overlap with other buffer is not okay
889 assert!(header.set_buffer_offset(0, 10).is_err());
890
891 header.buffer_offset[0].store(0);
892 header.write_offset[1].store(8);
893 // With samples, it's still an error.
894 assert!(header.set_buffer_offset(0, 10).is_err());
895
896 // Setting the offset past the end of the other buffer is okay
897 assert!(header.set_buffer_offset(0, 20).is_ok());
898
899 // Setting buffer offset such that buffer length is less than used_size
900 // is okay, but only at the end of the samples area.
901 assert!(header.set_buffer_offset(0, 21).is_ok());
902 assert!(header.set_buffer_offset(0, 27).is_ok());
903
904 // It's not okay if we get a buffer with length less than frame_size.
905 assert!(header.set_buffer_offset(0, 29).is_err());
906 assert!(header.set_buffer_offset(0, 30).is_err());
907
908 // If we try to overlap another buffer with that other buffer at the end,
909 // it's not okay.
910 assert!(header.set_buffer_offset(1, 25).is_err());
911 assert!(header.set_buffer_offset(1, 27).is_err());
912 assert!(header.set_buffer_offset(1, 28).is_err());
913
914 // Setting buffer offset past the end of samples is an error.
915 assert!(header.set_buffer_offset(0, 33).is_err());
916 }
917
918 #[test]
create_header_and_buffers_test()919 fn create_header_and_buffers_test() {
920 if !kernel_has_memfd() {
921 return;
922 }
923 let header_fd = cras_audio_header_fd();
924 let samples_fd = cras_audio_samples_fd(20);
925 let res = create_header_and_buffers(header_fd, samples_fd);
926 res.expect("Failed to create header and buffer.");
927 }
928
create_shm(size: usize) -> File929 fn create_shm(size: usize) -> File {
930 let mut shm = SharedMemory::new(None).expect("failed to create shm");
931 shm.set_size(size as u64).expect("failed to set shm size");
932 shm.into()
933 }
934
create_cras_audio_header<'a>(samples_len: usize) -> CrasAudioHeader<'a>935 fn create_cras_audio_header<'a>(samples_len: usize) -> CrasAudioHeader<'a> {
936 CrasAudioHeader::new(cras_audio_header_fd(), samples_len).unwrap()
937 }
938
cras_audio_header_fd() -> CrasAudioShmHeaderFd939 fn cras_audio_header_fd() -> CrasAudioShmHeaderFd {
940 let size = mem::size_of::<cras_audio_shm_header>();
941 let shm = create_shm(size);
942 unsafe { CrasAudioShmHeaderFd::new(shm.into_raw_fd()) }
943 }
944
cras_audio_samples_fd(size: usize) -> CrasShmFd945 fn cras_audio_samples_fd(size: usize) -> CrasShmFd {
946 let shm = create_shm(size);
947 unsafe { CrasShmFd::new(shm.into_raw_fd(), size) }
948 }
949
950 #[test]
cras_mmap_pass()951 fn cras_mmap_pass() {
952 if !kernel_has_memfd() {
953 return;
954 }
955 let shm = create_shm(100);
956 let rc = unsafe { cras_mmap(10, libc::PROT_READ, shm.as_raw_fd()) };
957 assert!(rc.is_ok());
958 unsafe { libc::munmap(rc.unwrap(), 10) };
959 }
960
961 #[test]
cras_mmap_failed()962 fn cras_mmap_failed() {
963 if !kernel_has_memfd() {
964 return;
965 }
966 let rc = unsafe { cras_mmap(10, libc::PROT_READ, -1) };
967 assert!(rc.is_err());
968 }
969 }
970