diff options
Diffstat (limited to 'cros_alsa/src/control_tlv.rs')
-rw-r--r-- | cros_alsa/src/control_tlv.rs | 313 |
1 files changed, 0 insertions, 313 deletions
diff --git a/cros_alsa/src/control_tlv.rs b/cros_alsa/src/control_tlv.rs deleted file mode 100644 index e1f35510..00000000 --- a/cros_alsa/src/control_tlv.rs +++ /dev/null @@ -1,313 +0,0 @@ -// 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()); - } -} |