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