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