1use anyhow::{format_err, Error};
6use fidl::endpoints::{Proxy, ServerEnd};
7use fidl_fuchsia_hardware_audio::*;
8
9use futures::future::{self, Either};
10use futures::{Future, FutureExt, TryFutureExt};
11use std::path::{Path, PathBuf};
12
13#[derive(Debug)]
14pub struct DigitalAudioInterface {
15 path: PathBuf,
17 proxy: Option<DaiProxy>,
19}
20
21impl DigitalAudioInterface {
22 pub fn new(path: &Path) -> Self {
25 Self { path: path.to_path_buf(), proxy: None }
26 }
27
28 pub(crate) fn from_proxy(proxy: DaiProxy) -> Self {
30 Self { path: PathBuf::new(), proxy: Some(proxy) }
31 }
32
33 pub fn connect(&mut self) -> Result<(), Error> {
35 if let Some(proxy) = &self.proxy {
36 if !proxy.is_closed() {
37 return Ok(());
38 }
39 }
40 let (dai_connect_proxy, dai_connect_server) =
41 fidl::endpoints::create_proxy::<DaiConnectorMarker>();
42 let path = self.path.to_str().ok_or_else(|| format_err!("invalid DAI path"))?;
43 fdio::service_connect(path, dai_connect_server.into_channel())?;
44
45 let (ours, theirs) = fidl::endpoints::create_proxy::<DaiMarker>();
46 dai_connect_proxy.connect(theirs)?;
47
48 self.proxy = Some(ours);
49 Ok(())
50 }
51
52 fn get_proxy(&self) -> Result<&DaiProxy, Error> {
53 self.proxy.as_ref().ok_or_else(|| format_err!("Proxy not connected"))
54 }
55
56 pub fn properties(&self) -> impl Future<Output = Result<DaiProperties, Error>> {
59 match self.get_proxy() {
60 Err(e) => Either::Left(future::ready(Err(e))),
61 Ok(proxy) => Either::Right(proxy.clone().get_properties().err_into()),
62 }
63 }
64
65 pub fn dai_formats(&self) -> impl Future<Output = Result<Vec<DaiSupportedFormats>, Error>> {
66 let proxy = match self.get_proxy() {
67 Err(e) => return Either::Left(future::ready(Err(e))),
68 Ok(proxy) => proxy,
69 };
70 Either::Right(proxy.clone().get_dai_formats().map(|o| match o {
71 Err(e) => Err(e.into()),
72 Ok(Err(e)) => Err(zx::Status::from_raw(e).into()),
73 Ok(Ok(o)) => Ok(o),
74 }))
75 }
76
77 pub fn ring_buffer_formats(
78 &self,
79 ) -> impl Future<Output = Result<Vec<SupportedFormats>, Error>> {
80 let proxy = match self.get_proxy() {
81 Err(e) => return Either::Left(future::ready(Err(e))),
82 Ok(proxy) => proxy,
83 };
84 Either::Right(proxy.clone().get_ring_buffer_formats().map(|o| match o {
85 Err(e) => Err(e.into()),
86 Ok(Err(e)) => Err(zx::Status::from_raw(e).into()),
87 Ok(Ok(o)) => Ok(o),
88 }))
89 }
90
91 pub fn create_ring_buffer(
92 &self,
93 dai_format: DaiFormat,
94 buffer_format: Format,
95 ring_buffer_client: ServerEnd<RingBufferMarker>,
96 ) -> Result<(), Error> {
97 let proxy = self.get_proxy()?;
98 proxy
99 .create_ring_buffer(&dai_format, &buffer_format, ring_buffer_client)
100 .map_err(Into::into)
101 }
102}
103
104pub(crate) fn ensure_pcm_format_is_supported(
105 ring_buffer_formats: &[SupportedFormats],
106 pcm_format: &PcmFormat,
107) -> Result<(), Error> {
108 for format in ring_buffer_formats {
109 if let SupportedFormats {
110 pcm_supported_formats:
111 Some(PcmSupportedFormats {
112 channel_sets: Some(channel_sets),
113 sample_formats: Some(sample_formats),
114 bytes_per_sample: Some(bytes_per_sample),
115 valid_bits_per_sample: Some(valid_bits_per_sample),
116 frame_rates: Some(frame_rates),
117 ..
118 }),
119 ..
120 } = format
121 {
122 if channel_sets[0].attributes.as_ref().unwrap().len()
123 == pcm_format.number_of_channels as usize
124 && sample_formats.contains(&pcm_format.sample_format)
125 && bytes_per_sample.contains(&pcm_format.bytes_per_sample)
126 && valid_bits_per_sample.contains(&pcm_format.valid_bits_per_sample)
127 && frame_rates.contains(&pcm_format.frame_rate)
128 {
129 return Ok(());
130 }
131 }
132 }
133 Err(format_err!("DAI does not support PCM format: {:?}", pcm_format))
134}
135
136pub(crate) fn ensure_dai_format_is_supported(
137 supported_formats: &[DaiSupportedFormats],
138 dai_format: &DaiFormat,
139) -> Result<(), Error> {
140 for dai_supported in supported_formats.iter() {
141 if dai_supported.number_of_channels.contains(&dai_format.number_of_channels)
142 && dai_supported.sample_formats.contains(&dai_format.sample_format)
143 && dai_supported.frame_formats.contains(&dai_format.frame_format)
144 && dai_supported.frame_rates.contains(&dai_format.frame_rate)
145 && dai_supported.bits_per_slot.contains(&dai_format.bits_per_slot)
146 && dai_supported.bits_per_sample.contains(&dai_format.bits_per_sample)
147 {
148 return Ok(());
149 }
150 }
151 Err(format_err!("DAI does not support DAI format: {dai_format:?} not in {supported_formats:?}"))
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 use async_utils::PollExt;
159 use fuchsia_async as fasync;
160 use futures::task::Poll;
161 use futures::StreamExt;
162 use std::pin::pin;
163
164 fn connected_dai() -> (DigitalAudioInterface, DaiRequestStream) {
165 let (proxy, requests) = fidl::endpoints::create_proxy_and_stream::<DaiMarker>();
166 let dai = DigitalAudioInterface::from_proxy(proxy);
167 (dai, requests)
168 }
169
170 #[fuchsia::test]
171 fn get_properties() {
172 let mut exec = fasync::TestExecutor::new();
173 let dai = DigitalAudioInterface { path: PathBuf::new(), proxy: None };
175
176 let _ = exec
177 .run_singlethreaded(&mut dai.properties())
178 .expect_err("properties of an unconnected DAI should be error");
179
180 let (dai, mut requests) = connected_dai();
181
182 let properties_fut = dai.properties();
183 let mut properties_fut = pin!(properties_fut);
184
185 exec.run_until_stalled(&mut properties_fut).expect_pending("should be pending");
186
187 match exec.run_until_stalled(&mut requests.next()) {
188 Poll::Ready(Some(Ok(DaiRequest::GetProperties { responder }))) => responder
189 .send(&DaiProperties {
190 is_input: Some(true),
191 manufacturer: Some(String::from("Fuchsia")),
192 product_name: Some(String::from("Spinny Audio")),
193 ..Default::default()
194 })
195 .expect("send response okay"),
196 x => panic!("Expected a ready GetProperties request, got {:?}", x),
197 };
198
199 let result = exec.run_until_stalled(&mut properties_fut).expect("response from properties");
200 let properties = result.expect("ok response");
201 assert_eq!(Some(true), properties.is_input);
202 }
203
204 #[fuchsia::test]
205 fn dai_formats() {
206 let mut exec = fasync::TestExecutor::new();
207 let (dai, mut requests) = connected_dai();
208
209 let supported_formats_fut = dai.dai_formats();
210 let mut supported_formats_fut = pin!(supported_formats_fut);
211
212 drop(dai);
214
215 exec.run_until_stalled(&mut supported_formats_fut).expect_pending("should be pending");
216
217 match exec.run_until_stalled(&mut requests.next()) {
218 Poll::Ready(Some(Ok(DaiRequest::GetDaiFormats { responder }))) => responder
219 .send(Ok(&[
220 DaiSupportedFormats {
221 number_of_channels: vec![1],
222 sample_formats: vec![
223 DaiSampleFormat::PcmSigned,
224 DaiSampleFormat::PcmUnsigned,
225 ],
226 frame_formats: vec![DaiFrameFormat::FrameFormatStandard(
227 DaiFrameFormatStandard::Tdm1,
228 )],
229 frame_rates: vec![44100],
230 bits_per_slot: vec![16],
231 bits_per_sample: vec![16],
232 },
233 DaiSupportedFormats {
234 number_of_channels: vec![2],
235 sample_formats: vec![DaiSampleFormat::PcmSigned],
236 frame_formats: vec![DaiFrameFormat::FrameFormatStandard(
237 DaiFrameFormatStandard::I2S,
238 )],
239 frame_rates: vec![8000],
240 bits_per_slot: vec![32],
241 bits_per_sample: vec![32],
242 },
243 ]))
244 .expect("send response okay"),
245 x => panic!("expected a ready GetDaiFormats, got {:?}", x),
246 };
247
248 let result = exec.run_until_stalled(&mut supported_formats_fut).expect("response");
249 let formats = result.expect("ok response");
250 assert_eq!(2, formats.len());
251 }
252}