blob: e1f3551045da9cf500c82ff142138f31cd23c806 [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.
//! `ControlTLV` supports read and write of the alsa TLV byte controls
//! Users can obtain a ControlTLV by Card::control_tlv_by_name().
//! # Examples
//! This is an example of how to use a `TLV`.
//!
//! ```
//! use std::assert_eq;
//! use std::convert::TryFrom;
//! use std::error::Error;
//!
//! use cros_alsa::{TLV, ControlTLVError};
//! use cros_alsa::elem::Elem;
//!
//! type Result<T> = std::result::Result<T, ControlTLVError>;
//!
//! let mut tlv = TLV::new(0, vec![1,2,3,4]);
//! assert_eq!(4, tlv.len());
//! assert_eq!(0, tlv.tlv_type());
//! assert_eq!(2, tlv[1]);
//! tlv[1] = 8;
//! assert_eq!(vec![1,8,3,4], tlv.value().to_vec());
//! assert_eq!(vec![0,16,1,8,3,4], Into::<Vec<u32>>::into(tlv));
//!
//! ```
use std::{
convert::TryFrom,
fmt,
ops::{Index, IndexMut},
slice::SliceIndex,
};
use std::{error, mem::size_of};
use remain::sorted;
use crate::control_primitive::{self, Ctl, ElemId, ElemInfo, ElemType};
/// The Result type of cros-alsa::control.
pub type Result<T> = std::result::Result<T, Error>;
#[sorted]
#[derive(Debug, PartialEq)]
/// Possible errors that can occur in cros-alsa::control.
pub enum Error {
/// Failed to call AlsaControlAPI.
AlsaControlAPI(control_primitive::Error),
/// Failed to convert buffer to TLV struct.
InvalidTLV,
/// ElemInfo::count() is not multiple of size_of::<u32>.
InvalidTLVSize(String, usize),
/// ElemInfo::elem_type() is not ElemType::Bytes.
InvalidTLVType(String, ElemType),
/// The control is not readable.
TLVNotReadable,
/// The control is not writeable.
TLVNotWritable,
/// Failed to call snd_ctl_elem_tlv_read.
TLVReadFailed(i32),
/// Failed to call snd_ctl_elem_tlv_write.
TVLWriteFailed(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),
InvalidTLV => write!(f, "failed to convert to TLV"),
InvalidTLVSize(name, elem_size) => write!(
f,
"ElemInfo::size() of {} should be multiple of size_of::<u32>, get: {}",
name, elem_size
),
InvalidTLVType(name, elem_type) => write!(
f,
"invalid ElemInfo::elem_type() of {}: expect: {}, get: {}",
name,
ElemType::Bytes,
elem_type
),
TLVNotReadable => write!(f, "the control is not readable."),
TLVNotWritable => write!(f, "the control is not writable."),
TLVReadFailed(rc) => write!(f, "snd_ctl_elem_tlv_read failed: {}", rc),
TVLWriteFailed(rc) => write!(f, "snd_ctl_elem_tlv_write failed: {}", rc),
}
}
}
/// TLV struct represents the TLV data to be read from
/// or write to an alsa TLV byte control.
#[derive(Debug)]
pub struct TLV {
/// data[Self::TYPE_OFFSET] contains the tlv type.
/// data[Self::LEN_OFFSET] contains the length of the value in bytes.
/// data[Self::VALUE_OFFSET..] contains the data.
data: Vec<u32>,
}
impl TLV {
const TYPE_OFFSET: usize = 0;
const LEN_OFFSET: usize = 1;
const VALUE_OFFSET: usize = 2;
const TLV_HEADER_SIZE_BYTES: usize = Self::VALUE_OFFSET * size_of::<u32>();
/// Initializes a `TLV` by giving the tlv type and tlv value.
pub fn new(tlv_type: u32, tlv_value: Vec<u32>) -> Self {
let mut data = vec![0; 2];
data[Self::TYPE_OFFSET] = tlv_type;
data[Self::LEN_OFFSET] = (tlv_value.len() * size_of::<u32>()) as u32;
data.extend(tlv_value.iter());
Self { data }
}
/// Returns the type of the tlv.
pub fn tlv_type(&self) -> u32 {
self.data[Self::TYPE_OFFSET]
}
/// Returns the length of the tlv value in dword.
pub fn len(&self) -> usize {
self.data[Self::LEN_OFFSET] as usize / size_of::<u32>()
}
/// Returns whether the tlv value is empty.
pub fn is_empty(&self) -> bool {
self.data[Self::LEN_OFFSET] == 0
}
/// Returns the tlv value in slice.
pub fn value(&self) -> &[u32] {
&self.data[Self::VALUE_OFFSET..]
}
}
impl<I: SliceIndex<[u32]>> Index<I> for TLV {
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
&self.data[Self::VALUE_OFFSET..][index]
}
}
impl<I: SliceIndex<[u32]>> IndexMut<I> for TLV {
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
&mut self.data[Self::VALUE_OFFSET..][index]
}
}
impl TryFrom<Vec<u32>> for TLV {
type Error = Error;
/// Constructs a TLV from a vector with the following alsa tlv header validation:
/// 1 . tlv_buf[Self::LEN_OFFSET] should be multiple of size_of::<u32>
/// 2 . tlv_buf[Self::LEN_OFFSET] is the length of tlv value in byte and
/// should be less than the buffer length * size_of::<u32>.
fn try_from(data: Vec<u32>) -> Result<Self> {
if data.len() < 2 {
return Err(Error::InvalidTLV);
}
if data[Self::LEN_OFFSET] % size_of::<u32>() as u32 != 0 {
return Err(Error::InvalidTLV);
}
if data[Self::LEN_OFFSET] / size_of::<u32>() as u32
> data[Self::VALUE_OFFSET..].len() as u32
{
return Err(Error::InvalidTLV);
}
Ok(Self { data })
}
}
impl Into<Vec<u32>> for TLV {
/// Returns the raw tlv data buffer (including the tlv header).
fn into(self) -> Vec<u32> {
self.data
}
}
/// `ControlTLV` supports read and write of the alsa TLV byte controls.
pub struct ControlTLV<'a> {
handle: &'a mut Ctl,
id: ElemId,
}
impl<'a> ControlTLV<'a> {
/// Called by `Card` to create a `ControlTLV`.
pub fn new(handle: &'a mut Ctl, id: ElemId) -> Result<Self> {
let info = ElemInfo::new(handle, &id)?;
if info.count() % size_of::<u32>() != 0 {
return Err(Error::InvalidTLVSize(id.name()?.to_owned(), info.count()));
}
match info.elem_type()? {
ElemType::Bytes => Ok(Self { handle, id }),
_ => Err(Error::InvalidTLVType(
id.name()?.to_owned(),
info.elem_type()?,
)),
}
}
/// Reads data from the byte control by `snd_ctl_elem_tlv_read`
///
/// #
/// # Errors
///
/// * If it fails to read from the control.
pub fn load(&mut self) -> Result<TLV> {
if !ElemInfo::new(self.handle, &self.id)?.tlv_readable() {
return Err(Error::TLVNotReadable);
}
let tlv_size = ElemInfo::new(self.handle, &self.id)?.count() + TLV::TLV_HEADER_SIZE_BYTES;
let mut tlv_buf = vec![0; tlv_size / size_of::<u32>()];
// Safe because handle.as_mut_ptr() is a valid *mut snd_ctl_t, id_as_ptr is valid and
// tlv_buf.as_mut_ptr() is also valid.
let rc = unsafe {
alsa_sys::snd_ctl_elem_tlv_read(
self.handle.as_mut_ptr(),
self.id.as_ptr(),
tlv_buf.as_mut_ptr(),
tlv_size as u32,
)
};
if rc < 0 {
return Err(Error::TLVReadFailed(rc));
}
Ok(TLV::try_from(tlv_buf)?)
}
/// Writes to the byte control by `snd_ctl_elem_tlv_write`
///
/// # Results
///
/// * `changed` - false on success.
/// - true on success when value was changed.
/// #
/// # Errors
///
/// * If it fails to write to the control.
pub fn save(&mut self, tlv: TLV) -> Result<bool> {
if !ElemInfo::new(self.handle, &self.id)?.tlv_writable() {
return Err(Error::TLVNotReadable);
}
// Safe because handle.as_mut_ptr() is a valid *mut snd_ctl_t, id_as_ptr is valid and
// tlv.as_mut_ptr() is also valid.
let rc = unsafe {
alsa_sys::snd_ctl_elem_tlv_write(
self.handle.as_mut_ptr(),
self.id.as_ptr(),
Into::<Vec<u32>>::into(tlv).as_mut_ptr(),
)
};
if rc < 0 {
return Err(Error::TVLWriteFailed(rc));
}
Ok(rc > 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tlv_try_from_raw_vec() {
let tlv_buf = vec![0, 12, 2, 3, 4];
assert!(TLV::try_from(tlv_buf).is_ok());
}
#[test]
fn test_tlv_length_is_not_multiple_of_sizeof_int() {
// Invalid tlv length in data[Self::LEN_OFFSET].
let tlv_buf = vec![0, 1, 2, 3, 4];
assert_eq!(TLV::try_from(tlv_buf).unwrap_err(), Error::InvalidTLV);
}
#[test]
fn test_tlv_length_larger_than_buff_size() {
// Invalid tlv length in data[Self::LEN_OFFSET].
let tlv_buf = vec![0, 16, 2, 3, 4];
assert_eq!(TLV::try_from(tlv_buf).unwrap_err(), Error::InvalidTLV);
}
#[test]
fn test_tlv_length_less_than_two() {
// tlv buffer length < 2
let tlv_buf = vec![0];
assert_eq!(TLV::try_from(tlv_buf).unwrap_err(), Error::InvalidTLV);
}
#[test]
fn test_tlv_length_equal_two() {
// tlv buffer size = 2.
let tlv_buf = vec![0, 0];
assert!(TLV::try_from(tlv_buf).is_ok());
}
}