Skip to main content

virtio_drivers/device/
sound.rs

1//! Driver for VirtIO Sound devices.
2
3#[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
26/// Audio driver based on virtio v1.2.
27///
28/// Supports synchronous blocking and asynchronous non-blocking audio playback.
29///
30/// Currently, only audio playback functionality has been implemented.
31pub 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 params
50    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>>, // includes pcm_xfer response msg
58
59    pcm_states: Vec<PCMState>,
60
61    token_buf: BTreeMap<u16, Vec<u8>>, // store token and its input buf
62}
63
64impl<H: Hal, T: Transport> VirtIOSound<H, T> {
65    /// Create a new VirtIO-Sound driver.
66    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        // read configuration space
99        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        // set pcm params to default
111        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    /// Total jack num.
146    pub fn jacks(&self) -> u32 {
147        self.jacks
148    }
149
150    /// Total stream num.
151    pub fn streams(&self) -> u32 {
152        self.streams
153    }
154
155    /// Total chmap num.
156    pub fn chmaps(&self) -> u32 {
157        self.chmaps
158    }
159
160    /// Acknowledge interrupt.
161    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    /// Set up the driver, initiate pcm_infos and jacks_infos
177    fn set_up(&mut self) -> Result<()> {
178        // init jack info
179        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        // init pcm info
190        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        // init chmap info
197        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        // set pcm state to default
208        for _ in 0..self.streams {
209            self.pcm_states.push(PCMState::default());
210        }
211        Ok(())
212    }
213
214    /// Enables interrupts from the device.
215    pub fn enable_interrupts(&mut self, enable: bool) {
216        self.event_queue.set_dev_notify(enable);
217    }
218
219    /// Query information about the available jacks.
220    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        // read struct VirtIOSndJackInfo
235        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    /// Query information about the available streams.
251    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        // read struct VirtIOSndPcmInfo
273        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    /// Query information about the available chmaps.
289    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    /// If the VIRTIO_SND_JACK_F_REMAP feature bit is set in the jack information, then the driver can send a
321    /// control request to change the association and/or sequence number for the specified jack ID.
322    /// # Arguments
323    ///
324    /// * `jack_id` - A u32 int which is in the range of [0, jacks)
325    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    /// Set selected stream parameters for the specified stream ID.
367    #[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        // rsp is just a header, so it can be compared with VirtIOSndHdr
403        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    /// Prepare a stream with specified stream ID.
420    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        // rsp is just a header, so it can be compared with VirtIOSndHdr
431        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
432            Ok(())
433        } else {
434            Err(Error::IoError)
435        }
436    }
437
438    /// Release a stream with specified stream ID.
439    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        // rsp is just a header, so it can be compared with VirtIOSndHdr
450        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
451            Ok(())
452        } else {
453            Err(Error::IoError)
454        }
455    }
456
457    /// Start a stream with specified stream ID.
458    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        // rsp is just a header, so it can be compared with VirtIOSndHdr
469        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
470            Ok(())
471        } else {
472            Err(Error::IoError)
473        }
474    }
475
476    /// Stop a stream with specified stream ID.
477    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        // rsp is just a header, so it can be compared with VirtIOSndHdr
488        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
489            Ok(())
490        } else {
491            Err(Error::IoError)
492        }
493    }
494
495    /// Transfer PCM frame to device, based on the stream type(OUTPUT/INPUT).
496    ///
497    /// Currently supports only output stream.
498    ///
499    /// This is a blocking method that will not return until the audio playback is complete.
500    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        // The next element of `statuses` and `tokens` to use for adding to the queue.
520        let mut head = 0;
521        // The next element of `statuses` and `tokens` to use for popping the queue.
522        let mut tail = 0;
523
524        loop {
525            // Add as buffers to the TX queue if possible. 3 descriptors are required for the 2
526            // input buffers and 1 output buffer.
527            if self.tx_queue.available_desc() >= 3 {
528                if let Some(buffer) = remaining_buffers.next() {
529                    // SAFETY: The buffers being added to the queue are non-empty and are not
530                    // accessed before the corresponding call to `pop_used`.
531                    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                // SAFETY: The same buffers passed to `add` are passed to `pop_used` by using
551                // `tail` to get the corresponding items from `tokens`, `buffers`, and
552                // `statuses`.
553                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    /// Transfer PCM frame to device, based on the stream type(OUTPUT/INPUT).
575    ///
576    /// Currently supports only output stream.
577    ///
578    /// This is a non-blocking method that returns a token.
579    ///
580    /// The length of the `frames` must be equal to the buffer size set for the stream corresponding to the `stream_id`.
581    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        // SAFETY: `buf` and `rsp` are owned allocations (a `Vec` and a `Box`, respectively)
599        // that are stored in `self` until the corresponding call to `pcm_xfer_ok`. The stored
600        // buffers are not used before the call to `pcm_xfer_ok`.
601        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    /// The PCM frame transmission corresponding to the given token has been completed.
612    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        // SAFETY: The buffers passed into `pop_used` are the same buffers from a previous call
617        // to `add` that returned `token`.
618        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    /// Get all output streams.
632    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    /// Get all input streams.
649    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    /// Get the rates that a stream supports.
666    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    /// Get the formats that a stream supports.
680    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    /// Get channel range that a stream supports.
694    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    /// Get features that a stream supports.
707    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    /// Get the latest notification.
720    pub fn latest_notification(&mut self) -> Result<Option<Notification>> {
721        // If the device has written notifications to the event_queue,
722        // then the oldest notification should be at the front of the queue.
723        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/// The status of the PCM stream.
738#[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        /// Jack remapping support.
763        const REMAP = 1 << 0;
764    }
765}
766
767bitflags! {
768    /// Supported PCM stream features.
769    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
770    #[repr(transparent)]
771    pub struct PcmFeatures: u32 {
772        /// Supports sharing a host memory with a guest.
773        const SHMEM_HOST = 1 << 0;
774        /// Supports sharing a guest memory with a host.
775        const SHMEM_GUEST = 1 << 1;
776        /// Supports polling mode for message-based transport.
777        const MSG_POLLING = 1 << 2;
778        /// Supports elapsed period notifications for shared memory transport.
779        const EVT_SHMEM_PERIODS = 1 << 3;
780        /// Supports underrun/overrun notifications.
781        const EVT_XRUNS = 1 << 4;
782    }
783}
784
785bitflags! {
786    /// Supported PCM sample formats.
787    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
788    #[repr(transparent)]
789    pub struct PcmFormats: u64 {
790        /// IMA ADPCM format.
791        const IMA_ADPCM = 1 << 0;
792        /// Mu-law format.
793        const MU_LAW = 1 << 1;
794        /// A-law format.
795        const A_LAW = 1 << 2;
796        /// Signed 8-bit format.
797        const S8 = 1 << 3;
798        /// Unsigned 8-bit format.
799        const U8 = 1 << 4;
800        /// Signed 16-bit format.
801        const S16 = 1 << 5;
802        /// Unsigned 16-bit format.
803        const U16 = 1 << 6;
804        /// Signed 18.3-bit format.
805        const S18_3 = 1 << 7;
806        /// Unsigned 18.3-bit format.
807        const U18_3 = 1 << 8;
808        /// Signed 20.3-bit format.
809        const S20_3 = 1 << 9;
810        /// Unsigned 20.3-bit format.
811        const U20_3 = 1 << 10;
812        /// Signed 24.3-bit format.
813        const S24_3 = 1 << 11;
814        /// Unsigned 24.3-bit format.
815        const U24_3 = 1 << 12;
816        /// Signed 20-bit format.
817        const S20 = 1 << 13;
818        /// Unsigned 20-bit format.
819        const U20 = 1 << 14;
820        /// Signed 24-bit format.
821        const S24 = 1 << 15;
822        /// Unsigned 24-bit format.
823        const U24 = 1 << 16;
824        /// Signed 32-bit format.
825        const S32 = 1 << 17;
826        /// Unsigned 32-bit format.
827        const U32 = 1 << 18;
828        /// 32-bit floating-point format.
829        const FLOAT = 1 << 19;
830        /// 64-bit floating-point format.
831        const FLOAT64 = 1 << 20;
832        /// DSD unsigned 8-bit format.
833        const DSD_U8 = 1 << 21;
834        /// DSD unsigned 16-bit format.
835        const DSD_U16 = 1 << 22;
836        /// DSD unsigned 32-bit format.
837        const DSD_U32 = 1 << 23;
838        /// IEC958 subframe format.
839        const IEC958_SUBFRAME = 1 << 24;
840    }
841}
842
843/// A single PCM sample format.
844#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
845#[repr(u8)]
846pub enum PcmFormat {
847    /// IMA ADPCM format.
848    #[default]
849    ImaAdpcm = 0,
850    /// Mu-law format.
851    MuLaw = 1,
852    /// A-law format.
853    ALaw = 2,
854    /// Signed 8-bit format.
855    S8 = 3,
856    /// Unsigned 8-bit format.
857    U8 = 4,
858    /// Signed 16-bit format.
859    S16 = 5,
860    /// Unsigned 16-bit format.
861    U16 = 6,
862    /// Signed 18.3-bit format.
863    S18_3 = 7,
864    /// Unsigned 18.3-bit format.
865    U18_3 = 8,
866    /// Signed 20.3-bit format.
867    S20_3 = 9,
868    /// Unsigned 20.3-bit format.
869    U20_3 = 10,
870    /// Signed 24.3-bit format.
871    S24_3 = 11,
872    /// Unsigned 24.3-bit format.
873    U24_3 = 12,
874    /// Signed 20-bit format.
875    S20 = 13,
876    /// Unsigned 20-bit format.
877    U20 = 14,
878    /// Signed 24-bit format.
879    S24 = 15,
880    /// Unsigned 24-bit format.
881    U24 = 16,
882    /// Signed 32-bit format.
883    S32 = 17,
884    /// Unsigned 32-bit format.
885    U32 = 18,
886    /// 32-bit floating-point format.
887    FLOAT = 19,
888    /// 64-bit floating-point format.
889    FLOAT64 = 20,
890    /// DSD unsigned 8-bit format.
891    DsdU8 = 21,
892    /// DSD unsigned 16-bit format.
893    DsdU16 = 22,
894    /// DSD unsigned 32-bit format.
895    DsdU32 = 23,
896    /// IEC958 subframe format.
897    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    /// Supported PCM frame rates.
940    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
941    #[repr(transparent)]
942    pub struct PcmRates: u64 {
943        /// 5512 Hz PCM rate.
944        const RATE_5512 = 1 << 0;
945        /// 8000 Hz PCM rate.
946        const RATE_8000 = 1 << 1;
947        /// 11025 Hz PCM rate.
948        const RATE_11025 = 1 << 2;
949        /// 16000 Hz PCM rate.
950        const RATE_16000 = 1 << 3;
951        /// 22050 Hz PCM rate.
952        const RATE_22050 = 1 << 4;
953        /// 32000 Hz PCM rate.
954        const RATE_32000 = 1 << 5;
955        /// 44100 Hz PCM rate.
956        const RATE_44100 = 1 << 6;
957        /// 48000 Hz PCM rate.
958        const RATE_48000 = 1 << 7;
959        /// 64000 Hz PCM rate.
960        const RATE_64000 = 1 << 8;
961        /// 88200 Hz PCM rate.
962        const RATE_88200 = 1 << 9;
963        /// 96000 Hz PCM rate.
964        const RATE_96000 = 1 << 10;
965        /// 176400 Hz PCM rate.
966        const RATE_176400 = 1 << 11;
967        /// 192000 Hz PCM rate.
968        const RATE_192000 = 1 << 12;
969        /// 384000 Hz PCM rate.
970        const RATE_384000 = 1 << 13;
971    }
972}
973
974/// A PCM frame rate.
975#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
976#[repr(u8)]
977pub enum PcmRate {
978    /// 5512 Hz PCM rate.
979    #[default]
980    Rate5512 = 0,
981    /// 8000 Hz PCM rate.
982    Rate8000 = 1,
983    /// 11025 Hz PCM rate.
984    Rate11025 = 2,
985    /// 16000 Hz PCM rate.
986    Rate16000 = 3,
987    /// 22050 Hz PCM rate.
988    Rate22050 = 4,
989    /// 32000 Hz PCM rate.
990    Rate32000 = 5,
991    /// 44100 Hz PCM rate.
992    Rate44100 = 6,
993    /// 48000 Hz PCM rate.
994    Rate48000 = 7,
995    /// 64000 Hz PCM rate.
996    Rate64000 = 8,
997    /// 88200 Hz PCM rate.
998    Rate88200 = 9,
999    /// 96000 Hz PCM rate.
1000    Rate96000 = 10,
1001    /// 176400 Hz PCM rate.
1002    Rate176400 = 11,
1003    /// 192000 Hz PCM rate.
1004    Rate192000 = 12,
1005    /// 384000 Hz PCM rate.
1006    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/// In virtIO v1.2, this enum should be called "Code".
1045///
1046/// To avoid ambiguity in its meaning, I use the term "CommandCode" here.
1047#[repr(u32)]
1048#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1049enum CommandCode {
1050    /* jack control request types */
1051    RJackInfo = 1,
1052    RJackRemap,
1053
1054    /* PCM control request types */
1055    RPcmInfo = 0x0100,
1056    RPcmSetParams,
1057    RPcmPrepare,
1058    RPcmRelease,
1059    RPcmStart,
1060    RPcmStop,
1061
1062    /* channel map control request types */
1063    RChmapInfo = 0x0200,
1064
1065    /* jack event types */
1066    EvtJackConnected = 0x1000,
1067    EvtJackDisconnected,
1068
1069    /* PCM event types */
1070    EvtPcmPeriodElapsed = 0x1100,
1071    EvtPcmXrun,
1072
1073    /* common status codes */
1074    /// success
1075    SOk = 0x8000,
1076    /// a control message is malformed or contains invalid parameters
1077    SBadMsg,
1078    /// requested operation or parameters are not supported
1079    SNotSupp,
1080    ///  an I/O error occurred
1081    SIoErr,
1082}
1083
1084impl From<CommandCode> for u32 {
1085    fn from(code: CommandCode) -> u32 {
1086        code as u32
1087    }
1088}
1089
1090/// Enum representing the types of item information requests.
1091#[repr(u32)]
1092#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1093pub enum ItemInformationRequestType {
1094    /// Represents a jack information request.
1095    RJackInfo = 1,
1096    /// Represents a PCM information request.
1097    RPcmInfo = 0x0100,
1098    /// Represents a channel map information request.
1099    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    /* common status codes */
1120    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/// A common header
1135#[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)]
1151/// An event notification
1152struct VirtIOSndEvent {
1153    hdr: VirtIOSndHdr,
1154    data: u32,
1155}
1156
1157/// The notification type.
1158#[repr(u32)]
1159#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1160pub enum NotificationType {
1161    /// An external device has been connected to the jack.
1162    JackConnected = 0x1000,
1163    /// An external device has been disconnected from the jack.
1164    JackDisconnected,
1165    /// A hardware buffer period has elapsed, the period size is controlled using the `period_bytes` field.
1166    PcmPeriodElapsed = 0x1100,
1167    /// An underflow for the output stream or an overflow for the inputstream has occurred.
1168    PcmXrun,
1169}
1170
1171impl NotificationType {
1172    /// Converts the given value to a variant of this enum, if any matches.
1173    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/// Notification from sound device.
1185#[derive(Clone, Debug, Eq, PartialEq)]
1186pub struct Notification {
1187    notification_type: NotificationType,
1188    data: u32,
1189}
1190
1191impl Notification {
1192    /// Get the resource index.
1193    pub fn data(&self) -> u32 {
1194        self.data
1195    }
1196
1197    /// Get the notification type.
1198    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    /// specifies a particular item request type (VIRTIO_SND_R_*_INFO)
1210    hdr: VirtIOSndHdr,
1211    /// specifies the starting identifier for the item
1212    /// (the range of available identifiers is limited
1213    ///  by the total number of particular items
1214    ///  that is indicated in the device configuration space)
1215    start_id: u32,
1216    /// specifies the number of items for which information is requested
1217    ///  (the total number of particular items is indicated
1218    ///  in the device configuration space).
1219    count: u32,
1220    /// specifies the size of the structure containing information for one item
1221    /// (used for backward compatibility)
1222    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/// Field `hda_fn_nid` indicates a function group node identifier.
1233#[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    /// specifies a jack identifier from 0 to jacks - 1
1244    jack_id: u32,
1245}
1246
1247/// Jack information.
1248#[repr(C)]
1249#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1250pub struct VirtIOSndJackInfo {
1251    hdr: VirtIOSndInfo,
1252    features: u32,
1253    /// indicates a pin default configuration value
1254    hda_reg_defconf: u32,
1255    /// indicates a pin capabilities value
1256    hda_reg_caps: u32,
1257    /// indicates the current jack connection status (1 - connected, 0 - disconnected)
1258    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    /// specifies request type (VIRTIO_SND_R_PCM_*)
1313    hdr: VirtIOSndHdr,
1314    /// specifies a PCM stream identifier from 0 to streams - 1
1315    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    /* analog formats (width / physical width) */
1337    ImaAdpcm = 0, /* 4 / 4 bits */
1338    MuLaw,        /* 8 / 8 bits */
1339    ALaw,         /* 8 / 8 bits */
1340    S8,           /* 8 / 8 bits */
1341    U8,           /* 8 / 8 bits */
1342    S16,          /* 16 / 16 bits */
1343    U16,          /* 16 / 16 bits */
1344    S18_3,        /* 18 / 24 bits */
1345    U18_3,        /* 18 / 24 bits */
1346    S20_3,        /* 20 / 24 bits */
1347    U20_3,        /* 20 / 24 bits */
1348    S24_3,        /* 24 / 24 bits */
1349    U24_3,        /* 24 / 24 bits */
1350    S20,          /* 20 / 32 bits */
1351    U20,          /* 20 / 32 bits */
1352    S24,          /* 24 / 32 bits */
1353    U24,          /* 24 / 32 bits */
1354    S32,          /* 32 / 32 bits */
1355    U32,          /* 32 / 32 bits */
1356    Float,        /* 32 / 32 bits */
1357    Float64,      /* 64 / 64 bits */
1358    /* digital formats (width / physical width) */
1359    DsdU8,          /* 8 / 8 bits */
1360    DsdU16,         /* 16 / 16 bits */
1361    DsdU32,         /* 32 / 32 bits */
1362    Iec958Subframe, /* 32 / 32 bits */
1363}
1364
1365impl From<PcmSampleFormat> for u64 {
1366    fn from(value: PcmSampleFormat) -> Self {
1367        value as _
1368    }
1369}
1370
1371/// PCM information.
1372#[repr(C)]
1373#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1374pub struct VirtIOSndPcmInfo {
1375    hdr: VirtIOSndInfo,
1376    features: u32, /* 1 << VIRTIO_SND_PCM_F_XXX */
1377    formats: u64,  /* 1 << VIRTIO_SND_PCM_FMT_XXX */
1378    rates: u64,    /* 1 << VIRTIO_SND_PCM_RATE_XXX */
1379    /// indicates the direction of data flow (VIRTIO_SND_D_*)
1380    direction: u8,
1381    /// indicates a minimum number of supported channels
1382    channels_min: u8,
1383    /// indicates a maximum number of supported channels
1384    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, /* .code = VIRTIO_SND_R_PCM_SET_PARAMS */
1436    buffer_bytes: u32,
1437    period_bytes: u32,
1438    features: u32, /* 1 << VIRTIO_SND_PCM_F_XXX */
1439    channels: u8,
1440    format: u8,
1441    rate: u8,
1442
1443    _padding: u8,
1444}
1445
1446/// An I/O header
1447#[repr(C)]
1448#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1449struct VirtIOSndPcmXfer {
1450    stream_id: u32,
1451}
1452
1453/// An I/O status
1454#[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    /// undefined
1465    None = 0,
1466    /// silent
1467    Na,
1468    /// mono stream
1469    Mono,
1470    /// front left
1471    Fl,
1472    /// front right
1473    Fr,
1474    /// rear left
1475    Rl,
1476    /// rear right
1477    Rr,
1478    /// front center
1479    Fc,
1480    /// low frequency (LFE)
1481    Lfe,
1482    /// side left
1483    Sl,
1484    /// side right
1485    Sr,
1486    /// rear center
1487    Rc,
1488    /// front left center
1489    Flc,
1490    /// front right center
1491    Frc,
1492    /// rear left center
1493    Rlc,
1494    /// rear right center
1495    Rrc,
1496    /// front left wide
1497    Flw,
1498    /// front right wide
1499    Frw,
1500    /// front left high
1501    Flh,
1502    /// front center high
1503    Fch,
1504    /// front right high
1505    Frh,
1506    /// top center
1507    Tc,
1508    /// top front left
1509    Tfl,
1510    /// top front right
1511    Tfr,
1512    /// top front center
1513    Tfc,
1514    /// top rear left
1515    Trl,
1516    /// top rear right
1517    Trr,
1518    /// top rear center
1519    Trc,
1520    /// top front left center
1521    Tflc,
1522    /// top front right center
1523    Tfrc,
1524    /// top side left
1525    Tsl,
1526    /// top side right
1527    Tsr,
1528    /// left LFE
1529    Llfe,
1530    /// right LFE
1531    Rlfe,
1532    /// bottom center
1533    Bc,
1534    /// bottom left center
1535    Blc,
1536    /// bottom right center
1537    Brc,
1538}
1539
1540/// maximum possible number of channels
1541const 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        // Playing an empty set of frames should be a no-op.
1765        println!("Playing empty");
1766        sound.pcm_xfer(0, &[]).unwrap();
1767        assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1768
1769        // Send one buffer worth.
1770        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        // Send two buffers worth.
1776        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        // Send half a buffer worth.
1782        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        // Send enough that the queue will fill up.
1788        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}