1 // Copyright 2022 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 use std::collections::VecDeque;
6 use std::sync::atomic::AtomicBool;
7 use std::sync::atomic::Ordering;
8 use std::sync::Arc;
9 use std::thread;
10 use std::time::Duration;
11 use std::time::Instant;
12
13 use audio_streams::shm_streams::ShmStream;
14 use audio_streams::BoxError;
15 use audio_streams::NoopStreamControl;
16 use audio_streams::SampleFormat;
17 use audio_streams::StreamDirection;
18 use audio_streams::StreamEffect;
19 use base::error;
20 use base::set_rt_prio_limit;
21 use base::set_rt_round_robin;
22 use base::warn;
23 use base::AsRawDescriptor;
24 use base::AsRawDescriptors;
25 use base::FromRawDescriptor;
26 use base::RawDescriptor;
27 use remain::sorted;
28 use sync::Condvar;
29 use sync::Mutex;
30 use thiserror::Error;
31 use vm_memory::GuestAddress;
32 use vm_memory::GuestMemory;
33
34 use crate::pci::ac97::sys::AudioStreamSource;
35 use crate::pci::ac97_bus_master::buffer_completed;
36 use crate::pci::ac97_bus_master::current_buffer_size;
37 use crate::pci::ac97_bus_master::get_buffer_samples;
38 use crate::pci::ac97_bus_master::Ac97BusMaster;
39 use crate::pci::ac97_bus_master::Ac97BusMasterRegs;
40 use crate::pci::ac97_bus_master::AudioResult;
41 use crate::pci::ac97_bus_master::AudioThreadInfo;
42 use crate::pci::ac97_bus_master::GuestMemoryError;
43 use crate::pci::ac97_bus_master::GuestMemoryResult;
44 use crate::pci::ac97_mixer::Ac97Mixer;
45 use crate::pci::ac97_regs::*;
46
47 // Internal error type used for reporting errors from the audio thread.
48 #[sorted]
49 #[derive(Error, Debug)]
50 pub(crate) enum AudioError {
51 // Failed to clone a descriptor.
52 #[error("Failed to clone a descriptor: {0}")]
53 CloneDescriptor(base::Error),
54 // Failed to create a shared memory.
55 #[error("Failed to create a shared memory: {0}.")]
56 CreateSharedMemory(base::Error),
57 // Failed to create a new stream.
58 #[error("Failed to create audio stream: {0}.")]
59 CreateStream(BoxError),
60 // Failure to get regions from guest memory.
61 #[error("Failed to get guest memory region: {0}.")]
62 GuestRegion(GuestMemoryError),
63 // Invalid buffer offset received from the audio server.
64 #[error("Offset > max usize")]
65 InvalidBufferOffset,
66 // Guest did not provide a buffer when needed.
67 #[error("No buffer was available from the Guest")]
68 NoBufferAvailable,
69 // Failure to read guest memory.
70 #[error("Failed to read guest memory: {0}.")]
71 ReadingGuestError(GuestMemoryError),
72 // Failure to respond to the ServerRequest.
73 #[error("Failed to respond to the ServerRequest: {0}")]
74 RespondRequest(BoxError),
75 // Failure to wait for a request from the stream.
76 #[error("Failed to wait for a message from the stream: {0}")]
77 WaitForAction(BoxError),
78 }
79
80 // Unix specific members of Ac97BusMaster - a placeholder.
81 #[derive(Default)]
82 pub struct Ac97BusMasterSys {}
83
84 impl AudioThreadInfo {
start(&mut self, mut worker: AudioWorker)85 fn start(&mut self, mut worker: AudioWorker) {
86 const AUDIO_THREAD_RTPRIO: u16 = 10; // Matches other cros audio clients.
87 self.thread_run.store(true, Ordering::Relaxed);
88 self.thread = Some(thread::spawn(move || {
89 if let Err(e) = set_rt_prio_limit(u64::from(AUDIO_THREAD_RTPRIO))
90 .and_then(|_| set_rt_round_robin(i32::from(AUDIO_THREAD_RTPRIO)))
91 {
92 warn!("Failed to set audio thread to real time: {}", e);
93 }
94
95 if let Err(e) = worker.run() {
96 error!("{:?} error: {}", worker.func, e);
97 }
98
99 worker.thread_run.store(false, Ordering::Relaxed);
100 }));
101
102 self.stream_control = Some(Box::new(NoopStreamControl::new()));
103 }
104 }
105
106 impl Ac97BusMaster {
107 /// Creates an Ac97BusMaster` object that plays audio from `mem` to streams provided by
108 /// `audio_server`.
new(mem: GuestMemory, audio_server: AudioStreamSource) -> Self109 pub(crate) fn new(mem: GuestMemory, audio_server: AudioStreamSource) -> Self {
110 Ac97BusMaster {
111 mem,
112 regs: Arc::new(Mutex::new(Ac97BusMasterRegs::new())),
113 acc_sema: 0,
114
115 po_info: AudioThreadInfo::new(),
116 pi_info: AudioThreadInfo::new(),
117 pmic_info: AudioThreadInfo::new(),
118 audio_server,
119
120 irq_resample_thread: None,
121 sys: Default::default(),
122 }
123 }
124
125 /// Returns any file descriptors that need to be kept open when entering a jail.
keep_rds(&self) -> Option<Vec<RawDescriptor>>126 pub fn keep_rds(&self) -> Option<Vec<RawDescriptor>> {
127 let mut rds = self.audio_server.keep_fds();
128 rds.append(&mut self.mem.as_raw_descriptors());
129 Some(rds)
130 }
131
check_and_move_to_next_buffer( func_regs: &mut Ac97FunctionRegs, )132 pub(in crate::pci::ac97_bus_master) fn check_and_move_to_next_buffer(
133 func_regs: &mut Ac97FunctionRegs,
134 ) {
135 if func_regs.sr & SR_CELV != 0 {
136 // CELV means we'd already processed the buffer at CIV.
137 // Move CIV to the next buffer now that LVI has moved.
138 func_regs.move_to_next_buffer();
139 }
140 }
141
thread_semaphore_notify(&self, func: Ac97Function)142 pub(in crate::pci::ac97_bus_master) fn thread_semaphore_notify(&self, func: Ac97Function) {
143 match func {
144 Ac97Function::Input => self.pi_info.thread_semaphore.notify_one(),
145 Ac97Function::Output => self.po_info.thread_semaphore.notify_one(),
146 Ac97Function::Microphone => self.pmic_info.thread_semaphore.notify_one(),
147 }
148 }
149
stream_effects(func: Ac97Function) -> Vec<StreamEffect>150 fn stream_effects(func: Ac97Function) -> Vec<StreamEffect> {
151 match func {
152 Ac97Function::Microphone => vec![StreamEffect::EchoCancellation],
153 _ => vec![StreamEffect::NoEffect],
154 }
155 }
156
create_audio_worker( &mut self, mixer: &Ac97Mixer, func: Ac97Function, ) -> AudioResult<AudioWorker>157 fn create_audio_worker(
158 &mut self,
159 mixer: &Ac97Mixer,
160 func: Ac97Function,
161 ) -> AudioResult<AudioWorker> {
162 let direction = match func {
163 Ac97Function::Microphone => StreamDirection::Capture,
164 Ac97Function::Input => StreamDirection::Capture,
165 Ac97Function::Output => StreamDirection::Playback,
166 };
167
168 let locked_regs = self.regs.lock();
169 let sample_rate = self.current_sample_rate(func, mixer);
170 let buffer_samples = current_buffer_size(locked_regs.func_regs(func), &self.mem)?;
171 let num_channels = locked_regs.tube_count(func);
172 let buffer_frames = buffer_samples / num_channels;
173
174 let mut pending_buffers = VecDeque::with_capacity(2);
175 let starting_offsets = match direction {
176 StreamDirection::Capture => {
177 let mut offsets = [0, 0];
178 for offset in &mut offsets {
179 let buffer = next_guest_buffer(&locked_regs, &self.mem, func, 0)?
180 .ok_or(AudioError::NoBufferAvailable)?;
181 *offset = buffer.offset as u64;
182 pending_buffers.push_back(Some(buffer));
183 }
184 offsets
185 }
186 StreamDirection::Playback => [0, 0],
187 };
188
189 // Create a `base::SharedMemory` object from a descriptor backing `self.mem`.
190 // This creation is expected to succeed because we can assume that `self.mem` was created
191 // from a `SharedMemory` object and its type was generalized to `dyn AsRawDescriptor`.
192 let desc: &dyn AsRawDescriptor = self
193 .mem
194 .offset_region(starting_offsets[0])
195 .map_err(|e| AudioError::GuestRegion(GuestMemoryError::ReadingGuestBufferAddress(e)))?;
196 let shm = {
197 let rd = base::clone_descriptor(desc).map_err(AudioError::CloneDescriptor)?;
198 // Safe because the fd is owned.
199 let sd = unsafe { base::SafeDescriptor::from_raw_descriptor(rd) };
200 base::SharedMemory::from_safe_descriptor(sd, None)
201 .map_err(AudioError::CreateSharedMemory)?
202 };
203
204 let stream = self
205 .audio_server
206 .new_stream(
207 direction,
208 num_channels,
209 SampleFormat::S16LE,
210 sample_rate,
211 buffer_frames,
212 &Self::stream_effects(func),
213 &shm,
214 starting_offsets,
215 )
216 .map_err(AudioError::CreateStream)?;
217
218 let params = AudioWorkerParams {
219 func,
220 stream,
221 pending_buffers,
222 message_interval: Duration::from_secs_f64(buffer_frames as f64 / sample_rate as f64),
223 };
224 Ok(AudioWorker::new(self, params))
225 }
226
start_audio( &mut self, func: Ac97Function, mixer: &Ac97Mixer, ) -> AudioResult<()>227 pub(in crate::pci::ac97_bus_master) fn start_audio(
228 &mut self,
229 func: Ac97Function,
230 mixer: &Ac97Mixer,
231 ) -> AudioResult<()> {
232 let audio_worker = self.create_audio_worker(mixer, func)?;
233 self.thread_info_mut(func).start(audio_worker);
234 self.update_mixer_settings(mixer);
235 Ok(())
236 }
237 }
238
239 #[derive(Debug)]
240 struct GuestBuffer {
241 offset: usize,
242 frames: usize,
243 }
244
get_buffer_offset( func_regs: &Ac97FunctionRegs, mem: &GuestMemory, index: u8, ) -> GuestMemoryResult<u64>245 fn get_buffer_offset(
246 func_regs: &Ac97FunctionRegs,
247 mem: &GuestMemory,
248 index: u8,
249 ) -> GuestMemoryResult<u64> {
250 let descriptor_addr = func_regs.bdbar + u32::from(index) * DESCRIPTOR_LENGTH as u32;
251 let buffer_addr_reg: u32 = mem
252 .read_obj_from_addr(GuestAddress(u64::from(descriptor_addr)))
253 .map_err(GuestMemoryError::ReadingGuestBufferAddress)?;
254 let buffer_addr = GuestAddress((buffer_addr_reg & !0x03u32) as u64); // The address must be aligned to four bytes.
255
256 mem.offset_from_base(buffer_addr)
257 .map_err(GuestMemoryError::ReadingGuestBufferAddress)
258 }
259
260 // Gets the start address and length of the buffer at `civ + offset` from the
261 // guest.
262 // This will return `None` if `civ + offset` is past LVI; if the DMA controlled
263 // stopped bit is set, such as after an underrun where CIV hits LVI; or if
264 // `civ + offset == LVI and the CELV flag is set.
next_guest_buffer( regs: &Ac97BusMasterRegs, mem: &GuestMemory, func: Ac97Function, offset: usize, ) -> AudioResult<Option<GuestBuffer>>265 fn next_guest_buffer(
266 regs: &Ac97BusMasterRegs,
267 mem: &GuestMemory,
268 func: Ac97Function,
269 offset: usize,
270 ) -> AudioResult<Option<GuestBuffer>> {
271 let func_regs = regs.func_regs(func);
272 let offset = (offset % 32) as u8;
273 let index = (func_regs.civ + offset) % 32;
274
275 // Check that value is between `low` and `high` modulo some `n`.
276 fn check_between(low: u8, high: u8, value: u8) -> bool {
277 // If low <= high, value must be in the interval between them:
278 // 0 l h n
279 // ......+++++++......
280 (low <= high && (low <= value && value <= high)) ||
281 // If low > high, value must not be in the interval between them:
282 // 0 h l n
283 // +++++++++......++++
284 (low > high && (low <= value || value <= high))
285 }
286
287 // Check if
288 // * we're halted
289 // * `index` is not between CIV and LVI (mod 32)
290 // * `index is LVI and we've already processed LVI (SR_CELV is set)
291 // if any of these are true `index` isn't valid.
292 if func_regs.sr & SR_DCH != 0
293 || !check_between(func_regs.civ, func_regs.lvi, index)
294 || func_regs.sr & SR_CELV != 0
295 {
296 return Ok(None);
297 }
298
299 let offset = get_buffer_offset(func_regs, mem, index)?
300 .try_into()
301 .map_err(|_| AudioError::InvalidBufferOffset)?;
302 let frames = get_buffer_samples(func_regs, mem, index)? / regs.tube_count(func);
303
304 Ok(Some(GuestBuffer { offset, frames }))
305 }
306
307 // Runs and updates the offset within the stream shm where samples can be
308 // found/placed for shm playback/capture streams, respectively
309 struct AudioWorker {
310 func: Ac97Function,
311 regs: Arc<Mutex<Ac97BusMasterRegs>>,
312 mem: GuestMemory,
313 thread_run: Arc<AtomicBool>,
314 lvi_semaphore: Arc<Condvar>,
315 message_interval: Duration,
316 stream: Box<dyn ShmStream>,
317 pending_buffers: VecDeque<Option<GuestBuffer>>,
318 }
319
320 struct AudioWorkerParams {
321 func: Ac97Function,
322 stream: Box<dyn ShmStream>,
323 pending_buffers: VecDeque<Option<GuestBuffer>>,
324 message_interval: Duration,
325 }
326
327 impl AudioWorker {
new(bus_master: &Ac97BusMaster, args: AudioWorkerParams) -> Self328 fn new(bus_master: &Ac97BusMaster, args: AudioWorkerParams) -> Self {
329 Self {
330 func: args.func,
331 regs: bus_master.regs.clone(),
332 mem: bus_master.mem.clone(),
333 thread_run: bus_master.thread_info(args.func).thread_run.clone(),
334 lvi_semaphore: bus_master.thread_info(args.func).thread_semaphore.clone(),
335 message_interval: args.message_interval,
336 stream: args.stream,
337 pending_buffers: args.pending_buffers,
338 }
339 }
340
341 // Runs and updates the offset within the stream shm where samples can be
342 // found/placed for shm playback/capture streams, respectively
run(&mut self) -> AudioResult<()>343 fn run(&mut self) -> AudioResult<()> {
344 let func = self.func;
345 let message_interval = self.message_interval;
346 // Set up picb.
347 {
348 let mut locked_regs = self.regs.lock();
349 locked_regs.func_regs_mut(func).picb =
350 current_buffer_size(locked_regs.func_regs(func), &self.mem)? as u16;
351 }
352
353 'audio_loop: while self.thread_run.load(Ordering::Relaxed) {
354 {
355 let mut locked_regs = self.regs.lock();
356 while locked_regs.func_regs(func).sr & SR_DCH != 0 {
357 locked_regs = self.lvi_semaphore.wait(locked_regs);
358 if !self.thread_run.load(Ordering::Relaxed) {
359 break 'audio_loop;
360 }
361 }
362 }
363
364 let timeout = Duration::from_secs(1);
365 let action = self
366 .stream
367 .wait_for_next_action_with_timeout(timeout)
368 .map_err(AudioError::WaitForAction)?;
369
370 let request = match action {
371 None => {
372 warn!("No audio message received within timeout of {:?}", timeout);
373 continue;
374 }
375 Some(request) => request,
376 };
377 let start = Instant::now();
378
379 let next_buffer = {
380 let mut locked_regs = self.regs.lock();
381 if self.pending_buffers.len() == 2 {
382 // When we have two pending buffers and receive a request for
383 // another, we know that oldest buffer has been completed.
384 // However, if that old buffer was an empty buffer we sent
385 // because the guest driver had no available buffers, we don't
386 // want to mark a buffer complete.
387 if let Some(Some(_)) = self.pending_buffers.pop_front() {
388 buffer_completed(&mut locked_regs, &self.mem, self.func)?;
389 }
390 }
391
392 // We count the number of pending, real buffers at the server, and
393 // then use that as our offset from CIV.
394 let offset = self.pending_buffers.iter().filter(|e| e.is_some()).count();
395
396 // Get a buffer to respond to our request. If there's no buffer
397 // available, we'll wait one buffer interval and check again.
398 loop {
399 if let Some(buffer) = next_guest_buffer(&locked_regs, &self.mem, func, offset)?
400 {
401 break Some(buffer);
402 }
403 let elapsed = start.elapsed();
404 if elapsed > message_interval {
405 break None;
406 }
407 locked_regs = self
408 .lvi_semaphore
409 .wait_timeout(locked_regs, message_interval - elapsed)
410 .0;
411 }
412 };
413
414 match next_buffer {
415 Some(ref buffer) => {
416 let requested_frames = request.requested_frames();
417 if requested_frames != buffer.frames {
418 // We should be able to handle when the number of frames in
419 // the buffer doesn't match the number of frames requested,
420 // but we don't yet.
421 warn!(
422 "Stream requested {} frames but buffer had {} frames: {:?}",
423 requested_frames, buffer.frames, buffer
424 );
425 }
426
427 request
428 .set_buffer_offset_and_frames(
429 buffer.offset,
430 std::cmp::min(requested_frames, buffer.frames),
431 )
432 .map_err(AudioError::RespondRequest)?;
433 }
434 None => {
435 request
436 .ignore_request()
437 .map_err(AudioError::RespondRequest)?;
438 }
439 }
440 self.pending_buffers.push_back(next_buffer);
441 }
442 Ok(())
443 }
444 }
445
446 #[cfg(test)]
447 mod tests {
448 use audio_streams::shm_streams::MockShmStreamSource;
449
450 use super::*;
451 use crate::pci::ac97_bus_master::tests::capture_release_cold_reset_and_setup_ping_pong_buffers;
452 use crate::pci::ac97_bus_master::tests::check_buffer_set_and_clear_bcis;
453 use crate::pci::ac97_bus_master::tests::clear_lvb_and_reset_lvi;
454 use crate::pci::ac97_bus_master::tests::playback_release_cold_reset_and_setup_ping_pong_buffers;
455 use crate::pci::ac97_bus_master::tests::stop;
456
457 #[test]
run_multi_tube_playback_2()458 fn run_multi_tube_playback_2() {
459 start_playback(2, 48000);
460 }
461
462 #[test]
run_multi_tube_playback_4()463 fn run_multi_tube_playback_4() {
464 start_playback(4, 48000);
465 }
466
467 #[test]
run_multi_tube_playback_6()468 fn run_multi_tube_playback_6() {
469 start_playback(6, 48000);
470 }
471
472 #[test]
run_multi_rate_playback_32()473 fn run_multi_rate_playback_32() {
474 start_playback(2, 32000);
475 }
476
477 #[test]
run_multi_rate_playback_44()478 fn run_multi_rate_playback_44() {
479 start_playback(2, 44100);
480 }
481
482 #[test]
run_multi_rate_playback_48()483 fn run_multi_rate_playback_48() {
484 start_playback(2, 32000);
485 }
486
start_playback(num_channels: usize, rate: u16)487 fn start_playback(num_channels: usize, rate: u16) {
488 const TIMEOUT: Duration = Duration::from_millis(500);
489 const LVI_MASK: u8 = 0x1f; // Five bits for 32 total entries.
490 const IOC_MASK: u32 = 0x8000_0000; // Interrupt on completion.
491 let num_buffers = LVI_MASK as usize + 1;
492 const BUFFER_SIZE: usize = 32768;
493 const FRAGMENT_SIZE: usize = BUFFER_SIZE / 2;
494
495 const GUEST_ADDR_BASE: u32 = 0x100_0000;
496 let mem = GuestMemory::new(&[(GuestAddress(GUEST_ADDR_BASE as u64), 1024 * 1024 * 8)])
497 .expect("Creating guest memory failed.");
498 let stream_source = MockShmStreamSource::new();
499 let mut bm = Ac97BusMaster::new(mem.clone(), Box::new(stream_source.clone()));
500 let mut mixer = Ac97Mixer::new();
501
502 playback_release_cold_reset_and_setup_ping_pong_buffers(
503 PO_BASE_10,
504 &mut mixer,
505 &mut bm,
506 &mem,
507 num_buffers,
508 GUEST_ADDR_BASE,
509 FRAGMENT_SIZE,
510 LVI_MASK,
511 IOC_MASK,
512 );
513
514 assert_eq!(bm.readb(PO_CIV_14), 0);
515
516 // Set tube count and sample rate.
517 let mut cnt = bm.readl(GLOB_CNT_2C);
518 cnt &= !GLOB_CNT_PCM_246_MASK;
519 mixer.writew(MIXER_PCM_FRONT_DAC_RATE_2C, rate);
520 if num_channels == 4 {
521 cnt |= GLOB_CNT_PCM_4;
522 mixer.writew(MIXER_PCM_SURR_DAC_RATE_2E, rate);
523 } else if num_channels == 6 {
524 cnt |= GLOB_CNT_PCM_6;
525 mixer.writew(MIXER_PCM_LFE_DAC_RATE_30, rate);
526 }
527 bm.writel(GLOB_CNT_2C, cnt, &mut mixer);
528
529 // Start.
530 bm.writeb(PO_CR_1B, CR_IOCE | CR_RPBM, &mixer);
531 // TODO(crbug.com/1058881): The test is flaky in builder.
532 // assert_eq!(bm.readw(PO_PICB_18), 0);
533
534 let mut stream = stream_source.get_last_stream();
535 // Trigger callback and see that CIV has not changed, since only 1
536 // buffer has been sent.
537 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
538
539 assert_eq!(stream.num_channels(), num_channels);
540 assert_eq!(stream.frame_rate(), rate as u32);
541
542 let mut civ = bm.readb(PO_CIV_14);
543 assert_eq!(civ, 0);
544
545 // After two more callbacks, CIV should now be 1 since we know that the
546 // first buffer must have been played.
547 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
548 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
549 civ = bm.readb(PO_CIV_14);
550 assert_eq!(civ, 1);
551
552 check_buffer_set_and_clear_bcis(PO_BASE_10, &mixer, &mut bm);
553
554 std::thread::sleep(Duration::from_millis(50));
555 let picb = bm.readw(PO_PICB_18, &mixer);
556 let pos = (FRAGMENT_SIZE - (picb as usize * 2)) / 4;
557
558 // Check that frames are consumed at least at a reasonable rate.
559 // This can't be exact as during unit tests the thread scheduling is highly variable, so the
560 // test only checks that some samples are consumed.
561 assert!(pos > 0);
562 assert!(bm.readw(PO_SR_16, &mixer) & SR_DCH == 0); // DMA is running.
563
564 // Set last valid to next buffer to be sent and trigger callback so we hit it.
565 bm.writeb(PO_LVI_15, civ + 2, &mixer);
566 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
567 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
568 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
569 assert!(bm.readw(PO_SR_16, &mixer) & SR_LVBCI != 0); // Hit last buffer
570 assert!(bm.readw(PO_SR_16, &mixer) & SR_DCH == SR_DCH); // DMA stopped because of lack of buffers.
571 assert!(bm.readw(PO_SR_16, &mixer) & SR_CELV == SR_CELV); // Processed the last buffer
572 assert_eq!(bm.readb(PO_LVI_15), bm.readb(PO_CIV_14));
573 assert!(
574 bm.readl(GLOB_STA_30) & GS_POINT != 0,
575 "POINT bit should be set."
576 );
577
578 clear_lvb_and_reset_lvi(PO_BASE_10, &mixer, &mut bm, LVI_MASK);
579
580 let restart_civ = bm.readb(PO_CIV_14);
581 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
582 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
583 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
584 assert!(bm.readb(PO_CIV_14) != restart_civ);
585
586 stop(PO_BASE_10, GS_POINT, &mixer, &mut bm);
587 }
588
589 #[test]
run_capture_input()590 fn run_capture_input() {
591 start_capture(Ac97Function::Input);
592 }
593
594 #[test]
run_capture_microphone()595 fn run_capture_microphone() {
596 start_capture(Ac97Function::Microphone);
597 }
598
start_capture(func: Ac97Function)599 fn start_capture(func: Ac97Function) {
600 const TIMEOUT: Duration = Duration::from_millis(500);
601 const LVI_MASK: u8 = 0x1f; // Five bits for 32 total entries.
602 const IOC_MASK: u32 = 0x8000_0000; // Interrupt on completion.
603 let num_buffers = LVI_MASK as usize + 1;
604 const BUFFER_SIZE: usize = 32768;
605 const FRAGMENT_SIZE: usize = BUFFER_SIZE / 2;
606
607 const GUEST_ADDR_BASE: u32 = 0x100_0000;
608 let mem = GuestMemory::new(&[(GuestAddress(GUEST_ADDR_BASE as u64), 1024 * 1024 * 8)])
609 .expect("Creating guest memory failed.");
610 let stream_source = MockShmStreamSource::new();
611 let mut bm = Ac97BusMaster::new(mem.clone(), Box::new(stream_source.clone()));
612 let mut mixer = Ac97Mixer::new();
613
614 let (base, bdbar_addr, lvi_addr, cr_addr, civ_addr, pcib_addr, sr_addr, int_mask) =
615 match func {
616 Ac97Function::Input => (
617 PI_BASE_00,
618 PI_BDBAR_00,
619 PI_LVI_05,
620 PI_CR_0B,
621 PI_CIV_04,
622 PI_PICB_08,
623 PI_SR_06,
624 GS_PIINT,
625 ),
626 Ac97Function::Microphone => (
627 MC_BASE_20,
628 MC_BDBAR_20,
629 MC_LVI_25,
630 MC_CR_2B,
631 MC_CIV_24,
632 MC_PICB_28,
633 MC_SR_26,
634 GS_MINT,
635 ),
636 _ => {
637 panic!("Invalid Ac97Function.");
638 }
639 };
640
641 capture_release_cold_reset_and_setup_ping_pong_buffers(
642 bdbar_addr,
643 &mut mixer,
644 &mut bm,
645 &mem,
646 num_buffers,
647 GUEST_ADDR_BASE,
648 FRAGMENT_SIZE,
649 LVI_MASK,
650 IOC_MASK,
651 );
652
653 // Start.
654 bm.writeb(cr_addr, CR_IOCE | CR_RPBM, &mixer);
655 // TODO(crbug.com/1086337): Test flakiness in build time.
656 // assert_eq!(bm.readw(PI_PICB_08), 0);
657
658 let mut stream = stream_source.get_last_stream();
659 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
660
661 // CIV is 1 here since we preemptively sent two buffer indices to the
662 // server before creating the stream. When we triggered the callback
663 // above, that means the first of those buffers was filled, so CIV
664 // increments to 1.
665 let civ = bm.readb(civ_addr);
666 assert_eq!(civ, 1);
667 std::thread::sleep(Duration::from_millis(20));
668 let picb = bm.readw(pcib_addr, &mixer);
669 assert!(picb > 0);
670 assert!(bm.readw(sr_addr, &mixer) & SR_DCH == 0); // DMA is running.
671
672 // Trigger 2 callbacks so that we'll move to buffer 3 since at that
673 // point we can be certain that buffers 1 and 2 have been captured to.
674 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
675 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
676 assert_eq!(bm.readb(civ_addr), 3);
677
678 let civ = bm.readb(civ_addr);
679 // Sets LVI to CIV + 2 to trigger last buffer hit
680 bm.writeb(lvi_addr, civ + 2, &mixer);
681 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
682 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
683 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
684 assert_ne!(bm.readw(sr_addr, &mixer) & SR_LVBCI, 0); // Hit last buffer
685 assert_eq!(bm.readw(sr_addr, &mixer) & SR_DCH, SR_DCH); // DMA stopped because of lack of buffers.
686 assert_eq!(bm.readw(sr_addr, &mixer) & SR_CELV, SR_CELV);
687 assert_eq!(bm.readb(lvi_addr), bm.readb(civ_addr));
688 assert!(
689 bm.readl(GLOB_STA_30) & int_mask != 0,
690 "int_mask bit should be set."
691 );
692
693 clear_lvb_and_reset_lvi(base, &mixer, &mut bm, LVI_MASK);
694
695 let restart_civ = bm.readb(civ_addr);
696 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
697 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
698 assert!(stream.trigger_callback_with_timeout(TIMEOUT));
699 assert_ne!(bm.readb(civ_addr), restart_civ);
700
701 stop(base, int_mask, &mixer, &mut bm);
702 }
703 }
704