1#[cfg(test)]
4mod fake;
5
6use super::common::Feature;
7use crate::{
8 queue::{owning::OwningQueue, VirtQueue},
9 transport::Transport,
10 volatile::{volread, ReadOnly},
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::{AsBytes, FromBytes, FromZeroes};
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 config_ptr = transport.config_space::<VirtIOSoundConfig>()?;
100 let (jacks, streams, chmaps) = unsafe {
102 (
103 volread!(config_ptr, jacks),
104 volread!(config_ptr, streams),
105 volread!(config_ptr, chmaps),
106 )
107 };
108 info!(
109 "[sound device] config: jacks: {}, streams: {}, chmaps: {}",
110 jacks, streams, chmaps
111 );
112
113 let queue_buf_send = FromZeroes::new_box_slice_zeroed(PAGE_SIZE);
114 let queue_buf_recv = FromZeroes::new_box_slice_zeroed(PAGE_SIZE);
115
116 let mut pcm_parameters = vec![];
118 for _ in 0..streams {
119 pcm_parameters.push(PcmParameters::default());
120 }
121
122 transport.finish_init();
123
124 if event_queue.should_notify() {
125 transport.notify(EVENT_QUEUE_IDX);
126 }
127
128 Ok(VirtIOSound {
129 transport,
130 control_queue,
131 event_queue,
132 tx_queue,
133 rx_queue,
134 negotiated_features,
135 jacks,
136 streams,
137 chmaps,
138 pcm_infos: None,
139 jack_infos: None,
140 chmap_infos: None,
141 queue_buf_send,
142 queue_buf_recv,
143 pcm_parameters,
144 set_up: false,
145 token_rsp: BTreeMap::new(),
146 pcm_states: vec![],
147 token_buf: BTreeMap::new(),
148 })
149 }
150
151 pub fn jacks(&self) -> u32 {
153 self.jacks
154 }
155
156 pub fn streams(&self) -> u32 {
158 self.streams
159 }
160
161 pub fn chmaps(&self) -> u32 {
163 self.chmaps
164 }
165
166 pub fn ack_interrupt(&mut self) -> bool {
168 self.transport.ack_interrupt()
169 }
170
171 fn request<Req: AsBytes>(&mut self, req: Req) -> Result<VirtIOSndHdr> {
172 self.control_queue.add_notify_wait_pop(
173 &[req.as_bytes()],
174 &mut [self.queue_buf_recv.as_bytes_mut()],
175 &mut self.transport,
176 )?;
177 Ok(VirtIOSndHdr::read_from_prefix(&self.queue_buf_recv).unwrap())
178 }
179
180 fn set_up(&mut self) -> Result<()> {
182 if let Ok(jack_infos) = self.jack_info(0, self.jacks) {
184 for jack_info in &jack_infos {
185 info!("[sound device] jack_info: {}", jack_info);
186 }
187 self.jack_infos = Some(jack_infos);
188 } else {
189 self.jack_infos = Some(vec![]);
190 warn!("[sound device] Error getting jack infos");
191 }
192
193 let pcm_infos = self.pcm_info(0, self.streams)?;
195 for pcm_info in &pcm_infos {
196 info!("[sound device] pcm_info: {}", pcm_info);
197 }
198 self.pcm_infos = Some(pcm_infos);
199
200 if let Ok(chmap_infos) = self.chmap_info(0, self.chmaps) {
202 for chmap_info in &chmap_infos {
203 info!("[sound device] chmap_info: {}", chmap_info);
204 }
205 self.chmap_infos = Some(chmap_infos);
206 } else {
207 self.chmap_infos = Some(vec![]);
208 warn!("[sound device] Error getting chmap infos");
209 }
210
211 for _ in 0..self.streams {
213 self.pcm_states.push(PCMState::default());
214 }
215 Ok(())
216 }
217
218 pub fn enable_interrupts(&mut self, enable: bool) {
220 self.event_queue.set_dev_notify(enable);
221 }
222
223 fn jack_info(&mut self, jack_start_id: u32, jack_count: u32) -> Result<Vec<VirtIOSndJackInfo>> {
225 if jack_start_id + jack_count > self.jacks {
226 error!("jack_start_id + jack_count > jacks! There are not enough jacks to be queried!");
227 return Err(Error::IoError);
228 }
229 let hdr = self.request(VirtIOSndQueryInfo {
230 hdr: ItemInformationRequestType::RJackInfo.into(),
231 start_id: jack_start_id,
232 count: jack_count,
233 size: size_of::<VirtIOSndJackInfo>() as u32,
234 })?;
235 if hdr != RequestStatusCode::Ok.into() {
236 return Err(Error::IoError);
237 }
238 let mut jack_infos = vec![];
240 for i in 0..jack_count as usize {
241 const HDR_SIZE: usize = size_of::<VirtIOSndHdr>();
242 const JACK_INFO_SIZE: usize = size_of::<VirtIOSndJackInfo>();
243 let start_byte_idx = HDR_SIZE + i * JACK_INFO_SIZE;
244 let end_byte_idx = HDR_SIZE + (i + 1) * JACK_INFO_SIZE;
245 let jack_info =
246 VirtIOSndJackInfo::read_from(&self.queue_buf_recv[start_byte_idx..end_byte_idx])
247 .unwrap();
248 jack_infos.push(jack_info)
249 }
250 Ok(jack_infos)
251 }
252
253 fn pcm_info(
255 &mut self,
256 stream_start_id: u32,
257 stream_count: u32,
258 ) -> Result<Vec<VirtIOSndPcmInfo>> {
259 if stream_start_id + stream_count > self.streams {
260 error!("stream_start_id + stream_count > streams! There are not enough streams to be queried!");
261 return Err(Error::IoError);
262 }
263 let request_hdr = VirtIOSndHdr::from(ItemInformationRequestType::RPcmInfo);
264 let hdr = self.request(VirtIOSndQueryInfo {
265 hdr: request_hdr,
266 start_id: stream_start_id,
267 count: stream_count,
268 size: size_of::<VirtIOSndPcmInfo>() as u32,
269 })?;
270 if hdr != RequestStatusCode::Ok.into() {
271 return Err(Error::IoError);
272 }
273 let mut pcm_infos = vec![];
275 for i in 0..stream_count as usize {
276 const HDR_SIZE: usize = size_of::<VirtIOSndHdr>();
277 const PCM_INFO_SIZE: usize = size_of::<VirtIOSndPcmInfo>();
278 let start_byte_idx = HDR_SIZE + i * PCM_INFO_SIZE;
279 let end_byte_idx = HDR_SIZE + (i + 1) * PCM_INFO_SIZE;
280 let pcm_info =
281 VirtIOSndPcmInfo::read_from(&self.queue_buf_recv[start_byte_idx..end_byte_idx])
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(&self.queue_buf_recv[start_byte..end_byte]).unwrap();
314 chmap_infos.push(chmap_info);
315 }
316 Ok(chmap_infos)
317 }
318
319 pub fn jack_remap(&mut self, jack_id: u32, association: u32, sequence: u32) -> Result {
325 if !self.set_up {
326 self.set_up()?;
327 self.set_up = true;
328 }
329 if self.jacks == 0 {
330 error!("[sound device] There is no available jacks!");
331 return Err(Error::InvalidParam);
332 }
333 if jack_id >= self.jacks {
334 error!("jack_id >= self.jacks! Make sure jack_id is in the range of [0, jacks - 1)!");
335 return Err(Error::InvalidParam);
336 }
337
338 let jack_features = JackFeatures::from_bits_retain(
339 self.jack_infos
340 .as_ref()
341 .unwrap()
342 .get(jack_id as usize)
343 .unwrap()
344 .features,
345 );
346 if !jack_features.contains(JackFeatures::REMAP) {
347 error!("The jack selected does not support VIRTIO_SND_JACK_F_REMAP!");
348 return Err(Error::Unsupported);
349 }
350 let hdr = self.request(VirtIOSndJackRemap {
351 hdr: VirtIOSndJackHdr {
352 hdr: CommandCode::RJackRemap.into(),
353 jack_id,
354 },
355 association,
356 sequence,
357 })?;
358 if hdr == RequestStatusCode::Ok.into() {
359 Ok(())
360 } else {
361 Err(Error::Unsupported)
362 }
363 }
364
365 pub fn pcm_set_params(
367 &mut self,
368 stream_id: u32,
369 buffer_bytes: u32,
370 period_bytes: u32,
371 features: PcmFeatures,
372 channels: u8,
373 format: PcmFormat,
374 rate: PcmRate,
375 ) -> Result {
376 if !self.set_up {
377 self.set_up()?;
378 self.set_up = true;
379 }
380 if period_bytes == 0 || period_bytes > buffer_bytes || buffer_bytes % period_bytes != 0 {
381 return Err(Error::InvalidParam);
382 }
383 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmSetParams);
384 let rsp = self.request(VirtIOSndPcmSetParams {
385 hdr: VirtIOSndPcmHdr {
386 hdr: request_hdr,
387 stream_id,
388 },
389 buffer_bytes,
390 period_bytes,
391 features: features.bits(),
392 channels,
393 format: format.into(),
394 rate: rate.into(),
395 _padding: 0,
396 })?;
397 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
399 self.pcm_parameters[stream_id as usize] = PcmParameters {
400 setup: true,
401 buffer_bytes,
402 period_bytes,
403 features,
404 channels,
405 format,
406 rate,
407 };
408 Ok(())
409 } else {
410 Err(Error::IoError)
411 }
412 }
413
414 pub fn pcm_prepare(&mut self, stream_id: u32) -> Result {
416 if !self.set_up {
417 self.set_up()?;
418 self.set_up = true;
419 }
420 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmPrepare);
421 let rsp = self.request(VirtIOSndPcmHdr {
422 hdr: request_hdr,
423 stream_id,
424 })?;
425 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
427 Ok(())
428 } else {
429 Err(Error::IoError)
430 }
431 }
432
433 pub fn pcm_release(&mut self, stream_id: u32) -> Result {
435 if !self.set_up {
436 self.set_up()?;
437 self.set_up = true;
438 }
439 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmRelease);
440 let rsp = self.request(VirtIOSndPcmHdr {
441 hdr: request_hdr,
442 stream_id,
443 })?;
444 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
446 Ok(())
447 } else {
448 Err(Error::IoError)
449 }
450 }
451
452 pub fn pcm_start(&mut self, stream_id: u32) -> Result {
454 if !self.set_up {
455 self.set_up()?;
456 self.set_up = true;
457 }
458 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmStart);
459 let rsp = self.request(VirtIOSndPcmHdr {
460 hdr: request_hdr,
461 stream_id,
462 })?;
463 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
465 Ok(())
466 } else {
467 Err(Error::IoError)
468 }
469 }
470
471 pub fn pcm_stop(&mut self, stream_id: u32) -> Result {
473 if !self.set_up {
474 self.set_up()?;
475 self.set_up = true;
476 }
477 let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmStop);
478 let rsp = self.request(VirtIOSndPcmHdr {
479 hdr: request_hdr,
480 stream_id,
481 })?;
482 if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
484 Ok(())
485 } else {
486 Err(Error::IoError)
487 }
488 }
489
490 pub fn pcm_xfer(&mut self, stream_id: u32, frames: &[u8]) -> Result {
496 const U32_SIZE: usize = size_of::<u32>();
497 if !self.set_up {
498 self.set_up()?;
499 self.set_up = true;
500 }
501 if !self.pcm_parameters[stream_id as usize].setup {
502 warn!("Please set parameters for a stream before using it!");
503 return Err(Error::IoError);
504 }
505
506 let stream_id_bytes = stream_id.to_le_bytes();
507 let period_size = self.pcm_parameters[stream_id as usize].period_bytes as usize;
508
509 let mut remaining_buffers = frames.chunks(period_size);
510 let mut buffers: [Option<&[u8]>; QUEUE_SIZE as usize] = [None; QUEUE_SIZE as usize];
511 let mut statuses: [VirtIOSndPcmStatus; QUEUE_SIZE as usize] =
512 array::from_fn(|_| Default::default());
513 let mut tokens = [0; QUEUE_SIZE as usize];
514 let mut head = 0;
516 let mut tail = 0;
518
519 loop {
520 if self.tx_queue.available_desc() >= 3 {
523 if let Some(buffer) = remaining_buffers.next() {
524 tokens[head] = unsafe {
525 self.tx_queue.add(
526 &[&stream_id_bytes, buffer],
527 &mut [statuses[head].as_bytes_mut()],
528 )?
529 };
530 if self.tx_queue.should_notify() {
531 self.transport.notify(TX_QUEUE_IDX);
532 }
533 buffers[head] = Some(buffer);
534 head += 1;
535 if head >= usize::from(QUEUE_SIZE) {
536 head = 0;
537 }
538 } else if head == tail {
539 break;
540 }
541 }
542 if self.tx_queue.can_pop() {
543 unsafe {
544 self.tx_queue.pop_used(
545 tokens[tail],
546 &[&stream_id_bytes, buffers[tail].unwrap()],
547 &mut [statuses[tail].as_bytes_mut()],
548 )?;
549 }
550 if statuses[tail].status != CommandCode::SOk.into() {
551 return Err(Error::IoError);
552 }
553 tail += 1;
554 if tail >= usize::from(QUEUE_SIZE) {
555 tail = 0;
556 }
557 }
558 spin_loop();
559 }
560
561 Ok(())
562 }
563
564 pub fn pcm_xfer_nb(&mut self, stream_id: u32, frames: &[u8]) -> Result<u16> {
572 if !self.set_up {
573 self.set_up()?;
574 self.set_up = true;
575 }
576 if !self.pcm_parameters[stream_id as usize].setup {
577 warn!("Please set parameters for a stream before using it!");
578 return Err(Error::IoError);
579 }
580 const U32_SIZE: usize = size_of::<u32>();
581 let period_size: usize = self.pcm_parameters[stream_id as usize].period_bytes as usize;
582 assert_eq!(period_size, frames.len());
583 let mut buf = vec![0; U32_SIZE + period_size];
584 buf[..U32_SIZE].copy_from_slice(&stream_id.to_le_bytes());
585 buf[U32_SIZE..U32_SIZE + period_size].copy_from_slice(frames);
586 let mut rsp = VirtIOSndPcmStatus::new_box_zeroed();
587 let token = unsafe { self.tx_queue.add(&[&buf], &mut [rsp.as_bytes_mut()])? };
588 if self.tx_queue.should_notify() {
589 self.transport.notify(TX_QUEUE_IDX);
590 }
591 self.token_buf.insert(token, buf);
592 self.token_rsp.insert(token, rsp);
593 Ok(token)
594 }
595
596 pub fn pcm_xfer_ok(&mut self, token: u16) -> Result {
598 assert!(self.token_buf.contains_key(&token));
599 assert!(self.token_rsp.contains_key(&token));
600 unsafe {
601 self.tx_queue.pop_used(
602 token,
603 &[&self.token_buf[&token]],
604 &mut [self.token_rsp.get_mut(&token).unwrap().as_bytes_mut()],
605 )?;
606 }
607
608 self.token_buf.remove(&token);
609 self.token_rsp.remove(&token);
610 Ok(())
611 }
612
613 pub fn output_streams(&mut self) -> Result<Vec<u32>> {
615 if !self.set_up {
616 self.set_up()?;
617 self.set_up = true;
618 }
619 Ok(self
620 .pcm_infos
621 .as_ref()
622 .unwrap()
623 .iter()
624 .enumerate()
625 .filter(|(_, info)| info.direction == VIRTIO_SND_D_OUTPUT)
626 .map(|(idx, _)| idx as u32)
627 .collect())
628 }
629
630 pub fn input_streams(&mut self) -> Result<Vec<u32>> {
632 if !self.set_up {
633 self.set_up()?;
634 self.set_up = true;
635 }
636 Ok(self
637 .pcm_infos
638 .as_ref()
639 .unwrap()
640 .iter()
641 .enumerate()
642 .filter(|(_, info)| info.direction == VIRTIO_SND_D_INPUT)
643 .map(|(idx, _)| idx as u32)
644 .collect())
645 }
646
647 pub fn rates_supported(&mut self, stream_id: u32) -> Result<PcmRates> {
649 if !self.set_up {
650 self.set_up()?;
651 self.set_up = true;
652 }
653 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
654 return Err(Error::InvalidParam);
655 }
656 Ok(PcmRates::from_bits_retain(
657 self.pcm_infos.as_ref().unwrap()[stream_id as usize].rates,
658 ))
659 }
660
661 pub fn formats_supported(&mut self, stream_id: u32) -> Result<PcmFormats> {
663 if !self.set_up {
664 self.set_up()?;
665 self.set_up = true;
666 }
667 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
668 return Err(Error::InvalidParam);
669 }
670 Ok(PcmFormats::from_bits_retain(
671 self.pcm_infos.as_ref().unwrap()[stream_id as usize].formats,
672 ))
673 }
674
675 pub fn channel_range_supported(&mut self, stream_id: u32) -> Result<RangeInclusive<u8>> {
677 if !self.set_up {
678 self.set_up()?;
679 self.set_up = true;
680 }
681 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
682 return Err(Error::InvalidParam);
683 }
684 let pcm_info = &self.pcm_infos.as_ref().unwrap()[stream_id as usize];
685 Ok(pcm_info.channels_min..=pcm_info.channels_max)
686 }
687
688 pub fn features_supported(&mut self, stream_id: u32) -> Result<PcmFeatures> {
690 if !self.set_up {
691 self.set_up()?;
692 self.set_up = true;
693 }
694 if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
695 return Err(Error::InvalidParam);
696 }
697 let pcm_info = &self.pcm_infos.as_ref().unwrap()[stream_id as usize];
698 Ok(PcmFeatures::from_bits_retain(pcm_info.features))
699 }
700
701 pub fn latest_notification(&mut self) -> Result<Option<Notification>> {
703 self.event_queue.poll(&mut self.transport, |buffer| {
706 if let Some(event) = VirtIOSndEvent::read_from(buffer) {
707 Ok(Some(Notification {
708 notification_type: NotificationType::n(event.hdr.command_code)
709 .ok_or(Error::IoError)?,
710 data: event.data,
711 }))
712 } else {
713 Ok(None)
714 }
715 })
716 }
717}
718
719#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
721enum PCMState {
722 #[default]
723 SetParams,
724 Prepare,
725 Release,
726 Start,
727 Stop,
728}
729
730const QUEUE_SIZE: u16 = 32;
731const CONTROL_QUEUE_IDX: u16 = 0;
732const EVENT_QUEUE_IDX: u16 = 1;
733const TX_QUEUE_IDX: u16 = 2;
734const RX_QUEUE_IDX: u16 = 3;
735
736const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC.union(Feature::RING_EVENT_IDX);
737
738bitflags! {
739 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
740 #[repr(transparent)]
741 struct JackFeatures: u32 {
742 const REMAP = 1 << 0;
744 }
745}
746
747bitflags! {
748 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
750 #[repr(transparent)]
751 pub struct PcmFeatures: u32 {
752 const SHMEM_HOST = 1 << 0;
754 const SHMEM_GUEST = 1 << 1;
756 const MSG_POLLING = 1 << 2;
758 const EVT_SHMEM_PERIODS = 1 << 3;
760 const EVT_XRUNS = 1 << 4;
762 }
763}
764
765bitflags! {
766 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
768 #[repr(transparent)]
769 pub struct PcmFormats: u64 {
770 const IMA_ADPCM = 1 << 0;
772 const MU_LAW = 1 << 1;
774 const A_LAW = 1 << 2;
776 const S8 = 1 << 3;
778 const U8 = 1 << 4;
780 const S16 = 1 << 5;
782 const U16 = 1 << 6;
784 const S18_3 = 1 << 7;
786 const U18_3 = 1 << 8;
788 const S20_3 = 1 << 9;
790 const U20_3 = 1 << 10;
792 const S24_3 = 1 << 11;
794 const U24_3 = 1 << 12;
796 const S20 = 1 << 13;
798 const U20 = 1 << 14;
800 const S24 = 1 << 15;
802 const U24 = 1 << 16;
804 const S32 = 1 << 17;
806 const U32 = 1 << 18;
808 const FLOAT = 1 << 19;
810 const FLOAT64 = 1 << 20;
812 const DSD_U8 = 1 << 21;
814 const DSD_U16 = 1 << 22;
816 const DSD_U32 = 1 << 23;
818 const IEC958_SUBFRAME = 1 << 24;
820 }
821}
822
823#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
825#[repr(u8)]
826pub enum PcmFormat {
827 #[default]
829 ImaAdpcm = 0,
830 MuLaw = 1,
832 ALaw = 2,
834 S8 = 3,
836 U8 = 4,
838 S16 = 5,
840 U16 = 6,
842 S18_3 = 7,
844 U18_3 = 8,
846 S20_3 = 9,
848 U20_3 = 10,
850 S24_3 = 11,
852 U24_3 = 12,
854 S20 = 13,
856 U20 = 14,
858 S24 = 15,
860 U24 = 16,
862 S32 = 17,
864 U32 = 18,
866 FLOAT = 19,
868 FLOAT64 = 20,
870 DsdU8 = 21,
872 DsdU16 = 22,
874 DsdU32 = 23,
876 Iec958Subframe = 24,
878}
879
880impl From<PcmFormat> for PcmFormats {
881 fn from(format: PcmFormat) -> Self {
882 match format {
883 PcmFormat::ImaAdpcm => PcmFormats::IMA_ADPCM,
884 PcmFormat::MuLaw => PcmFormats::MU_LAW,
885 PcmFormat::ALaw => PcmFormats::A_LAW,
886 PcmFormat::S8 => PcmFormats::S8,
887 PcmFormat::U8 => PcmFormats::U8,
888 PcmFormat::S16 => PcmFormats::S16,
889 PcmFormat::U16 => PcmFormats::U16,
890 PcmFormat::S18_3 => PcmFormats::S18_3,
891 PcmFormat::U18_3 => PcmFormats::U18_3,
892 PcmFormat::S20_3 => PcmFormats::S20_3,
893 PcmFormat::U20_3 => PcmFormats::U20_3,
894 PcmFormat::S24_3 => PcmFormats::S24_3,
895 PcmFormat::U24_3 => PcmFormats::U24_3,
896 PcmFormat::S20 => PcmFormats::S20,
897 PcmFormat::U20 => PcmFormats::U20,
898 PcmFormat::S24 => PcmFormats::S24,
899 PcmFormat::U24 => PcmFormats::U24,
900 PcmFormat::S32 => PcmFormats::S32,
901 PcmFormat::U32 => PcmFormats::U32,
902 PcmFormat::FLOAT => PcmFormats::FLOAT,
903 PcmFormat::FLOAT64 => PcmFormats::FLOAT64,
904 PcmFormat::DsdU8 => PcmFormats::DSD_U8,
905 PcmFormat::DsdU16 => PcmFormats::DSD_U16,
906 PcmFormat::DsdU32 => PcmFormats::DSD_U32,
907 PcmFormat::Iec958Subframe => PcmFormats::IEC958_SUBFRAME,
908 }
909 }
910}
911
912impl From<PcmFormat> for u8 {
913 fn from(format: PcmFormat) -> u8 {
914 format as _
915 }
916}
917
918bitflags! {
919 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
921 #[repr(transparent)]
922 pub struct PcmRates: u64 {
923 const RATE_5512 = 1 << 0;
925 const RATE_8000 = 1 << 1;
927 const RATE_11025 = 1 << 2;
929 const RATE_16000 = 1 << 3;
931 const RATE_22050 = 1 << 4;
933 const RATE_32000 = 1 << 5;
935 const RATE_44100 = 1 << 6;
937 const RATE_48000 = 1 << 7;
939 const RATE_64000 = 1 << 8;
941 const RATE_88200 = 1 << 9;
943 const RATE_96000 = 1 << 10;
945 const RATE_176400 = 1 << 11;
947 const RATE_192000 = 1 << 12;
949 const RATE_384000 = 1 << 13;
951 }
952}
953
954#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
956#[repr(u8)]
957pub enum PcmRate {
958 #[default]
960 Rate5512 = 0,
961 Rate8000 = 1,
963 Rate11025 = 2,
965 Rate16000 = 3,
967 Rate22050 = 4,
969 Rate32000 = 5,
971 Rate44100 = 6,
973 Rate48000 = 7,
975 Rate64000 = 8,
977 Rate88200 = 9,
979 Rate96000 = 10,
981 Rate176400 = 11,
983 Rate192000 = 12,
985 Rate384000 = 13,
987}
988
989impl From<PcmRate> for PcmRates {
990 fn from(rate: PcmRate) -> Self {
991 match rate {
992 PcmRate::Rate5512 => Self::RATE_5512,
993 PcmRate::Rate8000 => Self::RATE_8000,
994 PcmRate::Rate11025 => Self::RATE_11025,
995 PcmRate::Rate16000 => Self::RATE_16000,
996 PcmRate::Rate22050 => Self::RATE_22050,
997 PcmRate::Rate32000 => Self::RATE_32000,
998 PcmRate::Rate44100 => Self::RATE_44100,
999 PcmRate::Rate48000 => Self::RATE_48000,
1000 PcmRate::Rate64000 => Self::RATE_64000,
1001 PcmRate::Rate88200 => Self::RATE_88200,
1002 PcmRate::Rate96000 => Self::RATE_96000,
1003 PcmRate::Rate176400 => Self::RATE_176400,
1004 PcmRate::Rate192000 => Self::RATE_192000,
1005 PcmRate::Rate384000 => Self::RATE_384000,
1006 }
1007 }
1008}
1009
1010impl From<PcmRate> for u8 {
1011 fn from(rate: PcmRate) -> Self {
1012 rate as _
1013 }
1014}
1015
1016#[repr(C)]
1017struct VirtIOSoundConfig {
1018 jacks: ReadOnly<u32>,
1019 streams: ReadOnly<u32>,
1020 chmaps: ReadOnly<u32>,
1021}
1022
1023#[repr(u32)]
1027#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1028enum CommandCode {
1029 RJackInfo = 1,
1031 RJackRemap,
1032
1033 RPcmInfo = 0x0100,
1035 RPcmSetParams,
1036 RPcmPrepare,
1037 RPcmRelease,
1038 RPcmStart,
1039 RPcmStop,
1040
1041 RChmapInfo = 0x0200,
1043
1044 EvtJackConnected = 0x1000,
1046 EvtJackDisconnected,
1047
1048 EvtPcmPeriodElapsed = 0x1100,
1050 EvtPcmXrun,
1051
1052 SOk = 0x8000,
1055 SBadMsg,
1057 SNotSupp,
1059 SIoErr,
1061}
1062
1063impl From<CommandCode> for u32 {
1064 fn from(code: CommandCode) -> u32 {
1065 code as u32
1066 }
1067}
1068
1069#[repr(u32)]
1071#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1072pub enum ItemInformationRequestType {
1073 RJackInfo = 1,
1075 RPcmInfo = 0x0100,
1077 RChmapInfo = 0x0200,
1079}
1080
1081impl From<ItemInformationRequestType> for VirtIOSndHdr {
1082 fn from(value: ItemInformationRequestType) -> Self {
1083 VirtIOSndHdr {
1084 command_code: value.into(),
1085 }
1086 }
1087}
1088
1089impl From<ItemInformationRequestType> for u32 {
1090 fn from(request_type: ItemInformationRequestType) -> u32 {
1091 request_type as _
1092 }
1093}
1094
1095#[derive(Copy, Clone, Eq, PartialEq)]
1096#[repr(u32)]
1097enum RequestStatusCode {
1098 Ok = 0x8000,
1100 BadMsg,
1101 NotSupp,
1102 IoErr,
1103}
1104
1105impl From<RequestStatusCode> for VirtIOSndHdr {
1106 fn from(value: RequestStatusCode) -> Self {
1107 VirtIOSndHdr {
1108 command_code: value as _,
1109 }
1110 }
1111}
1112
1113#[repr(C)]
1115#[derive(AsBytes, Clone, Debug, Eq, FromBytes, FromZeroes, PartialEq)]
1116struct VirtIOSndHdr {
1117 command_code: u32,
1118}
1119
1120impl From<CommandCode> for VirtIOSndHdr {
1121 fn from(value: CommandCode) -> Self {
1122 VirtIOSndHdr {
1123 command_code: value.into(),
1124 }
1125 }
1126}
1127
1128#[repr(C)]
1129#[derive(FromBytes, FromZeroes)]
1130struct VirtIOSndEvent {
1132 hdr: VirtIOSndHdr,
1133 data: u32,
1134}
1135
1136#[repr(u32)]
1138#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1139pub enum NotificationType {
1140 JackConnected = 0x1000,
1142 JackDisconnected,
1144 PcmPeriodElapsed = 0x1100,
1146 PcmXrun,
1148}
1149
1150impl NotificationType {
1151 fn n(value: u32) -> Option<Self> {
1153 match value {
1154 0x1100 => Some(Self::PcmPeriodElapsed),
1155 0x1101 => Some(Self::PcmXrun),
1156 0x1000 => Some(Self::JackConnected),
1157 0x1001 => Some(Self::JackDisconnected),
1158 _ => None,
1159 }
1160 }
1161}
1162
1163#[derive(Clone, Debug, Eq, PartialEq)]
1165pub struct Notification {
1166 notification_type: NotificationType,
1167 data: u32,
1168}
1169
1170impl Notification {
1171 pub fn data(&self) -> u32 {
1173 self.data
1174 }
1175
1176 pub fn notification_type(&self) -> NotificationType {
1178 self.notification_type
1179 }
1180}
1181
1182const VIRTIO_SND_D_OUTPUT: u8 = 0;
1183const VIRTIO_SND_D_INPUT: u8 = 1;
1184
1185#[repr(C)]
1186#[derive(AsBytes, Debug, FromBytes, FromZeroes)]
1187struct VirtIOSndQueryInfo {
1188 hdr: VirtIOSndHdr,
1190 start_id: u32,
1195 count: u32,
1199 size: u32,
1202}
1203
1204#[repr(C)]
1205#[derive(AsBytes, Debug, FromBytes, FromZeroes)]
1206struct VirtIOSndQueryInfoRsp {
1207 hdr: VirtIOSndHdr,
1208 info: VirtIOSndInfo,
1209}
1210
1211#[repr(C)]
1213#[derive(AsBytes, Clone, Debug, Eq, FromBytes, FromZeroes, PartialEq)]
1214pub struct VirtIOSndInfo {
1215 hda_fn_nid: u32,
1216}
1217
1218#[repr(C)]
1219#[derive(AsBytes, Clone, Debug, FromBytes, FromZeroes)]
1220struct VirtIOSndJackHdr {
1221 hdr: VirtIOSndHdr,
1222 jack_id: u32,
1224}
1225
1226#[repr(C)]
1228#[derive(AsBytes, Clone, Eq, FromBytes, FromZeroes, PartialEq)]
1229pub struct VirtIOSndJackInfo {
1230 hdr: VirtIOSndInfo,
1231 features: u32,
1232 hda_reg_defconf: u32,
1234 hda_reg_caps: u32,
1236 connected: u8,
1238
1239 _padding: [u8; 7],
1240}
1241
1242impl Debug for VirtIOSndJackInfo {
1243 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1244 f.debug_struct("VirtIOSndJackInfo")
1245 .field("hdr", &self.hdr)
1246 .field("features", &JackFeatures::from_bits_retain(self.features))
1247 .field("hda_reg_defconf", &self.hda_reg_defconf)
1248 .field("hda_reg_caps", &self.hda_reg_caps)
1249 .field("connected", &self.connected)
1250 .field("_padding", &self._padding)
1251 .finish()
1252 }
1253}
1254
1255impl Display for VirtIOSndJackInfo {
1256 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1257 let connected_status = if self.connected == 1 {
1258 "CONNECTED"
1259 } else {
1260 "DISCONNECTED"
1261 };
1262 write!(
1263 f,
1264 "features: {:?}, hda_reg_defconf: {}, hda_reg_caps: {}, connected: {}",
1265 JackFeatures::from_bits_retain(self.features),
1266 self.hda_reg_defconf,
1267 self.hda_reg_caps,
1268 connected_status
1269 )
1270 }
1271}
1272
1273#[repr(C)]
1274#[derive(AsBytes, FromBytes, FromZeroes)]
1275struct VirtIOSndJackInfoRsp {
1276 hdr: VirtIOSndHdr,
1277 body: VirtIOSndJackInfo,
1278}
1279
1280#[repr(C)]
1281#[derive(AsBytes, FromBytes, FromZeroes)]
1282struct VirtIOSndJackRemap {
1283 hdr: VirtIOSndJackHdr,
1284 association: u32,
1285 sequence: u32,
1286}
1287
1288#[repr(C)]
1289#[derive(AsBytes, Debug, Eq, FromBytes, FromZeroes, PartialEq)]
1290struct VirtIOSndPcmHdr {
1291 hdr: VirtIOSndHdr,
1293 stream_id: u32,
1295}
1296
1297#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1298enum PcmStreamFeatures {
1299 ShmemHost = 0,
1300 ShmemGuest,
1301 MsgPolling,
1302 EvtShmemPeriods,
1303 EvtXruns,
1304}
1305
1306impl From<PcmStreamFeatures> for u32 {
1307 fn from(value: PcmStreamFeatures) -> Self {
1308 value as _
1309 }
1310}
1311
1312#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1313#[repr(u64)]
1314enum PcmSampleFormat {
1315 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, }
1343
1344impl From<PcmSampleFormat> for u64 {
1345 fn from(value: PcmSampleFormat) -> Self {
1346 value as _
1347 }
1348}
1349
1350#[repr(C)]
1352#[derive(AsBytes, Clone, Eq, FromBytes, FromZeroes, PartialEq)]
1353pub struct VirtIOSndPcmInfo {
1354 hdr: VirtIOSndInfo,
1355 features: u32, formats: u64, rates: u64, direction: u8,
1360 channels_min: u8,
1362 channels_max: u8,
1364 _padding: [u8; 5],
1365}
1366
1367impl Debug for VirtIOSndPcmInfo {
1368 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1369 f.debug_struct("VirtIOSndPcmInfo")
1370 .field("hdr", &self.hdr)
1371 .field("features", &PcmFeatures::from_bits_retain(self.features))
1372 .field("formats", &PcmFormats::from_bits_retain(self.formats))
1373 .field("rates", &PcmRates::from_bits_retain(self.rates))
1374 .field("direction", &self.direction)
1375 .field("channels_min", &self.channels_min)
1376 .field("channels_max", &self.channels_max)
1377 .field("_padding", &self._padding)
1378 .finish()
1379 }
1380}
1381
1382impl Display for VirtIOSndPcmInfo {
1383 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1384 let direction = if self.direction == VIRTIO_SND_D_INPUT {
1385 "INPUT"
1386 } else {
1387 "OUTPUT"
1388 };
1389 write!(
1390 f,
1391 "features: {:?}, rates: {:?}, formats: {:?}, direction: {}",
1392 PcmFeatures::from_bits_retain(self.features),
1393 PcmRates::from_bits_retain(self.rates),
1394 PcmFormats::from_bits_retain(self.formats),
1395 direction
1396 )
1397 }
1398}
1399
1400#[derive(Clone, Debug, Default, Eq, PartialEq)]
1401struct PcmParameters {
1402 setup: bool,
1403 buffer_bytes: u32,
1404 period_bytes: u32,
1405 features: PcmFeatures,
1406 channels: u8,
1407 format: PcmFormat,
1408 rate: PcmRate,
1409}
1410
1411#[repr(C)]
1412#[derive(AsBytes, Debug, Eq, FromBytes, FromZeroes, PartialEq)]
1413struct VirtIOSndPcmSetParams {
1414 hdr: VirtIOSndPcmHdr, buffer_bytes: u32,
1416 period_bytes: u32,
1417 features: u32, channels: u8,
1419 format: u8,
1420 rate: u8,
1421
1422 _padding: u8,
1423}
1424
1425#[repr(C)]
1427#[derive(AsBytes, FromBytes, FromZeroes)]
1428struct VirtIOSndPcmXfer {
1429 stream_id: u32,
1430}
1431
1432#[repr(C)]
1434#[derive(AsBytes, Default, FromBytes, FromZeroes)]
1435struct VirtIOSndPcmStatus {
1436 status: u32,
1437 latency_bytes: u32,
1438}
1439
1440#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1441#[repr(u8)]
1442enum ChannelPosition {
1443 None = 0,
1445 Na,
1447 Mono,
1449 Fl,
1451 Fr,
1453 Rl,
1455 Rr,
1457 Fc,
1459 Lfe,
1461 Sl,
1463 Sr,
1465 Rc,
1467 Flc,
1469 Frc,
1471 Rlc,
1473 Rrc,
1475 Flw,
1477 Frw,
1479 Flh,
1481 Fch,
1483 Frh,
1485 Tc,
1487 Tfl,
1489 Tfr,
1491 Tfc,
1493 Trl,
1495 Trr,
1497 Trc,
1499 Tflc,
1501 Tfrc,
1503 Tsl,
1505 Tsr,
1507 Llfe,
1509 Rlfe,
1511 Bc,
1513 Blc,
1515 Brc,
1517}
1518
1519const VIRTIO_SND_CHMAP_MAX_SIZE: usize = 18;
1521
1522#[repr(C)]
1523#[derive(AsBytes, Clone, Debug, FromBytes, FromZeroes)]
1524struct VirtIOSndChmapInfo {
1525 hdr: VirtIOSndInfo,
1526 direction: u8,
1527 channels: u8,
1528 positions: [u8; VIRTIO_SND_CHMAP_MAX_SIZE],
1529}
1530
1531impl Display for VirtIOSndChmapInfo {
1532 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1533 let direction = if self.direction == VIRTIO_SND_D_INPUT {
1534 "INPUT"
1535 } else {
1536 "OUTPUT"
1537 };
1538 write!(
1539 f,
1540 "direction: {}, channels: {}, postions: [",
1541 direction, self.channels
1542 )?;
1543 for i in 0..usize::from(self.channels) {
1544 if i != 0 {
1545 write!(f, ", ")?;
1546 }
1547 if let Some(position) = ChannelPosition::n(self.positions[i]) {
1548 write!(f, "{:?}", position)?;
1549 } else {
1550 write!(f, "{}", self.positions[i])?;
1551 }
1552 }
1553 write!(f, "]")?;
1554 Ok(())
1555 }
1556}
1557
1558#[cfg(test)]
1559mod tests {
1560 use super::*;
1561 use crate::{
1562 hal::fake::FakeHal,
1563 transport::{
1564 fake::{FakeTransport, QueueStatus, State},
1565 DeviceType,
1566 },
1567 volatile::ReadOnly,
1568 };
1569 use alloc::{sync::Arc, vec};
1570 use core::ptr::NonNull;
1571 use fake::FakeSoundDevice;
1572 use std::sync::Mutex;
1573
1574 #[test]
1575 fn config() {
1576 let mut config_space = VirtIOSoundConfig {
1577 jacks: ReadOnly::new(3),
1578 streams: ReadOnly::new(4),
1579 chmaps: ReadOnly::new(2),
1580 };
1581 let state = Arc::new(Mutex::new(State {
1582 queues: vec![
1583 QueueStatus::default(),
1584 QueueStatus::default(),
1585 QueueStatus::default(),
1586 QueueStatus::default(),
1587 ],
1588 ..Default::default()
1589 }));
1590 let transport = FakeTransport {
1591 device_type: DeviceType::Sound,
1592 max_queue_size: 32,
1593 device_features: 0,
1594 config_space: NonNull::from(&mut config_space),
1595 state: state.clone(),
1596 };
1597 let sound =
1598 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1599 assert_eq!(sound.jacks(), 3);
1600 assert_eq!(sound.streams(), 4);
1601 assert_eq!(sound.chmaps(), 2);
1602 }
1603
1604 #[test]
1605 fn empty_info() {
1606 let (fake, transport) = FakeSoundDevice::new(vec![], vec![], vec![]);
1607 let mut sound =
1608 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1609 let handle = fake.spawn();
1610
1611 assert_eq!(sound.jacks(), 0);
1612 assert_eq!(sound.streams(), 0);
1613 assert_eq!(sound.chmaps(), 0);
1614 assert_eq!(sound.output_streams().unwrap(), vec![]);
1615 assert_eq!(sound.input_streams().unwrap(), vec![]);
1616
1617 fake.terminate();
1618 handle.join().unwrap();
1619 }
1620
1621 #[test]
1622 fn stream_info() {
1623 let (fake, transport) = FakeSoundDevice::new(
1624 vec![VirtIOSndJackInfo {
1625 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1626 features: 0,
1627 hda_reg_defconf: 0,
1628 hda_reg_caps: 0,
1629 connected: 0,
1630 _padding: Default::default(),
1631 }],
1632 vec![
1633 VirtIOSndPcmInfo {
1634 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1635 features: 0,
1636 formats: (PcmFormats::U8 | PcmFormats::U32).bits(),
1637 rates: (PcmRates::RATE_44100 | PcmRates::RATE_32000).bits(),
1638 direction: VIRTIO_SND_D_OUTPUT,
1639 channels_min: 1,
1640 channels_max: 2,
1641 _padding: Default::default(),
1642 },
1643 VirtIOSndPcmInfo {
1644 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1645 features: 0,
1646 formats: 0,
1647 rates: 0,
1648 direction: VIRTIO_SND_D_INPUT,
1649 channels_min: 0,
1650 channels_max: 0,
1651 _padding: Default::default(),
1652 },
1653 ],
1654 vec![VirtIOSndChmapInfo {
1655 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1656 direction: 0,
1657 channels: 0,
1658 positions: [0; 18],
1659 }],
1660 );
1661 let mut sound =
1662 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1663 let handle = fake.spawn();
1664
1665 assert_eq!(sound.output_streams().unwrap(), vec![0]);
1666 assert_eq!(
1667 sound.rates_supported(0).unwrap(),
1668 PcmRates::RATE_44100 | PcmRates::RATE_32000
1669 );
1670 assert_eq!(
1671 sound.formats_supported(0).unwrap(),
1672 PcmFormats::U8 | PcmFormats::U32
1673 );
1674 assert_eq!(sound.channel_range_supported(0).unwrap(), 1..=2);
1675 assert_eq!(sound.features_supported(0).unwrap(), PcmFeatures::empty());
1676
1677 assert_eq!(sound.input_streams().unwrap(), vec![1]);
1678 assert_eq!(sound.rates_supported(1).unwrap(), PcmRates::empty());
1679 assert_eq!(sound.formats_supported(1).unwrap(), PcmFormats::empty());
1680 assert_eq!(sound.channel_range_supported(1).unwrap(), 0..=0);
1681 assert_eq!(sound.features_supported(1).unwrap(), PcmFeatures::empty());
1682
1683 fake.terminate();
1684 handle.join().unwrap();
1685 }
1686
1687 #[test]
1688 fn play() {
1689 let (fake, transport) = FakeSoundDevice::new(
1690 vec![],
1691 vec![VirtIOSndPcmInfo {
1692 hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1693 features: 0,
1694 formats: (PcmFormats::U8 | PcmFormats::U32).bits(),
1695 rates: (PcmRates::RATE_44100 | PcmRates::RATE_32000).bits(),
1696 direction: VIRTIO_SND_D_OUTPUT,
1697 channels_min: 1,
1698 channels_max: 2,
1699 _padding: Default::default(),
1700 }],
1701 vec![],
1702 );
1703 let mut sound =
1704 VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1705 let handle = fake.spawn();
1706
1707 assert_eq!(sound.output_streams().unwrap(), vec![0]);
1708 assert_eq!(sound.input_streams().unwrap(), vec![]);
1709
1710 sound
1711 .pcm_set_params(
1712 0,
1713 100,
1714 100,
1715 PcmFeatures::empty(),
1716 1,
1717 PcmFormat::U8,
1718 PcmRate::Rate8000,
1719 )
1720 .unwrap();
1721 assert_eq!(
1722 fake.params.lock().unwrap()[0],
1723 Some(VirtIOSndPcmSetParams {
1724 hdr: VirtIOSndPcmHdr {
1725 hdr: VirtIOSndHdr {
1726 command_code: CommandCode::RPcmSetParams.into(),
1727 },
1728 stream_id: 0,
1729 },
1730 buffer_bytes: 100,
1731 period_bytes: 100,
1732 features: 0,
1733 channels: 1,
1734 format: PcmFormat::U8.into(),
1735 rate: PcmRate::Rate8000.into(),
1736 _padding: Default::default(),
1737 })
1738 );
1739
1740 sound.pcm_prepare(0).unwrap();
1741 sound.pcm_start(0).unwrap();
1742
1743 let mut expected_sound = vec![];
1744
1745 println!("Playing empty");
1747 sound.pcm_xfer(0, &[]).unwrap();
1748 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1749
1750 println!("Playing 100");
1752 sound.pcm_xfer(0, &[42; 100]).unwrap();
1753 expected_sound.extend([42; 100]);
1754 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1755
1756 println!("Playing 200");
1758 sound.pcm_xfer(0, &[66; 200]).unwrap();
1759 expected_sound.extend([66; 200]);
1760 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1761
1762 println!("Playing 50");
1764 sound.pcm_xfer(0, &[55; 50]).unwrap();
1765 expected_sound.extend([55; 50]);
1766 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1767
1768 println!("Playing 5000");
1770 sound.pcm_xfer(0, &[12; 5000]).unwrap();
1771 expected_sound.extend([12; 5000]);
1772 assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1773
1774 sound.pcm_stop(0).unwrap();
1775 sound.pcm_release(0).unwrap();
1776
1777 fake.terminate();
1778 handle.join().unwrap();
1779 }
1780}