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::device::snd::SND_EXECUTOR;
26 use crate::virtio::vhost::user::VhostUserDeviceBuilder;
27
28 pub mod generic;
29 pub use generic as product;
30
31 #[derive(FromArgs, Debug)]
32 #[argh(subcommand, name = "snd", description = "")]
33 pub struct Options {
34 #[argh(
35 option,
36 description = "pipe handle end for Tube Transporter",
37 arg_name = "HANDLE"
38 )]
39 bootstrap: usize,
40 }
41
42 /// Main process end for a sound device.
43 #[derive(Deserialize, Serialize)]
44 pub struct SndVmmConfig {
45 // Tube for setting up the vhost-user connection. May not exist if not using vhost-user.
46 pub main_vhost_user_tube: Option<Tube>,
47 // Product related configuration.
48 pub product_config: product::SndVmmConfig,
49 }
50
51 /// Config arguments passed through the bootstrap Tube from the broker to the Snd backend
52 /// process.
53 #[derive(Deserialize, Serialize)]
54 pub struct SndBackendConfig {
55 // Tube for setting up the vhost-user connection. May not exist if not using vhost-user.
56 pub device_vhost_user_tube: Option<Tube>,
57 // An event for an incoming exit request.
58 pub exit_event: Event,
59 // Sound device parameters.
60 pub parameters: Parameters,
61 // Product related configuration.
62 pub product_config: product::SndBackendConfig,
63 }
64
65 /// Configuration for running a Snd device, split by a part sent to the main VMM and a part sent to
66 /// where the Snd worker will be running (either main process or a vhost-user process).
67 #[derive(Deserialize, Serialize)]
68 pub struct SndSplitConfig {
69 // Config sent to the backend.
70 pub backend_config: Option<SndBackendConfig>,
71 // Config sent to the main process.
72 pub vmm_config: Option<SndVmmConfig>,
73 }
74
75 /// Starts a vhost-user snd device.
76 /// Returns an error if the given `args` is invalid or the device fails to run.
run_snd_device(opts: Options) -> anyhow::Result<()>77 pub fn run_snd_device(opts: Options) -> anyhow::Result<()> {
78 let raw_transport_tube = opts.bootstrap as RawDescriptor;
79
80 let mut tubes = read_from_tube_transporter(raw_transport_tube)?;
81
82 let bootstrap_tube = tubes.get_tube(TubeToken::Bootstrap)?;
83
84 let startup_args: CommonChildStartupArgs = bootstrap_tube.recv::<CommonChildStartupArgs>()?;
85 let _child_cleanup = common_child_setup(startup_args)?;
86
87 let mut config: SndBackendConfig = bootstrap_tube
88 .recv()
89 .context("failed to parse Snd backend config from bootstrap tube")?;
90
91 let vhost_user_tube = config
92 .device_vhost_user_tube
93 .expect("vhost-user Snd tube must be set");
94
95 let ex = Executor::new().context("Failed to create executor")?;
96 let _ = SND_EXECUTOR.set(ex.clone());
97
98 let snd_device = Box::new(SndBackend::new(config.parameters)?);
99
100 // TODO(b/213170185): Uncomment once sandbox is upstreamed.
101 // if sandbox::is_sandbox_target() {
102 // sandbox::TargetServices::get()
103 // .expect("failed to get target services")
104 // .unwrap()
105 // .lower_token();
106 // }
107
108 // Set the audio thread priority here. This assumes our executor is running on a single thread.
109 let _thread_priority_handle = set_audio_thread_priority();
110 if let Err(e) = _thread_priority_handle {
111 warn!("Failed to set audio thread to real time: {}", e);
112 };
113
114 let handler = snd_device.build(&ex)?;
115
116 info!("vhost-user snd device ready, starting run loop...");
117 if let Err(e) = ex.run_until(run_handler(
118 handler,
119 vhost_user_tube,
120 config.exit_event,
121 &ex,
122 )) {
123 bail!("error occurred: {}", e);
124 }
125
126 Ok(())
127 }
128