diff options
Diffstat (limited to 'sound_card_init/dsm')
-rw-r--r-- | sound_card_init/dsm/Cargo.lock | 229 | ||||
-rw-r--r-- | sound_card_init/dsm/Cargo.toml | 15 | ||||
-rw-r--r-- | sound_card_init/dsm/src/datastore.rs | 72 | ||||
-rw-r--r-- | sound_card_init/dsm/src/error.rs | 128 | ||||
-rw-r--r-- | sound_card_init/dsm/src/lib.rs | 335 | ||||
-rw-r--r-- | sound_card_init/dsm/src/utils.rs | 82 | ||||
-rw-r--r-- | sound_card_init/dsm/src/vpd.rs | 41 | ||||
-rw-r--r-- | sound_card_init/dsm/src/zero_player.rs | 209 |
8 files changed, 0 insertions, 1111 deletions
diff --git a/sound_card_init/dsm/Cargo.lock b/sound_card_init/dsm/Cargo.lock deleted file mode 100644 index 411c7527..00000000 --- a/sound_card_init/dsm/Cargo.lock +++ /dev/null @@ -1,229 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "alsa-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644d308f5822c2b39fba5a6d850f74c208bf73c61d1d2dfad62505d6960e4977" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "assertions" -version = "0.1.0" - -[[package]] -name = "audio_streams" -version = "0.1.0" -dependencies = [ - "sync", - "sys_util", -] - -[[package]] -name = "cras-sys" -version = "0.1.0" -dependencies = [ - "audio_streams", - "data_model", -] - -[[package]] -name = "cros_alsa" -version = "0.1.0" -dependencies = [ - "alsa-sys", - "libc", - "remain", - "sys_util", -] - -[[package]] -name = "data_model" -version = "0.1.0" -dependencies = [ - "assertions", -] - -[[package]] -name = "dtoa" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" - -[[package]] -name = "libc" -version = "0.2.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" - -[[package]] -name = "libcras" -version = "0.1.0" -dependencies = [ - "audio_streams", - "cras-sys", - "data_model", - "libc", - "sys_util", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" - -[[package]] -name = "max98390d" -version = "0.1.0" -dependencies = [ - "audio_streams", - "cros_alsa", - "libcras", - "remain", - "serde", - "serde_yaml", - "sound_card_util", - "sys_util", -] - -[[package]] -name = "pkg-config" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" - -[[package]] -name = "poll_token_derive" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "remain" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c861227fc40c8da6fdaa3d58144ac84c0537080a43eb1d7d45c28f88dcb888" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_yaml" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35" -dependencies = [ - "dtoa", - "linked-hash-map", - "serde", - "yaml-rust", -] - -[[package]] -name = "sound_card_util" -version = "0.1.0" -dependencies = [ - "cros_alsa", - "remain", - "sys_util", -] - -[[package]] -name = "syn" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "sync" -version = "0.1.0" - -[[package]] -name = "sys_util" -version = "0.1.0" -dependencies = [ - "data_model", - "libc", - "poll_token_derive", - "sync", - "syscall_defines", - "tempfile", -] - -[[package]] -name = "syscall_defines" -version = "0.1.0" - -[[package]] -name = "tempfile" -version = "3.0.7" -dependencies = [ - "libc", -] - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" - -[[package]] -name = "yaml-rust" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" -dependencies = [ - "linked-hash-map", -] diff --git a/sound_card_init/dsm/Cargo.toml b/sound_card_init/dsm/Cargo.toml deleted file mode 100644 index 280896d2..00000000 --- a/sound_card_init/dsm/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "dsm" -version = "0.1.0" -authors = ["The Chromium OS Authors"] -edition = "2018" -description = "The boot time calibration logic for smart amp" - -[dependencies] -cros_alsa = "*" -audio_streams = "*" -libcras = "*" -remain = "0.2.1" -serde = { version = "1.0", features = ["derive"]} -serde_yaml = "0.8.11" -sys_util = "*"
\ No newline at end of file diff --git a/sound_card_init/dsm/src/datastore.rs b/sound_card_init/dsm/src/datastore.rs deleted file mode 100644 index f0180cc2..00000000 --- a/sound_card_init/dsm/src/datastore.rs +++ /dev/null @@ -1,72 +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. -use std::fs::{remove_file, File}; -use std::io::{prelude::*, BufReader, BufWriter}; -use std::path::PathBuf; - -use serde::{Deserialize, Serialize}; -use sys_util::info; - -use crate::error::{Error, Result}; - -/// `Datastore`, which stores and reads calibration values in yaml format. -#[derive(Debug, Deserialize, Serialize, Copy, Clone)] -pub enum Datastore { - /// Indicates using values in VPD. - UseVPD, - DSM { - rdc: i32, - temp: i32, - }, -} - -impl Datastore { - /// The dir of datastore. - pub const DATASTORE_DIR: &'static str = "/var/lib/sound_card_init"; - - /// Creates a `Datastore` and initializes its fields from the datastore file. - pub fn from_file(snd_card: &str, channel: usize) -> Result<Datastore> { - let path = Self::path(snd_card, channel); - let reader = - BufReader::new(File::open(&path).map_err(|e| Error::FileIOFailed(path.to_owned(), e))?); - let datastore: Datastore = - serde_yaml::from_reader(reader).map_err(|e| Error::SerdeError(path.to_owned(), e))?; - Ok(datastore) - } - - /// Saves a `Datastore` to file. - pub fn save(&self, snd_card: &str, channel: usize) -> Result<()> { - let path = Self::path(snd_card, channel); - - let mut writer = BufWriter::new( - File::create(&path).map_err(|e| Error::FileIOFailed(path.to_owned(), e))?, - ); - writer - .write( - serde_yaml::to_string(self) - .map_err(|e| Error::SerdeError(path.to_owned(), e))? - .as_bytes(), - ) - .map_err(|e| Error::FileIOFailed(path.to_owned(), e))?; - writer - .flush() - .map_err(|e| Error::FileIOFailed(path.to_owned(), e))?; - info!("update Datastore {}: {:?}", path.to_string_lossy(), self); - Ok(()) - } - - /// Deletes the datastore file. - pub fn delete(snd_card: &str, channel: usize) -> Result<()> { - let path = Self::path(snd_card, channel); - remove_file(&path).map_err(|e| Error::FileIOFailed(path.to_owned(), e))?; - info!("datastore: {:#?} is deleted.", path); - Ok(()) - } - - fn path(snd_card: &str, channel: usize) -> PathBuf { - PathBuf::from(Self::DATASTORE_DIR) - .join(snd_card) - .join(format!("calib_{}", channel)) - } -} diff --git a/sound_card_init/dsm/src/error.rs b/sound_card_init/dsm/src/error.rs deleted file mode 100644 index 4b6e8dc2..00000000 --- a/sound_card_init/dsm/src/error.rs +++ /dev/null @@ -1,128 +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. -use std::any::Any; -use std::error; -use std::fmt; -use std::io; -use std::num::ParseIntError; -use std::path::PathBuf; -use std::sync::PoisonError; -use std::time; - -use remain::sorted; - -use crate::CalibData; - -pub type Result<T> = std::result::Result<T, Error>; - -#[sorted] -#[derive(Debug)] -pub enum Error { - AlsaCardError(cros_alsa::CardError), - AlsaControlError(cros_alsa::ControlError), - AlsaControlTLVError(cros_alsa::ControlTLVError), - CalibrationTimeout, - CrasClientFailed(libcras::Error), - DeserializationFailed(String, serde_yaml::Error), - DSMParamUpdateFailed(cros_alsa::ControlTLVError), - FileIOFailed(PathBuf, io::Error), - InternalSpeakerNotFound, - InvalidDatastore, - InvalidDSMParam, - InvalidShutDownTime, - InvalidTemperature(f32), - LargeCalibrationDiff(CalibData), - MissingDSMParam, - MutexPoisonError, - NewPlayStreamFailed(libcras::BoxError), - NextPlaybackBufferFailed(libcras::BoxError), - PlaybackFailed(io::Error), - SerdeError(PathBuf, serde_yaml::Error), - StartPlaybackTimeout, - SystemTimeError(time::SystemTimeError), - UnsupportedSoundCard(String), - VPDParseFailed(String, ParseIntError), - WorkerPanics(Box<dyn Any + Send + 'static>), - ZeroPlayerIsNotRunning, - ZeroPlayerIsRunning, -} - -impl PartialEq for Error { - // Implement eq for more Error when needed. - fn eq(&self, other: &Error) -> bool { - match (self, other) { - (Error::InvalidDSMParam, Error::InvalidDSMParam) => true, - _ => false, - } - } -} - -impl From<cros_alsa::CardError> for Error { - fn from(err: cros_alsa::CardError) -> Error { - Error::AlsaCardError(err) - } -} - -impl From<cros_alsa::ControlError> for Error { - fn from(err: cros_alsa::ControlError) -> Error { - Error::AlsaControlError(err) - } -} - -impl From<cros_alsa::ControlTLVError> for Error { - fn from(err: cros_alsa::ControlTLVError) -> Error { - Error::AlsaControlTLVError(err) - } -} - -impl<T> From<PoisonError<T>> for Error { - fn from(_: PoisonError<T>) -> Error { - Error::MutexPoisonError - } -} - -impl error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - match self { - AlsaCardError(e) => write!(f, "AlsaCardError: {}", e), - AlsaControlError(e) => write!(f, "AlsaControlError: {}", e), - AlsaControlTLVError(e) => write!(f, "AlsaControlTLVError: {}", e), - CalibrationTimeout => write!(f, "calibration is not finished in time"), - DSMParamUpdateFailed(e) => write!(f, "failed to update DsmParam, err: {}", e), - CrasClientFailed(e) => write!(f, "failed to create cras client: {}", e), - DeserializationFailed(file_path, e) => { - write!(f, "failed to parse {}: {}", file_path, e) - } - FileIOFailed(file_path, e) => write!(f, "{:#?}: {}", file_path, e), - InvalidShutDownTime => write!(f, "invalid shutdown time"), - InternalSpeakerNotFound => write!(f, "internal speaker is not found in cras"), - InvalidTemperature(temp) => write!( - f, - "invalid calibration temperature: {}, and there is no datastore", - temp - ), - InvalidDatastore => write!(f, "invalid datastore format"), - InvalidDSMParam => write!(f, "invalid dsm param from kcontrol"), - LargeCalibrationDiff(calib) => { - write!(f, "calibration difference is too large, calib: {:?}", calib) - } - MissingDSMParam => write!(f, "missing dsm_param.bin"), - MutexPoisonError => write!(f, "mutex is poisoned"), - NewPlayStreamFailed(e) => write!(f, "{}", e), - NextPlaybackBufferFailed(e) => write!(f, "{}", e), - PlaybackFailed(e) => write!(f, "{}", e), - SerdeError(file_path, e) => write!(f, "{:?}: {}", file_path, e), - StartPlaybackTimeout => write!(f, "playback is not started in time"), - SystemTimeError(e) => write!(f, "{}", e), - UnsupportedSoundCard(name) => write!(f, "unsupported sound card: {}", name), - VPDParseFailed(file_path, e) => write!(f, "failed to parse vpd {}: {}", file_path, e), - WorkerPanics(e) => write!(f, "run_play_zero_worker panics: {:#?}", e), - ZeroPlayerIsNotRunning => write!(f, "zero player is not running"), - ZeroPlayerIsRunning => write!(f, "zero player is running"), - } - } -} diff --git a/sound_card_init/dsm/src/lib.rs b/sound_card_init/dsm/src/lib.rs deleted file mode 100644 index 0b3ec64c..00000000 --- a/sound_card_init/dsm/src/lib.rs +++ /dev/null @@ -1,335 +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. -//! `dsm` crate implements the required initialization workflows for smart amps. - -mod datastore; -mod error; -pub mod utils; -mod vpd; -mod zero_player; - -use std::{ - thread, - time::{Duration, SystemTime, UNIX_EPOCH}, -}; - -use libcras::{CrasClient, CrasNodeType}; -use sys_util::{error, info}; - -use crate::datastore::Datastore; -pub use crate::error::{Error, Result}; -use crate::utils::{run_time, shutdown_time}; -use crate::vpd::VPD; -pub use crate::zero_player::ZeroPlayer; - -#[derive(Debug, Clone, Copy)] -/// `CalibData` represents the calibration data. -pub struct CalibData { - /// The DC resistance of the speaker is DSM unit. - pub rdc: i32, - /// The ambient temperature in celsius unit at which the rdc is measured. - pub temp: f32, -} - -/// `TempConverter` converts the temperature value between celsius and unit in VPD::dsm_calib_temp. -pub struct TempConverter { - vpd_to_celsius: fn(i32) -> f32, - celsius_to_vpd: fn(f32) -> i32, -} - -impl Default for TempConverter { - fn default() -> Self { - let vpd_to_celsius = |x: i32| x as f32; - let celsius_to_vpd = |x: f32| x.round() as i32; - Self { - vpd_to_celsius, - celsius_to_vpd, - } - } -} - -impl TempConverter { - /// Creates a `TempConverter` - /// - /// # Arguments - /// - /// * `vpd_to_celsius` - function to convert VPD::dsm_calib_temp to celsius unit` - /// * `celsius_to_vpd` - function to convert celsius unit to VPD::dsm_calib_temp` - /// # Results - /// - /// * `TempConverter` - it converts the temperature value between celsius and unit in VPD::dsm_calib_temp. - pub fn new(vpd_to_celsius: fn(i32) -> f32, celsius_to_vpd: fn(f32) -> i32) -> Self { - Self { - vpd_to_celsius, - celsius_to_vpd, - } - } -} - -/// `SpeakerStatus` are the possible return results of -/// DSM::check_speaker_over_heated_workflow. -pub enum SpeakerStatus { - ///`SpeakerStatus::Cold` means the speakers are not overheated and the Amp can - /// trigger the boot time calibration. - Cold, - /// `SpeakerStatus::Hot(Vec<CalibData>)` means the speakers may be too hot for calibration. - /// The boot time calibration should be skipped and the Amp should use the previous - /// calibration values returned by the enum. - Hot(Vec<CalibData>), -} - -/// `DSM`, which implements the required initialization workflows for smart amps. -pub struct DSM { - snd_card: String, - num_channels: usize, - temp_converter: TempConverter, - rdc_to_ohm: fn(i32) -> f32, - temp_upper_limit: f32, - temp_lower_limit: f32, -} - -impl DSM { - const SPEAKER_COOL_DOWN_TIME: Duration = Duration::from_secs(180); - const CALI_ERROR_UPPER_LIMIT: f32 = 0.3; - const CALI_ERROR_LOWER_LIMIT: f32 = 0.03; - - /// Creates a `DSM` - /// - /// # Arguments - /// - /// * `snd_card` - `sound card name`. - /// * `num_channels` - `number of channels`. - /// * `rdc_to_ohm` - `fn(rdc: i32) -> f32 to convert the CalibData::rdc to ohm unit`. - /// * `temp_upper_limit` - the high limit of the valid ambient temperature in dsm unit. - /// * `temp_lower_limit` - the low limit of the valid ambient temperature in dsm unit. - /// - /// # Results - /// - /// * `DSM` - It implements the required initialization workflows for smart amps. - pub fn new( - snd_card: &str, - num_channels: usize, - rdc_to_ohm: fn(i32) -> f32, - temp_upper_limit: f32, - temp_lower_limit: f32, - ) -> Self { - Self { - snd_card: snd_card.to_owned(), - num_channels, - rdc_to_ohm, - temp_converter: TempConverter::default(), - temp_upper_limit, - temp_lower_limit, - } - } - - /// Sets self.temp_converter to the given temp_converter. - /// - /// # Arguments - /// - /// * `temp_converter` - the convert function to use. - pub fn set_temp_converter(&mut self, temp_converter: TempConverter) { - self.temp_converter = temp_converter; - } - - /// Checks whether the speakers are overheated or not according to the previous shutdown time. - /// The boot time calibration should be skipped when the speakers may be too hot - /// and the Amp should use the previous calibration value returned by the - /// SpeakerStatus::Hot(Vec<CalibData>). - /// - /// # Results - /// - /// * `SpeakerStatus::Cold` - which means the speakers are not overheated and the Amp can - /// trigger the boot time calibration. - /// * `SpeakerStatus::Hot(Vec<CalibData>)` - when the speakers may be too hot. The boot - /// time calibration should be skipped and the Amp should use the previous calibration values - /// returned by the enum. - /// - /// # Errors - /// - /// * The speakers are overheated and there are no previous calibration values stored. - /// * Cannot determine whether the speakers are overheated as previous shutdown time record is - /// invalid. - pub fn check_speaker_over_heated_workflow(&self) -> Result<SpeakerStatus> { - if self.is_first_boot() { - return Ok(SpeakerStatus::Cold); - } - match self.is_speaker_over_heated() { - Ok(overheated) => { - if overheated { - let calib: Vec<CalibData> = (0..self.num_channels) - .map(|ch| -> Result<CalibData> { self.get_previous_calibration_value(ch) }) - .collect::<Result<Vec<CalibData>>>()?; - info!("the speakers are hot, the boot time calibration should be skipped"); - return Ok(SpeakerStatus::Hot(calib)); - } - Ok(SpeakerStatus::Cold) - } - Err(err) => { - // We cannot assume the speakers are not replaced or not overheated - // when the shutdown time file is invalid; therefore we can not use the datastore - // value anymore and we can not trigger boot time calibration. - for ch in 0..self.num_channels { - if let Err(e) = Datastore::delete(&self.snd_card, ch) { - error!("error delete datastore: {}", e); - } - } - Err(err) - } - } - } - - /// Decides a good calibration value and updates the stored value according to the following - /// logic: - /// * Returns the previous value if the ambient temperature is not within a valid range. - /// * Returns Error::LargeCalibrationDiff if rdc difference is larger than - /// `CALI_ERROR_UPPER_LIMIT`. - /// * Returns the previous value if the rdc difference is smaller than `CALI_ERROR_LOWER_LIMIT`. - /// * Returns the boot time calibration value and updates the datastore value if the rdc. - /// difference is between `CALI_ERROR_UPPER_LIMIT` and `CALI_ERROR_LOWER_LIMIT`. - /// - /// # Arguments - /// - /// * `card` - `&Card`. - /// * `channel` - `channel number`. - /// * `calib_data` - `boot time calibrated data`. - /// - /// # Results - /// - /// * `CalibData` - the calibration data to be applied according to the deciding logic. - /// - /// # Errors - /// - /// * VPD does not exist. - /// * rdc difference is larger than `CALI_ERROR_UPPER_LIMIT`. - /// * Failed to update Datastore. - pub fn decide_calibration_value_workflow( - &self, - channel: usize, - calib_data: CalibData, - ) -> Result<CalibData> { - if calib_data.temp < self.temp_lower_limit || calib_data.temp > self.temp_upper_limit { - info!("invalid temperature: {}.", calib_data.temp); - return self - .get_previous_calibration_value(channel) - .map_err(|_| Error::InvalidTemperature(calib_data.temp)); - } - let (datastore_exist, previous_calib) = match self.get_previous_calibration_value(channel) { - Ok(previous_calib) => (true, previous_calib), - Err(e) => { - info!("{}, use vpd as previous calibration value", e); - (false, self.get_vpd_calibration_value(channel)?) - } - }; - - let diff = { - let calib_rdc_ohm = (self.rdc_to_ohm)(calib_data.rdc); - let previous_rdc_ohm = (self.rdc_to_ohm)(previous_calib.rdc); - (calib_rdc_ohm - previous_rdc_ohm) / previous_rdc_ohm - }; - if diff > Self::CALI_ERROR_UPPER_LIMIT { - Err(Error::LargeCalibrationDiff(calib_data)) - } else if diff < Self::CALI_ERROR_LOWER_LIMIT { - if !datastore_exist { - Datastore::UseVPD.save(&self.snd_card, channel)?; - } - Ok(previous_calib) - } else { - Datastore::DSM { - rdc: calib_data.rdc, - temp: (self.temp_converter.celsius_to_vpd)(calib_data.temp), - } - .save(&self.snd_card, channel)?; - Ok(calib_data) - } - } - - /// Gets the calibration values from vpd. - /// - /// # Results - /// - /// * `Vec<CalibData>` - the calibration values in vpd. - /// - /// # Errors - /// - /// * Failed to read vpd. - pub fn get_all_vpd_calibration_value(&self) -> Result<Vec<CalibData>> { - (0..self.num_channels) - .map(|ch| self.get_vpd_calibration_value(ch)) - .collect::<Result<Vec<_>>>() - } - - /// Blocks until the internal speakers are ready. - /// - /// # Errors - /// - /// * Failed to wait the internal speakers to be ready. - pub fn wait_for_speakers_ready(&self) -> Result<()> { - let find_speaker = || -> Result<()> { - let cras_client = CrasClient::new().map_err(Error::CrasClientFailed)?; - let _node = cras_client - .output_nodes() - .find(|node| node.node_type == CrasNodeType::CRAS_NODE_TYPE_INTERNAL_SPEAKER) - .ok_or(Error::InternalSpeakerNotFound)?; - Ok(()) - }; - // TODO(b/155007305): Implement cras_client.wait_node_change and use it here. - const RETRY: usize = 3; - const RETRY_INTERVAL: Duration = Duration::from_millis(500); - for _ in 0..RETRY { - match find_speaker() { - Ok(_) => return Ok(()), - Err(e) => error!("retry on finding speaker: {}", e), - }; - thread::sleep(RETRY_INTERVAL); - } - Err(Error::InternalSpeakerNotFound) - } - - fn is_first_boot(&self) -> bool { - !run_time::exists(&self.snd_card) - } - - // If (Current time - the latest CRAS shutdown time) < cool_down_time, we assume that - // the speakers may be overheated. - fn is_speaker_over_heated(&self) -> Result<bool> { - let last_run = run_time::from_file(&self.snd_card)?; - let last_shutdown = shutdown_time::from_file()?; - if last_shutdown < last_run { - return Err(Error::InvalidShutDownTime); - } - - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(Error::SystemTimeError)?; - - let elapsed = now - .checked_sub(last_shutdown) - .ok_or(Error::InvalidShutDownTime)?; - - if elapsed < Self::SPEAKER_COOL_DOWN_TIME { - return Ok(true); - } - Ok(false) - } - - fn get_previous_calibration_value(&self, ch: usize) -> Result<CalibData> { - let sci_calib = Datastore::from_file(&self.snd_card, ch)?; - match sci_calib { - Datastore::UseVPD => self.get_vpd_calibration_value(ch), - Datastore::DSM { rdc, temp } => Ok(CalibData { - rdc, - temp: (self.temp_converter.vpd_to_celsius)(temp), - }), - } - } - - fn get_vpd_calibration_value(&self, channel: usize) -> Result<CalibData> { - let vpd = VPD::new(channel)?; - Ok(CalibData { - rdc: vpd.dsm_calib_r0, - temp: (self.temp_converter.vpd_to_celsius)(vpd.dsm_calib_temp), - }) - } -} diff --git a/sound_card_init/dsm/src/utils.rs b/sound_card_init/dsm/src/utils.rs deleted file mode 100644 index 64f6c972..00000000 --- a/sound_card_init/dsm/src/utils.rs +++ /dev/null @@ -1,82 +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. -//! It contains common utils shared within sound_card_init. -#![deny(missing_docs)] - -use std::fs::File; -use std::io::{prelude::*, BufReader, BufWriter}; -use std::path::PathBuf; -use std::time::Duration; - -use crate::datastore::Datastore; -use crate::error::{Error, Result}; - -fn duration_from_file(path: &PathBuf) -> Result<Duration> { - let reader = - BufReader::new(File::open(&path).map_err(|e| Error::FileIOFailed(path.clone(), e))?); - serde_yaml::from_reader(reader).map_err(|e| Error::SerdeError(path.clone(), e)) -} - -/// The utils to parse CRAS shutdown time file. -pub mod shutdown_time { - use super::*; - // The path of CRAS shutdown time file. - const SHUTDOWN_TIME_FILE: &str = "/var/lib/cras/stop"; - - /// Reads the unix time from CRAS shutdown time file. - pub fn from_file() -> Result<Duration> { - duration_from_file(&PathBuf::from(SHUTDOWN_TIME_FILE)) - } -} - -/// The utils to create and parse sound_card_init run time file. -pub mod run_time { - use std::time::SystemTime; - - use super::*; - // The filename of sound_card_init run time file. - const RUN_TIME_FILE: &str = "run"; - - /// Returns the sound_card_init run time file existence. - pub fn exists(snd_card: &str) -> bool { - run_time_file(snd_card).exists() - } - - /// Reads the unix time from sound_card_init run time file. - pub fn from_file(snd_card: &str) -> Result<Duration> { - duration_from_file(&run_time_file(snd_card)) - } - - /// Saves the current unix time to sound_card_init run time file. - pub fn now_to_file(snd_card: &str) -> Result<()> { - match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { - Ok(t) => to_file(snd_card, t), - Err(e) => Err(Error::SystemTimeError(e)), - } - } - - /// Saves the unix time to sound_card_init run time file. - pub fn to_file(snd_card: &str, duration: Duration) -> Result<()> { - let path = run_time_file(snd_card); - let mut writer = - BufWriter::new(File::create(&path).map_err(|e| Error::FileIOFailed(path.clone(), e))?); - writer - .write_all( - serde_yaml::to_string(&duration) - .map_err(|e| Error::SerdeError(path.clone(), e))? - .as_bytes(), - ) - .map_err(|e| Error::FileIOFailed(path.clone(), e))?; - writer - .flush() - .map_err(|e| Error::FileIOFailed(path.clone(), e))?; - Ok(()) - } - - fn run_time_file(snd_card: &str) -> PathBuf { - PathBuf::from(Datastore::DATASTORE_DIR) - .join(snd_card) - .join(RUN_TIME_FILE) - } -} diff --git a/sound_card_init/dsm/src/vpd.rs b/sound_card_init/dsm/src/vpd.rs deleted file mode 100644 index b00864cc..00000000 --- a/sound_card_init/dsm/src/vpd.rs +++ /dev/null @@ -1,41 +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. -use std::fs::File; -use std::io::prelude::*; -use std::io::BufReader; -use std::path::PathBuf; - -use crate::error::{Error, Result}; - -const VPD_DIR: &str = "/sys/firmware/vpd/ro/vpdfile"; - -/// `VPD`, which represents the amplifier factory calibration values. -#[derive(Default, Debug)] -pub struct VPD { - pub dsm_calib_r0: i32, - pub dsm_calib_temp: i32, -} - -impl VPD { - /// Creates a `VPD` and initializes its fields from VPD_DIR/dsm_calib_r0_{channel}. - /// # Arguments - /// - /// * `channel` - channel number. - pub fn new(channel: usize) -> Result<VPD> { - let mut vpd: VPD = Default::default(); - vpd.dsm_calib_r0 = read_vpd_files(&format!("dsm_calib_r0_{}", channel))?; - vpd.dsm_calib_temp = read_vpd_files(&format!("dsm_calib_temp_{}", channel))?; - Ok(vpd) - } -} - -fn read_vpd_files(file: &str) -> Result<i32> { - let path = PathBuf::from(VPD_DIR).with_file_name(file); - let io_err = |e| Error::FileIOFailed(path.to_owned(), e); - let mut reader = BufReader::new(File::open(&path).map_err(io_err)?); - let mut line = String::new(); - reader.read_line(&mut line).map_err(io_err)?; - line.parse::<i32>() - .map_err(|e| Error::VPDParseFailed(path.to_string_lossy().to_string(), e)) -} diff --git a/sound_card_init/dsm/src/zero_player.rs b/sound_card_init/dsm/src/zero_player.rs deleted file mode 100644 index 441f7ffa..00000000 --- a/sound_card_init/dsm/src/zero_player.rs +++ /dev/null @@ -1,209 +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. -use std::io::Write; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Condvar, Mutex}; -use std::thread; -use std::thread::JoinHandle; -use std::time::Duration; - -use audio_streams::SampleFormat; -use libcras::{CrasClient, CrasNodeType}; -use sys_util::error; - -use crate::error::{Error, Result}; - -/// `ZeroPlayer` provides the functionality to play zeros sample in the background thread. -#[derive(Default)] -pub struct ZeroPlayer { - thread_info: Option<PlayZeroWorkerInfo>, -} - -impl Drop for ZeroPlayer { - fn drop(&mut self) { - if self.thread_info.is_some() { - if let Err(e) = self.stop() { - error!("{}", e); - } - } - } -} - -impl ZeroPlayer { - /// It takes about 400 ms to get CRAS_NODE_TYPE_INTERNAL_SPEAKER during the boot time. - const TIMEOUT: Duration = Duration::from_millis(1000); - - /// Returns whether the ZeroPlayer is running. - pub fn running(&self) -> bool { - self.thread_info.is_some() - } - - /// Starts to play zeros for at most `max_playback_time`. - /// This function blocks and returns until playback has started for `min_playback_time`. - /// This function must be called when self.running() returns false. - /// - /// # Arguments - /// - /// * `min_playback_time` - It blocks and returns until playback has started for - /// `min_playback_time`. - /// - /// # Errors - /// - /// * If it's called when the `ZeroPlayer` is already running. - /// * Failed to find internal speakers. - /// * Failed to start the background thread. - pub fn start(&mut self, min_playback_time: Duration) -> Result<()> { - if self.running() { - return Err(Error::ZeroPlayerIsRunning); - } - self.thread_info = Some(PlayZeroWorkerInfo::new(min_playback_time)); - if let Some(thread_info) = &mut self.thread_info { - // Block until playback of zeros has started for min_playback_time or timeout. - let (lock, cvar) = &*(thread_info.ready); - let result = cvar.wait_timeout_while( - lock.lock()?, - min_playback_time + ZeroPlayer::TIMEOUT, - |&mut is_ready| !is_ready, - )?; - if result.1.timed_out() { - return Err(Error::StartPlaybackTimeout); - } - } - Ok(()) - } - - /// Stops playing zeros in the background thread. - /// This function must be called when self.running() returns true. - /// - /// # Errors - /// - /// * If it's called again when the `ZeroPlayer` is not running. - /// * Failed to play zeros to internal speakers via CRAS client. - /// * Failed to join the background thread. - pub fn stop(&mut self) -> Result<()> { - match self.thread_info.take() { - Some(mut thread_info) => Ok(thread_info.destroy()?), - None => Err(Error::ZeroPlayerIsNotRunning), - } - } -} - -// Audio thread book-keeping data -struct PlayZeroWorkerInfo { - thread: Option<JoinHandle<Result<()>>>, - // Uses `thread_run` to notify the background thread to stop. - thread_run: Arc<AtomicBool>, - // The background thread uses `ready` to notify the main thread that playback - // of zeros has started for min_playback_time. - ready: Arc<(Mutex<bool>, Condvar)>, -} - -impl Drop for PlayZeroWorkerInfo { - fn drop(&mut self) { - if let Err(e) = self.destroy() { - error!("{}", e); - } - } -} - -impl PlayZeroWorkerInfo { - // Spawns the PlayZeroWorker. - fn new(min_playback_time: Duration) -> Self { - let thread_run = Arc::new(AtomicBool::new(false)); - let ready = Arc::new((Mutex::new(false), Condvar::new())); - let mut worker = PlayZeroWorker::new(min_playback_time, thread_run.clone(), ready.clone()); - Self { - thread: Some(thread::spawn(move || -> Result<()> { - worker.run()?; - Ok(()) - })), - thread_run, - ready, - } - } - - // Joins the PlayZeroWorker. - fn destroy(&mut self) -> Result<()> { - self.thread_run.store(false, Ordering::Relaxed); - if let Some(handle) = self.thread.take() { - let res = handle.join().map_err(Error::WorkerPanics)?; - return match res { - Err(e) => Err(e), - Ok(_) => Ok(()), - }; - } - Ok(()) - } -} - -struct PlayZeroWorker { - min_playback_time: Duration, - // Uses `thread_run` to notify the background thread to stop. - thread_run: Arc<AtomicBool>, - // The background thread uses `ready` to notify the main thread that playback - // of zeros has started for min_playback_time. - ready: Arc<(Mutex<bool>, Condvar)>, -} - -impl PlayZeroWorker { - const FRAMES_PER_BUFFER: usize = 256; - const FRAME_RATE: u32 = 48000; - const NUM_CHANNELS: usize = 2; - const FORMAT: SampleFormat = SampleFormat::S16LE; - - fn new( - min_playback_time: Duration, - thread_run: Arc<AtomicBool>, - ready: Arc<(Mutex<bool>, Condvar)>, - ) -> Self { - Self { - min_playback_time, - thread_run, - ready, - } - } - - fn run(&mut self) -> Result<()> { - let mut cras_client = CrasClient::new().map_err(Error::CrasClientFailed)?; - // TODO(b/155007305): Implement cras_client.wait_node_change and use it here. - let node = cras_client - .output_nodes() - .find(|node| node.node_type == CrasNodeType::CRAS_NODE_TYPE_INTERNAL_SPEAKER) - .ok_or(Error::InternalSpeakerNotFound)?; - let local_buffer = - vec![0u8; Self::FRAMES_PER_BUFFER * Self::NUM_CHANNELS * Self::FORMAT.sample_bytes()]; - let min_playback_iterations = (Self::FRAME_RATE - * self.min_playback_time.as_millis() as u32) - / Self::FRAMES_PER_BUFFER as u32 - / 1000; - let (_control, mut stream) = cras_client - .new_pinned_playback_stream( - node.iodev_index, - Self::NUM_CHANNELS, - Self::FORMAT, - Self::FRAME_RATE, - Self::FRAMES_PER_BUFFER, - ) - .map_err(|e| Error::NewPlayStreamFailed(e))?; - - let mut iter = 0; - self.thread_run.store(true, Ordering::Relaxed); - while self.thread_run.load(Ordering::Relaxed) { - let mut buffer = stream - .next_playback_buffer() - .map_err(|e| Error::NextPlaybackBufferFailed(e))?; - let _write_frames = buffer.write(&local_buffer).map_err(Error::PlaybackFailed)?; - - // Notifies the main thread that playback of zeros has started for min_playback_time. - if iter == min_playback_iterations { - let (lock, cvar) = &*self.ready; - let mut is_ready = lock.lock()?; - *is_ready = true; - cvar.notify_one(); - } - iter += 1; - } - Ok(()) - } -} |