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