1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::num::ParseIntError; 6 use std::str::ParseBoolError; 7 8 use audio_streams::StreamEffect; 9 #[cfg(all(unix, feature = "audio_cras"))] 10 use libcras::CrasClientType; 11 #[cfg(all(unix, feature = "audio_cras"))] 12 use libcras::CrasSocketType; 13 #[cfg(all(unix, feature = "audio_cras"))] 14 use libcras::CrasStreamType; 15 use serde::Deserialize; 16 use serde::Serialize; 17 use serde_keyvalue::FromKeyValues; 18 use thiserror::Error as ThisError; 19 20 use crate::virtio::snd::constants::*; 21 use crate::virtio::snd::layout::*; 22 use crate::virtio::snd::sys::StreamSourceBackend as SysStreamSourceBackend; 23 24 #[derive(ThisError, Debug, PartialEq, Eq)] 25 pub enum Error { 26 /// Unknown snd parameter value. 27 #[error("Invalid snd parameter value ({0}): {1}")] 28 InvalidParameterValue(String, String), 29 /// Failed to parse bool value. 30 #[error("Invalid bool value: {0}")] 31 InvalidBoolValue(ParseBoolError), 32 /// Failed to parse int value. 33 #[error("Invalid int value: {0}")] 34 InvalidIntValue(ParseIntError), 35 // Invalid backend. 36 #[error("Backend is not implemented")] 37 InvalidBackend, 38 /// Failed to parse parameters. 39 #[error("Invalid snd parameter: {0}")] 40 UnknownParameter(String), 41 /// Invalid PCM device config index. Happens when the length of PCM device config is less than 42 /// the number of PCM devices. 43 #[error("Invalid PCM device config index: {0}")] 44 InvalidPCMDeviceConfigIndex(usize), 45 /// Invalid PCM info direction (VIRTIO_SND_D_OUTPUT = 0, VIRTIO_SND_D_INPUT = 1) 46 #[error("Invalid PCM Info direction: {0}")] 47 InvalidPCMInfoDirection(u8), 48 } 49 50 #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] 51 #[serde(into = "String", try_from = "&str")] 52 pub enum StreamSourceBackend { 53 NULL, 54 FILE, 55 Sys(SysStreamSourceBackend), 56 } 57 58 // Implemented to make backend serialization possible, since we deserialize from str. 59 impl From<StreamSourceBackend> for String { from(backend: StreamSourceBackend) -> Self60 fn from(backend: StreamSourceBackend) -> Self { 61 match backend { 62 StreamSourceBackend::NULL => "null".to_owned(), 63 StreamSourceBackend::FILE => "file".to_owned(), 64 StreamSourceBackend::Sys(sys_backend) => sys_backend.into(), 65 } 66 } 67 } 68 69 impl TryFrom<&str> for StreamSourceBackend { 70 type Error = Error; 71 try_from(s: &str) -> Result<Self, Self::Error>72 fn try_from(s: &str) -> Result<Self, Self::Error> { 73 match s { 74 "null" => Ok(StreamSourceBackend::NULL), 75 "file" => Ok(StreamSourceBackend::FILE), 76 _ => SysStreamSourceBackend::try_from(s).map(StreamSourceBackend::Sys), 77 } 78 } 79 } 80 81 /// Holds the parameters for each PCM device 82 #[derive(Debug, Clone, Default, Deserialize, Serialize, FromKeyValues, PartialEq, Eq)] 83 #[serde(deny_unknown_fields, default)] 84 pub struct PCMDeviceParameters { 85 #[cfg(all(unix, feature = "audio_cras"))] 86 pub client_type: Option<CrasClientType>, 87 #[cfg(all(unix, feature = "audio_cras"))] 88 pub stream_type: Option<CrasStreamType>, 89 pub effects: Option<Vec<StreamEffect>>, 90 } 91 92 /// Holds the parameters for a cras sound device 93 #[derive(Debug, Clone, Deserialize, Serialize, FromKeyValues)] 94 #[serde(deny_unknown_fields, default)] 95 pub struct Parameters { 96 pub capture: bool, 97 pub num_output_devices: u32, 98 pub num_input_devices: u32, 99 pub backend: StreamSourceBackend, 100 pub num_output_streams: u32, 101 pub num_input_streams: u32, 102 pub playback_path: String, 103 pub playback_size: usize, 104 #[cfg(all(unix, feature = "audio_cras"))] 105 #[serde(deserialize_with = "libcras::deserialize_cras_client_type")] 106 pub client_type: CrasClientType, 107 #[cfg(all(unix, feature = "audio_cras"))] 108 pub socket_type: CrasSocketType, 109 pub output_device_config: Vec<PCMDeviceParameters>, 110 pub input_device_config: Vec<PCMDeviceParameters>, 111 } 112 113 impl Default for Parameters { default() -> Self114 fn default() -> Self { 115 Parameters { 116 capture: false, 117 num_output_devices: 1, 118 num_input_devices: 1, 119 backend: StreamSourceBackend::NULL, 120 num_output_streams: 1, 121 num_input_streams: 1, 122 playback_path: "".to_string(), 123 playback_size: 0, 124 #[cfg(all(unix, feature = "audio_cras"))] 125 client_type: CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 126 #[cfg(all(unix, feature = "audio_cras"))] 127 socket_type: CrasSocketType::Unified, 128 output_device_config: vec![], 129 input_device_config: vec![], 130 } 131 } 132 } 133 134 impl Parameters { get_total_output_streams(&self) -> u32135 pub(crate) fn get_total_output_streams(&self) -> u32 { 136 self.num_output_devices * self.num_output_streams 137 } 138 get_total_input_streams(&self) -> u32139 pub(crate) fn get_total_input_streams(&self) -> u32 { 140 self.num_input_devices * self.num_input_streams 141 } 142 get_total_streams(&self) -> u32143 pub(crate) fn get_total_streams(&self) -> u32 { 144 self.get_total_output_streams() + self.get_total_input_streams() 145 } 146 147 #[allow(dead_code)] get_device_params( &self, pcm_info: &virtio_snd_pcm_info, ) -> Result<PCMDeviceParameters, Error>148 pub(crate) fn get_device_params( 149 &self, 150 pcm_info: &virtio_snd_pcm_info, 151 ) -> Result<PCMDeviceParameters, Error> { 152 let device_config = match pcm_info.direction { 153 VIRTIO_SND_D_OUTPUT => &self.output_device_config, 154 VIRTIO_SND_D_INPUT => &self.input_device_config, 155 _ => return Err(Error::InvalidPCMInfoDirection(pcm_info.direction)), 156 }; 157 let device_idx = u32::from(pcm_info.hdr.hda_fn_nid) as usize; 158 device_config 159 .get(device_idx) 160 .cloned() 161 .ok_or(Error::InvalidPCMDeviceConfigIndex(device_idx)) 162 } 163 } 164 165 #[cfg(test)] 166 #[allow(clippy::needless_update)] 167 mod tests { 168 use super::*; 169 check_failure(s: &str)170 fn check_failure(s: &str) { 171 serde_keyvalue::from_key_values::<Parameters>(s).expect_err("parse should have failed"); 172 } 173 check_success( s: &str, capture: bool, backend: StreamSourceBackend, num_output_devices: u32, num_input_devices: u32, num_output_streams: u32, num_input_streams: u32, output_device_config: Vec<PCMDeviceParameters>, input_device_config: Vec<PCMDeviceParameters>, )174 fn check_success( 175 s: &str, 176 capture: bool, 177 backend: StreamSourceBackend, 178 num_output_devices: u32, 179 num_input_devices: u32, 180 num_output_streams: u32, 181 num_input_streams: u32, 182 output_device_config: Vec<PCMDeviceParameters>, 183 input_device_config: Vec<PCMDeviceParameters>, 184 ) { 185 let params: Parameters = 186 serde_keyvalue::from_key_values(s).expect("parse should have succeded"); 187 assert_eq!(params.capture, capture); 188 assert_eq!(params.backend, backend); 189 assert_eq!(params.num_output_devices, num_output_devices); 190 assert_eq!(params.num_input_devices, num_input_devices); 191 assert_eq!(params.num_output_streams, num_output_streams); 192 assert_eq!(params.num_input_streams, num_input_streams); 193 assert_eq!(params.output_device_config, output_device_config); 194 assert_eq!(params.input_device_config, input_device_config); 195 } 196 197 #[test] parameters_fromstr()198 fn parameters_fromstr() { 199 check_failure("capture=none"); 200 check_success( 201 "capture=false", 202 false, 203 StreamSourceBackend::NULL, 204 1, 205 1, 206 1, 207 1, 208 vec![], 209 vec![], 210 ); 211 check_success( 212 "capture=true,num_output_streams=2,num_input_streams=3", 213 true, 214 StreamSourceBackend::NULL, 215 1, 216 1, 217 2, 218 3, 219 vec![], 220 vec![], 221 ); 222 check_success( 223 "capture=true,num_output_devices=3,num_input_devices=2", 224 true, 225 StreamSourceBackend::NULL, 226 3, 227 2, 228 1, 229 1, 230 vec![], 231 vec![], 232 ); 233 check_success( 234 "capture=true,num_output_devices=2,num_input_devices=3,\ 235 num_output_streams=3,num_input_streams=2", 236 true, 237 StreamSourceBackend::NULL, 238 2, 239 3, 240 3, 241 2, 242 vec![], 243 vec![], 244 ); 245 check_success( 246 "capture=true,backend=null,num_output_devices=2,num_input_devices=3,\ 247 num_output_streams=3,num_input_streams=2", 248 true, 249 StreamSourceBackend::NULL, 250 2, 251 3, 252 3, 253 2, 254 vec![], 255 vec![], 256 ); 257 check_success( 258 "output_device_config=[[effects=[aec]],[]]", 259 false, 260 StreamSourceBackend::NULL, 261 1, 262 1, 263 1, 264 1, 265 vec![ 266 PCMDeviceParameters { 267 effects: Some(vec![StreamEffect::EchoCancellation]), 268 ..Default::default() 269 }, 270 Default::default(), 271 ], 272 vec![], 273 ); 274 check_success( 275 "input_device_config=[[effects=[aec]],[]]", 276 false, 277 StreamSourceBackend::NULL, 278 1, 279 1, 280 1, 281 1, 282 vec![], 283 vec![ 284 PCMDeviceParameters { 285 effects: Some(vec![StreamEffect::EchoCancellation]), 286 ..Default::default() 287 }, 288 Default::default(), 289 ], 290 ); 291 292 // Invalid effect in device config 293 check_failure("output_device_config=[[effects=[none]]]"); 294 } 295 296 #[test] 297 #[cfg(all(unix, feature = "audio_cras"))] cras_parameters_fromstr()298 fn cras_parameters_fromstr() { 299 fn cras_check_success( 300 s: &str, 301 backend: StreamSourceBackend, 302 client_type: CrasClientType, 303 socket_type: CrasSocketType, 304 output_device_config: Vec<PCMDeviceParameters>, 305 input_device_config: Vec<PCMDeviceParameters>, 306 ) { 307 let params: Parameters = 308 serde_keyvalue::from_key_values(s).expect("parse should have succeded"); 309 assert_eq!(params.backend, backend); 310 assert_eq!(params.client_type, client_type); 311 assert_eq!(params.socket_type, socket_type); 312 assert_eq!(params.output_device_config, output_device_config); 313 assert_eq!(params.input_device_config, input_device_config); 314 } 315 316 cras_check_success( 317 "backend=cras", 318 StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 319 CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 320 CrasSocketType::Unified, 321 vec![], 322 vec![], 323 ); 324 cras_check_success( 325 "backend=cras,client_type=crosvm", 326 StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 327 CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 328 CrasSocketType::Unified, 329 vec![], 330 vec![], 331 ); 332 cras_check_success( 333 "backend=cras,client_type=arcvm", 334 StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 335 CrasClientType::CRAS_CLIENT_TYPE_ARCVM, 336 CrasSocketType::Unified, 337 vec![], 338 vec![], 339 ); 340 check_failure("backend=cras,client_type=none"); 341 cras_check_success( 342 "backend=cras,socket_type=legacy", 343 StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 344 CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 345 CrasSocketType::Legacy, 346 vec![], 347 vec![], 348 ); 349 cras_check_success( 350 "backend=cras,socket_type=unified", 351 StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS), 352 CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 353 CrasSocketType::Unified, 354 vec![], 355 vec![], 356 ); 357 cras_check_success( 358 "output_device_config=[[client_type=crosvm],[client_type=arcvm,stream_type=pro_audio],[]]", 359 StreamSourceBackend::NULL, 360 CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 361 CrasSocketType::Unified, 362 vec![ 363 PCMDeviceParameters{ 364 client_type: Some(CrasClientType::CRAS_CLIENT_TYPE_CROSVM), 365 stream_type: None, 366 effects: None, 367 }, 368 PCMDeviceParameters{ 369 client_type: Some(CrasClientType::CRAS_CLIENT_TYPE_ARCVM), 370 stream_type: Some(CrasStreamType::CRAS_STREAM_TYPE_PRO_AUDIO), 371 effects: None, 372 }, 373 Default::default(), 374 ], 375 vec![], 376 ); 377 cras_check_success( 378 "input_device_config=[[client_type=crosvm],[client_type=arcvm,effects=[aec],stream_type=pro_audio],[effects=[EchoCancellation]],[]]", 379 StreamSourceBackend::NULL, 380 CrasClientType::CRAS_CLIENT_TYPE_CROSVM, 381 CrasSocketType::Unified, 382 vec![], 383 vec![ 384 PCMDeviceParameters{ 385 client_type: Some(CrasClientType::CRAS_CLIENT_TYPE_CROSVM), 386 stream_type: None, 387 effects: None, 388 }, 389 PCMDeviceParameters{ 390 client_type: Some(CrasClientType::CRAS_CLIENT_TYPE_ARCVM), 391 stream_type: Some(CrasStreamType::CRAS_STREAM_TYPE_PRO_AUDIO), 392 effects: Some(vec![StreamEffect::EchoCancellation]), 393 }, 394 PCMDeviceParameters{ 395 client_type: None, 396 stream_type: None, 397 effects: Some(vec![StreamEffect::EchoCancellation]), 398 }, 399 Default::default(), 400 ], 401 ); 402 403 // Invalid client_type in device config 404 check_failure("output_device_config=[[client_type=none]]"); 405 406 // Invalid stream type in device config 407 check_failure("output_device_config=[[stream_type=none]]"); 408 } 409 410 #[test] get_device_params_output()411 fn get_device_params_output() { 412 let params = Parameters { 413 output_device_config: vec![ 414 PCMDeviceParameters { 415 effects: Some(vec![StreamEffect::EchoCancellation]), 416 ..Default::default() 417 }, 418 PCMDeviceParameters { 419 effects: Some(vec![ 420 StreamEffect::EchoCancellation, 421 StreamEffect::EchoCancellation, 422 ]), 423 ..Default::default() 424 }, 425 ], 426 ..Default::default() 427 }; 428 429 let default_pcm_info = virtio_snd_pcm_info { 430 hdr: virtio_snd_info { 431 hda_fn_nid: 0.into(), 432 }, 433 features: 0.into(), 434 formats: 0.into(), 435 rates: 0.into(), 436 direction: VIRTIO_SND_D_OUTPUT, // Direction is OUTPUT 437 channels_min: 1, 438 channels_max: 6, 439 padding: [0; 5], 440 }; 441 442 let mut pcm_info = default_pcm_info; 443 pcm_info.hdr.hda_fn_nid = 0.into(); 444 assert_eq!( 445 params.get_device_params(&pcm_info), 446 Ok(params.output_device_config[0].clone()) 447 ); 448 449 let mut pcm_info = default_pcm_info; 450 pcm_info.hdr.hda_fn_nid = 1.into(); 451 assert_eq!( 452 params.get_device_params(&pcm_info), 453 Ok(params.output_device_config[1].clone()) 454 ); 455 456 let mut pcm_info = default_pcm_info; 457 pcm_info.hdr.hda_fn_nid = 2.into(); 458 assert_eq!( 459 params.get_device_params(&pcm_info), 460 Err(Error::InvalidPCMDeviceConfigIndex(2)) 461 ); 462 } 463 464 #[test] get_device_params_input()465 fn get_device_params_input() { 466 let params = Parameters { 467 input_device_config: vec![ 468 PCMDeviceParameters { 469 effects: Some(vec![ 470 StreamEffect::EchoCancellation, 471 StreamEffect::EchoCancellation, 472 ]), 473 ..Default::default() 474 }, 475 PCMDeviceParameters { 476 effects: Some(vec![StreamEffect::EchoCancellation]), 477 ..Default::default() 478 }, 479 ], 480 ..Default::default() 481 }; 482 483 let default_pcm_info = virtio_snd_pcm_info { 484 hdr: virtio_snd_info { 485 hda_fn_nid: 0.into(), 486 }, 487 features: 0.into(), 488 formats: 0.into(), 489 rates: 0.into(), 490 direction: VIRTIO_SND_D_INPUT, // Direction is INPUT 491 channels_min: 1, 492 channels_max: 6, 493 padding: [0; 5], 494 }; 495 496 let mut pcm_info = default_pcm_info; 497 pcm_info.hdr.hda_fn_nid = 0.into(); 498 assert_eq!( 499 params.get_device_params(&pcm_info), 500 Ok(params.input_device_config[0].clone()) 501 ); 502 503 let mut pcm_info = default_pcm_info; 504 pcm_info.hdr.hda_fn_nid = 1.into(); 505 assert_eq!( 506 params.get_device_params(&pcm_info), 507 Ok(params.input_device_config[1].clone()) 508 ); 509 510 let mut pcm_info = default_pcm_info; 511 pcm_info.hdr.hda_fn_nid = 2.into(); 512 assert_eq!( 513 params.get_device_params(&pcm_info), 514 Err(Error::InvalidPCMDeviceConfigIndex(2)) 515 ); 516 } 517 518 #[test] get_device_params_invalid_direction()519 fn get_device_params_invalid_direction() { 520 let params = Parameters::default(); 521 522 let pcm_info = virtio_snd_pcm_info { 523 hdr: virtio_snd_info { 524 hda_fn_nid: 0.into(), 525 }, 526 features: 0.into(), 527 formats: 0.into(), 528 rates: 0.into(), 529 direction: 2, // Invalid direction 530 channels_min: 1, 531 channels_max: 6, 532 padding: [0; 5], 533 }; 534 535 assert_eq!( 536 params.get_device_params(&pcm_info), 537 Err(Error::InvalidPCMInfoDirection(2)) 538 ); 539 } 540 } 541