1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use audio_streams::shm_streams::NullShmStreamSource;
use audio_streams::shm_streams::ShmStreamSource;
#[cfg(feature = "audio_cras")]
use base::error;
#[cfg(feature = "audio_cras")]
use libcras::CrasClient;
#[cfg(feature = "audio_cras")]
use libcras::CrasClientType;
#[cfg(feature = "audio_cras")]
use libcras::CrasSocketType;
use serde::Deserialize;
use serde::Serialize;
use vm_memory::GuestMemory;

use crate::pci::ac97::Ac97Dev;
use crate::pci::ac97::Ac97Error;
use crate::pci::ac97::Ac97Parameters;
#[cfg(feature = "audio_cras")]
use crate::pci::pci_device;
use crate::pci::pci_device::Result;

pub(crate) type AudioStreamSource = Box<dyn ShmStreamSource<base::Error>>;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Ac97Backend {
    #[cfg(feature = "audio_cras")]
    Cras,
}

impl Ac97Dev {
    pub(in crate::pci::ac97) fn initialize_backend(
        ac97_backend: &Ac97Backend,
        #[allow(unused_variables)] mem: GuestMemory,
        #[allow(unused_variables)] param: &Ac97Parameters,
    ) -> Result<Self> {
        match *ac97_backend {
            #[cfg(feature = "audio_cras")]
            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
                );
                Ok(Self::create_null_audio_device(mem))
            }),
        }
    }

    #[cfg(feature = "audio_cras")]
    fn create_cras_audio_device(params: &Ac97Parameters, mem: GuestMemory) -> Result<Self> {
        let mut server = Box::new(
            CrasClient::with_type(params.socket_type.unwrap_or(CrasSocketType::Unified))
                .map_err(pci_device::Error::CreateCrasClientFailed)?,
        );
        server.set_client_type(
            params
                .client_type
                .unwrap_or(CrasClientType::CRAS_CLIENT_TYPE_CROSVM),
        );
        if params.capture {
            server.enable_cras_capture();
        }

        let cras_audio = Self::new(
            mem,
            crate::pci::ac97::Ac97Backend::System(Ac97Backend::Cras),
            server,
        );
        Ok(cras_audio)
    }

    /// Return the minijail policy file path for the current Ac97Dev.
    pub fn minijail_policy(&self) -> &'static str {
        match &self.backend {
            crate::pci::ac97::Ac97Backend::System(backend) => match *backend {
                #[cfg(feature = "audio_cras")]
                Ac97Backend::Cras => "cras_audio_device",
            },
            crate::pci::ac97::Ac97Backend::NULL => "null_audio_device",
        }
    }
}

pub(in crate::pci::ac97) fn ac97_backend_from_str(
    s: &str,
) -> std::result::Result<crate::pci::ac97::Ac97Backend, Ac97Error> {
    match s {
        #[cfg(feature = "audio_cras")]
        "cras" => Ok(crate::pci::ac97::Ac97Backend::System(Ac97Backend::Cras)),
        _ => Err(Ac97Error::InvalidBackend),
    }
}

pub(in crate::pci::ac97) fn create_null_server() -> AudioStreamSource {
    Box::new(NullShmStreamSource::new())
}

#[cfg(test)]
pub(in crate::pci::ac97) mod tests {
    use audio_streams::shm_streams::MockShmStreamSource;

    use super::*;

    pub(in crate::pci::ac97) fn create_ac97_device(
        mem: GuestMemory,
        backend: crate::pci::ac97::Ac97Backend,
    ) -> Ac97Dev {
        Ac97Dev::new(mem, backend, Box::new(MockShmStreamSource::new()))
    }
}