blob: 08fe8b626490dcdb5d2ff4753bbaa1c5b83da013 [file] [log] [blame]
// 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<T> = std::result::Result<T, Error>;
#[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<control_primitive::Error> 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<Self::T>
{
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<bool> {
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<Self::T>;
/// Saves the value to the mixer control.
fn save(handle: &mut Ctl, id: &ElemId, val: Self::T) -> Result<bool>;
/// 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;
}