summaryrefslogtreecommitdiff
path: root/cras/client/libcras/src
diff options
context:
space:
mode:
Diffstat (limited to 'cras/client/libcras/src')
-rw-r--r--cras/client/libcras/src/audio_socket.rs316
-rw-r--r--cras/client/libcras/src/cras_client_message.rs200
-rw-r--r--cras/client/libcras/src/cras_server_socket.rs124
-rw-r--r--cras/client/libcras/src/cras_shm.rs1308
-rw-r--r--cras/client/libcras/src/cras_shm_stream.rs191
-rw-r--r--cras/client/libcras/src/cras_stream.rs224
-rw-r--r--cras/client/libcras/src/libcras.rs699
7 files changed, 0 insertions, 3062 deletions
diff --git a/cras/client/libcras/src/audio_socket.rs b/cras/client/libcras/src/audio_socket.rs
deleted file mode 100644
index ac56144c..00000000
--- a/cras/client/libcras/src/audio_socket.rs
+++ /dev/null
@@ -1,316 +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::io;
-use std::io::{Read, Write};
-use std::mem;
-use std::os::unix::{
- io::{AsRawFd, RawFd},
- net::UnixStream,
-};
-use std::time::Duration;
-
-use cras_sys::gen::{audio_message, CRAS_AUDIO_MESSAGE_ID};
-use data_model::DataInit;
-use sys_util::{PollContext, PollToken};
-
-/// A structure for interacting with the CRAS server audio thread through a `UnixStream::pair`.
-pub struct AudioSocket {
- socket: UnixStream,
-}
-
-/// Audio message results which are exchanged by `CrasStream` and CRAS audio server.
-/// through an audio socket.
-#[allow(dead_code)]
-#[derive(Debug)]
-pub enum AudioMessage {
- /// * `id` - Audio message id, which is a `enum CRAS_AUDIO_MESSAGE_ID`.
- /// * `frames` - A `u32` indicating the read or written frame count.
- Success {
- id: CRAS_AUDIO_MESSAGE_ID,
- frames: u32,
- },
- /// * `error` - Error code when a error occurs.
- Error(i32),
-}
-
-/// Converts AudioMessage to raw audio_message for CRAS audio server.
-impl Into<audio_message> for AudioMessage {
- fn into(self) -> audio_message {
- match self {
- AudioMessage::Success { id, frames } => audio_message {
- id,
- error: 0,
- frames,
- },
- AudioMessage::Error(error) => audio_message {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA,
- error,
- frames: 0,
- },
- }
- }
-}
-
-/// Converts AudioMessage from raw audio_message from CRAS audio server.
-impl From<audio_message> for AudioMessage {
- fn from(message: audio_message) -> Self {
- match message.error {
- 0 => AudioMessage::Success {
- id: message.id as CRAS_AUDIO_MESSAGE_ID,
- frames: message.frames,
- },
- error => AudioMessage::Error(error),
- }
- }
-}
-
-impl AudioSocket {
- /// Creates `AudioSocket` from a `UnixStream`.
- ///
- /// # Arguments
- /// `socket` - A `UnixStream`.
- pub fn new(socket: UnixStream) -> Self {
- AudioSocket { socket }
- }
-
- fn read_from_socket<T>(&mut self) -> io::Result<T>
- where
- T: Sized + DataInit + Default,
- {
- let mut message: T = Default::default();
- let rc = self.socket.read(message.as_mut_slice())?;
- if rc == mem::size_of::<T>() {
- Ok(message)
- } else {
- Err(io::Error::new(io::ErrorKind::Other, "Read truncated data."))
- }
- }
-
- /// Blocks reading an `audio message`.
- ///
- /// # Returns
- /// `AudioMessage` - AudioMessage enum.
- ///
- /// # Errors
- /// Returns io::Error if error occurs.
- pub fn read_audio_message(&mut self) -> io::Result<AudioMessage> {
- match self.read_audio_message_with_timeout(None)? {
- None => Err(io::Error::new(io::ErrorKind::Other, "Unexpected exit")),
- Some(message) => Ok(message),
- }
- }
-
- /// Blocks waiting for an `audio message` until `timeout` occurs. If `timeout`
- /// is None, blocks indefinitely.
- ///
- /// # Returns
- /// Some(AudioMessage) - AudioMessage enum if we receive a message before timeout.
- /// None - If the timeout expires.
- ///
- /// # Errors
- /// Returns io::Error if error occurs.
- pub fn read_audio_message_with_timeout(
- &mut self,
- timeout: Option<Duration>,
- ) -> io::Result<Option<AudioMessage>> {
- #[derive(PollToken)]
- enum Token {
- AudioMsg,
- }
- let poll_ctx: PollContext<Token> =
- match PollContext::new().and_then(|pc| pc.add(self, Token::AudioMsg).and(Ok(pc))) {
- Ok(pc) => pc,
- Err(e) => {
- return Err(io::Error::new(
- io::ErrorKind::Other,
- format!("Failed to create PollContext: {}", e),
- ));
- }
- };
- let events = {
- let result = match timeout {
- None => poll_ctx.wait(),
- Some(duration) => poll_ctx.wait_timeout(duration),
- };
- match result {
- Ok(v) => v,
- Err(e) => {
- return Err(io::Error::new(
- io::ErrorKind::Other,
- format!("Failed to poll: {:?}", e),
- ));
- }
- }
- };
-
- // Check the first readable message
- let tokens: Vec<Token> = events.iter_readable().map(|e| e.token()).collect();
- match tokens.get(0) {
- None => Ok(None),
- Some(&Token::AudioMsg) => {
- let raw_msg: audio_message = self.read_from_socket()?;
- Ok(Some(AudioMessage::from(raw_msg)))
- }
- }
- }
-
- /// Sends raw audio message with given AudioMessage enum.
- ///
- /// # Arguments
- /// * `msg` - enum AudioMessage, which could be `Success` with message id
- /// and frames or `Error` with error code.
- ///
- /// # Errors
- /// Returns error if `libc::write` fails.
- fn send_audio_message(&mut self, msg: AudioMessage) -> io::Result<()> {
- let msg: audio_message = msg.into();
- let rc = self.socket.write(msg.as_slice())?;
- if rc < mem::size_of::<audio_message>() {
- Err(io::Error::new(io::ErrorKind::Other, "Sent truncated data."))
- } else {
- Ok(())
- }
- }
-
- /// Sends the data ready message with written frame count.
- ///
- /// # Arguments
- /// * `frames` - An `u32` indicating the written frame count.
- pub fn data_ready(&mut self, frames: u32) -> io::Result<()> {
- self.send_audio_message(AudioMessage::Success {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY,
- frames,
- })
- }
-
- /// Sends the capture ready message with read frame count.
- ///
- /// # Arguments
- ///
- /// * `frames` - An `u32` indicating the number of read frames.
- pub fn capture_ready(&mut self, frames: u32) -> io::Result<()> {
- self.send_audio_message(AudioMessage::Success {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_CAPTURED,
- frames,
- })
- }
-}
-
-impl AsRawFd for AudioSocket {
- fn as_raw_fd(&self) -> RawFd {
- self.socket.as_raw_fd()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- // PartialEq for comparing AudioMessage in tests
- impl PartialEq for AudioMessage {
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (
- AudioMessage::Success { id, frames },
- AudioMessage::Success {
- id: other_id,
- frames: other_frames,
- },
- ) => id == other_id && frames == other_frames,
- (AudioMessage::Error(err), AudioMessage::Error(other_err)) => err == other_err,
- _ => false,
- }
- }
- }
-
- fn init_audio_socket_pair() -> (AudioSocket, AudioSocket) {
- let (sock1, sock2) = UnixStream::pair().unwrap();
- let sender = AudioSocket::new(sock1);
- let receiver = AudioSocket::new(sock2);
- (sender, receiver)
- }
-
- #[test]
- fn audio_socket_send_and_recv_audio_message() {
- let (mut sender, mut receiver) = init_audio_socket_pair();
- let message_succ = AudioMessage::Success {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA,
- frames: 0,
- };
- sender.send_audio_message(message_succ).unwrap();
- let res = receiver.read_audio_message().unwrap();
- assert_eq!(
- res,
- AudioMessage::Success {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA,
- frames: 0
- }
- );
-
- let message_err = AudioMessage::Error(123);
- sender.send_audio_message(message_err).unwrap();
- let res = receiver.read_audio_message().unwrap();
- assert_eq!(res, AudioMessage::Error(123));
- }
-
- #[test]
- fn audio_socket_data_ready_send_and_recv() {
- let (sock1, sock2) = UnixStream::pair().unwrap();
- let mut audio_socket_send = AudioSocket::new(sock1);
- let mut audio_socket_recv = AudioSocket::new(sock2);
- audio_socket_send.data_ready(256).unwrap();
-
- // Test receiving by using raw audio_message since CRAS audio server use this.
- let audio_msg: audio_message = audio_socket_recv.read_from_socket().unwrap();
- let ref_audio_msg = audio_message {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY,
- error: 0,
- frames: 256,
- };
- // Use brace to copy unaligned data locally
- assert_eq!({ audio_msg.id }, { ref_audio_msg.id });
- assert_eq!({ audio_msg.error }, { ref_audio_msg.error });
- assert_eq!({ audio_msg.frames }, { ref_audio_msg.frames });
- }
-
- #[test]
- fn audio_socket_capture_ready() {
- let (sock1, sock2) = UnixStream::pair().unwrap();
- let mut audio_socket_send = AudioSocket::new(sock1);
- let mut audio_socket_recv = AudioSocket::new(sock2);
- audio_socket_send
- .capture_ready(256)
- .expect("Failed to send capture ready message.");
-
- // Test receiving by using raw audio_message since CRAS audio server use this.
- let audio_msg: audio_message = audio_socket_recv
- .read_from_socket()
- .expect("Failed to read audio message from AudioSocket.");
- let ref_audio_msg = audio_message {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_CAPTURED,
- error: 0,
- frames: 256,
- };
- // Use brace to copy unaligned data locally
- assert_eq!({ audio_msg.id }, { ref_audio_msg.id });
- assert_eq!({ audio_msg.error }, { ref_audio_msg.error });
- assert_eq!({ audio_msg.frames }, { ref_audio_msg.frames });
- }
-
- #[test]
- fn audio_socket_send_when_broken_pipe() {
- let sock1 = {
- let (sock1, _) = UnixStream::pair().unwrap();
- sock1
- };
- let mut audio_socket = AudioSocket::new(sock1);
- let res = audio_socket.data_ready(256);
- //Broken pipe
- assert_eq!(
- res.expect_err("Result should be an error.").kind(),
- io::Error::from_raw_os_error(32).kind(),
- "Error should be broken pipe.",
- );
- }
-}
diff --git a/cras/client/libcras/src/cras_client_message.rs b/cras/client/libcras/src/cras_client_message.rs
deleted file mode 100644
index c1c5ec5c..00000000
--- a/cras/client/libcras/src/cras_client_message.rs
+++ /dev/null
@@ -1,200 +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::{array::TryFromSliceError, convert::TryInto, error, fmt, io, mem, os::unix::io::RawFd};
-
-use cras_sys::gen::{
- cras_client_connected, cras_client_message, cras_client_stream_connected,
- CRAS_CLIENT_MAX_MSG_SIZE,
- CRAS_CLIENT_MESSAGE_ID::{self, *},
-};
-use data_model::DataInit;
-use sys_util::ScmSocket;
-
-use crate::cras_server_socket::CrasServerSocket;
-use crate::cras_shm::*;
-use crate::cras_stream;
-
-#[derive(Debug)]
-pub enum Error {
- IoError(io::Error),
- SysUtilError(sys_util::Error),
- CrasStreamError(cras_stream::Error),
- ArrayTryFromSliceError(TryFromSliceError),
- InvalidSize,
- MessageTypeError,
- MessageNumFdError,
- MessageTruncated,
- MessageIdError,
- MessageFromSliceError,
-}
-
-impl error::Error for Error {}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Error::IoError(ref err) => err.fmt(f),
- Error::SysUtilError(ref err) => err.fmt(f),
- Error::MessageTypeError => write!(f, "Message type error"),
- Error::CrasStreamError(ref err) => err.fmt(f),
- Error::ArrayTryFromSliceError(ref err) => err.fmt(f),
- Error::MessageNumFdError => write!(f, "Message the number of fds is not matched"),
- Error::MessageTruncated => write!(f, "Read truncated message"),
- Error::MessageIdError => write!(f, "No such id"),
- Error::MessageFromSliceError => write!(f, "Message from slice error"),
- Error::InvalidSize => write!(f, "Invalid data size"),
- }
- }
-}
-
-type Result<T> = std::result::Result<T, Error>;
-
-impl From<io::Error> for Error {
- fn from(io_err: io::Error) -> Self {
- Error::IoError(io_err)
- }
-}
-
-impl From<sys_util::Error> for Error {
- fn from(sys_util_err: sys_util::Error) -> Self {
- Error::SysUtilError(sys_util_err)
- }
-}
-
-impl From<cras_stream::Error> for Error {
- fn from(err: cras_stream::Error) -> Self {
- Error::CrasStreamError(err)
- }
-}
-
-impl From<TryFromSliceError> for Error {
- fn from(err: TryFromSliceError) -> Self {
- Error::ArrayTryFromSliceError(err)
- }
-}
-
-/// A handled server result from one message sent from CRAS server.
-pub enum ServerResult {
- /// client_id, CrasServerStateShmFd
- Connected(u32, CrasServerStateShmFd),
- /// stream_id, header_fd, samples_fd
- StreamConnected(u32, CrasAudioShmHeaderFd, CrasShmFd),
- DebugInfoReady,
-}
-
-impl ServerResult {
- /// Reads and handles one server message and converts `CrasClientMessage` into `ServerResult`
- /// with error handling.
- ///
- /// # Arguments
- /// * `server_socket`: A reference to `CrasServerSocket`.
- pub fn handle_server_message(server_socket: &CrasServerSocket) -> Result<ServerResult> {
- let message = CrasClientMessage::try_new(&server_socket)?;
- match message.get_id()? {
- CRAS_CLIENT_MESSAGE_ID::CRAS_CLIENT_CONNECTED => {
- let cmsg: &cras_client_connected = message.get_message()?;
- // CRAS server should return a shared memory area which contains
- // `cras_server_state`.
- let server_state_fd = unsafe { CrasServerStateShmFd::new(message.fds[0]) };
- Ok(ServerResult::Connected(cmsg.client_id, server_state_fd))
- }
- CRAS_CLIENT_MESSAGE_ID::CRAS_CLIENT_STREAM_CONNECTED => {
- let cmsg: &cras_client_stream_connected = message.get_message()?;
- // CRAS should return two shared memory areas the first which has
- // mem::size_of::<cras_audio_shm_header>() bytes, and the second which has
- // `samples_shm_size` bytes.
- Ok(ServerResult::StreamConnected(
- cmsg.stream_id,
- // Safe because CRAS ensures that the first fd contains a cras_audio_shm_header
- unsafe { CrasAudioShmHeaderFd::new(message.fds[0]) },
- // Safe because CRAS ensures that the second fd has length 'samples_shm_size'
- unsafe { CrasShmFd::new(message.fds[1], cmsg.samples_shm_size as usize) },
- ))
- }
- CRAS_CLIENT_MESSAGE_ID::CRAS_CLIENT_AUDIO_DEBUG_INFO_READY => {
- Ok(ServerResult::DebugInfoReady)
- }
- _ => Err(Error::MessageTypeError),
- }
- }
-}
-
-// A structure for raw message with fds from CRAS server.
-struct CrasClientMessage {
- fds: [RawFd; 2],
- data: [u8; CRAS_CLIENT_MAX_MSG_SIZE as usize],
- len: usize,
-}
-
-/// The default constructor won't be used outside of this file and it's an optimization to prevent
-/// having to copy the message data from a temp buffer.
-impl Default for CrasClientMessage {
- // Initializes fields with default values.
- fn default() -> Self {
- Self {
- fds: [-1; 2],
- data: [0; CRAS_CLIENT_MAX_MSG_SIZE as usize],
- len: 0,
- }
- }
-}
-
-impl CrasClientMessage {
- // Reads a message from server_socket and checks validity of the read result
- fn try_new(server_socket: &CrasServerSocket) -> Result<CrasClientMessage> {
- let mut message: Self = Default::default();
- let (len, fd_nums) = server_socket.recv_with_fds(&mut message.data, &mut message.fds)?;
-
- if len < mem::size_of::<cras_client_message>() {
- Err(Error::MessageTruncated)
- } else {
- message.len = len;
- message.check_fd_nums(fd_nums)?;
- Ok(message)
- }
- }
-
- // Check if `fd nums` of a read result is valid
- fn check_fd_nums(&self, fd_nums: usize) -> Result<()> {
- match self.get_id()? {
- CRAS_CLIENT_CONNECTED => match fd_nums {
- 1 => Ok(()),
- _ => Err(Error::MessageNumFdError),
- },
- CRAS_CLIENT_STREAM_CONNECTED => match fd_nums {
- // CRAS should return two shared memory areas the first which has
- // mem::size_of::<cras_audio_shm_header>() bytes, and the second which has
- // `samples_shm_size` bytes.
- 2 => Ok(()),
- _ => Err(Error::MessageNumFdError),
- },
- CRAS_CLIENT_AUDIO_DEBUG_INFO_READY => match fd_nums {
- 0 => Ok(()),
- _ => Err(Error::MessageNumFdError),
- },
- _ => Err(Error::MessageTypeError),
- }
- }
-
- // Gets the message id
- fn get_id(&self) -> Result<CRAS_CLIENT_MESSAGE_ID> {
- let offset = mem::size_of::<u32>();
- match u32::from_le_bytes(self.data[offset..offset + 4].try_into()?) {
- id if id == (CRAS_CLIENT_CONNECTED as u32) => Ok(CRAS_CLIENT_CONNECTED),
- id if id == (CRAS_CLIENT_STREAM_CONNECTED as u32) => Ok(CRAS_CLIENT_STREAM_CONNECTED),
- id if id == (CRAS_CLIENT_AUDIO_DEBUG_INFO_READY as u32) => {
- Ok(CRAS_CLIENT_AUDIO_DEBUG_INFO_READY)
- }
- _ => Err(Error::MessageIdError),
- }
- }
-
- // Gets a reference to the message content
- fn get_message<T: DataInit>(&self) -> Result<&T> {
- if self.len != mem::size_of::<T>() {
- return Err(Error::InvalidSize);
- }
- T::from_slice(&self.data[..mem::size_of::<T>()]).ok_or(Error::MessageFromSliceError)
- }
-}
diff --git a/cras/client/libcras/src/cras_server_socket.rs b/cras/client/libcras/src/cras_server_socket.rs
deleted file mode 100644
index 4a7d9151..00000000
--- a/cras/client/libcras/src/cras_server_socket.rs
+++ /dev/null
@@ -1,124 +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::os::unix::io::{AsRawFd, RawFd};
-use std::{io, mem};
-
-use cras_sys::gen::{cras_disconnect_stream_message, cras_server_message, CRAS_SERVER_MESSAGE_ID};
-use sys_util::{net::UnixSeqpacket, ScmSocket};
-
-use data_model::DataInit;
-
-/// Server socket type to connect.
-pub enum CrasSocketType {
- /// A server socket type supports only playback function.
- Legacy,
- /// A server socket type supports both playback and capture functions.
- Unified,
-}
-
-impl CrasSocketType {
- fn sock_path(&self) -> &str {
- match self {
- Self::Legacy => "/run/cras/.cras_socket",
- Self::Unified => "/run/cras/.cras_unified",
- }
- }
-}
-
-/// A socket connecting to the CRAS audio server.
-pub struct CrasServerSocket {
- socket: UnixSeqpacket,
-}
-
-impl CrasServerSocket {
- pub fn new() -> io::Result<CrasServerSocket> {
- Self::with_type(CrasSocketType::Legacy)
- }
-
- /// Creates a `CrasServerSocket` with given `CrasSocketType`.
- ///
- /// # Errors
- ///
- /// Returns the `io::Error` generated when connecting to the socket on failure.
- pub fn with_type(socket_type: CrasSocketType) -> io::Result<CrasServerSocket> {
- Ok(CrasServerSocket {
- socket: UnixSeqpacket::connect(socket_type.sock_path())?,
- })
- }
-
- /// Sends a sized and packed server messge to the server socket. The message
- /// must implement `Sized` and `DataInit`.
- /// # Arguments
- /// * `message` - A sized and packed message.
- /// * `fds` - A slice of fds to send.
- ///
- /// # Returns
- /// * Length of written bytes in `usize`.
- ///
- /// # Errors
- /// Return error if the socket fails to write message to server.
- pub fn send_server_message_with_fds<M: Sized + DataInit>(
- &self,
- message: &M,
- fds: &[RawFd],
- ) -> io::Result<usize> {
- match fds.len() {
- 0 => self.socket.send(message.as_slice()),
- _ => {
- let ioslice = io::IoSlice::new(message.as_slice());
- match self.send_with_fds(&[ioslice], fds) {
- Ok(len) => Ok(len),
- Err(err) => Err(io::Error::new(io::ErrorKind::Other, format!("{}", err))),
- }
- }
- }
- }
-
- /// Creates a clone of the underlying socket. The returned clone can also be
- /// used to communicate with the cras server.
- pub fn try_clone(&self) -> io::Result<CrasServerSocket> {
- let new_sock = self.socket.try_clone()?;
- Ok(CrasServerSocket { socket: new_sock })
- }
-
- /// Send a message to request disconnection of the given stream.
- ///
- /// Builds a `cras_disconnect_stream_message` containing `stream_id` and
- /// sends it to the server.
- /// No response is expected.
- ///
- /// # Arguments
- ///
- /// * `stream_id` - The id of the stream that should be disconnected.
- ///
- /// # Errors
- ///
- /// * If the message was not written to the server socket successfully.
- pub fn disconnect_stream(&self, stream_id: u32) -> io::Result<()> {
- let msg_header = cras_server_message {
- length: mem::size_of::<cras_disconnect_stream_message>() as u32,
- id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_DISCONNECT_STREAM,
- };
- let server_cmsg = cras_disconnect_stream_message {
- header: msg_header,
- stream_id,
- };
- self.send_server_message_with_fds(&server_cmsg, &[])
- .map(|_| ())
- }
-}
-
-// For using `recv_with_fds` and `send_with_fds`.
-impl ScmSocket for CrasServerSocket {
- fn socket_fd(&self) -> RawFd {
- self.socket.as_raw_fd()
- }
-}
-
-// For using `PollContex`.
-impl AsRawFd for CrasServerSocket {
- fn as_raw_fd(&self) -> RawFd {
- self.socket.as_raw_fd()
- }
-}
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);
- }
-}
diff --git a/cras/client/libcras/src/cras_shm_stream.rs b/cras/client/libcras/src/cras_shm_stream.rs
deleted file mode 100644
index f72cc07c..00000000
--- a/cras/client/libcras/src/cras_shm_stream.rs
+++ /dev/null
@@ -1,191 +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::time::Duration;
-use std::{error, fmt};
-
-use audio_streams::{
- shm_streams::{BufferSet, ServerRequest, ShmStream},
- BoxError, SampleFormat, StreamDirection,
-};
-use cras_sys::gen::CRAS_AUDIO_MESSAGE_ID;
-use sys_util::error;
-
-use crate::audio_socket::{AudioMessage, AudioSocket};
-use crate::cras_server_socket::CrasServerSocket;
-use crate::cras_shm::{self, CrasAudioHeader, CrasAudioShmHeaderFd};
-
-#[derive(Debug)]
-pub enum Error {
- MessageTypeError,
- CaptureBufferTooSmall,
-}
-
-impl error::Error for Error {}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Error::MessageTypeError => write!(f, "Message type error"),
- Error::CaptureBufferTooSmall => write!(
- f,
- "Capture buffer too small, must have size at least 'used_size'."
- ),
- }
- }
-}
-
-/// An object that handles interactions with CRAS for a shm stream.
-/// The object implements `ShmStream` and so can be used to wait for
-/// `ServerRequest` and `BufferComplete` messages.
-pub struct CrasShmStream<'a> {
- stream_id: u32,
- server_socket: CrasServerSocket,
- audio_socket: AudioSocket,
- direction: StreamDirection,
- header: CrasAudioHeader<'a>,
- frame_size: usize,
- num_channels: usize,
- frame_rate: u32,
- // The index of the next buffer within SHM to set the buffer offset for.
- next_buffer_idx: usize,
-}
-
-impl<'a> CrasShmStream<'a> {
- /// Attempt to creates a CrasShmStream with the given arguments.
- ///
- /// # Arguments
- ///
- /// * `stream_id` - The server's ID for the stream.
- /// * `server_socket` - The socket that is connected to the server.
- /// * `audio_socket` - The socket for audio request and audio available messages.
- /// * `direction` - The direction of the stream, `Playback` or `Capture`.
- /// * `num_channels` - The number of audio channels for the stream.
- /// * `format` - The format to use for the stream's samples.
- /// * `header_fd` - The file descriptor for the audio header shm area.
- /// * `samples_len` - The size of the audio samples shm area.
- ///
- /// # Returns
- ///
- /// `CrasShmStream` - CRAS client stream.
- ///
- /// # Errors
- ///
- /// * If `header_fd` could not be successfully mmapped.
- #[allow(clippy::too_many_arguments)]
- pub fn try_new(
- stream_id: u32,
- server_socket: CrasServerSocket,
- audio_socket: AudioSocket,
- direction: StreamDirection,
- num_channels: usize,
- frame_rate: u32,
- format: SampleFormat,
- header_fd: CrasAudioShmHeaderFd,
- samples_len: usize,
- ) -> Result<Self, BoxError> {
- let header = cras_shm::create_header(header_fd, samples_len)?;
- Ok(Self {
- stream_id,
- server_socket,
- audio_socket,
- direction,
- header,
- frame_size: format.sample_bytes() * num_channels,
- num_channels,
- frame_rate,
- // We have either sent zero or two offsets to the server, so we will
- // need to update index 0 next.
- next_buffer_idx: 0,
- })
- }
-}
-
-impl<'a> Drop for CrasShmStream<'a> {
- /// Send the disconnect stream message and log an error if sending fails.
- fn drop(&mut self) {
- if let Err(e) = self.server_socket.disconnect_stream(self.stream_id) {
- error!("CrasShmStream::drop error: {}", e);
- }
- }
-}
-
-impl<'a> ShmStream for CrasShmStream<'a> {
- fn frame_size(&self) -> usize {
- self.frame_size
- }
-
- fn num_channels(&self) -> usize {
- self.num_channels
- }
-
- fn frame_rate(&self) -> u32 {
- self.frame_rate
- }
-
- fn wait_for_next_action_with_timeout(
- &mut self,
- timeout: Duration,
- ) -> Result<Option<ServerRequest>, BoxError> {
- let expected_id = match self.direction {
- StreamDirection::Playback => CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA,
- StreamDirection::Capture => CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY,
- };
-
- match self
- .audio_socket
- .read_audio_message_with_timeout(Some(timeout))?
- {
- Some(AudioMessage::Success { id, frames }) if id == expected_id => {
- Ok(Some(ServerRequest::new(frames as usize, self)))
- }
- None => Ok(None),
- _ => Err(Box::new(Error::MessageTypeError)),
- }
- }
-}
-
-impl BufferSet for CrasShmStream<'_> {
- fn callback(&mut self, offset: usize, frames: usize) -> Result<(), BoxError> {
- self.header
- .set_buffer_offset(self.next_buffer_idx, offset)?;
- self.next_buffer_idx ^= 1;
- let frames = frames as u32;
-
- match self.direction {
- StreamDirection::Playback => {
- self.header.commit_written_frames(frames)?;
-
- // Notify CRAS that we've made playback data available.
- self.audio_socket.data_ready(frames)?
- }
- StreamDirection::Capture => {
- let used_size = self.header.get_used_size();
- // Because CRAS doesn't know how long our buffer in shm is, we
- // must make sure that there are always at least buffer_size
- // frames available so that it doesn't write outside the buffer.
- if frames < (used_size / self.frame_size) as u32 {
- return Err(Box::new(Error::CaptureBufferTooSmall));
- }
-
- self.header.commit_read_frames(frames)?;
- self.audio_socket.capture_ready(frames)?;
- }
- }
-
- Ok(())
- }
-
- fn ignore(&mut self) -> Result<(), BoxError> {
- // We send an empty buffer for an ignored playback request since the
- // server will not read from a 0-length buffer. We don't do anything for
- // an ignored capture request, since we don't have a way to communicate
- // buffer length to the server, and we don't want the server writing
- // data to offsets within the SHM area that aren't audio buffers.
- if self.direction == StreamDirection::Playback {
- self.callback(0, 0)?;
- }
-
- Ok(())
- }
-}
diff --git a/cras/client/libcras/src/cras_stream.rs b/cras/client/libcras/src/cras_stream.rs
deleted file mode 100644
index f6004802..00000000
--- a/cras/client/libcras/src/cras_stream.rs
+++ /dev/null
@@ -1,224 +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::cmp::min;
-use std::io;
-use std::marker::PhantomData;
-use std::{error, fmt};
-
-use audio_streams::{
- capture::{CaptureBuffer, CaptureBufferStream},
- BoxError, BufferDrop, PlaybackBuffer, PlaybackBufferStream,
-};
-use cras_sys::gen::{snd_pcm_format_t, CRAS_AUDIO_MESSAGE_ID, CRAS_STREAM_DIRECTION};
-use sys_util::error;
-
-use crate::audio_socket::{AudioMessage, AudioSocket};
-use crate::cras_server_socket::CrasServerSocket;
-use crate::cras_shm::*;
-
-#[derive(Debug)]
-pub enum Error {
- IoError(io::Error),
- MessageTypeError,
-}
-
-impl error::Error for Error {}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Error::IoError(ref err) => err.fmt(f),
- Error::MessageTypeError => write!(f, "Message type error"),
- }
- }
-}
-
-impl From<io::Error> for Error {
- fn from(io_err: io::Error) -> Error {
- Error::IoError(io_err)
- }
-}
-
-/// A trait controls the state of `CrasAudioHeader` and
-/// interacts with server's audio thread through `AudioSocket`.
-pub trait CrasStreamData<'a>: Send {
- // Creates `CrasStreamData` with only `AudioSocket`.
- fn new(audio_sock: AudioSocket, header: CrasAudioHeader<'a>) -> Self;
- fn header_mut(&mut self) -> &mut CrasAudioHeader<'a>;
- fn audio_sock_mut(&mut self) -> &mut AudioSocket;
-}
-
-/// `CrasStreamData` implementation for `PlaybackBufferStream`.
-pub struct CrasPlaybackData<'a> {
- audio_sock: AudioSocket,
- header: CrasAudioHeader<'a>,
-}
-
-impl<'a> CrasStreamData<'a> for CrasPlaybackData<'a> {
- fn new(audio_sock: AudioSocket, header: CrasAudioHeader<'a>) -> Self {
- Self { audio_sock, header }
- }
-
- fn header_mut(&mut self) -> &mut CrasAudioHeader<'a> {
- &mut self.header
- }
-
- fn audio_sock_mut(&mut self) -> &mut AudioSocket {
- &mut self.audio_sock
- }
-}
-
-impl<'a> BufferDrop for CrasPlaybackData<'a> {
- fn trigger(&mut self, nframes: usize) {
- let log_err = |e| error!("BufferDrop error: {}", e);
- if let Err(e) = self.header.commit_written_frames(nframes as u32) {
- log_err(e);
- }
- if let Err(e) = self.audio_sock.data_ready(nframes as u32) {
- log_err(e);
- }
- }
-}
-
-/// `CrasStreamData` implementation for `CaptureBufferStream`.
-pub struct CrasCaptureData<'a> {
- audio_sock: AudioSocket,
- header: CrasAudioHeader<'a>,
-}
-
-impl<'a> CrasStreamData<'a> for CrasCaptureData<'a> {
- fn new(audio_sock: AudioSocket, header: CrasAudioHeader<'a>) -> Self {
- Self { audio_sock, header }
- }
-
- fn header_mut(&mut self) -> &mut CrasAudioHeader<'a> {
- &mut self.header
- }
-
- fn audio_sock_mut(&mut self) -> &mut AudioSocket {
- &mut self.audio_sock
- }
-}
-
-impl<'a> BufferDrop for CrasCaptureData<'a> {
- fn trigger(&mut self, nframes: usize) {
- let log_err = |e| error!("BufferDrop error: {}", e);
- if let Err(e) = self.header.commit_read_frames(nframes as u32) {
- log_err(e);
- }
- if let Err(e) = self.audio_sock.capture_ready(nframes as u32) {
- log_err(e);
- }
- }
-}
-
-#[allow(dead_code)]
-pub struct CrasStream<'a, T: CrasStreamData<'a> + BufferDrop> {
- stream_id: u32,
- server_socket: CrasServerSocket,
- block_size: u32,
- direction: CRAS_STREAM_DIRECTION,
- rate: u32,
- num_channels: usize,
- format: snd_pcm_format_t,
- /// A structure for stream to interact with server audio thread.
- controls: T,
- /// The `PhantomData` is used by `controls: T`
- phantom: PhantomData<CrasAudioHeader<'a>>,
- audio_buffer: CrasAudioBuffer,
-}
-
-impl<'a, T: CrasStreamData<'a> + BufferDrop> CrasStream<'a, T> {
- /// Creates a CrasStream by given arguments.
- ///
- /// # Returns
- /// `CrasStream` - CRAS client stream.
- #[allow(clippy::too_many_arguments)]
- pub fn try_new(
- stream_id: u32,
- server_socket: CrasServerSocket,
- block_size: u32,
- direction: CRAS_STREAM_DIRECTION,
- rate: u32,
- num_channels: usize,
- format: snd_pcm_format_t,
- audio_sock: AudioSocket,
- header_fd: CrasAudioShmHeaderFd,
- samples_fd: CrasShmFd,
- ) -> Result<Self, Error> {
- let (header, audio_buffer) = create_header_and_buffers(header_fd, samples_fd)?;
-
- Ok(Self {
- stream_id,
- server_socket,
- block_size,
- direction,
- rate,
- num_channels,
- format,
- controls: T::new(audio_sock, header),
- phantom: PhantomData,
- audio_buffer,
- })
- }
-
- fn wait_request_data(&mut self) -> Result<(), Error> {
- match self.controls.audio_sock_mut().read_audio_message()? {
- AudioMessage::Success {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA,
- ..
- } => Ok(()),
- _ => Err(Error::MessageTypeError),
- }
- }
-
- fn wait_data_ready(&mut self) -> Result<u32, Error> {
- match self.controls.audio_sock_mut().read_audio_message()? {
- AudioMessage::Success {
- id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY,
- frames,
- } => Ok(frames),
- _ => Err(Error::MessageTypeError),
- }
- }
-}
-
-impl<'a, T: CrasStreamData<'a> + BufferDrop> Drop for CrasStream<'a, T> {
- /// A blocking drop function, sends the disconnect message to `CrasClient` and waits for
- /// the return message.
- /// Logs an error message to stderr if the method fails.
- fn drop(&mut self) {
- if let Err(e) = self.server_socket.disconnect_stream(self.stream_id) {
- error!("CrasStream::Drop error: {}", e);
- }
- }
-}
-
-impl<'a, T: CrasStreamData<'a> + BufferDrop> PlaybackBufferStream for CrasStream<'a, T> {
- fn next_playback_buffer(&mut self) -> Result<PlaybackBuffer, BoxError> {
- // Wait for request audio message
- self.wait_request_data()?;
- let header = self.controls.header_mut();
- let frame_size = header.get_frame_size();
- let (offset, len) = header.get_write_offset_and_len()?;
- let buf = &mut self.audio_buffer.get_buffer()[offset..offset + len];
-
- PlaybackBuffer::new(frame_size, buf, &mut self.controls).map_err(Box::from)
- }
-}
-
-impl<'a, T: CrasStreamData<'a> + BufferDrop> CaptureBufferStream for CrasStream<'a, T> {
- fn next_capture_buffer(&mut self) -> Result<CaptureBuffer, BoxError> {
- // Wait for data ready message
- let frames = self.wait_data_ready()?;
- let header = self.controls.header_mut();
- let frame_size = header.get_frame_size();
- let shm_frames = header.get_readable_frames()?;
- let len = min(shm_frames, frames as usize) * frame_size;
- let offset = header.get_read_buffer_offset()?;
- let buf = &mut self.audio_buffer.get_buffer()[offset..offset + len];
-
- CaptureBuffer::new(frame_size, buf, &mut self.controls).map_err(Box::from)
- }
-}
diff --git a/cras/client/libcras/src/libcras.rs b/cras/client/libcras/src/libcras.rs
deleted file mode 100644
index 402a4a27..00000000
--- a/cras/client/libcras/src/libcras.rs
+++ /dev/null
@@ -1,699 +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.
-//! Provides an interface for playing and recording audio through CRAS server.
-//!
-//! `CrasClient` implements `StreamSource` trait and it can create playback or capture
-//! stream - `CrasStream` which can be a
-//! - `PlaybackBufferStream` for audio playback or
-//! - `CaptureBufferStream` for audio capture.
-//!
-//! # Example of file audio playback
-//!
-//! `PlaybackBuffer`s to be filled with audio samples are obtained by calling
-//! `next_playback_buffer` from `CrasStream`.
-//!
-//! Users playing audio fill the provided buffers with audio. When a `PlaybackBuffer` is dropped,
-//! the samples written to it are committed to the `CrasStream` it came from.
-//!
-//!
-//! ```
-//! // An example of playing raw audio data from a given file
-//! use std::env;
-//! use std::fs::File;
-//! use std::io::{Read, Write};
-//! use std::thread::{spawn, JoinHandle};
-//! type Result<T> = std::result::Result<T, BoxError>;
-//!
-//! use libcras::{BoxError, CrasClient, CrasClientType};
-//! use audio_streams::{SampleFormat, StreamSource};
-//!
-//! const BUFFER_SIZE: usize = 256;
-//! const FRAME_RATE: u32 = 44100;
-//! const NUM_CHANNELS: usize = 2;
-//! const FORMAT: SampleFormat = SampleFormat::S16LE;
-//!
-//! # fn main() -> Result<()> {
-//! # let args: Vec<String> = env::args().collect();
-//! # match args.len() {
-//! # 2 => {
-//! let mut cras_client = CrasClient::new()?;
-//! cras_client.set_client_type(CrasClientType::CRAS_CLIENT_TYPE_TEST);
-//! let (_control, mut stream) = cras_client
-//! .new_playback_stream(NUM_CHANNELS, FORMAT, FRAME_RATE, BUFFER_SIZE)?;
-//!
-//! // Plays 1000 * BUFFER_SIZE samples from the given file
-//! let mut file = File::open(&args[1])?;
-//! let mut local_buffer = [0u8; BUFFER_SIZE * NUM_CHANNELS * 2];
-//! for _i in 0..1000 {
-//! // Reads data to local buffer
-//! let _read_count = file.read(&mut local_buffer)?;
-//!
-//! // Gets writable buffer from stream and
-//! let mut buffer = stream.next_playback_buffer()?;
-//! // Writes data to stream buffer
-//! let _write_frames = buffer.write(&local_buffer)?;
-//! }
-//! // Stream and client should gracefully be closed out of this scope
-//! # }
-//! # _ => {
-//! # println!("{} /path/to/playback_file.raw", args[0]);
-//! # }
-//! # };
-//! # Ok(())
-//! # }
-//! ```
-//!
-//! # Example of file audio capture
-//!
-//! `CaptureBuffer`s which contain audio samples are obtained by calling
-//! `next_capture_buffer` from `CrasStream`.
-//!
-//! Users get captured audio samples from the provided buffers. When a `CaptureBuffer` is dropped,
-//! the number of read samples will be committed to the `CrasStream` it came from.
-//! ```
-//! use std::env;
-//! use std::fs::File;
-//! use std::io::{Read, Write};
-//! use std::thread::{spawn, JoinHandle};
-//! type Result<T> = std::result::Result<T, BoxError>;
-//!
-//! use libcras::{BoxError, CrasClient, CrasClientType};
-//! use audio_streams::{SampleFormat, StreamSource};
-//!
-//! const BUFFER_SIZE: usize = 256;
-//! const FRAME_RATE: u32 = 44100;
-//! const NUM_CHANNELS: usize = 2;
-//! const FORMAT: SampleFormat = SampleFormat::S16LE;
-//!
-//! # fn main() -> Result<()> {
-//! # let args: Vec<String> = env::args().collect();
-//! # match args.len() {
-//! # 2 => {
-//! let mut cras_client = CrasClient::new()?;
-//! cras_client.set_client_type(CrasClientType::CRAS_CLIENT_TYPE_TEST);
-//! let (_control, mut stream) = cras_client
-//! .new_capture_stream(NUM_CHANNELS, FORMAT, FRAME_RATE, BUFFER_SIZE)?;
-//!
-//! // Capture 1000 * BUFFER_SIZE samples to the given file
-//! let mut file = File::create(&args[1])?;
-//! let mut local_buffer = [0u8; BUFFER_SIZE * NUM_CHANNELS * 2];
-//! for _i in 0..1000 {
-//!
-//! // Gets readable buffer from stream and
-//! let mut buffer = stream.next_capture_buffer()?;
-//! // Reads data to local buffer
-//! let read_count = buffer.read(&mut local_buffer)?;
-//! // Writes data to file
-//! let _read_frames = file.write(&local_buffer[..read_count])?;
-//! }
-//! // Stream and client should gracefully be closed out of this scope
-//! # }
-//! # _ => {
-//! # println!("{} /path/to/capture_file.raw", args[0]);
-//! # }
-//! # };
-//! # Ok(())
-//! # }
-//! ```
-use std::io;
-use std::mem;
-use std::os::unix::{
- io::{AsRawFd, RawFd},
- net::UnixStream,
-};
-use std::{error, fmt};
-
-pub use audio_streams::BoxError;
-use audio_streams::{
- capture::{CaptureBufferStream, NoopCaptureStream},
- shm_streams::{NullShmStream, ShmStream, ShmStreamSource},
- BufferDrop, NoopStreamControl, PlaybackBufferStream, SampleFormat, StreamControl,
- StreamDirection, StreamEffect, StreamSource,
-};
-use cras_sys::gen::*;
-pub use cras_sys::gen::{
- CRAS_CLIENT_TYPE as CrasClientType, CRAS_NODE_TYPE as CrasNodeType,
- CRAS_STREAM_EFFECT as CrasStreamEffect,
-};
-pub use cras_sys::{AudioDebugInfo, CrasIodevInfo, CrasIonodeInfo, Error as CrasSysError};
-use sys_util::{PollContext, PollToken, SharedMemory};
-
-mod audio_socket;
-use crate::audio_socket::AudioSocket;
-mod cras_server_socket;
-use crate::cras_server_socket::CrasServerSocket;
-pub use crate::cras_server_socket::CrasSocketType;
-mod cras_shm;
-use crate::cras_shm::CrasServerState;
-pub mod cras_shm_stream;
-use crate::cras_shm_stream::CrasShmStream;
-mod cras_stream;
-use crate::cras_stream::{CrasCaptureData, CrasPlaybackData, CrasStream, CrasStreamData};
-mod cras_client_message;
-use crate::cras_client_message::*;
-
-#[derive(Debug)]
-pub enum Error {
- CrasClientMessageError(cras_client_message::Error),
- CrasStreamError(cras_stream::Error),
- CrasSysError(cras_sys::Error),
- IoError(io::Error),
- SysUtilError(sys_util::Error),
- MessageTypeError,
- UnexpectedExit,
-}
-
-impl error::Error for Error {}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Error::CrasClientMessageError(ref err) => err.fmt(f),
- Error::CrasStreamError(ref err) => err.fmt(f),
- Error::CrasSysError(ref err) => err.fmt(f),
- Error::IoError(ref err) => err.fmt(f),
- Error::SysUtilError(ref err) => err.fmt(f),
- Error::MessageTypeError => write!(f, "Message type error"),
- Error::UnexpectedExit => write!(f, "Unexpected exit"),
- }
- }
-}
-
-type Result<T> = std::result::Result<T, Error>;
-
-impl From<io::Error> for Error {
- fn from(io_err: io::Error) -> Self {
- Error::IoError(io_err)
- }
-}
-
-impl From<sys_util::Error> for Error {
- fn from(sys_util_err: sys_util::Error) -> Self {
- Error::SysUtilError(sys_util_err)
- }
-}
-
-impl From<cras_stream::Error> for Error {
- fn from(err: cras_stream::Error) -> Self {
- Error::CrasStreamError(err)
- }
-}
-
-impl From<cras_client_message::Error> for Error {
- fn from(err: cras_client_message::Error) -> Self {
- Error::CrasClientMessageError(err)
- }
-}
-
-/// A CRAS server client, which implements StreamSource and ShmStreamSource.
-/// It can create audio streams connecting to CRAS server.
-pub struct CrasClient<'a> {
- server_socket: CrasServerSocket,
- server_state: CrasServerState<'a>,
- client_id: u32,
- next_stream_id: u32,
- cras_capture: bool,
- client_type: CRAS_CLIENT_TYPE,
-}
-
-impl<'a> CrasClient<'a> {
- /// Blocks creating a `CrasClient` with registered `client_id`
- ///
- /// # Results
- ///
- /// * `CrasClient` - A client to interact with CRAS server
- ///
- /// # Errors
- ///
- /// Returns error if error occurs while handling server message or message
- /// type is incorrect
- pub fn new() -> Result<Self> {
- Self::with_type(CrasSocketType::Legacy)
- }
-
- /// Tries to create a `CrasClient` with a given `CrasSocketType`.
- ///
- /// # Errors
- ///
- /// Returns error if error occurs while handling server message or message
- /// type is incorrect.
- pub fn with_type(socket_type: CrasSocketType) -> Result<Self> {
- // Create a connection to the server.
- let mut server_socket = CrasServerSocket::with_type(socket_type)?;
- // Gets client ID and server state fd from server
- if let ServerResult::Connected(client_id, server_state_fd) =
- CrasClient::wait_for_message(&mut server_socket)?
- {
- Ok(Self {
- server_socket,
- server_state: CrasServerState::try_new(server_state_fd)?,
- client_id,
- next_stream_id: 0,
- cras_capture: false,
- client_type: CRAS_CLIENT_TYPE::CRAS_CLIENT_TYPE_UNKNOWN,
- })
- } else {
- Err(Error::MessageTypeError)
- }
- }
-
- /// Enables capturing audio through CRAS server.
- pub fn enable_cras_capture(&mut self) {
- self.cras_capture = true;
- }
-
- /// Set the type of this client to report to CRAS when connecting streams.
- pub fn set_client_type(&mut self, client_type: CRAS_CLIENT_TYPE) {
- self.client_type = client_type;
- }
-
- /// Sets the system volume to `volume`.
- ///
- /// Send a message to the server to request setting the system volume
- /// to `volume`. No response is returned from the server.
- ///
- /// # Errors
- ///
- /// If writing the message to the server socket failed.
- pub fn set_system_volume(&mut self, volume: u32) -> Result<()> {
- let header = cras_server_message {
- length: mem::size_of::<cras_set_system_volume>() as u32,
- id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_SET_SYSTEM_VOLUME,
- };
- let msg = cras_set_system_volume { header, volume };
-
- self.server_socket.send_server_message_with_fds(&msg, &[])?;
- Ok(())
- }
-
- /// Sets the system mute status to `mute`.
- ///
- /// Send a message to the server to request setting the system mute
- /// to `mute`. No response is returned from the server.
- ///
- /// # Errors
- ///
- /// If writing the message to the server socket failed.
- pub fn set_system_mute(&mut self, mute: bool) -> Result<()> {
- let header = cras_server_message {
- length: mem::size_of::<cras_set_system_mute>() as u32,
- id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_SET_SYSTEM_MUTE,
- };
- let msg = cras_set_system_mute {
- header,
- mute: mute as i32,
- };
-
- self.server_socket.send_server_message_with_fds(&msg, &[])?;
- Ok(())
- }
-
- /// Gets the system volume.
- ///
- /// Read the current value for system volume from the server shared memory.
- pub fn get_system_volume(&self) -> u32 {
- self.server_state.get_system_volume()
- }
-
- /// Gets the system mute.
- ///
- /// Read the current value for system mute from the server shared memory.
- pub fn get_system_mute(&self) -> bool {
- self.server_state.get_system_mute()
- }
-
- /// Gets a list of output devices
- ///
- /// Read a list of the currently attached output devices from the server shared memory.
- pub fn output_devices(&self) -> impl Iterator<Item = CrasIodevInfo> {
- self.server_state.output_devices()
- }
-
- /// Gets a list of input devices
- ///
- /// Read a list of the currently attached input devices from the server shared memory.
- pub fn input_devices(&self) -> impl Iterator<Item = CrasIodevInfo> {
- self.server_state.input_devices()
- }
-
- /// Gets a list of output nodes
- ///
- /// Read a list of the currently attached output nodes from the server shared memory.
- pub fn output_nodes(&self) -> impl Iterator<Item = CrasIonodeInfo> {
- self.server_state.output_nodes()
- }
-
- /// Gets a list of input nodes
- ///
- /// Read a list of the currently attached input nodes from the server shared memory.
- pub fn input_nodes(&self) -> impl Iterator<Item = CrasIonodeInfo> {
- self.server_state.input_nodes()
- }
-
- /// Gets the server's audio debug info.
- ///
- /// Sends a message to the server requesting an update of audio debug info,
- /// waits for the response, and then reads the info from the server state.
- ///
- /// # Errors
- ///
- /// * If sending the message to the server failed.
- /// * If an unexpected response message is received.
- pub fn get_audio_debug_info(&mut self) -> Result<AudioDebugInfo> {
- let header = cras_server_message {
- length: mem::size_of::<cras_dump_audio_thread>() as u32,
- id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_DUMP_AUDIO_THREAD,
- };
- let msg = cras_dump_audio_thread { header };
-
- self.server_socket.send_server_message_with_fds(&msg, &[])?;
-
- match CrasClient::wait_for_message(&mut self.server_socket)? {
- ServerResult::DebugInfoReady => Ok(self
- .server_state
- .get_audio_debug_info()
- .map_err(Error::CrasSysError)?),
- _ => Err(Error::MessageTypeError),
- }
- }
-
- // Gets next server_stream_id from client and increment stream_id counter.
- fn next_server_stream_id(&mut self) -> u32 {
- let res = self.next_stream_id;
- self.next_stream_id += 1;
- self.server_stream_id(res)
- }
-
- // Gets server_stream_id from given stream_id
- fn server_stream_id(&self, stream_id: u32) -> u32 {
- (self.client_id << 16) | stream_id
- }
-
- // Creates general stream with given parameters
- fn create_stream<'b, T: BufferDrop + CrasStreamData<'b>>(
- &mut self,
- device_index: Option<u32>,
- block_size: u32,
- direction: CRAS_STREAM_DIRECTION,
- rate: u32,
- channel_num: usize,
- format: SampleFormat,
- ) -> Result<CrasStream<'b, T>> {
- let stream_id = self.next_server_stream_id();
-
- // Prepares server message
- let audio_format =
- cras_audio_format_packed::new(format.into(), rate, channel_num, direction);
- let msg_header = cras_server_message {
- length: mem::size_of::<cras_connect_message>() as u32,
- id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_CONNECT_STREAM,
- };
- let server_cmsg = cras_connect_message {
- header: msg_header,
- proto_version: CRAS_PROTO_VER,
- direction,
- stream_id,
- stream_type: CRAS_STREAM_TYPE::CRAS_STREAM_TYPE_DEFAULT,
- buffer_frames: block_size,
- cb_threshold: block_size,
- flags: 0,
- format: audio_format,
- dev_idx: device_index.unwrap_or(CRAS_SPECIAL_DEVICE::NO_DEVICE as u32),
- effects: 0,
- client_type: self.client_type,
- client_shm_size: 0,
- buffer_offsets: [0, 0],
- };
-
- // Creates AudioSocket pair
- let (sock1, sock2) = UnixStream::pair()?;
-
- // Sends `CRAS_SERVER_CONNECT_STREAM` message
- let socks = [sock2.as_raw_fd()];
- self.server_socket
- .send_server_message_with_fds(&server_cmsg, &socks)?;
-
- let audio_socket = AudioSocket::new(sock1);
- loop {
- let result = CrasClient::wait_for_message(&mut self.server_socket)?;
- if let ServerResult::StreamConnected(_stream_id, header_fd, samples_fd) = result {
- return CrasStream::try_new(
- stream_id,
- self.server_socket.try_clone()?,
- block_size,
- direction,
- rate,
- channel_num,
- format.into(),
- audio_socket,
- header_fd,
- samples_fd,
- )
- .map_err(Error::CrasStreamError);
- }
- }
- }
-
- /// Creates a new playback stream pinned to the device at `device_index`.
- ///
- /// # Arguments
- ///
- /// * `device_index` - The device to which the stream will be attached.
- /// * `num_channels` - The count of audio channels for the stream.
- /// * `format` - The format to use for stream audio samples.
- /// * `frame_rate` - The sample rate of the stream.
- /// * `buffer_size` - The transfer size granularity in frames.
- #[allow(clippy::type_complexity)]
- pub fn new_pinned_playback_stream(
- &mut self,
- device_index: u32,
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>
- {
- Ok((
- Box::new(NoopStreamControl::new()),
- Box::new(self.create_stream::<CrasPlaybackData>(
- Some(device_index),
- buffer_size as u32,
- CRAS_STREAM_DIRECTION::CRAS_STREAM_OUTPUT,
- frame_rate,
- num_channels,
- format,
- )?),
- ))
- }
-
- /// Creates a new capture stream pinned to the device at `device_index`.
- ///
- /// This is useful for, among other things, capturing from a loopback
- /// device.
- ///
- /// # Arguments
- ///
- /// * `device_index` - The device to which the stream will be attached.
- /// * `num_channels` - The count of audio channels for the stream.
- /// * `format` - The format to use for stream audio samples.
- /// * `frame_rate` - The sample rate of the stream.
- /// * `buffer_size` - The transfer size granularity in frames.
- #[allow(clippy::type_complexity)]
- pub fn new_pinned_capture_stream(
- &mut self,
- device_index: u32,
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
- Ok((
- Box::new(NoopStreamControl::new()),
- Box::new(self.create_stream::<CrasCaptureData>(
- Some(device_index),
- buffer_size as u32,
- CRAS_STREAM_DIRECTION::CRAS_STREAM_INPUT,
- frame_rate,
- num_channels,
- format,
- )?),
- ))
- }
-
- // Blocks handling the first server message received from `socket`.
- fn wait_for_message(socket: &mut CrasServerSocket) -> Result<ServerResult> {
- #[derive(PollToken)]
- enum Token {
- ServerMsg,
- }
- let poll_ctx: PollContext<Token> =
- PollContext::new().and_then(|pc| pc.add(socket, Token::ServerMsg).and(Ok(pc)))?;
-
- let events = poll_ctx.wait()?;
- // Check the first readable message
- let tokens: Vec<Token> = events.iter_readable().map(|e| e.token()).collect();
- tokens
- .get(0)
- .ok_or(Error::UnexpectedExit)
- .and_then(|ref token| {
- match token {
- Token::ServerMsg => ServerResult::handle_server_message(socket),
- }
- .map_err(Into::into)
- })
- }
-
- /// Returns any open file descriptors needed by CrasClient.
- /// This function is shared between StreamSource and ShmStreamSource.
- fn keep_fds(&self) -> Vec<RawFd> {
- vec![self.server_socket.as_raw_fd()]
- }
-}
-
-impl<'a> StreamSource for CrasClient<'a> {
- #[allow(clippy::type_complexity)]
- fn new_playback_stream(
- &mut self,
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>
- {
- Ok((
- Box::new(NoopStreamControl::new()),
- Box::new(self.create_stream::<CrasPlaybackData>(
- None,
- buffer_size as u32,
- CRAS_STREAM_DIRECTION::CRAS_STREAM_OUTPUT,
- frame_rate,
- num_channels,
- format,
- )?),
- ))
- }
-
- #[allow(clippy::type_complexity)]
- fn new_capture_stream(
- &mut self,
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> {
- if self.cras_capture {
- Ok((
- Box::new(NoopStreamControl::new()),
- Box::new(self.create_stream::<CrasCaptureData>(
- None,
- buffer_size as u32,
- CRAS_STREAM_DIRECTION::CRAS_STREAM_INPUT,
- frame_rate,
- num_channels,
- format,
- )?),
- ))
- } else {
- Ok((
- Box::new(NoopStreamControl::new()),
- Box::new(NoopCaptureStream::new(
- num_channels,
- format,
- frame_rate,
- buffer_size,
- )),
- ))
- }
- }
-
- fn keep_fds(&self) -> Option<Vec<RawFd>> {
- Some(CrasClient::keep_fds(self))
- }
-}
-
-impl<'a> ShmStreamSource for CrasClient<'a> {
- fn new_stream(
- &mut self,
- direction: StreamDirection,
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- effects: &[StreamEffect],
- client_shm: &SharedMemory,
- buffer_offsets: [u64; 2],
- ) -> std::result::Result<Box<dyn ShmStream>, BoxError> {
- if direction == StreamDirection::Capture && !self.cras_capture {
- return Ok(Box::new(NullShmStream::new(
- buffer_size,
- num_channels,
- format,
- frame_rate,
- )));
- }
-
- let buffer_size = buffer_size as u32;
-
- // Prepares server message
- let stream_id = self.next_server_stream_id();
- let audio_format = cras_audio_format_packed::new(
- format.into(),
- frame_rate,
- num_channels,
- direction.into(),
- );
- let msg_header = cras_server_message {
- length: mem::size_of::<cras_connect_message>() as u32,
- id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_CONNECT_STREAM,
- };
-
- let server_cmsg = cras_connect_message {
- header: msg_header,
- proto_version: CRAS_PROTO_VER,
- direction: direction.into(),
- stream_id,
- stream_type: CRAS_STREAM_TYPE::CRAS_STREAM_TYPE_DEFAULT,
- buffer_frames: buffer_size,
- cb_threshold: buffer_size,
- flags: 0,
- format: audio_format,
- dev_idx: CRAS_SPECIAL_DEVICE::NO_DEVICE as u32,
- effects: effects.iter().collect::<CrasStreamEffect>().into(),
- client_type: self.client_type,
- client_shm_size: client_shm.size(),
- buffer_offsets,
- };
-
- // Creates AudioSocket pair
- let (sock1, sock2) = UnixStream::pair()?;
-
- // Sends `CRAS_SERVER_CONNECT_STREAM` message
- let fds = [sock2.as_raw_fd(), client_shm.as_raw_fd()];
- self.server_socket
- .send_server_message_with_fds(&server_cmsg, &fds)?;
-
- loop {
- let result = CrasClient::wait_for_message(&mut self.server_socket)?;
- if let ServerResult::StreamConnected(_stream_id, header_fd, _samples_fd) = result {
- let audio_socket = AudioSocket::new(sock1);
- let stream = CrasShmStream::try_new(
- stream_id,
- self.server_socket.try_clone()?,
- audio_socket,
- direction,
- num_channels,
- frame_rate,
- format,
- header_fd,
- client_shm.size() as usize,
- )?;
- return Ok(Box::new(stream));
- }
- }
- }
-
- fn keep_fds(&self) -> Vec<RawFd> {
- CrasClient::keep_fds(self)
- }
-}