diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index 7e2ec4f71..f407b81c2 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -37,6 +37,5 @@ runs: libvirglrenderer-dev \ libepoxy-dev \ libdrm-dev \ - libpipewire-0.3-dev \ clang-format \ libclang-dev diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index f1abf9088..9ea7773d1 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -23,8 +23,8 @@ jobs: - name: Clippy (tdx) run: cargo clippy --locked --features tdx -- -D warnings - - name: Clippy (net+blk+gpu+snd+input) - run: cargo clippy --locked --features net,blk,gpu,snd,input -- -D warnings + - name: Clippy (net+blk+gpu+input) + run: cargo clippy --locked --features net,blk,gpu,input -- -D warnings code-quality-linux-aarch64: name: libkrun (Linux aarch64) @@ -41,8 +41,8 @@ jobs: - name: Clippy (default) run: cargo clippy --locked -- -D warnings - - name: Clippy (net+blk+gpu+snd+input) - run: cargo clippy --locked --features net,blk,gpu,snd,input -- -D warnings + - name: Clippy (net+blk+gpu+input) + run: cargo clippy --locked --features net,blk,gpu,input -- -D warnings code-quality-macos: name: libkrun (macOS aarch64) diff --git a/Cargo.lock b/Cargo.lock index c066dd4eb..e829f9189 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,16 +23,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "annotate-snippets" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" -dependencies = [ - "anstyle", - "unicode-width", -] - [[package]] name = "anstream" version = "0.6.21" @@ -137,7 +127,6 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "annotate-snippets", "bitflags 2.11.0", "cexpr", "clang-sys", @@ -243,17 +232,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.3", -] - -[[package]] -name = "cfg-expr" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" -dependencies = [ - "smallvec", - "target-lexicon", + "nom", ] [[package]] @@ -296,21 +275,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" -[[package]] -name = "convert_case" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cookie-factory" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -713,7 +677,6 @@ dependencies = [ "log", "lru", "nix 0.30.1", - "pipewire", "rand 0.9.2", "thiserror 2.0.18", "vhost", @@ -926,34 +889,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "libspa" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b8cfa2a7656627b4c92c6b9ef929433acd673d5ab3708cda1b18478ac00df4" -dependencies = [ - "bitflags 2.11.0", - "cc", - "convert_case", - "cookie-factory", - "libc", - "libspa-sys", - "nix 0.30.1", - "nom 8.0.0", - "system-deps", -] - -[[package]] -name = "libspa-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901049455d2eb6decf9058235d745237952f4804bc584c5fcb41412e6adcc6e0" -dependencies = [ - "bindgen", - "cc", - "system-deps", -] - [[package]] name = "linux-loader" version = "0.13.2" @@ -1098,15 +1033,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.4" @@ -1141,34 +1067,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pipewire" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9688b89abf11d756499f7c6190711d6dbe5a3acdb30c8fbf001d6596d06a8d44" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "libc", - "libspa", - "libspa-sys", - "nix 0.30.1", - "once_cell", - "pipewire-sys", - "thiserror 2.0.18", -] - -[[package]] -name = "pipewire-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb028afee0d6ca17020b090e3b8fa2d7de23305aef975c7e5192a5050246ea36" -dependencies = [ - "bindgen", - "libspa-sys", - "system-deps", -] - [[package]] name = "pkg-config" version = "0.3.32" @@ -1423,15 +1321,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "serde_spanned" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" -dependencies = [ - "serde_core", -] - [[package]] name = "sha2" version = "0.10.9" @@ -1484,12 +1373,6 @@ dependencies = [ "digest", ] -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - [[package]] name = "static_assertions" version = "1.1.0" @@ -1528,19 +1411,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "system-deps" -version = "7.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" -dependencies = [ - "cfg-expr", - "heck", - "pkg-config", - "toml", - "version-compare", -] - [[package]] name = "tar" version = "0.4.45" @@ -1552,12 +1422,6 @@ dependencies = [ "xattr", ] -[[package]] -name = "target-lexicon" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" - [[package]] name = "tdx" version = "0.1.0" @@ -1622,45 +1486,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "toml" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime", - "toml_parser", - "toml_writer", - "winnow", -] - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow", -] - -[[package]] -name = "toml_writer" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" - [[package]] name = "tracing" version = "0.1.44" @@ -1704,18 +1529,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -1746,12 +1559,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "version-compare" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" - [[package]] name = "version_check" version = "0.9.5" @@ -1974,12 +1781,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "winnow" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" - [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/Makefile b/Makefile index 17ec2f2d3..1c16cb199 100644 --- a/Makefile +++ b/Makefile @@ -42,9 +42,6 @@ endif ifeq ($(GPU),1) FEATURE_FLAGS += --features gpu endif -ifeq ($(SND),1) - FEATURE_FLAGS += --features snd -endif ifeq ($(INPUT),1) FEATURE_FLAGS += --features input endif diff --git a/README.md b/README.md index 2ff03a25d..0fa1029a3 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Each variant generates a dynamic library with a different name (and ```soname``` * virtio-vsock (for TSI and socket redirection) * virtio-balloon (only free-page reporting) * virtio-rng -* virtio-snd + ## Networking @@ -119,7 +119,7 @@ When TSI is enabled, the VMM acts as a proxy for AF_INET, AF_INET6 and AF_UNIX s * **VIRGL_RESOURCE_MAP2=1**: Uses virgl_resource_map2 function. Requires a virglrenderer-devel patched with [1374](https://gitlab.freedesktop.org/virgl/virglrenderer/-/merge_requests/1374) * **BLK=1**: Enables virtio-block. * **NET=1**: Enables virtio-net. -* **SND=1**: Enables virtio-snd. + #### Compiling diff --git a/include/libkrun.h b/include/libkrun.h index 87d5e1fa1..3004110f6 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -735,18 +735,6 @@ int krun_add_input_device(uint32_t ctx_id, const void *config_backend, size_t co */ int krun_add_input_device_fd(uint32_t ctx_id, int input_fd); -/** - * Enables or disables a virtio-snd device. - * - * Arguments: - * "ctx_id" - the configuration context ID. - * "enable" - boolean indicating whether virtio-snd should be enabled or disabled. - * - * Returns: - * Zero on success or a negative error number on failure. - */ -int32_t krun_set_snd_device(uint32_t ctx_id, bool enable); - /** * Vhost-user device types. * These correspond to virtio device type IDs for devices. @@ -1122,7 +1110,6 @@ int32_t krun_check_nested_virt(void); #define KRUN_FEATURE_NET 0 #define KRUN_FEATURE_BLK 1 #define KRUN_FEATURE_GPU 2 -#define KRUN_FEATURE_SND 3 #define KRUN_FEATURE_INPUT 4 #define KRUN_FEATURE_TEE 6 #define KRUN_FEATURE_AMD_SEV 7 diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index ab6ecfe2a..eacb6cc97 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -15,7 +15,6 @@ tdx = ["blk", "tee"] net = [] blk = [] gpu = ["rutabaga_gfx", "thiserror", "zerocopy", "krun_display"] -snd = ["pw", "thiserror"] input = ["zerocopy", "krun_input"] virgl_resource_map2 = [] aws-nitro = [] @@ -29,7 +28,6 @@ libc = ">=0.2.39" libloading = "0.8" log = "0.4.0" nix = { version = "0.30.1", features = ["ioctl", "net", "poll", "socket", "fs"] } -pw = { package = "pipewire", version = "0.9.2", optional = true } rand = "0.9.2" thiserror = { version = "2.0", optional = true } vhost = { version = "0.15", optional = true, features = ["vhost-user-frontend"] } diff --git a/src/devices/src/virtio/mod.rs b/src/devices/src/virtio/mod.rs index 64f19c35b..ce3d0a022 100644 --- a/src/devices/src/virtio/mod.rs +++ b/src/devices/src/virtio/mod.rs @@ -34,8 +34,6 @@ pub mod net; mod queue; #[cfg(not(feature = "tee"))] pub mod rng; -#[cfg(feature = "snd")] -pub mod snd; #[cfg(feature = "vhost-user")] pub mod vhost_user; pub mod vsock; @@ -56,8 +54,6 @@ pub use self::net::Net; pub use self::queue::{Descriptor, DescriptorChain, Queue}; #[cfg(not(feature = "tee"))] pub use self::rng::*; -#[cfg(feature = "snd")] -pub use self::snd::Snd; #[cfg(feature = "vhost-user")] pub use self::vhost_user::VhostUserDevice; pub use self::vsock::*; diff --git a/src/devices/src/virtio/snd/audio_backends.rs b/src/devices/src/virtio/snd/audio_backends.rs deleted file mode 100644 index f35cbd72f..000000000 --- a/src/devices/src/virtio/snd/audio_backends.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Manos Pitsidianakis -// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - -mod pipewire; - -use std::sync::{Arc, RwLock}; - -use self::pipewire::PwBackend; -use super::{stream::Stream, BackendType, Result, VirtioSndPcmSetParams}; - -pub trait AudioBackend { - fn write(&self, stream_id: u32) -> Result<()>; - - #[allow(dead_code)] - fn read(&self, stream_id: u32) -> Result<()>; - - fn set_parameters(&self, _stream_id: u32, _: VirtioSndPcmSetParams) -> Result<()> { - Ok(()) - } - - fn prepare(&self, _stream_id: u32) -> Result<()> { - Ok(()) - } - - fn release(&self, _stream_id: u32) -> Result<()> { - Ok(()) - } - - fn start(&self, _stream_id: u32) -> Result<()> { - Ok(()) - } - - fn stop(&self, _stream_id: u32) -> Result<()> { - Ok(()) - } - - #[cfg(test)] - fn as_any(&self) -> &dyn std::any::Any; -} - -pub fn alloc_audio_backend( - backend: BackendType, - streams: Arc>>, -) -> Result> { - log::trace!("allocating audio backend {backend:?}"); - match backend { - BackendType::Pipewire => Ok(Box::new(PwBackend::new(streams))), - } -} - -#[cfg(test)] -mod tests { - use std::any::TypeId; - - use super::*; - - #[test] - fn test_alloc_audio_backend() { - crate::init_logger(); - { - let v = BackendType::Null; - let value = alloc_audio_backend(v, Default::default()).unwrap(); - assert_eq!(TypeId::of::(), value.as_any().type_id()); - } - #[cfg(all(feature = "pw-backend", target_env = "gnu"))] - { - use pipewire::{test_utils::PipewireTestHarness, *}; - - let _test_harness = PipewireTestHarness::new(); - let v = BackendType::Pipewire; - let value = alloc_audio_backend(v, Default::default()).unwrap(); - assert_eq!(TypeId::of::(), value.as_any().type_id()); - } - #[cfg(all(feature = "alsa-backend", target_env = "gnu"))] - { - let v = BackendType::Alsa; - let value = alloc_audio_backend(v, Default::default()).unwrap(); - assert_eq!(TypeId::of::(), value.as_any().type_id()); - } - } -} diff --git a/src/devices/src/virtio/snd/audio_backends/pipewire.rs b/src/devices/src/virtio/snd/audio_backends/pipewire.rs deleted file mode 100644 index 89415b2fb..000000000 --- a/src/devices/src/virtio/snd/audio_backends/pipewire.rs +++ /dev/null @@ -1,650 +0,0 @@ -// Pipewire backend device -// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - -use std::{ - collections::HashMap, - convert::TryFrom, - mem::size_of, - ptr, - sync::{Arc, RwLock}, -}; - -use log::debug; -use pw::{ - context::ContextRc, core::CoreRc, properties::properties, spa, sys::PW_ID_CORE, - thread_loop::ThreadLoopRc, -}; -use spa::{ - param::{ - audio::{AudioFormat, AudioInfoRaw}, - ParamType, - }, - pod::{serialize::PodSerializer, Object, Pod, Value}, - sys::{ - spa_audio_info_raw, SPA_PARAM_EnumFormat, SPA_TYPE_OBJECT_Format, SPA_AUDIO_CHANNEL_FC, - SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_MONO, - SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, - SPA_AUDIO_CHANNEL_UNKNOWN, SPA_AUDIO_FORMAT_ALAW, SPA_AUDIO_FORMAT_F32, - SPA_AUDIO_FORMAT_F64, SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_S18_LE, SPA_AUDIO_FORMAT_S20, - SPA_AUDIO_FORMAT_S20_LE, SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_S24_LE, - SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S8, SPA_AUDIO_FORMAT_U16, SPA_AUDIO_FORMAT_U18_LE, - SPA_AUDIO_FORMAT_U20, SPA_AUDIO_FORMAT_U20_LE, SPA_AUDIO_FORMAT_U24, - SPA_AUDIO_FORMAT_U24_LE, SPA_AUDIO_FORMAT_U32, SPA_AUDIO_FORMAT_U8, SPA_AUDIO_FORMAT_ULAW, - SPA_AUDIO_FORMAT_UNKNOWN, - }, -}; - -use super::super::{ - stream::{Error as StreamError, PCMState}, - virtio_sound::{ - VirtioSndPcmSetParams, VIRTIO_SND_PCM_FMT_A_LAW, VIRTIO_SND_PCM_FMT_FLOAT, - VIRTIO_SND_PCM_FMT_FLOAT64, VIRTIO_SND_PCM_FMT_MU_LAW, VIRTIO_SND_PCM_FMT_S16, - VIRTIO_SND_PCM_FMT_S18_3, VIRTIO_SND_PCM_FMT_S20, VIRTIO_SND_PCM_FMT_S20_3, - VIRTIO_SND_PCM_FMT_S24, VIRTIO_SND_PCM_FMT_S24_3, VIRTIO_SND_PCM_FMT_S32, - VIRTIO_SND_PCM_FMT_S8, VIRTIO_SND_PCM_FMT_U16, VIRTIO_SND_PCM_FMT_U18_3, - VIRTIO_SND_PCM_FMT_U20, VIRTIO_SND_PCM_FMT_U20_3, VIRTIO_SND_PCM_FMT_U24, - VIRTIO_SND_PCM_FMT_U24_3, VIRTIO_SND_PCM_FMT_U32, VIRTIO_SND_PCM_FMT_U8, - VIRTIO_SND_PCM_RATE_11025, VIRTIO_SND_PCM_RATE_16000, VIRTIO_SND_PCM_RATE_176400, - VIRTIO_SND_PCM_RATE_192000, VIRTIO_SND_PCM_RATE_22050, VIRTIO_SND_PCM_RATE_32000, - VIRTIO_SND_PCM_RATE_384000, VIRTIO_SND_PCM_RATE_44100, VIRTIO_SND_PCM_RATE_48000, - VIRTIO_SND_PCM_RATE_5512, VIRTIO_SND_PCM_RATE_64000, VIRTIO_SND_PCM_RATE_8000, - VIRTIO_SND_PCM_RATE_88200, VIRTIO_SND_PCM_RATE_96000, - }, - Direction, Error, Result, Stream, -}; -use super::AudioBackend; - -impl From for spa::utils::Direction { - fn from(val: Direction) -> Self { - match val { - Direction::Output => Self::Output, - Direction::Input => Self::Input, - } - } -} - -// SAFETY: Safe as the structure can be sent to another thread. -unsafe impl Send for PwBackend {} - -// SAFETY: Safe as the structure can be shared with another thread as the state -// is protected with a lock. -unsafe impl Sync for PwBackend {} - -// FIXME: make PwBackend impl Send on all fields. -#[allow(clippy::non_send_fields_in_send_ty)] -pub struct PwBackend { - pub stream_params: Arc>>, - thread_loop: ThreadLoopRc, - pub core: CoreRc, - #[allow(dead_code)] - context: ContextRc, - pub stream_hash: RwLock>, - pub stream_listener: RwLock>>, -} - -impl PwBackend { - pub fn new(stream_params: Arc>>) -> Self { - // SAFETY: safe as the thread loop cannot access objects associated - // with the loop while the lock is held - let thread_loop = unsafe { ThreadLoopRc::new(Some("Pipewire thread loop"), None).unwrap() }; - - let lock_guard = thread_loop.lock(); - - let context = ContextRc::new(&thread_loop, None).expect("failed to create context"); - thread_loop.start(); - let core = context.connect_rc(None).expect("Failed to connect to core"); - - // Create new reference for the variable so that it can be moved into the - // closure. - let thread_clone = thread_loop.clone(); - - // Trigger the sync event. The server's answer won't be processed until we start - // the thread loop, so we can safely do this before setting up a - // callback. This lets us avoid using a Cell. - let pending = core.sync(0).expect("sync failed"); - let _listener_core = core - .add_listener_local() - .done(move |id, seq| { - if id == PW_ID_CORE && seq == pending { - thread_clone.signal(false); - } - }) - .register(); - - thread_loop.wait(); - lock_guard.unlock(); - - log::trace!("pipewire backend running"); - - Self { - stream_params, - thread_loop, - core, - context, - stream_hash: RwLock::new(HashMap::new()), - stream_listener: RwLock::new(HashMap::new()), - } - } -} - -impl Drop for PwBackend { - fn drop(&mut self) { - self.thread_loop.stop(); - } -} - -impl AudioBackend for PwBackend { - fn write(&self, stream_id: u32) -> Result<()> { - if !matches!( - self.stream_params.read().unwrap()[stream_id as usize].state, - PCMState::Start | PCMState::Prepare - ) { - return Err(Error::Stream(StreamError::InvalidState( - "write", - self.stream_params.read().unwrap()[stream_id as usize].state, - ))); - } - Ok(()) - } - - fn read(&self, stream_id: u32) -> Result<()> { - log::trace!("PipewireBackend read stream_id {stream_id}"); - if !matches!( - self.stream_params.read().unwrap()[stream_id as usize].state, - PCMState::Start | PCMState::Prepare - ) { - return Err(Error::Stream(StreamError::InvalidState( - "read", - self.stream_params.read().unwrap()[stream_id as usize].state, - ))); - } - Ok(()) - } - - fn set_parameters(&self, stream_id: u32, request: VirtioSndPcmSetParams) -> Result<()> { - let stream_clone = self.stream_params.clone(); - let mut stream_params = stream_clone.write().unwrap(); - if let Some(st) = stream_params.get_mut(stream_id as usize) { - if let Err(err) = st.state.set_parameters() { - log::error!("Stream {stream_id} set_parameters {err}"); - return Err(Error::Stream(err)); - } else if !st.supports_format(request.format) || !st.supports_rate(request.rate) { - return Err(Error::UnexpectedAudioBackendConfiguration); - } else { - st.params.features = request.features; - st.params.buffer_bytes = request.buffer_bytes; - st.params.period_bytes = request.period_bytes; - st.params.channels = request.channels; - st.params.format = request.format; - st.params.rate = request.rate; - } - } else { - return Err(Error::StreamWithIdNotFound(stream_id)); - } - - Ok(()) - } - - fn prepare(&self, stream_id: u32) -> Result<()> { - debug!("pipewire prepare"); - let prepare_result = self - .stream_params - .write() - .unwrap() - .get_mut(stream_id as usize) - .ok_or(Error::StreamWithIdNotFound(stream_id))? - .state - .prepare(); - if let Err(err) = prepare_result { - log::error!("Stream {stream_id} prepare {err}"); - return Err(Error::Stream(err)); - } else { - let mut stream_hash = self.stream_hash.write().unwrap(); - let mut stream_listener = self.stream_listener.write().unwrap(); - let lock_guard = self.thread_loop.lock(); - let stream_params = self.stream_params.read().unwrap(); - - let params = &stream_params[stream_id as usize].params; - - if let Some(stream) = stream_hash.remove(&stream_id) { - stream_listener.remove(&stream_id); - if let Err(err) = stream.disconnect() { - log::error!("Stream {stream_id} disconnect {err}"); - return Err(Error::Stream(StreamError::CouldNotDisconnectStream)); - } - } - - let mut pos: [u32; 64] = [SPA_AUDIO_CHANNEL_UNKNOWN; 64]; - - match params.channels { - 6 => { - pos[0] = SPA_AUDIO_CHANNEL_FL; - pos[1] = SPA_AUDIO_CHANNEL_FR; - pos[2] = SPA_AUDIO_CHANNEL_FC; - pos[3] = SPA_AUDIO_CHANNEL_LFE; - pos[4] = SPA_AUDIO_CHANNEL_RL; - pos[5] = SPA_AUDIO_CHANNEL_RR; - } - 5 => { - pos[0] = SPA_AUDIO_CHANNEL_FL; - pos[1] = SPA_AUDIO_CHANNEL_FR; - pos[2] = SPA_AUDIO_CHANNEL_FC; - pos[3] = SPA_AUDIO_CHANNEL_LFE; - pos[4] = SPA_AUDIO_CHANNEL_RC; - } - 4 => { - pos[0] = SPA_AUDIO_CHANNEL_FL; - pos[1] = SPA_AUDIO_CHANNEL_FR; - pos[2] = SPA_AUDIO_CHANNEL_FC; - pos[3] = SPA_AUDIO_CHANNEL_RC; - } - 3 => { - pos[0] = SPA_AUDIO_CHANNEL_FL; - pos[1] = SPA_AUDIO_CHANNEL_FR; - pos[2] = SPA_AUDIO_CHANNEL_LFE; - } - 2 => { - pos[0] = SPA_AUDIO_CHANNEL_FL; - pos[1] = SPA_AUDIO_CHANNEL_FR; - } - 1 => { - pos[0] = SPA_AUDIO_CHANNEL_MONO; - } - _ => { - return Err(Error::ChannelNotSupported(params.channels)); - } - } - - let info = spa_audio_info_raw { - format: match params.format { - VIRTIO_SND_PCM_FMT_MU_LAW => SPA_AUDIO_FORMAT_ULAW, - VIRTIO_SND_PCM_FMT_A_LAW => SPA_AUDIO_FORMAT_ALAW, - VIRTIO_SND_PCM_FMT_S8 => SPA_AUDIO_FORMAT_S8, - VIRTIO_SND_PCM_FMT_U8 => SPA_AUDIO_FORMAT_U8, - VIRTIO_SND_PCM_FMT_S16 => SPA_AUDIO_FORMAT_S16, - VIRTIO_SND_PCM_FMT_U16 => SPA_AUDIO_FORMAT_U16, - VIRTIO_SND_PCM_FMT_S18_3 => SPA_AUDIO_FORMAT_S18_LE, - VIRTIO_SND_PCM_FMT_U18_3 => SPA_AUDIO_FORMAT_U18_LE, - VIRTIO_SND_PCM_FMT_S20_3 => SPA_AUDIO_FORMAT_S20_LE, - VIRTIO_SND_PCM_FMT_U20_3 => SPA_AUDIO_FORMAT_U20_LE, - VIRTIO_SND_PCM_FMT_S24_3 => SPA_AUDIO_FORMAT_S24_LE, - VIRTIO_SND_PCM_FMT_U24_3 => SPA_AUDIO_FORMAT_U24_LE, - VIRTIO_SND_PCM_FMT_S20 => SPA_AUDIO_FORMAT_S20, - VIRTIO_SND_PCM_FMT_U20 => SPA_AUDIO_FORMAT_U20, - VIRTIO_SND_PCM_FMT_S24 => SPA_AUDIO_FORMAT_S24, - VIRTIO_SND_PCM_FMT_U24 => SPA_AUDIO_FORMAT_U24, - VIRTIO_SND_PCM_FMT_S32 => SPA_AUDIO_FORMAT_S32, - VIRTIO_SND_PCM_FMT_U32 => SPA_AUDIO_FORMAT_U32, - VIRTIO_SND_PCM_FMT_FLOAT => SPA_AUDIO_FORMAT_F32, - VIRTIO_SND_PCM_FMT_FLOAT64 => SPA_AUDIO_FORMAT_F64, - _ => SPA_AUDIO_FORMAT_UNKNOWN, - }, - rate: match params.rate { - VIRTIO_SND_PCM_RATE_5512 => 5512, - VIRTIO_SND_PCM_RATE_8000 => 8000, - VIRTIO_SND_PCM_RATE_11025 => 11025, - VIRTIO_SND_PCM_RATE_16000 => 16000, - VIRTIO_SND_PCM_RATE_22050 => 22050, - VIRTIO_SND_PCM_RATE_32000 => 32000, - VIRTIO_SND_PCM_RATE_44100 => 44100, - VIRTIO_SND_PCM_RATE_48000 => 48000, - VIRTIO_SND_PCM_RATE_64000 => 64000, - VIRTIO_SND_PCM_RATE_88200 => 88200, - VIRTIO_SND_PCM_RATE_96000 => 96000, - VIRTIO_SND_PCM_RATE_176400 => 176400, - VIRTIO_SND_PCM_RATE_192000 => 192000, - VIRTIO_SND_PCM_RATE_384000 => 384000, - _ => 44100, - }, - flags: 0, - channels: u32::from(params.channels), - position: pos, - }; - - let mut audio_info = AudioInfoRaw::new(); - audio_info.set_format(AudioFormat::S16LE); - audio_info.set_rate(info.rate); - audio_info.set_channels(info.channels); - audio_info.set_position(pos); - - let values: Vec = PodSerializer::serialize( - std::io::Cursor::new(Vec::new()), - &Value::Object(Object { - type_: SPA_TYPE_OBJECT_Format, - id: SPA_PARAM_EnumFormat, - properties: audio_info.into(), - }), - ) - .unwrap() - .0 - .into_inner(); - - let value_clone = values.clone(); - - let mut param = [Pod::from_bytes(&values).unwrap()]; - - let direction = stream_params[stream_id as usize].direction; - - let media_category = match direction { - Direction::Input => "Capture", - Direction::Output => "Playback", - }; - let stream_name = match direction { - Direction::Input => "audio-input", - Direction::Output => "audio-output", - }; - - let props = properties! { - *pw::keys::MEDIA_TYPE => "Audio", - *pw::keys::MEDIA_CATEGORY => media_category, - }; - - let stream = pw::stream::StreamRc::new(self.core.clone(), stream_name, props) - .expect("could not create new stream"); - - let streams = self.stream_params.clone(); - - let listener_stream = stream - .add_local_listener() - .state_changed(|_, _, old, new| { - debug!("State changed: {old:?} -> {new:?}"); - }) - .param_changed(move |stream, _data, id, param| { - let Some(_param) = param else { - return; - }; - if id != ParamType::Format.as_raw() { - return; - } - let mut param = [Pod::from_bytes(&value_clone).unwrap()]; - - //callback to negotiate new set of streams - stream - .update_params(&mut param) - .expect("could not update params"); - }) - .process(move |stream, _data| match stream.dequeue_buffer() { - None => debug!("No buffer recieved"), - Some(mut buf) => { - match direction { - Direction::Input => { - let datas = buf.datas_mut(); - let data = &mut datas[0]; - let mut n_samples = data.chunk().size() as usize; - let Some(slice) = data.data() else { - return; - }; - let mut streams = streams.write().unwrap(); - let stream = streams - .get_mut(stream_id as usize) - .expect("Stream does not exist"); - - let mut start = 0; - while n_samples > 0 { - let Some(buffer) = stream.buffers.front_mut() else { - return; - }; - - let avail = usize::try_from(buffer.desc_len()) - .unwrap() - .saturating_sub(buffer.pos); - let n_bytes = n_samples.min(avail); - let p = &slice[start..start + n_bytes]; - - if buffer - .write_input(p) - .expect("Could not write data to guest memory") - == 0 - { - break; - } - - n_samples -= n_bytes; - start += n_bytes; - - if buffer.pos >= buffer.desc_len() as usize { - stream.buffers.pop_front(); - } - } - } - Direction::Output => { - let datas = buf.datas_mut(); - let frame_size = info.channels * size_of::() as u32; - let data = &mut datas[0]; - let n_bytes = if let Some(slice) = data.data() { - let mut n_bytes = slice.len(); - let mut streams = streams.write().unwrap(); - let streams = streams - .get_mut(stream_id as usize) - .expect("Stream does not exist"); - let Some(buffer) = streams.buffers.front_mut() else { - return; - }; - - let mut start = buffer.pos; - - let avail = usize::try_from(buffer.desc_len()) - .unwrap() - .saturating_sub(start); - - if avail < n_bytes { - n_bytes = avail; - } - let p = &mut slice[0..n_bytes]; - if avail == 0 { - // SAFETY: We have assured above that the pointer is not - // null - // safe to zero-initialize the pointer. - unsafe { - // pad with silence - ptr::write_bytes(p.as_mut_ptr(), 0, n_bytes); - } - } else { - // read_output() always reads (buffer.desc_len() - - // buffer.pos) bytes - buffer - .read_output(p) - .expect("failed to read buffer from guest"); - - start += n_bytes; - - buffer.pos = start; - - if start >= buffer.desc_len() as usize { - streams.buffers.pop_front(); - } - } - n_bytes - } else { - 0 - }; - let chunk = data.chunk_mut(); - *chunk.offset_mut() = 0; - *chunk.stride_mut() = i32::try_from(frame_size).unwrap(); - *chunk.size_mut() = u32::try_from(n_bytes).unwrap(); - } - }; - } - }) - .register() - .expect("failed to register stream listener"); - - stream_listener.insert(stream_id, listener_stream); - - stream - .connect( - stream_params[stream_id as usize].direction.into(), - Some(pw::constants::ID_ANY), - pw::stream::StreamFlags::RT_PROCESS - | pw::stream::StreamFlags::AUTOCONNECT - | pw::stream::StreamFlags::INACTIVE - | pw::stream::StreamFlags::MAP_BUFFERS, - &mut param, - ) - .expect("could not connect to the stream"); - - // insert created stream in a hash table - stream_hash.insert(stream_id, stream); - - lock_guard.unlock(); - } - - Ok(()) - } - - fn release(&self, stream_id: u32) -> Result<()> { - debug!("pipewire backend, release function"); - let release_result = self - .stream_params - .write() - .unwrap() - .get_mut(stream_id as usize) - .ok_or(Error::StreamWithIdNotFound(stream_id))? - .state - .release(); - if let Err(err) = release_result { - log::error!("Stream {stream_id} release {err}"); - return Err(Error::Stream(err)); - } - let lock_guard = self.thread_loop.lock(); - let mut stream_hash = self.stream_hash.write().unwrap(); - let mut stream_listener = self.stream_listener.write().unwrap(); - let st_buffer = &mut self.stream_params.write().unwrap(); - let stream = stream_hash - .get(&stream_id) - .expect("Could not find stream with this id in `stream_hash`."); - stream.disconnect().expect("could not disconnect stream"); - std::mem::take(&mut st_buffer[stream_id as usize].buffers); - stream_hash.remove(&stream_id); - stream_listener.remove(&stream_id); - lock_guard.unlock(); - Ok(()) - } - - fn start(&self, stream_id: u32) -> Result<()> { - debug!("pipewire start"); - let start_result = self - .stream_params - .write() - .unwrap() - .get_mut(stream_id as usize) - .ok_or(Error::StreamWithIdNotFound(stream_id))? - .state - .start(); - if let Err(err) = start_result { - // log the error and continue - log::error!("Stream {stream_id} start {err}"); - return Err(Error::Stream(err)); - } - let lock_guard = self.thread_loop.lock(); - let stream_hash = self.stream_hash.read().unwrap(); - let stream = stream_hash - .get(&stream_id) - .expect("Could not find stream with this id in `stream_hash`."); - stream.set_active(true).expect("could not start stream"); - lock_guard.unlock(); - Ok(()) - } - - fn stop(&self, stream_id: u32) -> Result<()> { - debug!("pipewire stop"); - let stop_result = self - .stream_params - .write() - .unwrap() - .get_mut(stream_id as usize) - .ok_or(Error::StreamWithIdNotFound(stream_id))? - .state - .stop(); - if let Err(err) = stop_result { - log::error!("Stream {stream_id} stop {err}"); - return Err(Error::Stream(err)); - } - let lock_guard = self.thread_loop.lock(); - let stream_hash = self.stream_hash.read().unwrap(); - let stream = stream_hash - .get(&stream_id) - .expect("Could not find stream with this id in `stream_hash`."); - stream.set_active(false).expect("could not stop stream"); - lock_guard.unlock(); - Ok(()) - } - - #[cfg(test)] - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -#[cfg(test)] -/// Utilities for building a temporary Dbus session and a pipewire instance for -/// testing. -pub mod test_utils; - -#[cfg(test)] -mod tests { - use super::{test_utils::PipewireTestHarness, *}; - - #[test] - fn test_pipewire_backend_success() { - crate::init_logger(); - let streams = Arc::new(RwLock::new(vec![Stream::default()])); - let stream_params = streams.clone(); - - let _test_harness = PipewireTestHarness::new(); - - let pw_backend = PwBackend::new(stream_params); - assert_eq!(pw_backend.stream_hash.read().unwrap().len(), 0); - assert_eq!(pw_backend.stream_listener.read().unwrap().len(), 0); - // set up minimal configuration for test - let request = VirtioSndPcmSetParams { - format: VIRTIO_SND_PCM_FMT_S16, - rate: VIRTIO_SND_PCM_RATE_11025, - channels: 1, - ..Default::default() - }; - pw_backend.set_parameters(0, request).unwrap(); - pw_backend.prepare(0).unwrap(); - pw_backend.start(0).unwrap(); - pw_backend.write(0).unwrap(); - pw_backend.read(0).unwrap(); - pw_backend.stop(0).unwrap(); - pw_backend.release(0).unwrap(); - let streams = streams.read().unwrap(); - assert_eq!(streams[0].buffers.len(), 0); - } - - #[test] - fn test_pipewire_backend_invalid_stream() { - crate::init_logger(); - let stream_params = Arc::new(RwLock::new(vec![])); - - let _test_harness = PipewireTestHarness::new(); - - let pw_backend = PwBackend::new(stream_params); - - let request = VirtioSndPcmSetParams::default(); - let res = pw_backend.set_parameters(0, request); - assert_eq!( - res.unwrap_err().to_string(), - Error::StreamWithIdNotFound(0).to_string() - ); - - for res in [ - pw_backend.prepare(0), - pw_backend.start(0), - pw_backend.stop(0), - ] { - assert_eq!( - res.unwrap_err().to_string(), - Error::StreamWithIdNotFound(0).to_string() - ); - } - - let res = pw_backend.release(0); - assert_eq!( - res.unwrap_err().to_string(), - Error::StreamWithIdNotFound(0).to_string() - ); - } -} diff --git a/src/devices/src/virtio/snd/audio_backends/pipewire/test_utils.rs b/src/devices/src/virtio/snd/audio_backends/pipewire/test_utils.rs deleted file mode 100644 index f7321213e..000000000 --- a/src/devices/src/virtio/snd/audio_backends/pipewire/test_utils.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Manos Pitsidianakis -// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - -use std::{ - io::Read, - path::Path, - process::{Child, Command, Stdio}, -}; - -use tempfile::{tempdir, TempDir}; - -/// Temporary Dbus session which is killed in drop(). -pub struct DbusSession { - pub child: Child, - pub address: String, -} - -impl DbusSession { - pub fn new(working_dir: &Path) -> Self { - let address_prefix = format!("unix:path={}", working_dir.join("dbus").display()); - let child = Command::new("/usr/bin/dbus-daemon") - .args(["--session", "--address", &address_prefix, "--print-address"]) - .env("DBUS_VERBOSE", "1") - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .stdin(Stdio::null()) - .current_dir(working_dir) - .spawn() - .expect("ERROR: dbus-daemon binary not found"); - - Self { - child, - address: address_prefix, - } - } -} - -impl Drop for DbusSession { - fn drop(&mut self) { - println!("INFO: Killing Dbus session {}", self.child.id()); - if let Err(err) = self.child.kill() { - println!( - "ERROR: could not kill dbus process {}: {err}", - self.child.id() - ); - } - // We mustn't panic in drop(), so use a wrapper function for convenience - print_output(&mut self.child, "dbus"); - } -} - -/// The pipewire test harness. It must only be constructed via -/// `PipewireTestHarness::new()`. -#[non_exhaustive] -pub struct PipewireTestHarness { - pub dbus: DbusSession, - pub pipewire_child: Child, - pub tempdir: TempDir, -} - -pub fn launch_pipewire( - tempdir: &Path, - dbus_session_bus_address: &Path, -) -> Result { - Command::new("pipewire") - .env("DBUS_SESSION_BUS_ADDRESS", dbus_session_bus_address) - .env("XDG_RUNTIME_DIR", tempdir) - .current_dir(tempdir) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .stdin(Stdio::null()) - .spawn() -} - -impl PipewireTestHarness { - pub fn new() -> Self { - let tempdir = tempdir().unwrap(); - - let dbus_session = DbusSession::new(tempdir.path()); - println!("INFO: dbus_session_bus_address={}", dbus_session.address); - - println!("INFO: Wait for dbus to setup..."); - std::thread::sleep(std::time::Duration::from_secs(1)); - - println!("INFO: Launch pipewire."); - let pipewire_child = launch_pipewire(tempdir.path(), Path::new(&dbus_session.address)) - .expect("ERROR: Could not launch pipewire"); - println!("INFO: Wait for pipewire to setup..."); - std::thread::sleep(std::time::Duration::from_secs(1)); - - std::env::set_var("DBUS_SESSION_BUS_ADDRESS", &dbus_session.address); - std::env::set_var("XDG_RUNTIME_DIR", tempdir.path()); - - Self { - dbus: dbus_session, - pipewire_child, - tempdir, - } - } -} - -impl Drop for PipewireTestHarness { - fn drop(&mut self) { - println!("INFO: Killing pipewire pid {}", self.pipewire_child.id()); - if let Err(err) = self.pipewire_child.kill() { - println!( - "ERROR: could not kill Pipewire process {}: {err}", - self.pipewire_child.id() - ); - } - // We mustn't panic in drop(), so use a wrapper function for convenience - print_output(&mut self.pipewire_child, "pipewire"); - } -} - -fn print_output(child: &mut Child, id: &'static str) -> Option<()> { - let mut stdout = child.stdout.take()?; - let mut stderr = child.stderr.take()?; - - let mut buf = String::new(); - stdout.read_to_string(&mut buf).ok()?; - if !buf.trim().is_empty() { - println!("INFO: {id} stdout {buf}"); - } - - buf.clear(); - - stderr.read_to_string(&mut buf).ok()?; - if !buf.trim().is_empty() { - println!("ERROR: {id} stderr {buf}"); - } - - None -} diff --git a/src/devices/src/virtio/snd/device.rs b/src/devices/src/virtio/snd/device.rs deleted file mode 100644 index 4607790d7..000000000 --- a/src/devices/src/virtio/snd/device.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::io::Write; -use std::thread::JoinHandle; - -use utils::eventfd::EventFd; -use vm_memory::{ByteValued, GuestMemoryMmap}; - -use super::super::{ActivateError, ActivateResult, DeviceQueue, QueueConfig, VirtioDevice}; -use super::virtio_sound::VirtioSoundConfig; -use super::worker::SndWorker; -use super::{defs, defs::uapi, Error}; - -use crate::virtio::{DeviceState, InterruptTransport}; - -// Supported features. -pub(crate) const AVAIL_FEATURES: u64 = 1 << uapi::VIRTIO_F_VERSION_1 as u64; - -pub struct Snd { - pub(crate) avail_features: u64, - pub(crate) acked_features: u64, - pub(crate) activate_evt: EventFd, - pub(crate) device_state: DeviceState, - worker_thread: Option>, - worker_stopfd: EventFd, -} - -impl Snd { - pub fn new() -> super::Result { - Ok(Snd { - avail_features: AVAIL_FEATURES, - acked_features: 0, - activate_evt: EventFd::new(utils::eventfd::EFD_NONBLOCK) - .map_err(Error::EventFdCreate)?, - device_state: DeviceState::Inactive, - worker_thread: None, - worker_stopfd: EventFd::new(utils::eventfd::EFD_NONBLOCK) - .map_err(Error::EventFdCreate)?, - }) - } - - pub fn id(&self) -> &str { - defs::SND_DEV_ID - } -} - -impl VirtioDevice for Snd { - fn avail_features(&self) -> u64 { - self.avail_features - } - - fn acked_features(&self) -> u64 { - self.acked_features - } - - fn set_acked_features(&mut self, acked_features: u64) { - self.acked_features = acked_features - } - - fn device_type(&self) -> u32 { - uapi::VIRTIO_ID_SND - } - - fn device_name(&self) -> &str { - "snd" - } - - fn queue_config(&self) -> &[QueueConfig] { - &defs::QUEUE_CONFIG - } - - fn read_config(&self, offset: u64, mut data: &mut [u8]) { - let config = VirtioSoundConfig { - jacks: 0.into(), - streams: 2.into(), - chmaps: 1.into(), - }; - - let config_slice = config.as_slice(); - let config_len = config_slice.len() as u64; - if offset >= config_len { - error!("Failed to read config space"); - return; - } - if let Some(end) = offset.checked_add(data.len() as u64) { - // This write can't fail, offset and end are checked against config_len. - data.write_all(&config_slice[offset as usize..std::cmp::min(end, config_len) as usize]) - .unwrap(); - } - } - - fn write_config(&mut self, offset: u64, data: &[u8]) { - warn!( - "snd: guest driver attempted to write device config (offset={:x}, len={:x})", - offset, - data.len() - ); - } - - fn activate( - &mut self, - mem: GuestMemoryMmap, - interrupt: InterruptTransport, - queues: Vec, - ) -> ActivateResult { - if self.worker_thread.is_some() { - panic!("virtio_snd: worker thread already exists"); - } - - if queues.len() != defs::NUM_QUEUES { - error!( - "Cannot perform activate. Expected {} queue(s), got {}", - defs::NUM_QUEUES, - queues.len() - ); - return Err(ActivateError::BadActivate); - } - - let worker = SndWorker::new( - queues, - interrupt.clone(), - mem.clone(), - self.worker_stopfd.try_clone().unwrap(), - ); - self.worker_thread = Some(worker.run()); - - if self.activate_evt.write(1).is_err() { - error!("Cannot write to activate_evt",); - return Err(ActivateError::BadActivate); - } - - self.device_state = DeviceState::Activated(mem, interrupt); - - Ok(()) - } - - fn is_activated(&self) -> bool { - self.device_state.is_activated() - } - - fn reset(&mut self) -> bool { - if let Some(worker) = self.worker_thread.take() { - let _ = self.worker_stopfd.write(1); - if let Err(e) = worker.join() { - error!("error waiting for worker thread: {e:?}"); - } - } - self.device_state = DeviceState::Inactive; - true - } -} diff --git a/src/devices/src/virtio/snd/mod.rs b/src/devices/src/virtio/snd/mod.rs deleted file mode 100644 index ea2f31f4a..000000000 --- a/src/devices/src/virtio/snd/mod.rs +++ /dev/null @@ -1,312 +0,0 @@ -use std::{ - io::Error as IoError, - sync::{Arc, Mutex}, -}; - -mod audio_backends; -mod device; -pub mod stream; -#[allow(dead_code)] -mod virtio_sound; -mod worker; - -use thiserror::Error as ThisError; -use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryMmap}; - -pub use self::defs::uapi::VIRTIO_ID_SND as TYPE_SND; -pub use self::device::Snd; -pub use stream::Stream; -use virtio_sound::*; - -use super::{Descriptor, InterruptTransport, Queue}; -use crate::virtio::snd::virtio_sound::{VirtioSoundHeader, VirtioSoundPcmStatus}; - -mod defs { - use super::super::QueueConfig; - use super::virtio_sound::*; - - pub const SND_DEV_ID: &str = "virtio_snd"; - pub const NUM_QUEUES: usize = 4; - pub const CTL_INDEX: usize = 0; - pub const EVT_INDEX: usize = 1; - pub const TXQ_INDEX: usize = 2; - pub const RXQ_INDEX: usize = 3; - pub const QUEUE_INDEXES: [usize; 4] = [CTL_INDEX, EVT_INDEX, TXQ_INDEX, RXQ_INDEX]; - - const QUEUE_SIZE: u16 = 256; - pub static QUEUE_CONFIG: [QueueConfig; NUM_QUEUES] = [QueueConfig::new(QUEUE_SIZE); NUM_QUEUES]; - - pub const SUPPORTED_FORMATS: u64 = (1 << VIRTIO_SND_PCM_FMT_U8) - | (1 << VIRTIO_SND_PCM_FMT_S16) - | (1 << VIRTIO_SND_PCM_FMT_S24) - | (1 << VIRTIO_SND_PCM_FMT_S32); - - pub const SUPPORTED_RATES: u64 = (1 << VIRTIO_SND_PCM_RATE_8000) - | (1 << VIRTIO_SND_PCM_RATE_11025) - | (1 << VIRTIO_SND_PCM_RATE_16000) - | (1 << VIRTIO_SND_PCM_RATE_22050) - | (1 << VIRTIO_SND_PCM_RATE_32000) - | (1 << VIRTIO_SND_PCM_RATE_44100) - | (1 << VIRTIO_SND_PCM_RATE_48000); - - pub mod uapi { - pub const VIRTIO_F_VERSION_1: u32 = 32; - pub const VIRTIO_ID_SND: u32 = 25; - } -} - -pub type Result = std::result::Result; - -/// Stream direction. -/// -/// Equivalent to `VIRTIO_SND_D_OUTPUT` and `VIRTIO_SND_D_INPUT`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[repr(u8)] -pub enum Direction { - /// [`VIRTIO_SND_D_OUTPUT`](crate::virtio_sound::VIRTIO_SND_D_OUTPUT) - Output = VIRTIO_SND_D_OUTPUT, - /// [`VIRTIO_SND_D_INPUT`](crate::virtio_sound::VIRTIO_SND_D_INPUT) - Input = VIRTIO_SND_D_INPUT, -} - -impl TryFrom for Direction { - type Error = Error; - - fn try_from(val: u8) -> std::result::Result { - Ok(match val { - virtio_sound::VIRTIO_SND_D_OUTPUT => Self::Output, - virtio_sound::VIRTIO_SND_D_INPUT => Self::Input, - other => { - return Err(Error::InvalidMessageValue( - stringify!(Direction), - other.into(), - )) - } - }) - } -} - -/// Custom error types -#[derive(Debug, ThisError)] -pub enum Error { - #[error("Notification send failed")] - SendNotificationFailed, - #[error("Descriptor not found")] - DescriptorNotFound, - #[error("Descriptor read failed")] - DescriptorReadFailed, - #[error("Descriptor write failed")] - DescriptorWriteFailed, - #[error("Failed to handle event other than EPOLLIN event")] - HandleEventNotEpollIn, - #[error("Failed to handle unknown event with id {0}")] - HandleUnknownEvent(u16), - #[error("Invalid control message code {0}")] - InvalidControlMessage(u32), - #[error("Invalid value in {0}: {1}")] - InvalidMessageValue(&'static str, u16), - #[error("Failed to create a new EventFd")] - EventFdCreate(IoError), - #[error("Request missing data buffer")] - SoundReqMissingData, - #[error("Audio backend not supported")] - AudioBackendNotSupported, - #[error("Audio backend unexpected error: {0}")] - UnexpectedAudioBackendError(String), - #[error("Audio backend configuration not supported")] - UnexpectedAudioBackendConfiguration, - #[error("No memory configured")] - NoMemoryConfigured, - #[error("Invalid virtio_snd_hdr size, expected: {0}, found: {1}")] - UnexpectedSoundHeaderSize(usize, u32), - #[error("Received unexpected write only descriptor at index {0}")] - UnexpectedWriteOnlyDescriptor(usize), - #[error("Received unexpected readable descriptor at index {0}")] - UnexpectedReadableDescriptor(usize), - #[error("Invalid descriptor count {0}")] - UnexpectedDescriptorCount(usize), - #[error("Invalid descriptor size, expected: {0}, found: {1}")] - UnexpectedDescriptorSize(usize, u32), - #[error("Protocol or device error: {0}")] - Stream(stream::Error), - #[error("Stream with id {0} not found")] - StreamWithIdNotFound(u32), - #[error("Channel number not supported: {0}")] - ChannelNotSupported(u8), - #[error("No audio backend in present")] - MissingAudioBackend, -} - -impl From for IoError { - fn from(e: Error) -> Self { - Self::other(e) - } -} - -impl From for Error { - fn from(val: stream::Error) -> Self { - Self::Stream(val) - } -} - -#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)] -pub enum BackendType { - #[default] - Pipewire, -} - -#[derive(Debug, PartialEq, Eq)] -pub struct InvalidControlMessage(u32); - -impl std::fmt::Display for InvalidControlMessage { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "Invalid control message code {}", self.0) - } -} - -impl From for Error { - fn from(val: InvalidControlMessage) -> Self { - Self::InvalidControlMessage(val.0) - } -} - -impl std::error::Error for InvalidControlMessage {} - -pub struct Vring { - mem: GuestMemoryMmap, - queue: Queue, - interrupt: InterruptTransport, -} - -impl Vring { - pub fn signal_used_queue(&self) { - debug!("snd: raising IRQ"); - if let Err(e) = self.interrupt.try_signal_used_queue() { - warn!("Failed to signal queue: {e:?}"); - } - } -} - -#[derive(Copy, Debug, Clone, Eq, PartialEq)] -#[repr(u32)] -pub enum ControlMessageKind { - JackInfo = 1, - JackRemap = 2, - PcmInfo = 0x0100, - PcmSetParams = 0x0101, - PcmPrepare = 0x0102, - PcmRelease = 0x0103, - PcmStart = 0x0104, - PcmStop = 0x0105, - ChmapInfo = 0x0200, -} - -impl TryFrom for ControlMessageKind { - type Error = InvalidControlMessage; - - fn try_from(val: u32) -> std::result::Result { - Ok(match val { - VIRTIO_SND_R_JACK_INFO => Self::JackInfo, - VIRTIO_SND_R_JACK_REMAP => Self::JackRemap, - VIRTIO_SND_R_PCM_INFO => Self::PcmInfo, - VIRTIO_SND_R_PCM_SET_PARAMS => Self::PcmSetParams, - VIRTIO_SND_R_PCM_PREPARE => Self::PcmPrepare, - VIRTIO_SND_R_PCM_RELEASE => Self::PcmRelease, - VIRTIO_SND_R_PCM_START => Self::PcmStart, - VIRTIO_SND_R_PCM_STOP => Self::PcmStop, - VIRTIO_SND_R_CHMAP_INFO => Self::ChmapInfo, - other => return Err(InvalidControlMessage(other)), - }) - } -} - -pub struct ControlMessage { - pub kind: ControlMessageKind, - pub code: u32, - pub desc_addr: GuestAddress, - pub head_index: u16, - pub vring: Arc>, -} - -impl std::fmt::Debug for ControlMessage { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - fmt.debug_struct(stringify!(ControlMessage)) - .field("kind", &self.kind) - .field("code", &self.code) - .finish() - } -} - -impl Drop for ControlMessage { - fn drop(&mut self) { - debug!( - "dropping ControlMessage {:?} reply = {}", - self.kind, - match self.code { - virtio_sound::VIRTIO_SND_S_OK => "VIRTIO_SND_S_OK", - virtio_sound::VIRTIO_SND_S_BAD_MSG => "VIRTIO_SND_S_BAD_MSG", - virtio_sound::VIRTIO_SND_S_NOT_SUPP => "VIRTIO_SND_S_NOT_SUPP", - virtio_sound::VIRTIO_SND_S_IO_ERR => "VIRTIO_SND_S_IO_ERR", - _ => "other", - } - ); - let resp = VirtioSoundHeader { - code: self.code.into(), - }; - - let mut vring = self.vring.lock().unwrap(); - let mem = vring.mem.clone(); - - if let Err(err) = vring.mem.write_obj(resp, self.desc_addr) { - log::error!("Error::DescriptorWriteFailed: {err}"); - return; - } - if let Err(err) = vring - .queue - .add_used(&mem, self.head_index, resp.as_slice().len() as u32) - { - log::error!("Error adding used descriptors: {err}"); - return; - } - vring.signal_used_queue(); - } -} - -pub struct IOMessage { - status: std::sync::atomic::AtomicU32, - pub used_len: std::sync::atomic::AtomicU32, - pub latency_bytes: std::sync::atomic::AtomicU32, - - head_index: u16, - response_descriptor: Descriptor, - vring: Arc>, -} - -impl Drop for IOMessage { - fn drop(&mut self) { - let resp = VirtioSoundPcmStatus { - status: self.status.load(std::sync::atomic::Ordering::SeqCst).into(), - latency_bytes: self - .latency_bytes - .load(std::sync::atomic::Ordering::SeqCst) - .into(), - }; - let used_len: u32 = self.used_len.load(std::sync::atomic::Ordering::SeqCst); - log::trace!("dropping IOMessage {resp:?}"); - - let mut vring = self.vring.lock().unwrap(); - let mem = vring.mem.clone(); - if let Err(err) = mem.write_obj(resp, GuestAddress(self.response_descriptor.addr)) { - log::error!("Error::DescriptorWriteFailed: {err}"); - return; - } - if let Err(err) = vring.queue.add_used( - &mem, - self.head_index, - resp.as_slice().len() as u32 + used_len, - ) { - log::error!("Couldn't add used bytes count to vring: {err}"); - } - vring.signal_used_queue(); - } -} diff --git a/src/devices/src/virtio/snd/stream.rs b/src/devices/src/virtio/snd/stream.rs deleted file mode 100644 index f32d80d8f..000000000 --- a/src/devices/src/virtio/snd/stream.rs +++ /dev/null @@ -1,624 +0,0 @@ -// Manos Pitsidianakis -// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - -use std::{collections::VecDeque, sync::Arc}; - -use thiserror::Error as ThisError; -use vm_memory::{Address, Bytes, GuestAddress, Le32, Le64}; - -use super::super::Descriptor; -use super::defs::{SUPPORTED_FORMATS, SUPPORTED_RATES}; -use super::{virtio_sound::*, Direction, IOMessage}; - -/// Stream errors. -#[derive(Debug, ThisError, PartialEq, Eq)] -pub enum Error { - #[error("Guest driver request {0} in an invalid stream state {1}")] - InvalidState(&'static str, PCMState), - #[error("Guest driver request an invalid stream state transition from {0} to {1}.")] - InvalidStateTransition(PCMState, PCMState), - #[error("Guest requested an invalid stream id: {0}")] - InvalidStreamId(u32), - #[error("Descriptor read failed")] - DescriptorReadFailed, - #[error("Descriptor write failed")] - DescriptorWriteFailed, - #[error("Could not disconnect stream")] - CouldNotDisconnectStream, -} - -type Result = std::result::Result; - -/// PCM stream state machine. -/// -/// ## 5.14.6.6.1 PCM Command Lifecycle -/// -/// A PCM stream has the following command lifecycle: -/// -/// - `SET PARAMETERS` -/// -/// The driver negotiates the stream parameters (format, transport, etc) with -/// the device. -/// -/// Possible valid transitions: `SET PARAMETERS`, `PREPARE`. -/// -/// - `PREPARE` -/// -/// The device prepares the stream (allocates resources, etc). -/// -/// Possible valid transitions: `SET PARAMETERS`, `PREPARE`, `START`, -/// `RELEASE`. Output only: the driver transfers data for pre-buffing. -/// -/// - `START` -/// -/// The device starts the stream (unmute, putting into running state, etc). -/// -/// Possible valid transitions: `STOP`. -/// The driver transfers data to/from the stream. -/// -/// - `STOP` -/// -/// The device stops the stream (mute, putting into non-running state, etc). -/// -/// Possible valid transitions: `START`, `RELEASE`. -/// -/// - `RELEASE` -/// -/// The device releases the stream (frees resources, etc). -/// -/// Possible valid transitions: `SET PARAMETERS`, `PREPARE`. -/// -/// ```text -/// +---------------+ +---------+ +---------+ +-------+ +-------+ -/// | SetParameters | | Prepare | | Release | | Start | | Stop | -/// +---------------+ +---------+ +---------+ +-------+ +-------+ -/// | | | | | -/// |- | | | | -/// || | | | | -/// |< | | | | -/// | | | | | -/// |------------->| | | | -/// | | | | | -/// |<-------------| | | | -/// | | | | | -/// | |- | | | -/// | || | | | -/// | |< | | | -/// | | | | | -/// | |--------------------->| | -/// | | | | | -/// | |---------->| | | -/// | | | | | -/// | | | |-------->| -/// | | | | | -/// | | | |<--------| -/// | | | | | -/// | | |<-------------------| -/// | | | | | -/// |<-------------------------| | | -/// | | | | | -/// | |<----------| | | -/// ``` -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] -pub enum PCMState { - #[default] - #[doc(alias = "VIRTIO_SND_R_PCM_SET_PARAMS")] - SetParameters, - #[doc(alias = "VIRTIO_SND_R_PCM_PREPARE")] - Prepare, - #[doc(alias = "VIRTIO_SND_R_PCM_RELEASE")] - Release, - #[doc(alias = "VIRTIO_SND_R_PCM_START")] - Start, - #[doc(alias = "VIRTIO_SND_R_PCM_STOP")] - Stop, -} - -macro_rules! set_new_state { - ($new_state_fn:ident, $new_state:expr, $($valid_source_states:tt)*) => { - pub fn $new_state_fn(&mut self) -> Result<()> { - if !matches!(self, $($valid_source_states)*) { - return Err(Error::InvalidStateTransition(*self, $new_state)); - } - *self = $new_state; - Ok(()) - } - }; -} - -impl PCMState { - pub fn new() -> Self { - Self::default() - } - - set_new_state!( - set_parameters, - Self::SetParameters, - Self::SetParameters | Self::Prepare | Self::Release - ); - - set_new_state!( - prepare, - Self::Prepare, - Self::SetParameters | Self::Prepare | Self::Release - ); - - set_new_state!(start, Self::Start, Self::Prepare | Self::Stop); - - set_new_state!(stop, Self::Stop, Self::Start); - - set_new_state!(release, Self::Release, Self::Prepare | Self::Stop); -} - -impl std::fmt::Display for PCMState { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - use PCMState::*; - match *self { - SetParameters => { - write!(fmt, "VIRTIO_SND_R_PCM_SET_PARAMS") - } - Prepare => { - write!(fmt, "VIRTIO_SND_R_PCM_PREPARE") - } - Release => { - write!(fmt, "VIRTIO_SND_R_PCM_RELEASE") - } - Start => { - write!(fmt, "VIRTIO_SND_R_PCM_START") - } - Stop => { - write!(fmt, "VIRTIO_SND_R_PCM_STOP") - } - } - } -} - -/// Internal state of a PCM stream of the VIRTIO Sound device. -#[derive(Debug)] -pub struct Stream { - pub id: usize, - pub params: PcmParams, - pub formats: Le64, - pub rates: Le64, - pub direction: Direction, - pub channels_min: u8, - pub channels_max: u8, - pub state: PCMState, - pub buffers: VecDeque, -} - -impl Default for Stream { - fn default() -> Self { - Self { - id: 0, - direction: Direction::Output, - formats: SUPPORTED_FORMATS.into(), - rates: SUPPORTED_RATES.into(), - params: PcmParams::default(), - channels_min: 1, - channels_max: 6, - state: Default::default(), - buffers: VecDeque::new(), - } - } -} - -impl Stream { - #[inline] - pub fn supports_format(&self, format: u8) -> bool { - let formats: u64 = self.formats.into(); - (formats & (1_u64 << format)) != 0 - } - - #[inline] - pub fn supports_rate(&self, rate: u8) -> bool { - let rates: u64 = self.rates.into(); - (rates & (1_u64 << rate)) != 0 - } -} - -/// Stream params -#[derive(Debug)] -pub struct PcmParams { - /// size of hardware buffer in bytes - pub buffer_bytes: Le32, - /// size of hardware period in bytes - pub period_bytes: Le32, - pub features: Le32, - pub channels: u8, - pub format: u8, - pub rate: u8, -} - -impl Default for PcmParams { - fn default() -> Self { - Self { - buffer_bytes: 8192.into(), - period_bytes: 4096.into(), - features: 0.into(), - channels: 1, - format: VIRTIO_SND_PCM_FMT_S16, - rate: VIRTIO_SND_PCM_RATE_44100, - } - } -} - -pub struct Buffer { - data_descriptor: Descriptor, - pub pos: usize, - pub message: Arc, - direction: Direction, -} - -impl std::fmt::Debug for Buffer { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - fmt.debug_struct(stringify!(Buffer)) - .field("pos", &self.pos) - .field("direction", &self.direction) - .field("message", &Arc::as_ptr(&self.message)) - .finish() - } -} - -impl Buffer { - pub fn new(data_descriptor: Descriptor, message: Arc, direction: Direction) -> Self { - Self { - pos: 0, - data_descriptor, - message, - direction, - } - } - - pub fn read_output(&self, buf: &mut [u8]) -> Result { - let addr = self.data_descriptor.addr; - let offset = self.pos as u64; - let len = self - .message - .vring - .lock() - .unwrap() - .mem - .read( - buf, - GuestAddress(addr) - .checked_add(offset) - .expect("invalid guest memory address"), - ) - .map_err(|_| Error::DescriptorReadFailed)?; - Ok(len as u32) - } - - pub fn write_input(&mut self, buf: &[u8]) -> Result { - if self.desc_len() <= self.pos as u32 { - return Ok(0); - } - let addr = self.data_descriptor.addr; - let offset = self.pos as u64; - let len = self - .message - .vring - .lock() - .unwrap() - .mem - .write( - buf, - GuestAddress(addr) - .checked_add(offset) - .expect("invalid guest memory address"), - ) - .map_err(|_| Error::DescriptorWriteFailed)?; - self.pos += len; - Ok(len as u32) - } - - #[inline] - /// Returns the length of the sound data [`virtio_queue::Descriptor`]. - pub fn desc_len(&self) -> u32 { - self.data_descriptor.len - } -} - -impl Drop for Buffer { - fn drop(&mut self) { - match self.direction { - Direction::Input => { - let used_len = std::cmp::min(self.pos as u32, self.desc_len()); - self.message - .used_len - .fetch_add(used_len, std::sync::atomic::Ordering::SeqCst); - self.message - .latency_bytes - .fetch_add(used_len, std::sync::atomic::Ordering::SeqCst); - } - Direction::Output => { - self.message - .latency_bytes - .fetch_add(self.desc_len(), std::sync::atomic::Ordering::SeqCst); - } - } - log::trace!("dropping {:?} buffer {:?}", self.direction, self); - } -} - -#[cfg(test)] -mod tests { - use std::fmt::Write; - - use vhost_user_backend::{VringRwLock, VringT}; - use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; - use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue, QueueOwnedT}; - use vm_memory::{ - Address, ByteValued, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap, - }; - - use super::*; - use crate::SoundDescriptorChain; - - // Prepares a single chain of descriptors for request queue - fn prepare_desc_chain( - start_addr: GuestAddress, - hdr: R, - response_len: u32, - ) -> SoundDescriptorChain { - let mem = &GuestMemoryMmap::<()>::from_ranges(&[(start_addr, 0x1000)]).unwrap(); - let vq = MockSplitQueue::new(mem, 16); - let mut next_addr = vq.desc_table().total_size() + 0x100; - let mut index = 0; - - let desc_out = Descriptor::new( - next_addr, - std::mem::size_of::() as u32, - VRING_DESC_F_NEXT as u16, - index + 1, - ); - - mem.write_obj::(hdr, desc_out.addr()).unwrap(); - vq.desc_table().store(index, desc_out).unwrap(); - next_addr += u64::from(desc_out.len()); - index += 1; - - // In response descriptor - let desc_in = Descriptor::new(next_addr, response_len, VRING_DESC_F_WRITE as u16, 0); - vq.desc_table().store(index, desc_in).unwrap(); - - // Put the descriptor index 0 in the first available ring position. - mem.write_obj(0u16, vq.avail_addr().unchecked_add(4)) - .unwrap(); - - // Set `avail_idx` to 1. - mem.write_obj(1u16, vq.avail_addr().unchecked_add(2)) - .unwrap(); - - // Create descriptor chain from pre-filled memory - vq.create_queue::() - .unwrap() - .iter(GuestMemoryAtomic::new(mem.clone()).memory()) - .unwrap() - .next() - .unwrap() - } - - fn iomsg() -> IOMessage { - let hdr = VirtioSndPcmSetParams::default(); - let memr = GuestMemoryAtomic::new( - GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(), - ); - let vring = VringRwLock::new(memr, 0x1000).unwrap(); - let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); - let vq = MockSplitQueue::new(mem, 16); - let next_addr = vq.desc_table().total_size() + 0x100; - IOMessage { - status: VIRTIO_SND_S_OK.into(), - latency_bytes: 0.into(), - used_len: 0.into(), - desc_chain: prepare_desc_chain::(GuestAddress(0), hdr, 1), - response_descriptor: Descriptor::new(next_addr, 0x200, VRING_DESC_F_NEXT as u16, 1), - vring, - } - } - - #[test] - fn test_display_fmt() { - assert_eq!(&PCMState::Stop.to_string(), "VIRTIO_SND_R_PCM_STOP"); - } - - #[test] - fn test_logging() { - let data_descriptor = Descriptor::new(0, 0, 0, 0); - let msg = iomsg(); - let message = Arc::new(msg); - let direction = Direction::Input; - let buffer = Buffer::new(data_descriptor, message, direction); - assert_eq!(format!("{direction:?}"), "Input"); - assert_eq!( - format!("{buffer:?}"), - format!( - "Buffer {{ pos: 0, direction: Input, message: {:?} }}", - &Arc::as_ptr(&buffer.message) - ) - ); - } - - #[test] - fn test_pcm_state_transitions() { - let mut state = PCMState::new(); - assert_eq!(state, PCMState::SetParameters); - - state.set_parameters().unwrap(); - assert_eq!(state, PCMState::SetParameters); - - state.prepare().unwrap(); - assert_eq!(state, PCMState::Prepare); - - state.release().unwrap(); - assert_eq!(state, PCMState::Release); - } - - #[test] - fn test_invalid_state_transition() { - let mut state = PCMState::new(); - assert_eq!(state, PCMState::SetParameters); - - // Attempt to transition from set_params state to Release state - let result = state.release(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::SetParameters, - PCMState::Release - )) - ); - - let result = state.start(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::SetParameters, - PCMState::Start - )) - ); - - let result = state.stop(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::SetParameters, - PCMState::Stop - )) - ); - - state.prepare().unwrap(); - let result = state.stop(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::Prepare, - PCMState::Stop - )) - ); - - state.start().unwrap(); - let result = state.set_parameters(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::Start, - PCMState::SetParameters - )) - ); - - let result = state.release(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::Start, - PCMState::Release - )) - ); - - let result = state.prepare(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::Start, - PCMState::Prepare - )) - ); - - state.stop().unwrap(); - let result = state.set_parameters(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::Stop, - PCMState::SetParameters - )) - ); - - let result = state.prepare(); - assert_eq!( - result, - Err(Error::InvalidStateTransition( - PCMState::Stop, - PCMState::Prepare - )) - ); - } - - #[test] - fn test_stream_supports_format() { - let stream = Stream::default(); - assert!(stream.supports_format(VIRTIO_SND_PCM_FMT_S16)); - assert!(stream.supports_rate(VIRTIO_SND_PCM_RATE_44100)); - } - - #[test] - fn test_pcm_params_default() { - let params = PcmParams::default(); - assert_eq!(params.buffer_bytes, 8192); - assert_eq!(params.period_bytes, 4096); - assert_eq!(params.features, 0); - assert_eq!(params.channels, 1); - assert_eq!(params.format, VIRTIO_SND_PCM_FMT_S16); - assert_eq!(params.rate, VIRTIO_SND_PCM_RATE_44100); - } - - #[test] - fn test_buffer_read_output() { - let msg = iomsg(); - let message = Arc::new(msg); - let desc_msg = iomsg(); - let buffer = Buffer::new( - desc_msg.desc_chain.clone().readable().next().unwrap(), - message, - Direction::Output, - ); - - let mut buf = vec![0; 5]; - buffer.read_output(&mut buf).unwrap(); - } - - #[test] - fn test_buffer_write_input() { - let msg = iomsg(); - let message = Arc::new(msg); - let desc_msg = iomsg(); - let mut buffer = Buffer::new( - desc_msg.desc_chain.clone().readable().next().unwrap(), - message, - Direction::Input, - ); - - let buf = vec![0; 5]; - buffer.write_input(&buf).unwrap(); - } - - #[test] - fn test_buffer_fn() { - let data_descriptor = Descriptor::new(0, 0, 0, 0); - let msg = iomsg(); - let message = Arc::new(msg); - let direction = Direction::Input; - let buffer = Buffer::new(data_descriptor, message, direction); - - assert_eq!(buffer.desc_len() as usize, buffer.pos); - assert_eq!(buffer.desc_len(), 0); - assert_eq!(buffer.direction, Direction::Input); - - // Test debug format representation for Buffer - let mut debug_output = String::new(); - - // Format the Debug representation into the String. - write!(&mut debug_output, "{:?}", buffer).unwrap(); - - let expected_debug = format!( - "Buffer {{ pos: {}, direction: {:?}, message: {:?} }}", - buffer.pos, - buffer.direction, - Arc::as_ptr(&buffer.message) - ); - - assert_eq!(debug_output, expected_debug); - } -} diff --git a/src/devices/src/virtio/snd/virtio_sound.rs b/src/devices/src/virtio/snd/virtio_sound.rs deleted file mode 100644 index a666b3f19..000000000 --- a/src/devices/src/virtio/snd/virtio_sound.rs +++ /dev/null @@ -1,512 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause -use vm_memory::{ByteValued, Le32, Le64}; - -// virtqueues - -pub const CONTROL_QUEUE_IDX: u16 = 0; -pub const EVENT_QUEUE_IDX: u16 = 1; -pub const TX_QUEUE_IDX: u16 = 2; -pub const RX_QUEUE_IDX: u16 = 3; -pub const NUM_QUEUES: u16 = 4; - -// jack control request types - -pub const VIRTIO_SND_R_JACK_INFO: u32 = 1; -pub const VIRTIO_SND_R_JACK_REMAP: u32 = 2; - -// PCM control request types - -pub const VIRTIO_SND_R_PCM_INFO: u32 = 0x0100; -pub const VIRTIO_SND_R_PCM_SET_PARAMS: u32 = 0x0101; -pub const VIRTIO_SND_R_PCM_PREPARE: u32 = 0x0102; -pub const VIRTIO_SND_R_PCM_RELEASE: u32 = 0x0103; -pub const VIRTIO_SND_R_PCM_START: u32 = 0x0104; -pub const VIRTIO_SND_R_PCM_STOP: u32 = 0x0105; - -// channel map control request types - -pub const VIRTIO_SND_R_CHMAP_INFO: u32 = 0x0200; - -// jack event types - -pub const VIRTIO_SND_EVT_JACK_CONNECTED: u32 = 0x1000; -pub const VIRTIO_SND_EVT_JACK_DISCONNECTED: u32 = 0x1001; - -// PCM event types - -pub const VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED: u32 = 0x1100; -pub const VIRTIO_SND_EVT_PCM_XRUN: u32 = 0x1101; - -// common status codes - -pub const VIRTIO_SND_S_OK: u32 = 0x8000; -pub const VIRTIO_SND_S_BAD_MSG: u32 = 0x8001; -pub const VIRTIO_SND_S_NOT_SUPP: u32 = 0x8002; -pub const VIRTIO_SND_S_IO_ERR: u32 = 0x8003; - -// device data flow directions - -pub const VIRTIO_SND_D_OUTPUT: u8 = 0; -pub const VIRTIO_SND_D_INPUT: u8 = 1; - -// supported jack features - -pub const VIRTIO_SND_JACK_F_REMAP: u32 = 0; - -// supported PCM stream features - -pub const VIRTIO_SND_PCM_F_SHMEM_HOST: u8 = 0; -pub const VIRTIO_SND_PCM_F_SHMEM_GUEST: u8 = 1; -pub const VIRTIO_SND_PCM_F_MSG_POLLING: u8 = 2; -pub const VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS: u8 = 3; -pub const VIRTIO_SND_PCM_F_EVT_XRUNS: u8 = 4; - -// supported PCM sample formats - -pub const VIRTIO_SND_PCM_FMT_IMA_ADPCM: u8 = 0; -pub const VIRTIO_SND_PCM_FMT_MU_LAW: u8 = 1; -pub const VIRTIO_SND_PCM_FMT_A_LAW: u8 = 2; -pub const VIRTIO_SND_PCM_FMT_S8: u8 = 3; -pub const VIRTIO_SND_PCM_FMT_U8: u8 = 4; -pub const VIRTIO_SND_PCM_FMT_S16: u8 = 5; -pub const VIRTIO_SND_PCM_FMT_U16: u8 = 6; -pub const VIRTIO_SND_PCM_FMT_S18_3: u8 = 7; -pub const VIRTIO_SND_PCM_FMT_U18_3: u8 = 8; -pub const VIRTIO_SND_PCM_FMT_S20_3: u8 = 9; -pub const VIRTIO_SND_PCM_FMT_U20_3: u8 = 10; -pub const VIRTIO_SND_PCM_FMT_S24_3: u8 = 11; -pub const VIRTIO_SND_PCM_FMT_U24_3: u8 = 12; -pub const VIRTIO_SND_PCM_FMT_S20: u8 = 13; -pub const VIRTIO_SND_PCM_FMT_U20: u8 = 14; -pub const VIRTIO_SND_PCM_FMT_S24: u8 = 15; -pub const VIRTIO_SND_PCM_FMT_U24: u8 = 16; -pub const VIRTIO_SND_PCM_FMT_S32: u8 = 17; -pub const VIRTIO_SND_PCM_FMT_U32: u8 = 18; -pub const VIRTIO_SND_PCM_FMT_FLOAT: u8 = 19; -pub const VIRTIO_SND_PCM_FMT_FLOAT64: u8 = 20; -// digital formats (width / physical width) -pub const VIRTIO_SND_PCM_FMT_DSD_U8: u8 = 21; -pub const VIRTIO_SND_PCM_FMT_DSD_U16: u8 = 22; -pub const VIRTIO_SND_PCM_FMT_DSD_U32: u8 = 23; -pub const VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME: u8 = 24; -pub(crate) const _VIRTIO_SND_PCM_FMT_MAX: u8 = 25; - -// supported PCM frame rates - -pub const VIRTIO_SND_PCM_RATE_5512: u8 = 0; -pub const VIRTIO_SND_PCM_RATE_8000: u8 = 1; -pub const VIRTIO_SND_PCM_RATE_11025: u8 = 2; -pub const VIRTIO_SND_PCM_RATE_16000: u8 = 3; -pub const VIRTIO_SND_PCM_RATE_22050: u8 = 4; -pub const VIRTIO_SND_PCM_RATE_32000: u8 = 5; -pub const VIRTIO_SND_PCM_RATE_44100: u8 = 6; -pub const VIRTIO_SND_PCM_RATE_48000: u8 = 7; -pub const VIRTIO_SND_PCM_RATE_64000: u8 = 8; -pub const VIRTIO_SND_PCM_RATE_88200: u8 = 9; -pub const VIRTIO_SND_PCM_RATE_96000: u8 = 10; -pub const VIRTIO_SND_PCM_RATE_176400: u8 = 11; -pub const VIRTIO_SND_PCM_RATE_192000: u8 = 12; -pub const VIRTIO_SND_PCM_RATE_384000: u8 = 13; -pub(crate) const _VIRTIO_SND_PCM_RATE_MAX: u8 = 14; - -// standard channel position definition - -pub const VIRTIO_SND_CHMAP_NONE: u8 = 0; /* undefined */ -pub const VIRTIO_SND_CHMAP_NA: u8 = 1; /* silent */ -pub const VIRTIO_SND_CHMAP_MONO: u8 = 2; /* mono stream */ -pub const VIRTIO_SND_CHMAP_FL: u8 = 3; /* front left */ -pub const VIRTIO_SND_CHMAP_FR: u8 = 4; /* front right */ -pub const VIRTIO_SND_CHMAP_RL: u8 = 5; /* rear left */ -pub const VIRTIO_SND_CHMAP_RR: u8 = 6; /* rear right */ -pub const VIRTIO_SND_CHMAP_FC: u8 = 7; /* front center */ -pub const VIRTIO_SND_CHMAP_LFE: u8 = 8; /* low frequency (LFE) */ -pub const VIRTIO_SND_CHMAP_SL: u8 = 9; /* side left */ -pub const VIRTIO_SND_CHMAP_SR: u8 = 10; /* side right */ -pub const VIRTIO_SND_CHMAP_RC: u8 = 11; /* rear center */ -pub const VIRTIO_SND_CHMAP_FLC: u8 = 12; /* front left center */ -pub const VIRTIO_SND_CHMAP_FRC: u8 = 13; /* front right center */ -pub const VIRTIO_SND_CHMAP_RLC: u8 = 14; /* rear left center */ -pub const VIRTIO_SND_CHMAP_RRC: u8 = 15; /* rear right center */ -pub const VIRTIO_SND_CHMAP_FLW: u8 = 16; /* front left wide */ -pub const VIRTIO_SND_CHMAP_FRW: u8 = 17; /* front right wide */ -pub const VIRTIO_SND_CHMAP_FLH: u8 = 18; /* front left high */ -pub const VIRTIO_SND_CHMAP_FCH: u8 = 19; /* front center high */ -pub const VIRTIO_SND_CHMAP_FRH: u8 = 20; /* front right high */ -pub const VIRTIO_SND_CHMAP_TC: u8 = 21; /* top center */ -pub const VIRTIO_SND_CHMAP_TFL: u8 = 22; /* top front left */ -pub const VIRTIO_SND_CHMAP_TFR: u8 = 23; /* top front right */ -pub const VIRTIO_SND_CHMAP_TFC: u8 = 24; /* top front center */ -pub const VIRTIO_SND_CHMAP_TRL: u8 = 25; /* top rear left */ -pub const VIRTIO_SND_CHMAP_TRR: u8 = 26; /* top rear right */ -pub const VIRTIO_SND_CHMAP_TRC: u8 = 27; /* top rear center */ -pub const VIRTIO_SND_CHMAP_TFLC: u8 = 28; /* top front left center */ -pub const VIRTIO_SND_CHMAP_TFRC: u8 = 29; /* top front right center */ -pub const VIRTIO_SND_CHMAP_TSL: u8 = 34; /* top side left */ -pub const VIRTIO_SND_CHMAP_TSR: u8 = 35; /* top side right */ -pub const VIRTIO_SND_CHMAP_LLFE: u8 = 36; /* left LFE */ -pub const VIRTIO_SND_CHMAP_RLFE: u8 = 37; /* right LFE */ -pub const VIRTIO_SND_CHMAP_BC: u8 = 38; /* bottom center */ -pub const VIRTIO_SND_CHMAP_BLC: u8 = 39; /* bottom left center */ -pub const VIRTIO_SND_CHMAP_BRC: u8 = 40; /* bottom right center */ -// maximum possible number of channels -pub const VIRTIO_SND_CHMAP_MAX_SIZE: usize = 18; - -/// Virtio Sound Configuration -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundConfig { - /// total number of all available jacks - pub jacks: Le32, - /// total number of all available PCM streams - pub streams: Le32, - /// total number of all available channel maps - pub chmaps: Le32, -} - -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundConfig {} - -/// Virtio Sound Request / Response common header -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundHeader { - /// request type / response status - pub code: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundHeader {} - -/// Virtio Sound event notification -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundEvent { - /// PCM stream event type - pub hdr: VirtioSoundHeader, - /// PCM stream identifier from 0 to streams - 1 - pub data: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundEvent {} - -/// Virtio Sound request information about any kind of configuration item -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundQueryInfo { - /// item request type (VIRTIO_SND_R_*_INFO) - pub hdr: VirtioSoundHeader, - /// starting identifier for the item - pub start_id: Le32, - /// number of items for which information is requested - pub count: Le32, - /// size of the structure containing information for one item - pub size: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundQueryInfo {} - -/// Virtio Sound response common information header -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundInfo { - /// function group node identifier - pub hda_fn_nid: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundInfo {} - -/// Jack control request / Jack common header -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundJackHeader { - /// jack request type (VIRTIO_SND_R_JACK_*) - pub hdr: VirtioSoundHeader, - /// jack identifier - pub jack_id: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundJackHeader {} - -/// Jack response information about available jacks -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundJackInfo { - /// jack response header type - pub hdr: VirtioSoundInfo, - /// supported feature bit map (VIRTIO_SND_JACK_F_XXX) - pub feature: Le32, - /// pin default configuration value - pub hda_reg_defconf: Le32, - /// pin capabilities value - pub hda_reg_caps: Le32, - /// current jack connection status - pub connected: u8, - pub padding: [u8; 7], -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundJackInfo {} - -///If the VIRTIO_SND_JACK_F_REMAP feature bit is set in the jack information -/// Remap control request -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundJackRemap { - pub hdr: VirtioSoundJackHeader, /* .code = VIRTIO_SND_R_JACK_REMAP */ - pub association: Le32, - pub sequence: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundJackRemap {} - -/// PCM control request / PCM common header -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundPcmHeader { - pub hdr: VirtioSoundHeader, - pub stream_id: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundPcmHeader {} - -/// PCM response information -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundPcmInfo { - pub hdr: VirtioSoundInfo, - pub features: Le32, /* 1 << VIRTIO_SND_PCM_F_XXX */ - pub formats: Le64, /* 1 << VIRTIO_SND_PCM_FMT_XXX */ - pub rates: Le64, /* 1 << VIRTIO_SND_PCM_RATE_XXX */ - pub direction: u8, - pub channels_min: u8, - pub channels_max: u8, - - pub padding: [u8; 5], -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundPcmInfo {} - -/// Set selected stream parameters for the specified stream ID -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSndPcmSetParams { - pub hdr: VirtioSoundPcmHeader, - pub buffer_bytes: Le32, - pub period_bytes: Le32, - pub features: Le32, /* 1 << VIRTIO_SND_PCM_F_XXX */ - pub channels: u8, - pub format: u8, - pub rate: u8, - pub padding: u8, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSndPcmSetParams {} - -/// PCM I/O header -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundPcmXfer { - pub stream_id: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundPcmXfer {} - -/// PCM I/O status -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundPcmStatus { - pub status: Le32, - pub latency_bytes: Le32, -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundPcmStatus {} - -/// channel maps response information -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct VirtioSoundChmapInfo { - pub hdr: VirtioSoundInfo, - pub direction: u8, - pub channels: u8, - pub positions: [u8; VIRTIO_SND_CHMAP_MAX_SIZE], -} -// SAFETY: The layout of the structure is fixed and can be initialized by -// reading its content from byte array. -unsafe impl ByteValued for VirtioSoundChmapInfo {} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn test_virtiosound_structs_debug() { - let val = VirtioSoundConfig::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = - "VirtioSoundConfig { jacks: Le32(0), streams: Le32(0), chmaps: Le32(0) }".to_string(); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundHeader::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = "VirtioSoundHeader { code: Le32(0) }".to_string(); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundEvent::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!("VirtioSoundEvent {{ hdr: {:?}, data: Le32(0) }}", val.hdr); - - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundQueryInfo::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!( - "VirtioSoundQueryInfo {{ hdr: {:?}, start_id: Le32(0), count: Le32(0), size: Le32(0) \ - }}", - val.hdr - ); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundInfo::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = "VirtioSoundInfo { hda_fn_nid: Le32(0) }".to_string(); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundJackHeader::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!( - "VirtioSoundJackHeader {{ hdr: {:?}, jack_id: Le32(0) }}", - val.hdr - ); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundJackInfo::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!( - "VirtioSoundJackInfo {{ hdr: {:?}, feature: Le32(0), hda_reg_defconf: Le32(0), \ - hda_reg_caps: Le32(0), connected: 0, padding: {:?} }}", - val.hdr, val.padding - ); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundJackRemap::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!( - "VirtioSoundJackRemap {{ hdr: {:?}, association: Le32(0), sequence: Le32(0) }}", - val.hdr - ); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundPcmHeader::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!( - "VirtioSoundPcmHeader {{ hdr: {:?}, stream_id: Le32(0) }}", - val.hdr - ); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundPcmInfo::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!( - "VirtioSoundPcmInfo {{ hdr: {:?}, features: Le32(0), formats: Le64(0), rates: \ - Le64(0), direction: 0, channels_min: 0, channels_max: 0, padding: {:?} }}", - val.hdr, val.padding - ); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSndPcmSetParams::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!( - "VirtioSndPcmSetParams {{ hdr: {:?}, buffer_bytes: Le32(0), period_bytes: Le32(0), \ - features: Le32(0), channels: 0, format: 0, rate: 0, padding: 0 }}", - val.hdr - ); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundPcmXfer::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = "VirtioSoundPcmXfer { stream_id: Le32(0) }".to_string(); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundPcmStatus::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = - "VirtioSoundPcmStatus { status: Le32(0), latency_bytes: Le32(0) }".to_string(); - assert_eq!(debug_output, expected_debug); - - let val = VirtioSoundChmapInfo::default(); - - let debug_output = format!("{:?}", val); - let expected_debug = format!( - "VirtioSoundChmapInfo {{ hdr: {:?}, direction: 0, channels: 0, positions: {:?} }}", - val.hdr, val.positions - ); - assert_eq!(debug_output, expected_debug); - } - #[test] - fn test_virtiosound_structs_clone() { - let val = VirtioSoundConfig::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundHeader::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundEvent::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundQueryInfo::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundInfo::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundJackHeader::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundJackInfo::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundJackRemap::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundPcmHeader::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundPcmInfo::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSndPcmSetParams::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundPcmXfer::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundPcmStatus::default(); - assert_eq!(val, val.clone()); - - let val = VirtioSoundChmapInfo::default(); - assert_eq!(val, val.clone()); - } -} diff --git a/src/devices/src/virtio/snd/worker.rs b/src/devices/src/virtio/snd/worker.rs deleted file mode 100644 index d19c98b00..000000000 --- a/src/devices/src/virtio/snd/worker.rs +++ /dev/null @@ -1,648 +0,0 @@ -use std::collections::BTreeSet; -use std::mem::size_of; -use std::os::fd::AsRawFd; -use std::sync::{Arc, Mutex, RwLock}; -use std::{result, thread}; - -use utils::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; -use utils::eventfd::EventFd; -use vm_memory::{ByteValued, Bytes, GuestMemoryMmap}; - -use super::super::DeviceQueue; -use super::audio_backends::{alloc_audio_backend, AudioBackend}; -use super::defs::{CTL_INDEX, EVT_INDEX, QUEUE_INDEXES, RXQ_INDEX, TXQ_INDEX}; -use super::stream::{Error as StreamError, Stream}; -use super::virtio_sound::{ - VirtioSndPcmSetParams, VirtioSoundHeader, VirtioSoundPcmHeader, VirtioSoundPcmInfo, - VirtioSoundPcmStatus, VirtioSoundPcmXfer, VirtioSoundQueryInfo, VIRTIO_SND_D_INPUT, - VIRTIO_SND_D_OUTPUT, VIRTIO_SND_S_BAD_MSG, VIRTIO_SND_S_IO_ERR, VIRTIO_SND_S_NOT_SUPP, - VIRTIO_SND_S_OK, -}; -use super::{ - BackendType, Direction, Error, VirtioSoundChmapInfo, VirtioSoundJackInfo, Vring, - VIRTIO_SND_CHMAP_FL, VIRTIO_SND_CHMAP_FR, VIRTIO_SND_CHMAP_MAX_SIZE, VIRTIO_SND_CHMAP_NONE, -}; -use crate::virtio::snd::stream::Buffer; -use crate::virtio::snd::{ControlMessageKind, IOMessage}; -use crate::virtio::{DescriptorChain, InterruptTransport}; - -pub struct SndWorker { - vrings: Vec>>, - queue_events: Vec>, - interrupt: InterruptTransport, - mem: GuestMemoryMmap, - streams: Arc>>, - streams_no: usize, - chmaps: Arc>>, - jacks: Arc>>, - audio_backend: RwLock>, - stop_fd: EventFd, -} - -impl SndWorker { - pub fn new( - queues: Vec, - interrupt: InterruptTransport, - mem: GuestMemoryMmap, - stop_fd: EventFd, - ) -> Self { - let streams = vec![ - Stream { - id: 0, - direction: Direction::Output, - ..Stream::default() - }, - Stream { - id: 1, - direction: Direction::Input, - ..Stream::default() - }, - ]; - let streams_no = streams.len(); - let streams = Arc::new(RwLock::new(streams)); - let jacks: Arc>> = Arc::new(RwLock::new(Vec::new())); - let mut positions = [VIRTIO_SND_CHMAP_NONE; VIRTIO_SND_CHMAP_MAX_SIZE]; - positions[0] = VIRTIO_SND_CHMAP_FL; - positions[1] = VIRTIO_SND_CHMAP_FR; - let chmaps_info: Vec = vec![ - VirtioSoundChmapInfo { - direction: VIRTIO_SND_D_OUTPUT, - channels: 2, - positions, - ..VirtioSoundChmapInfo::default() - }, - VirtioSoundChmapInfo { - direction: VIRTIO_SND_D_INPUT, - channels: 2, - positions, - ..VirtioSoundChmapInfo::default() - }, - ]; - let chmaps: Arc>> = Arc::new(RwLock::new(chmaps_info)); - - let audio_backend = - RwLock::new(alloc_audio_backend(BackendType::Pipewire, streams.clone()).unwrap()); - - let mut vrings: Vec>> = Vec::new(); - let mut queue_events: Vec> = Vec::new(); - - for dq in queues { - vrings.push(Arc::new(Mutex::new(Vring { - mem: mem.clone(), - queue: dq.queue, - interrupt: interrupt.clone(), - }))); - queue_events.push(dq.event); - } - - Self { - vrings, - queue_events, - interrupt, - mem, - streams, - streams_no, - jacks, - chmaps, - audio_backend, - stop_fd, - } - } - - pub fn run(self) -> thread::JoinHandle<()> { - thread::Builder::new() - .name("virtio-snd worker".into()) - .spawn(|| self.work()) - .unwrap() - } - - fn work(mut self) { - let mut epoll = Epoll::new().unwrap(); - - for idx in QUEUE_INDEXES { - let fd = self.queue_events[idx].as_raw_fd(); - epoll - .ctl( - ControlOperation::Add, - fd, - &EpollEvent::new(EventSet::IN, idx as u64), - ) - .unwrap(); - } - - let stop_ev_fd = self.stop_fd.as_raw_fd(); - epoll - .ctl( - ControlOperation::Add, - stop_ev_fd, - &EpollEvent::new(EventSet::IN, stop_ev_fd as u64), - ) - .unwrap(); - - let mut epoll_events = vec![EpollEvent::new(EventSet::empty(), 0); 32]; - loop { - match epoll.wait(epoll_events.len(), -1, epoll_events.as_mut_slice()) { - Ok(ev_cnt) => { - for event in &epoll_events[0..ev_cnt] { - let source = event.fd(); - let data = event.data(); - let event_set = event.event_set(); - match event_set { - EventSet::IN if data < QUEUE_INDEXES.len() as u64 => { - self.handle_event(data.try_into().unwrap()); - } - EventSet::IN if source == stop_ev_fd => { - debug!("stopping worker thread"); - let _ = self.stop_fd.read(); - return; - } - _ => { - log::warn!( - "Received unknown event: {event_set:?} from fd: {source:?}" - ); - } - } - } - } - Err(e) => { - debug!("failed to consume muxer epoll event: {e}"); - } - } - } - } - - fn handle_event(&mut self, queue_index: usize) { - debug!("Fs: queue event: {queue_index}"); - if let Err(e) = self.queue_events[queue_index].read() { - error!("Failed to get queue event: {e:?}"); - } - - let vring_lock = &self.vrings[queue_index]; - - loop { - vring_lock - .lock() - .unwrap() - .queue - .disable_notification(&self.mem) - .unwrap(); - - self.process_queue(vring_lock, queue_index); - - if !vring_lock - .lock() - .unwrap() - .queue - .enable_notification(&self.mem) - .unwrap() - { - break; - } - } - } - - pub fn process_queue(&self, vring_lock: &Arc>, queue_index: usize) { - debug!("snd: process_queue()"); - - loop { - let mut vring = vring_lock.lock().unwrap(); - let head = vring.queue.pop(&self.mem); - drop(vring); - - if let Some(head) = head { - let ret = match queue_index { - CTL_INDEX => self.process_ctl(vring_lock, head), - EVT_INDEX => self.process_evt(vring_lock, head), - RXQ_INDEX => self.process_io(vring_lock, head, Direction::Input), - TXQ_INDEX => self.process_io(vring_lock, head, Direction::Output), - _ => unreachable!(), - }; - if let Err(err) = ret { - error!("error processing queue {queue_index}: {err}"); - } - - if vring_lock - .lock() - .unwrap() - .queue - .needs_notification(&self.mem) - .unwrap() - { - self.interrupt.signal_used_queue(); - } - } else { - break; - } - } - } - - fn process_ctl( - &self, - vring_lock: &Arc>, - head: DescriptorChain, - ) -> result::Result<(), Error> { - let descriptors: Vec<_> = head.clone().into_iter().collect(); - if descriptors.len() < 2 { - return Err(Error::UnexpectedDescriptorCount(descriptors.len())); - } - - // Request descriptor. - let desc_request = &descriptors[0]; - if desc_request.is_write_only() { - return Err(Error::UnexpectedWriteOnlyDescriptor(0)); - } - - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - - // Keep track of bytes that will be written in the VQ. - let mut used_len = 0; - - // Reply header descriptor. - let desc_hdr = &descriptors[1]; - if !desc_hdr.is_write_only() { - return Err(Error::UnexpectedReadableDescriptor(1)); - } - - let mut resp = VirtioSoundHeader { - code: VIRTIO_SND_S_OK.into(), - }; - - let code = ControlMessageKind::try_from(request.code.to_native()).unwrap(); - match code { - ControlMessageKind::ChmapInfo => { - if descriptors.len() != 3 { - log::error!("a CHMAP_INFO request should have three descriptors total."); - return Err(Error::UnexpectedDescriptorCount(descriptors.len())); - } else if !descriptors[2].is_write_only() { - log::error!( - "a CHMAP_INFO request should have a writeable descriptor for the info \ - payload response after the header status response" - ); - return Err(Error::UnexpectedReadableDescriptor(2)); - } - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - let start_id = u32::from(request.start_id) as usize; - let count = u32::from(request.count) as usize; - let chmaps = self.chmaps.read().unwrap(); - if chmaps.len() <= start_id || chmaps.len() < start_id + count { - resp.code = VIRTIO_SND_S_BAD_MSG.into(); - } else { - let desc_response = &descriptors[2]; - let mut buf = vec![]; - - for i in chmaps.iter().skip(start_id).take(count) { - buf.extend_from_slice(i.as_slice()); - } - drop(chmaps); - self.mem - .write_slice(&buf, desc_response.addr) - .map_err(|_| Error::DescriptorWriteFailed)?; - used_len += desc_response.len; - } - } - ControlMessageKind::JackInfo => { - if descriptors.len() != 3 { - log::error!("a JACK_INFO request should have three descriptors total."); - return Err(Error::UnexpectedDescriptorCount(descriptors.len())); - } else if !descriptors[2].is_write_only() { - log::error!( - "a JACK_INFO request should have a writeable descriptor for the info \ - payload response after the header status response" - ); - return Err(Error::UnexpectedReadableDescriptor(2)); - } - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - - let start_id = u32::from(request.start_id) as usize; - let count = u32::from(request.count) as usize; - let jacks = self.jacks.read().unwrap(); - if jacks.len() <= start_id || jacks.len() < start_id + count { - resp.code = VIRTIO_SND_S_BAD_MSG.into(); - } else { - let desc_response = &descriptors[2]; - let mut buf = vec![]; - - for i in jacks.iter().skip(start_id).take(count) { - buf.extend_from_slice(i.as_slice()); - } - drop(jacks); - self.mem - .write_slice(&buf, desc_response.addr) - .map_err(|_| Error::DescriptorWriteFailed)?; - used_len += desc_response.len; - } - } - ControlMessageKind::JackRemap => { - resp.code = VIRTIO_SND_S_NOT_SUPP.into(); - } - ControlMessageKind::PcmInfo => { - if descriptors.len() != 3 { - log::error!("a PCM_INFO request should have three descriptors total."); - return Err(Error::UnexpectedDescriptorCount(descriptors.len())); - } else if !descriptors[2].is_write_only() { - log::error!( - "a PCM_INFO request should have a writeable descriptor for the info \ - payload response after the header status response" - ); - return Err(Error::UnexpectedReadableDescriptor(2)); - } - - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - - let start_id = u32::from(request.start_id) as usize; - let count = u32::from(request.count) as usize; - let streams = self.streams.read().unwrap(); - if streams.len() <= start_id || streams.len() < start_id + count { - resp.code = VIRTIO_SND_S_BAD_MSG.into(); - } else { - let desc_response = &descriptors[2]; - - let mut buf = vec![]; - let mut p: VirtioSoundPcmInfo; - - for s in streams - .iter() - .skip(u32::from(request.start_id) as usize) - .take(u32::from(request.count) as usize) - { - p = VirtioSoundPcmInfo::default(); - p.hdr.hda_fn_nid = 0.into(); - p.features = s.params.features; - p.formats = s.formats; - p.rates = s.rates; - p.direction = s.direction as u8; - p.channels_min = s.channels_min; - p.channels_max = s.channels_max; - buf.extend_from_slice(p.as_slice()); - } - drop(streams); - self.mem - .write_slice(&buf, desc_response.addr) - .map_err(|_| Error::DescriptorWriteFailed)?; - used_len += desc_response.len; - } - } - ControlMessageKind::PcmSetParams => { - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - let stream_id: u32 = request.hdr.stream_id.into(); - - if stream_id as usize >= self.streams_no { - log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); - resp.code = VIRTIO_SND_S_BAD_MSG.into(); - } else if let Err(err) = self - .audio_backend - .read() - .unwrap() - .set_parameters(stream_id, request) - { - match err { - Error::Stream(_) | Error::StreamWithIdNotFound(_) => { - resp.code = VIRTIO_SND_S_BAD_MSG.into() - } - Error::UnexpectedAudioBackendConfiguration => { - resp.code = VIRTIO_SND_S_NOT_SUPP.into() - } - _ => { - log::error!("{err}"); - resp.code = VIRTIO_SND_S_IO_ERR.into() - } - } - } - } - ControlMessageKind::PcmPrepare => { - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - let stream_id = request.stream_id.into(); - - if stream_id as usize >= self.streams_no { - log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); - resp.code = VIRTIO_SND_S_BAD_MSG.into(); - } else { - self.audio_backend - .write() - .unwrap() - .prepare(stream_id) - .unwrap(); - } - } - ControlMessageKind::PcmRelease => { - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - let stream_id = request.stream_id.into(); - - if stream_id as usize >= self.streams_no { - log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); - resp.code = VIRTIO_SND_S_BAD_MSG.into(); - } else if let Err(err) = self.audio_backend.write().unwrap().release(stream_id) { - match err { - Error::Stream(_) | Error::StreamWithIdNotFound(_) => { - resp.code = VIRTIO_SND_S_BAD_MSG.into() - } - _ => { - log::error!("{err}"); - resp.code = VIRTIO_SND_S_IO_ERR.into() - } - } - } - } - ControlMessageKind::PcmStart => { - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - let stream_id = request.stream_id.into(); - - if stream_id as usize >= self.streams_no { - log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); - resp.code = VIRTIO_SND_S_BAD_MSG.into(); - } else { - self.audio_backend - .write() - .unwrap() - .start(stream_id) - .unwrap(); - } - } - ControlMessageKind::PcmStop => { - let request = self - .mem - .read_obj::(desc_request.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - let stream_id = request.stream_id.into(); - - if stream_id as usize >= self.streams_no { - log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); - resp.code = VIRTIO_SND_S_BAD_MSG.into(); - } else { - self.audio_backend.write().unwrap().stop(stream_id).unwrap(); - } - } - } - debug!( - "returned {} for ctrl msg {:?}", - match u32::from(resp.code) { - v if v == VIRTIO_SND_S_OK => "OK", - v if v == VIRTIO_SND_S_BAD_MSG => "BAD_MSG", - v if v == VIRTIO_SND_S_NOT_SUPP => "NOT_SUPP", - v if v == VIRTIO_SND_S_IO_ERR => "IO_ERR", - _ => unreachable!(), - }, - code - ); - - self.mem.write_obj(resp, desc_hdr.addr).unwrap(); - if let Err(err) = vring_lock - .lock() - .unwrap() - .queue - .add_used(&self.mem, head.index, used_len) - { - error!("Error adding used descriptors to the queue: {err}"); - } - - Ok(()) - } - - fn process_evt( - &self, - _vring_lock: &Arc>, - _head: DescriptorChain, - ) -> result::Result<(), Error> { - error!("virtio_snd: unimplemented process_evt"); - Ok(()) - } - - fn process_io( - &self, - vring_lock: &Arc>, - desc_chain: DescriptorChain, - direction: Direction, - ) -> result::Result<(), Error> { - #[derive(Copy, Clone, PartialEq, Debug)] - enum IoState { - Ready, - WaitingBufferForStreamId(u32), - Done, - } - - let mut stream_ids = BTreeSet::default(); - - let mut state = IoState::Ready; - let mut buffers: Vec = vec![]; - - let descriptors: Vec<_> = desc_chain.clone().into_iter().collect(); - let message = Arc::new(IOMessage { - status: VIRTIO_SND_S_OK.into(), - used_len: 0.into(), - latency_bytes: 0.into(), - head_index: desc_chain.index, - response_descriptor: descriptors - .last() - .ok_or_else(|| { - log::error!("Received IO request with an empty descriptor chain."); - Error::UnexpectedDescriptorCount(0) - })? - .descriptor(), - vring: vring_lock.clone(), - }); - - for descriptor in &descriptors { - match state { - IoState::Done => { - return Err(Error::UnexpectedDescriptorCount(descriptors.len())); - } - IoState::Ready - if matches!(direction, Direction::Output) && descriptor.is_write_only() => - { - if descriptor.len as usize != size_of::() { - return Err(Error::UnexpectedDescriptorSize( - size_of::(), - descriptor.len, - )); - } - state = IoState::Done; - } - IoState::WaitingBufferForStreamId(stream_id) - if descriptor.len as usize == size_of::() => - { - self.streams.write().unwrap()[stream_id as usize] - .buffers - .extend(std::mem::take(&mut buffers)); - state = IoState::Done; - } - IoState::Ready if descriptor.len as usize != size_of::() => { - return Err(Error::UnexpectedDescriptorSize( - size_of::(), - descriptor.len, - )); - } - IoState::Ready => { - let xfer = self - .mem - .read_obj::(descriptor.addr) - .map_err(|_| Error::DescriptorReadFailed)?; - let stream_id: u32 = xfer.stream_id.into(); - stream_ids.insert(stream_id); - - state = IoState::WaitingBufferForStreamId(stream_id); - } - IoState::WaitingBufferForStreamId(stream_id) - if descriptor.len as usize == size_of::() => - { - return Err(Error::UnexpectedDescriptorSize( - u32::from( - self.streams.read().unwrap()[stream_id as usize] - .params - .period_bytes, - ) as usize, - descriptor.len, - )); - } - IoState::WaitingBufferForStreamId(_) => { - // In the case of TX/Playback: - // - // Rather than copying the content of a descriptor, buffer keeps a pointer - // to it. When we copy just after the request is enqueued, the guest's - // userspace may or may not have updated the buffer contents. Guest driver - // simply moves buffers from the used ring to the available ring without - // knowing whether the content has been updated. The device only reads the - // buffer from guest memory when the audio engine requires it, which is - // about after a period thus ensuring that the buffer is up-to-date. - buffers.push(Buffer::new( - descriptor.descriptor(), - Arc::clone(&message), - direction, - )); - } - } - } - - if !stream_ids.is_empty() { - let b = self.audio_backend.read().unwrap(); - for id in stream_ids { - b.write(id).unwrap(); - } - } - - Ok(()) - } -} diff --git a/src/libkrun/Cargo.toml b/src/libkrun/Cargo.toml index 24db7a9ff..4e54bf99c 100644 --- a/src/libkrun/Cargo.toml +++ b/src/libkrun/Cargo.toml @@ -15,7 +15,6 @@ tdx = ["blk", "tee", "vmm/tdx", "devices/tdx"] net = ["devices/net", "vmm/net"] blk = ["devices/blk", "vmm/blk"] gpu = ["vmm/gpu", "devices/gpu", "krun_display"] -snd = ["vmm/snd", "devices/snd"] input = ["krun_input", "vmm/input", "devices/input"] virgl_resource_map2 = ["devices/virgl_resource_map2"] aws-nitro = ["vmm/aws-nitro", "devices/aws-nitro", "dep:aws-nitro", "dep:nitro-enclaves"] diff --git a/src/libkrun/src/lib.rs b/src/libkrun/src/lib.rs index 1d8b3fcb1..a7b7eee6a 100644 --- a/src/libkrun/src/lib.rs +++ b/src/libkrun/src/lib.rs @@ -163,7 +163,6 @@ struct ContextConfig { shutdown_efd: Option, gpu_virgl_flags: Option, gpu_shm_size: Option, - enable_snd: bool, console_output: Option, vmm_uid: Option, vmm_gid: Option, @@ -1787,20 +1786,6 @@ pub extern "C" fn krun_display_set_dpi(_ctx_id: u32, _display_id: u32, _dpi: u32 -libc::ENOTSUP } -#[allow(clippy::missing_safety_doc)] -#[no_mangle] -pub unsafe extern "C" fn krun_set_snd_device(ctx_id: u32, enable: bool) -> i32 { - match CTX_MAP.lock().unwrap().entry(ctx_id) { - Entry::Occupied(mut ctx_cfg) => { - let cfg = ctx_cfg.get_mut(); - cfg.enable_snd = enable; - } - Entry::Vacant(_) => return -libc::ENOENT, - } - - KRUN_SUCCESS -} - #[allow(clippy::missing_safety_doc)] #[no_mangle] #[cfg(feature = "vhost-user")] @@ -1974,7 +1959,6 @@ pub unsafe extern "C" fn krun_check_nested_virt() -> i32 { const KRUN_FEATURE_NET: u64 = 0; const KRUN_FEATURE_BLK: u64 = 1; const KRUN_FEATURE_GPU: u64 = 2; -const KRUN_FEATURE_SND: u64 = 3; const KRUN_FEATURE_INPUT: u64 = 4; const KRUN_FEATURE_TEE: u64 = 6; const KRUN_FEATURE_AMD_SEV: u64 = 7; @@ -1988,7 +1972,6 @@ pub extern "C" fn krun_has_feature(feature: u64) -> c_int { KRUN_FEATURE_NET => cfg!(feature = "net"), KRUN_FEATURE_BLK => cfg!(feature = "blk"), KRUN_FEATURE_GPU => cfg!(feature = "gpu"), - KRUN_FEATURE_SND => cfg!(feature = "snd"), KRUN_FEATURE_INPUT => cfg!(feature = "input"), KRUN_FEATURE_TEE => cfg!(feature = "tee"), KRUN_FEATURE_AMD_SEV => cfg!(feature = "amd-sev"), @@ -2793,9 +2776,6 @@ pub extern "C" fn krun_start_enter(ctx_id: u32) -> i32 { ctx_cfg.vmr.set_gpu_shm_size(shm_size); } - #[cfg(feature = "snd")] - ctx_cfg.vmr.set_snd_device(ctx_cfg.enable_snd); - if let Some(console_output) = ctx_cfg.console_output { ctx_cfg.vmr.set_console_output(console_output); } diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index 2385bc343..377f9c731 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -15,7 +15,6 @@ tdx = ["blk", "tee", "kbs-types", "serde", "serde_json", "dep:tdx", "devices/tdx net = ["devices/net"] blk = ["devices/blk"] gpu = ["devices/gpu", "krun_display"] -snd = ["devices/snd"] input = ["devices/input", "krun_input"] aws-nitro = [] vhost-user = ["devices/vhost-user"] diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 1aa9c5c48..b92b931d4 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -207,8 +207,6 @@ pub enum StartMicrovmError { RegisterNetDevice(device_manager::mmio::Error), /// Cannot initialize a MMIO Rng device or add a device to the MMIO Bus. RegisterRngDevice(device_manager::mmio::Error), - /// Cannot initialize a MMIO Snd device or add a device to the MMIO Bus. - RegisterSndDevice(device_manager::mmio::Error), /// Cannot initialize a vhost-user device or add a device to the MMIO Bus. RegisterVhostUserDevice(device_manager::mmio::Error), /// Cannot initialize a MMIO Vsock Device or add a device to the MMIO Bus. @@ -455,14 +453,6 @@ impl Display for StartMicrovmError { "Cannot initialize a MMIO Rng Device or add a device to the MMIO Bus. {err_msg}" ) } - RegisterSndDevice(ref err) => { - let mut err_msg = format!("{err}"); - err_msg = err_msg.replace('\"', ""); - write!( - f, - "Cannot initialize a MMIO Snd Device or add a device to the MMIO Bus. {err_msg}" - ) - } RegisterVhostUserDevice(ref err) => { let mut err_msg = err.to_string(); err_msg = err_msg.replace('\"', ""); @@ -1094,11 +1084,6 @@ pub fn build_microvm( vmm.kernel_cmdline.insert_str("KRUN_DHCP=1")?; } - #[cfg(feature = "snd")] - if vm_resources.snd_device { - attach_snd_device(&mut vmm, intc.clone())?; - } - if let Some(s) = &vm_resources.kernel_cmdline.epilog { vmm.kernel_cmdline.insert_str(s).unwrap(); }; @@ -2520,19 +2505,6 @@ fn attach_input_devices( Ok(()) } -#[cfg(feature = "snd")] -fn attach_snd_device(vmm: &mut Vmm, intc: IrqChip) -> std::result::Result<(), StartMicrovmError> { - use self::StartMicrovmError::*; - - let snd = Arc::new(Mutex::new(devices::virtio::Snd::new().unwrap())); - let id = String::from(snd.lock().unwrap().id()); - - // The device mutex mustn't be locked here otherwise it will deadlock. - attach_mmio_device(vmm, id, intc, snd).map_err(RegisterSndDevice)?; - - Ok(()) -} - #[cfg(test)] pub mod tests { use super::*; diff --git a/src/vmm/src/resources.rs b/src/vmm/src/resources.rs index 62756ca1c..ffa9e6eac 100644 --- a/src/vmm/src/resources.rs +++ b/src/vmm/src/resources.rs @@ -186,9 +186,6 @@ pub struct VmResources { krun_input::InputConfigBackend<'static>, krun_input::InputEventProviderBackend<'static>, )>, - #[cfg(feature = "snd")] - /// Enable the virtio-snd device. - pub snd_device: bool, #[cfg(feature = "vhost-user")] /// Vhost-user device configurations pub vhost_user_devices: Vec, @@ -361,11 +358,6 @@ impl VmResources { self.gpu_shm_size = Some(shm_size); } - #[cfg(feature = "snd")] - pub fn set_snd_device(&mut self, enabled: bool) { - self.snd_device = enabled; - } - pub fn set_console_output(&mut self, console_output: PathBuf) { self.console_output = Some(console_output); } @@ -446,8 +438,6 @@ mod tests { displays: Vec::new(), #[cfg(feature = "input")] input_backends: Vec::new(), - #[cfg(feature = "snd")] - snd_device: false, #[cfg(feature = "vhost-user")] vhost_user_devices: Vec::new(), console_output: None,