summaryrefslogtreecommitdiff
path: root/cros_alsa/src/elem.rs
blob: 08fe8b626490dcdb5d2ff4753bbaa1c5b83da013 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// 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;
}