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    config::{read_config, ReadOnly},
9    queue::{owning::OwningQueue, VirtQueue},
10    transport::{InterruptStatus, Transport},
11    Error, Hal, Result, PAGE_SIZE,
12};
13use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
14use bitflags::bitflags;
15use core::{
16    array,
17    fmt::{self, Debug, Display, Formatter},
18    hint::spin_loop,
19    mem::size_of,
20    ops::RangeInclusive,
21};
22use enumn::N;
23use log::{error, info, warn};
24use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout};
25
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!("stream_start_id + stream_count > streams! There are not enough streams to be queried!");
258            return Err(Error::IoError);
259        }
260        let request_hdr = VirtIOSndHdr::from(ItemInformationRequestType::RPcmInfo);
261        let hdr = self.request(VirtIOSndQueryInfo {
262            hdr: request_hdr,
263            start_id: stream_start_id,
264            count: stream_count,
265            size: size_of::<VirtIOSndPcmInfo>() as u32,
266        })?;
267        if hdr != RequestStatusCode::Ok.into() {
268            return Err(Error::IoError);
269        }
270        // read struct VirtIOSndPcmInfo
271        let mut pcm_infos = vec![];
272        for i in 0..stream_count as usize {
273            const HDR_SIZE: usize = size_of::<VirtIOSndHdr>();
274            const PCM_INFO_SIZE: usize = size_of::<VirtIOSndPcmInfo>();
275            let start_byte_idx = HDR_SIZE + i * PCM_INFO_SIZE;
276            let end_byte_idx = HDR_SIZE + (i + 1) * PCM_INFO_SIZE;
277            let pcm_info = VirtIOSndPcmInfo::read_from_bytes(
278                &self.queue_buf_recv[start_byte_idx..end_byte_idx],
279            )
280            .unwrap();
281            pcm_infos.push(pcm_info);
282        }
283        Ok(pcm_infos)
284    }
285
286    /// Query information about the available chmaps.
287    fn chmap_info(
288        &mut self,
289        chmaps_start_id: u32,
290        chmaps_count: u32,
291    ) -> Result<Vec<VirtIOSndChmapInfo>> {
292        if chmaps_start_id + chmaps_count > self.chmaps {
293            error!("chmaps_start_id + chmaps_count > self.chmaps");
294            return Err(Error::IoError);
295        }
296        let hdr = self.request(VirtIOSndQueryInfo {
297            hdr: ItemInformationRequestType::RChmapInfo.into(),
298            start_id: chmaps_start_id,
299            count: chmaps_count,
300            size: size_of::<VirtIOSndChmapInfo>() as u32,
301        })?;
302        if hdr != RequestStatusCode::Ok.into() {
303            return Err(Error::IoError);
304        }
305        let mut chmap_infos = vec![];
306        for i in 0..chmaps_count as usize {
307            const OFFSET: usize = size_of::<VirtIOSndHdr>();
308            let start_byte = OFFSET + i * size_of::<VirtIOSndChmapInfo>();
309            let end_byte = OFFSET + (i + 1) * size_of::<VirtIOSndChmapInfo>();
310            let chmap_info =
311                VirtIOSndChmapInfo::read_from_bytes(&self.queue_buf_recv[start_byte..end_byte])
312                    .unwrap();
313            chmap_infos.push(chmap_info);
314        }
315        Ok(chmap_infos)
316    }
317
318    /// If the VIRTIO_SND_JACK_F_REMAP feature bit is set in the jack information, then the driver can send a
319    /// control request to change the association and/or sequence number for the specified jack ID.
320    /// # Arguments
321    ///
322    /// * `jack_id` - A u32 int which is in the range of [0, jacks)
323    pub fn jack_remap(&mut self, jack_id: u32, association: u32, sequence: u32) -> Result {
324        if !self.set_up {
325            self.set_up()?;
326            self.set_up = true;
327        }
328        if self.jacks == 0 {
329            error!("[sound device] There is no available jacks!");
330            return Err(Error::InvalidParam);
331        }
332        if jack_id >= self.jacks {
333            error!("jack_id >= self.jacks! Make sure jack_id is in the range of [0, jacks - 1)!");
334            return Err(Error::InvalidParam);
335        }
336
337        let jack_features = JackFeatures::from_bits_retain(
338            self.jack_infos
339                .as_ref()
340                .unwrap()
341                .get(jack_id as usize)
342                .unwrap()
343                .features,
344        );
345        if !jack_features.contains(JackFeatures::REMAP) {
346            error!("The jack selected does not support VIRTIO_SND_JACK_F_REMAP!");
347            return Err(Error::Unsupported);
348        }
349        let hdr = self.request(VirtIOSndJackRemap {
350            hdr: VirtIOSndJackHdr {
351                hdr: CommandCode::RJackRemap.into(),
352                jack_id,
353            },
354            association,
355            sequence,
356        })?;
357        if hdr == RequestStatusCode::Ok.into() {
358            Ok(())
359        } else {
360            Err(Error::Unsupported)
361        }
362    }
363
364    /// Set selected stream parameters for the specified stream ID.
365    pub fn pcm_set_params(
366        &mut self,
367        stream_id: u32,
368        buffer_bytes: u32,
369        period_bytes: u32,
370        features: PcmFeatures,
371        channels: u8,
372        format: PcmFormat,
373        rate: PcmRate,
374    ) -> Result {
375        if !self.set_up {
376            self.set_up()?;
377            self.set_up = true;
378        }
379        if period_bytes == 0 || period_bytes > buffer_bytes || buffer_bytes % period_bytes != 0 {
380            return Err(Error::InvalidParam);
381        }
382        let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmSetParams);
383        let rsp = self.request(VirtIOSndPcmSetParams {
384            hdr: VirtIOSndPcmHdr {
385                hdr: request_hdr,
386                stream_id,
387            },
388            buffer_bytes,
389            period_bytes,
390            features: features.bits(),
391            channels,
392            format: format.into(),
393            rate: rate.into(),
394            _padding: 0,
395        })?;
396        // rsp is just a header, so it can be compared with VirtIOSndHdr
397        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
398            self.pcm_parameters[stream_id as usize] = PcmParameters {
399                setup: true,
400                buffer_bytes,
401                period_bytes,
402                features,
403                channels,
404                format,
405                rate,
406            };
407            Ok(())
408        } else {
409            Err(Error::IoError)
410        }
411    }
412
413    /// Prepare a stream with specified stream ID.
414    pub fn pcm_prepare(&mut self, stream_id: u32) -> Result {
415        if !self.set_up {
416            self.set_up()?;
417            self.set_up = true;
418        }
419        let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmPrepare);
420        let rsp = self.request(VirtIOSndPcmHdr {
421            hdr: request_hdr,
422            stream_id,
423        })?;
424        // rsp is just a header, so it can be compared with VirtIOSndHdr
425        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
426            Ok(())
427        } else {
428            Err(Error::IoError)
429        }
430    }
431
432    /// Release a stream with specified stream ID.
433    pub fn pcm_release(&mut self, stream_id: u32) -> Result {
434        if !self.set_up {
435            self.set_up()?;
436            self.set_up = true;
437        }
438        let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmRelease);
439        let rsp = self.request(VirtIOSndPcmHdr {
440            hdr: request_hdr,
441            stream_id,
442        })?;
443        // rsp is just a header, so it can be compared with VirtIOSndHdr
444        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
445            Ok(())
446        } else {
447            Err(Error::IoError)
448        }
449    }
450
451    /// Start a stream with specified stream ID.
452    pub fn pcm_start(&mut self, stream_id: u32) -> Result {
453        if !self.set_up {
454            self.set_up()?;
455            self.set_up = true;
456        }
457        let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmStart);
458        let rsp = self.request(VirtIOSndPcmHdr {
459            hdr: request_hdr,
460            stream_id,
461        })?;
462        // rsp is just a header, so it can be compared with VirtIOSndHdr
463        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
464            Ok(())
465        } else {
466            Err(Error::IoError)
467        }
468    }
469
470    /// Stop a stream with specified stream ID.
471    pub fn pcm_stop(&mut self, stream_id: u32) -> Result {
472        if !self.set_up {
473            self.set_up()?;
474            self.set_up = true;
475        }
476        let request_hdr = VirtIOSndHdr::from(CommandCode::RPcmStop);
477        let rsp = self.request(VirtIOSndPcmHdr {
478            hdr: request_hdr,
479            stream_id,
480        })?;
481        // rsp is just a header, so it can be compared with VirtIOSndHdr
482        if rsp == VirtIOSndHdr::from(RequestStatusCode::Ok) {
483            Ok(())
484        } else {
485            Err(Error::IoError)
486        }
487    }
488
489    /// Transfer PCM frame to device, based on the stream type(OUTPUT/INPUT).
490    ///
491    /// Currently supports only output stream.
492    ///
493    /// This is a blocking method that will not return until the audio playback is complete.
494    pub fn pcm_xfer(&mut self, stream_id: u32, frames: &[u8]) -> Result {
495        const U32_SIZE: usize = size_of::<u32>();
496        if !self.set_up {
497            self.set_up()?;
498            self.set_up = true;
499        }
500        if !self.pcm_parameters[stream_id as usize].setup {
501            warn!("Please set parameters for a stream before using it!");
502            return Err(Error::IoError);
503        }
504
505        let stream_id_bytes = stream_id.to_le_bytes();
506        let period_size = self.pcm_parameters[stream_id as usize].period_bytes as usize;
507
508        let mut remaining_buffers = frames.chunks(period_size);
509        let mut buffers: [Option<&[u8]>; QUEUE_SIZE as usize] = [None; QUEUE_SIZE as usize];
510        let mut statuses: [VirtIOSndPcmStatus; QUEUE_SIZE as usize] =
511            array::from_fn(|_| Default::default());
512        let mut tokens = [0; QUEUE_SIZE as usize];
513        // The next element of `statuses` and `tokens` to use for adding to the queue.
514        let mut head = 0;
515        // The next element of `statuses` and `tokens` to use for popping the queue.
516        let mut tail = 0;
517
518        loop {
519            // Add as buffers to the TX queue if possible. 3 descriptors are required for the 2
520            // input buffers and 1 output buffer.
521            if self.tx_queue.available_desc() >= 3 {
522                if let Some(buffer) = remaining_buffers.next() {
523                    // SAFETY: The buffers being added to the queue are non-empty and are not
524                    // accessed before the corresponding call to `pop_used`.
525                    tokens[head] = unsafe {
526                        self.tx_queue.add(
527                            &[&stream_id_bytes, buffer],
528                            &mut [statuses[head].as_mut_bytes()],
529                        )?
530                    };
531                    if self.tx_queue.should_notify() {
532                        self.transport.notify(TX_QUEUE_IDX);
533                    }
534                    buffers[head] = Some(buffer);
535                    head += 1;
536                    if head >= usize::from(QUEUE_SIZE) {
537                        head = 0;
538                    }
539                } else if head == tail {
540                    break;
541                }
542            }
543            if self.tx_queue.can_pop() {
544                // SAFETY: The same buffers passed to `add` are passed to `pop_used` by using
545                // `tail` to get the corresponding items from `tokens`, `buffers`, and
546                // `statuses`.
547                unsafe {
548                    self.tx_queue.pop_used(
549                        tokens[tail],
550                        &[&stream_id_bytes, buffers[tail].unwrap()],
551                        &mut [statuses[tail].as_mut_bytes()],
552                    )?;
553                }
554                if statuses[tail].status != CommandCode::SOk.into() {
555                    return Err(Error::IoError);
556                }
557                tail += 1;
558                if tail >= usize::from(QUEUE_SIZE) {
559                    tail = 0;
560                }
561            }
562            spin_loop();
563        }
564
565        Ok(())
566    }
567
568    /// Transfer PCM frame to device, based on the stream type(OUTPUT/INPUT).
569    ///
570    /// Currently supports only output stream.
571    ///
572    /// This is a non-blocking method that returns a token.
573    ///
574    /// The length of the `frames` must be equal to the buffer size set for the stream corresponding to the `stream_id`.
575    pub fn pcm_xfer_nb(&mut self, stream_id: u32, frames: &[u8]) -> Result<u16> {
576        if !self.set_up {
577            self.set_up()?;
578            self.set_up = true;
579        }
580        if !self.pcm_parameters[stream_id as usize].setup {
581            warn!("Please set parameters for a stream before using it!");
582            return Err(Error::IoError);
583        }
584        const U32_SIZE: usize = size_of::<u32>();
585        let period_size: usize = self.pcm_parameters[stream_id as usize].period_bytes as usize;
586        assert_eq!(period_size, frames.len());
587        let mut buf = vec![0; U32_SIZE + period_size];
588        buf[..U32_SIZE].copy_from_slice(&stream_id.to_le_bytes());
589        buf[U32_SIZE..U32_SIZE + period_size].copy_from_slice(frames);
590        let mut rsp = VirtIOSndPcmStatus::new_box_zeroed().unwrap();
591
592        // SAFETY: `buf` and `rsp` are owned allocations (a `Vec` and a `Box`, respectively)
593        // that are stored in `self` until the corresponding call to `pcm_xfer_ok`. The stored
594        // buffers are not used before the call to `pcm_xfer_ok`.
595        let token = unsafe { self.tx_queue.add(&[&buf], &mut [rsp.as_mut_bytes()])? };
596
597        if self.tx_queue.should_notify() {
598            self.transport.notify(TX_QUEUE_IDX);
599        }
600        self.token_buf.insert(token, buf);
601        self.token_rsp.insert(token, rsp);
602        Ok(token)
603    }
604
605    /// The PCM frame transmission corresponding to the given token has been completed.
606    pub fn pcm_xfer_ok(&mut self, token: u16) -> Result {
607        assert!(self.token_buf.contains_key(&token));
608        assert!(self.token_rsp.contains_key(&token));
609
610        // SAFETY: The buffers passed into `pop_used` are the same buffers from a previous call
611        // to `add` that returned `token`.
612        unsafe {
613            self.tx_queue.pop_used(
614                token,
615                &[&self.token_buf[&token]],
616                &mut [self.token_rsp.get_mut(&token).unwrap().as_mut_bytes()],
617            )?;
618        }
619
620        self.token_buf.remove(&token);
621        self.token_rsp.remove(&token);
622        Ok(())
623    }
624
625    /// Get all output streams.
626    pub fn output_streams(&mut self) -> Result<Vec<u32>> {
627        if !self.set_up {
628            self.set_up()?;
629            self.set_up = true;
630        }
631        Ok(self
632            .pcm_infos
633            .as_ref()
634            .unwrap()
635            .iter()
636            .enumerate()
637            .filter(|(_, info)| info.direction == VIRTIO_SND_D_OUTPUT)
638            .map(|(idx, _)| idx as u32)
639            .collect())
640    }
641
642    /// Get all input streams.
643    pub fn input_streams(&mut self) -> Result<Vec<u32>> {
644        if !self.set_up {
645            self.set_up()?;
646            self.set_up = true;
647        }
648        Ok(self
649            .pcm_infos
650            .as_ref()
651            .unwrap()
652            .iter()
653            .enumerate()
654            .filter(|(_, info)| info.direction == VIRTIO_SND_D_INPUT)
655            .map(|(idx, _)| idx as u32)
656            .collect())
657    }
658
659    /// Get the rates that a stream supports.
660    pub fn rates_supported(&mut self, stream_id: u32) -> Result<PcmRates> {
661        if !self.set_up {
662            self.set_up()?;
663            self.set_up = true;
664        }
665        if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
666            return Err(Error::InvalidParam);
667        }
668        Ok(PcmRates::from_bits_retain(
669            self.pcm_infos.as_ref().unwrap()[stream_id as usize].rates,
670        ))
671    }
672
673    /// Get the formats that a stream supports.
674    pub fn formats_supported(&mut self, stream_id: u32) -> Result<PcmFormats> {
675        if !self.set_up {
676            self.set_up()?;
677            self.set_up = true;
678        }
679        if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
680            return Err(Error::InvalidParam);
681        }
682        Ok(PcmFormats::from_bits_retain(
683            self.pcm_infos.as_ref().unwrap()[stream_id as usize].formats,
684        ))
685    }
686
687    /// Get channel range that a stream supports.
688    pub fn channel_range_supported(&mut self, stream_id: u32) -> Result<RangeInclusive<u8>> {
689        if !self.set_up {
690            self.set_up()?;
691            self.set_up = true;
692        }
693        if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
694            return Err(Error::InvalidParam);
695        }
696        let pcm_info = &self.pcm_infos.as_ref().unwrap()[stream_id as usize];
697        Ok(pcm_info.channels_min..=pcm_info.channels_max)
698    }
699
700    /// Get features that a stream supports.
701    pub fn features_supported(&mut self, stream_id: u32) -> Result<PcmFeatures> {
702        if !self.set_up {
703            self.set_up()?;
704            self.set_up = true;
705        }
706        if stream_id >= self.pcm_infos.as_ref().unwrap().len() as u32 {
707            return Err(Error::InvalidParam);
708        }
709        let pcm_info = &self.pcm_infos.as_ref().unwrap()[stream_id as usize];
710        Ok(PcmFeatures::from_bits_retain(pcm_info.features))
711    }
712
713    /// Get the latest notification.
714    pub fn latest_notification(&mut self) -> Result<Option<Notification>> {
715        // If the device has written notifications to the event_queue,
716        // then the oldest notification should be at the front of the queue.
717        self.event_queue.poll(&mut self.transport, |buffer| {
718            if let Ok(event) = VirtIOSndEvent::read_from_bytes(buffer) {
719                Ok(Some(Notification {
720                    notification_type: NotificationType::n(event.hdr.command_code)
721                        .ok_or(Error::IoError)?,
722                    data: event.data,
723                }))
724            } else {
725                Ok(None)
726            }
727        })
728    }
729}
730
731/// The status of the PCM stream.
732#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
733enum PCMState {
734    #[default]
735    SetParams,
736    Prepare,
737    Release,
738    Start,
739    Stop,
740}
741
742const QUEUE_SIZE: u16 = 32;
743const CONTROL_QUEUE_IDX: u16 = 0;
744const EVENT_QUEUE_IDX: u16 = 1;
745const TX_QUEUE_IDX: u16 = 2;
746const RX_QUEUE_IDX: u16 = 3;
747
748const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC
749    .union(Feature::RING_EVENT_IDX)
750    .union(Feature::VERSION_1);
751
752bitflags! {
753    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
754    #[repr(transparent)]
755    struct JackFeatures: u32 {
756        /// Jack remapping support.
757        const REMAP = 1 << 0;
758    }
759}
760
761bitflags! {
762    /// Supported PCM stream features.
763    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
764    #[repr(transparent)]
765    pub struct PcmFeatures: u32 {
766        /// Supports sharing a host memory with a guest.
767        const SHMEM_HOST = 1 << 0;
768        /// Supports sharing a guest memory with a host.
769        const SHMEM_GUEST = 1 << 1;
770        /// Supports polling mode for message-based transport.
771        const MSG_POLLING = 1 << 2;
772        /// Supports elapsed period notifications for shared memory transport.
773        const EVT_SHMEM_PERIODS = 1 << 3;
774        /// Supports underrun/overrun notifications.
775        const EVT_XRUNS = 1 << 4;
776    }
777}
778
779bitflags! {
780    /// Supported PCM sample formats.
781    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
782    #[repr(transparent)]
783    pub struct PcmFormats: u64 {
784        /// IMA ADPCM format.
785        const IMA_ADPCM = 1 << 0;
786        /// Mu-law format.
787        const MU_LAW = 1 << 1;
788        /// A-law format.
789        const A_LAW = 1 << 2;
790        /// Signed 8-bit format.
791        const S8 = 1 << 3;
792        /// Unsigned 8-bit format.
793        const U8 = 1 << 4;
794        /// Signed 16-bit format.
795        const S16 = 1 << 5;
796        /// Unsigned 16-bit format.
797        const U16 = 1 << 6;
798        /// Signed 18.3-bit format.
799        const S18_3 = 1 << 7;
800        /// Unsigned 18.3-bit format.
801        const U18_3 = 1 << 8;
802        /// Signed 20.3-bit format.
803        const S20_3 = 1 << 9;
804        /// Unsigned 20.3-bit format.
805        const U20_3 = 1 << 10;
806        /// Signed 24.3-bit format.
807        const S24_3 = 1 << 11;
808        /// Unsigned 24.3-bit format.
809        const U24_3 = 1 << 12;
810        /// Signed 20-bit format.
811        const S20 = 1 << 13;
812        /// Unsigned 20-bit format.
813        const U20 = 1 << 14;
814        /// Signed 24-bit format.
815        const S24 = 1 << 15;
816        /// Unsigned 24-bit format.
817        const U24 = 1 << 16;
818        /// Signed 32-bit format.
819        const S32 = 1 << 17;
820        /// Unsigned 32-bit format.
821        const U32 = 1 << 18;
822        /// 32-bit floating-point format.
823        const FLOAT = 1 << 19;
824        /// 64-bit floating-point format.
825        const FLOAT64 = 1 << 20;
826        /// DSD unsigned 8-bit format.
827        const DSD_U8 = 1 << 21;
828        /// DSD unsigned 16-bit format.
829        const DSD_U16 = 1 << 22;
830        /// DSD unsigned 32-bit format.
831        const DSD_U32 = 1 << 23;
832        /// IEC958 subframe format.
833        const IEC958_SUBFRAME = 1 << 24;
834    }
835}
836
837/// A single PCM sample format.
838#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
839#[repr(u8)]
840pub enum PcmFormat {
841    /// IMA ADPCM format.
842    #[default]
843    ImaAdpcm = 0,
844    /// Mu-law format.
845    MuLaw = 1,
846    /// A-law format.
847    ALaw = 2,
848    /// Signed 8-bit format.
849    S8 = 3,
850    /// Unsigned 8-bit format.
851    U8 = 4,
852    /// Signed 16-bit format.
853    S16 = 5,
854    /// Unsigned 16-bit format.
855    U16 = 6,
856    /// Signed 18.3-bit format.
857    S18_3 = 7,
858    /// Unsigned 18.3-bit format.
859    U18_3 = 8,
860    /// Signed 20.3-bit format.
861    S20_3 = 9,
862    /// Unsigned 20.3-bit format.
863    U20_3 = 10,
864    /// Signed 24.3-bit format.
865    S24_3 = 11,
866    /// Unsigned 24.3-bit format.
867    U24_3 = 12,
868    /// Signed 20-bit format.
869    S20 = 13,
870    /// Unsigned 20-bit format.
871    U20 = 14,
872    /// Signed 24-bit format.
873    S24 = 15,
874    /// Unsigned 24-bit format.
875    U24 = 16,
876    /// Signed 32-bit format.
877    S32 = 17,
878    /// Unsigned 32-bit format.
879    U32 = 18,
880    /// 32-bit floating-point format.
881    FLOAT = 19,
882    /// 64-bit floating-point format.
883    FLOAT64 = 20,
884    /// DSD unsigned 8-bit format.
885    DsdU8 = 21,
886    /// DSD unsigned 16-bit format.
887    DsdU16 = 22,
888    /// DSD unsigned 32-bit format.
889    DsdU32 = 23,
890    /// IEC958 subframe format.
891    Iec958Subframe = 24,
892}
893
894impl From<PcmFormat> for PcmFormats {
895    fn from(format: PcmFormat) -> Self {
896        match format {
897            PcmFormat::ImaAdpcm => PcmFormats::IMA_ADPCM,
898            PcmFormat::MuLaw => PcmFormats::MU_LAW,
899            PcmFormat::ALaw => PcmFormats::A_LAW,
900            PcmFormat::S8 => PcmFormats::S8,
901            PcmFormat::U8 => PcmFormats::U8,
902            PcmFormat::S16 => PcmFormats::S16,
903            PcmFormat::U16 => PcmFormats::U16,
904            PcmFormat::S18_3 => PcmFormats::S18_3,
905            PcmFormat::U18_3 => PcmFormats::U18_3,
906            PcmFormat::S20_3 => PcmFormats::S20_3,
907            PcmFormat::U20_3 => PcmFormats::U20_3,
908            PcmFormat::S24_3 => PcmFormats::S24_3,
909            PcmFormat::U24_3 => PcmFormats::U24_3,
910            PcmFormat::S20 => PcmFormats::S20,
911            PcmFormat::U20 => PcmFormats::U20,
912            PcmFormat::S24 => PcmFormats::S24,
913            PcmFormat::U24 => PcmFormats::U24,
914            PcmFormat::S32 => PcmFormats::S32,
915            PcmFormat::U32 => PcmFormats::U32,
916            PcmFormat::FLOAT => PcmFormats::FLOAT,
917            PcmFormat::FLOAT64 => PcmFormats::FLOAT64,
918            PcmFormat::DsdU8 => PcmFormats::DSD_U8,
919            PcmFormat::DsdU16 => PcmFormats::DSD_U16,
920            PcmFormat::DsdU32 => PcmFormats::DSD_U32,
921            PcmFormat::Iec958Subframe => PcmFormats::IEC958_SUBFRAME,
922        }
923    }
924}
925
926impl From<PcmFormat> for u8 {
927    fn from(format: PcmFormat) -> u8 {
928        format as _
929    }
930}
931
932bitflags! {
933    /// Supported PCM frame rates.
934    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
935    #[repr(transparent)]
936    pub struct PcmRates: u64 {
937        /// 5512 Hz PCM rate.
938        const RATE_5512 = 1 << 0;
939        /// 8000 Hz PCM rate.
940        const RATE_8000 = 1 << 1;
941        /// 11025 Hz PCM rate.
942        const RATE_11025 = 1 << 2;
943        /// 16000 Hz PCM rate.
944        const RATE_16000 = 1 << 3;
945        /// 22050 Hz PCM rate.
946        const RATE_22050 = 1 << 4;
947        /// 32000 Hz PCM rate.
948        const RATE_32000 = 1 << 5;
949        /// 44100 Hz PCM rate.
950        const RATE_44100 = 1 << 6;
951        /// 48000 Hz PCM rate.
952        const RATE_48000 = 1 << 7;
953        /// 64000 Hz PCM rate.
954        const RATE_64000 = 1 << 8;
955        /// 88200 Hz PCM rate.
956        const RATE_88200 = 1 << 9;
957        /// 96000 Hz PCM rate.
958        const RATE_96000 = 1 << 10;
959        /// 176400 Hz PCM rate.
960        const RATE_176400 = 1 << 11;
961        /// 192000 Hz PCM rate.
962        const RATE_192000 = 1 << 12;
963        /// 384000 Hz PCM rate.
964        const RATE_384000 = 1 << 13;
965    }
966}
967
968/// A PCM frame rate.
969#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
970#[repr(u8)]
971pub enum PcmRate {
972    /// 5512 Hz PCM rate.
973    #[default]
974    Rate5512 = 0,
975    /// 8000 Hz PCM rate.
976    Rate8000 = 1,
977    /// 11025 Hz PCM rate.
978    Rate11025 = 2,
979    /// 16000 Hz PCM rate.
980    Rate16000 = 3,
981    /// 22050 Hz PCM rate.
982    Rate22050 = 4,
983    /// 32000 Hz PCM rate.
984    Rate32000 = 5,
985    /// 44100 Hz PCM rate.
986    Rate44100 = 6,
987    /// 48000 Hz PCM rate.
988    Rate48000 = 7,
989    /// 64000 Hz PCM rate.
990    Rate64000 = 8,
991    /// 88200 Hz PCM rate.
992    Rate88200 = 9,
993    /// 96000 Hz PCM rate.
994    Rate96000 = 10,
995    /// 176400 Hz PCM rate.
996    Rate176400 = 11,
997    /// 192000 Hz PCM rate.
998    Rate192000 = 12,
999    /// 384000 Hz PCM rate.
1000    Rate384000 = 13,
1001}
1002
1003impl From<PcmRate> for PcmRates {
1004    fn from(rate: PcmRate) -> Self {
1005        match rate {
1006            PcmRate::Rate5512 => Self::RATE_5512,
1007            PcmRate::Rate8000 => Self::RATE_8000,
1008            PcmRate::Rate11025 => Self::RATE_11025,
1009            PcmRate::Rate16000 => Self::RATE_16000,
1010            PcmRate::Rate22050 => Self::RATE_22050,
1011            PcmRate::Rate32000 => Self::RATE_32000,
1012            PcmRate::Rate44100 => Self::RATE_44100,
1013            PcmRate::Rate48000 => Self::RATE_48000,
1014            PcmRate::Rate64000 => Self::RATE_64000,
1015            PcmRate::Rate88200 => Self::RATE_88200,
1016            PcmRate::Rate96000 => Self::RATE_96000,
1017            PcmRate::Rate176400 => Self::RATE_176400,
1018            PcmRate::Rate192000 => Self::RATE_192000,
1019            PcmRate::Rate384000 => Self::RATE_384000,
1020        }
1021    }
1022}
1023
1024impl From<PcmRate> for u8 {
1025    fn from(rate: PcmRate) -> Self {
1026        rate as _
1027    }
1028}
1029
1030#[derive(FromBytes, Immutable, IntoBytes)]
1031#[repr(C)]
1032struct VirtIOSoundConfig {
1033    jacks: ReadOnly<u32>,
1034    streams: ReadOnly<u32>,
1035    chmaps: ReadOnly<u32>,
1036}
1037
1038/// In virtIO v1.2, this enum should be called "Code".
1039///
1040/// To avoid ambiguity in its meaning, I use the term "CommandCode" here.
1041#[repr(u32)]
1042#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1043enum CommandCode {
1044    /* jack control request types */
1045    RJackInfo = 1,
1046    RJackRemap,
1047
1048    /* PCM control request types */
1049    RPcmInfo = 0x0100,
1050    RPcmSetParams,
1051    RPcmPrepare,
1052    RPcmRelease,
1053    RPcmStart,
1054    RPcmStop,
1055
1056    /* channel map control request types */
1057    RChmapInfo = 0x0200,
1058
1059    /* jack event types */
1060    EvtJackConnected = 0x1000,
1061    EvtJackDisconnected,
1062
1063    /* PCM event types */
1064    EvtPcmPeriodElapsed = 0x1100,
1065    EvtPcmXrun,
1066
1067    /* common status codes */
1068    /// success
1069    SOk = 0x8000,
1070    /// a control message is malformed or contains invalid parameters
1071    SBadMsg,
1072    /// requested operation or parameters are not supported
1073    SNotSupp,
1074    ///  an I/O error occurred
1075    SIoErr,
1076}
1077
1078impl From<CommandCode> for u32 {
1079    fn from(code: CommandCode) -> u32 {
1080        code as u32
1081    }
1082}
1083
1084/// Enum representing the types of item information requests.
1085#[repr(u32)]
1086#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1087pub enum ItemInformationRequestType {
1088    /// Represents a jack information request.
1089    RJackInfo = 1,
1090    /// Represents a PCM information request.
1091    RPcmInfo = 0x0100,
1092    /// Represents a channel map information request.
1093    RChmapInfo = 0x0200,
1094}
1095
1096impl From<ItemInformationRequestType> for VirtIOSndHdr {
1097    fn from(value: ItemInformationRequestType) -> Self {
1098        VirtIOSndHdr {
1099            command_code: value.into(),
1100        }
1101    }
1102}
1103
1104impl From<ItemInformationRequestType> for u32 {
1105    fn from(request_type: ItemInformationRequestType) -> u32 {
1106        request_type as _
1107    }
1108}
1109
1110#[derive(Copy, Clone, Eq, PartialEq)]
1111#[repr(u32)]
1112enum RequestStatusCode {
1113    /* common status codes */
1114    Ok = 0x8000,
1115    BadMsg,
1116    NotSupp,
1117    IoErr,
1118}
1119
1120impl From<RequestStatusCode> for VirtIOSndHdr {
1121    fn from(value: RequestStatusCode) -> Self {
1122        VirtIOSndHdr {
1123            command_code: value as _,
1124        }
1125    }
1126}
1127
1128/// A common header
1129#[repr(C)]
1130#[derive(Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1131struct VirtIOSndHdr {
1132    command_code: u32,
1133}
1134
1135impl From<CommandCode> for VirtIOSndHdr {
1136    fn from(value: CommandCode) -> Self {
1137        VirtIOSndHdr {
1138            command_code: value.into(),
1139        }
1140    }
1141}
1142
1143#[repr(C)]
1144#[derive(FromBytes, Immutable, KnownLayout)]
1145/// An event notification
1146struct VirtIOSndEvent {
1147    hdr: VirtIOSndHdr,
1148    data: u32,
1149}
1150
1151/// The notification type.
1152#[repr(u32)]
1153#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1154pub enum NotificationType {
1155    /// An external device has been connected to the jack.
1156    JackConnected = 0x1000,
1157    /// An external device has been disconnected from the jack.
1158    JackDisconnected,
1159    /// A hardware buffer period has elapsed, the period size is controlled using the `period_bytes` field.
1160    PcmPeriodElapsed = 0x1100,
1161    /// An underflow for the output stream or an overflow for the inputstream has occurred.
1162    PcmXrun,
1163}
1164
1165impl NotificationType {
1166    /// Converts the given value to a variant of this enum, if any matches.
1167    fn n(value: u32) -> Option<Self> {
1168        match value {
1169            0x1100 => Some(Self::PcmPeriodElapsed),
1170            0x1101 => Some(Self::PcmXrun),
1171            0x1000 => Some(Self::JackConnected),
1172            0x1001 => Some(Self::JackDisconnected),
1173            _ => None,
1174        }
1175    }
1176}
1177
1178/// Notification from sound device.
1179#[derive(Clone, Debug, Eq, PartialEq)]
1180pub struct Notification {
1181    notification_type: NotificationType,
1182    data: u32,
1183}
1184
1185impl Notification {
1186    /// Get the resource index.
1187    pub fn data(&self) -> u32 {
1188        self.data
1189    }
1190
1191    /// Get the notification type.
1192    pub fn notification_type(&self) -> NotificationType {
1193        self.notification_type
1194    }
1195}
1196
1197const VIRTIO_SND_D_OUTPUT: u8 = 0;
1198const VIRTIO_SND_D_INPUT: u8 = 1;
1199
1200#[repr(C)]
1201#[derive(Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1202struct VirtIOSndQueryInfo {
1203    /// specifies a particular item request type (VIRTIO_SND_R_*_INFO)
1204    hdr: VirtIOSndHdr,
1205    /// specifies the starting identifier for the item
1206    /// (the range of available identifiers is limited
1207    ///  by the total number of particular items
1208    ///  that is indicated in the device configuration space)
1209    start_id: u32,
1210    /// specifies the number of items for which information is requested
1211    ///  (the total number of particular items is indicated
1212    ///  in the device configuration space).
1213    count: u32,
1214    /// specifies the size of the structure containing information for one item
1215    /// (used for backward compatibility)
1216    size: u32,
1217}
1218
1219#[repr(C)]
1220#[derive(Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1221struct VirtIOSndQueryInfoRsp {
1222    hdr: VirtIOSndHdr,
1223    info: VirtIOSndInfo,
1224}
1225
1226/// Field `hda_fn_nid` indicates a function group node identifier.
1227#[repr(C)]
1228#[derive(Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1229pub struct VirtIOSndInfo {
1230    hda_fn_nid: u32,
1231}
1232
1233#[repr(C)]
1234#[derive(Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1235struct VirtIOSndJackHdr {
1236    hdr: VirtIOSndHdr,
1237    /// specifies a jack identifier from 0 to jacks - 1
1238    jack_id: u32,
1239}
1240
1241/// Jack information.
1242#[repr(C)]
1243#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1244pub struct VirtIOSndJackInfo {
1245    hdr: VirtIOSndInfo,
1246    features: u32,
1247    /// indicates a pin default configuration value
1248    hda_reg_defconf: u32,
1249    /// indicates a pin capabilities value
1250    hda_reg_caps: u32,
1251    /// indicates the current jack connection status (1 - connected, 0 - disconnected)
1252    connected: u8,
1253
1254    _padding: [u8; 7],
1255}
1256
1257impl Debug for VirtIOSndJackInfo {
1258    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1259        f.debug_struct("VirtIOSndJackInfo")
1260            .field("hdr", &self.hdr)
1261            .field("features", &JackFeatures::from_bits_retain(self.features))
1262            .field("hda_reg_defconf", &self.hda_reg_defconf)
1263            .field("hda_reg_caps", &self.hda_reg_caps)
1264            .field("connected", &self.connected)
1265            .field("_padding", &self._padding)
1266            .finish()
1267    }
1268}
1269
1270impl Display for VirtIOSndJackInfo {
1271    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1272        let connected_status = if self.connected == 1 {
1273            "CONNECTED"
1274        } else {
1275            "DISCONNECTED"
1276        };
1277        write!(
1278            f,
1279            "features: {:?}, hda_reg_defconf: {}, hda_reg_caps: {}, connected: {}",
1280            JackFeatures::from_bits_retain(self.features),
1281            self.hda_reg_defconf,
1282            self.hda_reg_caps,
1283            connected_status
1284        )
1285    }
1286}
1287
1288#[repr(C)]
1289#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1290struct VirtIOSndJackInfoRsp {
1291    hdr: VirtIOSndHdr,
1292    body: VirtIOSndJackInfo,
1293}
1294
1295#[repr(C)]
1296#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1297struct VirtIOSndJackRemap {
1298    hdr: VirtIOSndJackHdr,
1299    association: u32,
1300    sequence: u32,
1301}
1302
1303#[repr(C)]
1304#[derive(Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1305struct VirtIOSndPcmHdr {
1306    /// specifies request type (VIRTIO_SND_R_PCM_*)
1307    hdr: VirtIOSndHdr,
1308    /// specifies a PCM stream identifier from 0 to streams - 1
1309    stream_id: u32,
1310}
1311
1312#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1313enum PcmStreamFeatures {
1314    ShmemHost = 0,
1315    ShmemGuest,
1316    MsgPolling,
1317    EvtShmemPeriods,
1318    EvtXruns,
1319}
1320
1321impl From<PcmStreamFeatures> for u32 {
1322    fn from(value: PcmStreamFeatures) -> Self {
1323        value as _
1324    }
1325}
1326
1327#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1328#[repr(u64)]
1329enum PcmSampleFormat {
1330    /* analog formats (width / physical width) */
1331    ImaAdpcm = 0, /* 4 / 4 bits */
1332    MuLaw,        /* 8 / 8 bits */
1333    ALaw,         /* 8 / 8 bits */
1334    S8,           /* 8 / 8 bits */
1335    U8,           /* 8 / 8 bits */
1336    S16,          /* 16 / 16 bits */
1337    U16,          /* 16 / 16 bits */
1338    S18_3,        /* 18 / 24 bits */
1339    U18_3,        /* 18 / 24 bits */
1340    S20_3,        /* 20 / 24 bits */
1341    U20_3,        /* 20 / 24 bits */
1342    S24_3,        /* 24 / 24 bits */
1343    U24_3,        /* 24 / 24 bits */
1344    S20,          /* 20 / 32 bits */
1345    U20,          /* 20 / 32 bits */
1346    S24,          /* 24 / 32 bits */
1347    U24,          /* 24 / 32 bits */
1348    S32,          /* 32 / 32 bits */
1349    U32,          /* 32 / 32 bits */
1350    Float,        /* 32 / 32 bits */
1351    Float64,      /* 64 / 64 bits */
1352    /* digital formats (width / physical width) */
1353    DsdU8,          /* 8 / 8 bits */
1354    DsdU16,         /* 16 / 16 bits */
1355    DsdU32,         /* 32 / 32 bits */
1356    Iec958Subframe, /* 32 / 32 bits */
1357}
1358
1359impl From<PcmSampleFormat> for u64 {
1360    fn from(value: PcmSampleFormat) -> Self {
1361        value as _
1362    }
1363}
1364
1365/// PCM information.
1366#[repr(C)]
1367#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1368pub struct VirtIOSndPcmInfo {
1369    hdr: VirtIOSndInfo,
1370    features: u32, /* 1 << VIRTIO_SND_PCM_F_XXX */
1371    formats: u64,  /* 1 << VIRTIO_SND_PCM_FMT_XXX */
1372    rates: u64,    /* 1 << VIRTIO_SND_PCM_RATE_XXX */
1373    /// indicates the direction of data flow (VIRTIO_SND_D_*)
1374    direction: u8,
1375    /// indicates a minimum number of supported channels
1376    channels_min: u8,
1377    /// indicates a maximum number of supported channels
1378    channels_max: u8,
1379    _padding: [u8; 5],
1380}
1381
1382impl Debug for VirtIOSndPcmInfo {
1383    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1384        f.debug_struct("VirtIOSndPcmInfo")
1385            .field("hdr", &self.hdr)
1386            .field("features", &PcmFeatures::from_bits_retain(self.features))
1387            .field("formats", &PcmFormats::from_bits_retain(self.formats))
1388            .field("rates", &PcmRates::from_bits_retain(self.rates))
1389            .field("direction", &self.direction)
1390            .field("channels_min", &self.channels_min)
1391            .field("channels_max", &self.channels_max)
1392            .field("_padding", &self._padding)
1393            .finish()
1394    }
1395}
1396
1397impl Display for VirtIOSndPcmInfo {
1398    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1399        let direction = if self.direction == VIRTIO_SND_D_INPUT {
1400            "INPUT"
1401        } else {
1402            "OUTPUT"
1403        };
1404        write!(
1405            f,
1406            "features: {:?}, rates: {:?}, formats: {:?}, direction: {}",
1407            PcmFeatures::from_bits_retain(self.features),
1408            PcmRates::from_bits_retain(self.rates),
1409            PcmFormats::from_bits_retain(self.formats),
1410            direction
1411        )
1412    }
1413}
1414
1415#[derive(Clone, Debug, Default, Eq, PartialEq)]
1416struct PcmParameters {
1417    setup: bool,
1418    buffer_bytes: u32,
1419    period_bytes: u32,
1420    features: PcmFeatures,
1421    channels: u8,
1422    format: PcmFormat,
1423    rate: PcmRate,
1424}
1425
1426#[repr(C)]
1427#[derive(Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
1428struct VirtIOSndPcmSetParams {
1429    hdr: VirtIOSndPcmHdr, /* .code = VIRTIO_SND_R_PCM_SET_PARAMS */
1430    buffer_bytes: u32,
1431    period_bytes: u32,
1432    features: u32, /* 1 << VIRTIO_SND_PCM_F_XXX */
1433    channels: u8,
1434    format: u8,
1435    rate: u8,
1436
1437    _padding: u8,
1438}
1439
1440/// An I/O header
1441#[repr(C)]
1442#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
1443struct VirtIOSndPcmXfer {
1444    stream_id: u32,
1445}
1446
1447/// An I/O status
1448#[repr(C)]
1449#[derive(Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
1450struct VirtIOSndPcmStatus {
1451    status: u32,
1452    latency_bytes: u32,
1453}
1454
1455#[derive(Copy, Clone, Debug, Eq, N, PartialEq)]
1456#[repr(u8)]
1457enum ChannelPosition {
1458    /// undefined
1459    None = 0,
1460    /// silent
1461    Na,
1462    /// mono stream
1463    Mono,
1464    /// front left
1465    Fl,
1466    /// front right
1467    Fr,
1468    /// rear left
1469    Rl,
1470    /// rear right
1471    Rr,
1472    /// front center
1473    Fc,
1474    /// low frequency (LFE)
1475    Lfe,
1476    /// side left
1477    Sl,
1478    /// side right
1479    Sr,
1480    /// rear center
1481    Rc,
1482    /// front left center
1483    Flc,
1484    /// front right center
1485    Frc,
1486    /// rear left center
1487    Rlc,
1488    /// rear right center
1489    Rrc,
1490    /// front left wide
1491    Flw,
1492    /// front right wide
1493    Frw,
1494    /// front left high
1495    Flh,
1496    /// front center high
1497    Fch,
1498    /// front right high
1499    Frh,
1500    /// top center
1501    Tc,
1502    /// top front left
1503    Tfl,
1504    /// top front right
1505    Tfr,
1506    /// top front center
1507    Tfc,
1508    /// top rear left
1509    Trl,
1510    /// top rear right
1511    Trr,
1512    /// top rear center
1513    Trc,
1514    /// top front left center
1515    Tflc,
1516    /// top front right center
1517    Tfrc,
1518    /// top side left
1519    Tsl,
1520    /// top side right
1521    Tsr,
1522    /// left LFE
1523    Llfe,
1524    /// right LFE
1525    Rlfe,
1526    /// bottom center
1527    Bc,
1528    /// bottom left center
1529    Blc,
1530    /// bottom right center
1531    Brc,
1532}
1533
1534/// maximum possible number of channels
1535const VIRTIO_SND_CHMAP_MAX_SIZE: usize = 18;
1536
1537#[repr(C)]
1538#[derive(Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
1539struct VirtIOSndChmapInfo {
1540    hdr: VirtIOSndInfo,
1541    direction: u8,
1542    channels: u8,
1543    positions: [u8; VIRTIO_SND_CHMAP_MAX_SIZE],
1544}
1545
1546impl Display for VirtIOSndChmapInfo {
1547    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1548        let direction = if self.direction == VIRTIO_SND_D_INPUT {
1549            "INPUT"
1550        } else {
1551            "OUTPUT"
1552        };
1553        write!(
1554            f,
1555            "direction: {}, channels: {}, positions: [",
1556            direction, self.channels
1557        )?;
1558        for i in 0..usize::from(self.channels) {
1559            if i != 0 {
1560                write!(f, ", ")?;
1561            }
1562            if let Some(position) = ChannelPosition::n(self.positions[i]) {
1563                write!(f, "{:?}", position)?;
1564            } else {
1565                write!(f, "{}", self.positions[i])?;
1566            }
1567        }
1568        write!(f, "]")?;
1569        Ok(())
1570    }
1571}
1572
1573#[cfg(test)]
1574mod tests {
1575    use super::*;
1576    use crate::{
1577        config::ReadOnly,
1578        hal::fake::FakeHal,
1579        transport::{
1580            fake::{FakeTransport, QueueStatus, State},
1581            DeviceType,
1582        },
1583    };
1584    use alloc::{sync::Arc, vec};
1585    use fake::FakeSoundDevice;
1586    use std::sync::Mutex;
1587
1588    #[test]
1589    fn config() {
1590        let config_space = VirtIOSoundConfig {
1591            jacks: ReadOnly::new(3),
1592            streams: ReadOnly::new(4),
1593            chmaps: ReadOnly::new(2),
1594        };
1595        let state = Arc::new(Mutex::new(State::new(
1596            vec![
1597                QueueStatus::default(),
1598                QueueStatus::default(),
1599                QueueStatus::default(),
1600                QueueStatus::default(),
1601            ],
1602            config_space,
1603        )));
1604        let transport = FakeTransport {
1605            device_type: DeviceType::Sound,
1606            max_queue_size: 32,
1607            device_features: 0,
1608            state: state.clone(),
1609        };
1610        let sound =
1611            VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1612        assert_eq!(sound.jacks(), 3);
1613        assert_eq!(sound.streams(), 4);
1614        assert_eq!(sound.chmaps(), 2);
1615    }
1616
1617    #[test]
1618    fn empty_info() {
1619        let (fake, transport) = FakeSoundDevice::new(vec![], vec![], vec![]);
1620        let mut sound =
1621            VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1622        let handle = fake.spawn();
1623
1624        assert_eq!(sound.jacks(), 0);
1625        assert_eq!(sound.streams(), 0);
1626        assert_eq!(sound.chmaps(), 0);
1627        assert_eq!(sound.output_streams().unwrap(), vec![]);
1628        assert_eq!(sound.input_streams().unwrap(), vec![]);
1629
1630        fake.terminate();
1631        handle.join().unwrap();
1632    }
1633
1634    #[test]
1635    fn stream_info() {
1636        let (fake, transport) = FakeSoundDevice::new(
1637            vec![VirtIOSndJackInfo {
1638                hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1639                features: 0,
1640                hda_reg_defconf: 0,
1641                hda_reg_caps: 0,
1642                connected: 0,
1643                _padding: Default::default(),
1644            }],
1645            vec![
1646                VirtIOSndPcmInfo {
1647                    hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1648                    features: 0,
1649                    formats: (PcmFormats::U8 | PcmFormats::U32).bits(),
1650                    rates: (PcmRates::RATE_44100 | PcmRates::RATE_32000).bits(),
1651                    direction: VIRTIO_SND_D_OUTPUT,
1652                    channels_min: 1,
1653                    channels_max: 2,
1654                    _padding: Default::default(),
1655                },
1656                VirtIOSndPcmInfo {
1657                    hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1658                    features: 0,
1659                    formats: 0,
1660                    rates: 0,
1661                    direction: VIRTIO_SND_D_INPUT,
1662                    channels_min: 0,
1663                    channels_max: 0,
1664                    _padding: Default::default(),
1665                },
1666            ],
1667            vec![VirtIOSndChmapInfo {
1668                hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1669                direction: 0,
1670                channels: 0,
1671                positions: [0; 18],
1672            }],
1673        );
1674        let mut sound =
1675            VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1676        let handle = fake.spawn();
1677
1678        assert_eq!(sound.output_streams().unwrap(), vec![0]);
1679        assert_eq!(
1680            sound.rates_supported(0).unwrap(),
1681            PcmRates::RATE_44100 | PcmRates::RATE_32000
1682        );
1683        assert_eq!(
1684            sound.formats_supported(0).unwrap(),
1685            PcmFormats::U8 | PcmFormats::U32
1686        );
1687        assert_eq!(sound.channel_range_supported(0).unwrap(), 1..=2);
1688        assert_eq!(sound.features_supported(0).unwrap(), PcmFeatures::empty());
1689
1690        assert_eq!(sound.input_streams().unwrap(), vec![1]);
1691        assert_eq!(sound.rates_supported(1).unwrap(), PcmRates::empty());
1692        assert_eq!(sound.formats_supported(1).unwrap(), PcmFormats::empty());
1693        assert_eq!(sound.channel_range_supported(1).unwrap(), 0..=0);
1694        assert_eq!(sound.features_supported(1).unwrap(), PcmFeatures::empty());
1695
1696        fake.terminate();
1697        handle.join().unwrap();
1698    }
1699
1700    #[test]
1701    fn play() {
1702        let (fake, transport) = FakeSoundDevice::new(
1703            vec![],
1704            vec![VirtIOSndPcmInfo {
1705                hdr: VirtIOSndInfo { hda_fn_nid: 0 },
1706                features: 0,
1707                formats: (PcmFormats::U8 | PcmFormats::U32).bits(),
1708                rates: (PcmRates::RATE_44100 | PcmRates::RATE_32000).bits(),
1709                direction: VIRTIO_SND_D_OUTPUT,
1710                channels_min: 1,
1711                channels_max: 2,
1712                _padding: Default::default(),
1713            }],
1714            vec![],
1715        );
1716        let mut sound =
1717            VirtIOSound::<FakeHal, FakeTransport<VirtIOSoundConfig>>::new(transport).unwrap();
1718        let handle = fake.spawn();
1719
1720        assert_eq!(sound.output_streams().unwrap(), vec![0]);
1721        assert_eq!(sound.input_streams().unwrap(), vec![]);
1722
1723        sound
1724            .pcm_set_params(
1725                0,
1726                100,
1727                100,
1728                PcmFeatures::empty(),
1729                1,
1730                PcmFormat::U8,
1731                PcmRate::Rate8000,
1732            )
1733            .unwrap();
1734        assert_eq!(
1735            fake.params.lock().unwrap()[0],
1736            Some(VirtIOSndPcmSetParams {
1737                hdr: VirtIOSndPcmHdr {
1738                    hdr: VirtIOSndHdr {
1739                        command_code: CommandCode::RPcmSetParams.into(),
1740                    },
1741                    stream_id: 0,
1742                },
1743                buffer_bytes: 100,
1744                period_bytes: 100,
1745                features: 0,
1746                channels: 1,
1747                format: PcmFormat::U8.into(),
1748                rate: PcmRate::Rate8000.into(),
1749                _padding: Default::default(),
1750            })
1751        );
1752
1753        sound.pcm_prepare(0).unwrap();
1754        sound.pcm_start(0).unwrap();
1755
1756        let mut expected_sound = vec![];
1757
1758        // Playing an empty set of frames should be a no-op.
1759        println!("Playing empty");
1760        sound.pcm_xfer(0, &[]).unwrap();
1761        assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1762
1763        // Send one buffer worth.
1764        println!("Playing 100");
1765        sound.pcm_xfer(0, &[42; 100]).unwrap();
1766        expected_sound.extend([42; 100]);
1767        assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1768
1769        // Send two buffers worth.
1770        println!("Playing 200");
1771        sound.pcm_xfer(0, &[66; 200]).unwrap();
1772        expected_sound.extend([66; 200]);
1773        assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1774
1775        // Send half a buffer worth.
1776        println!("Playing 50");
1777        sound.pcm_xfer(0, &[55; 50]).unwrap();
1778        expected_sound.extend([55; 50]);
1779        assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1780
1781        // Send enough that the queue will fill up.
1782        println!("Playing 5000");
1783        sound.pcm_xfer(0, &[12; 5000]).unwrap();
1784        expected_sound.extend([12; 5000]);
1785        assert_eq!(fake.played_bytes.lock().unwrap()[0], expected_sound);
1786
1787        sound.pcm_stop(0).unwrap();
1788        sound.pcm_release(0).unwrap();
1789
1790        fake.terminate();
1791        handle.join().unwrap();
1792    }
1793}