1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
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
11 pub use self::shm_vios::*;
12
13 pub mod streams;
14
15 mod worker;
16
17 use std::thread;
18
19 use crate::virtio::{copy_config, DescriptorError, Interrupt, Queue, VirtioDevice, TYPE_SOUND};
20 use base::{error, Error as BaseError, Event, RawDescriptor};
21 use data_model::{DataInit, Le32};
22 use remain::sorted;
23 use sync::Mutex;
24 use vm_memory::GuestMemory;
25
26 use std::path::Path;
27 use std::sync::mpsc::{RecvError, SendError};
28 use std::sync::Arc;
29
30 use super::layout::*;
31 use streams::StreamMsg;
32 use worker::*;
33
34 use std::io::Error as IoError;
35 use thiserror::Error as ThisError;
36
37 const QUEUE_SIZES: &[u16] = &[64, 64, 64, 64];
38
39 #[sorted]
40 #[derive(ThisError, Debug)]
41 pub enum SoundError {
42 #[error("The driver sent an invalid message")]
43 BadDriverMsg,
44 #[error("Failed to get event notifier from VioS client: {0}")]
45 ClientEventNotifier(Error),
46 #[error("Failed to create VioS client: {0}")]
47 ClientNew(Error),
48 #[error("Failed to create event pair: {0}")]
49 CreateEvent(BaseError),
50 #[error("Failed to create Reader from descriptor chain: {0}")]
51 CreateReader(DescriptorError),
52 #[error("Failed to create thread: {0}")]
53 CreateThread(IoError),
54 #[error("Failed to create Writer from descriptor chain: {0}")]
55 CreateWriter(DescriptorError),
56 #[error("Error with queue descriptor: {0}")]
57 Descriptor(DescriptorError),
58 #[error("Attempted a {0} operation while on the wrong state: {1}, this is a bug")]
59 ImpossibleState(&'static str, &'static str),
60 #[error("Error consuming queue event: {0}")]
61 QueueEvt(BaseError),
62 #[error("Failed to read/write from/to queue: {0}")]
63 QueueIO(IoError),
64 #[error("Failed to receive message: {0}")]
65 StreamThreadRecv(RecvError),
66 #[error("Failed to send message: {0}")]
67 StreamThreadSend(SendError<StreamMsg>),
68 #[error("Error creating WaitContext: {0}")]
69 WaitCtx(BaseError),
70 }
71
72 pub type Result<T> = std::result::Result<T, SoundError>;
73
74 pub struct Sound {
75 config: virtio_snd_config,
76 virtio_features: u64,
77 worker_thread: Option<thread::JoinHandle<bool>>,
78 kill_evt: Option<Event>,
79 vios_client: Arc<VioSClient>,
80 }
81
82 impl VirtioDevice for Sound {
keep_rds(&self) -> Vec<RawDescriptor>83 fn keep_rds(&self) -> Vec<RawDescriptor> {
84 self.vios_client.keep_fds()
85 }
86
device_type(&self) -> u3287 fn device_type(&self) -> u32 {
88 TYPE_SOUND
89 }
90
queue_max_sizes(&self) -> &[u16]91 fn queue_max_sizes(&self) -> &[u16] {
92 QUEUE_SIZES
93 }
94
read_config(&self, offset: u64, data: &mut [u8])95 fn read_config(&self, offset: u64, data: &mut [u8]) {
96 copy_config(data, 0, self.config.as_slice(), offset);
97 }
98
write_config(&mut self, _offset: u64, _data: &[u8])99 fn write_config(&mut self, _offset: u64, _data: &[u8]) {
100 error!("virtio-snd: driver attempted a config write which is not allowed by the spec");
101 }
102
features(&self) -> u64103 fn features(&self) -> u64 {
104 self.virtio_features
105 }
106
activate( &mut self, mem: GuestMemory, interrupt: Interrupt, mut queues: Vec<Queue>, mut queue_evts: Vec<Event>, )107 fn activate(
108 &mut self,
109 mem: GuestMemory,
110 interrupt: Interrupt,
111 mut queues: Vec<Queue>,
112 mut queue_evts: Vec<Event>,
113 ) {
114 if self.worker_thread.is_some() {
115 error!("virtio-snd: Device is already active");
116 return;
117 }
118 if queues.len() != 4 || queue_evts.len() != 4 {
119 error!(
120 "virtio-snd: device activated with wrong number of queues: {}, {}",
121 queues.len(),
122 queue_evts.len()
123 );
124 return;
125 }
126 let (self_kill_evt, kill_evt) = match Event::new().and_then(|e| Ok((e.try_clone()?, e))) {
127 Ok(v) => v,
128 Err(e) => {
129 error!("virtio-snd: failed to create kill Event pair: {}", e);
130 return;
131 }
132 };
133 self.kill_evt = Some(self_kill_evt);
134 let control_queue = queues.remove(0);
135 let control_queue_evt = queue_evts.remove(0);
136 let event_queue = queues.remove(0);
137 let event_queue_evt = queue_evts.remove(0);
138 let tx_queue = queues.remove(0);
139 let tx_queue_evt = queue_evts.remove(0);
140 let rx_queue = queues.remove(0);
141 let rx_queue_evt = queue_evts.remove(0);
142
143 let vios_client = self.vios_client.clone();
144 if let Err(e) = vios_client.start_bg_thread() {
145 error!("Failed to start vios background thread: {}", e);
146 }
147
148 let thread_result = thread::Builder::new()
149 .name(String::from("virtio_snd"))
150 .spawn(move || {
151 match Worker::try_new(
152 vios_client,
153 Arc::new(interrupt),
154 mem,
155 Arc::new(Mutex::new(control_queue)),
156 control_queue_evt,
157 event_queue,
158 event_queue_evt,
159 Arc::new(Mutex::new(tx_queue)),
160 tx_queue_evt,
161 Arc::new(Mutex::new(rx_queue)),
162 rx_queue_evt,
163 ) {
164 Ok(mut worker) => match worker.control_loop(kill_evt) {
165 Ok(_) => true,
166 Err(e) => {
167 error!("virtio-snd: Error in worker loop: {}", e);
168 false
169 }
170 },
171 Err(e) => {
172 error!("virtio-snd: Failed to create worker: {}", e);
173 false
174 }
175 }
176 });
177 match thread_result {
178 Err(e) => {
179 error!("failed to spawn virtio_snd worker thread: {}", e);
180 }
181 Ok(join_handle) => {
182 self.worker_thread = Some(join_handle);
183 }
184 }
185 }
186
reset(&mut self) -> bool187 fn reset(&mut self) -> bool {
188 let mut ret = true;
189 if let Some(kill_evt) = self.kill_evt.take() {
190 if let Err(e) = kill_evt.write(1) {
191 error!("virtio-snd: failed to notify the kill event: {}", e);
192 ret = false;
193 }
194 } else if let Some(worker_thread) = self.worker_thread.take() {
195 match worker_thread.join() {
196 Err(e) => {
197 error!("virtio-snd: Worker thread panicked: {:?}", e);
198 ret = false;
199 }
200 Ok(worker_status) => {
201 ret = worker_status;
202 }
203 }
204 }
205 if let Err(e) = self.vios_client.stop_bg_thread() {
206 error!("virtio-snd: Failed to stop vios background thread: {}", e);
207 ret = false;
208 }
209 ret
210 }
211 }
212
213 /// Creates a new virtio sound device connected to a VioS backend
new_sound<P: AsRef<Path>>(path: P, virtio_features: u64) -> Result<Sound>214 pub fn new_sound<P: AsRef<Path>>(path: P, virtio_features: u64) -> Result<Sound> {
215 let vios_client = Arc::new(VioSClient::try_new(path).map_err(SoundError::ClientNew)?);
216 Ok(Sound {
217 config: virtio_snd_config {
218 jacks: Le32::from(vios_client.num_jacks()),
219 streams: Le32::from(vios_client.num_streams()),
220 chmaps: Le32::from(vios_client.num_chmaps()),
221 },
222 virtio_features,
223 worker_thread: None,
224 kill_evt: None,
225 vios_client,
226 })
227 }
228