1#[cfg(test)]
4mod fake;
5
6use super::common::Feature;
7use crate::{
8 Error, Hal, PAGE_SIZE, Result,
9 config::{ReadOnly, read_config},
10 queue::{OwningQueue, VirtQueue},
11 transport::{InterruptStatus, Transport},
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!(
258 "stream_start_id + stream_count > streams! There are not enough streams to be queried!"
259 );
260 return Err(Error::IoError);
261 }
262 let request_hdr = VirtIOSndHdr::from(ItemInformationRequestType::RPcmInfo);
263 let hdr = self.request(VirtIOSndQueryInfo {
264 hdr: request_hdr,
265 start_id: stream_start_id,
266 count: stream_count,
267 size: size_of::<VirtIOSndPcmInfo>() as u32,
268 })?;
269 if hdr != RequestStatusCode::Ok.into() {
270 return Err(Error::IoError);
271 }
272 let mut pcm_infos = vec![];
274 for i in 0..stream_count as usize {
275 const HDR_SIZE: usize = size_of::<VirtIOSndHdr>();
276 const PCM_INFO_SIZE: usize = size_of::<VirtIOSndPcmInfo>();
277 let start_byte_idx = HDR_SIZE + i * PCM_INFO_SIZE;
278 let end_byte_idx = HDR_SIZE + (i + 1) * PCM_INFO_SIZE;
279 let pcm_info = VirtIOSndPcmInfo::read_from_bytes(
280 &self.queue_buf_recv[start_byte_idx..end_byte_idx],
281 )
282 .unwrap();
283 pcm_infos.push(pcm_info);
284 }
285 Ok(pcm_infos)
286 }
287
288 fn chmap_info(
290 &mut self,
291 chmaps_start_id: u32,
292 chmaps_count: u32,
293 ) -> Result<Vec<VirtIOSndChmapInfo>> {
294 if chmaps_start_id + chmaps_count > self.chmaps {
295 error!("chmaps_start_id + chmaps_count > self.chmaps");
296 return Err(Error::IoError);
297 }
298 let hdr = self.request(VirtIOSndQueryInfo {
299 hdr: ItemInformationRequestType::RChmapInfo.into(),
300 start_id: chmaps_start_id,
301 count: chmaps_count,
302 size: size_of::<VirtIOSndChmapInfo>() as u32,
303 })?;
304 if hdr != RequestStatusCode::Ok.into() {
305 return Err(Error::IoError);
306 }
307 let mut chmap_infos = vec![];
308 for i in 0..chmaps_count as usize {
309 const OFFSET: usize = size_of::<VirtIOSndHdr>();
310 let start_byte = OFFSET + i * size_of::<VirtIOSndChmapInfo>();
311 let end_byte = OFFSET + (i + 1) * size_of::<VirtIOSndChmapInfo>();
312 let chmap_info =
313 VirtIOSndChmapInfo::read_from_bytes(&self.queue_buf_recv[start_byte..end_byte])
314 .unwrap();
315 chmap_infos.push(chmap_info);
316 }
317 Ok(chmap_infos)
318 }
319
320 pub fn jack_remap(&mut self, jack_id: u32, association: u32, sequence: u32) -> Result {
326 if !self.set_up {
327 self.set_up()?;
328 self.set_up = true;
329 }
330 if self.jacks == 0 {
331 error!("[sound device] There is no available jacks!");
332 return Err(Error::InvalidParam);
333 }
334 if jack_id >= self.jacks {
335 error!("jack_id >= self.jacks! Make sure jack_id is in the range of [0, jacks - 1)!");
336 return Err(Error::InvalidParam);
337 }
338
339 let jack_features = JackFeatures::from_bits_retain(
340 self.jack_infos
341 .as_ref()
342 .unwrap()
343 .get(jack_id as usize)
344 .unwrap()
345 .features,
346 );
347 if !jack_features.contains(JackFeatures::REMAP) {
348 error!("The jack selected does not support VIRTIO_SND_JACK_F_REMAP!");
349 return Err(Error::Unsupported);
350 }
351 let hdr = self.request(VirtIOSndJackRemap {
352 hdr: VirtIOSndJackHdr {
353 hdr: CommandCode::RJackRemap.into(),
354 jack_id,
355 },
356 association,
357 sequence,
358 })?;
359 if hdr == RequestStatusCode::Ok.into() {
360 Ok(())
361 } else {
362 Err(Error::Unsupported)
363 }
364 }
365
366 #[allow(clippy::too_many_arguments)]
368 pub fn pcm_set_params(
369 &mut self,
370 stream_id: u32,
371 buffer_bytes: u32,
372 period_bytes: u32,
373 features: PcmFeatures,
374 channels: u8,
375 format: PcmFormat,
376 rate: PcmRate,
377 ) -> Result {
378 if !self.set_up {
379 self.set_up()?;
380 self.set_up = true;
381 }
382 if period_bytes == 0
383 || period_bytes > buffer_bytes
384 || !buffer_bytes.is_multiple_of(period_bytes)
385 {
386 return Err(Error::InvalidParam);
387 }
388 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmSetParams);
389 let rsp = self.request(VirtIOSndPcmSetParams {
390 hdr: VirtIOSndPcmHdr {
391 hdr: request_hdr,
392 stream_id,
393 },
394 buffer_bytes,
395 period_bytes,
396 features: features.bits(),
397 channels,
398 format: format.into(),
399 rate: rate.into(),
400 _padding: 0,
401 })?;
402 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
404 self.pcm_parameters[stream_id as usize] = PcmParameters {
405 setup: true,
406 buffer_bytes,
407 period_bytes,
408 features,
409 channels,
410 format,
411 rate,
412 };
413 Ok(())
414 } else {
415 Err(Error::IoError)
416 }
417 }
418
419 pub fn pcm_prepare(&mut self, stream_id: u32) -> Result {
421 if !self.set_up {
422 self.set_up()?;
423 self.set_up = true;
424 }
425 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmPrepare);
426 let rsp = self.request(VirtIOSndPcmHdr {
427 hdr: request_hdr,
428 stream_id,
429 })?;
430 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
432 Ok(())
433 } else {
434 Err(Error::IoError)
435 }
436 }
437
438 pub fn pcm_release(&mut self, stream_id: u32) -> Result {
440 if !self.set_up {
441 self.set_up()?;
442 self.set_up = true;
443 }
444 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmRelease);
445 let rsp = self.request(VirtIOSndPcmHdr {
446 hdr: request_hdr,
447 stream_id,
448 })?;
449 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
451 Ok(())
452 } else {
453 Err(Error::IoError)
454 }
455 }
456
457 pub fn pcm_start(&mut self, stream_id: u32) -> Result {
459 if !self.set_up {
460 self.set_up()?;
461 self.set_up = true;
462 }
463 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmStart);
464 let rsp = self.request(VirtIOSndPcmHdr {
465 hdr: request_hdr,
466 stream_id,
467 })?;
468 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
470 Ok(())
471 } else {
472 Err(Error::IoError)
473 }
474 }
475
476 pub fn pcm_stop(&mut self, stream_id: u32) -> Result {
478 if !self.set_up {
479 self.set_up()?;
480 self.set_up = true;
481 }
482 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmStop);
483 let rsp = self.request(VirtIOSndPcmHdr {
484 hdr: request_hdr,
485 stream_id,
486 })?;
487 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
489 Ok(())
490 } else {
491 Err(Error::IoError)
492 }
493 }
494
495 pub fn pcm_xfer(&mut self, stream_id: u32, frames: &[u8]) -> Result {
501 const U32_SIZE: usize = size_of::<u32>();
502 if !self.set_up {
503 self.set_up()?;
504 self.set_up = true;
505 }
506 if !self.pcm_parameters[stream_id as usize].setup {
507 warn!("Please set parameters for a stream before using it!");
508 return Err(Error::IoError);
509 }
510
511 let stream_id_bytes = stream_id.to_le_bytes();
512 let period_size = self.pcm_parameters[stream_id as usize].period_bytes as usize;
513
514 let mut remaining_buffers = frames.chunks(period_size);
515 let mut buffers: [Option<&[u8]>; QUEUE_SIZE as usize] = [None; QUEUE_SIZE as usize];
516 let mut statuses: [VirtIOSndPcmStatus; QUEUE_SIZE as usize] =
517 array::from_fn(|_| Default::default());
518 let mut tokens = [0; QUEUE_SIZE as usize];
519 let mut head = 0;
521 let mut tail = 0;
523
524 loop {
525 if self.tx_queue.available_desc() >= 3 {
528 if let Some(buffer) = remaining_buffers.next() {
529 tokens[head] = unsafe {
532 self.tx_queue.add(
533 &[&stream_id_bytes, buffer],
534 &mut [statuses[head].as_mut_bytes()],
535 )?
536 };
537 if self.tx_queue.should_notify() {
538 self.transport.notify(TX_QUEUE_IDX);
539 }
540 buffers[head] = Some(buffer);
541 head += 1;
542 if head >= usize::from(QUEUE_SIZE) {
543 head = 0;
544 }
545 } else if head == tail {
546 break;
547 }
548 }
549 if self.tx_queue.can_pop() {
550 unsafe {
554 self.tx_queue.pop_used(
555 tokens[tail],
556 &[&stream_id_bytes, buffers[tail].unwrap()],
557 &mut [statuses[tail].as_mut_bytes()],
558 )?;
559 }
560 if statuses[tail].status != CommandCode::SOk.into() {
561 return Err(Error::IoError);
562 }
563 tail += 1;
564 if tail >= usize::from(QUEUE_SIZE) {
565 tail = 0;
566 }
567 }
568 spin_loop();
569 }
570
571 Ok(())
572 }
573
574 pub fn pcm_xfer_nb(&mut self, stream_id: u32, frames: &[u8]) -> Result<u16> {
582 if !self.set_up {
583 self.set_up()?;
584 self.set_up = true;
585 }
586 if !self.pcm_parameters[stream_id as usize].setup {
587 warn!("Please set parameters for a stream before using it!");
588 return Err(Error::IoError);
589 }
590 const U32_SIZE: usize = size_of::<u32>();
591 let period_size: usize = self.pcm_parameters[stream_id as usize].period_bytes as usize;
592 assert_eq!(period_size, frames.len());
593 let mut buf = vec![0; U32_SIZE + period_size];
594 buf[..U32_SIZE].copy_from_slice(&stream_id.to_le_bytes());
595 buf[U32_SIZE..U32_SIZE + period_size].copy_from_slice(frames);
596 let mut rsp = VirtIOSndPcmStatus::new_box_zeroed().unwrap();
597
598 let token = unsafe { self.tx_queue.add(&[&buf], &mut [rsp.as_mut_bytes()])? };
602
603 if self.tx_queue.should_notify() {
604 self.transport.notify(TX_QUEUE_IDX);
605 }
606 self.token_buf.insert(token, buf);
607 self.token_rsp.insert(token, rsp);
608 Ok(token)
609 }
610
611 pub fn pcm_xfer_ok(&mut self, token: u16) -> Result {
613 assert!(self.token_buf.contains_key(&token));
614 assert!(self.token_rsp.contains_key(&token));
615
616 unsafe {
619 self.tx_queue.pop_used(
620 token,
621 &[&self.token_buf[&token]],
622 &mut [self.token_rsp.get_mut(&token).unwrap().as_mut_bytes()],
623 )?;
624 }
625
626 self.token_buf.remove(&token);
627 self.token_rsp.remove(&token);
628 Ok(())
629 }
630
631 pub fn output_streams(&mut self) -> Result<Vec<u32>> {
633 if !self.set_up {
634 self.set_up()?;
635 self.set_up = true;
636 }
637 Ok(self
638 .pcm_infos
639 .as_ref()
640 .unwrap()
641 .iter()
642 .enumerate()
643 .filter(|(_, info)| info.direction == VIRTIO_SND_D_OUTPUT)
644 .map(|(idx, _)| idx as u32)
645 .collect())
646 }
647
648 pub fn input_streams(&mut self) -> Result<Vec<u32>> {
650 if !self.set_up {
651 self.set_up()?;
652 self.set_up = true;
653 }
654 Ok(self
655 .pcm_infos
656 .as_ref()
657 .unwrap()
658 .iter()
659 .enumerate()
660 .filter(|(_, info)| info.direction == VIRTIO_SND_D_INPUT)
661 .map(|(idx, _)| idx as u32)
662 .collect())
663 }
664
665 pub fn rates_supported(&mut self, stream_id: u32) -> Result<PcmRates> {
667 if !self.set_up {
668 self.set_up()?;
669 self.set_up = true;
670 }
671 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
672 return Err(Error::InvalidParam);
673 }
674 Ok(PcmRates::from_bits_retain(
675 self.pcm_infos.as_ref().unwrap()[stream_id as usize].rates,
676 ))
677 }
678
679 pub fn formats_supported(&mut self, stream_id: u32) -> Result<PcmFormats> {
681 if !self.set_up {
682 self.set_up()?;
683 self.set_up = true;
684 }
685 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
686 return Err(Error::InvalidParam);
687 }
688 Ok(PcmFormats::from_bits_retain(
689 self.pcm_infos.as_ref().unwrap()[stream_id as usize].formats,
690 ))
691 }
692
693 pub fn channel_range_supported(&mut self, stream_id: u32) -> Result<RangeInclusive<u8>> {
695 if !self.set_up {
696 self.set_up()?;
697 self.set_up = true;
698 }
699 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
700 return Err(Error::InvalidParam);
701 }
702 let pcm_info = &self.pcm_infos.as_ref().unwrap()[stream_id as usize];
703 Ok(pcm_info.channels_min..=pcm_info.channels_max)
704 }
705
706 pub fn features_supported(&mut self, stream_id: u32) -> Result<PcmFeatures> {
708 if !self.set_up {
709 self.set_up()?;
710 self.set_up = true;
711 }
712 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
713 return Err(Error::InvalidParam);
714 }
715 let pcm_info = &self.pcm_infos.as_ref().unwrap()[stream_id as usize];
716 Ok(PcmFeatures::from_bits_retain(pcm_info.features))
717 }
718
719 pub fn latest_notification(&mut self) -> Result<Option<Notification>> {
721 self.event_queue.poll(&mut self.transport, |buffer| {
724 if let Ok(event) = VirtIOSndEvent::read_from_bytes(buffer) {
725 Ok(Some(Notification {
726 notification_type: NotificationType::n(event.hdr.command_code)
727 .ok_or(Error::IoError)?,
728 data: event.data,
729 }))
730 } else {
731 Ok(None)
732 }
733 })
734 }
735}
736
737#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
739enum PCMState {
740 #[default]
741 SetParams,
742 Prepare,
743 Release,
744 Start,
745 Stop,
746}
747
748const QUEUE_SIZE: u16 = 32;
749const CONTROL_QUEUE_IDX: u16 = 0;
750const EVENT_QUEUE_IDX: u16 = 1;
751const TX_QUEUE_IDX: u16 = 2;
752const RX_QUEUE_IDX: u16 = 3;
753
754const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC
755 .union(Feature::RING_EVENT_IDX)
756 .union(Feature::VERSION_1);
757
758bitflags! {
759 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
760 #[repr(transparent)]
761 struct JackFeatures: u32 {
762 const REMAP = 1 << 0;
764 }
765}
766
767bitflags! {
768 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
770 #[repr(transparent)]
771 pub struct PcmFeatures: u32 {
772 const SHMEM_HOST = 1 << 0;
774 const SHMEM_GUEST = 1 << 1;
776 const MSG_POLLING = 1 << 2;
778 const EVT_SHMEM_PERIODS = 1 << 3;
780 const EVT_XRUNS = 1 << 4;
782 }
783}
784
785bitflags! {
786 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
788 #[repr(transparent)]
789 pub struct PcmFormats: u64 {
790 const IMA_ADPCM = 1 << 0;
792 const MU_LAW = 1 << 1;
794 const A_LAW = 1 << 2;
796 const S8 = 1 << 3;
798 const U8 = 1 << 4;
800 const S16 = 1 << 5;
802 const U16 = 1 << 6;
804 const S18_3 = 1 << 7;
806 const U18_3 = 1 << 8;
808 const S20_3 = 1 << 9;
810 const U20_3 = 1 << 10;
812 const S24_3 = 1 << 11;
814 const U24_3 = 1 << 12;
816 const S20 = 1 << 13;
818 const U20 = 1 << 14;
820 const S24 = 1 << 15;
822 const U24 = 1 << 16;
824 const S32 = 1 << 17;
826 const U32 = 1 << 18;
828 const FLOAT = 1 << 19;
830 const FLOAT64 = 1 << 20;
832 const DSD_U8 = 1 << 21;
834 const DSD_U16 = 1 << 22;
836 const DSD_U32 = 1 << 23;
838 const IEC958_SUBFRAME = 1 << 24;
840 }
841}
842
843#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
845#[repr(u8)]
846pub enum PcmFormat {
847 #[default]
849 ImaAdpcm = 0,
850 MuLaw = 1,
852 ALaw = 2,
854 S8 = 3,
856 U8 = 4,
858 S16 = 5,
860 U16 = 6,
862 S18_3 = 7,
864 U18_3 = 8,
866 S20_3 = 9,
868 U20_3 = 10,
870 S24_3 = 11,
872 U24_3 = 12,
874 S20 = 13,
876 U20 = 14,
878 S24 = 15,
880 U24 = 16,
882 S32 = 17,
884 U32 = 18,
886 FLOAT = 19,
888 FLOAT64 = 20,
890 DsdU8 = 21,
892 DsdU16 = 22,
894 DsdU32 = 23,
896 Iec958Subframe = 24,
898}
899
900impl From<PcmFormat> for PcmFormats {
901 fn from(format: PcmFormat) -> Self {
902 match format {
903 PcmFormat::ImaAdpcm => PcmFormats::IMA_ADPCM,
904 PcmFormat::MuLaw => PcmFormats::MU_LAW,
905 PcmFormat::ALaw => PcmFormats::A_LAW,
906 PcmFormat::S8 => PcmFormats::S8,
907 PcmFormat::U8 => PcmFormats::U8,
908 PcmFormat::S16 => PcmFormats::S16,
909 PcmFormat::U16 => PcmFormats::U16,
910 PcmFormat::S18_3 => PcmFormats::S18_3,
911 PcmFormat::U18_3 => PcmFormats::U18_3,
912 PcmFormat::S20_3 => PcmFormats::S20_3,
913 PcmFormat::U20_3 => PcmFormats::U20_3,
914 PcmFormat::S24_3 => PcmFormats::S24_3,
915 PcmFormat::U24_3 => PcmFormats::U24_3,
916 PcmFormat::S20 => PcmFormats::S20,
917 PcmFormat::U20 => PcmFormats::U20,
918 PcmFormat::S24 => PcmFormats::S24,
919 PcmFormat::U24 => PcmFormats::U24,
920 PcmFormat::S32 => PcmFormats::S32,
921 PcmFormat::U32 => PcmFormats::U32,
922 PcmFormat::FLOAT => PcmFormats::FLOAT,
923 PcmFormat::FLOAT64 => PcmFormats::FLOAT64,
924 PcmFormat::DsdU8 => PcmFormats::DSD_U8,
925 PcmFormat::DsdU16 => PcmFormats::DSD_U16,
926 PcmFormat::DsdU32 => PcmFormats::DSD_U32,
927 PcmFormat::Iec958Subframe => PcmFormats::IEC958_SUBFRAME,
928 }
929 }
930}
931
932impl From<PcmFormat> for u8 {
933 fn from(format: PcmFormat) -> u8 {
934 format as _
935 }
936}
937
938bitflags! {
939 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
941 #[repr(transparent)]
942 pub struct PcmRates: u64 {
943 const RATE_5512 = 1 << 0;
945 const RATE_8000 = 1 << 1;
947 const RATE_11025 = 1 << 2;
949 const RATE_16000 = 1 << 3;
951 const RATE_22050 = 1 << 4;
953 const RATE_32000 = 1 << 5;
955 const RATE_44100 = 1 << 6;
957 const RATE_48000 = 1 << 7;
959 const RATE_64000 = 1 << 8;
961 const RATE_88200 = 1 << 9;
963 const RATE_96000 = 1 << 10;
965 const RATE_176400 = 1 << 11;
967 const RATE_192000 = 1 << 12;
969 const RATE_384000 = 1 << 13;
971 }
972}
973
974#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
976#[repr(u8)]
977pub enum PcmRate {
978 #[default]
980 Rate5512 = 0,
981 Rate8000 = 1,
983 Rate11025 = 2,
985 Rate16000 = 3,
987 Rate22050 = 4,
989 Rate32000 = 5,
991 Rate44100 = 6,
993 Rate48000 = 7,
995 Rate64000 = 8,
997 Rate88200 = 9,
999 Rate96000 = 10,
1001 Rate176400 = 11,
1003 Rate192000 = 12,
1005 Rate384000 = 13,
1007}
1008
1009impl From<PcmRate> for PcmRates {
1010 fn from(rate: PcmRate) -> Self {
1011 match rate {
1012 PcmRate::Rate5512 => Self::RATE_5512,
1013 PcmRate::Rate8000 => Self::RATE_8000,
1014 PcmRate::Rate11025 => Self::RATE_11025,
1015 PcmRate::Rate16000 => Self::RATE_16000,
1016 PcmRate::Rate22050 => Self::RATE_22050,
1017 PcmRate::Rate32000 => Self::RATE_32000,
1018 PcmRate::Rate44100 => Self::RATE_44100,
1019 PcmRate::Rate48000 => Self::RATE_48000,
1020 PcmRate::Rate64000 => Self::RATE_64000,
1021 PcmRate::Rate88200 => Self::RATE_88200,
1022 PcmRate::Rate96000 => Self::RATE_96000,
1023 PcmRate::Rate176400 => Self::RATE_176400,
1024 PcmRate::Rate192000 => Self::RATE_192000,
1025 PcmRate::Rate384000 => Self::RATE_384000,
1026 }
1027 }
1028}
1029
1030impl From<PcmRate> for u8 {
1031 fn from(rate: PcmRate) -> Self {
1032 rate as _
1033 }
1034}
1035
1036#[derive(FromBytes, Immutable, IntoBytes)]
1037#[repr(C)]
1038struct VirtIOSoundConfig {
1039 jacks: ReadOnly<u32>,
1040 streams: ReadOnly<u32>,
1041 chmaps: ReadOnly<u32>,
1042}
1043
1044#[repr(u32)]
1048#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1049enum CommandCode {
1050 RJackInfo = 1,
1052 RJackRemap,
1053
1054 RPcmInfo = 0x0100,
1056 RPcmSetParams,
1057 RPcmPrepare,
1058 RPcmRelease,
1059 RPcmStart,
1060 RPcmStop,
1061
1062 RChmapInfo = 0x0200,
1064
1065 EvtJackConnected = 0x1000,
1067 EvtJackDisconnected,
1068
1069 EvtPcmPeriodElapsed = 0x1100,
1071 EvtPcmXrun,
1072
1073 SOk = 0x8000,
1076 SBadMsg,
1078 SNotSupp,
1080 SIoErr,
1082}
1083
1084impl From<CommandCode> for u32 {
1085 fn from(code: CommandCode) -> u32 {
1086 code as u32
1087 }
1088}
1089
1090#[repr(u32)]
1092#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1093pub enum ItemInformationRequestType {
1094 RJackInfo = 1,
1096 RPcmInfo = 0x0100,
1098 RChmapInfo = 0x0200,
1100}
1101
1102impl From<ItemInformationRequestType> for VirtIOSndHdr {
1103 fn from(value: ItemInformationRequestType) -> Self {
1104 VirtIOSndHdr {
1105 command_code: value.into(),
1106 }
1107 }
1108}
1109
1110impl From<ItemInformationRequestType> for u32 {
1111 fn from(request_type: ItemInformationRequestType) -> u32 {
1112 request_type as _
1113 }
1114}
1115
1116#[derive(Copy, Clone, Eq, PartialEq)]
1117#[repr(u32)]
1118enum RequestStatusCode {
1119 Ok = 0x8000,
1121 BadMsg,
1122 NotSupp,
1123 IoErr,
1124}
1125
1126impl From<RequestStatusCode> for VirtIOSndHdr {
1127 fn from(value: RequestStatusCode) -> Self {
1128 VirtIOSndHdr {
1129 command_code: value as _,
1130 }
1131 }
1132}
1133
1134#[repr(C)]
1136#[derive(Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1137struct VirtIOSndHdr {
1138 command_code: u32,
1139}
1140
1141impl From<CommandCode> for VirtIOSndHdr {
1142 fn from(value: CommandCode) -> Self {
1143 VirtIOSndHdr {
1144 command_code: value.into(),
1145 }
1146 }
1147}
1148
1149#[repr(C)]
1150#[derive(FromBytes, Immutable, KnownLayout)]
1151struct VirtIOSndEvent {
1153 hdr: VirtIOSndHdr,
1154 data: u32,
1155}
1156
1157#[repr(u32)]
1159#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1160pub enum NotificationType {
1161 JackConnected = 0x1000,
1163 JackDisconnected,
1165 PcmPeriodElapsed = 0x1100,
1167 PcmXrun,
1169}
1170
1171impl NotificationType {
1172 fn n(value: u32) -> Option<Self> {
1174 match value {
1175 0x1100 => Some(Self::PcmPeriodElapsed),
1176 0x1101 => Some(Self::PcmXrun),
1177 0x1000 => Some(Self::JackConnected),
1178 0x1001 => Some(Self::JackDisconnected),
1179 _ => None,
1180 }
1181 }
1182}
1183
1184#[derive(Clone, Debug, Eq, PartialEq)]
1186pub struct Notification {
1187 notification_type: NotificationType,
1188 data: u32,
1189}
1190
1191impl Notification {
1192 pub fn data(&self) -> u32 {
1194 self.data
1195 }
1196
1197 pub fn notification_type(&self) -> NotificationType {
1199 self.notification_type
1200 }
1201}
1202
1203const VIRTIO_SND_D_OUTPUT: u8 = 0;
1204const VIRTIO_SND_D_INPUT: u8 = 1;
1205
1206#[repr(C)]
1207#[derive(Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1208struct VirtIOSndQueryInfo {
1209 hdr: VirtIOSndHdr,
1211 start_id: u32,
1216 count: u32,
1220 size: u32,
1223}
1224
1225#[repr(C)]
1226#[derive(Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1227struct VirtIOSndQueryInfoRsp {
1228 hdr: VirtIOSndHdr,
1229 info: VirtIOSndInfo,
1230}
1231
1232#[repr(C)]
1234#[derive(Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1235pub struct VirtIOSndInfo {
1236 hda_fn_nid: u32,
1237}
1238
1239#[repr(C)]
1240#[derive(Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1241struct VirtIOSndJackHdr {
1242 hdr: VirtIOSndHdr,
1243 jack_id: u32,
1245}
1246
1247#[repr(C)]
1249#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1250pub struct VirtIOSndJackInfo {
1251 hdr: VirtIOSndInfo,
1252 features: u32,
1253 hda_reg_defconf: u32,
1255 hda_reg_caps: u32,
1257 connected: u8,
1259
1260 _padding: [u8; 7],
1261}
1262
1263impl Debug for VirtIOSndJackInfo {
1264 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1265 f.debug_struct("VirtIOSndJackInfo")
1266 .field("hdr", &self.hdr)
1267 .field("features", &JackFeatures::from_bits_retain(self.features))
1268 .field("hda_reg_defconf", &self.hda_reg_defconf)
1269 .field("hda_reg_caps", &self.hda_reg_caps)
1270 .field("connected", &self.connected)
1271 .field("_padding", &self._padding)
1272 .finish()
1273 }
1274}
1275
1276impl Display for VirtIOSndJackInfo {
1277 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1278 let connected_status = if self.connected == 1 {
1279 "CONNECTED"
1280 } else {
1281 "DISCONNECTED"
1282 };
1283 write!(
1284 f,
1285 "features: {:?}, hda_reg_defconf: {}, hda_reg_caps: {}, connected: {}",
1286 JackFeatures::from_bits_retain(self.features),
1287 self.hda_reg_defconf,
1288 self.hda_reg_caps,
1289 connected_status
1290 )
1291 }
1292}
1293
1294#[repr(C)]
1295#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1296struct VirtIOSndJackInfoRsp {
1297 hdr: VirtIOSndHdr,
1298 body: VirtIOSndJackInfo,
1299}
1300
1301#[repr(C)]
1302#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1303struct VirtIOSndJackRemap {
1304 hdr: VirtIOSndJackHdr,
1305 association: u32,
1306 sequence: u32,
1307}
1308
1309#[repr(C)]
1310#[derive(Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1311struct VirtIOSndPcmHdr {
1312 hdr: VirtIOSndHdr,
1314 stream_id: u32,
1316}
1317
1318#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1319enum PcmStreamFeatures {
1320 ShmemHost = 0,
1321 ShmemGuest,
1322 MsgPolling,
1323 EvtShmemPeriods,
1324 EvtXruns,
1325}
1326
1327impl From<PcmStreamFeatures> for u32 {
1328 fn from(value: PcmStreamFeatures) -> Self {
1329 value as _
1330 }
1331}
1332
1333#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1334#[repr(u64)]
1335enum PcmSampleFormat {
1336 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, }
1364
1365impl From<PcmSampleFormat> for u64 {
1366 fn from(value: PcmSampleFormat) -> Self {
1367 value as _
1368 }
1369}
1370
1371#[repr(C)]
1373#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1374pub struct VirtIOSndPcmInfo {
1375 hdr: VirtIOSndInfo,
1376 features: u32, formats: u64, rates: u64, direction: u8,
1381 channels_min: u8,
1383 channels_max: u8,
1385 _padding: [u8; 5],
1386}
1387
1388impl Debug for VirtIOSndPcmInfo {
1389 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1390 f.debug_struct("VirtIOSndPcmInfo")
1391 .field("hdr", &self.hdr)
1392 .field("features", &PcmFeatures::from_bits_retain(self.features))
1393 .field("formats", &PcmFormats::from_bits_retain(self.formats))
1394 .field("rates", &PcmRates::from_bits_retain(self.rates))
1395 .field("direction", &self.direction)
1396 .field("channels_min", &self.channels_min)
1397 .field("channels_max", &self.channels_max)
1398 .field("_padding", &self._padding)
1399 .finish()
1400 }
1401}
1402
1403impl Display for VirtIOSndPcmInfo {
1404 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1405 let direction = if self.direction == VIRTIO_SND_D_INPUT {
1406 "INPUT"
1407 } else {
1408 "OUTPUT"
1409 };
1410 write!(
1411 f,
1412 "features: {:?}, rates: {:?}, formats: {:?}, direction: {}",
1413 PcmFeatures::from_bits_retain(self.features),
1414 PcmRates::from_bits_retain(self.rates),
1415 PcmFormats::from_bits_retain(self.formats),
1416 direction
1417 )
1418 }
1419}
1420
1421#[derive(Clone, Debug, Default, Eq, PartialEq)]
1422struct PcmParameters {
1423 setup: bool,
1424 buffer_bytes: u32,
1425 period_bytes: u32,
1426 features: PcmFeatures,
1427 channels: u8,
1428 format: PcmFormat,
1429 rate: PcmRate,
1430}
1431
1432#[repr(C)]
1433#[derive(Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1434struct VirtIOSndPcmSetParams {
1435 hdr: VirtIOSndPcmHdr, buffer_bytes: u32,
1437 period_bytes: u32,
1438 features: u32, channels: u8,
1440 format: u8,
1441 rate: u8,
1442
1443 _padding: u8,
1444}
1445
1446#[repr(C)]
1448#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1449struct VirtIOSndPcmXfer {
1450 stream_id: u32,
1451}
1452
1453#[repr(C)]
1455#[derive(Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
1456struct VirtIOSndPcmStatus {
1457 status: u32,
1458 latency_bytes: u32,
1459}
1460
1461#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1462#[repr(u8)]
1463enum ChannelPosition {
1464 None = 0,
1466 Na,
1468 Mono,
1470 Fl,
1472 Fr,
1474 Rl,
1476 Rr,
1478 Fc,
1480 Lfe,
1482 Sl,
1484 Sr,
1486 Rc,
1488 Flc,
1490 Frc,
1492 Rlc,
1494 Rrc,
1496 Flw,
1498 Frw,
1500 Flh,
1502 Fch,
1504 Frh,
1506 Tc,
1508 Tfl,
1510 Tfr,
1512 Tfc,
1514 Trl,
1516 Trr,
1518 Trc,
1520 Tflc,
1522 Tfrc,
1524 Tsl,
1526 Tsr,
1528 Llfe,
1530 Rlfe,
1532 Bc,
1534 Blc,
1536 Brc,
1538}
1539
1540const VIRTIO_SND_CHMAP_MAX_SIZE: usize = 18;
1542
1543#[repr(C)]
1544#[derive(Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1545struct VirtIOSndChmapInfo {
1546 hdr: VirtIOSndInfo,
1547 direction: u8,
1548 channels: u8,
1549 positions: [u8; VIRTIO_SND_CHMAP_MAX_SIZE],
1550}
1551
1552impl Display for VirtIOSndChmapInfo {
1553 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1554 let direction = if self.direction == VIRTIO_SND_D_INPUT {
1555 "INPUT"
1556 } else {
1557 "OUTPUT"
1558 };
1559 write!(
1560 f,
1561 "direction: {}, channels: {}, positions: [",
1562 direction, self.channels
1563 )?;
1564 for i in 0..usize::from(self.channels) {
1565 if i != 0 {
1566 write!(f, ", ")?;
1567 }
1568 if let Some(position) = ChannelPosition::n(self.positions[i]) {
1569 write!(f, "{:?}", position)?;
1570 } else {
1571 write!(f, "{}", self.positions[i])?;
1572 }
1573 }
1574 write!(f, "]")?;
1575 Ok(())
1576 }
1577}
1578
1579#[cfg(test)]
1580mod tests {
1581 use super::*;
1582 use crate::{
1583 config::ReadOnly,
1584 hal::fake::FakeHal,
1585 transport::{
1586 DeviceType,
1587 fake::{FakeTransport, QueueStatus, State},
1588 },
1589 };
1590 use alloc::{sync::Arc, vec};
1591 use fake::FakeSoundDevice;
1592 use std::sync::Mutex;
1593
1594 #[test]
1595 fn config() {
1596 let config_space = VirtIOSoundConfig {
1597 jacks: ReadOnly::new(3),
1598 streams: ReadOnly::new(4),
1599 chmaps: ReadOnly::new(2),
1600 };
1601 let state = Arc::new(Mutex::new(State::new(
1602 vec![
1603 QueueStatus::default(),
1604 QueueStatus::default(),
1605 QueueStatus::default(),
1606 QueueStatus::default(),
1607 ],
1608 config_space,
1609 )));
1610 let transport = FakeTransport {
1611 device_type: DeviceType::Sound,
1612 max_queue_size: 32,
1613 device_features: 0,
1614 state: state.clone(),
1615 };
1616 let sound =
1617 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1618 assert_eq!(sound.jacks(), 3);
1619 assert_eq!(sound.streams(), 4);
1620 assert_eq!(sound.chmaps(), 2);
1621 }
1622
1623 #[test]
1624 fn empty_info() {
1625 let (fake, transport) = FakeSoundDevice::new(vec![], vec![], vec![]);
1626 let mut sound =
1627 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1628 let handle = fake.spawn();
1629
1630 assert_eq!(sound.jacks(), 0);
1631 assert_eq!(sound.streams(), 0);
1632 assert_eq!(sound.chmaps(), 0);
1633 assert_eq!(sound.output_streams().unwrap(), vec![]);
1634 assert_eq!(sound.input_streams().unwrap(), vec![]);
1635
1636 fake.terminate();
1637 handle.join().unwrap();
1638 }
1639
1640 #[test]
1641 fn stream_info() {
1642 let (fake, transport) = FakeSoundDevice::new(
1643 vec![VirtIOSndJackInfo {
1644 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1645 features: 0,
1646 hda_reg_defconf: 0,
1647 hda_reg_caps: 0,
1648 connected: 0,
1649 _padding: Default::default(),
1650 }],
1651 vec![
1652 VirtIOSndPcmInfo {
1653 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1654 features: 0,
1655 formats: (PcmFormats::U8 | PcmFormats::U32).bits(),
1656 rates: (PcmRates::RATE_44100 | PcmRates::RATE_32000).bits(),
1657 direction: VIRTIO_SND_D_OUTPUT,
1658 channels_min: 1,
1659 channels_max: 2,
1660 _padding: Default::default(),
1661 },
1662 VirtIOSndPcmInfo {
1663 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1664 features: 0,
1665 formats: 0,
1666 rates: 0,
1667 direction: VIRTIO_SND_D_INPUT,
1668 channels_min: 0,
1669 channels_max: 0,
1670 _padding: Default::default(),
1671 },
1672 ],
1673 vec![VirtIOSndChmapInfo {
1674 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1675 direction: 0,
1676 channels: 0,
1677 positions: [0; 18],
1678 }],
1679 );
1680 let mut sound =
1681 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1682 let handle = fake.spawn();
1683
1684 assert_eq!(sound.output_streams().unwrap(), vec![0]);
1685 assert_eq!(
1686 sound.rates_supported(0).unwrap(),
1687 PcmRates::RATE_44100 | PcmRates::RATE_32000
1688 );
1689 assert_eq!(
1690 sound.formats_supported(0).unwrap(),
1691 PcmFormats::U8 | PcmFormats::U32
1692 );
1693 assert_eq!(sound.channel_range_supported(0).unwrap(), 1..=2);
1694 assert_eq!(sound.features_supported(0).unwrap(), PcmFeatures::empty());
1695
1696 assert_eq!(sound.input_streams().unwrap(), vec![1]);
1697 assert_eq!(sound.rates_supported(1).unwrap(), PcmRates::empty());
1698 assert_eq!(sound.formats_supported(1).unwrap(), PcmFormats::empty());
1699 assert_eq!(sound.channel_range_supported(1).unwrap(), 0..=0);
1700 assert_eq!(sound.features_supported(1).unwrap(), PcmFeatures::empty());
1701
1702 fake.terminate();
1703 handle.join().unwrap();
1704 }
1705
1706 #[test]
1707 fn play() {
1708 let (fake, transport) = FakeSoundDevice::new(
1709 vec![],
1710 vec![VirtIOSndPcmInfo {
1711 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1712 features: 0,
1713 formats: (PcmFormats::U8 | PcmFormats::U32).bits(),
1714 rates: (PcmRates::RATE_44100 | PcmRates::RATE_32000).bits(),
1715 direction: VIRTIO_SND_D_OUTPUT,
1716 channels_min: 1,
1717 channels_max: 2,
1718 _padding: Default::default(),
1719 }],
1720 vec![],
1721 );
1722 let mut sound =
1723 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1724 let handle = fake.spawn();
1725
1726 assert_eq!(sound.output_streams().unwrap(), vec![0]);
1727 assert_eq!(sound.input_streams().unwrap(), vec![]);
1728
1729 sound
1730 .pcm_set_params(
1731 0,
1732 100,
1733 100,
1734 PcmFeatures::empty(),
1735 1,
1736 PcmFormat::U8,
1737 PcmRate::Rate8000,
1738 )
1739 .unwrap();
1740 assert_eq!(
1741 fake.params.lock().unwrap()[0],
1742 Some(VirtIOSndPcmSetParams {
1743 hdr: VirtIOSndPcmHdr {
1744 hdr: VirtIOSndHdr {
1745 command_code: CommandCode::RPcmSetParams.into(),
1746 },
1747 stream_id: 0,
1748 },
1749 buffer_bytes: 100,
1750 period_bytes: 100,
1751 features: 0,
1752 channels: 1,
1753 format: PcmFormat::U8.into(),
1754 rate: PcmRate::Rate8000.into(),
1755 _padding: Default::default(),
1756 })
1757 );
1758
1759 sound.pcm_prepare(0).unwrap();
1760 sound.pcm_start(0).unwrap();
1761
1762 let mut expected_sound = vec![];
1763
1764 println!("Playing empty");
1766 sound.pcm_xfer(0, &[]).unwrap();
1767 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1768
1769 println!("Playing 100");
1771 sound.pcm_xfer(0, &[42; 100]).unwrap();
1772 expected_sound.extend([42; 100]);
1773 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1774
1775 println!("Playing 200");
1777 sound.pcm_xfer(0, &[66; 200]).unwrap();
1778 expected_sound.extend([66; 200]);
1779 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1780
1781 println!("Playing 50");
1783 sound.pcm_xfer(0, &[55; 50]).unwrap();
1784 expected_sound.extend([55; 50]);
1785 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1786
1787 println!("Playing 5000");
1789 sound.pcm_xfer(0, &[12; 5000]).unwrap();
1790 expected_sound.extend([12; 5000]);
1791 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1792
1793 sound.pcm_stop(0).unwrap();
1794 sound.pcm_release(0).unwrap();
1795
1796 fake.terminate();
1797 handle.join().unwrap();
1798 }
1799}