• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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 anyhow::bail;
6 use anyhow::Context;
7 use argh::FromArgs;
8 use base::info;
9 use base::warn;
10 use base::Event;
11 use base::RawDescriptor;
12 use base::Tube;
13 use broker_ipc::common_child_setup;
14 use broker_ipc::CommonChildStartupArgs;
15 use cros_async::Executor;
16 use serde::Deserialize;
17 use serde::Serialize;
18 use tube_transporter::TubeToken;
19 
20 use crate::virtio::snd::parameters::Parameters;
21 use crate::virtio::snd::sys::set_audio_thread_priority;
22 use crate::virtio::vhost::user::device::handler::sys::windows::read_from_tube_transporter;
23 use crate::virtio::vhost::user::device::handler::sys::windows::run_handler;
24 use crate::virtio::vhost::user::device::snd::SndBackend;
25 use crate::virtio::vhost::user::VhostUserDeviceBuilder;
26 
27 pub mod generic;
28 pub use generic as product;
29 
30 #[derive(FromArgs, Debug)]
31 #[argh(subcommand, name = "snd", description = "")]
32 pub struct Options {
33     #[argh(
34         option,
35         description = "pipe handle end for Tube Transporter",
36         arg_name = "HANDLE"
37     )]
38     bootstrap: usize,
39 }
40 
41 /// Main process end for a sound device.
42 #[derive(Deserialize, Serialize)]
43 pub struct SndVmmConfig {
44     // Tube for setting up the vhost-user connection. May not exist if not using vhost-user.
45     pub main_vhost_user_tube: Option<Tube>,
46     // GUID that will be passed into `IAudioClient::Initialize`.
47     pub audio_client_guid: String,
48     // Used to identify the device backend.
49     pub card_index: usize,
50     // Product related configuration.
51     pub product_config: product::SndVmmConfig,
52 }
53 
54 /// Config arguments passed through the bootstrap Tube from the broker to the Snd backend
55 /// process.
56 #[derive(Deserialize, Serialize)]
57 pub struct SndBackendConfig {
58     // Tube for setting up the vhost-user connection. May not exist if not using vhost-user.
59     pub device_vhost_user_tube: Option<Tube>,
60     // An event for an incoming exit request.
61     pub exit_event: Event,
62     // Sound device parameters.
63     pub parameters: Parameters,
64     // This field is used to pass this GUID to `IAudioClient::Initialize`.
65     pub audio_client_guid: String,
66     // Used to append to logs in the vhost user device backends.
67     pub card_index: usize,
68     // Product related configuration.
69     pub product_config: product::SndBackendConfig,
70 }
71 
72 /// Configuration for running a Snd device, split by a part sent to the main VMM and a part sent to
73 /// where the Snd worker will be running (either main process or a vhost-user process).
74 #[derive(Deserialize, Serialize)]
75 pub struct SndSplitConfig {
76     // Config sent to the backend.
77     pub backend_config: Option<SndBackendConfig>,
78     // Config sent to the main process.
79     pub vmm_config: Option<SndVmmConfig>,
80 }
81 
82 /// Starts a vhost-user snd device.
83 /// Returns an error if the given `args` is invalid or the device fails to run.
run_snd_device(opts: Options) -> anyhow::Result<()>84 pub fn run_snd_device(opts: Options) -> anyhow::Result<()> {
85     let raw_transport_tube = opts.bootstrap as RawDescriptor;
86 
87     let mut tubes = read_from_tube_transporter(raw_transport_tube)?;
88 
89     let bootstrap_tube = tubes.get_tube(TubeToken::Bootstrap)?;
90 
91     let startup_args: CommonChildStartupArgs = bootstrap_tube.recv::<CommonChildStartupArgs>()?;
92     let _child_cleanup = common_child_setup(startup_args)?;
93 
94     let mut config: SndBackendConfig = bootstrap_tube
95         .recv()
96         .context("failed to parse Snd backend config from bootstrap tube")?;
97 
98     // TODO(b/213170185): Uncomment once sandbox is upstreamed.
99     // if sandbox::is_sandbox_target() {
100     //     sandbox::TargetServices::get()
101     //         .expect("failed to get target services")
102     //         .unwrap()
103     //         .lower_token();
104     // }
105 
106     run_snd_device_worker(config)
107 }
108 
109 /// Run the SND device worker.
run_snd_device_worker(config: SndBackendConfig) -> anyhow::Result<()>110 pub fn run_snd_device_worker(config: SndBackendConfig) -> anyhow::Result<()> {
111     let card_index = config.card_index;
112     let vhost_user_tube = config
113         .device_vhost_user_tube
114         .unwrap_or_else(|| panic!("[Card {}] vhost-user Snd tube must be set", card_index));
115 
116     let ex = Executor::new().context(format!("[Card {}] Failed to create executor", card_index))?;
117 
118     let snd_device = Box::new(SndBackend::new(
119         &ex,
120         config.parameters,
121         Some(config.audio_client_guid),
122         config.card_index,
123     )?);
124 
125     // Set the audio thread priority here. This assumes our executor is running on a single thread.
126     let _thread_priority_handle = set_audio_thread_priority();
127     if let Err(e) = _thread_priority_handle {
128         warn!(
129             "[Card {}] Failed to set audio thread to real time: {}",
130             card_index, e
131         );
132     };
133 
134     let handler = snd_device.build(&ex)?;
135 
136     info!(
137         "[Card {}] vhost-user snd device ready, starting run loop...",
138         card_index
139     );
140     ex.run_until(run_handler(
141         handler,
142         vhost_user_tube,
143         config.exit_event,
144         &ex,
145     ))
146     .context("run_until error")?
147     .context("run_handler error")
148 }
149