• 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::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