blob: 35ba39693abc69e0c27556d14926bfebee54d8a9 [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::default::Default;
use std::error;
use std::fmt::{self, Display};
use std::os::unix::io::RawFd;
use std::str::FromStr;
use audio_streams::shm_streams::{NullShmStreamSource, ShmStreamSource};
use base::{error, Event};
use libcras::{CrasClient, CrasClientType, CrasSocketType};
use resources::{Alloc, MmioType, SystemAllocator};
use vm_memory::GuestMemory;
use crate::pci::ac97_bus_master::Ac97BusMaster;
use crate::pci::ac97_mixer::Ac97Mixer;
use crate::pci::ac97_regs::*;
use crate::pci::pci_configuration::{
PciBarConfiguration, PciClassCode, PciConfiguration, PciHeaderType, PciMultimediaSubclass,
};
use crate::pci::pci_device::{self, PciDevice, Result};
use crate::pci::{PciAddress, PciInterruptPin};
// 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 smaples between guest memory and
/// the audio backend.
#[derive(Debug, Clone)]
pub enum Ac97Backend {
NULL,
CRAS,
}
impl Default for Ac97Backend {
fn default() -> Self {
Ac97Backend::NULL
}
}
/// Errors that are possible from a `Ac97`.
#[derive(Debug)]
pub enum Ac97Error {
InvalidBackend,
}
impl error::Error for Ac97Error {}
impl Display for Ac97Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Ac97Error::InvalidBackend => write!(f, "Must be cras or null"),
}
}
}
impl FromStr for Ac97Backend {
type Err = Ac97Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"cras" => Ok(Ac97Backend::CRAS),
"null" => Ok(Ac97Backend::NULL),
_ => Err(Ac97Error::InvalidBackend),
}
}
}
/// Holds the parameters for a AC97 device
#[derive(Default, Debug, Clone)]
pub struct Ac97Parameters {
pub backend: Ac97Backend,
pub capture: bool,
}
pub struct Ac97Dev {
config_regs: PciConfiguration,
pci_address: Option<PciAddress>,
// The irq events are temporarily saved here. They need to be passed to the device after the
// jail forks. This happens when the bus is first written.
irq_evt: Option<Event>,
irq_resample_evt: Option<Event>,
bus_master: Ac97BusMaster,
mixer: Ac97Mixer,
backend: Ac97Backend,
}
impl Ac97Dev {
/// Creates an 'Ac97Dev' that uses the given `GuestMemory` and starts with all registers at
/// default values.
pub fn new(
mem: GuestMemory,
backend: Ac97Backend,
audio_server: Box<dyn ShmStreamSource>,
) -> Self {
let config_regs = PciConfiguration::new(
0x8086,
PCI_DEVICE_ID_INTEL_82801AA_5,
PciClassCode::MultimediaController,
&PciMultimediaSubclass::AudioDevice,
None, // No Programming interface.
PciHeaderType::Device,
0x8086, // Subsystem Vendor ID
0x1, // Subsystem ID.
);
Self {
config_regs,
pci_address: None,
irq_evt: None,
irq_resample_evt: None,
bus_master: Ac97BusMaster::new(mem, audio_server),
mixer: Ac97Mixer::new(),
backend,
}
}
/// 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(mem: GuestMemory, param: Ac97Parameters) -> Result<Self> {
match param.backend {
Ac97Backend::CRAS => Self::create_cras_audio_device(param, mem.clone()).or_else(|e| {
error!(
"Ac97Dev: create_cras_audio_device: {}. Fallback to null audio device",
e
);
Self::create_null_audio_device(mem)
}),
Ac97Backend::NULL => Self::create_null_audio_device(mem),
}
}
/// Return the minijail policy file path for the current Ac97Dev.
pub fn minijail_policy(&self) -> &'static str {
match self.backend {
Ac97Backend::CRAS => "cras_audio_device",
Ac97Backend::NULL => "null_audio_device",
}
}
fn create_cras_audio_device(params: Ac97Parameters, mem: GuestMemory) -> Result<Self> {
let mut server = Box::new(
CrasClient::with_type(CrasSocketType::Unified)
.map_err(|e| pci_device::Error::CreateCrasClientFailed(e))?,
);
server.set_client_type(CrasClientType::CRAS_CLIENT_TYPE_CROSVM);
if params.capture {
server.enable_cras_capture();
}
let cras_audio = Self::new(mem, Ac97Backend::CRAS, server);
Ok(cras_audio)
}
fn create_null_audio_device(mem: GuestMemory) -> Result<Self> {
let server = Box::new(NullShmStreamSource::new());
let null_audio = Self::new(mem, Ac97Backend::NULL, server);
Ok(null_audio)
}
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 => error!("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 => error!("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 => error!("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 => error!("write length of {}", l),
}
}
}
impl PciDevice for Ac97Dev {
fn debug_label(&self) -> String {
"AC97".to_owned()
}
fn assign_address(&mut self, address: PciAddress) {
self.pci_address = Some(address);
}
fn assign_irq(
&mut self,
irq_evt: Event,
irq_resample_evt: Event,
irq_num: u32,
irq_pin: PciInterruptPin,
) {
self.config_regs.set_irq(irq_num as u8, irq_pin);
self.irq_evt = Some(irq_evt);
self.irq_resample_evt = Some(irq_resample_evt);
}
fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
let address = self
.pci_address
.expect("assign_address must be called prior to allocate_io_bars");
let mut ranges = Vec::new();
let mixer_regs_addr = resources
.mmio_allocator(MmioType::Low)
.allocate_with_align(
MIXER_REGS_SIZE,
Alloc::PciBar {
bus: address.bus,
dev: address.dev,
func: address.func,
bar: 0,
},
"ac97-mixer_regs".to_string(),
MIXER_REGS_SIZE,
)
.map_err(|e| pci_device::Error::IoAllocationFailed(MIXER_REGS_SIZE, e))?;
let mixer_config = PciBarConfiguration::default()
.set_register_index(0)
.set_address(mixer_regs_addr)
.set_size(MIXER_REGS_SIZE);
self.config_regs
.add_pci_bar(mixer_config)
.map_err(|e| pci_device::Error::IoRegistrationFailed(mixer_regs_addr, e))?;
ranges.push((mixer_regs_addr, MIXER_REGS_SIZE));
let master_regs_addr = resources
.mmio_allocator(MmioType::Low)
.allocate_with_align(
MASTER_REGS_SIZE,
Alloc::PciBar {
bus: address.bus,
dev: address.dev,
func: address.func,
bar: 1,
},
"ac97-master_regs".to_string(),
MASTER_REGS_SIZE,
)
.map_err(|e| pci_device::Error::IoAllocationFailed(MASTER_REGS_SIZE, e))?;
let master_config = PciBarConfiguration::default()
.set_register_index(1)
.set_address(master_regs_addr)
.set_size(MASTER_REGS_SIZE);
self.config_regs
.add_pci_bar(master_config)
.map_err(|e| pci_device::Error::IoRegistrationFailed(master_regs_addr, e))?;
ranges.push((master_regs_addr, MASTER_REGS_SIZE));
Ok(ranges)
}
fn read_config_register(&self, reg_idx: usize) -> u32 {
self.config_regs.read_reg(reg_idx)
}
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
(&mut self.config_regs).write_reg(reg_idx, offset, data)
}
fn keep_fds(&self) -> Vec<RawFd> {
if let Some(server_fds) = self.bus_master.keep_fds() {
server_fds
} else {
Vec::new()
}
}
fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
let bar0 = self.config_regs.get_bar_addr(0);
let bar1 = self.config_regs.get_bar_addr(1);
match addr {
a if a >= bar0 && a < bar0 + MIXER_REGS_SIZE => self.read_mixer(addr - bar0, data),
a if a >= bar1 && a < bar1 + MASTER_REGS_SIZE => {
self.read_bus_master(addr - bar1, data)
}
_ => (),
}
}
fn write_bar(&mut self, addr: u64, data: &[u8]) {
let bar0 = self.config_regs.get_bar_addr(0);
let bar1 = self.config_regs.get_bar_addr(1);
match addr {
a if a >= bar0 && a < bar0 + MIXER_REGS_SIZE => self.write_mixer(addr - bar0, data),
a if a >= bar1 && a < bar1 + MASTER_REGS_SIZE => {
// Check if the irq needs to be passed to the device.
if let (Some(irq_evt), Some(irq_resample_evt)) =
(self.irq_evt.take(), self.irq_resample_evt.take())
{
self.bus_master.set_irq_event(irq_evt, irq_resample_evt);
}
self.write_bus_master(addr - bar1, data)
}
_ => (),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use audio_streams::shm_streams::MockShmStreamSource;
use vm_memory::GuestAddress;
#[test]
fn create() {
let mem = GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)]).unwrap();
let mut ac97_dev =
Ac97Dev::new(mem, Ac97Backend::NULL, Box::new(MockShmStreamSource::new()));
let mut allocator = SystemAllocator::builder()
.add_io_addresses(0x1000_0000, 0x1000_0000)
.add_low_mmio_addresses(0x2000_0000, 0x1000_0000)
.add_high_mmio_addresses(0x3000_0000, 0x1000_0000)
.create_allocator(5, false)
.unwrap();
ac97_dev.assign_address(PciAddress {
bus: 0,
dev: 0,
func: 0,
});
assert!(ac97_dev.allocate_io_bars(&mut allocator).is_ok());
}
}