// Copyright 2020 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! This module provides different implementations of `Elem` that use the alsa-lib control interface //! API to read and write alsa control elements. //! //! The `Elem::type()` returns the type of value that a control element can interact with, //! and it is one of integer, integer64, boolean, enumerators, bytes or IEC958 structure. //! The `Elem::size()` returns the number of values it reads from or writes to the hardware //! at a time. //! The `Elem::load(..)` and `Elem::save(..)` are used by `ControlOps` trait to read and write //! the underlying mixer control. //! //! Users should use the provided implementations of `Elem` to define the associated type in //! their owner encapsulation of `Control`. use std::default::Default; use std::error; use std::fmt; use libc::{c_long, c_uint}; use remain::sorted; use crate::control_primitive::{self, snd_strerror, Ctl, ElemId, ElemType, ElemValue}; /// The Result type of cros-alsa::elem. pub type Result = std::result::Result; #[sorted] #[derive(Debug)] /// Possible errors that can occur in cros-alsa::elem. pub enum Error { /// Failed to call AlsaControlAPI. AlsaControlAPI(control_primitive::Error), /// Failed to call `snd_ctl_elem_read()`. ElemReadFailed(i32), /// Failed to call `snd_ctl_elem_write()`. ElemWriteFailed(i32), } impl error::Error for Error {} impl From for Error { fn from(err: control_primitive::Error) -> Error { Error::AlsaControlAPI(err) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Error::*; match self { AlsaControlAPI(e) => write!(f, "{}", e), ElemReadFailed(e) => write!(f, "snd_ctl_elem_read failed: {}", snd_strerror(*e)?), ElemWriteFailed(e) => write!(f, "snd_ctl_elem_write failed: {}", snd_strerror(*e)?), } } } // Uses a recursive macro to generate implementation for [bool; n] and [i32; n], n = 1 to 128. // The `$t:ident $($ts:ident)*` part matches and removes one token at a time. It's used for // counting recursive steps. macro_rules! impl_for_array { {$n:expr, $type:ty, $t:ident $($ts:ident)*} => { impl Elem for [$type; $n] { type T = Self; /// Reads [$type; $n] data from the mixer control. /// /// # Errors /// /// * If it fails to call `snd_ctl_elem_read()`. fn load(handle: &mut Ctl, id: &ElemId) -> Result { let mut elem = ElemValue::new(id)?; // Safe because self.handle.as_mut_ptr() is a valid *mut snd_ctl_t and // elem.as_mut_ptr() is also a valid *mut snd_ctl_elem_value_t. let rc = unsafe { alsa_sys::snd_ctl_elem_read(handle.as_mut_ptr(), elem.as_mut_ptr()) }; if rc < 0 { return Err(Error::ElemReadFailed(rc)); } let mut ret = [Default::default(); $n]; for i in 0..$n { // Safe because elem.as_ptr() is a valid snd_ctl_elem_value_t* and i is guaranteed to be // within a valid range. ret[i] = unsafe { <$type>::elem_value_get(&elem, i) }; } Ok(ret) } /// Updates [$type; $n] data to the mixer control. /// /// # Results /// /// * `changed` - false on success. /// - true on success when value was changed. /// /// # Errors /// /// * If it fails to call `snd_ctl_elem_write()`. fn save(handle: &mut Ctl, id: &ElemId, val: Self::T) -> Result { let mut elem = ElemValue::new(id)?; for i in 0..$n { // Safe because elem.as_mut_ptr() is a valid snd_ctl_elem_value_t* and i is guaranteed to be // within a valid range. unsafe { <$type>::elem_value_set(&mut elem, i, val[i]) }; } // Safe because self.handle.as_mut_ptr() is a valid *mut snd_ctl_t and // elem.as_mut_ptr() is also a valid *mut snd_ctl_elem_value_t. let rc = unsafe { alsa_sys::snd_ctl_elem_write(handle.as_mut_ptr(), elem.as_mut_ptr()) }; if rc < 0 { return Err(Error::ElemWriteFailed(rc)); } Ok(rc > 0) } /// Gets the data type itself can read and write. fn elem_type() -> ElemType { <$type>::elem_type() } /// Gets the number of value entries itself can read and write. fn size() -> usize { $n } } impl_for_array!{($n - 1), $type, $($ts)*} }; {$n:expr, $type:ty,} => {}; } // Implements `Elem` for [i32; n] where n = 1 to 128. impl_for_array! {128, i32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T } // Implements `Elem` for [bool; n] where n = 1 to 128. impl_for_array! {128, bool, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T } impl CtlElemValue for bool { type T = bool; /// Gets a bool from the ElemValue. unsafe fn elem_value_get(elem: &ElemValue, idx: usize) -> bool { alsa_sys::snd_ctl_elem_value_get_boolean(elem.as_ptr(), idx as c_uint) != 0 } /// Sets a bool to the ElemValue. unsafe fn elem_value_set(elem: &mut ElemValue, idx: usize, val: bool) { alsa_sys::snd_ctl_elem_value_set_boolean(elem.as_mut_ptr(), idx as c_uint, val as c_long); } /// Returns ElemType::Boolean. fn elem_type() -> ElemType { ElemType::Boolean } } impl CtlElemValue for i32 { type T = i32; /// Gets an i32 from the ElemValue. unsafe fn elem_value_get(elem: &ElemValue, idx: usize) -> i32 { alsa_sys::snd_ctl_elem_value_get_integer(elem.as_ptr(), idx as c_uint) as i32 } /// Sets an i32 to the ElemValue. unsafe fn elem_value_set(elem: &mut ElemValue, idx: usize, val: i32) { alsa_sys::snd_ctl_elem_value_set_integer(elem.as_mut_ptr(), idx as c_uint, val as c_long); } /// Returns ElemType::Integer. fn elem_type() -> ElemType { ElemType::Integer } } /// All primitive types of a control element should implement `CtlElemValue` trait. trait CtlElemValue { /// The primitive type of a control element. type T; /// Gets the value from the ElemValue. unsafe fn elem_value_get(value: &ElemValue, idx: usize) -> Self::T; /// Sets the value to the ElemValue. unsafe fn elem_value_set(value: &mut ElemValue, id: usize, val: Self::T); /// Gets the data type itself can read and write. fn elem_type() -> ElemType; } /// Use `Elem` trait to access the underlying control element through the given `Ctl` and `ElemId`. pub trait Elem: Sized { /// The data type of a control element. type T; /// Reads the value from the mixer control. fn load(handle: &mut Ctl, id: &ElemId) -> Result; /// Saves the value to the mixer control. fn save(handle: &mut Ctl, id: &ElemId, val: Self::T) -> Result; /// Gets the data type itself can read and write. fn elem_type() -> ElemType; /// Gets the number of value entries itself can read and write. fn size() -> usize; }