• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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