From 500a78cd6862c4b17e38795fecfc12749bacbaad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dav=C3=AD=C3=B0=20Steinn=20Geirsson?= Date: Tue, 17 Mar 2026 19:20:55 +0000 Subject: [PATCH] sound: dynamically construct streams/chmaps based on --streams flag Also fixes existing bug where chmaps count was hard-coded to 1 despite 2 chmap entries being created. Co-Authored-By: Claude Opus 4.6 (1M context) --- vhost-device-sound/src/device.rs | 97 ++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 22 deletions(-) diff --git a/vhost-device-sound/src/device.rs b/vhost-device-sound/src/device.rs index 915661a..90dc97f 100644 --- a/vhost-device-sound/src/device.rs +++ b/vhost-device-sound/src/device.rs @@ -493,39 +493,50 @@ pub struct VhostUserSoundBackend { impl VhostUserSoundBackend { pub fn new(config: SoundConfig) -> Result { - 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 streams = Vec::new(); + let mut chmaps_info: Vec = 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 { + + if config.has_output() { + streams.push(Stream { + id: streams.len(), + direction: Direction::Output, + ..Stream::default() + }); + chmaps_info.push(VirtioSoundChmapInfo { direction: VIRTIO_SND_D_OUTPUT, channels: 2, positions, ..VirtioSoundChmapInfo::default() - }, - VirtioSoundChmapInfo { + }); + } + + if config.has_input() { + streams.push(Stream { + id: streams.len(), + direction: Direction::Input, + ..Stream::default() + }); + chmaps_info.push(VirtioSoundChmapInfo { direction: VIRTIO_SND_D_INPUT, channels: 2, positions, ..VirtioSoundChmapInfo::default() - }, - ]; + }); + } + + let chmaps_no = chmaps_info.len(); + let streams_no = streams.len(); + let streams = Arc::new(RwLock::new(streams)); + let jacks: Arc>> = Arc::new(RwLock::new(Vec::new())); let chmaps: Arc>> = Arc::new(RwLock::new(chmaps_info)); + + if streams_no == 0 { + return Err(Error::UnexpectedAudioBackendConfiguration); + } log::trace!("VhostUserSoundBackend::new(config = {:?})", &config); let threads = if config.multi_thread { vec![ @@ -576,7 +587,7 @@ impl VhostUserSoundBackend { virtio_cfg: VirtioSoundConfig { jacks: 0.into(), streams: Le32::from(streams_no as u32), - chmaps: 1.into(), + chmaps: Le32::from(chmaps_no as u32), controls: 0.into(), }, exit_consumer, @@ -1066,4 +1077,46 @@ mod tests { test_dir.close().unwrap(); } + + #[test] + fn test_sound_backend_output_only() { + crate::init_logger(); + let config = SoundConfig::new(false, BackendType::Null, true, false); + let backend = VhostUserSoundBackend::new(config).expect("Could not create backend."); + + // VirtioSoundConfig: jacks(4) + streams(4) + chmaps(4) + controls(4) = 16 bytes + let cfg = backend.get_config(0, 16); + assert_eq!(cfg.len(), 16); + // streams is at offset 4, little-endian u32 + let streams = u32::from_le_bytes([cfg[4], cfg[5], cfg[6], cfg[7]]); + let chmaps = u32::from_le_bytes([cfg[8], cfg[9], cfg[10], cfg[11]]); + assert_eq!(streams, 1); + assert_eq!(chmaps, 1); + } + + #[test] + fn test_sound_backend_input_only() { + crate::init_logger(); + let config = SoundConfig::new(false, BackendType::Null, false, true); + let backend = VhostUserSoundBackend::new(config).expect("Could not create backend."); + + let cfg = backend.get_config(0, 16); + let streams = u32::from_le_bytes([cfg[4], cfg[5], cfg[6], cfg[7]]); + let chmaps = u32::from_le_bytes([cfg[8], cfg[9], cfg[10], cfg[11]]); + assert_eq!(streams, 1); + assert_eq!(chmaps, 1); + } + + #[test] + fn test_sound_backend_both_streams() { + crate::init_logger(); + let config = SoundConfig::new(false, BackendType::Null, true, true); + let backend = VhostUserSoundBackend::new(config).expect("Could not create backend."); + + let cfg = backend.get_config(0, 16); + let streams = u32::from_le_bytes([cfg[4], cfg[5], cfg[6], cfg[7]]); + let chmaps = u32::from_le_bytes([cfg[8], cfg[9], cfg[10], cfg[11]]); + assert_eq!(streams, 2); + assert_eq!(chmaps, 2); // Also verifies the chmaps bug fix (was hard-coded to 1) + } }