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