1#[cfg(test)]
4mod fake;
5
6use super::common::Feature;
7use crate::{
8 config::{read_config, ReadOnly},
9 queue::{owning::OwningQueue, VirtQueue},
10 transport::{InterruptStatus, Transport},
11 Error, Hal, Result, PAGE_SIZE,
12};
13use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
14use bitflags::bitflags;
15use core::{
16 array,
17 fmt::{self, Debug, Display, Formatter},
18 hint::spin_loop,
19 mem::size_of,
20 ops::RangeInclusive,
21};
22use enumn::N;
23use log::{error, info, warn};
24use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout};
25
26pub struct VirtIOSound<H: Hal, T: Transport> {
32 transport: T,
33
34 control_queue: VirtQueue<H, { QUEUE_SIZE as usize }>,
35 event_queue: OwningQueue<H, { QUEUE_SIZE as usize }, { size_of::<VirtIOSndEvent>() }>,
36 tx_queue: VirtQueue<H, { QUEUE_SIZE as usize }>,
37 rx_queue: VirtQueue<H, { QUEUE_SIZE as usize }>,
38
39 negotiated_features: Feature,
40
41 jacks: u32,
42 streams: u32,
43 chmaps: u32,
44
45 pcm_infos: Option<Vec<VirtIOSndPcmInfo>>,
46 jack_infos: Option<Vec<VirtIOSndJackInfo>>,
47 chmap_infos: Option<Vec<VirtIOSndChmapInfo>>,
48
49 pcm_parameters: Vec<PcmParameters>,
51
52 queue_buf_send: Box<[u8]>,
53 queue_buf_recv: Box<[u8]>,
54
55 set_up: bool,
56
57 token_rsp: BTreeMap<u16, Box<VirtIOSndPcmStatus>>, pcm_states: Vec<PCMState>,
60
61 token_buf: BTreeMap<u16, Vec<u8>>, }
63
64impl<H: Hal, T: Transport> VirtIOSound<H, T> {
65 pub fn new(mut transport: T) -> Result<Self> {
67 let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
68 info!(
69 "[sound device] negotiated_features: {:?}",
70 negotiated_features
71 );
72
73 let control_queue = VirtQueue::new(
74 &mut transport,
75 CONTROL_QUEUE_IDX,
76 negotiated_features.contains(Feature::RING_INDIRECT_DESC),
77 negotiated_features.contains(Feature::RING_EVENT_IDX),
78 )?;
79 let event_queue = OwningQueue::new(VirtQueue::new(
80 &mut transport,
81 EVENT_QUEUE_IDX,
82 negotiated_features.contains(Feature::RING_INDIRECT_DESC),
83 negotiated_features.contains(Feature::RING_EVENT_IDX),
84 )?)?;
85 let tx_queue = VirtQueue::new(
86 &mut transport,
87 TX_QUEUE_IDX,
88 negotiated_features.contains(Feature::RING_INDIRECT_DESC),
89 negotiated_features.contains(Feature::RING_EVENT_IDX),
90 )?;
91 let rx_queue = VirtQueue::new(
92 &mut transport,
93 RX_QUEUE_IDX,
94 negotiated_features.contains(Feature::RING_INDIRECT_DESC),
95 negotiated_features.contains(Feature::RING_EVENT_IDX),
96 )?;
97
98 let jacks = read_config!(transport, VirtIOSoundConfig, jacks)?;
100 let streams = read_config!(transport, VirtIOSoundConfig, streams)?;
101 let chmaps = read_config!(transport, VirtIOSoundConfig, chmaps)?;
102 info!(
103 "[sound device] config: jacks: {}, streams: {}, chmaps: {}",
104 jacks, streams, chmaps
105 );
106
107 let queue_buf_send = FromZeros::new_box_zeroed_with_elems(PAGE_SIZE).unwrap();
108 let queue_buf_recv = FromZeros::new_box_zeroed_with_elems(PAGE_SIZE).unwrap();
109
110 let mut pcm_parameters = vec![];
112 for _ in 0..streams {
113 pcm_parameters.push(PcmParameters::default());
114 }
115
116 transport.finish_init();
117
118 if event_queue.should_notify() {
119 transport.notify(EVENT_QUEUE_IDX);
120 }
121
122 Ok(VirtIOSound {
123 transport,
124 control_queue,
125 event_queue,
126 tx_queue,
127 rx_queue,
128 negotiated_features,
129 jacks,
130 streams,
131 chmaps,
132 pcm_infos: None,
133 jack_infos: None,
134 chmap_infos: None,
135 queue_buf_send,
136 queue_buf_recv,
137 pcm_parameters,
138 set_up: false,
139 token_rsp: BTreeMap::new(),
140 pcm_states: vec![],
141 token_buf: BTreeMap::new(),
142 })
143 }
144
145 pub fn jacks(&self) -> u32 {
147 self.jacks
148 }
149
150 pub fn streams(&self) -> u32 {
152 self.streams
153 }
154
155 pub fn chmaps(&self) -> u32 {
157 self.chmaps
158 }
159
160 pub fn ack_interrupt(&mut self) -> InterruptStatus {
162 self.transport.ack_interrupt()
163 }
164
165 fn request<Req: IntoBytes + Immutable>(&mut self, req: Req) -> Result<VirtIOSndHdr> {
166 self.control_queue.add_notify_wait_pop(
167 &[req.as_bytes()],
168 &mut [self.queue_buf_recv.as_mut_bytes()],
169 &mut self.transport,
170 )?;
171 Ok(VirtIOSndHdr::read_from_prefix(&self.queue_buf_recv)
172 .unwrap()
173 .0)
174 }
175
176 fn set_up(&mut self) -> Result<()> {
178 if let Ok(jack_infos) = self.jack_info(0, self.jacks) {
180 for jack_info in &jack_infos {
181 info!("[sound device] jack_info: {}", jack_info);
182 }
183 self.jack_infos = Some(jack_infos);
184 } else {
185 self.jack_infos = Some(vec![]);
186 warn!("[sound device] Error getting jack infos");
187 }
188
189 let pcm_infos = self.pcm_info(0, self.streams)?;
191 for pcm_info in &pcm_infos {
192 info!("[sound device] pcm_info: {}", pcm_info);
193 }
194 self.pcm_infos = Some(pcm_infos);
195
196 if let Ok(chmap_infos) = self.chmap_info(0, self.chmaps) {
198 for chmap_info in &chmap_infos {
199 info!("[sound device] chmap_info: {}", chmap_info);
200 }
201 self.chmap_infos = Some(chmap_infos);
202 } else {
203 self.chmap_infos = Some(vec![]);
204 warn!("[sound device] Error getting chmap infos");
205 }
206
207 for _ in 0..self.streams {
209 self.pcm_states.push(PCMState::default());
210 }
211 Ok(())
212 }
213
214 pub fn enable_interrupts(&mut self, enable: bool) {
216 self.event_queue.set_dev_notify(enable);
217 }
218
219 fn jack_info(&mut self, jack_start_id: u32, jack_count: u32) -> Result<Vec<VirtIOSndJackInfo>> {
221 if jack_start_id + jack_count > self.jacks {
222 error!("jack_start_id + jack_count > jacks! There are not enough jacks to be queried!");
223 return Err(Error::IoError);
224 }
225 let hdr = self.request(VirtIOSndQueryInfo {
226 hdr: ItemInformationRequestType::RJackInfo.into(),
227 start_id: jack_start_id,
228 count: jack_count,
229 size: size_of::<VirtIOSndJackInfo>() as u32,
230 })?;
231 if hdr != RequestStatusCode::Ok.into() {
232 return Err(Error::IoError);
233 }
234 let mut jack_infos = vec![];
236 for i in 0..jack_count as usize {
237 const HDR_SIZE: usize = size_of::<VirtIOSndHdr>();
238 const JACK_INFO_SIZE: usize = size_of::<VirtIOSndJackInfo>();
239 let start_byte_idx = HDR_SIZE + i * JACK_INFO_SIZE;
240 let end_byte_idx = HDR_SIZE + (i + 1) * JACK_INFO_SIZE;
241 let jack_info = VirtIOSndJackInfo::read_from_bytes(
242 &self.queue_buf_recv[start_byte_idx..end_byte_idx],
243 )
244 .unwrap();
245 jack_infos.push(jack_info)
246 }
247 Ok(jack_infos)
248 }
249
250 fn pcm_info(
252 &mut self,
253 stream_start_id: u32,
254 stream_count: u32,
255 ) -> Result<Vec<VirtIOSndPcmInfo>> {
256 if stream_start_id + stream_count > self.streams {
257 error!("stream_start_id + stream_count > streams! There are not enough streams to be queried!");
258 return Err(Error::IoError);
259 }
260 let request_hdr = VirtIOSndHdr::from(ItemInformationRequestType::RPcmInfo);
261 let hdr = self.request(VirtIOSndQueryInfo {
262 hdr: request_hdr,
263 start_id: stream_start_id,
264 count: stream_count,
265 size: size_of::<VirtIOSndPcmInfo>() as u32,
266 })?;
267 if hdr != RequestStatusCode::Ok.into() {
268 return Err(Error::IoError);
269 }
270 let mut pcm_infos = vec![];
272 for i in 0..stream_count as usize {
273 const HDR_SIZE: usize = size_of::<VirtIOSndHdr>();
274 const PCM_INFO_SIZE: usize = size_of::<VirtIOSndPcmInfo>();
275 let start_byte_idx = HDR_SIZE + i * PCM_INFO_SIZE;
276 let end_byte_idx = HDR_SIZE + (i + 1) * PCM_INFO_SIZE;
277 let pcm_info = VirtIOSndPcmInfo::read_from_bytes(
278 &self.queue_buf_recv[start_byte_idx..end_byte_idx],
279 )
280 .unwrap();
281 pcm_infos.push(pcm_info);
282 }
283 Ok(pcm_infos)
284 }
285
286 fn chmap_info(
288 &mut self,
289 chmaps_start_id: u32,
290 chmaps_count: u32,
291 ) -> Result<Vec<VirtIOSndChmapInfo>> {
292 if chmaps_start_id + chmaps_count > self.chmaps {
293 error!("chmaps_start_id + chmaps_count > self.chmaps");
294 return Err(Error::IoError);
295 }
296 let hdr = self.request(VirtIOSndQueryInfo {
297 hdr: ItemInformationRequestType::RChmapInfo.into(),
298 start_id: chmaps_start_id,
299 count: chmaps_count,
300 size: size_of::<VirtIOSndChmapInfo>() as u32,
301 })?;
302 if hdr != RequestStatusCode::Ok.into() {
303 return Err(Error::IoError);
304 }
305 let mut chmap_infos = vec![];
306 for i in 0..chmaps_count as usize {
307 const OFFSET: usize = size_of::<VirtIOSndHdr>();
308 let start_byte = OFFSET + i * size_of::<VirtIOSndChmapInfo>();
309 let end_byte = OFFSET + (i + 1) * size_of::<VirtIOSndChmapInfo>();
310 let chmap_info =
311 VirtIOSndChmapInfo::read_from_bytes(&self.queue_buf_recv[start_byte..end_byte])
312 .unwrap();
313 chmap_infos.push(chmap_info);
314 }
315 Ok(chmap_infos)
316 }
317
318 pub fn jack_remap(&mut self, jack_id: u32, association: u32, sequence: u32) -> Result {
324 if !self.set_up {
325 self.set_up()?;
326 self.set_up = true;
327 }
328 if self.jacks == 0 {
329 error!("[sound device] There is no available jacks!");
330 return Err(Error::InvalidParam);
331 }
332 if jack_id >= self.jacks {
333 error!("jack_id >= self.jacks! Make sure jack_id is in the range of [0, jacks - 1)!");
334 return Err(Error::InvalidParam);
335 }
336
337 let jack_features = JackFeatures::from_bits_retain(
338 self.jack_infos
339 .as_ref()
340 .unwrap()
341 .get(jack_id as usize)
342 .unwrap()
343 .features,
344 );
345 if !jack_features.contains(JackFeatures::REMAP) {
346 error!("The jack selected does not support VIRTIO_SND_JACK_F_REMAP!");
347 return Err(Error::Unsupported);
348 }
349 let hdr = self.request(VirtIOSndJackRemap {
350 hdr: VirtIOSndJackHdr {
351 hdr: CommandCode::RJackRemap.into(),
352 jack_id,
353 },
354 association,
355 sequence,
356 })?;
357 if hdr == RequestStatusCode::Ok.into() {
358 Ok(())
359 } else {
360 Err(Error::Unsupported)
361 }
362 }
363
364 pub fn pcm_set_params(
366 &mut self,
367 stream_id: u32,
368 buffer_bytes: u32,
369 period_bytes: u32,
370 features: PcmFeatures,
371 channels: u8,
372 format: PcmFormat,
373 rate: PcmRate,
374 ) -> Result {
375 if !self.set_up {
376 self.set_up()?;
377 self.set_up = true;
378 }
379 if period_bytes == 0 || period_bytes > buffer_bytes || buffer_bytes % period_bytes != 0 {
380 return Err(Error::InvalidParam);
381 }
382 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmSetParams);
383 let rsp = self.request(VirtIOSndPcmSetParams {
384 hdr: VirtIOSndPcmHdr {
385 hdr: request_hdr,
386 stream_id,
387 },
388 buffer_bytes,
389 period_bytes,
390 features: features.bits(),
391 channels,
392 format: format.into(),
393 rate: rate.into(),
394 _padding: 0,
395 })?;
396 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
398 self.pcm_parameters[stream_id as usize] = PcmParameters {
399 setup: true,
400 buffer_bytes,
401 period_bytes,
402 features,
403 channels,
404 format,
405 rate,
406 };
407 Ok(())
408 } else {
409 Err(Error::IoError)
410 }
411 }
412
413 pub fn pcm_prepare(&mut self, stream_id: u32) -> Result {
415 if !self.set_up {
416 self.set_up()?;
417 self.set_up = true;
418 }
419 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmPrepare);
420 let rsp = self.request(VirtIOSndPcmHdr {
421 hdr: request_hdr,
422 stream_id,
423 })?;
424 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
426 Ok(())
427 } else {
428 Err(Error::IoError)
429 }
430 }
431
432 pub fn pcm_release(&mut self, stream_id: u32) -> Result {
434 if !self.set_up {
435 self.set_up()?;
436 self.set_up = true;
437 }
438 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmRelease);
439 let rsp = self.request(VirtIOSndPcmHdr {
440 hdr: request_hdr,
441 stream_id,
442 })?;
443 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
445 Ok(())
446 } else {
447 Err(Error::IoError)
448 }
449 }
450
451 pub fn pcm_start(&mut self, stream_id: u32) -> Result {
453 if !self.set_up {
454 self.set_up()?;
455 self.set_up = true;
456 }
457 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmStart);
458 let rsp = self.request(VirtIOSndPcmHdr {
459 hdr: request_hdr,
460 stream_id,
461 })?;
462 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
464 Ok(())
465 } else {
466 Err(Error::IoError)
467 }
468 }
469
470 pub fn pcm_stop(&mut self, stream_id: u32) -> Result {
472 if !self.set_up {
473 self.set_up()?;
474 self.set_up = true;
475 }
476 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmStop);
477 let rsp = self.request(VirtIOSndPcmHdr {
478 hdr: request_hdr,
479 stream_id,
480 })?;
481 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
483 Ok(())
484 } else {
485 Err(Error::IoError)
486 }
487 }
488
489 pub fn pcm_xfer(&mut self, stream_id: u32, frames: &[u8]) -> Result {
495 const U32_SIZE: usize = size_of::<u32>();
496 if !self.set_up {
497 self.set_up()?;
498 self.set_up = true;
499 }
500 if !self.pcm_parameters[stream_id as usize].setup {
501 warn!("Please set parameters for a stream before using it!");
502 return Err(Error::IoError);
503 }
504
505 let stream_id_bytes = stream_id.to_le_bytes();
506 let period_size = self.pcm_parameters[stream_id as usize].period_bytes as usize;
507
508 let mut remaining_buffers = frames.chunks(period_size);
509 let mut buffers: [Option<&[u8]>; QUEUE_SIZE as usize] = [None; QUEUE_SIZE as usize];
510 let mut statuses: [VirtIOSndPcmStatus; QUEUE_SIZE as usize] =
511 array::from_fn(|_| Default::default());
512 let mut tokens = [0; QUEUE_SIZE as usize];
513 let mut head = 0;
515 let mut tail = 0;
517
518 loop {
519 if self.tx_queue.available_desc() >= 3 {
522 if let Some(buffer) = remaining_buffers.next() {
523 tokens[head] = unsafe {
526 self.tx_queue.add(
527 &[&stream_id_bytes, buffer],
528 &mut [statuses[head].as_mut_bytes()],
529 )?
530 };
531 if self.tx_queue.should_notify() {
532 self.transport.notify(TX_QUEUE_IDX);
533 }
534 buffers[head] = Some(buffer);
535 head += 1;
536 if head >= usize::from(QUEUE_SIZE) {
537 head = 0;
538 }
539 } else if head == tail {
540 break;
541 }
542 }
543 if self.tx_queue.can_pop() {
544 unsafe {
548 self.tx_queue.pop_used(
549 tokens[tail],
550 &[&stream_id_bytes, buffers[tail].unwrap()],
551 &mut [statuses[tail].as_mut_bytes()],
552 )?;
553 }
554 if statuses[tail].status != CommandCode::SOk.into() {
555 return Err(Error::IoError);
556 }
557 tail += 1;
558 if tail >= usize::from(QUEUE_SIZE) {
559 tail = 0;
560 }
561 }
562 spin_loop();
563 }
564
565 Ok(())
566 }
567
568 pub fn pcm_xfer_nb(&mut self, stream_id: u32, frames: &[u8]) -> Result<u16> {
576 if !self.set_up {
577 self.set_up()?;
578 self.set_up = true;
579 }
580 if !self.pcm_parameters[stream_id as usize].setup {
581 warn!("Please set parameters for a stream before using it!");
582 return Err(Error::IoError);
583 }
584 const U32_SIZE: usize = size_of::<u32>();
585 let period_size: usize = self.pcm_parameters[stream_id as usize].period_bytes as usize;
586 assert_eq!(period_size, frames.len());
587 let mut buf = vec![0; U32_SIZE + period_size];
588 buf[..U32_SIZE].copy_from_slice(&stream_id.to_le_bytes());
589 buf[U32_SIZE..U32_SIZE + period_size].copy_from_slice(frames);
590 let mut rsp = VirtIOSndPcmStatus::new_box_zeroed().unwrap();
591
592 let token = unsafe { self.tx_queue.add(&[&buf], &mut [rsp.as_mut_bytes()])? };
596
597 if self.tx_queue.should_notify() {
598 self.transport.notify(TX_QUEUE_IDX);
599 }
600 self.token_buf.insert(token, buf);
601 self.token_rsp.insert(token, rsp);
602 Ok(token)
603 }
604
605 pub fn pcm_xfer_ok(&mut self, token: u16) -> Result {
607 assert!(self.token_buf.contains_key(&token));
608 assert!(self.token_rsp.contains_key(&token));
609
610 unsafe {
613 self.tx_queue.pop_used(
614 token,
615 &[&self.token_buf[&token]],
616 &mut [self.token_rsp.get_mut(&token).unwrap().as_mut_bytes()],
617 )?;
618 }
619
620 self.token_buf.remove(&token);
621 self.token_rsp.remove(&token);
622 Ok(())
623 }
624
625 pub fn output_streams(&mut self) -> Result<Vec<u32>> {
627 if !self.set_up {
628 self.set_up()?;
629 self.set_up = true;
630 }
631 Ok(self
632 .pcm_infos
633 .as_ref()
634 .unwrap()
635 .iter()
636 .enumerate()
637 .filter(|(_, info)| info.direction == VIRTIO_SND_D_OUTPUT)
638 .map(|(idx, _)| idx as u32)
639 .collect())
640 }
641
642 pub fn input_streams(&mut self) -> Result<Vec<u32>> {
644 if !self.set_up {
645 self.set_up()?;
646 self.set_up = true;
647 }
648 Ok(self
649 .pcm_infos
650 .as_ref()
651 .unwrap()
652 .iter()
653 .enumerate()
654 .filter(|(_, info)| info.direction == VIRTIO_SND_D_INPUT)
655 .map(|(idx, _)| idx as u32)
656 .collect())
657 }
658
659 pub fn rates_supported(&mut self, stream_id: u32) -> Result<PcmRates> {
661 if !self.set_up {
662 self.set_up()?;
663 self.set_up = true;
664 }
665 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
666 return Err(Error::InvalidParam);
667 }
668 Ok(PcmRates::from_bits_retain(
669 self.pcm_infos.as_ref().unwrap()[stream_id as usize].rates,
670 ))
671 }
672
673 pub fn formats_supported(&mut self, stream_id: u32) -> Result<PcmFormats> {
675 if !self.set_up {
676 self.set_up()?;
677 self.set_up = true;
678 }
679 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
680 return Err(Error::InvalidParam);
681 }
682 Ok(PcmFormats::from_bits_retain(
683 self.pcm_infos.as_ref().unwrap()[stream_id as usize].formats,
684 ))
685 }
686
687 pub fn channel_range_supported(&mut self, stream_id: u32) -> Result<RangeInclusive<u8>> {
689 if !self.set_up {
690 self.set_up()?;
691 self.set_up = true;
692 }
693 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
694 return Err(Error::InvalidParam);
695 }
696 let pcm_info = &self.pcm_infos.as_ref().unwrap()[stream_id as usize];
697 Ok(pcm_info.channels_min..=pcm_info.channels_max)
698 }
699
700 pub fn features_supported(&mut self, stream_id: u32) -> Result<PcmFeatures> {
702 if !self.set_up {
703 self.set_up()?;
704 self.set_up = true;
705 }
706 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
707 return Err(Error::InvalidParam);
708 }
709 let pcm_info = &self.pcm_infos.as_ref().unwrap()[stream_id as usize];
710 Ok(PcmFeatures::from_bits_retain(pcm_info.features))
711 }
712
713 pub fn latest_notification(&mut self) -> Result<Option<Notification>> {
715 self.event_queue.poll(&mut self.transport, |buffer| {
718 if let Ok(event) = VirtIOSndEvent::read_from_bytes(buffer) {
719 Ok(Some(Notification {
720 notification_type: NotificationType::n(event.hdr.command_code)
721 .ok_or(Error::IoError)?,
722 data: event.data,
723 }))
724 } else {
725 Ok(None)
726 }
727 })
728 }
729}
730
731#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
733enum PCMState {
734 #[default]
735 SetParams,
736 Prepare,
737 Release,
738 Start,
739 Stop,
740}
741
742const QUEUE_SIZE: u16 = 32;
743const CONTROL_QUEUE_IDX: u16 = 0;
744const EVENT_QUEUE_IDX: u16 = 1;
745const TX_QUEUE_IDX: u16 = 2;
746const RX_QUEUE_IDX: u16 = 3;
747
748const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC
749 .union(Feature::RING_EVENT_IDX)
750 .union(Feature::VERSION_1);
751
752bitflags! {
753 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
754 #[repr(transparent)]
755 struct JackFeatures: u32 {
756 const REMAP = 1 << 0;
758 }
759}
760
761bitflags! {
762 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
764 #[repr(transparent)]
765 pub struct PcmFeatures: u32 {
766 const SHMEM_HOST = 1 << 0;
768 const SHMEM_GUEST = 1 << 1;
770 const MSG_POLLING = 1 << 2;
772 const EVT_SHMEM_PERIODS = 1 << 3;
774 const EVT_XRUNS = 1 << 4;
776 }
777}
778
779bitflags! {
780 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
782 #[repr(transparent)]
783 pub struct PcmFormats: u64 {
784 const IMA_ADPCM = 1 << 0;
786 const MU_LAW = 1 << 1;
788 const A_LAW = 1 << 2;
790 const S8 = 1 << 3;
792 const U8 = 1 << 4;
794 const S16 = 1 << 5;
796 const U16 = 1 << 6;
798 const S18_3 = 1 << 7;
800 const U18_3 = 1 << 8;
802 const S20_3 = 1 << 9;
804 const U20_3 = 1 << 10;
806 const S24_3 = 1 << 11;
808 const U24_3 = 1 << 12;
810 const S20 = 1 << 13;
812 const U20 = 1 << 14;
814 const S24 = 1 << 15;
816 const U24 = 1 << 16;
818 const S32 = 1 << 17;
820 const U32 = 1 << 18;
822 const FLOAT = 1 << 19;
824 const FLOAT64 = 1 << 20;
826 const DSD_U8 = 1 << 21;
828 const DSD_U16 = 1 << 22;
830 const DSD_U32 = 1 << 23;
832 const IEC958_SUBFRAME = 1 << 24;
834 }
835}
836
837#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
839#[repr(u8)]
840pub enum PcmFormat {
841 #[default]
843 ImaAdpcm = 0,
844 MuLaw = 1,
846 ALaw = 2,
848 S8 = 3,
850 U8 = 4,
852 S16 = 5,
854 U16 = 6,
856 S18_3 = 7,
858 U18_3 = 8,
860 S20_3 = 9,
862 U20_3 = 10,
864 S24_3 = 11,
866 U24_3 = 12,
868 S20 = 13,
870 U20 = 14,
872 S24 = 15,
874 U24 = 16,
876 S32 = 17,
878 U32 = 18,
880 FLOAT = 19,
882 FLOAT64 = 20,
884 DsdU8 = 21,
886 DsdU16 = 22,
888 DsdU32 = 23,
890 Iec958Subframe = 24,
892}
893
894impl From<PcmFormat> for PcmFormats {
895 fn from(format: PcmFormat) -> Self {
896 match format {
897 PcmFormat::ImaAdpcm => PcmFormats::IMA_ADPCM,
898 PcmFormat::MuLaw => PcmFormats::MU_LAW,
899 PcmFormat::ALaw => PcmFormats::A_LAW,
900 PcmFormat::S8 => PcmFormats::S8,
901 PcmFormat::U8 => PcmFormats::U8,
902 PcmFormat::S16 => PcmFormats::S16,
903 PcmFormat::U16 => PcmFormats::U16,
904 PcmFormat::S18_3 => PcmFormats::S18_3,
905 PcmFormat::U18_3 => PcmFormats::U18_3,
906 PcmFormat::S20_3 => PcmFormats::S20_3,
907 PcmFormat::U20_3 => PcmFormats::U20_3,
908 PcmFormat::S24_3 => PcmFormats::S24_3,
909 PcmFormat::U24_3 => PcmFormats::U24_3,
910 PcmFormat::S20 => PcmFormats::S20,
911 PcmFormat::U20 => PcmFormats::U20,
912 PcmFormat::S24 => PcmFormats::S24,
913 PcmFormat::U24 => PcmFormats::U24,
914 PcmFormat::S32 => PcmFormats::S32,
915 PcmFormat::U32 => PcmFormats::U32,
916 PcmFormat::FLOAT => PcmFormats::FLOAT,
917 PcmFormat::FLOAT64 => PcmFormats::FLOAT64,
918 PcmFormat::DsdU8 => PcmFormats::DSD_U8,
919 PcmFormat::DsdU16 => PcmFormats::DSD_U16,
920 PcmFormat::DsdU32 => PcmFormats::DSD_U32,
921 PcmFormat::Iec958Subframe => PcmFormats::IEC958_SUBFRAME,
922 }
923 }
924}
925
926impl From<PcmFormat> for u8 {
927 fn from(format: PcmFormat) -> u8 {
928 format as _
929 }
930}
931
932bitflags! {
933 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
935 #[repr(transparent)]
936 pub struct PcmRates: u64 {
937 const RATE_5512 = 1 << 0;
939 const RATE_8000 = 1 << 1;
941 const RATE_11025 = 1 << 2;
943 const RATE_16000 = 1 << 3;
945 const RATE_22050 = 1 << 4;
947 const RATE_32000 = 1 << 5;
949 const RATE_44100 = 1 << 6;
951 const RATE_48000 = 1 << 7;
953 const RATE_64000 = 1 << 8;
955 const RATE_88200 = 1 << 9;
957 const RATE_96000 = 1 << 10;
959 const RATE_176400 = 1 << 11;
961 const RATE_192000 = 1 << 12;
963 const RATE_384000 = 1 << 13;
965 }
966}
967
968#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
970#[repr(u8)]
971pub enum PcmRate {
972 #[default]
974 Rate5512 = 0,
975 Rate8000 = 1,
977 Rate11025 = 2,
979 Rate16000 = 3,
981 Rate22050 = 4,
983 Rate32000 = 5,
985 Rate44100 = 6,
987 Rate48000 = 7,
989 Rate64000 = 8,
991 Rate88200 = 9,
993 Rate96000 = 10,
995 Rate176400 = 11,
997 Rate192000 = 12,
999 Rate384000 = 13,
1001}
1002
1003impl From<PcmRate> for PcmRates {
1004 fn from(rate: PcmRate) -> Self {
1005 match rate {
1006 PcmRate::Rate5512 => Self::RATE_5512,
1007 PcmRate::Rate8000 => Self::RATE_8000,
1008 PcmRate::Rate11025 => Self::RATE_11025,
1009 PcmRate::Rate16000 => Self::RATE_16000,
1010 PcmRate::Rate22050 => Self::RATE_22050,
1011 PcmRate::Rate32000 => Self::RATE_32000,
1012 PcmRate::Rate44100 => Self::RATE_44100,
1013 PcmRate::Rate48000 => Self::RATE_48000,
1014 PcmRate::Rate64000 => Self::RATE_64000,
1015 PcmRate::Rate88200 => Self::RATE_88200,
1016 PcmRate::Rate96000 => Self::RATE_96000,
1017 PcmRate::Rate176400 => Self::RATE_176400,
1018 PcmRate::Rate192000 => Self::RATE_192000,
1019 PcmRate::Rate384000 => Self::RATE_384000,
1020 }
1021 }
1022}
1023
1024impl From<PcmRate> for u8 {
1025 fn from(rate: PcmRate) -> Self {
1026 rate as _
1027 }
1028}
1029
1030#[derive(FromBytes, Immutable, IntoBytes)]
1031#[repr(C)]
1032struct VirtIOSoundConfig {
1033 jacks: ReadOnly<u32>,
1034 streams: ReadOnly<u32>,
1035 chmaps: ReadOnly<u32>,
1036}
1037
1038#[repr(u32)]
1042#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1043enum CommandCode {
1044 RJackInfo = 1,
1046 RJackRemap,
1047
1048 RPcmInfo = 0x0100,
1050 RPcmSetParams,
1051 RPcmPrepare,
1052 RPcmRelease,
1053 RPcmStart,
1054 RPcmStop,
1055
1056 RChmapInfo = 0x0200,
1058
1059 EvtJackConnected = 0x1000,
1061 EvtJackDisconnected,
1062
1063 EvtPcmPeriodElapsed = 0x1100,
1065 EvtPcmXrun,
1066
1067 SOk = 0x8000,
1070 SBadMsg,
1072 SNotSupp,
1074 SIoErr,
1076}
1077
1078impl From<CommandCode> for u32 {
1079 fn from(code: CommandCode) -> u32 {
1080 code as u32
1081 }
1082}
1083
1084#[repr(u32)]
1086#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1087pub enum ItemInformationRequestType {
1088 RJackInfo = 1,
1090 RPcmInfo = 0x0100,
1092 RChmapInfo = 0x0200,
1094}
1095
1096impl From<ItemInformationRequestType> for VirtIOSndHdr {
1097 fn from(value: ItemInformationRequestType) -> Self {
1098 VirtIOSndHdr {
1099 command_code: value.into(),
1100 }
1101 }
1102}
1103
1104impl From<ItemInformationRequestType> for u32 {
1105 fn from(request_type: ItemInformationRequestType) -> u32 {
1106 request_type as _
1107 }
1108}
1109
1110#[derive(Copy, Clone, Eq, PartialEq)]
1111#[repr(u32)]
1112enum RequestStatusCode {
1113 Ok = 0x8000,
1115 BadMsg,
1116 NotSupp,
1117 IoErr,
1118}
1119
1120impl From<RequestStatusCode> for VirtIOSndHdr {
1121 fn from(value: RequestStatusCode) -> Self {
1122 VirtIOSndHdr {
1123 command_code: value as _,
1124 }
1125 }
1126}
1127
1128#[repr(C)]
1130#[derive(Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1131struct VirtIOSndHdr {
1132 command_code: u32,
1133}
1134
1135impl From<CommandCode> for VirtIOSndHdr {
1136 fn from(value: CommandCode) -> Self {
1137 VirtIOSndHdr {
1138 command_code: value.into(),
1139 }
1140 }
1141}
1142
1143#[repr(C)]
1144#[derive(FromBytes, Immutable, KnownLayout)]
1145struct VirtIOSndEvent {
1147 hdr: VirtIOSndHdr,
1148 data: u32,
1149}
1150
1151#[repr(u32)]
1153#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1154pub enum NotificationType {
1155 JackConnected = 0x1000,
1157 JackDisconnected,
1159 PcmPeriodElapsed = 0x1100,
1161 PcmXrun,
1163}
1164
1165impl NotificationType {
1166 fn n(value: u32) -> Option<Self> {
1168 match value {
1169 0x1100 => Some(Self::PcmPeriodElapsed),
1170 0x1101 => Some(Self::PcmXrun),
1171 0x1000 => Some(Self::JackConnected),
1172 0x1001 => Some(Self::JackDisconnected),
1173 _ => None,
1174 }
1175 }
1176}
1177
1178#[derive(Clone, Debug, Eq, PartialEq)]
1180pub struct Notification {
1181 notification_type: NotificationType,
1182 data: u32,
1183}
1184
1185impl Notification {
1186 pub fn data(&self) -> u32 {
1188 self.data
1189 }
1190
1191 pub fn notification_type(&self) -> NotificationType {
1193 self.notification_type
1194 }
1195}
1196
1197const VIRTIO_SND_D_OUTPUT: u8 = 0;
1198const VIRTIO_SND_D_INPUT: u8 = 1;
1199
1200#[repr(C)]
1201#[derive(Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1202struct VirtIOSndQueryInfo {
1203 hdr: VirtIOSndHdr,
1205 start_id: u32,
1210 count: u32,
1214 size: u32,
1217}
1218
1219#[repr(C)]
1220#[derive(Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1221struct VirtIOSndQueryInfoRsp {
1222 hdr: VirtIOSndHdr,
1223 info: VirtIOSndInfo,
1224}
1225
1226#[repr(C)]
1228#[derive(Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1229pub struct VirtIOSndInfo {
1230 hda_fn_nid: u32,
1231}
1232
1233#[repr(C)]
1234#[derive(Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1235struct VirtIOSndJackHdr {
1236 hdr: VirtIOSndHdr,
1237 jack_id: u32,
1239}
1240
1241#[repr(C)]
1243#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1244pub struct VirtIOSndJackInfo {
1245 hdr: VirtIOSndInfo,
1246 features: u32,
1247 hda_reg_defconf: u32,
1249 hda_reg_caps: u32,
1251 connected: u8,
1253
1254 _padding: [u8; 7],
1255}
1256
1257impl Debug for VirtIOSndJackInfo {
1258 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1259 f.debug_struct("VirtIOSndJackInfo")
1260 .field("hdr", &self.hdr)
1261 .field("features", &JackFeatures::from_bits_retain(self.features))
1262 .field("hda_reg_defconf", &self.hda_reg_defconf)
1263 .field("hda_reg_caps", &self.hda_reg_caps)
1264 .field("connected", &self.connected)
1265 .field("_padding", &self._padding)
1266 .finish()
1267 }
1268}
1269
1270impl Display for VirtIOSndJackInfo {
1271 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1272 let connected_status = if self.connected == 1 {
1273 "CONNECTED"
1274 } else {
1275 "DISCONNECTED"
1276 };
1277 write!(
1278 f,
1279 "features: {:?}, hda_reg_defconf: {}, hda_reg_caps: {}, connected: {}",
1280 JackFeatures::from_bits_retain(self.features),
1281 self.hda_reg_defconf,
1282 self.hda_reg_caps,
1283 connected_status
1284 )
1285 }
1286}
1287
1288#[repr(C)]
1289#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1290struct VirtIOSndJackInfoRsp {
1291 hdr: VirtIOSndHdr,
1292 body: VirtIOSndJackInfo,
1293}
1294
1295#[repr(C)]
1296#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1297struct VirtIOSndJackRemap {
1298 hdr: VirtIOSndJackHdr,
1299 association: u32,
1300 sequence: u32,
1301}
1302
1303#[repr(C)]
1304#[derive(Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1305struct VirtIOSndPcmHdr {
1306 hdr: VirtIOSndHdr,
1308 stream_id: u32,
1310}
1311
1312#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1313enum PcmStreamFeatures {
1314 ShmemHost = 0,
1315 ShmemGuest,
1316 MsgPolling,
1317 EvtShmemPeriods,
1318 EvtXruns,
1319}
1320
1321impl From<PcmStreamFeatures> for u32 {
1322 fn from(value: PcmStreamFeatures) -> Self {
1323 value as _
1324 }
1325}
1326
1327#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1328#[repr(u64)]
1329enum PcmSampleFormat {
1330 ImaAdpcm = 0, MuLaw, ALaw, S8, U8, S16, U16, S18_3, U18_3, S20_3, U20_3, S24_3, U24_3, S20, U20, S24, U24, S32, U32, Float, Float64, DsdU8, DsdU16, DsdU32, Iec958Subframe, }
1358
1359impl From<PcmSampleFormat> for u64 {
1360 fn from(value: PcmSampleFormat) -> Self {
1361 value as _
1362 }
1363}
1364
1365#[repr(C)]
1367#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1368pub struct VirtIOSndPcmInfo {
1369 hdr: VirtIOSndInfo,
1370 features: u32, formats: u64, rates: u64, direction: u8,
1375 channels_min: u8,
1377 channels_max: u8,
1379 _padding: [u8; 5],
1380}
1381
1382impl Debug for VirtIOSndPcmInfo {
1383 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1384 f.debug_struct("VirtIOSndPcmInfo")
1385 .field("hdr", &self.hdr)
1386 .field("features", &PcmFeatures::from_bits_retain(self.features))
1387 .field("formats", &PcmFormats::from_bits_retain(self.formats))
1388 .field("rates", &PcmRates::from_bits_retain(self.rates))
1389 .field("direction", &self.direction)
1390 .field("channels_min", &self.channels_min)
1391 .field("channels_max", &self.channels_max)
1392 .field("_padding", &self._padding)
1393 .finish()
1394 }
1395}
1396
1397impl Display for VirtIOSndPcmInfo {
1398 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1399 let direction = if self.direction == VIRTIO_SND_D_INPUT {
1400 "INPUT"
1401 } else {
1402 "OUTPUT"
1403 };
1404 write!(
1405 f,
1406 "features: {:?}, rates: {:?}, formats: {:?}, direction: {}",
1407 PcmFeatures::from_bits_retain(self.features),
1408 PcmRates::from_bits_retain(self.rates),
1409 PcmFormats::from_bits_retain(self.formats),
1410 direction
1411 )
1412 }
1413}
1414
1415#[derive(Clone, Debug, Default, Eq, PartialEq)]
1416struct PcmParameters {
1417 setup: bool,
1418 buffer_bytes: u32,
1419 period_bytes: u32,
1420 features: PcmFeatures,
1421 channels: u8,
1422 format: PcmFormat,
1423 rate: PcmRate,
1424}
1425
1426#[repr(C)]
1427#[derive(Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1428struct VirtIOSndPcmSetParams {
1429 hdr: VirtIOSndPcmHdr, buffer_bytes: u32,
1431 period_bytes: u32,
1432 features: u32, channels: u8,
1434 format: u8,
1435 rate: u8,
1436
1437 _padding: u8,
1438}
1439
1440#[repr(C)]
1442#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1443struct VirtIOSndPcmXfer {
1444 stream_id: u32,
1445}
1446
1447#[repr(C)]
1449#[derive(Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
1450struct VirtIOSndPcmStatus {
1451 status: u32,
1452 latency_bytes: u32,
1453}
1454
1455#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1456#[repr(u8)]
1457enum ChannelPosition {
1458 None = 0,
1460 Na,
1462 Mono,
1464 Fl,
1466 Fr,
1468 Rl,
1470 Rr,
1472 Fc,
1474 Lfe,
1476 Sl,
1478 Sr,
1480 Rc,
1482 Flc,
1484 Frc,
1486 Rlc,
1488 Rrc,
1490 Flw,
1492 Frw,
1494 Flh,
1496 Fch,
1498 Frh,
1500 Tc,
1502 Tfl,
1504 Tfr,
1506 Tfc,
1508 Trl,
1510 Trr,
1512 Trc,
1514 Tflc,
1516 Tfrc,
1518 Tsl,
1520 Tsr,
1522 Llfe,
1524 Rlfe,
1526 Bc,
1528 Blc,
1530 Brc,
1532}
1533
1534const VIRTIO_SND_CHMAP_MAX_SIZE: usize = 18;
1536
1537#[repr(C)]
1538#[derive(Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1539struct VirtIOSndChmapInfo {
1540 hdr: VirtIOSndInfo,
1541 direction: u8,
1542 channels: u8,
1543 positions: [u8; VIRTIO_SND_CHMAP_MAX_SIZE],
1544}
1545
1546impl Display for VirtIOSndChmapInfo {
1547 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1548 let direction = if self.direction == VIRTIO_SND_D_INPUT {
1549 "INPUT"
1550 } else {
1551 "OUTPUT"
1552 };
1553 write!(
1554 f,
1555 "direction: {}, channels: {}, positions: [",
1556 direction, self.channels
1557 )?;
1558 for i in 0..usize::from(self.channels) {
1559 if i != 0 {
1560 write!(f, ", ")?;
1561 }
1562 if let Some(position) = ChannelPosition::n(self.positions[i]) {
1563 write!(f, "{:?}", position)?;
1564 } else {
1565 write!(f, "{}", self.positions[i])?;
1566 }
1567 }
1568 write!(f, "]")?;
1569 Ok(())
1570 }
1571}
1572
1573#[cfg(test)]
1574mod tests {
1575 use super::*;
1576 use crate::{
1577 config::ReadOnly,
1578 hal::fake::FakeHal,
1579 transport::{
1580 fake::{FakeTransport, QueueStatus, State},
1581 DeviceType,
1582 },
1583 };
1584 use alloc::{sync::Arc, vec};
1585 use fake::FakeSoundDevice;
1586 use std::sync::Mutex;
1587
1588 #[test]
1589 fn config() {
1590 let config_space = VirtIOSoundConfig {
1591 jacks: ReadOnly::new(3),
1592 streams: ReadOnly::new(4),
1593 chmaps: ReadOnly::new(2),
1594 };
1595 let state = Arc::new(Mutex::new(State::new(
1596 vec![
1597 QueueStatus::default(),
1598 QueueStatus::default(),
1599 QueueStatus::default(),
1600 QueueStatus::default(),
1601 ],
1602 config_space,
1603 )));
1604 let transport = FakeTransport {
1605 device_type: DeviceType::Sound,
1606 max_queue_size: 32,
1607 device_features: 0,
1608 state: state.clone(),
1609 };
1610 let sound =
1611 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1612 assert_eq!(sound.jacks(), 3);
1613 assert_eq!(sound.streams(), 4);
1614 assert_eq!(sound.chmaps(), 2);
1615 }
1616
1617 #[test]
1618 fn empty_info() {
1619 let (fake, transport) = FakeSoundDevice::new(vec![], vec![], vec![]);
1620 let mut sound =
1621 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1622 let handle = fake.spawn();
1623
1624 assert_eq!(sound.jacks(), 0);
1625 assert_eq!(sound.streams(), 0);
1626 assert_eq!(sound.chmaps(), 0);
1627 assert_eq!(sound.output_streams().unwrap(), vec![]);
1628 assert_eq!(sound.input_streams().unwrap(), vec![]);
1629
1630 fake.terminate();
1631 handle.join().unwrap();
1632 }
1633
1634 #[test]
1635 fn stream_info() {
1636 let (fake, transport) = FakeSoundDevice::new(
1637 vec![VirtIOSndJackInfo {
1638 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1639 features: 0,
1640 hda_reg_defconf: 0,
1641 hda_reg_caps: 0,
1642 connected: 0,
1643 _padding: Default::default(),
1644 }],
1645 vec![
1646 VirtIOSndPcmInfo {
1647 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1648 features: 0,
1649 formats: (PcmFormats::U8 | PcmFormats::U32).bits(),
1650 rates: (PcmRates::RATE_44100 | PcmRates::RATE_32000).bits(),
1651 direction: VIRTIO_SND_D_OUTPUT,
1652 channels_min: 1,
1653 channels_max: 2,
1654 _padding: Default::default(),
1655 },
1656 VirtIOSndPcmInfo {
1657 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1658 features: 0,
1659 formats: 0,
1660 rates: 0,
1661 direction: VIRTIO_SND_D_INPUT,
1662 channels_min: 0,
1663 channels_max: 0,
1664 _padding: Default::default(),
1665 },
1666 ],
1667 vec![VirtIOSndChmapInfo {
1668 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1669 direction: 0,
1670 channels: 0,
1671 positions: [0; 18],
1672 }],
1673 );
1674 let mut sound =
1675 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1676 let handle = fake.spawn();
1677
1678 assert_eq!(sound.output_streams().unwrap(), vec![0]);
1679 assert_eq!(
1680 sound.rates_supported(0).unwrap(),
1681 PcmRates::RATE_44100 | PcmRates::RATE_32000
1682 );
1683 assert_eq!(
1684 sound.formats_supported(0).unwrap(),
1685 PcmFormats::U8 | PcmFormats::U32
1686 );
1687 assert_eq!(sound.channel_range_supported(0).unwrap(), 1..=2);
1688 assert_eq!(sound.features_supported(0).unwrap(), PcmFeatures::empty());
1689
1690 assert_eq!(sound.input_streams().unwrap(), vec![1]);
1691 assert_eq!(sound.rates_supported(1).unwrap(), PcmRates::empty());
1692 assert_eq!(sound.formats_supported(1).unwrap(), PcmFormats::empty());
1693 assert_eq!(sound.channel_range_supported(1).unwrap(), 0..=0);
1694 assert_eq!(sound.features_supported(1).unwrap(), PcmFeatures::empty());
1695
1696 fake.terminate();
1697 handle.join().unwrap();
1698 }
1699
1700 #[test]
1701 fn play() {
1702 let (fake, transport) = FakeSoundDevice::new(
1703 vec![],
1704 vec![VirtIOSndPcmInfo {
1705 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1706 features: 0,
1707 formats: (PcmFormats::U8 | PcmFormats::U32).bits(),
1708 rates: (PcmRates::RATE_44100 | PcmRates::RATE_32000).bits(),
1709 direction: VIRTIO_SND_D_OUTPUT,
1710 channels_min: 1,
1711 channels_max: 2,
1712 _padding: Default::default(),
1713 }],
1714 vec![],
1715 );
1716 let mut sound =
1717 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1718 let handle = fake.spawn();
1719
1720 assert_eq!(sound.output_streams().unwrap(), vec![0]);
1721 assert_eq!(sound.input_streams().unwrap(), vec![]);
1722
1723 sound
1724 .pcm_set_params(
1725 0,
1726 100,
1727 100,
1728 PcmFeatures::empty(),
1729 1,
1730 PcmFormat::U8,
1731 PcmRate::Rate8000,
1732 )
1733 .unwrap();
1734 assert_eq!(
1735 fake.params.lock().unwrap()[0],
1736 Some(VirtIOSndPcmSetParams {
1737 hdr: VirtIOSndPcmHdr {
1738 hdr: VirtIOSndHdr {
1739 command_code: CommandCode::RPcmSetParams.into(),
1740 },
1741 stream_id: 0,
1742 },
1743 buffer_bytes: 100,
1744 period_bytes: 100,
1745 features: 0,
1746 channels: 1,
1747 format: PcmFormat::U8.into(),
1748 rate: PcmRate::Rate8000.into(),
1749 _padding: Default::default(),
1750 })
1751 );
1752
1753 sound.pcm_prepare(0).unwrap();
1754 sound.pcm_start(0).unwrap();
1755
1756 let mut expected_sound = vec![];
1757
1758 println!("Playing empty");
1760 sound.pcm_xfer(0, &[]).unwrap();
1761 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1762
1763 println!("Playing 100");
1765 sound.pcm_xfer(0, &[42; 100]).unwrap();
1766 expected_sound.extend([42; 100]);
1767 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1768
1769 println!("Playing 200");
1771 sound.pcm_xfer(0, &[66; 200]).unwrap();
1772 expected_sound.extend([66; 200]);
1773 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1774
1775 println!("Playing 50");
1777 sound.pcm_xfer(0, &[55; 50]).unwrap();
1778 expected_sound.extend([55; 50]);
1779 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1780
1781 println!("Playing 5000");
1783 sound.pcm_xfer(0, &[12; 5000]).unwrap();
1784 expected_sound.extend([12; 5000]);
1785 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1786
1787 sound.pcm_stop(0).unwrap();
1788 sound.pcm_release(0).unwrap();
1789
1790 fake.terminate();
1791 handle.join().unwrap();
1792 }
1793}