Port of ac97 from crosvm with pulseaudio backend.

This commit is contained in:
Bruce Leidl 2023-08-03 08:23:20 -04:00
parent 13efc4fa11
commit cded52b7c9
19 changed files with 2579 additions and 5 deletions

View File

@ -16,4 +16,5 @@ thiserror = "1.0"
vmm-sys-util = "0.11.1"
kvm-ioctls = "0.12.0"
kvm-bindings = "0.6.0"
pulse = { version = "2.27.1", package = "libpulse-binding" }
libcitadel = { git = "https://github.com/brl/citadel-tools", rev="44d5ce660f1f5cf8a3ad1060b143926a99be5148" }

113
src/audio/mod.rs Normal file
View File

@ -0,0 +1,113 @@
use std::{error, fmt};
use std::fmt::Display;
use std::str::FromStr;
use thiserror::Error;
pub mod shm_streams;
pub mod pulse;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SampleFormat {
U8,
S16LE,
S24LE,
S32LE,
}
impl SampleFormat {
pub fn sample_bytes(self) -> usize {
use SampleFormat::*;
match self {
U8 => 1,
S16LE => 2,
S24LE => 4, // Not a typo, S24_LE samples are stored in 4 byte chunks.
S32LE => 4,
}
}
}
impl Display for SampleFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use SampleFormat::*;
match self {
U8 => write!(f, "Unsigned 8 bit"),
S16LE => write!(f, "Signed 16 bit Little Endian"),
S24LE => write!(f, "Signed 24 bit Little Endian"),
S32LE => write!(f, "Signed 32 bit Little Endian"),
}
}
}
impl FromStr for SampleFormat {
type Err = SampleFormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"U8" => Ok(SampleFormat::U8),
"S16_LE" => Ok(SampleFormat::S16LE),
"S24_LE" => Ok(SampleFormat::S24LE),
"S32_LE" => Ok(SampleFormat::S32LE),
_ => Err(SampleFormatError::InvalidSampleFormat),
}
}
}
/// Errors that are possible from a `SampleFormat`.
#[derive(Error, Debug)]
pub enum SampleFormatError {
#[error("Must be in [U8, S16_LE, S24_LE, S32_LE]")]
InvalidSampleFormat,
}
/// Valid directions of an audio stream.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StreamDirection {
Playback,
Capture,
}
/// Valid effects for an audio stream.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StreamEffect {
NoEffect,
EchoCancellation,
}
impl Default for StreamEffect {
fn default() -> Self {
StreamEffect::NoEffect
}
}
/// Errors that can pass across threads.
pub type BoxError = Box<dyn error::Error + Send + Sync>;
/// Errors that are possible from a `StreamEffect`.
#[derive(Error, Debug)]
pub enum StreamEffectError {
#[error("Must be in [EchoCancellation, aec]")]
InvalidEffect,
}
impl FromStr for StreamEffect {
type Err = StreamEffectError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"EchoCancellation" | "aec" => Ok(StreamEffect::EchoCancellation),
_ => Err(StreamEffectError::InvalidEffect),
}
}
}
/// `StreamControl` provides a way to set the volume and mute states of a stream. `StreamControl`
/// is separate from the stream so it can be owned by a different thread if needed.
pub trait StreamControl: Send + Sync {
fn set_volume(&mut self, _scaler: f64) {}
fn set_mute(&mut self, _mute: bool) {}
}
/// `BufferCommit` is a cleanup funcion that must be called before dropping the buffer,
/// allowing arbitrary code to be run after the buffer is filled or read by the user.
pub trait BufferCommit {
/// `write_playback_buffer` or `read_capture_buffer` would trigger this automatically. `nframes`
/// indicates the number of audio frames that were read or written to the device.
fn commit(&mut self, nframes: usize);
}

66
src/audio/pulse/client.rs Normal file
View File

@ -0,0 +1,66 @@
use std::sync::mpsc;
use std::thread;
use pulse::sample::{Format, Spec};
use crate::audio::pulse::context::PulseContext;
use crate::audio::pulse::message::PulseMessageChannel;
use crate::audio::pulse::Result;
use crate::audio::{SampleFormat, StreamDirection};
use crate::audio::shm_streams::{GenericResult, NullShmStream, ShmStream, ShmStreamSource};
use crate::memory::GuestRam;
pub struct PulseClient {
channel: PulseMessageChannel,
}
impl PulseClient {
pub fn connect(guest_ram: GuestRam) -> Result<Self> {
let (tx,rx) = mpsc::channel();
let _ = thread::spawn(move || {
let mut ctx = PulseContext::new(guest_ram);
if let Err(err) = ctx.connect() {
warn!("PulseAudio Error: {}", err);
} else {
ctx.run(rx);
}
});
Ok(PulseClient {
channel: PulseMessageChannel::new(tx),
})
}
fn create_spec(num_channels: usize, format: SampleFormat, frame_rate: u32) -> Spec {
let format = match format {
SampleFormat::U8 => Format::U8,
SampleFormat::S16LE => Format::S16le,
SampleFormat::S24LE => Format::S24le,
SampleFormat::S32LE => Format::S32le,
};
Spec {
format,
rate: frame_rate,
channels: num_channels as u8,
}
}
}
impl ShmStreamSource for PulseClient {
fn new_stream(&mut self,
direction: StreamDirection,
num_channels: usize,
format: SampleFormat,
frame_rate: u32,
buffer_size: usize)-> GenericResult<Box<dyn ShmStream>> {
if direction != StreamDirection::Playback {
let stream = NullShmStream::new(buffer_size, num_channels, format, frame_rate);
return Ok(Box::new(stream))
}
let spec = PulseClient::create_spec(num_channels, format, frame_rate);
let stream = self.channel.send_new_playback_stream(spec, buffer_size, self.channel.clone())?;
Ok(Box::new(stream))
}
}

148
src/audio/pulse/context.rs Normal file
View File

@ -0,0 +1,148 @@
use std::cell::RefCell;
use std::ops::DerefMut;
use std::rc::Rc;
use std::sync::mpsc::Receiver;
use pulse::context::{Context, FlagSet, State};
use pulse::mainloop::threaded::Mainloop;
use pulse::proplist::{properties, Proplist};
use pulse::sample::Spec;
use pulse::stream::Stream;
use crate::memory::GuestRam;
use crate::audio::pulse::{Result, PulseError, PulseStream};
use crate::audio::pulse::message::{PulseContextMessage, PulseContextRequest, PulseMessageChannel};
pub struct PulseContext {
guest_ram: GuestRam,
mainloop: Rc<RefCell<Mainloop>>,
context: Rc<RefCell<Context>>,
}
impl PulseContext {
pub fn mainloop_lock(&self) {
self.mainloop.borrow_mut().lock();
}
pub fn mainloop_unlock(&self) {
self.mainloop.borrow_mut().unlock();
}
pub fn mainloop_wait(&self) {
self.mainloop.borrow_mut().wait();
}
pub fn mainloop(&self) -> Rc<RefCell<Mainloop>> {
self.mainloop.clone()
}
pub fn new(guest_ram: GuestRam) -> Self {
let mainloop = Mainloop::new()
.expect("Failed to create a pulseaudio mainloop");
let mut proplist = Proplist::new()
.expect("Failed to create pulseaudio proplist");
proplist.set_str(properties::APPLICATION_NAME, "pH")
.expect("Failed to set pulseaudio property");
let context = Context::new_with_proplist(
&mainloop,
"pHContext",
&proplist
).expect("Failed to create a pulseaudio context");
PulseContext {
guest_ram,
mainloop: Rc::new(RefCell::new(mainloop)),
context: Rc::new(RefCell::new(context)),
}
}
fn start_and_connect(&self) -> Result<()> {
self.mainloop_lock();
self.context.borrow_mut().set_state_callback(Some(Box::new({
let ml_ref = self.mainloop.clone();
move || unsafe {
(*ml_ref.as_ptr()).signal(false);
}
})));
self.context.borrow_mut().connect(None, FlagSet::NOFLAGS, None)
.map_err(PulseError::ConnectFailed)?;
self.mainloop.borrow_mut().start()
.map_err(PulseError::StartFailed)?;
Ok(())
}
fn wait_context_connected(&self) -> Result<()> {
loop {
let st = self.context.borrow().get_state();
if st == State::Ready {
break;
} else if !st.is_good() {
return Err(PulseError::ConnectFailedErr)
}
self.mainloop.borrow_mut().wait();
}
Ok(())
}
fn context_connect_finish(&self) {
self.context.borrow_mut().set_state_callback(None);
self.mainloop_unlock();
}
pub fn connect(&self) -> Result<()> {
let result = self.start_and_connect().and_then(|()| {
self.wait_context_connected()
});
self.context_connect_finish();
result
}
fn new_playback_stream(&self, spec: Spec, buffer_size: usize, channel: PulseMessageChannel) -> PulseStream {
self.mainloop_lock();
let stream = Stream::new(self.context.borrow_mut().deref_mut(),
"ph-pa-playback",
&spec,
None)
.expect("Failed to create pulseaudio stream");
let ps = PulseStream::new_playback(stream, self.guest_ram.clone(), spec, buffer_size, channel);
self.mainloop_unlock();
ps
}
pub fn run(&mut self, receiver: Receiver<PulseContextMessage>) {
loop {
match receiver.recv() {
Ok(msg) => self.dispatch_message(msg),
Err(_) => break,
}
}
}
fn dispatch_message(&mut self, msg: PulseContextMessage) {
match msg.request() {
PulseContextRequest::MainloopLock => {
self.mainloop_lock();
msg.respond_ok();
}
PulseContextRequest::MainloopUnlock => {
self.mainloop_unlock();
msg.respond_ok();
}
PulseContextRequest::NewPlaybackStream {spec, buffer_size, channel} => {
let mut ps = self.new_playback_stream(*spec, *buffer_size, channel.clone());
match ps.connect(self) {
Ok(()) => msg.respond_stream(ps),
Err(err) => msg.respond_err(err),
}
}
}
}
}

104
src/audio/pulse/message.rs Normal file
View File

@ -0,0 +1,104 @@
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use pulse::sample::Spec;
use crate::audio::pulse::{PulseError, PulseStream, Result};
use crate::audio::pulse::PulseError::UnexpectedResponse;
pub enum PulseContextRequest {
MainloopLock,
MainloopUnlock,
NewPlaybackStream {
spec: Spec,
buffer_size: usize,
channel: PulseMessageChannel,
},
}
pub enum PulseContextResponse {
ResponseOk,
ResponseError(PulseError),
ResponseStream(PulseStream),
}
pub struct PulseContextMessage {
request: PulseContextRequest,
response_channel: Sender<PulseContextResponse>,
}
impl PulseContextMessage {
pub fn new(request: PulseContextRequest) -> (Self, Receiver<PulseContextResponse>) {
let (tx,rx) = mpsc::channel();
let msg = PulseContextMessage {
request,
response_channel: tx,
};
(msg, rx)
}
pub fn request(&self) -> &PulseContextRequest {
&self.request
}
fn send_response(&self, response: PulseContextResponse) {
if let Err(err) = self.response_channel.send(response) {
warn!("PulseAudio: Error sending message response: {}", err);
}
}
pub fn respond_ok(&self) {
self.send_response(PulseContextResponse::ResponseOk)
}
pub fn respond_err(&self, err: PulseError) {
self.send_response(PulseContextResponse::ResponseError(err))
}
pub fn respond_stream(&self, stream: PulseStream) {
self.send_response(PulseContextResponse::ResponseStream(stream));
}
}
#[derive(Clone)]
pub struct PulseMessageChannel {
sender: Sender<PulseContextMessage>
}
impl PulseMessageChannel {
pub fn new(sender: Sender<PulseContextMessage>) -> Self {
PulseMessageChannel { sender }
}
fn exchange_message(&self, req: PulseContextRequest) -> Result<PulseContextResponse> {
let (msg, rx) = PulseContextMessage::new(req);
self.sender.send(msg).map_err(|_| PulseError::SendMessageFailed)?;
let resp = rx.recv().map_err(|_| PulseError::RecvMessageFailed)?;
Ok(resp)
}
fn send_expect_ok(&self, req: PulseContextRequest) -> Result<()> {
let (msg, rx) = PulseContextMessage::new(req);
self.sender.send(msg).map_err(|_| PulseError::SendMessageFailed)?;
let response = rx.recv().map_err(|_| PulseError::RecvMessageFailed)?;
if let PulseContextResponse::ResponseError(err) = response {
return Err(err);
}
Ok(())
}
pub fn send_mainloop_lock(&self) -> Result<()> {
self.send_expect_ok(PulseContextRequest::MainloopLock)
}
pub fn send_mainloop_unlock(&self) -> Result<()> {
self.send_expect_ok(PulseContextRequest::MainloopUnlock)
}
pub fn send_new_playback_stream(&self, spec: Spec, buffer_size: usize, channel: PulseMessageChannel) -> Result<PulseStream> {
match self.exchange_message(PulseContextRequest::NewPlaybackStream { spec, buffer_size, channel})? {
PulseContextResponse::ResponseOk => Err(UnexpectedResponse),
PulseContextResponse::ResponseError(err) => Err(err),
PulseContextResponse::ResponseStream(stream) => Ok(stream),
}
}
}

32
src/audio/pulse/mod.rs Normal file
View File

@ -0,0 +1,32 @@
use std::result;
use pulse::error::PAErr;
mod client;
mod context;
mod message;
mod stream;
pub type Result<T> = result::Result<T, PulseError>;
#[derive(thiserror::Error, Debug)]
pub enum PulseError {
#[error("connection to pulseaudio failed: {0}")]
ConnectFailed(PAErr),
#[error("failed to connect to pulseaudio server")]
ConnectFailedErr,
#[error("failed to start pulseaudio mainloop: {0}")]
StartFailed(PAErr),
#[error("failed to connect pulseaudio stream: {0}")]
StreamConnect(PAErr),
#[error("stream connect failed")]
StreamConnectFailed,
#[error("failed to send channel message")]
SendMessageFailed,
#[error("failed to receive channel response message")]
RecvMessageFailed,
#[error("unexpected response to channel message")]
UnexpectedResponse,
}
pub use stream::PulseStream;
pub use client::PulseClient;

182
src/audio/pulse/stream.rs Normal file
View File

@ -0,0 +1,182 @@
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use std::time::Duration;
use pulse::sample::Spec;
use pulse::stream::{FlagSet, SeekMode, State, Stream};
use crate::audio::pulse::{PulseError,Result};
use crate::audio::pulse::context::PulseContext;
use crate::audio::pulse::message::PulseMessageChannel;
use crate::audio::shm_streams::{BufferSet, GenericResult, ServerRequest, ShmStream};
use crate::memory::GuestRam;
struct Available {
byte_count: Mutex<usize>,
cond: Condvar,
}
impl Available {
fn new() -> Self {
Available {
byte_count: Mutex::new(0),
cond: Condvar::new(),
}
}
fn byte_count_lock(&self) -> MutexGuard<usize> {
self.byte_count.lock().unwrap()
}
fn update(&self, value: usize) {
let mut byte_count = self.byte_count_lock();
*byte_count = value;
self.cond.notify_one();
}
fn decrement(&self, amount: usize) {
let mut byte_count = self.byte_count_lock();
*byte_count -= amount;
}
fn wait_space(&self, timeout: Duration) -> Option<usize> {
let mut byte_count = self.byte_count_lock();
while *byte_count == 0 {
let (new_lock, wt_result) = self.cond.wait_timeout(byte_count, timeout).unwrap();
if wt_result.timed_out() {
return None;
}
byte_count = new_lock;
}
Some(*byte_count)
}
}
pub struct PulseStream {
spec: Spec,
buffer_size: usize,
guest_ram: GuestRam,
stream: Arc<Mutex<Stream>>,
avail: Arc<Available>,
channel: PulseMessageChannel,
}
impl PulseStream {
fn stream_connected_finish(&mut self, ctx: &PulseContext) {
self.stream().set_state_callback(None);
ctx.mainloop_unlock();
}
fn wait_stream_connected(&mut self, ctx: &PulseContext) -> Result<()> {
loop {
let state = self.stream().get_state();
if state == State::Ready {
break;
} else if !state.is_good() {
return Err(PulseError::StreamConnectFailed);
}
ctx.mainloop_wait();
}
Ok(())
}
pub fn connect(&mut self, ctx: &PulseContext) -> Result<()> {
ctx.mainloop_lock();
self.stream().set_state_callback(Some(Box::new({
let ml_ref = ctx.mainloop();
move || unsafe {
(*ml_ref.as_ptr()).signal(false);
}
})));
if let Err(err) = self.stream().connect_playback(
None,
None,
FlagSet::NOFLAGS,
None,
None) {
self.stream().set_state_callback(None);
ctx.mainloop_unlock();
return Err(PulseError::StreamConnect(err))
}
let result = self.wait_stream_connected(ctx);
self.stream_connected_finish(ctx);
result
}
pub fn new_playback(mut stream: Stream, guest_ram: GuestRam, spec: Spec, buffer_size: usize, channel: PulseMessageChannel) -> Self {
let avail = Arc::new(Available::new());
stream.set_write_callback(Some(Box::new({
let avail = avail.clone();
move |writeable_bytes| {
avail.update(writeable_bytes);
}
})));
let stream = Arc::new(Mutex::new(stream));
PulseStream {
spec,
buffer_size,
guest_ram,
avail,
stream,
channel,
}
}
fn stream(&self) -> MutexGuard<Stream> {
self.stream.lock().unwrap()
}
fn uncork(&self) -> GenericResult<()> {
self.channel.send_mainloop_lock()?;
if self.stream().is_corked().unwrap() {
self.stream().uncork(None);
}
self.channel.send_mainloop_unlock()?;
Ok(())
}
}
impl ShmStream for PulseStream {
fn frame_size(&self) -> usize {
self.spec.frame_size()
}
fn num_channels(&self) -> usize {
self.spec.channels as usize
}
fn frame_rate(&self) -> u32 {
self.spec.rate
}
fn wait_for_next_action_with_timeout(&self, timeout: Duration) -> GenericResult<Option<ServerRequest>> {
if let Some(bytes) = self.avail.wait_space(timeout) {
let frames = bytes / self.frame_size();
let req = frames.min(self.buffer_size);
return Ok(Some(ServerRequest::new(req, self)))
}
Ok(None)
}
}
impl BufferSet for PulseStream {
fn callback(&self, address: u64, frames: usize) -> GenericResult<()> {
self.uncork()?;
let bytes = self.guest_ram.slice(address, frames * self.frame_size())?;
self.channel.send_mainloop_lock()?;
self.stream().write_copy(bytes, 0, SeekMode::Relative)?;
self.channel.send_mainloop_unlock()?;
self.avail.decrement(bytes.len());
Ok(())
}
fn ignore(&self) -> GenericResult<()> {
info!("Request ignored...");
Ok(())
}
}

278
src/audio/shm_streams.rs Normal file
View File

@ -0,0 +1,278 @@
// Copyright 2019 The ChromiumOS Authors
// 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::RawFd;
use std::sync::Mutex;
use std::time::Duration;
use std::time::Instant;
use thiserror::Error;
use crate::audio::{BoxError, SampleFormat, StreamDirection};
pub(crate) type GenericResult<T> = Result<T, BoxError>;
/// `BufferSet` is used as a callback mechanism for `ServerRequest` objects.
/// It is meant to be implemented by the audio stream, allowing arbitrary code
/// to be run after a buffer address and length is set.
pub trait BufferSet {
/// Called when the client sets a buffer address and length.
///
/// `address` is the guest address of the buffer and `frames`
/// indicates the number of audio frames that can be read from or written to
/// the buffer.
fn callback(&self, address: u64, frames: usize) -> GenericResult<()>;
/// Called when the client ignores a request from the server.
fn ignore(&self) -> GenericResult<()>;
}
#[derive(Error, Debug)]
pub enum Error {
#[error("Provided number of frames {0} exceeds requested number of frames {1}")]
TooManyFrames(usize, usize),
}
/// `ServerRequest` represents an active request from the server for the client
/// to provide a buffer in shared memory to playback from or capture to.
pub struct ServerRequest<'a> {
requested_frames: usize,
buffer_set: &'a dyn BufferSet,
}
impl<'a> ServerRequest<'a> {
/// Create a new ServerRequest object
///
/// Create a ServerRequest object representing a request from the server
/// for a buffer `requested_frames` in size.
///
/// When the client responds to this request by calling
/// [`set_buffer_address_and_frames`](ServerRequest::set_buffer_address_and_frames),
/// BufferSet::callback will be called on `buffer_set`.
///
/// # Arguments
/// * `requested_frames` - The requested buffer size in frames.
/// * `buffer_set` - The object implementing the callback for when a buffer is provided.
//pub fn new<D: BufferSet>(requested_frames: usize, buffer_set: &'a mut D) -> Self {
pub fn new<D: BufferSet>(requested_frames: usize, buffer_set: &'a D) -> Self {
Self {
requested_frames,
buffer_set,
}
}
/// Get the number of frames of audio data requested by the server.
///
/// The returned value should never be greater than the `buffer_size`
/// given in [`new_stream`](ShmStreamSource::new_stream).
pub fn requested_frames(&self) -> usize {
self.requested_frames
}
/// Sets the buffer address and length for the requested buffer.
///
/// Sets the buffer address and length of the buffer that fulfills this
/// server request to `address` and `length`, respectively. This means that
/// `length` bytes of audio samples may be read from/written to that
/// location in `client_shm` for a playback/capture stream, respectively.
/// This function may only be called once for a `ServerRequest`, at which
/// point the ServerRequest is dropped and no further calls are possible.
///
/// # Arguments
///
/// * `address` - The value to use as the address for the next buffer.
/// * `frames` - The length of the next buffer in frames.
///
/// # Errors
///
/// * If `frames` is greater than `requested_frames`.
pub fn set_buffer_address_and_frames(self, address: u64, frames: usize) -> GenericResult<()> {
if frames > self.requested_frames {
return Err(Box::new(Error::TooManyFrames(
frames,
self.requested_frames,
)));
}
self.buffer_set.callback(address, frames)
}
/// Ignore this request
///
/// If the client does not intend to respond to this ServerRequest with a
/// buffer, they should call this function. The stream will be notified that
/// the request has been ignored and will handle it properly.
pub fn ignore_request(self) -> GenericResult<()> {
self.buffer_set.ignore()
}
}
/// `ShmStream` allows a client to interact with an active CRAS stream.
pub trait ShmStream: Send {
/// Get the size of a frame of audio data for this stream.
fn frame_size(&self) -> usize;
/// Get the number of channels of audio data for this stream.
fn num_channels(&self) -> usize;
/// Get the frame rate of audio data for this stream.
fn frame_rate(&self) -> u32;
/// Waits until the next server message indicating action is required.
///
/// For playback streams, this will be `AUDIO_MESSAGE_REQUEST_DATA`, meaning
/// that we must set the buffer address to the next location where playback
/// data can be found.
/// For capture streams, this will be `AUDIO_MESSAGE_DATA_READY`, meaning
/// that we must set the buffer address to the next location where captured
/// data can be written to.
/// Will return early if `timeout` elapses before a message is received.
///
/// # Arguments
///
/// * `timeout` - The amount of time to wait until a message is received.
///
/// # Return value
///
/// Returns `Some(request)` where `request` is an object that implements the
/// [`ServerRequest`](ServerRequest) trait and which can be used to get the
/// number of bytes requested for playback streams or that have already been
/// written to shm for capture streams.
///
/// If the timeout occurs before a message is received, returns `None`.
///
/// # Errors
///
/// * If an invalid message type is received for the stream.
fn wait_for_next_action_with_timeout(
&self,
timeout: Duration,
) -> GenericResult<Option<ServerRequest>>;
}
/// `SharedMemory` specifies features of shared memory areas passed on to `ShmStreamSource`.
pub trait SharedMemory {
type Error: std::error::Error;
/// Creates a new shared memory file descriptor without specifying a name.
fn anon(size: u64) -> Result<Self, Self::Error>
where
Self: Sized;
/// Gets the size in bytes of the shared memory.
///
/// The size returned here does not reflect changes by other interfaces or users of the shared
/// memory file descriptor..
fn size(&self) -> u64;
/// Returns the underlying raw fd.
#[cfg(unix)]
fn as_raw_fd(&self) -> RawFd;
}
/// `ShmStreamSource` creates streams for playback or capture of audio.
pub trait ShmStreamSource: Send {
/// Creates a new [`ShmStream`](ShmStream)
///
/// Creates a new `ShmStream` object, which allows:
/// * Waiting until the server has communicated that data is ready or
/// requested that we make more data available.
/// * Setting the location and length of buffers for reading/writing audio data.
///
/// # Arguments
///
/// * `direction` - The direction of the stream, either `Playback` or `Capture`.
/// * `num_channels` - The number of audio channels for the stream.
/// * `format` - The audio format to use for audio samples.
/// * `frame_rate` - The stream's frame rate in Hz.
/// * `buffer_size` - The maximum size of an audio buffer. This will be the
/// size used for transfers of audio data between client
/// and server.
///
/// # Errors
///
/// * If sending the connect stream message to the server fails.
fn new_stream(
&mut self,
direction: StreamDirection,
num_channels: usize,
format: SampleFormat,
frame_rate: u32,
buffer_size: usize,
) -> GenericResult<Box<dyn ShmStream>>;
}
/// Class that implements ShmStream trait but does nothing with the samples
pub struct NullShmStream {
num_channels: usize,
frame_rate: u32,
buffer_size: usize,
frame_size: usize,
interval: Duration,
next_frame: Mutex<Duration>,
start_time: Instant,
}
impl NullShmStream {
/// Attempt to create a new NullShmStream with the given number of channels,
/// format, frame_rate, and buffer_size.
pub fn new(
buffer_size: usize,
num_channels: usize,
format: SampleFormat,
frame_rate: u32,
) -> Self {
let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64);
Self {
num_channels,
frame_rate,
buffer_size,
frame_size: format.sample_bytes() * num_channels,
interval,
next_frame: Mutex::new(interval),
start_time: Instant::now(),
}
}
}
impl BufferSet for NullShmStream {
fn callback(&self, _address: u64, _frames: usize) -> GenericResult<()> {
Ok(())
}
fn ignore(&self) -> GenericResult<()> {
Ok(())
}
}
impl ShmStream for NullShmStream {
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(
&self,
timeout: Duration,
) -> GenericResult<Option<ServerRequest>> {
let elapsed = self.start_time.elapsed();
let mut next_frame = self.next_frame.lock().unwrap();
if elapsed < *next_frame {
if timeout < *next_frame - elapsed {
std::thread::sleep(timeout);
return Ok(None);
} else {
std::thread::sleep(*next_frame - elapsed);
}
}
*next_frame += self.interval;
Ok(Some(ServerRequest::new(self.buffer_size, self)))
}
}

188
src/devices/ac97/ac97.rs Normal file
View File

@ -0,0 +1,188 @@
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::io;
use thiserror::Error;
use crate::audio::pulse::{PulseClient, PulseError};
use crate::devices::ac97::ac97_bus_master::{Ac97BusMaster, AudioStreamSource};
use crate::devices::ac97::ac97_mixer::Ac97Mixer;
use crate::devices::ac97::ac97_regs::{MASTER_REGS_SIZE, MIXER_REGS_SIZE};
use crate::devices::irq_event::IrqLevelEvent;
use crate::io::pci::{PciBar, PciBarAllocation, PciConfiguration, PciDevice};
use crate::memory::GuestRam;
use crate::vm::KvmVm;
// Use 82801AA because it's what qemu does.
const PCI_DEVICE_ID_INTEL_82801AA_5: u16 = 0x2415;
/// AC97 audio device emulation.
/// Provides the PCI interface for the internal Ac97 emulation.
/// Internally the `Ac97BusMaster` and `Ac97Mixer` structs are used to emulated the bus master and
/// mixer registers respectively. `Ac97BusMaster` handles moving samples between guest memory and
/// the audio backend.
/// Errors that are possible from a `Ac97`.
#[derive(Error, Debug)]
pub enum Ac97Error {
#[error("Error creating IRQ level event: {0}")]
IrqLevelEventError(io::Error),
#[error("PulseAudio: {0}")]
PulseError(PulseError),
}
pub struct Ac97Dev {
irq: u8,
pci_config: PciConfiguration,
bus_master: Ac97BusMaster,
mixer: Ac97Mixer,
}
const PCI_CLASS_MULTIMEDIA_AUDIO:u16 = 0x0401;
const PCI_VENDOR_ID_INTEL: u16 = 0x8086;
impl Ac97Dev {
/// Creates an 'Ac97Dev' that uses the given `GuestRam` and starts with all registers at
/// default values.
pub fn new(
irq: u8,
mem: GuestRam,
audio_server: AudioStreamSource,
) -> Self {
let pci_config = PciConfiguration::new(irq, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_5, PCI_CLASS_MULTIMEDIA_AUDIO);
Self {
irq,
pci_config,
bus_master: Ac97BusMaster::new(
mem,
audio_server,
),
mixer: Ac97Mixer::new(),
}
}
/// Creates an `Ac97Dev` with suitable audio server inside based on Ac97Parameters. If it fails
/// to create `Ac97Dev` with the given back-end, it'll fallback to the null audio device.
pub fn try_new(
kvm_vm: &KvmVm,
irq: u8,
mem: GuestRam,
) -> Result<Self, Ac97Error> {
let mut ac97 = Self::initialize_pulseaudio(irq, mem)?;
let irq_event = IrqLevelEvent::register(kvm_vm, irq)
.map_err(Ac97Error::IrqLevelEventError)?;
ac97.bus_master.set_irq_event(irq_event);
Ok(ac97)
}
fn initialize_pulseaudio(irq: u8, mem: GuestRam) -> Result<Self, Ac97Error> {
let server = PulseClient::connect(mem.clone())
.map_err(Ac97Error::PulseError)?;
Ok(Self::new(
irq,
mem,
Box::new(server),
))
}
fn read_mixer(&mut self, offset: u64, data: &mut [u8]) {
match data.len() {
// The mixer is only accessed with 16-bit words.
2 => {
let val: u16 = self.mixer.readw(offset);
data[0] = val as u8;
data[1] = (val >> 8) as u8;
}
l => warn!("mixer read length of {}", l),
}
}
fn write_mixer(&mut self, offset: u64, data: &[u8]) {
match data.len() {
// The mixer is only accessed with 16-bit words.
2 => self
.mixer
.writew(offset, u16::from(data[0]) | u16::from(data[1]) << 8),
l => warn!("mixer write length of {}", l),
}
// Apply the new mixer settings to the bus master.
self.bus_master.update_mixer_settings(&self.mixer);
}
fn read_bus_master(&mut self, offset: u64, data: &mut [u8]) {
match data.len() {
1 => data[0] = self.bus_master.readb(offset),
2 => {
let val: u16 = self.bus_master.readw(offset, &self.mixer);
data[0] = val as u8;
data[1] = (val >> 8) as u8;
}
4 => {
let val: u32 = self.bus_master.readl(offset);
data[0] = val as u8;
data[1] = (val >> 8) as u8;
data[2] = (val >> 16) as u8;
data[3] = (val >> 24) as u8;
}
l => warn!("read length of {}", l),
}
}
fn write_bus_master(&mut self, offset: u64, data: &[u8]) {
match data.len() {
1 => self.bus_master.writeb(offset, data[0], &self.mixer),
2 => self
.bus_master
.writew(offset, u16::from(data[0]) | u16::from(data[1]) << 8),
4 => self.bus_master.writel(
offset,
(u32::from(data[0]))
| (u32::from(data[1]) << 8)
| (u32::from(data[2]) << 16)
| (u32::from(data[3]) << 24),
&mut self.mixer,
),
l => warn!("write length of {}", l),
}
}
}
impl PciDevice for Ac97Dev {
fn config(&self) -> &PciConfiguration {
&self.pci_config
}
fn config_mut(&mut self) -> &mut PciConfiguration {
&mut self.pci_config
}
fn read_bar(&mut self, bar: PciBar, offset: u64, data: &mut [u8]) {
match bar {
PciBar::Bar0 => self.read_mixer(offset, data),
PciBar::Bar1 => self.read_bus_master(offset, data),
_ => {},
}
}
fn write_bar(&mut self, bar: PciBar, offset: u64, data: &[u8]) {
match bar {
PciBar::Bar0 => self.write_mixer(offset, data),
PciBar::Bar1 => self.write_bus_master(offset, data),
_ => {},
}
}
fn irq(&self) -> Option<u8> {
Some(self.irq)
}
fn bar_allocations(&self) -> Vec<PciBarAllocation> {
vec![
PciBarAllocation::Mmio(PciBar::Bar0, MIXER_REGS_SIZE as usize),
PciBarAllocation::Mmio(PciBar::Bar1, MASTER_REGS_SIZE as usize)
]
}
}

View File

@ -0,0 +1,947 @@
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::collections::VecDeque;
use std::convert::TryInto;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::atomic::Ordering;
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use std::{cmp, thread};
use std::fmt::{Debug, Formatter};
use std::time::{Duration, Instant};
use thiserror::Error;
use crate::audio::shm_streams::{ShmStream, ShmStreamSource};
use crate::audio::{BoxError, SampleFormat, StreamControl, StreamDirection};
use crate::devices::ac97::ac97_mixer::Ac97Mixer;
use crate::devices::ac97::ac97_regs::*;
use crate::devices::irq_event::IrqLevelEvent;
use crate::memory::GuestRam;
use crate::system;
const INPUT_SAMPLE_RATE: u32 = 48000;
const DEVICE_INPUT_CHANNEL_COUNT: usize = 2;
pub(crate) type AudioStreamSource = Box<dyn ShmStreamSource>;
// Bus Master registers. Keeps the state of the bus master register values. Used to share the state
// between the main and audio threads.
struct Ac97BusMasterRegs {
pi_regs: Ac97FunctionRegs, // Input
po_regs: Ac97FunctionRegs, // Output
po_pointer_update_time: Instant, // Time the picb and civ regs were last updated.
mc_regs: Ac97FunctionRegs, // Microphone
glob_cnt: u32,
glob_sta: u32,
// IRQ event - driven by the glob_sta register.
irq_evt: Option<IrqLevelEvent>,
}
impl Ac97BusMasterRegs {
fn new() -> Ac97BusMasterRegs {
Ac97BusMasterRegs {
pi_regs: Ac97FunctionRegs::new("Input"),
po_regs: Ac97FunctionRegs::new("Output"),
po_pointer_update_time: Instant::now(),
mc_regs: Ac97FunctionRegs::new("Microphone"),
glob_cnt: 0,
glob_sta: GLOB_STA_RESET_VAL,
irq_evt: None,
}
}
fn func_regs(&self, func: Ac97Function) -> &Ac97FunctionRegs {
match func {
Ac97Function::Input => &self.pi_regs,
Ac97Function::Output => &self.po_regs,
Ac97Function::Microphone => &self.mc_regs,
}
}
fn func_regs_mut(&mut self, func: Ac97Function) -> &mut Ac97FunctionRegs {
match func {
Ac97Function::Input => &mut self.pi_regs,
Ac97Function::Output => &mut self.po_regs,
Ac97Function::Microphone => &mut self.mc_regs,
}
}
fn tube_count(&self, func: Ac97Function) -> usize {
fn output_tube_count(glob_cnt: u32) -> usize {
let val = (glob_cnt & GLOB_CNT_PCM_246_MASK) >> 20;
match val {
0 => 2,
1 => 4,
2 => 6,
_ => {
warn!("unknown tube_count: 0x{:x}", val);
2
}
}
}
match func {
Ac97Function::Output => output_tube_count(self.glob_cnt),
_ => DEVICE_INPUT_CHANNEL_COUNT,
}
}
/// Returns whether the irq is set for any one of the bus master function registers.
pub fn has_irq(&self) -> bool {
self.pi_regs.has_irq() || self.po_regs.has_irq() || self.mc_regs.has_irq()
}
}
// Internal error type used for reporting errors from guest memory reading.
#[derive(Error, Debug)]
pub(crate) enum GuestMemoryError {
// Failure getting the address of the audio buffer.
#[error("Failed to get the address of the audio buffer: {0}.")]
ReadingGuestBufferAddress(system::Error),
}
#[derive(Error, Debug)]
pub(crate) enum AudioError {
#[error("Failed to create audio stream: {0}.")]
CreateStream(BoxError),
#[error("Offset > max usize")]
InvalidBufferOffset,
#[error("Failed to read guest memory: {0}.")]
ReadingGuestError(GuestMemoryError),
// Failure to respond to the ServerRequest.
#[error("Failed to respond to the ServerRequest: {0}")]
RespondRequest(BoxError),
// Failure to wait for a request from the stream.
#[error("Failed to wait for a message from the stream: {0}")]
WaitForAction(BoxError),
}
impl From<GuestMemoryError> for AudioError {
fn from(err: GuestMemoryError) -> Self {
AudioError::ReadingGuestError(err)
}
}
type GuestMemoryResult<T> = Result<T, GuestMemoryError>;
type AudioResult<T> = Result<T, AudioError>;
// Audio thread book-keeping data
struct AudioThreadInfo {
thread: Option<thread::JoinHandle<()>>,
thread_run: Arc<AtomicBool>,
thread_semaphore: Arc<Condvar>,
stream_control: Option<Box<dyn StreamControl>>,
}
impl AudioThreadInfo {
fn new() -> Self {
Self {
thread: None,
thread_run: Arc::new(AtomicBool::new(false)),
thread_semaphore: Arc::new(Condvar::new()),
stream_control: None,
}
}
fn is_running(&self) -> bool {
self.thread_run.load(Ordering::Relaxed)
}
fn start(&mut self, mut worker: AudioWorker) {
self.thread_run.store(true, Ordering::Relaxed);
self.thread = Some(thread::spawn(move || {
if let Err(e) = worker.run() {
warn!("{:?} error: {}", worker.func, e);
}
worker.thread_run.store(false, Ordering::Relaxed);
}));
}
fn stop(&mut self) {
self.thread_run.store(false, Ordering::Relaxed);
self.thread_semaphore.notify_one();
if let Some(thread) = self.thread.take() {
if let Err(e) = thread.join() {
warn!("Failed to join thread: {:?}.", e);
}
}
}
}
/// `Ac97BusMaster` emulates the bus master portion of AC97. It exposes a register read/write
/// interface compliant with the ICH bus master.
pub struct Ac97BusMaster {
// Keep guest memory as each function will use it for buffer descriptors.
mem: GuestRam,
regs: Arc<Mutex<Ac97BusMasterRegs>>,
acc_sema: u8,
// Bookkeeping info for playback and capture stream.
po_info: AudioThreadInfo,
pi_info: AudioThreadInfo,
pmic_info: AudioThreadInfo,
// Audio server used to create playback or capture streams.
audio_server: AudioStreamSource,
// Thread for hadlind IRQ resample events from the guest.
irq_resample_thread: Option<thread::JoinHandle<()>>,
}
impl Ac97BusMaster {
/// Creates an Ac97BusMaster` object that plays audio from `mem` to streams provided by
/// `audio_server`.
pub fn new(mem: GuestRam, audio_server: AudioStreamSource) -> Self {
Ac97BusMaster {
mem,
regs: Arc::new(Mutex::new(Ac97BusMasterRegs::new())),
acc_sema: 0,
po_info: AudioThreadInfo::new(),
pi_info: AudioThreadInfo::new(),
pmic_info: AudioThreadInfo::new(),
audio_server,
irq_resample_thread: None,
}
}
fn regs(&self) -> MutexGuard<Ac97BusMasterRegs> {
self.regs.lock().unwrap()
}
/// Provides the events needed to raise interrupts in the guest.
pub fn set_irq_event(&mut self, irq_evt: IrqLevelEvent) {
let thread_regs = self.regs.clone();
self.regs().irq_evt = Some(irq_evt.try_clone().expect("cloning irq_evt failed"));
self.irq_resample_thread = Some(thread::spawn(move || {
loop {
if let Err(e) = irq_evt.wait_resample() {
warn!(
"Failed to read the irq event from the resample thread: {}.",
e,
);
break;
}
{
// Scope for the lock on thread_regs.
let regs = thread_regs.lock().unwrap();
if regs.has_irq() {
if let Err(e) = irq_evt.trigger() {
warn!("Failed to set the irq from the resample thread: {}.", e);
break;
}
}
}
}
}));
}
/// Called when `mixer` has been changed and the new values should be applied to currently
/// active streams.
pub fn update_mixer_settings(&mut self, mixer: &Ac97Mixer) {
if let Some(control) = self.po_info.stream_control.as_mut() {
// The audio server only supports one volume, not separate left and right.
let (muted, left_volume, _right_volume) = mixer.get_master_volume();
control.set_volume(left_volume);
control.set_mute(muted);
}
}
/// Checks if the bus master is in the cold reset state.
pub fn is_cold_reset(&self) -> bool {
self.regs().glob_cnt & GLOB_CNT_COLD_RESET == 0
}
/// Reads a byte from the given `offset`.
pub fn readb(&mut self, offset: u64) -> u8 {
fn readb_func_regs(func_regs: &Ac97FunctionRegs, offset: u64) -> u8 {
let result = match offset {
CIV_OFFSET => func_regs.civ,
LVI_OFFSET => func_regs.lvi,
SR_OFFSET => func_regs.sr as u8,
PIV_OFFSET => func_regs.piv,
CR_OFFSET => func_regs.cr,
_ => 0,
};
result
}
let regs = self.regs();
match offset {
PI_BASE_00..=PI_CR_0B => readb_func_regs(&regs.pi_regs, offset - PI_BASE_00),
PO_BASE_10..=PO_CR_1B => readb_func_regs(&regs.po_regs, offset - PO_BASE_10),
MC_BASE_20..=MC_CR_2B => readb_func_regs(&regs.mc_regs, offset - MC_BASE_20),
ACC_SEMA_34 => self.acc_sema,
_ => 0,
}
}
/// Reads a word from the given `offset`.
pub fn readw(&mut self, offset: u64, mixer: &Ac97Mixer) -> u16 {
let regs = self.regs();
match offset {
PI_SR_06 => regs.pi_regs.sr,
PI_PICB_08 => regs.pi_regs.picb,
PO_SR_16 => regs.po_regs.sr,
PO_PICB_18 => {
// PO PICB
if !self.thread_info(Ac97Function::Output).is_running() {
// Not running, no need to estimate what has been consumed.
regs.po_regs.picb
} else {
// Estimate how many samples have been played since the last audio callback.
let num_channels = regs.tube_count(Ac97Function::Output) as u64;
let micros = regs.po_pointer_update_time.elapsed().subsec_micros();
// Round down to the next 10 millisecond boundary. The linux driver often
// assumes that two rapid reads from picb will return the same value.
let millis = micros / 1000 / 10 * 10;
let sample_rate = self.current_sample_rate(Ac97Function::Output, mixer);
let frames_consumed = sample_rate as u64 * u64::from(millis) / 1000;
regs.po_regs
.picb
.saturating_sub((num_channels * frames_consumed) as u16)
}
}
MC_SR_26 => regs.mc_regs.sr,
MC_PICB_28 => regs.mc_regs.picb,
_ => 0,
}
}
/// Reads a 32-bit word from the given `offset`.
pub fn readl(&mut self, offset: u64) -> u32 {
let regs = self.regs();
let result = match offset {
PI_BDBAR_00 => regs.pi_regs.bdbar,
PI_CIV_04 => regs.pi_regs.atomic_status_regs(),
PO_BDBAR_10 => regs.po_regs.bdbar,
PO_CIV_14 => regs.po_regs.atomic_status_regs(),
MC_BDBAR_20 => regs.mc_regs.bdbar,
MC_CIV_24 => regs.mc_regs.atomic_status_regs(),
GLOB_CNT_2C => regs.glob_cnt,
GLOB_STA_30 => regs.glob_sta,
_ => 0,
};
result
}
/// Writes the byte `val` to the register specified by `offset`.
pub fn writeb(&mut self, offset: u64, val: u8, mixer: &Ac97Mixer) {
// Only process writes to the control register when cold reset is set.
if self.is_cold_reset() {
info!("Ignoring control register write at offset {:02x} due to cold reset status");
return;
}
match offset {
PI_CIV_04 => (), // RO
PI_LVI_05 => self.set_lvi(Ac97Function::Input, val),
PI_SR_06 => self.set_sr(Ac97Function::Input, u16::from(val)),
PI_PIV_0A => (), // RO
PI_CR_0B => self.set_cr(Ac97Function::Input, val, mixer),
PO_CIV_14 => (), // RO
PO_LVI_15 => self.set_lvi(Ac97Function::Output, val),
PO_SR_16 => self.set_sr(Ac97Function::Output, u16::from(val)),
PO_PIV_1A => (), // RO
PO_CR_1B => self.set_cr(Ac97Function::Output, val, mixer),
MC_CIV_24 => (), // RO
MC_LVI_25 => self.set_lvi(Ac97Function::Microphone, val),
MC_SR_26 => self.set_sr(Ac97Function::Microphone, u16::from(val)),
MC_PIV_2A => (), // RO
MC_CR_2B => self.set_cr(Ac97Function::Microphone, val, mixer),
ACC_SEMA_34 => self.acc_sema = val,
o => warn!("AC97: write byte to 0x{:x}", o),
}
}
/// Writes the word `val` to the register specified by `offset`.
pub fn writew(&mut self, offset: u64, val: u16) {
// Only process writes to the control register when cold reset is set.
if self.is_cold_reset() {
return;
}
match offset {
PI_SR_06 => self.set_sr(Ac97Function::Input, val),
PI_PICB_08 => (), // RO
PO_SR_16 => self.set_sr(Ac97Function::Output, val),
PO_PICB_18 => (), // RO
MC_SR_26 => self.set_sr(Ac97Function::Microphone, val),
MC_PICB_28 => (), // RO
o => warn!("AC97: write word to 0x{:x}", o),
}
}
/// Writes the 32-bit `val` to the register specified by `offset`.
pub fn writel(&mut self, offset: u64, val: u32, mixer: &mut Ac97Mixer) {
// Only process writes to the control register when cold reset is set.
if self.is_cold_reset() && offset != 0x2c {
return;
}
match offset {
PI_BDBAR_00 => self.set_bdbar(Ac97Function::Input, val),
PO_BDBAR_10 => self.set_bdbar(Ac97Function::Output, val),
MC_BDBAR_20 => self.set_bdbar(Ac97Function::Microphone, val),
GLOB_CNT_2C => self.set_glob_cnt(val, mixer),
GLOB_STA_30 => (), // RO
o => warn!("AC97: write long to 0x{:x}", o),
}
}
fn set_bdbar(&mut self, func: Ac97Function, val: u32) {
self.regs().func_regs_mut(func).bdbar = val & !0x07;
}
fn set_lvi(&mut self, func: Ac97Function, val: u8) {
let mut regs = self.regs();
let func_regs = regs.func_regs_mut(func);
func_regs.lvi = val % 32; // LVI wraps at 32.
// If running and stalled waiting for more valid buffers, restart by clearing the "DMA
// stopped" bit.
if func_regs.cr & CR_RPBM == CR_RPBM
&& func_regs.sr & SR_DCH == SR_DCH
&& func_regs.civ != func_regs.lvi
{
Ac97BusMaster::check_and_move_to_next_buffer(func_regs);
func_regs.sr &= !(SR_DCH | SR_CELV);
self.thread_semaphore_notify(func);
}
}
fn set_sr(&mut self, func: Ac97Function, val: u16) {
let mut sr = self.regs().func_regs(func).sr;
if val & SR_FIFOE != 0 {
sr &= !SR_FIFOE;
}
if val & SR_LVBCI != 0 {
sr &= !SR_LVBCI;
}
if val & SR_BCIS != 0 {
sr &= !SR_BCIS;
}
let mut regs = self.regs();
update_sr(&mut regs, func, sr);
}
fn set_cr(&mut self, func: Ac97Function, val: u8, mixer: &Ac97Mixer) {
if val & CR_RR != 0 {
let mut regs = self.regs();
Self::reset_func_regs(&mut regs, func);
} else {
let cr = self.regs().func_regs(func).cr;
if val & CR_RPBM == 0 {
// Run/Pause set to pause.
self.thread_info_mut(func).stop();
let mut regs = self.regs();
regs.func_regs_mut(func).sr |= SR_DCH;
} else if cr & CR_RPBM == 0 {
// Not already running.
// Run/Pause set to run.
{
let mut regs = self.regs();
let func_regs = regs.func_regs_mut(func);
func_regs.piv = 1;
func_regs.civ = 0;
func_regs.sr &= !SR_DCH;
}
if let Err(e) = self.start_audio(func, mixer) {
warn!("Failed to start audio: {}", e);
}
}
let mut regs = self.regs();
regs.func_regs_mut(func).cr = val & CR_VALID_MASK;
}
}
fn set_glob_cnt(&mut self, new_glob_cnt: u32, mixer: &mut Ac97Mixer) {
// Only the reset bits are emulated, the GPI and PCM formatting are not supported.
if new_glob_cnt & GLOB_CNT_COLD_RESET == 0 {
self.reset_audio_regs();
mixer.reset();
self.regs().glob_cnt = new_glob_cnt & GLOB_CNT_STABLE_BITS;
self.acc_sema = 0;
return;
}
if new_glob_cnt & GLOB_CNT_WARM_RESET != 0 {
// Check if running and if so, ignore. Warm reset is specified to no-op when the device
// is playing or recording audio.
if !self.is_audio_running() {
self.stop_all_audio();
let mut regs = self.regs();
regs.glob_cnt = new_glob_cnt & !GLOB_CNT_WARM_RESET; // Auto-cleared reset bit.
return;
}
}
self.regs().glob_cnt = new_glob_cnt;
}
fn current_sample_rate(&self, func: Ac97Function, mixer: &Ac97Mixer) -> u32 {
match func {
Ac97Function::Output => mixer.get_sample_rate().into(),
_ => INPUT_SAMPLE_RATE,
}
}
fn thread_info(&self, func: Ac97Function) -> &AudioThreadInfo {
match func {
Ac97Function::Microphone => &self.pmic_info,
Ac97Function::Input => &self.pi_info,
Ac97Function::Output => &self.po_info,
}
}
fn thread_info_mut(&mut self, func: Ac97Function) -> &mut AudioThreadInfo {
match func {
Ac97Function::Microphone => &mut self.pmic_info,
Ac97Function::Input => &mut self.pi_info,
Ac97Function::Output => &mut self.po_info,
}
}
fn is_audio_running(&self) -> bool {
self.thread_info(Ac97Function::Output).is_running()
|| self.thread_info(Ac97Function::Input).is_running()
|| self.thread_info(Ac97Function::Microphone).is_running()
}
fn stop_all_audio(&mut self) {
self.thread_info_mut(Ac97Function::Input).stop();
self.thread_info_mut(Ac97Function::Output).stop();
self.thread_info_mut(Ac97Function::Microphone).stop();
}
// Helper function for resetting function registers.
fn reset_func_regs(regs: &mut Ac97BusMasterRegs, func: Ac97Function) {
regs.func_regs_mut(func).do_reset();
update_sr(regs, func, SR_DCH);
}
fn reset_audio_regs(&mut self) {
self.stop_all_audio();
let mut regs = self.regs();
Self::reset_func_regs(&mut regs, Ac97Function::Input);
Self::reset_func_regs(&mut regs, Ac97Function::Output);
Self::reset_func_regs(&mut regs, Ac97Function::Microphone);
}
fn check_and_move_to_next_buffer(func_regs: &mut Ac97FunctionRegs) {
if func_regs.sr & SR_CELV != 0 {
// CELV means we'd already processed the buffer at CIV.
// Move CIV to the next buffer now that LVI has moved.
func_regs.move_to_next_buffer();
}
}
fn thread_semaphore_notify(&self, func: Ac97Function) {
match func {
Ac97Function::Input => self.pi_info.thread_semaphore.notify_one(),
Ac97Function::Output => self.po_info.thread_semaphore.notify_one(),
Ac97Function::Microphone => self.pmic_info.thread_semaphore.notify_one(),
}
}
fn create_audio_worker(&mut self, mixer: &Ac97Mixer, func: Ac97Function) -> AudioResult<AudioWorker> {
info!("AC97: create_audio_worker({:?})", func);
let direction = match func {
Ac97Function::Microphone => StreamDirection::Capture,
Ac97Function::Input => StreamDirection::Capture,
Ac97Function::Output => StreamDirection::Playback,
};
let locked_regs = self.regs.lock().unwrap();
let sample_rate = self.current_sample_rate(func, mixer);
let buffer_samples = current_buffer_size(locked_regs.func_regs(func), &self.mem)?;
let num_channels = locked_regs.tube_count(func);
let buffer_frames = buffer_samples / num_channels;
let pending_buffers = VecDeque::with_capacity(2);
let stream = self
.audio_server
.new_stream(
direction,
num_channels,
SampleFormat::S16LE,
sample_rate,
buffer_frames)
.map_err(AudioError::CreateStream)?;
let params = AudioWorkerParams {
func,
stream,
pending_buffers,
message_interval: Duration::from_secs_f64(buffer_frames as f64 / sample_rate as f64),
};
Ok(AudioWorker::new(self, params))
}
fn start_audio(&mut self, func: Ac97Function, mixer: &Ac97Mixer) -> AudioResult<()> {
let audio_worker = self.create_audio_worker(mixer, func)?;
self.thread_info_mut(func).start(audio_worker);
self.update_mixer_settings(mixer);
Ok(())
}
}
fn get_buffer_samples(
func_regs: &Ac97FunctionRegs,
mem: &GuestRam,
index: u8,
) -> GuestMemoryResult<usize> {
let descriptor_addr = func_regs.bdbar + u32::from(index) * DESCRIPTOR_LENGTH as u32;
let control_reg: u32 = mem
.read_int(u64::from(descriptor_addr) + 4)
.map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
let buffer_samples = control_reg as usize & 0x0000_ffff;
Ok(buffer_samples)
}
// Marks the current buffer completed and moves to the next buffer for the given
// function and registers.
fn buffer_completed(
regs: &mut Ac97BusMasterRegs,
mem: &GuestRam,
func: Ac97Function,
) -> AudioResult<()> {
// check if the completed descriptor wanted an interrupt on completion.
let civ = regs.func_regs(func).civ;
let descriptor_addr = regs.func_regs(func).bdbar + u32::from(civ) * DESCRIPTOR_LENGTH as u32;
let control_reg: u32 = mem
.read_int(u64::from(descriptor_addr) + 4)
.map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
let mut new_sr = regs.func_regs(func).sr & !SR_CELV;
if control_reg & BD_IOC != 0 {
new_sr |= SR_BCIS;
}
let lvi = regs.func_regs(func).lvi;
// if the current buffer was the last valid buffer, then update the status register to
// indicate that the end of audio was hit and possibly raise an interrupt.
if civ == lvi {
new_sr |= SR_DCH | SR_CELV | SR_LVBCI;
} else {
regs.func_regs_mut(func).move_to_next_buffer();
}
update_sr(regs, func, new_sr);
regs.func_regs_mut(func).picb = current_buffer_size(regs.func_regs(func), mem)? as u16;
if func == Ac97Function::Output {
regs.po_pointer_update_time = Instant::now();
}
Ok(())
}
// Update the status register and if any interrupts need to fire, raise them.
fn update_sr(regs: &mut Ac97BusMasterRegs, func: Ac97Function, val: u16) {
let int_mask = match func {
Ac97Function::Input => GS_PIINT,
Ac97Function::Output => GS_POINT,
Ac97Function::Microphone => GS_MINT,
};
let mut interrupt_high = false;
{
let func_regs = regs.func_regs_mut(func);
let old_sr = func_regs.sr;
func_regs.sr = val;
if (old_sr ^ val) & SR_INT_MASK != 0 {
if (val & SR_LVBCI) != 0 && (func_regs.cr & CR_LVBIE) != 0 {
interrupt_high = true;
}
if (val & SR_BCIS) != 0 && (func_regs.cr & CR_IOCE) != 0 {
interrupt_high = true;
}
} else {
return;
}
}
if interrupt_high {
regs.glob_sta |= int_mask;
if let Some(ref irq_evt) = regs.irq_evt {
// Ignore write failure, nothing can be done about it from here.
let _ = irq_evt.trigger();
} else {
info!("AC97: No interrupt! uh oh");
}
} else {
regs.glob_sta &= !int_mask;
}
}
// Returns the size in samples of the buffer pointed to by the CIV register.
fn current_buffer_size(
func_regs: &Ac97FunctionRegs,
mem: &GuestRam,
) -> GuestMemoryResult<usize> {
let civ = func_regs.civ;
get_buffer_samples(func_regs, mem, civ)
}
#[derive(Clone)]
struct GuestBuffer {
index: u8,
address: u64,
samples: usize,
channels: usize,
consumed_frames: Arc<AtomicUsize>,
}
impl GuestBuffer {
fn new(index: u8, address: u64, samples: usize, channels: usize) -> Self {
GuestBuffer {
index,
address,
samples,
channels,
consumed_frames: Arc::new(AtomicUsize::new(0)),
}
}
fn start_address(&self, frame_size: usize) -> u64 {
self.address + (self.consumed_frames() * frame_size) as u64
}
fn frames(&self) -> usize {
self.samples / self.channels
}
fn add_consumed(&self, frames: usize) {
self.consumed_frames.fetch_add(frames, Ordering::Relaxed);
}
fn consumed_frames(&self) -> usize {
self.consumed_frames.load(Ordering::Relaxed)
}
fn remaining_frames(&self) -> usize {
self.frames() - self.consumed_frames()
}
fn is_consumed(&self) -> bool {
self.consumed_frames() >= self.frames()
}
}
impl Debug for GuestBuffer {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "GuestBuffer([{}]@0x{:08x}, [{} of {} frames remaining])", self.index, self.address, self.remaining_frames(), self.frames())
}
}
fn get_buffer_address(
func_regs: &Ac97FunctionRegs,
mem: &GuestRam,
index: u8,
) -> GuestMemoryResult<u64> {
let descriptor_addr = func_regs.bdbar + u32::from(index) * DESCRIPTOR_LENGTH as u32;
let buffer_addr_reg: u32 = mem
.read_int(u64::from(descriptor_addr))
.map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
let buffer_addr = (buffer_addr_reg & !0x03u32) as u64; // The address must be aligned to four bytes.
Ok(buffer_addr)
}
// Gets the start address and length of the buffer at `civ + offset` from the
// guest.
// This will return `None` if `civ + offset` is past LVI; if the DMA controlled
// stopped bit is set, such as after an underrun where CIV hits LVI; or if
// `civ + offset == LVI and the CELV flag is set.
fn next_guest_buffer(
regs: &Ac97BusMasterRegs,
mem: &GuestRam,
func: Ac97Function,
offset: usize,
) -> AudioResult<Option<GuestBuffer>> {
let func_regs = regs.func_regs(func);
let offset = (offset % 32) as u8;
let index = (func_regs.civ + offset) % 32;
// Check that value is between `low` and `high` modulo some `n`.
fn check_between(low: u8, high: u8, value: u8) -> bool {
// If low <= high, value must be in the interval between them:
// 0 l h n
// ......+++++++......
(low <= high && (low <= value && value <= high)) ||
// If low > high, value must not be in the interval between them:
// 0 h l n
// +++++++++......++++
(low > high && (low <= value || value <= high))
}
// Check if
// * we're halted
// * `index` is not between CIV and LVI (mod 32)
// * `index is LVI and we've already processed LVI (SR_CELV is set)
// if any of these are true `index` isn't valid.
if func_regs.sr & SR_DCH != 0
|| !check_between(func_regs.civ, func_regs.lvi, index)
|| func_regs.sr & SR_CELV != 0
{
return Ok(None);
}
let address = get_buffer_address(func_regs, mem, index)?
.try_into()
.map_err(|_| AudioError::InvalidBufferOffset)?;
let samples = get_buffer_samples(func_regs, mem, index)?;
let channels = regs.tube_count(func);
Ok(Some(GuestBuffer::new(index, address, samples, channels)))
}
// Runs and updates the offset within the stream shm where samples can be
// found/placed for shm playback/capture streams, respectively
struct AudioWorker {
func: Ac97Function,
regs: Arc<Mutex<Ac97BusMasterRegs>>,
mem: GuestRam,
thread_run: Arc<AtomicBool>,
lvi_semaphore: Arc<Condvar>,
message_interval: Duration,
stream: Box<dyn ShmStream>,
pending_buffers: Arc<Mutex<VecDeque<Option<GuestBuffer>>>>,
}
struct AudioWorkerParams {
func: Ac97Function,
stream: Box<dyn ShmStream>,
pending_buffers: VecDeque<Option<GuestBuffer>>,
message_interval: Duration,
}
impl AudioWorker {
fn new(bus_master: &Ac97BusMaster, args: AudioWorkerParams) -> Self {
Self {
func: args.func,
regs: bus_master.regs.clone(),
mem: bus_master.mem.clone(),
thread_run: bus_master.thread_info(args.func).thread_run.clone(),
lvi_semaphore: bus_master.thread_info(args.func).thread_semaphore.clone(),
message_interval: args.message_interval,
stream: args.stream,
pending_buffers: Arc::new( Mutex::new(args.pending_buffers)),
}
}
fn next_guest_buffer(&self) -> AudioResult<Option<GuestBuffer>> {
let mut pending = self.pending_buffers.lock().unwrap();
if let Some(Some(front_buffer)) = pending.front() {
if !front_buffer.is_consumed() {
return Ok(Some(front_buffer.clone()))
}
}
let start = Instant::now();
let mut locked_regs = self.regs.lock().unwrap();
if pending.len() == 2 {
// When we have two pending buffers and receive a request for
// another, we know that oldest buffer has been completed.
// However, if that old buffer was an empty buffer we sent
// because the guest driver had no available buffers, we don't
// want to mark a buffer complete.
if let Some(Some(_)) = pending.pop_front() {
buffer_completed(&mut locked_regs, &self.mem, self.func)?;
if let Some(Some(front_buffer)) = pending.front() {
if !front_buffer.is_consumed() {
return Ok(Some(front_buffer.clone()))
}
}
}
}
// We count the number of pending, real buffers at the server, and
// then use that as our offset from CIV.
let offset = pending.iter().filter(|e| e.is_some()).count();
// Get a buffer to respond to our request. If there's no buffer
// available, we'll wait one buffer interval and check again.
let buffer = loop {
if let Some(buffer) = next_guest_buffer(&locked_regs, &self.mem, self.func, offset)?
{
break Some(buffer);
}
let elapsed = start.elapsed();
if elapsed > self.message_interval {
break None;
}
locked_regs = self
.lvi_semaphore
.wait_timeout(locked_regs, self.message_interval - elapsed)
.unwrap()
.0;
};
pending.push_back(buffer.clone());
Ok(buffer)
}
// Runs and updates the offset within the stream shm where samples can be
// found/placed for shm playback/capture streams, respectively
fn run(&mut self) -> AudioResult<()> {
let func = self.func;
// Set up picb.
{
let mut locked_regs = self.regs.lock().unwrap();
locked_regs.func_regs_mut(func).picb =
current_buffer_size(locked_regs.func_regs(func), &self.mem)? as u16;
}
'audio_loop: while self.thread_run.load(Ordering::Relaxed) {
{
let mut locked_regs = self.regs.lock().unwrap();
while locked_regs.func_regs(func).sr & SR_DCH != 0 {
locked_regs = self.lvi_semaphore.wait(locked_regs).unwrap();
if !self.thread_run.load(Ordering::Relaxed) {
break 'audio_loop;
}
}
}
let timeout = Duration::from_secs(1);
let action = self
.stream
.wait_for_next_action_with_timeout(timeout)
.map_err(AudioError::WaitForAction)?;
let request = match action {
None => {
warn!("No audio message received within timeout of {:?}", timeout);
continue;
}
Some(request) => request,
};
match self.next_guest_buffer()? {
None => request.ignore_request().map_err(AudioError::RespondRequest)?,
Some(buffer) => {
let addr = buffer.start_address(self.stream.frame_size());
let nframes = cmp::min(request.requested_frames(), buffer.remaining_frames());
buffer.add_consumed(nframes);
request.set_buffer_address_and_frames(addr, nframes)
.map_err(AudioError::RespondRequest)?;
}
}
}
Ok(())
}
}

View File

@ -0,0 +1,200 @@
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::devices::ac97::ac97_regs::*;
// Extented Audio ID
const AC97_EXTENDED_ID: u16 = MIXER_EI_VRA | MIXER_EI_CDAC | MIXER_EI_SDAC | MIXER_EI_LDAC;
const PCI_VENDOR_ID_INTEL: u16 = 0x8086;
// Master volume register is specified in 1.5dB steps.
const MASTER_VOLUME_STEP_DB: f64 = 1.5;
/// `Ac97Mixer` holds the mixer state for the AC97 bus.
/// The mixer is used by calling the `readb`/`readw`/`readl` functions to read register values and
/// the `writeb`/`writew`/`writel` functions to set register values.
pub struct Ac97Mixer {
// Mixer Registers
master_volume_l: u8,
master_volume_r: u8,
master_mute: bool,
mic_muted: bool,
mic_20db: bool,
mic_volume: u8,
record_gain_l: u8,
record_gain_r: u8,
record_gain_mute: bool,
pcm_out_vol_l: u16,
pcm_out_vol_r: u16,
pcm_out_mute: bool,
power_down_control: u16,
ext_audio_status_ctl: u16,
pcm_front_dac_rate: u16,
pcm_surr_dac_rate: u16,
pcm_lfe_dac_rate: u16,
}
impl Ac97Mixer {
/// Creates an 'Ac97Mixer' with the standard default register values.
pub fn new() -> Self {
Ac97Mixer {
master_volume_l: 0,
master_volume_r: 0,
master_mute: true,
mic_muted: true,
mic_20db: false,
mic_volume: 0x8,
record_gain_l: 0,
record_gain_r: 0,
record_gain_mute: true,
pcm_out_vol_l: 0x8,
pcm_out_vol_r: 0x8,
pcm_out_mute: true,
power_down_control: PD_REG_STATUS_MASK, // Report everything is ready.
ext_audio_status_ctl: 0,
// Default to 48 kHz.
pcm_front_dac_rate: 0xBB80,
pcm_surr_dac_rate: 0xBB80,
pcm_lfe_dac_rate: 0xBB80,
}
}
pub fn reset(&mut self) {
// Upon reset, the audio sample rate registers default to 48 kHz, and VRA=0.
self.ext_audio_status_ctl &= !MIXER_EI_VRA;
self.pcm_front_dac_rate = 0xBB80;
self.pcm_surr_dac_rate = 0xBB80;
self.pcm_lfe_dac_rate = 0xBB80;
}
/// Reads a word from the register at `offset`.
pub fn readw(&self, offset: u64) -> u16 {
match offset {
MIXER_RESET_00 => BC_DEDICATED_MIC,
MIXER_MASTER_VOL_MUTE_02 => self.get_master_reg(),
MIXER_MIC_VOL_MUTE_0E => self.get_mic_volume(),
MIXER_PCM_OUT_VOL_MUTE_18 => self.get_pcm_out_volume(),
MIXER_REC_VOL_MUTE_1C => self.get_record_gain_reg(),
MIXER_POWER_DOWN_CONTROL_26 => self.power_down_control,
MIXER_EXTENDED_AUDIO_ID_28 => AC97_EXTENDED_ID,
MIXER_VENDOR_ID1_7C => PCI_VENDOR_ID_INTEL,
MIXER_VENDOR_ID2_7E => PCI_VENDOR_ID_INTEL,
MIXER_EXTENDED_AUDIO_STATUS_CONTROL_28 => self.ext_audio_status_ctl,
MIXER_PCM_FRONT_DAC_RATE_2C => self.pcm_front_dac_rate,
MIXER_PCM_SURR_DAC_RATE_2E => self.pcm_surr_dac_rate,
MIXER_PCM_LFE_DAC_RATE_30 => self.pcm_lfe_dac_rate,
_ => 0,
}
}
/// Writes a word `val` to the register `offset`.
pub fn writew(&mut self, offset: u64, val: u16) {
match offset {
MIXER_RESET_00 => self.reset(),
MIXER_MASTER_VOL_MUTE_02 => self.set_master_reg(val),
MIXER_MIC_VOL_MUTE_0E => self.set_mic_volume(val),
MIXER_PCM_OUT_VOL_MUTE_18 => self.set_pcm_out_volume(val),
MIXER_REC_VOL_MUTE_1C => self.set_record_gain_reg(val),
MIXER_POWER_DOWN_CONTROL_26 => self.set_power_down_reg(val),
MIXER_EXTENDED_AUDIO_STATUS_CONTROL_28 => self.ext_audio_status_ctl = val,
MIXER_PCM_FRONT_DAC_RATE_2C => self.pcm_front_dac_rate = val,
MIXER_PCM_SURR_DAC_RATE_2E => self.pcm_surr_dac_rate = val,
MIXER_PCM_LFE_DAC_RATE_30 => self.pcm_lfe_dac_rate = val,
_ => (),
}
}
/// Returns the mute status and left and right attenuation from the master volume register.
pub fn get_master_volume(&self) -> (bool, f64, f64) {
(
self.master_mute,
f64::from(self.master_volume_l) * MASTER_VOLUME_STEP_DB,
f64::from(self.master_volume_r) * MASTER_VOLUME_STEP_DB,
)
}
/// Returns the front sample rate (reg 0x2c).
pub fn get_sample_rate(&self) -> u16 {
// MIXER_PCM_FRONT_DAC_RATE_2C, MIXER_PCM_SURR_DAC_RATE_2E, and MIXER_PCM_LFE_DAC_RATE_30
// are updated to the same rate when playback with 2,4 and 6 tubes.
self.pcm_front_dac_rate
}
// Returns the master mute and l/r volumes (reg 0x02).
fn get_master_reg(&self) -> u16 {
let reg = (u16::from(self.master_volume_l)) << 8 | u16::from(self.master_volume_r);
if self.master_mute {
reg | MUTE_REG_BIT
} else {
reg
}
}
// Handles writes to the master register (0x02).
fn set_master_reg(&mut self, val: u16) {
self.master_mute = val & MUTE_REG_BIT != 0;
self.master_volume_r = (val & VOL_REG_MASK) as u8;
self.master_volume_l = (val >> 8 & VOL_REG_MASK) as u8;
}
// Returns the value read in the Mic volume register (0x0e).
fn get_mic_volume(&self) -> u16 {
let mut reg = u16::from(self.mic_volume);
if self.mic_muted {
reg |= MUTE_REG_BIT;
}
if self.mic_20db {
reg |= MIXER_MIC_20DB;
}
reg
}
// Sets the mic input mute, boost, and volume settings (0x0e).
fn set_mic_volume(&mut self, val: u16) {
self.mic_volume = (val & MIXER_VOL_MASK) as u8;
self.mic_muted = val & MUTE_REG_BIT != 0;
self.mic_20db = val & MIXER_MIC_20DB != 0;
}
// Returns the value read in the Mic volume register (0x18).
fn get_pcm_out_volume(&self) -> u16 {
let reg = (self.pcm_out_vol_l as u16) << 8 | self.pcm_out_vol_r as u16;
if self.pcm_out_mute {
reg | MUTE_REG_BIT
} else {
reg
}
}
// Sets the pcm output mute and volume states (0x18).
fn set_pcm_out_volume(&mut self, val: u16) {
self.pcm_out_vol_r = val & MIXER_VOL_MASK;
self.pcm_out_vol_l = (val >> MIXER_VOL_LEFT_SHIFT) & MIXER_VOL_MASK;
self.pcm_out_mute = val & MUTE_REG_BIT != 0;
}
// Returns the record gain register (0x01c).
fn get_record_gain_reg(&self) -> u16 {
let reg = u16::from(self.record_gain_l) << 8 | u16::from(self.record_gain_r);
if self.record_gain_mute {
reg | MUTE_REG_BIT
} else {
reg
}
}
// Handles writes to the record_gain register (0x1c).
fn set_record_gain_reg(&mut self, val: u16) {
self.record_gain_mute = val & MUTE_REG_BIT != 0;
self.record_gain_r = (val & VOL_REG_MASK) as u8;
self.record_gain_l = (val >> 8 & VOL_REG_MASK) as u8;
}
// Handles writes to the powerdown ctrl/status register (0x26).
fn set_power_down_reg(&mut self, val: u16) {
self.power_down_control =
(val & !PD_REG_STATUS_MASK) | (self.power_down_control & PD_REG_STATUS_MASK);
}
}

View File

@ -0,0 +1,292 @@
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![allow(dead_code)]
// Audio Mixer Registers
// 00h Reset
// 02h Master Volume Mute
// 04h Headphone Volume Mute
// 06h Master Volume Mono Mute
// 08h Master Tone (R & L)
// 0Ah PC_BEEP Volume Mute
// 0Ch Phone Volume Mute
// 0Eh Mic Volume Mute
// 10h Line In Volume Mute
// 12h CD Volume Mute
// 14h Video Volume Mute
// 16h Aux Volume Mute
// 18h PCM Out Volume Mute
// 1Ah Record Select
// 1Ch Record Gain Mute
// 1Eh Record Gain Mic Mute
// 20h General Purpose
// 22h 3D Control
// 24h AC97 RESERVED
// 26h Powerdown Ctrl/Stat
// 28h Extended Audio ID
// 2Ah Extended Audio Status and Control
// 2CH PCM Front DAC Rate
// 2Eh PCM Surr DAC Rate
// 30h PCM LFE DAC Rate
// 32h PCM L/R ADC Rate
// 34h PCM MIC ADC Rate
// Size of IO register regions
pub const MIXER_REGS_SIZE: u64 = 0x400;
pub const MASTER_REGS_SIZE: u64 = 0x100;
pub const MIXER_RESET_00: u64 = 0x00;
pub const MIXER_MASTER_VOL_MUTE_02: u64 = 0x02;
pub const MIXER_MIC_VOL_MUTE_0E: u64 = 0x0e;
pub const MIXER_PCM_OUT_VOL_MUTE_18: u64 = 0x18;
pub const MIXER_REC_VOL_MUTE_1C: u64 = 0x1c;
pub const MIXER_POWER_DOWN_CONTROL_26: u64 = 0x26;
pub const MIXER_EXTENDED_AUDIO_ID_28: u64 = 0x28;
pub const MIXER_EXTENDED_AUDIO_STATUS_CONTROL_28: u64 = 0x2a;
pub const MIXER_PCM_FRONT_DAC_RATE_2C: u64 = 0x2c;
pub const MIXER_PCM_SURR_DAC_RATE_2E: u64 = 0x2e;
pub const MIXER_PCM_LFE_DAC_RATE_30: u64 = 0x30;
pub const MIXER_VENDOR_ID1_7C: u64 = 0x7c;
pub const MIXER_VENDOR_ID2_7E: u64 = 0x7e;
// Extended Audio ID Bits.
pub const MIXER_EI_VRA: u16 = 0x0001; // Variable Rate Audio mode is available.
pub const MIXER_EI_CDAC: u16 = 0x0040; // PCM Center DAC is available.
pub const MIXER_EI_SDAC: u16 = 0x0080; // PCM Surround DAC is available.
pub const MIXER_EI_LDAC: u16 = 0x0100; // PCM LFE DAC is available.
// Basic capabilities for MIXER_RESET_00
pub const BC_DEDICATED_MIC: u16 = 0x0001; /* Dedicated Mic PCM In Tube */
// Bus Master regs from ICH spec:
// 00h PI_BDBAR PCM In Buffer Descriptor list Base Address Register
// 04h PI_CIV PCM In Current Index Value
// 05h PI_LVI PCM In Last Valid Index
// 06h PI_SR PCM In Status Register
// 08h PI_PICB PCM In Position In Current Buffer
// 0Ah PI_PIV PCM In Prefetched Index Value
// 0Bh PI_CR PCM In Control Register
// 10h PO_BDBAR PCM Out Buffer Descriptor list Base Address Register
// 14h PO_CIV PCM Out Current Index Value
// 15h PO_LVI PCM Out Last Valid Index
// 16h PO_SR PCM Out Status Register
// 18h PO_PICB PCM Out Position In Current Buffer
// 1Ah PO_PIV PCM Out Prefetched Index Value
// 1Bh PO_CR PCM Out Control Register
// 20h MC_BDBAR Mic. In Buffer Descriptor list Base Address Register
// 24h PM_CIV Mic. In Current Index Value
// 25h MC_LVI Mic. In Last Valid Index
// 26h MC_SR Mic. In Status Register
// 28h MC_PICB Mic In Position In Current Buffer
// 2Ah MC_PIV Mic. In Prefetched Index Value
// 2Bh MC_CR Mic. In Control Register
// 2Ch GLOB_CNT Global Control
// 30h GLOB_STA Global Status
// 34h ACC_SEMA Codec Write Semaphore Register
// Global Control
pub const GLOB_CNT_2C: u64 = 0x2C;
pub const GLOB_CNT_COLD_RESET: u32 = 0x0000_0002;
pub const GLOB_CNT_WARM_RESET: u32 = 0x0000_0004;
pub const GLOB_CNT_STABLE_BITS: u32 = 0x0000_007f; // Bits not affected by reset.
// PCM 4/6 Enable bits
pub const GLOB_CNT_PCM_2: u32 = 0x0000_0000; // 2 tubes
pub const GLOB_CNT_PCM_4: u32 = 0x0010_0000; // 4 tubes
pub const GLOB_CNT_PCM_6: u32 = 0x0020_0000; // 6 tubes
pub const GLOB_CNT_PCM_246_MASK: u32 = GLOB_CNT_PCM_4 | GLOB_CNT_PCM_6; // tube mask
// Global status
pub const GLOB_STA_30: u64 = 0x30;
// Primary codec ready set and turn on D20:21 to support 4 and 6 tubes on PCM out.
pub const GLOB_STA_RESET_VAL: u32 = 0x0030_0100;
// glob_sta bits
pub const GS_MD3: u32 = 1 << 17;
pub const GS_AD3: u32 = 1 << 16;
pub const GS_RCS: u32 = 1 << 15;
pub const GS_B3S12: u32 = 1 << 14;
pub const GS_B2S12: u32 = 1 << 13;
pub const GS_B1S12: u32 = 1 << 12;
pub const GS_S1R1: u32 = 1 << 11;
pub const GS_S0R1: u32 = 1 << 10;
pub const GS_S1CR: u32 = 1 << 9;
pub const GS_S0CR: u32 = 1 << 8;
pub const GS_MINT: u32 = 1 << 7;
pub const GS_POINT: u32 = 1 << 6;
pub const GS_PIINT: u32 = 1 << 5;
pub const GS_RSRVD: u32 = 1 << 4 | 1 << 3;
pub const GS_MOINT: u32 = 1 << 2;
pub const GS_MIINT: u32 = 1 << 1;
pub const GS_GSCI: u32 = 1;
pub const GS_RO_MASK: u32 = GS_B3S12
| GS_B2S12
| GS_B1S12
| GS_S1CR
| GS_S0CR
| GS_MINT
| GS_POINT
| GS_PIINT
| GS_RSRVD
| GS_MOINT
| GS_MIINT;
pub const GS_VALID_MASK: u32 = 0x0003_ffff;
pub const GS_WCLEAR_MASK: u32 = GS_RCS | GS_S1R1 | GS_S0R1 | GS_GSCI;
pub const ACC_SEMA_34: u64 = 0x34;
// Audio funciton registers.
pub const CIV_OFFSET: u64 = 0x04;
pub const LVI_OFFSET: u64 = 0x05;
pub const SR_OFFSET: u64 = 0x06;
pub const PICB_OFFSET: u64 = 0x08;
pub const PIV_OFFSET: u64 = 0x0a;
pub const CR_OFFSET: u64 = 0x0b;
// Capture
pub const PI_BASE_00: u64 = 0x00;
pub const PI_BDBAR_00: u64 = PI_BASE_00;
pub const PI_CIV_04: u64 = PI_BASE_00 + CIV_OFFSET;
pub const PI_LVI_05: u64 = PI_BASE_00 + LVI_OFFSET;
pub const PI_SR_06: u64 = PI_BASE_00 + SR_OFFSET;
pub const PI_PICB_08: u64 = PI_BASE_00 + PICB_OFFSET;
pub const PI_PIV_0A: u64 = PI_BASE_00 + PIV_OFFSET;
pub const PI_CR_0B: u64 = PI_BASE_00 + CR_OFFSET;
// Play Out
pub const PO_BASE_10: u64 = 0x10;
pub const PO_BDBAR_10: u64 = PO_BASE_10;
pub const PO_CIV_14: u64 = PO_BASE_10 + CIV_OFFSET;
pub const PO_LVI_15: u64 = PO_BASE_10 + LVI_OFFSET;
pub const PO_SR_16: u64 = PO_BASE_10 + SR_OFFSET;
pub const PO_PICB_18: u64 = PO_BASE_10 + PICB_OFFSET;
pub const PO_PIV_1A: u64 = PO_BASE_10 + PIV_OFFSET;
pub const PO_CR_1B: u64 = PO_BASE_10 + CR_OFFSET;
// Microphone
pub const MC_BASE_20: u64 = 0x20;
pub const MC_BDBAR_20: u64 = MC_BASE_20;
pub const MC_CIV_24: u64 = MC_BASE_20 + CIV_OFFSET;
pub const MC_LVI_25: u64 = MC_BASE_20 + LVI_OFFSET;
pub const MC_SR_26: u64 = MC_BASE_20 + SR_OFFSET;
pub const MC_PICB_28: u64 = MC_BASE_20 + PICB_OFFSET;
pub const MC_PIV_2A: u64 = MC_BASE_20 + PIV_OFFSET;
pub const MC_CR_2B: u64 = MC_BASE_20 + CR_OFFSET;
// Status Register Bits.
pub const SR_DCH: u16 = 0x01;
pub const SR_CELV: u16 = 0x02;
pub const SR_LVBCI: u16 = 0x04;
pub const SR_BCIS: u16 = 0x08;
pub const SR_FIFOE: u16 = 0x10;
pub const SR_VALID_MASK: u16 = 0x1f;
pub const SR_WCLEAR_MASK: u16 = SR_FIFOE | SR_BCIS | SR_LVBCI;
pub const SR_RO_MASK: u16 = SR_DCH | SR_CELV;
pub const SR_INT_MASK: u16 = SR_BCIS | SR_LVBCI;
// Control Register Bits.
pub const CR_RPBM: u8 = 0x01;
pub const CR_RR: u8 = 0x02;
pub const CR_LVBIE: u8 = 0x04;
pub const CR_FEIE: u8 = 0x08;
pub const CR_IOCE: u8 = 0x10;
pub const CR_VALID_MASK: u8 = 0x1f;
pub const CR_DONT_CLEAR_MASK: u8 = CR_IOCE | CR_FEIE | CR_LVBIE;
// Mixer register bits
pub const MUTE_REG_BIT: u16 = 0x8000;
pub const VOL_REG_MASK: u16 = 0x003f;
pub const MIXER_VOL_MASK: u16 = 0x001f;
pub const MIXER_VOL_LEFT_SHIFT: usize = 8;
pub const MIXER_MIC_20DB: u16 = 0x0040;
// Powerdown reg
pub const PD_REG_STATUS_MASK: u16 = 0x000f;
pub const PD_REG_OUTPUT_MUTE_MASK: u16 = 0xb200;
pub const PD_REG_INPUT_MUTE_MASK: u16 = 0x0d00;
// Buffer descriptors are four bytes of pointer and 4 bytes of control/length.
pub const DESCRIPTOR_LENGTH: usize = 8;
pub const BD_IOC: u32 = 1 << 31;
/// The functions that are supported by the Ac97 subsystem.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Ac97Function {
Input,
Output,
Microphone,
}
/// Registers for individual audio functions.
/// Each audio function in Ac97 gets a set of these registers.
#[derive(Clone, Default)]
pub struct Ac97FunctionRegs {
label: String,
pub bdbar: u32,
pub civ: u8,
pub lvi: u8,
pub sr: u16,
pub picb: u16,
pub piv: u8,
pub cr: u8,
}
impl Ac97FunctionRegs {
/// Creates a new set of function registers, these can be used for the capture, playback, or
/// microphone functions.
pub fn new(label: &str) -> Self {
let mut regs = Ac97FunctionRegs {
label: label.to_string(),
sr: SR_DCH,
..Default::default()
};
regs.do_reset();
regs
}
pub fn label(&self) -> &str {
&self.label
}
/// Reset all the registers to the PoR defaults. `sr` should be updated by `update_sr`.
pub fn do_reset(&mut self) {
self.bdbar = 0;
self.civ = 0;
self.lvi = 0;
self.picb = 0;
self.piv = 0;
self.cr &= CR_DONT_CLEAR_MASK;
}
/// Read register 4, 5, and 6 as one 32 bit word.
/// According to the ICH spec, reading these three with one 32 bit access is allowed.
pub fn atomic_status_regs(&self) -> u32 {
u32::from(self.civ) | u32::from(self.lvi) << 8 | u32::from(self.sr) << 16
}
/// Returns the mask for enabled interrupts. The returned mask represents the bits in the status
/// register that should trigger and interrupt.
pub fn int_mask(&self) -> u16 {
let mut int_mask = 0;
if self.cr & CR_LVBIE != 0 {
int_mask |= SR_LVBCI;
}
if self.cr & CR_IOCE != 0 {
int_mask |= SR_BCIS;
}
int_mask
}
/// Sets the current buffer to the next buffer by updating CIV to PIV, and
/// updates related fields.
pub fn move_to_next_buffer(&mut self) {
self.civ = self.piv;
self.piv = (self.piv + 1) % 32; // move piv to the next buffer.
}
/// Returns irq status.
pub fn has_irq(&self) -> bool {
self.sr & self.int_mask() != 0
}
}

7
src/devices/ac97/mod.rs Normal file
View File

@ -0,0 +1,7 @@
mod ac97;
mod ac97_mixer;
mod ac97_bus_master;
mod ac97_regs;
pub use ac97::Ac97Dev;

View File

@ -1,3 +1,4 @@
pub mod ac97;
pub mod serial;
pub mod rtc;
mod virtio_9p;
@ -6,6 +7,7 @@ mod virtio_rng;
mod virtio_wl;
mod virtio_block;
mod virtio_net;
mod irq_event;
pub use self::virtio_serial::VirtioSerial;
pub use self::virtio_9p::VirtioP9;

View File

@ -7,8 +7,9 @@ pub mod util;
mod vm;
mod memory;
mod devices;
mod virtio;
mod disk;
mod io;
mod audio;
pub use util::{Logger,LogLevel};
pub use vm::VmConfig;

View File

@ -15,6 +15,7 @@ pub struct VmConfig {
wayland: bool,
dmabuf: bool,
network: bool,
audio: bool,
home: String,
colorscheme: String,
bridge_name: String,
@ -39,6 +40,7 @@ impl VmConfig {
wayland: true,
dmabuf: false,
network: true,
audio: true,
bridge_name: "vz-clear".to_string(),
home: Self::default_homedir(),
colorscheme: "dracula".to_string(),
@ -213,6 +215,10 @@ impl VmConfig {
self.dmabuf
}
pub fn is_audio_enable(&self) -> bool {
self.audio
}
pub fn bridge(&self) -> &str {
&self.bridge_name
}

View File

@ -1,10 +1,12 @@
use std::{result, io};
use kvm_ioctls::Cap;
use crate::{system, virtio};
use crate::system;
use crate::system::netlink;
use crate::vm::arch;
use thiserror::Error;
use crate::io::virtio;
pub type Result<T> = result::Result<T, Error>;
#[derive(Error,Debug)]
@ -37,4 +39,6 @@ pub enum Error {
SetupVirtio(virtio::Error),
#[error("failed to create Vcpu: {0}")]
CreateVcpu(kvm_ioctls::Error),
#[error("{0}")]
VirtioError(#[from]crate::io::VirtioError),
}

View File

@ -13,6 +13,7 @@ fn add_defaults(cmdline: &mut KernelCmdLine) {
.push("init_on_alloc=0")
.push("init_on_free=0")
.push_set_val("console", "hvc0")
.push("i8042.direct")
@ -20,10 +21,12 @@ fn add_defaults(cmdline: &mut KernelCmdLine) {
.push("i8042.nopnp")
.push("i8042.noaux")
.push("i8042.nomux")
// .push("initcall_debug")
.push_set_val("iommu", "off")
.push("cryptomgr.notests")
.push("snd_intel8x0.inside_vm=1")
.push("snd_intel8x0.ac97_clock=48000")
.push_set_val("8250.nr_uarts", "0");
}

View File

@ -11,7 +11,7 @@ use crate::memory::MemoryManager;
use std::sync::atomic::AtomicBool;
use kvm_ioctls::VmFd;
use vmm_sys_util::eventfd::EventFd;
use crate::devices::ac97::{Ac97Dev, Ac97Parameters};
use crate::devices::ac97::Ac97Dev;
use crate::devices::serial::SerialPort;
use crate::io::manager::IoManager;
use crate::{Logger, LogLevel};
@ -132,7 +132,7 @@ impl <T: ArchSetup> VmSetup <T> {
let irq = vm.io_manager.allocator().allocate_irq();
let mem = vm.memory.guest_ram().clone();
// XXX expect()
let ac97 = Ac97Dev::try_new(&vm.kvm_vm, irq, mem, Ac97Parameters::new_pulseaudio()).expect("audio initialize error");
let ac97 = Ac97Dev::try_new(&vm.kvm_vm, irq, mem).expect("audio initialize error");
vm.io_manager.add_pci_device(Arc::new(Mutex::new(ac97)));
}