From cded52b7c98762c5f60b26526ac63fa86e0b9bc9 Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Thu, 3 Aug 2023 08:23:20 -0400 Subject: [PATCH] Port of ac97 from crosvm with pulseaudio backend. --- Cargo.toml | 1 + src/audio/mod.rs | 113 ++++ src/audio/pulse/client.rs | 66 ++ src/audio/pulse/context.rs | 148 +++++ src/audio/pulse/message.rs | 104 +++ src/audio/pulse/mod.rs | 32 + src/audio/pulse/stream.rs | 182 ++++++ src/audio/shm_streams.rs | 278 ++++++++ src/devices/ac97/ac97.rs | 188 ++++++ src/devices/ac97/ac97_bus_master.rs | 947 ++++++++++++++++++++++++++++ src/devices/ac97/ac97_mixer.rs | 200 ++++++ src/devices/ac97/ac97_regs.rs | 292 +++++++++ src/devices/ac97/mod.rs | 7 + src/devices/mod.rs | 2 + src/lib.rs | 3 +- src/vm/config.rs | 6 + src/vm/error.rs | 6 +- src/vm/kernel_cmdline.rs | 5 +- src/vm/setup.rs | 4 +- 19 files changed, 2579 insertions(+), 5 deletions(-) create mode 100644 src/audio/mod.rs create mode 100644 src/audio/pulse/client.rs create mode 100644 src/audio/pulse/context.rs create mode 100644 src/audio/pulse/message.rs create mode 100644 src/audio/pulse/mod.rs create mode 100644 src/audio/pulse/stream.rs create mode 100644 src/audio/shm_streams.rs create mode 100644 src/devices/ac97/ac97.rs create mode 100644 src/devices/ac97/ac97_bus_master.rs create mode 100644 src/devices/ac97/ac97_mixer.rs create mode 100644 src/devices/ac97/ac97_regs.rs create mode 100644 src/devices/ac97/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 7a8a8de..faa6581 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/src/audio/mod.rs b/src/audio/mod.rs new file mode 100644 index 0000000..361a91e --- /dev/null +++ b/src/audio/mod.rs @@ -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 { + 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; + +/// 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 { + 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); +} \ No newline at end of file diff --git a/src/audio/pulse/client.rs b/src/audio/pulse/client.rs new file mode 100644 index 0000000..94c666e --- /dev/null +++ b/src/audio/pulse/client.rs @@ -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 { + 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> { + + 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)) + } +} \ No newline at end of file diff --git a/src/audio/pulse/context.rs b/src/audio/pulse/context.rs new file mode 100644 index 0000000..e66fd98 --- /dev/null +++ b/src/audio/pulse/context.rs @@ -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>, + context: Rc>, +} + +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> { + 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) { + 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), + } + } + } + } +} + diff --git a/src/audio/pulse/message.rs b/src/audio/pulse/message.rs new file mode 100644 index 0000000..7a84701 --- /dev/null +++ b/src/audio/pulse/message.rs @@ -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, +} + +impl PulseContextMessage { + + pub fn new(request: PulseContextRequest) -> (Self, Receiver) { + 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 +} + +impl PulseMessageChannel { + pub fn new(sender: Sender) -> Self { + PulseMessageChannel { sender } + } + + fn exchange_message(&self, req: PulseContextRequest) -> Result { + 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 { + match self.exchange_message(PulseContextRequest::NewPlaybackStream { spec, buffer_size, channel})? { + PulseContextResponse::ResponseOk => Err(UnexpectedResponse), + PulseContextResponse::ResponseError(err) => Err(err), + PulseContextResponse::ResponseStream(stream) => Ok(stream), + } + } +} \ No newline at end of file diff --git a/src/audio/pulse/mod.rs b/src/audio/pulse/mod.rs new file mode 100644 index 0000000..759f824 --- /dev/null +++ b/src/audio/pulse/mod.rs @@ -0,0 +1,32 @@ +use std::result; +use pulse::error::PAErr; + +mod client; +mod context; +mod message; +mod stream; + +pub type Result = result::Result; + +#[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; \ No newline at end of file diff --git a/src/audio/pulse/stream.rs b/src/audio/pulse/stream.rs new file mode 100644 index 0000000..7ecfbc8 --- /dev/null +++ b/src/audio/pulse/stream.rs @@ -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, + cond: Condvar, +} + +impl Available { + fn new() -> Self { + Available { + byte_count: Mutex::new(0), + cond: Condvar::new(), + } + } + + fn byte_count_lock(&self) -> MutexGuard { + 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 { + 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>, + avail: Arc, + 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 { + 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> { + 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(()) + } +} \ No newline at end of file diff --git a/src/audio/shm_streams.rs b/src/audio/shm_streams.rs new file mode 100644 index 0000000..750fb5e --- /dev/null +++ b/src/audio/shm_streams.rs @@ -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 = Result; + +/// `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(requested_frames: usize, buffer_set: &'a mut D) -> Self { + pub fn new(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>; +} + +/// `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 + 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>; +} + +/// 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, + 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> { + 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))) + } +} diff --git a/src/devices/ac97/ac97.rs b/src/devices/ac97/ac97.rs new file mode 100644 index 0000000..6c0d466 --- /dev/null +++ b/src/devices/ac97/ac97.rs @@ -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 { + 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 { + 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 { + Some(self.irq) + } + + fn bar_allocations(&self) -> Vec { + vec![ + PciBarAllocation::Mmio(PciBar::Bar0, MIXER_REGS_SIZE as usize), + PciBarAllocation::Mmio(PciBar::Bar1, MASTER_REGS_SIZE as usize) + ] + } +} \ No newline at end of file diff --git a/src/devices/ac97/ac97_bus_master.rs b/src/devices/ac97/ac97_bus_master.rs new file mode 100644 index 0000000..a6e58e6 --- /dev/null +++ b/src/devices/ac97/ac97_bus_master.rs @@ -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; + +// 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, +} + +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 for AudioError { + fn from(err: GuestMemoryError) -> Self { + AudioError::ReadingGuestError(err) + } +} + +type GuestMemoryResult = Result; + +type AudioResult = Result; + +// Audio thread book-keeping data +struct AudioThreadInfo { + thread: Option>, + thread_run: Arc, + thread_semaphore: Arc, + stream_control: Option>, +} + +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>, + 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>, +} + +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 { + 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(®s.pi_regs, offset - PI_BASE_00), + PO_BASE_10..=PO_CR_1B => readb_func_regs(®s.po_regs, offset - PO_BASE_10), + MC_BASE_20..=MC_CR_2B => readb_func_regs(®s.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 { + 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 { + 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 { + 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, +} + +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 { + 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> { + 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>, + mem: GuestRam, + thread_run: Arc, + lvi_semaphore: Arc, + message_interval: Duration, + stream: Box, + pending_buffers: Arc>>>, +} + +struct AudioWorkerParams { + func: Ac97Function, + stream: Box, + pending_buffers: VecDeque>, + 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> { + 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(()) + } +} diff --git a/src/devices/ac97/ac97_mixer.rs b/src/devices/ac97/ac97_mixer.rs new file mode 100644 index 0000000..46d70ee --- /dev/null +++ b/src/devices/ac97/ac97_mixer.rs @@ -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); + } +} diff --git a/src/devices/ac97/ac97_regs.rs b/src/devices/ac97/ac97_regs.rs new file mode 100644 index 0000000..af19587 --- /dev/null +++ b/src/devices/ac97/ac97_regs.rs @@ -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 AC’97 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 + } +} diff --git a/src/devices/ac97/mod.rs b/src/devices/ac97/mod.rs new file mode 100644 index 0000000..dc7600c --- /dev/null +++ b/src/devices/ac97/mod.rs @@ -0,0 +1,7 @@ + +mod ac97; +mod ac97_mixer; +mod ac97_bus_master; +mod ac97_regs; + +pub use ac97::Ac97Dev; diff --git a/src/devices/mod.rs b/src/devices/mod.rs index fe76f43..860c4b6 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -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; diff --git a/src/lib.rs b/src/lib.rs index fe82095..2c06133 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/vm/config.rs b/src/vm/config.rs index d9ea864..4ffc687 100644 --- a/src/vm/config.rs +++ b/src/vm/config.rs @@ -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 } diff --git a/src/vm/error.rs b/src/vm/error.rs index 1e94d55..6f90260 100644 --- a/src/vm/error.rs +++ b/src/vm/error.rs @@ -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 = result::Result; #[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), } \ No newline at end of file diff --git a/src/vm/kernel_cmdline.rs b/src/vm/kernel_cmdline.rs index 66c2c19..623da62 100644 --- a/src/vm/kernel_cmdline.rs +++ b/src/vm/kernel_cmdline.rs @@ -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"); } diff --git a/src/vm/setup.rs b/src/vm/setup.rs index 4bfe68b..35b5798 100644 --- a/src/vm/setup.rs +++ b/src/vm/setup.rs @@ -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 VmSetup { 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))); }