1 // Copyright 2020 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 mod shm_streams;
6 mod shm_vios;
7
8 #[cfg(any(target_os = "linux", target_os = "android"))]
9 pub use self::shm_streams::*;
10 pub use self::shm_vios::*;
11
12 pub mod streams;
13 mod worker;
14
15 use std::collections::BTreeMap;
16 use std::io::Error as IoError;
17 use std::path::Path;
18 use std::sync::mpsc::RecvError;
19 use std::sync::mpsc::SendError;
20 use std::sync::Arc;
21
22 use anyhow::anyhow;
23 use anyhow::Context;
24 use base::error;
25 use base::Error as BaseError;
26 use base::RawDescriptor;
27 use base::WorkerThread;
28 use data_model::Le32;
29 use remain::sorted;
30 use serde::Deserialize;
31 use serde::Serialize;
32 use streams::StreamMsg;
33 use streams::StreamSnapshot;
34 use sync::Mutex;
35 use thiserror::Error as ThisError;
36 use vm_memory::GuestMemory;
37 use worker::*;
38 use zerocopy::AsBytes;
39
40 use crate::virtio::copy_config;
41 use crate::virtio::device_constants::snd::virtio_snd_config;
42 use crate::virtio::DeviceType;
43 use crate::virtio::Interrupt;
44 use crate::virtio::Queue;
45 use crate::virtio::VirtioDevice;
46
47 const QUEUE_SIZES: &[u16] = &[64, 64, 64, 64];
48
49 #[sorted]
50 #[derive(ThisError, Debug)]
51 pub enum SoundError {
52 #[error("The driver sent an invalid message")]
53 BadDriverMsg,
54 #[error("Failed to get event notifier from VioS client: {0}")]
55 ClientEventNotifier(Error),
56 #[error("Failed to create VioS client: {0}")]
57 ClientNew(Error),
58 #[error("Failed to create event pair: {0}")]
59 CreateEvent(BaseError),
60 #[error("Failed to create thread: {0}")]
61 CreateThread(IoError),
62 #[error("Attempted a {0} operation while on the wrong state: {1}, this is a bug")]
63 ImpossibleState(&'static str, &'static str),
64 #[error("Error consuming queue event: {0}")]
65 QueueEvt(BaseError),
66 #[error("Failed to read/write from/to queue: {0}")]
67 QueueIO(IoError),
68 #[error("Failed to receive message: {0}")]
69 StreamThreadRecv(RecvError),
70 #[error("Failed to send message: {0}")]
71 StreamThreadSend(SendError<Box<StreamMsg>>),
72 #[error("Error creating WaitContext: {0}")]
73 WaitCtx(BaseError),
74 }
75
76 pub type Result<T> = std::result::Result<T, SoundError>;
77
78 pub struct Sound {
79 config: virtio_snd_config,
80 virtio_features: u64,
81 worker_thread: Option<WorkerThread<anyhow::Result<Worker>>>,
82 vios_client: Arc<Mutex<VioSClient>>,
83 saved_stream_state: Vec<StreamSnapshot>,
84 }
85
86 #[derive(Serialize, Deserialize)]
87 struct SoundSnapshot {
88 config: virtio_snd_config,
89 virtio_features: u64,
90 vios_client: VioSClientSnapshot,
91 saved_stream_state: Vec<StreamSnapshot>,
92 }
93
94 impl VirtioDevice for Sound {
keep_rds(&self) -> Vec<RawDescriptor>95 fn keep_rds(&self) -> Vec<RawDescriptor> {
96 self.vios_client.lock().keep_rds()
97 }
98
device_type(&self) -> DeviceType99 fn device_type(&self) -> DeviceType {
100 DeviceType::Sound
101 }
102
queue_max_sizes(&self) -> &[u16]103 fn queue_max_sizes(&self) -> &[u16] {
104 QUEUE_SIZES
105 }
106
read_config(&self, offset: u64, data: &mut [u8])107 fn read_config(&self, offset: u64, data: &mut [u8]) {
108 copy_config(data, 0, self.config.as_bytes(), offset);
109 }
110
write_config(&mut self, _offset: u64, _data: &[u8])111 fn write_config(&mut self, _offset: u64, _data: &[u8]) {
112 error!("virtio-snd: driver attempted a config write which is not allowed by the spec");
113 }
114
features(&self) -> u64115 fn features(&self) -> u64 {
116 self.virtio_features
117 }
118
activate( &mut self, _mem: GuestMemory, interrupt: Interrupt, mut queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>119 fn activate(
120 &mut self,
121 _mem: GuestMemory,
122 interrupt: Interrupt,
123 mut queues: BTreeMap<usize, Queue>,
124 ) -> anyhow::Result<()> {
125 if self.worker_thread.is_some() {
126 return Err(anyhow!("virtio-snd: Device is already active"));
127 }
128 if queues.len() != 4 {
129 return Err(anyhow!(
130 "virtio-snd: device activated with wrong number of queues: {}",
131 queues.len(),
132 ));
133 }
134 let control_queue = queues.remove(&0).unwrap();
135 let event_queue = queues.remove(&1).unwrap();
136 let tx_queue = queues.remove(&2).unwrap();
137 let rx_queue = queues.remove(&3).unwrap();
138
139 let vios_client = self.vios_client.clone();
140 vios_client
141 .lock()
142 .start_bg_thread()
143 .context("Failed to start vios background thread")?;
144
145 let saved_stream_state: Vec<StreamSnapshot> = self.saved_stream_state.drain(..).collect();
146 self.worker_thread =
147 Some(WorkerThread::start(
148 "v_snd_vios",
149 move |kill_evt| match Worker::try_new(
150 vios_client,
151 interrupt,
152 Arc::new(Mutex::new(control_queue)),
153 event_queue,
154 Arc::new(Mutex::new(tx_queue)),
155 Arc::new(Mutex::new(rx_queue)),
156 saved_stream_state,
157 ) {
158 Ok(mut worker) => match worker.control_loop(kill_evt) {
159 Ok(_) => Ok(worker),
160 Err(e) => {
161 error!("virtio-snd: Error in worker loop: {}", e);
162 Err(anyhow!("virtio-snd: Error in worker loop: {}", e))
163 }
164 },
165 Err(e) => {
166 error!("virtio-snd: Failed to create worker: {}", e);
167 Err(anyhow!("virtio-snd: Failed to create worker: {}", e))
168 }
169 },
170 ));
171
172 Ok(())
173 }
174
reset(&mut self) -> anyhow::Result<()>175 fn reset(&mut self) -> anyhow::Result<()> {
176 if let Some(worker_thread) = self.worker_thread.take() {
177 let worker = worker_thread.stop();
178 self.vios_client
179 .lock()
180 .stop_bg_thread()
181 .context("failed to stop VioS Client background thread")?;
182 let _worker = worker.context("failed to stop worker_thread")?;
183 }
184 Ok(())
185 }
186
virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>>187 fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
188 if let Some(worker_thread) = self.worker_thread.take() {
189 // The worker is stopped first but not unwrapped until after the VioSClient is stopped.
190 // If the worker fails to stop and returns an error, but that error is unwrapped, the
191 // vios_client background thread could remain running. Instead, by delaying the unwrap,
192 // we can ensure the signal to both threads to stop is sent.
193 let worker = worker_thread.stop();
194 self.vios_client
195 .lock()
196 .stop_bg_thread()
197 .context("failed to stop VioS Client background thread")?;
198 let mut worker = worker.context("failed to stop worker_thread")?;
199 self.saved_stream_state = worker.saved_stream_state.drain(..).collect();
200 let ctrl_queue = worker.control_queue.clone();
201 let event_queue = worker.event_queue.take().unwrap();
202 let tx_queue = worker.tx_queue.clone();
203 let rx_queue = worker.rx_queue.clone();
204
205 // Must drop worker to drop all references to queues.
206 // This also drops the io_thread
207 drop(worker);
208
209 let ctrl_queue = match Arc::try_unwrap(ctrl_queue) {
210 Ok(q) => q.into_inner(),
211 Err(_) => panic!("too many refs to snd control queue"),
212 };
213 let tx_queue = match Arc::try_unwrap(tx_queue) {
214 Ok(q) => q.into_inner(),
215 Err(_) => panic!("too many refs to snd tx queue"),
216 };
217 let rx_queue = match Arc::try_unwrap(rx_queue) {
218 Ok(q) => q.into_inner(),
219 Err(_) => panic!("too many refs to snd rx queue"),
220 };
221 let queues = vec![ctrl_queue, event_queue, tx_queue, rx_queue];
222 return Ok(Some(BTreeMap::from_iter(queues.into_iter().enumerate())));
223 }
224 Ok(None)
225 }
226
virtio_wake( &mut self, device_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, ) -> anyhow::Result<()>227 fn virtio_wake(
228 &mut self,
229 device_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
230 ) -> anyhow::Result<()> {
231 match device_state {
232 None => Ok(()),
233 Some((mem, interrupt, queues)) => {
234 // TODO: activate is just what we want at the moment, but we should probably move
235 // it into a "start workers" function to make it obvious that it isn't strictly
236 // used for activate events.
237 self.activate(mem, interrupt, queues)?;
238 Ok(())
239 }
240 }
241 }
242
virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value>243 fn virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
244 serde_json::to_value(SoundSnapshot {
245 config: self.config,
246 virtio_features: self.virtio_features,
247 vios_client: self.vios_client.lock().snapshot(),
248 saved_stream_state: self.saved_stream_state.clone(),
249 })
250 .context("failed to serialize VioS Client")
251 }
252
virtio_restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>253 fn virtio_restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
254 let data: SoundSnapshot =
255 serde_json::from_value(data).context("failed to deserialize VioS Client")?;
256 anyhow::ensure!(
257 data.config == self.config,
258 "config doesn't match on restore: expected: {:?}, got: {:?}",
259 data.config,
260 self.config
261 );
262 anyhow::ensure!(
263 data.virtio_features == self.virtio_features,
264 "virtio_features doesn't match on restore: expected: {}, got: {}",
265 data.virtio_features,
266 self.virtio_features
267 );
268 self.saved_stream_state = data.saved_stream_state;
269 self.vios_client.lock().restore(data.vios_client)
270 }
271 }
272
273 /// Creates a new virtio sound device connected to a VioS backend
new_sound<P: AsRef<Path>>(path: P, virtio_features: u64) -> Result<Sound>274 pub fn new_sound<P: AsRef<Path>>(path: P, virtio_features: u64) -> Result<Sound> {
275 let vios_client = VioSClient::try_new(path).map_err(SoundError::ClientNew)?;
276 let jacks = Le32::from(vios_client.num_jacks());
277 let streams = Le32::from(vios_client.num_streams());
278 let chmaps = Le32::from(vios_client.num_chmaps());
279 Ok(Sound {
280 config: virtio_snd_config {
281 jacks,
282 streams,
283 chmaps,
284 },
285 virtio_features,
286 worker_thread: None,
287 vios_client: Arc::new(Mutex::new(vios_client)),
288 saved_stream_state: Vec::new(),
289 })
290 }
291