1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::convert::TryFrom;
6 use std::error;
7 use std::ffi::{CStr, CString, FromBytesWithNulError, NulError};
8 use std::fmt;
9 use std::marker::PhantomData;
10 use std::ptr;
11 use std::slice;
12 use std::str;
13 
14 use alsa_sys::*;
15 use libc::strlen;
16 use remain::sorted;
17 
18 pub type Result<T> = std::result::Result<T, Error>;
19 
20 #[derive(Debug, PartialEq)]
21 /// Possible errors that can occur in FFI functions.
22 pub enum FFIError {
23     Rc(i32),
24     NullPtr,
25 }
26 
27 impl fmt::Display for FFIError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result28     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29         use FFIError::*;
30         match self {
31             Rc(rc) => write!(f, "{}", snd_strerror(*rc)?),
32             NullPtr => write!(f, "the return value is a null pointer"),
33         }
34     }
35 }
36 
37 #[sorted]
38 #[derive(Debug, PartialEq)]
39 /// Possible errors that can occur in cros-alsa::control_primitive.
40 pub enum Error {
41     /// Control with the given name does not exist.
42     ControlNotFound(String),
43     /// Failed to call snd_ctl_open().
44     CtlOpenFailed(FFIError, String),
45     /// snd_ctl_elem_id_get_name() returns null.
46     ElemIdGetNameFailed,
47     /// Failed to call snd_ctl_elem_id_malloc().
48     ElemIdMallocFailed(FFIError),
49     /// Failed to call snd_ctl_elem_info_malloc().
50     ElemInfoMallocFailed(FFIError),
51     /// Failed to call snd_ctl_elem_value_malloc().
52     ElemValueMallocFailed(FFIError),
53     /// The slice used to create a CStr does not have one and only one null
54     /// byte positioned at the end.
55     FromBytesWithNulError(FromBytesWithNulError),
56     /// Failed to convert to a valid ElemType.
57     InvalidElemType(u32),
58     /// An error indicating that an interior nul byte was found.
59     NulError(NulError),
60     /// Failed to call snd_strerror().
61     SndStrErrorFailed(i32),
62     /// UTF-8 validation failed
63     Utf8Error(str::Utf8Error),
64 }
65 
66 impl error::Error for Error {}
67 
68 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result69     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70         use Error::*;
71         match self {
72             ControlNotFound(name) => write!(f, "control: {} does not exist", name),
73             CtlOpenFailed(e, name) => write!(f, "{} snd_ctl_open failed: {}", name, e,),
74             ElemIdGetNameFailed => write!(f, "snd_ctl_elem_id_get_name failed"),
75             ElemIdMallocFailed(e) => write!(f, "snd_ctl_elem_id_malloc failed: {}", e),
76             ElemInfoMallocFailed(e) => write!(f, "snd_ctl_elem_info_malloc failed: {}", e),
77             ElemValueMallocFailed(e) => write!(f, "snd_ctl_elem_value_malloc failed: {}", e),
78             FromBytesWithNulError(e) => write!(f, "invalid CString: {}", e),
79             InvalidElemType(v) => write!(f, "invalid ElemType: {}", v),
80             NulError(e) => write!(f, "invalid CString: {}", e),
81             SndStrErrorFailed(e) => write!(f, "snd_strerror() failed: {}", e),
82             Utf8Error(e) => write!(f, "{}", e),
83         }
84     }
85 }
86 
87 impl From<Error> for fmt::Error {
from(_err: Error) -> fmt::Error88     fn from(_err: Error) -> fmt::Error {
89         fmt::Error
90     }
91 }
92 
93 impl From<str::Utf8Error> for Error {
from(err: str::Utf8Error) -> Error94     fn from(err: str::Utf8Error) -> Error {
95         Error::Utf8Error(err)
96     }
97 }
98 
99 impl From<FromBytesWithNulError> for Error {
from(err: FromBytesWithNulError) -> Error100     fn from(err: FromBytesWithNulError) -> Error {
101         Error::FromBytesWithNulError(err)
102     }
103 }
104 
105 impl From<NulError> for Error {
from(err: NulError) -> Error106     fn from(err: NulError) -> Error {
107         Error::NulError(err)
108     }
109 }
110 
111 /// [snd_ctl_elem_iface_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#ga14baa0febb91cc4c5d72dcc825acf518) wrapper.
112 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
113 pub enum ElemIface {
114     Card = SND_CTL_ELEM_IFACE_CARD as isize,
115     Hwdep = SND_CTL_ELEM_IFACE_HWDEP as isize,
116     Mixer = SND_CTL_ELEM_IFACE_MIXER as isize,
117     PCM = SND_CTL_ELEM_IFACE_PCM as isize,
118     Rawmidi = SND_CTL_ELEM_IFACE_RAWMIDI as isize,
119     Timer = SND_CTL_ELEM_IFACE_TIMER as isize,
120     Sequencer = SND_CTL_ELEM_IFACE_SEQUENCER as isize,
121 }
122 
123 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
124 /// [snd_ctl_elem_type_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#gac42e0ed6713b62711af5e80b4b3bcfec) wrapper.
125 pub enum ElemType {
126     None = SND_CTL_ELEM_TYPE_NONE as isize,
127     Boolean = SND_CTL_ELEM_TYPE_BOOLEAN as isize,
128     Integer = SND_CTL_ELEM_TYPE_INTEGER as isize,
129     Enumerated = SND_CTL_ELEM_TYPE_ENUMERATED as isize,
130     Bytes = SND_CTL_ELEM_TYPE_BYTES as isize,
131     IEC958 = SND_CTL_ELEM_TYPE_IEC958 as isize,
132     Integer64 = SND_CTL_ELEM_TYPE_INTEGER64 as isize,
133 }
134 
135 impl fmt::Display for ElemType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result136     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137         match self {
138             ElemType::None => write!(f, "SND_CTL_ELEM_TYPE_NONE"),
139             ElemType::Boolean => write!(f, "SND_CTL_ELEM_TYPE_BOOLEAN"),
140             ElemType::Integer => write!(f, "SND_CTL_ELEM_TYPE_INTEGER"),
141             ElemType::Enumerated => write!(f, "SND_CTL_ELEM_TYPE_ENUMERATED"),
142             ElemType::Bytes => write!(f, "SND_CTL_ELEM_TYPE_BYTES"),
143             ElemType::IEC958 => write!(f, "SND_CTL_ELEM_TYPE_IEC958"),
144             ElemType::Integer64 => write!(f, "SND_CTL_ELEM_TYPE_INTEGER64"),
145         }
146     }
147 }
148 
149 impl TryFrom<u32> for ElemType {
150     type Error = Error;
try_from(elem_type: u32) -> Result<ElemType>151     fn try_from(elem_type: u32) -> Result<ElemType> {
152         match elem_type {
153             SND_CTL_ELEM_TYPE_NONE => Ok(ElemType::None),
154             SND_CTL_ELEM_TYPE_BOOLEAN => Ok(ElemType::Boolean),
155             SND_CTL_ELEM_TYPE_INTEGER => Ok(ElemType::Integer),
156             SND_CTL_ELEM_TYPE_ENUMERATED => Ok(ElemType::Enumerated),
157             SND_CTL_ELEM_TYPE_BYTES => Ok(ElemType::Bytes),
158             SND_CTL_ELEM_TYPE_IEC958 => Ok(ElemType::IEC958),
159             SND_CTL_ELEM_TYPE_INTEGER64 => Ok(ElemType::Integer64),
160             _ => Err(Error::InvalidElemType(elem_type)),
161         }
162     }
163 }
164 
165 /// [snd_ctl_elem_id_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#gad6c3746f1925bfec6a4fd0e913430e55) wrapper.
166 pub struct ElemId(
167     ptr::NonNull<snd_ctl_elem_id_t>,
168     PhantomData<snd_ctl_elem_id_t>,
169 );
170 
171 impl Drop for ElemId {
drop(&mut self)172     fn drop(&mut self) {
173         // Safe because self.0.as_ptr() is a valid snd_ctl_elem_id_t*.
174         unsafe { snd_ctl_elem_id_free(self.0.as_ptr()) };
175     }
176 }
177 
178 impl ElemId {
179     /// Creates an `ElemId` object by `ElemIface` and name.
180     ///
181     /// # Errors
182     ///
183     /// * If memory allocation fails.
184     /// * If ctl_name is not a valid CString.
new(iface: ElemIface, ctl_name: &str) -> Result<ElemId>185     pub fn new(iface: ElemIface, ctl_name: &str) -> Result<ElemId> {
186         let mut id_ptr = ptr::null_mut();
187         // Safe because we provide a valid id_ptr to be filled,
188         // and we validate the return code before using id_ptr.
189         let rc = unsafe { snd_ctl_elem_id_malloc(&mut id_ptr) };
190         if rc < 0 {
191             return Err(Error::ElemIdMallocFailed(FFIError::Rc(rc)));
192         }
193         let id = ptr::NonNull::new(id_ptr).ok_or(Error::ElemIdMallocFailed(FFIError::NullPtr))?;
194 
195         // Safe because id.as_ptr() is a valid snd_ctl_elem_id_t*.
196         unsafe { snd_ctl_elem_id_set_interface(id.as_ptr(), iface as u32) };
197         let name = CString::new(ctl_name)?;
198         // Safe because id.as_ptr() is a valid snd_ctl_elem_id_t* and name is a safe CString.
199         unsafe { snd_ctl_elem_id_set_name(id.as_ptr(), name.as_ptr()) };
200         Ok(ElemId(id, PhantomData))
201     }
202 
203     /// Borrows the const inner pointer.
as_ptr(&self) -> *const snd_ctl_elem_id_t204     pub fn as_ptr(&self) -> *const snd_ctl_elem_id_t {
205         self.0.as_ptr()
206     }
207 
208     /// Safe [snd_ctl_elem_id_get_name()] (https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#gaa6cfea3ac963bfdaeb8189e03e927a76) wrapper.
209     ///
210     /// # Errors
211     ///
212     /// * If snd_ctl_elem_id_get_name() fails.
213     /// * If control element name is not a valid CString.
214     /// * If control element name is not valid UTF-8 data.
name(&self) -> Result<&str>215     pub fn name(&self) -> Result<&str> {
216         // Safe because self.as_ptr() is a valid snd_ctl_elem_id_t*.
217         let name = unsafe { snd_ctl_elem_id_get_name(self.as_ptr()) };
218         if name.is_null() {
219             return Err(Error::ElemIdGetNameFailed);
220         }
221         // Safe because name is a valid *const i8, and its life time
222         // is the same as the passed reference of self.
223         let s = CStr::from_bytes_with_nul(unsafe {
224             slice::from_raw_parts(name as *const u8, strlen(name) + 1)
225         })?;
226         Ok(s.to_str()?)
227     }
228 }
229 
230 /// [snd_ctl_elem_value_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#ga266b478eb64f1cdd75e337df4b4b995e) wrapper.
231 pub struct ElemValue(
232     ptr::NonNull<snd_ctl_elem_value_t>,
233     PhantomData<snd_ctl_elem_value_t>,
234 );
235 
236 impl Drop for ElemValue {
237     // Safe because self.0.as_ptr() is valid.
drop(&mut self)238     fn drop(&mut self) {
239         unsafe { snd_ctl_elem_value_free(self.0.as_ptr()) };
240     }
241 }
242 
243 impl ElemValue {
244     /// Creates an `ElemValue`.
245     ///
246     /// # Errors
247     ///
248     /// * If memory allocation fails.
new(id: &ElemId) -> Result<ElemValue>249     pub fn new(id: &ElemId) -> Result<ElemValue> {
250         let mut v_ptr = ptr::null_mut();
251         // Safe because we provide a valid v_ptr to be filled,
252         // and we validate the return code before using v_ptr.
253         let rc = unsafe { snd_ctl_elem_value_malloc(&mut v_ptr) };
254         if rc < 0 {
255             return Err(Error::ElemValueMallocFailed(FFIError::Rc(rc)));
256         }
257         let value =
258             ptr::NonNull::new(v_ptr).ok_or(Error::ElemValueMallocFailed(FFIError::NullPtr))?;
259         // Safe because value.as_ptr() is a valid snd_ctl_elem_value_t* and id.as_ptr() is also valid.
260         unsafe { snd_ctl_elem_value_set_id(value.as_ptr(), id.as_ptr()) };
261         Ok(ElemValue(value, PhantomData))
262     }
263 
264     /// Borrows the mutable inner pointer.
as_mut_ptr(&mut self) -> *mut snd_ctl_elem_value_t265     pub fn as_mut_ptr(&mut self) -> *mut snd_ctl_elem_value_t {
266         self.0.as_ptr()
267     }
268 
269     /// Borrows the const inner pointer.
as_ptr(&self) -> *const snd_ctl_elem_value_t270     pub fn as_ptr(&self) -> *const snd_ctl_elem_value_t {
271         self.0.as_ptr()
272     }
273 }
274 
275 /// [snd_ctl_elem_info_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#ga2cae0bb76df919368e4ff9a7021dd3ab) wrapper.
276 pub struct ElemInfo(
277     ptr::NonNull<snd_ctl_elem_info_t>,
278     PhantomData<snd_ctl_elem_info_t>,
279 );
280 
281 impl Drop for ElemInfo {
drop(&mut self)282     fn drop(&mut self) {
283         // Safe because self.0.as_ptr() is a valid snd_ctl_elem_info_t*.
284         unsafe { snd_ctl_elem_info_free(self.0.as_ptr()) };
285     }
286 }
287 
288 impl ElemInfo {
289     /// Creates an `ElemInfo`.
290     ///
291     /// # Errors
292     ///
293     /// * If memory allocation fails.
294     /// * If control does not exist.
new(handle: &mut Ctl, id: &ElemId) -> Result<ElemInfo>295     pub fn new(handle: &mut Ctl, id: &ElemId) -> Result<ElemInfo> {
296         let mut info_ptr = ptr::null_mut();
297 
298         // Safe because we provide a valid info_ptr to be filled,
299         // and we validate the return code before using info_ptr.
300         let rc = unsafe { snd_ctl_elem_info_malloc(&mut info_ptr) };
301         if rc < 0 {
302             return Err(Error::ElemInfoMallocFailed(FFIError::Rc(rc)));
303         }
304         let info =
305             ptr::NonNull::new(info_ptr).ok_or(Error::ElemInfoMallocFailed(FFIError::NullPtr))?;
306 
307         // Safe because info.as_ptr() is a valid snd_ctl_elem_info_t* and id.as_ptr() is also valid.
308         unsafe { snd_ctl_elem_info_set_id(info.as_ptr(), id.as_ptr()) };
309 
310         // Safe because handle.as_mut_ptr() is a valid snd_ctl_t* and info.as_ptr() is a valid
311         // snd_ctl_elem_info_t*.
312         let rc = unsafe { snd_ctl_elem_info(handle.as_mut_ptr(), info.as_ptr()) };
313         if rc < 0 {
314             return Err(Error::ControlNotFound(id.name()?.to_owned()));
315         }
316         Ok(ElemInfo(info, PhantomData))
317     }
318 
319     /// Safe [snd_ctl_elem_info_get_type](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#ga0fec5d22ee58d04f14b59f405adc595e) wrapper.
elem_type(&self) -> Result<ElemType>320     pub fn elem_type(&self) -> Result<ElemType> {
321         // Safe because self.0.as_ptr() is a valid snd_ctl_elem_info_t*.
322         unsafe { ElemType::try_from(snd_ctl_elem_info_get_type(self.0.as_ptr())) }
323     }
324 
325     /// Safe [snd_ctl_elem_info_get_count](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#gaa75a20d4190d324bcda5fd6659a4b377) wrapper.
count(&self) -> usize326     pub fn count(&self) -> usize {
327         // Safe because self.0.as_ptr() is a valid snd_ctl_elem_info_t*.
328         unsafe { snd_ctl_elem_info_get_count(self.0.as_ptr()) as usize }
329     }
330 
331     /// Safe [snd_ctl_elem_info_is_tlv_readable](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#gaac6bb412e5a9fffb5509e98a10de45b5) wrapper.
tlv_readable(&self) -> bool332     pub fn tlv_readable(&self) -> bool {
333         // Safe because self.0.as_ptr() is a valid snd_ctl_elem_info_t*.
334         unsafe { snd_ctl_elem_info_is_tlv_readable(self.0.as_ptr()) as usize == 1 }
335     }
336 
337     /// Safe [snd_ctl_elem_info_is_tlv_writable](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#gacfbaae80d710b6feac682f8ba10a0341) wrapper.
tlv_writable(&self) -> bool338     pub fn tlv_writable(&self) -> bool {
339         // Safe because self.0.as_ptr() is a valid snd_ctl_elem_info_t*.
340         unsafe { snd_ctl_elem_info_is_tlv_writable(self.0.as_ptr()) as usize == 1 }
341     }
342 }
343 
344 /// [snd_ctl_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#ga06628f38def84a0fe3da74041db9d51f) wrapper.
345 #[derive(Debug)]
346 pub struct Ctl(ptr::NonNull<snd_ctl_t>, PhantomData<snd_ctl_t>);
347 
348 impl Drop for Ctl {
drop(&mut self)349     fn drop(&mut self) {
350         // Safe as we provide a valid snd_ctl_t*.
351         unsafe { snd_ctl_close(self.0.as_ptr()) };
352     }
353 }
354 
355 impl Ctl {
356     /// Creates a `Ctl`.
357     /// Safe [snd_ctl_open](https://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html#ga58537f5b74c9c1f51699f9908a0d7f56).
358     /// Does not support async mode.
359     ///
360     /// # Errors
361     ///
362     /// * If `card` is an invalid CString.
363     /// * If `snd_ctl_open()` fails.
new(card: &str) -> Result<Ctl>364     pub fn new(card: &str) -> Result<Ctl> {
365         let name = CString::new(card)?;
366         let mut ctl_ptr = ptr::null_mut();
367         // Safe because we provide a valid ctl_ptr to be filled, name is a safe CString
368         // and we validate the return code before using ctl_ptr.
369         let rc = unsafe { snd_ctl_open(&mut ctl_ptr, name.as_ptr(), 0) };
370         if rc < 0 {
371             return Err(Error::CtlOpenFailed(
372                 FFIError::Rc(rc),
373                 name.to_str()?.to_owned(),
374             ));
375         }
376         let ctl = ptr::NonNull::new(ctl_ptr).ok_or(Error::CtlOpenFailed(
377             FFIError::NullPtr,
378             name.to_str()?.to_owned(),
379         ))?;
380         Ok(Ctl(ctl, PhantomData))
381     }
382 
383     /// Borrows the mutable inner pointer
as_mut_ptr(&mut self) -> *mut snd_ctl_t384     pub fn as_mut_ptr(&mut self) -> *mut snd_ctl_t {
385         self.0.as_ptr()
386     }
387 }
388 
389 /// Safe [snd_strerror](https://www.alsa-project.org/alsa-doc/alsa-lib/group___error.html#ga182bbadf2349e11602bc531e8cf22f7e) wrapper.
390 ///
391 /// # Errors
392 ///
393 /// * If `snd_strerror` returns invalid UTF-8 data.
snd_strerror(err_num: i32) -> Result<&'static str>394 pub fn snd_strerror(err_num: i32) -> Result<&'static str> {
395     // Safe because we validate the return pointer of snd_strerror()
396     // before using it.
397     let s_ptr = unsafe { alsa_sys::snd_strerror(err_num) };
398     if s_ptr.is_null() {
399         return Err(Error::SndStrErrorFailed(err_num));
400     }
401     // Safe because s_ptr is a non-null *const u8 and its lifetime is static.
402     let s = CStr::from_bytes_with_nul(unsafe {
403         slice::from_raw_parts(s_ptr as *const u8, strlen(s_ptr) + 1)
404     })?;
405     Ok(s.to_str()?)
406 }
407