• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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