blob: ac56144cb42304e7e11008496591a881d585f3af [file] [log] [blame]
// 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.",
);
}
}