summaryrefslogtreecommitdiff
path: root/cras/client/libcras/src/cras_shm.rs
diff options
context:
space:
mode:
authorAndrew Walbran <qwandor@google.com>2021-08-03 14:20:19 +0000
committerAndrew Walbran <qwandor@google.com>2021-08-03 14:20:19 +0000
commit8dce65084f73d40bb081312769a24b4bd533f667 (patch)
treee2118ad5dbee0370c6ab114bb3974cd9f09a2251 /cras/client/libcras/src/cras_shm.rs
parentbcf1f249f11b6865cff3f0d3f0ae5801e67e0e7e (diff)
downloadadhd-android-s-qpr3-beta-1.tar.gz
This repository will be removed from the manifest change, but Treehugger seems unable to test the manifest change, so this change first removes all files so we can test that instead. Bug: 190503456 Test: m crosvm Change-Id: I133ef3bd8b39035a68113c4da8fe4c637a40daac
Diffstat (limited to 'cras/client/libcras/src/cras_shm.rs')
-rw-r--r--cras/client/libcras/src/cras_shm.rs1308
1 files changed, 0 insertions, 1308 deletions
diff --git a/cras/client/libcras/src/cras_shm.rs b/cras/client/libcras/src/cras_shm.rs
deleted file mode 100644
index 05533753..00000000
--- a/cras/client/libcras/src/cras_shm.rs
+++ /dev/null
@@ -1,1308 +0,0 @@
-// Copyright 2019 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-use std::convert::TryFrom;
-use std::io;
-use std::mem;
-use std::os::unix::io::{AsRawFd, RawFd};
-use std::ptr;
-use std::ptr::NonNull;
-use std::slice;
-use std::sync::atomic::{self, Ordering};
-use std::thread;
-
-use cras_sys::gen::{
- audio_dev_debug_info, audio_stream_debug_info, cras_audio_shm_header, cras_iodev_info,
- cras_ionode_info, cras_server_state, CRAS_MAX_IODEVS, CRAS_MAX_IONODES, CRAS_NUM_SHM_BUFFERS,
- CRAS_SERVER_STATE_VERSION, CRAS_SHM_BUFFERS_MASK, MAX_DEBUG_DEVS, MAX_DEBUG_STREAMS,
-};
-use cras_sys::{
- AudioDebugInfo, AudioDevDebugInfo, AudioStreamDebugInfo, CrasIodevInfo, CrasIonodeInfo,
-};
-use data_model::{VolatileRef, VolatileSlice};
-use sys_util::warn;
-
-/// A structure wrapping a fd which contains a shared `cras_audio_shm_header`.
-/// * `shm_fd` - A shared memory fd contains a `cras_audio_shm_header`
-pub struct CrasAudioShmHeaderFd {
- fd: CrasShmFd,
-}
-
-impl CrasAudioShmHeaderFd {
- /// Creates a `CrasAudioShmHeaderFd` by shared memory fd
- /// # Arguments
- /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and
- /// the fd will be closed on drop.
- ///
- /// # Returns
- /// A structure wrapping a `CrasShmFd` with the input fd and `size` which equals to
- /// the size of `cras_audio_shm_header`.
- ///
- /// To use this function safely, we need to make sure
- /// - The input fd is a valid shared memory fd.
- /// - The input shared memory fd won't be used by others.
- /// - The shared memory area in the input fd contains a `cras_audio_shm_header`.
- pub unsafe fn new(fd: libc::c_int) -> Self {
- Self {
- fd: CrasShmFd::new(fd, mem::size_of::<cras_audio_shm_header>()),
- }
- }
-}
-
-/// A wrapper for the raw structure `cras_audio_shm_header` with
-/// size information for the separate audio samples shm area and several
-/// `VolatileRef` to sub fields for safe access to the header.
-pub struct CrasAudioHeader<'a> {
- addr: *mut libc::c_void,
- /// Size of the buffer for samples in CrasAudioBuffer
- samples_len: usize,
- used_size: VolatileRef<'a, u32>,
- frame_size: VolatileRef<'a, u32>,
- read_buf_idx: VolatileRef<'a, u32>,
- write_buf_idx: VolatileRef<'a, u32>,
- read_offset: [VolatileRef<'a, u32>; CRAS_NUM_SHM_BUFFERS as usize],
- write_offset: [VolatileRef<'a, u32>; CRAS_NUM_SHM_BUFFERS as usize],
- buffer_offset: [VolatileRef<'a, u64>; CRAS_NUM_SHM_BUFFERS as usize],
-}
-
-// It is safe to send audio buffers between threads as this struct has exclusive ownership of the
-// pointers contained in it.
-unsafe impl<'a> Send for CrasAudioHeader<'a> {}
-
-/// An unsafe macro for getting `VolatileRef` for a field from a given NonNull pointer.
-/// It Supports
-/// - Nested sub-field
-/// - Element of an array field
-///
-/// To use this macro safely, we need to
-/// - Make sure the pointer address is readable and writable for its structure.
-/// - Make sure all `VolatileRef`s generated from this macro have exclusive ownership for the same
-/// pointer.
-#[macro_export]
-macro_rules! vref_from_addr {
- ($addr:ident, $($field:ident).*) => {
- VolatileRef::new(&mut $addr.as_mut().$($field).* as *mut _)
- };
-
- ($addr:ident, $field:ident[$idx:tt]) => {
- VolatileRef::new(&mut $addr.as_mut().$field[$idx] as *mut _)
- };
-}
-
-// Generates error when an index is out of range.
-fn index_out_of_range() -> io::Error {
- io::Error::new(io::ErrorKind::InvalidInput, "Index out of range.")
-}
-
-impl<'a> CrasAudioHeader<'a> {
- // Creates a `CrasAudioHeader` with given `CrasAudioShmHeaderFd` and `samples_len`
- fn new(header_fd: CrasAudioShmHeaderFd, samples_len: usize) -> io::Result<Self> {
- // Safe because the creator of CrasAudioShmHeaderFd already
- // ensured that header_fd contains a cras_audio_shm_header.
- let mmap_addr = unsafe {
- cras_mmap(
- header_fd.fd.size,
- libc::PROT_READ | libc::PROT_WRITE,
- header_fd.fd.as_raw_fd(),
- )?
- };
-
- let mut addr = NonNull::new(mmap_addr as *mut cras_audio_shm_header)
- .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to create header."))?;
-
- // Safe because we know that mmap_addr (contained in addr) contains a
- // cras_audio_shm_header, and the mapped area will be exclusively
- // owned by this struct.
- unsafe {
- Ok(CrasAudioHeader {
- addr: addr.as_ptr() as *mut libc::c_void,
- samples_len,
- used_size: vref_from_addr!(addr, config.used_size),
- frame_size: vref_from_addr!(addr, config.frame_bytes),
- read_buf_idx: vref_from_addr!(addr, read_buf_idx),
- write_buf_idx: vref_from_addr!(addr, write_buf_idx),
- read_offset: [
- vref_from_addr!(addr, read_offset[0]),
- vref_from_addr!(addr, read_offset[1]),
- ],
- write_offset: [
- vref_from_addr!(addr, write_offset[0]),
- vref_from_addr!(addr, write_offset[1]),
- ],
- buffer_offset: [
- vref_from_addr!(addr, buffer_offset[0]),
- vref_from_addr!(addr, buffer_offset[1]),
- ],
- })
- }
- }
-
- /// Calculates the length of a buffer with the given offset. This length will
- /// be `used_size`, unless the offset is closer than `used_size` to the end
- /// of samples, in which case the length will be as long as possible.
- ///
- /// If that buffer length is invalid (too small to hold a frame of audio data),
- /// then returns an error.
- /// The returned buffer length will be rounded down to a multiple of `frame_size`.
- fn buffer_len_from_offset(&self, offset: usize) -> io::Result<usize> {
- if offset > self.samples_len {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- format!(
- "Buffer offset {} exceeds the length of samples area ({}).",
- offset, self.samples_len
- ),
- ));
- }
-
- let used_size = self.get_used_size();
- let frame_size = self.get_frame_size();
-
- // We explicitly allow a buffer shorter than used_size, but only
- // at the end of the samples area.
- // This is useful if we're playing a file where the number of samples is
- // not a multiple of used_size (meaning the length of the samples area
- // won't be either). Then, the last buffer played will be smaller than
- // used_size.
- let mut buffer_length = used_size.min(self.samples_len - offset);
- if buffer_length < frame_size {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- format!(
- "Buffer offset {} gives buffer length {} smaller than frame size {}.",
- offset, buffer_length, frame_size
- ),
- ));
- }
-
- // Round buffer_length down to a multiple of frame size
- buffer_length = buffer_length / frame_size * frame_size;
- Ok(buffer_length)
- }
-
- /// Gets the base of the write buffer and the writable length (rounded to `frame_size`).
- /// Does not take into account the write offset.
- ///
- /// # Returns
- ///
- /// * (`usize`, `usize`) - write buffer base as an offset from the start of
- /// the samples area and buffer length in bytes.
- pub fn get_write_offset_and_len(&self) -> io::Result<(usize, usize)> {
- let idx = self.get_write_buf_idx() as usize;
- let offset = self.get_buffer_offset(idx)?;
- let len = self.buffer_len_from_offset(offset)?;
-
- Ok((offset, len))
- }
-
- /// Gets the buffer offset of the read buffer.
- ///
- /// # Returns
- ///
- /// * `usize` - read offset in bytes
- pub fn get_read_buffer_offset(&self) -> io::Result<usize> {
- let idx = self.get_read_buf_idx() as usize;
- self.get_buffer_offset(idx)
- }
-
- /// Gets the offset of a buffer from the start of samples.
- ///
- /// # Arguments
- /// `index` - 0 <= `index` < `CRAS_NUM_SHM_BUFFERS`. The index of the buffer
- /// for which we want the `buffer_offset`.
- ///
- /// # Returns
- /// * `usize` - buffer offset in bytes
- fn get_buffer_offset(&self, idx: usize) -> io::Result<usize> {
- let buffer_offset = self
- .buffer_offset
- .get(idx)
- .ok_or_else(index_out_of_range)?
- .load() as usize;
- self.check_buffer_offset(idx, buffer_offset)?;
- Ok(buffer_offset)
- }
-
- /// Gets the number of bytes per frame from the shared memory structure.
- ///
- /// # Returns
- ///
- /// * `usize` - Number of bytes per frame
- pub fn get_frame_size(&self) -> usize {
- self.frame_size.load() as usize
- }
-
- /// Gets the max size in bytes of each shared memory buffer within
- /// the samples area.
- ///
- /// # Returns
- ///
- /// * `usize` - Value of `used_size` fetched from the shared memory header.
- pub fn get_used_size(&self) -> usize {
- self.used_size.load() as usize
- }
-
- /// Gets the index of the current written buffer.
- ///
- /// # Returns
- /// `u32` - the returned index is less than `CRAS_NUM_SHM_BUFFERS`.
- fn get_write_buf_idx(&self) -> u32 {
- self.write_buf_idx.load() & CRAS_SHM_BUFFERS_MASK
- }
-
- fn get_read_buf_idx(&self) -> u32 {
- self.read_buf_idx.load() & CRAS_SHM_BUFFERS_MASK
- }
-
- /// Switches the written buffer.
- fn switch_write_buf_idx(&mut self) {
- self.write_buf_idx
- .store(self.get_write_buf_idx() as u32 ^ 1u32)
- }
-
- /// Switches the buffer to read.
- fn switch_read_buf_idx(&mut self) {
- self.read_buf_idx
- .store(self.get_read_buf_idx() as u32 ^ 1u32)
- }
-
- /// Checks if the offset value for setting write_offset or read_offset is
- /// out of range or not.
- ///
- /// # Arguments
- /// `idx` - The index of the buffer for which we're checking the offset.
- /// `offset` - 0 <= `offset` <= `used_size` && `buffer_offset[idx]` + `offset` <=
- /// `samples_len`. Writable or readable size equals to 0 when offset equals
- /// to `used_size`.
- ///
- /// # Errors
- /// Returns an error if `offset` is out of range or if idx is not a valid
- /// buffer idx.
- fn check_rw_offset(&self, idx: usize, offset: u32) -> io::Result<()> {
- let buffer_len = self.buffer_len_from_offset(self.get_buffer_offset(idx)?)?;
- if offset as usize > buffer_len {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- format!(
- "Offset {} is larger than buffer size {}.",
- offset, buffer_len
- ),
- ));
- }
-
- Ok(())
- }
-
- /// Sets `write_offset[idx]` to the count of written bytes.
- ///
- /// # Arguments
- /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS`
- /// `offset` - 0 <= `offset` <= `used_size` && `offset` + `used_size` <=
- /// `samples_len`. Writable size equals to 0 when offset equals to
- /// `used_size`.
- ///
- /// # Errors
- /// Returns an error if `offset` is out of range.
- fn set_write_offset(&mut self, idx: usize, offset: u32) -> io::Result<()> {
- self.check_rw_offset(idx, offset)?;
- let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?;
- write_offset.store(offset);
- Ok(())
- }
-
- /// Sets `read_offset[idx]` to count of written bytes.
- ///
- /// # Arguments
- /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS`
- /// `offset` - 0 <= `offset` <= `used_size` && `offset` + `used_size` <=
- /// `samples_len`. Readable size equals to 0 when offset equals to
- /// `used_size`.
- ///
- /// # Errors
- /// Returns error if index out of range.
- fn set_read_offset(&mut self, idx: usize, offset: u32) -> io::Result<()> {
- self.check_rw_offset(idx, offset)?;
- let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?;
- read_offset.store(offset);
- Ok(())
- }
-
- /// Check that `offset` is a valid buffer offset for the buffer at `idx`
- /// An offset is not valid if it is
- /// * outside of the samples area
- /// * overlaps some other buffer `[other_offset, other_offset + used_size)`
- /// * is close enough to the end of the samples area that the buffer would
- /// be shorter than `frame_size`.
- fn check_buffer_offset(&self, idx: usize, offset: usize) -> io::Result<()> {
- let start = offset;
- let end = start + self.buffer_len_from_offset(start)?;
-
- let other_idx = (idx ^ 1) as usize;
- let other_start = self
- .buffer_offset
- .get(other_idx)
- .ok_or_else(index_out_of_range)?
- .load() as usize;
- let other_end = other_start + self.buffer_len_from_offset(other_start)?;
- if start < other_end && other_start < end {
- // Special case: occasionally we get the same buffer offset twice
- // from the intel8x0 kernel driver in crosvm's AC97 device, and we
- // don't want to crash in that case.
- if start == other_start && end == other_end {
- warn!(
- "Setting buffer {} to same index/offset as buffer {}, [{}, {})",
- idx, other_idx, other_start, other_end
- );
- } else {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- format!(
- "Setting buffer {} to [{}, {}) overlaps buffer {} at [{}, {})",
- idx, start, end, other_idx, other_start, other_end,
- ),
- ));
- }
- }
- Ok(())
- }
-
- /// Sets the location of the audio buffer `idx` within the samples area to
- /// `offset`, so that CRAS will read/write samples for that buffer from that
- /// offset.
- ///
- /// # Arguments
- /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS`
- /// `offset` - 0 <= `offset` && `offset` + `frame_size` <= `samples_len`
- ///
- /// # Errors
- /// If `idx` is out of range
- /// If the offset is invalid, which can happen if `offset` is
- /// * outside of the samples area
- /// * overlaps some other buffer `[other_offset, other_offset + used_size)`
- /// * is close enough to the end of the samples area that the buffer would
- /// be shorter than `frame_size`.
- pub fn set_buffer_offset(&mut self, idx: usize, offset: usize) -> io::Result<()> {
- self.check_buffer_offset(idx, offset)?;
-
- let buffer_offset = self.buffer_offset.get(idx).ok_or_else(index_out_of_range)?;
- buffer_offset.store(offset as u64);
- Ok(())
- }
-
- /// Commits written frames by switching the current buffer to the other one
- /// after samples are ready and indexes of current buffer are all set.
- /// - Sets `write_offset` of current buffer to `frame_count * frame_size`
- /// - Sets `read_offset` of current buffer to `0`.
- ///
- /// # Arguments
- ///
- /// * `frame_count` - Number of frames written to the current buffer
- ///
- /// # Errors
- ///
- /// * Returns error if `frame_count` is larger than buffer size
- ///
- /// This function is safe because we switch `write_buf_idx` after letting
- /// `write_offset` and `read_offset` ready and we read / write shared memory
- /// variables with volatile operations.
- pub fn commit_written_frames(&mut self, frame_count: u32) -> io::Result<()> {
- // Uses `u64` to prevent possible overflow
- let byte_count = frame_count as u64 * self.get_frame_size() as u64;
- if byte_count > self.get_used_size() as u64 {
- Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- "frame_count * frame_size is larger than used_size",
- ))
- } else {
- let idx = self.get_write_buf_idx() as usize;
- // Sets `write_offset` of current buffer to frame_count * frame_size
- self.set_write_offset(idx, byte_count as u32)?;
- // Sets `read_offset` of current buffer to `0`.
- self.set_read_offset(idx, 0)?;
- // Switch to the other buffer
- self.switch_write_buf_idx();
- Ok(())
- }
- }
-
- /// Get readable frames in current buffer.
- ///
- /// # Returns
- ///
- /// * `usize` - number of readable frames.
- ///
- /// # Errors
- ///
- /// Returns error if index out of range.
- pub fn get_readable_frames(&self) -> io::Result<usize> {
- let idx = self.get_read_buf_idx() as usize;
- let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?;
- let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?;
- let nframes =
- (write_offset.load() as i32 - read_offset.load() as i32) / self.get_frame_size() as i32;
- if nframes < 0 {
- Ok(0)
- } else {
- Ok(nframes as usize)
- }
- }
-
- /// Commit read frames from reader, .
- /// - Sets `read_offset` of current buffer to `read_offset + frame_count * frame_size`.
- /// If `read_offset` is larger than or equal to `write_offset`, then
- /// - Sets `read_offset` and `write_offset` to `0` and switch `read_buf_idx`.
- ///
- /// # Arguments
- ///
- /// * `frame_count` - Read frames in current read buffer.
- ///
- /// # Errors
- ///
- /// Returns error if index out of range.
- pub fn commit_read_frames(&mut self, frame_count: u32) -> io::Result<()> {
- let idx = self.get_read_buf_idx() as usize;
- let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?;
- let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?;
- read_offset.store(read_offset.load() + frame_count * self.get_frame_size() as u32);
- if read_offset.load() >= write_offset.load() {
- read_offset.store(0);
- write_offset.store(0);
- self.switch_read_buf_idx();
- }
- Ok(())
- }
-}
-
-impl<'a> Drop for CrasAudioHeader<'a> {
- fn drop(&mut self) {
- // Safe because all references must be gone by the time drop is called.
- unsafe {
- libc::munmap(self.addr as *mut _, mem::size_of::<cras_audio_shm_header>());
- }
- }
-}
-
-// To use this safely, we need to make sure
-// - The given fd contains valid space which is larger than `len` + `offset`
-unsafe fn cras_mmap_offset(
- len: usize,
- prot: libc::c_int,
- fd: libc::c_int,
- offset: usize,
-) -> io::Result<*mut libc::c_void> {
- if offset > libc::off_t::max_value() as usize {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- "Requested offset is out of range of `libc::off_t`.",
- ));
- }
- // It's safe because we handle its returned results.
- match libc::mmap(
- ptr::null_mut(),
- len,
- prot,
- libc::MAP_SHARED,
- fd,
- offset as libc::off_t,
- ) {
- libc::MAP_FAILED => Err(io::Error::last_os_error()),
- shm_ptr => Ok(shm_ptr),
- }
-}
-
-// To use this safely, we need to make sure
-// - The given fd contains valid space which is larger than `len`
-unsafe fn cras_mmap(
- len: usize,
- prot: libc::c_int,
- fd: libc::c_int,
-) -> io::Result<*mut libc::c_void> {
- cras_mmap_offset(len, prot, fd, 0)
-}
-
-/// An unsafe macro for getting a `VolatileSlice` representing an entire array
-/// field from a given NonNull pointer.
-///
-/// To use this macro safely, we need to
-/// - Make sure the pointer address is readable and writeable for its struct.
-/// - Make sure all `VolatileSlice`s generated from this macro have exclusive ownership for the same
-/// pointer.
-/// - Make sure the length of the array field is non-zero.
-#[macro_export]
-macro_rules! vslice_from_addr {
- ($addr:ident, $($field:ident).*) => {{
- let ptr = &mut $addr.as_mut().$($field).* as *mut _ as *mut u8;
- let size = std::mem::size_of_val(&$addr.as_mut().$($field).*);
- VolatileSlice::from_raw_parts(ptr, size)
- }};
-}
-
-/// A structure that points to RO shared memory area - `cras_server_state`
-/// The structure is created from a shared memory fd which contains the structure.
-#[derive(Debug)]
-pub struct CrasServerState<'a> {
- addr: *mut libc::c_void,
- volume: VolatileRef<'a, u32>,
- mute: VolatileRef<'a, i32>,
- num_output_devs: VolatileRef<'a, u32>,
- output_devs: VolatileSlice<'a>,
- num_input_devs: VolatileRef<'a, u32>,
- input_devs: VolatileSlice<'a>,
- num_output_nodes: VolatileRef<'a, u32>,
- num_input_nodes: VolatileRef<'a, u32>,
- output_nodes: VolatileSlice<'a>,
- input_nodes: VolatileSlice<'a>,
- update_count: VolatileRef<'a, u32>,
- debug_info_num_devs: VolatileRef<'a, u32>,
- debug_info_devs: VolatileSlice<'a>,
- debug_info_num_streams: VolatileRef<'a, u32>,
- debug_info_streams: VolatileSlice<'a>,
-}
-
-// It is safe to send server_state between threads as this struct has exclusive
-// ownership of the shared memory area contained in it.
-unsafe impl<'a> Send for CrasServerState<'a> {}
-
-impl<'a> CrasServerState<'a> {
- /// Create a CrasServerState
- pub fn try_new(state_fd: CrasServerStateShmFd) -> io::Result<Self> {
- // Safe because the creator of CrasServerStateShmFd already
- // ensured that state_fd contains a cras_server_state.
- let mmap_addr =
- unsafe { cras_mmap(state_fd.fd.size, libc::PROT_READ, state_fd.fd.as_raw_fd())? };
-
- let mut addr = NonNull::new(mmap_addr as *mut cras_server_state).ok_or_else(|| {
- io::Error::new(io::ErrorKind::Other, "Failed to create CrasServerState.")
- })?;
-
- // Safe because we know that addr is a non-null pointer to cras_server_state.
- let state_version = unsafe { vref_from_addr!(addr, state_version) };
- if state_version.load() != CRAS_SERVER_STATE_VERSION {
- return Err(io::Error::new(
- io::ErrorKind::Other,
- format!(
- "CrasServerState version {} does not match expected version {}",
- state_version.load(),
- CRAS_SERVER_STATE_VERSION
- ),
- ));
- }
-
- // Safe because we know that mmap_addr (contained in addr) contains a
- // cras_server_state, and the mapped area will be exclusively
- // owned by this struct.
- unsafe {
- Ok(CrasServerState {
- addr: addr.as_ptr() as *mut libc::c_void,
- volume: vref_from_addr!(addr, volume),
- mute: vref_from_addr!(addr, mute),
- num_output_devs: vref_from_addr!(addr, num_output_devs),
- num_input_devs: vref_from_addr!(addr, num_input_devs),
- output_devs: vslice_from_addr!(addr, output_devs),
- input_devs: vslice_from_addr!(addr, input_devs),
- num_output_nodes: vref_from_addr!(addr, num_output_nodes),
- num_input_nodes: vref_from_addr!(addr, num_input_nodes),
- output_nodes: vslice_from_addr!(addr, output_nodes),
- input_nodes: vslice_from_addr!(addr, input_nodes),
- update_count: vref_from_addr!(addr, update_count),
- debug_info_num_devs: vref_from_addr!(addr, audio_debug_info.num_devs),
- debug_info_devs: vslice_from_addr!(addr, audio_debug_info.devs),
- debug_info_num_streams: vref_from_addr!(addr, audio_debug_info.num_streams),
- debug_info_streams: vslice_from_addr!(addr, audio_debug_info.streams),
- })
- }
- }
-
- /// Gets the system volume.
- ///
- /// Read the current value for system volume from shared memory.
- pub fn get_system_volume(&self) -> u32 {
- self.volume.load()
- }
-
- /// Gets the system mute.
- ///
- /// Read the current value for system mute from shared memory.
- pub fn get_system_mute(&self) -> bool {
- self.mute.load() != 0
- }
-
- /// Runs a closure safely such that it can be sure that the server state
- /// was not updated during the read.
- /// This can be used for an "atomic" read of non-atomic data from the
- /// state shared memory.
- fn synchronized_state_read<F, T>(&self, mut func: F) -> T
- where
- F: FnMut() -> T,
- {
- // Waits until the server has completed a state update before returning
- // the current update count.
- let begin_server_state_read = || -> u32 {
- loop {
- let update_count = self.update_count.load();
- if update_count % 2 == 0 {
- atomic::fence(Ordering::Acquire);
- return update_count;
- } else {
- thread::yield_now();
- }
- }
- };
-
- // Checks that the update count has not changed since the start
- // of the server state read.
- let end_server_state_read = |count: u32| -> bool {
- let result = count == self.update_count.load();
- atomic::fence(Ordering::Release);
- result
- };
-
- // Get the state's update count and run the provided closure.
- // If the update count has not changed once the closure is finished,
- // return the result, otherwise repeat the process.
- loop {
- let update_count = begin_server_state_read();
- let result = func();
- if end_server_state_read(update_count) {
- return result;
- }
- }
- }
-
- /// Gets a list of output devices
- ///
- /// Read a list of the currently attached output devices from shared memory.
- pub fn output_devices(&self) -> impl Iterator<Item = CrasIodevInfo> {
- let mut devs: Vec<cras_iodev_info> = vec![Default::default(); CRAS_MAX_IODEVS as usize];
- let num_devs = self.synchronized_state_read(|| {
- self.output_devs.copy_to(&mut devs);
- self.num_output_devs.load()
- });
- devs.into_iter()
- .take(num_devs as usize)
- .map(CrasIodevInfo::from)
- }
-
- /// Gets a list of input devices
- ///
- /// Read a list of the currently attached input devices from shared memory.
- pub fn input_devices(&self) -> impl Iterator<Item = CrasIodevInfo> {
- let mut devs: Vec<cras_iodev_info> = vec![Default::default(); CRAS_MAX_IODEVS as usize];
- let num_devs = self.synchronized_state_read(|| {
- self.input_devs.copy_to(&mut devs);
- self.num_input_devs.load()
- });
- devs.into_iter()
- .take(num_devs as usize)
- .map(CrasIodevInfo::from)
- }
-
- /// Gets a list of output nodes
- ///
- /// Read a list of the currently attached output nodes from shared memory.
- pub fn output_nodes(&self) -> impl Iterator<Item = CrasIonodeInfo> {
- let mut nodes: Vec<cras_ionode_info> = vec![Default::default(); CRAS_MAX_IONODES as usize];
- let num_nodes = self.synchronized_state_read(|| {
- self.output_nodes.copy_to(&mut nodes);
- self.num_output_nodes.load()
- });
- nodes
- .into_iter()
- .take(num_nodes as usize)
- .map(CrasIonodeInfo::from)
- }
-
- /// Gets a list of input nodes
- ///
- /// Read a list of the currently attached input nodes from shared memory.
- pub fn input_nodes(&self) -> impl Iterator<Item = CrasIonodeInfo> {
- let mut nodes: Vec<cras_ionode_info> = vec![Default::default(); CRAS_MAX_IONODES as usize];
- let num_nodes = self.synchronized_state_read(|| {
- self.input_nodes.copy_to(&mut nodes);
- self.num_input_nodes.load()
- });
- nodes
- .into_iter()
- .take(num_nodes as usize)
- .map(CrasIonodeInfo::from)
- }
-
- /// Get audio debug info
- ///
- /// Loads the server's audio_debug_info struct and converts it into an
- /// idiomatic rust representation.
- ///
- /// # Errors
- /// * If any of the stream debug information structs are invalid.
- pub fn get_audio_debug_info(&self) -> Result<AudioDebugInfo, cras_sys::Error> {
- let mut devs: Vec<audio_dev_debug_info> = vec![Default::default(); MAX_DEBUG_DEVS as usize];
- let mut streams: Vec<audio_stream_debug_info> =
- vec![Default::default(); MAX_DEBUG_STREAMS as usize];
- let (num_devs, num_streams) = self.synchronized_state_read(|| {
- self.debug_info_devs.copy_to(&mut devs);
- self.debug_info_streams.copy_to(&mut streams);
- (
- self.debug_info_num_devs.load(),
- self.debug_info_num_streams.load(),
- )
- });
- let dev_info = devs
- .into_iter()
- .take(num_devs as usize)
- .map(AudioDevDebugInfo::from)
- .collect();
- let stream_info = streams
- .into_iter()
- .take(num_streams as usize)
- .map(AudioStreamDebugInfo::try_from)
- .collect::<Result<Vec<_>, _>>()?;
- Ok(AudioDebugInfo::new(dev_info, stream_info))
- }
-}
-
-impl<'a> Drop for CrasServerState<'a> {
- /// Call `munmap` for `addr`.
- fn drop(&mut self) {
- unsafe {
- // Safe because all references must be gone by the time drop is called.
- libc::munmap(self.addr, mem::size_of::<cras_server_state>());
- }
- }
-}
-
-/// A structure holding the mapped shared memory area used to exchange
-/// samples with CRAS. The shared memory is owned exclusively by this structure,
-/// and will be cleaned up on drop.
-/// * `addr` - The address of the mapped shared memory.
-/// * `len` - Length of the mapped shared memory in bytes.
-pub struct CrasAudioBuffer {
- addr: *mut u8,
- len: usize,
-}
-
-// It is safe to send audio buffers between threads as this struct has exclusive ownership of the
-// shared memory area contained in it.
-unsafe impl Send for CrasAudioBuffer {}
-
-impl CrasAudioBuffer {
- fn new(samples_fd: CrasShmFd) -> io::Result<Self> {
- // This is safe because we checked that the size of the shm in samples_fd
- // was at least samples_fd.size when it was created.
- let addr = unsafe {
- cras_mmap(
- samples_fd.size,
- libc::PROT_READ | libc::PROT_WRITE,
- samples_fd.as_raw_fd(),
- )? as *mut u8
- };
- Ok(Self {
- addr,
- len: samples_fd.size,
- })
- }
-
- /// Provides a mutable slice to be filled with audio samples.
- pub fn get_buffer(&mut self) -> &mut [u8] {
- // This is safe because it takes a mutable reference to self, and there can only be one
- // taken at a time. Although this is shared memory, the reader side must have it mapped as
- // read only.
- unsafe { slice::from_raw_parts_mut(self.addr, self.len) }
- }
-}
-
-impl Drop for CrasAudioBuffer {
- fn drop(&mut self) {
- // Safe because all references must be gone by the time drop is called.
- unsafe {
- libc::munmap(self.addr as *mut _, self.len);
- }
- }
-}
-
-/// Creates header and buffer from given shared memory fds.
-pub fn create_header_and_buffers<'a>(
- header_fd: CrasAudioShmHeaderFd,
- samples_fd: CrasShmFd,
-) -> io::Result<(CrasAudioHeader<'a>, CrasAudioBuffer)> {
- let header = CrasAudioHeader::new(header_fd, samples_fd.size)?;
- let buffer = CrasAudioBuffer::new(samples_fd)?;
-
- Ok((header, buffer))
-}
-
-/// Creates header from header shared memory fds. Use this function
-/// when mapping the samples shm is not necessary, for instance with a
-/// client-provided shm stream.
-pub fn create_header<'a>(
- header_fd: CrasAudioShmHeaderFd,
- samples_len: usize,
-) -> io::Result<CrasAudioHeader<'a>> {
- Ok(CrasAudioHeader::new(header_fd, samples_len)?)
-}
-
-/// A structure wrapping a fd which contains a shared memory area and its size.
-/// * `fd` - The shared memory file descriptor, a `libc::c_int`.
-/// * `size` - Size of the shared memory area.
-pub struct CrasShmFd {
- fd: libc::c_int,
- size: usize,
-}
-
-impl CrasShmFd {
- /// Creates a `CrasShmFd` by shared memory fd and size
- /// # Arguments
- /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and
- /// the fd will be closed on drop.
- /// * `size` - Size of the shared memory.
- ///
- /// # Returns
- /// * `CrasShmFd` - Wrap the input arguments without doing anything.
- ///
- /// To use this function safely, we need to make sure
- /// - The input fd is a valid shared memory fd.
- /// - The input shared memory fd won't be used by others.
- /// - The input fd contains memory size larger than `size`.
- pub unsafe fn new(fd: libc::c_int, size: usize) -> CrasShmFd {
- CrasShmFd { fd, size }
- }
-}
-
-impl AsRawFd for CrasShmFd {
- fn as_raw_fd(&self) -> RawFd {
- self.fd
- }
-}
-
-impl Drop for CrasShmFd {
- fn drop(&mut self) {
- // It's safe here if we make sure
- // - the input fd is valid and
- // - `CrasShmFd` is the only owner
- // in `new` function
- unsafe {
- libc::close(self.fd);
- }
- }
-}
-
-/// A structure wrapping a fd which contains a shared `cras_server_state`.
-/// * `shm_fd` - A shared memory fd contains a `cras_server_state`
-pub struct CrasServerStateShmFd {
- fd: CrasShmFd,
-}
-
-impl CrasServerStateShmFd {
- /// Creates a `CrasServerStateShmFd` by shared memory fd
- /// # Arguments
- /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and
- /// the fd will be closed on drop.
- ///
- /// # Returns
- /// A structure wrapping a `CrasShmFd` with the input fd and `size` which equals to
- /// the size of `cras_server_sate`.
- ///
- /// To use this function safely, we need to make sure
- /// - The input fd is a valid shared memory fd.
- /// - The input shared memory fd won't be used by others.
- /// - The shared memory area in the input fd contains a `cras_server_state`.
- pub unsafe fn new(fd: libc::c_int) -> Self {
- Self {
- fd: CrasShmFd::new(fd, mem::size_of::<cras_server_state>()),
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::fs::File;
- use std::os::unix::io::IntoRawFd;
- use std::sync::{Arc, Mutex};
- use std::thread;
- use sys_util::{kernel_has_memfd, SharedMemory};
-
- #[test]
- fn cras_audio_header_switch_test() {
- if !kernel_has_memfd() {
- return;
- }
- let mut header = create_cras_audio_header(20);
- assert_eq!(0, header.get_write_buf_idx());
- header.switch_write_buf_idx();
- assert_eq!(1, header.get_write_buf_idx());
- }
-
- #[test]
- fn cras_audio_header_write_offset_test() {
- if !kernel_has_memfd() {
- return;
- }
- let mut header = create_cras_audio_header(20);
- header.frame_size.store(2);
- header.used_size.store(5);
- header.set_buffer_offset(0, 12).unwrap();
-
- assert_eq!(0, header.write_offset[0].load());
- // Index out of bound
- assert!(header.set_write_offset(2, 5).is_err());
- // Offset out of bound
- // Buffer length is 4, since that's the largest multiple of frame_size
- // less than used_size.
- assert!(header.set_write_offset(0, 6).is_err());
- assert_eq!(0, header.write_offset[0].load());
- assert!(header.set_write_offset(0, 5).is_err());
- assert_eq!(0, header.write_offset[0].load());
- assert!(header.set_write_offset(0, 4).is_ok());
- assert_eq!(4, header.write_offset[0].load());
- }
-
- #[test]
- fn cras_audio_header_read_offset_test() {
- if !kernel_has_memfd() {
- return;
- }
- let mut header = create_cras_audio_header(20);
- header.frame_size.store(2);
- header.used_size.store(5);
- header.set_buffer_offset(0, 12).unwrap();
-
- assert_eq!(0, header.read_offset[0].load());
- // Index out of bound
- assert!(header.set_read_offset(2, 5).is_err());
- // Offset out of bound
- // Buffer length is 4, since that's the largest multiple of frame_size
- // less than used_size.
- assert!(header.set_read_offset(0, 6).is_err());
- assert_eq!(0, header.read_offset[0].load());
- assert!(header.set_read_offset(0, 5).is_err());
- assert_eq!(0, header.read_offset[0].load());
- assert!(header.set_read_offset(0, 4).is_ok());
- assert_eq!(4, header.read_offset[0].load());
- }
-
- #[test]
- fn cras_audio_header_commit_written_frame_test() {
- if !kernel_has_memfd() {
- return;
- }
- let mut header = create_cras_audio_header(20);
- header.frame_size.store(2);
- header.used_size.store(10);
- header.read_offset[0].store(10);
- header.set_buffer_offset(0, 10).unwrap();
-
- assert!(header.commit_written_frames(5).is_ok());
- assert_eq!(header.write_offset[0].load(), 10);
- assert_eq!(header.read_offset[0].load(), 0);
- assert_eq!(header.write_buf_idx.load(), 1);
- }
-
- #[test]
- fn cras_audio_header_get_readable_frames_test() {
- if !kernel_has_memfd() {
- return;
- }
- let header = create_cras_audio_header(20);
- header.frame_size.store(2);
- header.used_size.store(10);
- header.read_offset[0].store(2);
- header.write_offset[0].store(10);
- let frames = header
- .get_readable_frames()
- .expect("Failed to get readable frames.");
- assert_eq!(frames, 4);
- }
-
- #[test]
- fn cras_audio_header_commit_read_frames_test() {
- if !kernel_has_memfd() {
- return;
- }
- let mut header = create_cras_audio_header(20);
- header.frame_size.store(2);
- header.used_size.store(10);
- header.read_offset[0].store(2);
- header.write_offset[0].store(10);
- header
- .commit_read_frames(3)
- .expect("Failed to commit read frames.");
- assert_eq!(header.get_read_buf_idx(), 0);
- assert_eq!(header.read_offset[0].load(), 8);
-
- header
- .commit_read_frames(1)
- .expect("Failed to commit read frames.");
- // Read buffer should be switched
- assert_eq!(header.get_read_buf_idx(), 1);
- assert_eq!(header.read_offset[0].load(), 0);
- assert_eq!(header.read_offset[0].load(), 0);
- }
-
- #[test]
- fn cras_audio_header_get_write_offset_and_len() {
- if !kernel_has_memfd() {
- return;
- }
- let header = create_cras_audio_header(30);
- header.frame_size.store(2);
- header.used_size.store(10);
- header.write_buf_idx.store(0);
- header.read_offset[0].store(0);
- header.write_offset[0].store(0);
- header.buffer_offset[0].store(0);
-
- header.read_buf_idx.store(1);
- header.read_offset[1].store(0);
- header.write_offset[1].store(0);
- header.buffer_offset[1].store(10);
-
- // standard offsets and lens
- let (offset, len) = header.get_write_offset_and_len().unwrap();
- assert_eq!(offset, 0);
- assert_eq!(len, 10);
-
- header.write_buf_idx.store(1);
- header.read_buf_idx.store(0);
- let (offset, len) = header.get_write_offset_and_len().unwrap();
- assert_eq!(offset, 10);
- assert_eq!(len, 10);
-
- // relocate buffer offsets
- header.buffer_offset[1].store(16);
- let (offset, len) = header.get_write_offset_and_len().unwrap();
- assert_eq!(offset, 16);
- assert_eq!(len, 10);
-
- header.buffer_offset[0].store(5);
- header.write_buf_idx.store(0);
- let (offset, len) = header.get_write_offset_and_len().unwrap();
- assert_eq!(offset, 5);
- assert_eq!(len, 10);
-
- header.write_buf_idx.store(0);
- header.buffer_offset[0].store(2);
- header.read_buf_idx.store(1);
- header.buffer_offset[1].store(10);
- let result = header.get_write_offset_and_len();
- // Should be an error as write buffer would overrun into other buffer.
- assert!(result.is_err());
-
- header.buffer_offset[0].store(24);
- header.buffer_offset[1].store(10);
- let (offset, len) = header.get_write_offset_and_len().unwrap();
- // Should be ok since we're only running up against the end of samples.
- assert_eq!(offset, 24);
- assert_eq!(len, 6);
-
- header.buffer_offset[0].store(25);
- let (offset, len) = header.get_write_offset_and_len().unwrap();
- // Should be ok, but we'll truncate len to frame_size.
- assert_eq!(offset, 25);
- assert_eq!(len, 4);
-
- header.buffer_offset[0].store(29);
- let result = header.get_write_offset_and_len();
- // Should be an error as buffer is smaller than frame_size.
- assert!(result.is_err());
- }
-
- #[test]
- fn cras_audio_header_set_buffer_offset() {
- if !kernel_has_memfd() {
- return;
- }
- let mut header = create_cras_audio_header(30);
- header.frame_size.store(2);
- header.used_size.store(10);
- header.write_buf_idx.store(0);
- header.read_offset[0].store(0);
- header.write_offset[0].store(0);
- header.buffer_offset[0].store(0);
-
- header.read_buf_idx.store(1);
- header.read_offset[1].store(0);
- header.write_offset[1].store(0);
- header.buffer_offset[1].store(10);
-
- // Setting buffer_offset to exactly overlap with other buffer is okay
- assert!(header.set_buffer_offset(0, 10).is_ok());
-
- // Setting buffer_offset to partially overlap other buffer is not okay
- assert!(header.set_buffer_offset(0, 9).is_err());
-
- header.buffer_offset[0].store(0);
- header.write_offset[1].store(8);
- // With samples, it's still an error.
- assert!(header.set_buffer_offset(0, 9).is_err());
-
- // Setting the offset past the end of the other buffer is okay
- assert!(header.set_buffer_offset(0, 20).is_ok());
-
- // Setting buffer offset such that buffer length is less than used_size
- // is okay, but only at the end of the samples area.
- assert!(header.set_buffer_offset(0, 21).is_ok());
- assert!(header.set_buffer_offset(0, 27).is_ok());
-
- // It's not okay if we get a buffer with length less than frame_size.
- assert!(header.set_buffer_offset(0, 29).is_err());
- assert!(header.set_buffer_offset(0, 30).is_err());
-
- // If we try to overlap another buffer with that other buffer at the end,
- // it's not okay, unless it's the exact same index.
- assert!(header.set_buffer_offset(1, 25).is_err());
- assert!(header.set_buffer_offset(1, 27).is_ok());
- assert!(header.set_buffer_offset(1, 28).is_err());
-
- // Setting buffer offset past the end of samples is an error.
- assert!(header.set_buffer_offset(0, 33).is_err());
- }
-
- #[test]
- fn create_header_and_buffers_test() {
- if !kernel_has_memfd() {
- return;
- }
- let header_fd = cras_audio_header_fd();
- let samples_fd = cras_audio_samples_fd(20);
- let res = create_header_and_buffers(header_fd, samples_fd);
- res.expect("Failed to create header and buffer.");
- }
-
- fn create_shm(size: usize) -> File {
- let mut shm = SharedMemory::new(None).expect("failed to create shm");
- shm.set_size(size as u64).expect("failed to set shm size");
- shm.into()
- }
-
- fn create_cras_audio_header<'a>(samples_len: usize) -> CrasAudioHeader<'a> {
- CrasAudioHeader::new(cras_audio_header_fd(), samples_len).unwrap()
- }
-
- fn cras_audio_header_fd() -> CrasAudioShmHeaderFd {
- let size = mem::size_of::<cras_audio_shm_header>();
- let shm = create_shm(size);
- unsafe { CrasAudioShmHeaderFd::new(shm.into_raw_fd()) }
- }
-
- fn cras_audio_samples_fd(size: usize) -> CrasShmFd {
- let shm = create_shm(size);
- unsafe { CrasShmFd::new(shm.into_raw_fd(), size) }
- }
-
- #[test]
- fn cras_mmap_pass() {
- if !kernel_has_memfd() {
- return;
- }
- let shm = create_shm(100);
- let rc = unsafe { cras_mmap(10, libc::PROT_READ, shm.as_raw_fd()) };
- assert!(rc.is_ok());
- unsafe { libc::munmap(rc.unwrap(), 10) };
- }
-
- #[test]
- fn cras_mmap_failed() {
- if !kernel_has_memfd() {
- return;
- }
- let rc = unsafe { cras_mmap(10, libc::PROT_READ, -1) };
- assert!(rc.is_err());
- }
-
- #[test]
- fn cras_server_state() {
- let size = mem::size_of::<cras_server_state>();
- let shm = create_shm(size);
- unsafe {
- let addr = cras_mmap(size, libc::PROT_WRITE, shm.as_raw_fd())
- .expect("failed to mmap state shm");
- {
- let state: &mut cras_server_state = &mut *(addr as *mut cras_server_state);
- state.state_version = CRAS_SERVER_STATE_VERSION;
- state.volume = 47;
- state.mute = 1;
- }
- libc::munmap(addr, size);
- };
- let state_fd = unsafe { CrasServerStateShmFd::new(shm.into_raw_fd()) };
- let state =
- CrasServerState::try_new(state_fd).expect("try_new failed for valid server_state fd");
- assert_eq!(state.get_system_volume(), 47);
- assert_eq!(state.get_system_mute(), true);
- }
-
- #[test]
- fn cras_server_state_old_version() {
- let size = mem::size_of::<cras_server_state>();
- let shm = create_shm(size);
- unsafe {
- let addr = cras_mmap(size, libc::PROT_WRITE, shm.as_raw_fd())
- .expect("failed to mmap state shm");
- {
- let state: &mut cras_server_state = &mut *(addr as *mut cras_server_state);
- state.state_version = CRAS_SERVER_STATE_VERSION - 1;
- state.volume = 29;
- state.mute = 0;
- }
- libc::munmap(addr, size);
- };
- let state_fd = unsafe { CrasServerStateShmFd::new(shm.into_raw_fd()) };
- CrasServerState::try_new(state_fd)
- .expect_err("try_new succeeded for invalid state version");
- }
-
- #[test]
- fn cras_server_sync_state_read() {
- let size = mem::size_of::<cras_server_state>();
- let shm = create_shm(size);
- let addr = unsafe { cras_mmap(size, libc::PROT_WRITE, shm.as_raw_fd()).unwrap() };
- let state: &mut cras_server_state = unsafe { &mut *(addr as *mut cras_server_state) };
- state.state_version = CRAS_SERVER_STATE_VERSION;
- state.update_count = 14;
- state.volume = 12;
-
- let state_fd = unsafe { CrasServerStateShmFd::new(shm.into_raw_fd()) };
- let state_struct = CrasServerState::try_new(state_fd).unwrap();
-
- // Create a lock so that we can block the reader while we change the
- // update_count;
- let lock = Arc::new(Mutex::new(()));
- let thread_lock = lock.clone();
- let reader_thread = {
- let _guard = lock.lock().unwrap();
-
- // Create reader thread that will get the value of volume. Since we
- // hold the lock currently, this will block until we release the lock.
- let reader_thread = thread::spawn(move || {
- state_struct.synchronized_state_read(|| {
- let _guard = thread_lock.lock().unwrap();
- state_struct.volume.load()
- })
- });
-
- // Update volume and change update count so that the synchronized read
- // will not return (odd update count means update in progress).
- state.volume = 27;
- state.update_count = 15;
-
- reader_thread
- };
-
- // The lock has been released, but the reader thread should still not
- // terminate, because of the update in progress.
-
- // Yield thread to give reader_thread a chance to get scheduled.
- thread::yield_now();
- {
- let _guard = lock.lock().unwrap();
-
- // Update volume and change update count to indicate the write has
- // finished.
- state.volume = 42;
- state.update_count = 16;
- }
-
- let read_value = reader_thread.join().unwrap();
- assert_eq!(read_value, 42);
- }
-}