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