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