blob: e121a6f7b432c32b2eec1fd167e433a8a012ef21 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::collections::VecDeque;
use std::convert::TryInto;
use std::fmt::{self, Display};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
use audio_streams::{
shm_streams::{ShmStream, ShmStreamSource},
BoxError, NoopStreamControl, SampleFormat, StreamControl, StreamDirection, StreamEffect,
};
use base::{
self, error, set_rt_prio_limit, set_rt_round_robin, warn, AsRawDescriptors, Event,
RawDescriptor,
};
use sync::{Condvar, Mutex};
use vm_memory::{GuestAddress, GuestMemory};
use crate::pci::ac97_mixer::Ac97Mixer;
use crate::pci::ac97_regs::*;
const INPUT_SAMPLE_RATE: u32 = 48000;
const DEVICE_INPUT_CHANNEL_COUNT: usize = 2;
// 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<Event>,
}
impl Ac97BusMasterRegs {
fn new() -> Ac97BusMasterRegs {
Ac97BusMasterRegs {
pi_regs: Ac97FunctionRegs::new(),
po_regs: Ac97FunctionRegs::new(),
po_pointer_update_time: Instant::now(),
mc_regs: Ac97FunctionRegs::new(),
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(Debug)]
enum GuestMemoryError {
// Failure getting the address of the audio buffer.
ReadingGuestBufferAddress(vm_memory::GuestMemoryError),
}
impl std::error::Error for GuestMemoryError {}
impl Display for GuestMemoryError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::GuestMemoryError::*;
match self {
ReadingGuestBufferAddress(e) => {
write!(f, "Failed to get the address of the audio buffer: {}.", e)
}
}
}
}
impl From<GuestMemoryError> for AudioError {
fn from(err: GuestMemoryError) -> Self {
AudioError::ReadingGuestError(err)
}
}
type GuestMemoryResult<T> = std::result::Result<T, GuestMemoryError>;
// Internal error type used for reporting errors from the audio thread.
#[derive(Debug)]
enum AudioError {
// Failed to create a new stream.
CreateStream(BoxError),
// Failure to get regions from guest memory.
GuestRegion(GuestMemoryError),
// Invalid buffer offset received from the audio server.
InvalidBufferOffset,
// Guest did not provide a buffer when needed.
NoBufferAvailable,
// Failure to read guest memory.
ReadingGuestError(GuestMemoryError),
// Failure to respond to the ServerRequest.
RespondRequest(BoxError),
// Failure to wait for a request from the stream.
WaitForAction(BoxError),
}
impl std::error::Error for AudioError {}
impl Display for AudioError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::AudioError::*;
match self {
CreateStream(e) => write!(f, "Failed to create audio stream: {}.", e),
GuestRegion(e) => write!(f, "Failed to get guest memory region: {}.", e),
InvalidBufferOffset => write!(f, "Offset > max usize"),
NoBufferAvailable => write!(f, "No buffer was available from the Guest"),
ReadingGuestError(e) => write!(f, "Failed to read guest memory: {}.", e),
RespondRequest(e) => write!(f, "Failed to respond to the ServerRequest: {}", e),
WaitForAction(e) => write!(f, "Failed to wait for a message from the stream: {}", e),
}
}
}
type AudioResult<T> = std::result::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) {
const AUDIO_THREAD_RTPRIO: u16 = 10; // Matches other cros audio clients.
self.thread_run.store(true, Ordering::Relaxed);
self.thread = Some(thread::spawn(move || {
if let Err(e) = set_rt_prio_limit(u64::from(AUDIO_THREAD_RTPRIO))
.and_then(|_| set_rt_round_robin(i32::from(AUDIO_THREAD_RTPRIO)))
{
warn!("Failed to set audio thread to real time: {}", e);
}
if let Err(e) = worker.run() {
error!("{:?} error: {}", worker.func, e);
}
worker.thread_run.store(false, Ordering::Relaxed);
}));
self.stream_control = Some(Box::new(NoopStreamControl::new()));
}
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() {
error!("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: GuestMemory,
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: Box<dyn ShmStreamSource>,
// 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: GuestMemory, audio_server: Box<dyn ShmStreamSource>) -> 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,
}
}
/// Returns any file descriptors that need to be kept open when entering a jail.
pub fn keep_rds(&self) -> Option<Vec<RawDescriptor>> {
let mut rds = self.audio_server.keep_fds();
rds.append(&mut self.mem.as_raw_descriptors());
Some(rds)
}
/// Provides the events needed to raise interrupts in the guest.
pub fn set_irq_event(&mut self, irq_evt: Event, irq_resample_evt: Event) {
let thread_regs = self.regs.clone();
self.regs.lock().irq_evt = Some(irq_evt);
self.irq_resample_thread = Some(thread::spawn(move || {
loop {
if let Err(e) = irq_resample_evt.read() {
error!(
"Failed to read the irq event from the resample thread: {}.",
e,
);
break;
}
{
// Scope for the lock on thread_regs.
let regs = thread_regs.lock();
if regs.has_irq() {
if let Some(irq_evt) = regs.irq_evt.as_ref() {
if let Err(e) = irq_evt.write(1) {
error!("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.lock().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 {
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,
}
}
let regs = self.regs.lock();
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.lock();
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.lock();
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,
}
}
/// 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() {
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!("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!("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!("write long to 0x{:x}", o),
}
}
fn set_bdbar(&mut self, func: Ac97Function, val: u32) {
self.regs.lock().func_regs_mut(func).bdbar = val & !0x07;
}
fn set_lvi(&mut self, func: Ac97Function, val: u8) {
let mut regs = self.regs.lock();
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
{
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();
}
func_regs.sr &= !(SR_DCH | SR_CELV);
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 set_sr(&mut self, func: Ac97Function, val: u16) {
let mut sr = self.regs.lock().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;
}
update_sr(&mut self.regs.lock(), func, sr);
}
fn set_cr(&mut self, func: Ac97Function, val: u8, mixer: &Ac97Mixer) {
if val & CR_RR != 0 {
let mut regs = self.regs.lock();
Self::reset_func_regs(&mut regs, func);
} else {
let cr = self.regs.lock().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.lock();
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.lock();
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.lock();
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();
let mut regs = self.regs.lock();
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.lock();
regs.glob_cnt = new_glob_cnt & !GLOB_CNT_WARM_RESET; // Auto-cleared reset bit.
return;
}
}
self.regs.lock().glob_cnt = new_glob_cnt;
}
fn stream_effects(func: Ac97Function) -> Vec<StreamEffect> {
match func {
Ac97Function::Microphone => vec![StreamEffect::EchoCancellation],
_ => vec![StreamEffect::NoEffect],
}
}
fn current_sample_rate(&self, func: Ac97Function, mixer: &Ac97Mixer) -> u32 {
match func {
Ac97Function::Output => mixer.get_sample_rate().into(),
_ => INPUT_SAMPLE_RATE,
}
}
fn create_audio_worker(
&mut self,
mixer: &Ac97Mixer,
func: Ac97Function,
) -> AudioResult<AudioWorker> {
let direction = match func {
Ac97Function::Microphone => StreamDirection::Capture,
Ac97Function::Input => StreamDirection::Capture,
Ac97Function::Output => StreamDirection::Playback,
};
let locked_regs = self.regs.lock();
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 mut pending_buffers = VecDeque::with_capacity(2);
let starting_offsets = match direction {
StreamDirection::Capture => {
let mut offsets = [0, 0];
for offset in &mut offsets {
let buffer = next_guest_buffer(&locked_regs, &self.mem, func, 0)?
.ok_or(AudioError::NoBufferAvailable)?;
*offset = buffer.offset as u64;
pending_buffers.push_back(Some(buffer));
}
offsets
}
StreamDirection::Playback => [0, 0],
};
let stream = self
.audio_server
.new_stream(
direction,
num_channels,
SampleFormat::S16LE,
sample_rate,
buffer_frames,
&Self::stream_effects(func),
self.mem
.offset_region(starting_offsets[0])
.map_err(|e| {
AudioError::GuestRegion(GuestMemoryError::ReadingGuestBufferAddress(e))
})?
.inner(),
starting_offsets,
)
.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 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 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 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.lock();
Self::reset_func_regs(&mut regs, Ac97Function::Input);
Self::reset_func_regs(&mut regs, Ac97Function::Output);
Self::reset_func_regs(&mut regs, Ac97Function::Microphone);
}
}
#[derive(Debug)]
struct GuestBuffer {
index: u8,
offset: usize,
frames: usize,
}
fn get_buffer_offset(
func_regs: &Ac97FunctionRegs,
mem: &GuestMemory,
index: u8,
) -> GuestMemoryResult<u64> {
let descriptor_addr = func_regs.bdbar + u32::from(index) * DESCRIPTOR_LENGTH as u32;
let buffer_addr_reg: u32 = mem
.read_obj_from_addr(GuestAddress(u64::from(descriptor_addr)))
.map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
let buffer_addr = GuestAddress((buffer_addr_reg & !0x03u32) as u64); // The address must be aligned to four bytes.
mem.offset_from_base(buffer_addr)
.map_err(GuestMemoryError::ReadingGuestBufferAddress)
}
fn get_buffer_samples(
func_regs: &Ac97FunctionRegs,
mem: &GuestMemory,
index: u8,
) -> GuestMemoryResult<usize> {
let descriptor_addr = func_regs.bdbar + u32::from(index) * DESCRIPTOR_LENGTH as u32;
let control_reg: u32 = mem
.read_obj_from_addr(GuestAddress(u64::from(descriptor_addr) + 4))
.map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
let buffer_samples = control_reg as usize & 0x0000_ffff;
Ok(buffer_samples)
}
// 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: &GuestMemory,
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 offset = get_buffer_offset(func_regs, mem, index)?
.try_into()
.map_err(|_| AudioError::InvalidBufferOffset)?;
let frames = get_buffer_samples(func_regs, mem, index)? / regs.tube_count(func);
Ok(Some(GuestBuffer {
index,
offset,
frames,
}))
}
// Marks the current buffer completed and moves to the next buffer for the given
// function and registers.
fn buffer_completed(
regs: &mut Ac97BusMasterRegs,
mem: &GuestMemory,
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_obj_from_addr(GuestAddress(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(())
}
struct AudioWorker {
func: Ac97Function,
regs: Arc<Mutex<Ac97BusMasterRegs>>,
mem: GuestMemory,
thread_run: Arc<AtomicBool>,
lvi_semaphore: Arc<Condvar>,
message_interval: Duration,
stream: Box<dyn ShmStream>,
pending_buffers: 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: args.pending_buffers,
}
}
// 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;
let message_interval = self.message_interval;
// Set up picb.
{
let mut locked_regs = self.regs.lock();
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();
while locked_regs.func_regs(func).sr & SR_DCH != 0 {
locked_regs = self.lvi_semaphore.wait(locked_regs);
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,
};
let start = Instant::now();
let next_buffer = {
let mut locked_regs = self.regs.lock();
if self.pending_buffers.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(_)) = self.pending_buffers.pop_front() {
buffer_completed(&mut locked_regs, &self.mem, self.func)?;
}
}
// We count the number of pending, real buffers at the server, and
// then use that as our offset from CIV.
let offset = self.pending_buffers.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.
loop {
if let Some(buffer) = next_guest_buffer(&locked_regs, &self.mem, func, offset)?
{
break Some(buffer);
}
let elapsed = start.elapsed();
if elapsed > message_interval {
break None;
}
locked_regs = self
.lvi_semaphore
.wait_timeout(locked_regs, message_interval - elapsed)
.0;
}
};
match next_buffer {
Some(ref buffer) => {
let requested_frames = request.requested_frames();
if requested_frames != buffer.frames {
// We should be able to handle when the number of frames in
// the buffer doesn't match the number of frames requested,
// but we don't yet.
warn!(
"Stream requested {} frames but buffer had {} frames: {:?}",
requested_frames, buffer.frames, buffer
);
}
request
.set_buffer_offset_and_frames(
buffer.offset,
std::cmp::min(requested_frames, buffer.frames),
)
.map_err(AudioError::RespondRequest)?;
}
None => {
request
.ignore_request()
.map_err(AudioError::RespondRequest)?;
}
}
self.pending_buffers.push_back(next_buffer);
}
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(irq_evt) = regs.irq_evt.as_ref() {
// Ignore write failure, nothing can be done about it from here.
let _ = irq_evt.write(1);
}
} 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: &GuestMemory,
) -> GuestMemoryResult<usize> {
let civ = func_regs.civ;
get_buffer_samples(func_regs, mem, civ)
}
#[cfg(test)]
mod test {
use super::*;
use audio_streams::shm_streams::MockShmStreamSource;
#[test]
fn bm_bdbar() {
let mut bm = Ac97BusMaster::new(
GuestMemory::new(&[]).expect("Creating guest memory failed."),
Box::new(MockShmStreamSource::new()),
);
let mut mixer = Ac97Mixer::new();
let bdbars = [0x00u64, 0x10, 0x20];
// Make sure writes have no affect during cold reset.
bm.writel(0x00, 0x5555_555f, &mut mixer);
assert_eq!(bm.readl(0x00), 0x0000_0000);
// Relesase cold reset.
bm.writel(GLOB_CNT_2C, 0x0000_0002, &mut mixer);
// Tests that the base address is writable and that the bottom three bits are read only.
for bdbar in &bdbars {
assert_eq!(bm.readl(*bdbar), 0x0000_0000);
bm.writel(*bdbar, 0x5555_555f, &mut mixer);
assert_eq!(bm.readl(*bdbar), 0x5555_5558);
}
}
#[test]
fn bm_status_reg() {
let mut bm = Ac97BusMaster::new(
GuestMemory::new(&[]).expect("Creating guest memory failed."),
Box::new(MockShmStreamSource::new()),
);
let mixer = Ac97Mixer::new();
let sr_addrs = [0x06u64, 0x16, 0x26];
for sr in &sr_addrs {
assert_eq!(bm.readw(*sr, &mixer), 0x0001);
bm.writew(*sr, 0xffff);
assert_eq!(bm.readw(*sr, &mixer), 0x0001);
}
}
#[test]
fn bm_global_control() {
let mut bm = Ac97BusMaster::new(
GuestMemory::new(&[]).expect("Creating guest memory failed."),
Box::new(MockShmStreamSource::new()),
);
let mut mixer = Ac97Mixer::new();
assert_eq!(bm.readl(GLOB_CNT_2C), 0x0000_0000);
// Relesase cold reset.
bm.writel(GLOB_CNT_2C, 0x0000_0002, &mut mixer);
// Check interrupt enable bits are writable.
bm.writel(GLOB_CNT_2C, 0x0000_0072, &mut mixer);
assert_eq!(bm.readl(GLOB_CNT_2C), 0x0000_0072);
// A Warm reset should doesn't affect register state and is auto cleared.
bm.writel(0x00, 0x5555_5558, &mut mixer);
bm.writel(GLOB_CNT_2C, 0x0000_0076, &mut mixer);
assert_eq!(bm.readl(GLOB_CNT_2C), 0x0000_0072);
assert_eq!(bm.readl(0x00), 0x5555_5558);
// Check that a cold reset works, but setting bdbar and checking it is zeroed.
bm.writel(0x00, 0x5555_555f, &mut mixer);
bm.writel(GLOB_CNT_2C, 0x000_0070, &mut mixer);
assert_eq!(bm.readl(GLOB_CNT_2C), 0x0000_0070);
assert_eq!(bm.readl(0x00), 0x0000_0000);
}
#[test]
fn run_multi_tube_playback() {
start_playback(2, 48000);
start_playback(4, 48000);
start_playback(6, 48000);
}
#[test]
fn run_multi_rate_playback() {
start_playback(2, 32000);
start_playback(2, 44100);
start_playback(2, 48000);
}
fn start_playback(num_channels: usize, rate: u16) {
const TIMEOUT: Duration = Duration::from_millis(500);
const LVI_MASK: u8 = 0x1f; // Five bits for 32 total entries.
const IOC_MASK: u32 = 0x8000_0000; // Interrupt on completion.
let num_buffers = LVI_MASK as usize + 1;
const BUFFER_SIZE: usize = 32768;
const FRAGMENT_SIZE: usize = BUFFER_SIZE / 2;
const GUEST_ADDR_BASE: u32 = 0x100_0000;
let mem = GuestMemory::new(&[(GuestAddress(GUEST_ADDR_BASE as u64), 1024 * 1024 * 1024)])
.expect("Creating guest memory failed.");
let stream_source = MockShmStreamSource::new();
let mut bm = Ac97BusMaster::new(mem.clone(), Box::new(stream_source.clone()));
let mut mixer = Ac97Mixer::new();
// Release cold reset.
bm.writel(GLOB_CNT_2C, 0x0000_0002, &mut mixer);
// Setup ping-pong buffers. A and B repeating for every possible index.
bm.writel(PO_BDBAR_10, GUEST_ADDR_BASE, &mut mixer);
for i in 0..num_buffers {
let pointer_addr = GuestAddress(GUEST_ADDR_BASE as u64 + i as u64 * 8);
let control_addr = GuestAddress(GUEST_ADDR_BASE as u64 + i as u64 * 8 + 4);
if i % 2 == 0 {
mem.write_obj_at_addr(GUEST_ADDR_BASE, pointer_addr)
.expect("Writing guest memory failed.");
} else {
mem.write_obj_at_addr(GUEST_ADDR_BASE + FRAGMENT_SIZE as u32, pointer_addr)
.expect("Writing guest memory failed.");
};
mem.write_obj_at_addr(IOC_MASK | (FRAGMENT_SIZE as u32) / 2, control_addr)
.expect("Writing guest memory failed.");
}
bm.writeb(PO_LVI_15, LVI_MASK, &mixer);
assert_eq!(bm.readb(PO_CIV_14), 0);
// Set tube count and sample rate.
let mut cnt = bm.readl(GLOB_CNT_2C);
cnt &= !GLOB_CNT_PCM_246_MASK;
mixer.writew(MIXER_PCM_FRONT_DAC_RATE_2C, rate);
if num_channels == 4 {
cnt |= GLOB_CNT_PCM_4;
mixer.writew(MIXER_PCM_SURR_DAC_RATE_2E, rate);
} else if num_channels == 6 {
cnt |= GLOB_CNT_PCM_6;
mixer.writew(MIXER_PCM_LFE_DAC_RATE_30, rate);
}
bm.writel(GLOB_CNT_2C, cnt, &mut mixer);
// Start.
bm.writeb(PO_CR_1B, CR_IOCE | CR_RPBM, &mixer);
// TODO(crbug.com/1058881): The test is flaky in builder.
// assert_eq!(bm.readw(PO_PICB_18), 0);
let mut stream = stream_source.get_last_stream();
// Trigger callback and see that CIV has not changed, since only 1
// buffer has been sent.
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert_eq!(stream.num_channels(), num_channels);
assert_eq!(stream.frame_rate(), rate as u32);
let mut civ = bm.readb(PO_CIV_14);
assert_eq!(civ, 0);
// After two more callbacks, CIV should now be 1 since we know that the
// first buffer must have been played.
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
civ = bm.readb(PO_CIV_14);
assert_eq!(civ, 1);
// Buffer complete should be set as the IOC bit was set in the descriptor.
assert!(bm.readw(PO_SR_16, &mixer) & SR_BCIS != 0);
// Clear the BCIS bit
bm.writew(PO_SR_16, SR_BCIS);
assert!(bm.readw(PO_SR_16, &mixer) & SR_BCIS == 0);
std::thread::sleep(Duration::from_millis(50));
let picb = bm.readw(PO_PICB_18, &mixer);
let pos = (FRAGMENT_SIZE - (picb as usize * 2)) / 4;
// Check that frames are consumed at least at a reasonable rate.
// This can't be exact as during unit tests the thread scheduling is highly variable, so the
// test only checks that some samples are consumed.
assert!(pos > 0);
assert!(bm.readw(PO_SR_16, &mixer) & SR_DCH == 0); // DMA is running.
// Set last valid to next buffer to be sent and trigger callback so we hit it.
bm.writeb(PO_LVI_15, civ + 2, &mixer);
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(bm.readw(PO_SR_16, &mixer) & SR_LVBCI != 0); // Hit last buffer
assert!(bm.readw(PO_SR_16, &mixer) & SR_DCH == SR_DCH); // DMA stopped because of lack of buffers.
assert!(bm.readw(PO_SR_16, &mixer) & SR_CELV == SR_CELV); // Processed the last buffer
assert_eq!(bm.readb(PO_LVI_15), bm.readb(PO_CIV_14));
assert!(
bm.readl(GLOB_STA_30) & GS_POINT != 0,
"POINT bit should be set."
);
// Clear the LVB bit
bm.writeb(PO_SR_16, SR_LVBCI as u8, &mixer);
assert!(bm.readw(PO_SR_16, &mixer) & SR_LVBCI == 0);
// Reset the LVI to the last buffer and check that playback resumes
bm.writeb(PO_LVI_15, LVI_MASK, &mixer);
assert!(bm.readw(PO_SR_16, &mixer) & SR_DCH == 0); // DMA restarts.
assert_eq!(bm.readw(PO_SR_16, &mixer) & SR_CELV, 0);
let restart_civ = bm.readb(PO_CIV_14);
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(bm.readb(PO_CIV_14) != restart_civ);
// Stop.
bm.writeb(PO_CR_1B, 0, &mixer);
assert!(bm.readw(PO_SR_16, &mixer) & 0x01 != 0); // DMA is not running.
bm.writeb(PO_CR_1B, CR_RR, &mixer);
assert!(
bm.readl(GLOB_STA_30) & GS_POINT == 0,
"POINT bit should be disabled."
);
}
#[test]
fn run_capture() {
start_capture(Ac97Function::Input);
start_capture(Ac97Function::Microphone);
}
fn start_capture(func: Ac97Function) {
const TIMEOUT: Duration = Duration::from_millis(500);
const LVI_MASK: u8 = 0x1f; // Five bits for 32 total entries.
const IOC_MASK: u32 = 0x8000_0000; // Interrupt on completion.
let num_buffers = LVI_MASK as usize + 1;
const BUFFER_SIZE: usize = 32768;
const FRAGMENT_SIZE: usize = BUFFER_SIZE / 2;
const GUEST_ADDR_BASE: u32 = 0x100_0000;
let mem = GuestMemory::new(&[(GuestAddress(GUEST_ADDR_BASE as u64), 1024 * 1024 * 1024)])
.expect("Creating guest memory failed.");
let stream_source = MockShmStreamSource::new();
let mut bm = Ac97BusMaster::new(mem.clone(), Box::new(stream_source.clone()));
let mut mixer = Ac97Mixer::new();
let (bdbar_addr, lvi_addr, cr_addr, civ_addr, pcib_addr, sr_addr, int_mask) = match func {
Ac97Function::Input => (
PI_BDBAR_00,
PI_LVI_05,
PI_CR_0B,
PI_CIV_04,
PI_PICB_08,
PI_SR_06,
GS_PIINT,
),
Ac97Function::Microphone => (
MC_BDBAR_20,
MC_LVI_25,
MC_CR_2B,
MC_CIV_24,
MC_PICB_28,
MC_SR_26,
GS_MINT,
),
_ => {
assert!(false, "Invalid Ac97Function.");
(0, 0, 0, 0, 0, 0, 0)
}
};
// Release cold reset.
bm.writel(GLOB_CNT_2C, 0x0000_0002, &mut mixer);
// Setup ping-pong buffers.
bm.writel(bdbar_addr, GUEST_ADDR_BASE, &mut mixer);
for i in 0..num_buffers {
let pointer_addr = GuestAddress(GUEST_ADDR_BASE as u64 + i as u64 * 8);
let control_addr = GuestAddress(GUEST_ADDR_BASE as u64 + i as u64 * 8 + 4);
mem.write_obj_at_addr(GUEST_ADDR_BASE + FRAGMENT_SIZE as u32, pointer_addr)
.expect("Writing guest memory failed.");
mem.write_obj_at_addr(IOC_MASK | (FRAGMENT_SIZE as u32) / 2, control_addr)
.expect("Writing guest memory failed.");
}
bm.writeb(lvi_addr, LVI_MASK, &mixer);
// Start.
bm.writeb(cr_addr, CR_IOCE | CR_RPBM, &mixer);
// TODO(crbug.com/1086337): Test flakiness in build time.
// assert_eq!(bm.readw(PI_PICB_08), 0);
let mut stream = stream_source.get_last_stream();
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
// CIV is 1 here since we preemptively sent two buffer indices to the
// server before creating the stream. When we triggered the callback
// above, that means the first of those buffers was filled, so CIV
// increments to 1.
let civ = bm.readb(civ_addr);
assert_eq!(civ, 1);
std::thread::sleep(Duration::from_millis(20));
let picb = bm.readw(pcib_addr, &mixer);
assert!(picb > 0);
assert!(bm.readw(sr_addr, &mixer) & SR_DCH == 0); // DMA is running.
// Trigger 2 callbacks so that we'll move to buffer 3 since at that
// point we can be certain that buffers 1 and 2 have been captured to.
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert_eq!(bm.readb(civ_addr), 3);
let civ = bm.readb(civ_addr);
// Sets LVI to CIV + 2 to trigger last buffer hit
bm.writeb(lvi_addr, civ + 2, &mixer);
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert_ne!(bm.readw(sr_addr, &mixer) & SR_LVBCI, 0); // Hit last buffer
assert_eq!(bm.readw(sr_addr, &mixer) & SR_DCH, SR_DCH); // DMA stopped because of lack of buffers.
assert_eq!(bm.readw(sr_addr, &mixer) & SR_CELV, SR_CELV);
assert_eq!(bm.readb(lvi_addr), bm.readb(civ_addr));
assert!(
bm.readl(GLOB_STA_30) & int_mask != 0,
"int_mask bit should be set."
);
// Clear the LVB bit
bm.writeb(sr_addr, SR_LVBCI as u8, &mixer);
assert!(bm.readw(sr_addr, &mixer) & SR_LVBCI == 0);
// Reset the LVI to the last buffer and check that playback resumes
bm.writeb(lvi_addr, LVI_MASK, &mixer);
assert!(bm.readw(sr_addr, &mixer) & SR_DCH == 0); // DMA restarts.
assert_eq!(bm.readw(sr_addr, &mixer) & SR_CELV, 0);
let restart_civ = bm.readb(civ_addr);
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
assert_ne!(bm.readb(civ_addr), restart_civ);
// Stop.
bm.writeb(cr_addr, 0, &mixer);
assert!(bm.readw(sr_addr, &mixer) & 0x01 != 0); // DMA is not running.
bm.writeb(cr_addr, CR_RR, &mixer);
assert!(
bm.readl(GLOB_STA_30) & int_mask == 0,
"int_mask bit should be disabled."
);
}
}