Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/snd/vios_backend/mod.rs
5394 views
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 snapshot::AnySnapshot;
33
use streams::StreamMsg;
34
use streams::StreamSnapshot;
35
use sync::Mutex;
36
use thiserror::Error as ThisError;
37
use vm_memory::GuestMemory;
38
use worker::*;
39
use zerocopy::IntoBytes;
40
41
use crate::virtio::copy_config;
42
use crate::virtio::device_constants::snd::virtio_snd_config;
43
use crate::virtio::DeviceType;
44
use crate::virtio::Interrupt;
45
use crate::virtio::Queue;
46
use crate::virtio::VirtioDevice;
47
48
const QUEUE_SIZES: &[u16] = &[64, 64, 64, 64];
49
50
#[sorted]
51
#[derive(ThisError, Debug)]
52
pub enum SoundError {
53
#[error("The driver sent an invalid message")]
54
BadDriverMsg,
55
#[error("Failed to get event notifier from VioS client: {0}")]
56
ClientEventNotifier(Error),
57
#[error("Failed to create VioS client: {0}")]
58
ClientNew(Error),
59
#[error("Failed to create event pair: {0}")]
60
CreateEvent(BaseError),
61
#[error("Failed to create thread: {0}")]
62
CreateThread(IoError),
63
#[error("Attempted a {0} operation while on the wrong state: {1}, this is a bug")]
64
ImpossibleState(&'static str, &'static str),
65
#[error("Error consuming queue event: {0}")]
66
QueueEvt(BaseError),
67
#[error("Failed to read/write from/to queue: {0}")]
68
QueueIO(IoError),
69
#[error("Failed to receive message: {0}")]
70
StreamThreadRecv(RecvError),
71
#[error("Failed to send message: {0}")]
72
StreamThreadSend(SendError<Box<StreamMsg>>),
73
#[error("Error creating WaitContext: {0}")]
74
WaitCtx(BaseError),
75
}
76
77
pub type Result<T> = std::result::Result<T, SoundError>;
78
79
pub struct Sound {
80
config: virtio_snd_config,
81
virtio_features: u64,
82
worker_thread: Option<WorkerThread<anyhow::Result<Worker>>>,
83
vios_client: Arc<Mutex<VioSClient>>,
84
saved_stream_state: Vec<StreamSnapshot>,
85
}
86
87
#[derive(Serialize, Deserialize)]
88
struct SoundSnapshot {
89
config: virtio_snd_config,
90
virtio_features: u64,
91
vios_client: VioSClientSnapshot,
92
saved_stream_state: Vec<StreamSnapshot>,
93
}
94
95
impl VirtioDevice for Sound {
96
fn keep_rds(&self) -> Vec<RawDescriptor> {
97
self.vios_client.lock().keep_rds()
98
}
99
100
fn device_type(&self) -> DeviceType {
101
DeviceType::Sound
102
}
103
104
fn queue_max_sizes(&self) -> &[u16] {
105
QUEUE_SIZES
106
}
107
108
fn read_config(&self, offset: u64, data: &mut [u8]) {
109
copy_config(data, 0, self.config.as_bytes(), offset);
110
}
111
112
fn write_config(&mut self, _offset: u64, _data: &[u8]) {
113
error!("virtio-snd: driver attempted a config write which is not allowed by the spec");
114
}
115
116
fn features(&self) -> u64 {
117
self.virtio_features
118
}
119
120
fn activate(
121
&mut self,
122
_mem: GuestMemory,
123
_interrupt: Interrupt,
124
mut queues: BTreeMap<usize, Queue>,
125
) -> anyhow::Result<()> {
126
if self.worker_thread.is_some() {
127
return Err(anyhow!("virtio-snd: Device is already active"));
128
}
129
if queues.len() != 4 {
130
return Err(anyhow!(
131
"virtio-snd: device activated with wrong number of queues: {}",
132
queues.len(),
133
));
134
}
135
let control_queue = queues.remove(&0).unwrap();
136
let event_queue = queues.remove(&1).unwrap();
137
let tx_queue = queues.remove(&2).unwrap();
138
let rx_queue = queues.remove(&3).unwrap();
139
140
let vios_client = self.vios_client.clone();
141
vios_client
142
.lock()
143
.start_bg_thread()
144
.context("Failed to start vios background thread")?;
145
146
let saved_stream_state: Vec<StreamSnapshot> = self.saved_stream_state.drain(..).collect();
147
self.worker_thread =
148
Some(WorkerThread::start(
149
"v_snd_vios",
150
move |kill_evt| match Worker::try_new(
151
vios_client,
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
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
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
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
243
fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
244
AnySnapshot::to_any(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
253
fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
254
let data: SoundSnapshot =
255
AnySnapshot::from_any(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
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
292