summaryrefslogtreecommitdiff
path: root/sound_card_init/dsm/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sound_card_init/dsm/src/lib.rs')
-rw-r--r--sound_card_init/dsm/src/lib.rs335
1 files changed, 0 insertions, 335 deletions
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),
- })
- }
-}