xref: /aosp_15_r20/external/crosvm/devices/src/virtio/snd/vios_backend/mod.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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