blob: da7c645e54bb3c0c18e1c6bc16a14049e1e6184c [file]
#include "tlbmc/host_state/power_control.h"
#include <array>
#include <chrono> // NOLINT
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "boost/asio/steady_timer.hpp" // NOLINT
#include "g3/macros.h"
#include "thread/thread.h"
#include "time/clock.h"
#include "gbmc_sel_defs.h"
#include "gbmc_sel_pub.h"
#include <nlohmann/json.hpp>
#include "tlbmc/collector/gpio_collector.h"
#include "gpio_config.pb.h"
#include "power_control.pb.h"
namespace milotic_tlbmc {
namespace {
struct PowerStateSelInfo {
absl::string_view message_suffix;
absl::string_view state_value;
};
PowerStateSelInfo GetPowerStateSelInfo(PowerState state) {
switch (state) {
case PowerState::POWER_STATE_ON:
return {.message_suffix = "ON", .state_value = "On"};
case PowerState::POWER_STATE_OFF:
return {.message_suffix = "OFF", .state_value = "Off"};
case PowerState::POWER_STATE_WAIT_FOR_POWER_OK:
return {.message_suffix = "Wait for Power OK",
.state_value = "WaitForPowerOk"};
case PowerState::POWER_STATE_TRANSITION_TO_OFF:
return {.message_suffix = "Transition to Off",
.state_value = "TransitionToOff"};
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF:
return {.message_suffix = "Graceful Transition to Off",
.state_value = "GracefulTransitionToOff"};
case PowerState::POWER_STATE_CYCLE_OFF:
return {.message_suffix = "Cycle Off", .state_value = "CycleOff"};
case PowerState::POWER_STATE_TRANSITION_TO_CYCLE_OFF:
return {.message_suffix = "Transition to Cycle Off",
.state_value = "TransitionToCycleOff"};
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF:
return {.message_suffix = "Graceful Transition to Cycle Off",
.state_value = "GracefulTransitionToCycleOff"};
case PowerState::POWER_STATE_CHECK_FOR_WARM_RESET:
return {.message_suffix = "Check for Warm Reset",
.state_value = "CheckForWarmReset"};
default:
return {.message_suffix = "Unknown", .state_value = "Unknown"};
}
}
} // namespace
constexpr auto kDefaultTimerMap =
std::to_array<std::pair<absl::string_view, uint32_t>>(
{{"ForceOffPulseMs", 15000},
{"GracefulPowerOffS", 300},
{"PowerCycleMs", 5000},
{"PowerOKWatchdogMs", 8000},
{"PowerPulseMs", 200},
{"ResetPulseMs", 500},
{"WarmResetCheckMs", 500}});
absl::StatusOr<std::unique_ptr<PowerControlGpio>> PowerControlGpio::Create(
const Params& params,
gbmc_sel_framework::JournalSenderFunc journal_sender) {
PowerControlConfig power_control_config = params.power_control_config;
GpioCollector* gpio_collector = params.gpio_collector;
HostState host_state;
ECCLESIA_ASSIGN_OR_RETURN(
bool power_ok,
gpio_collector->GetValue(power_control_config.power_ok_gpio_name()));
LOG(INFO) << "Power OK: " << power_ok << " "
<< power_control_config.power_ok_gpio_name();
if (power_ok) {
host_state.set_power_state(PowerState::POWER_STATE_ON);
} else {
host_state.set_power_state(PowerState::POWER_STATE_OFF);
}
ECCLESIA_ASSIGN_OR_RETURN(
bool post_complete,
gpio_collector->GetValue(power_control_config.post_complete_gpio_name()));
LOG(INFO) << "Post Complete: " << post_complete << " "
<< power_control_config.post_complete_gpio_name();
if (post_complete) {
host_state.set_os_state(OSState::OS_STATE_STANDBY);
} else {
host_state.set_os_state(OSState::OS_STATE_INACTIVE);
}
std::shared_ptr<boost::asio::io_context> io_context =
std::make_shared<boost::asio::io_context>();
boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
work_guard(boost::asio::make_work_guard(*io_context));
boost::asio::steady_timer power_cycle_timer(*io_context);
boost::asio::steady_timer power_ok_watchdog_timer(*io_context);
boost::asio::steady_timer graceful_power_off_timer(*io_context);
boost::asio::steady_timer warm_reset_check_timer(*io_context);
auto thread_manager =
std::make_unique<PowerControlThreadManager>(params.clock);
thread_manager->threads.push_back(
params.thread_factory->New([io_context]() { io_context->run(); }));
thread_manager->work_guards.push_back(std::move(work_guard));
thread_manager->io_contexts.push_back(std::move(io_context));
// Update the timer map with default values if not already present.
for (const auto& [key, value] : kDefaultTimerMap) {
if (!power_control_config.timer_map().contains(key)) {
power_control_config.mutable_timer_map()->insert(
{std::string(key), value});
}
}
return absl::WrapUnique(new PowerControlGpio(
std::move(host_state), power_control_config, gpio_collector,
std::move(thread_manager), std::move(power_cycle_timer),
std::move(power_ok_watchdog_timer), std::move(graceful_power_off_timer),
std::move(warm_reset_check_timer), params.clock,
std::move(journal_sender)));
}
PowerControlGpio::PowerControlGpio(
HostState host_state, PowerControlConfig power_control_config,
GpioCollector* gpio_collector,
std::unique_ptr<PowerControlThreadManager>&& thread_manager,
boost::asio::steady_timer&& power_cycle_timer,
boost::asio::steady_timer&& power_ok_watchdog_timer,
boost::asio::steady_timer&& graceful_power_off_timer,
boost::asio::steady_timer&& warm_reset_check_timer, ecclesia::Clock* clock,
gbmc_sel_framework::JournalSenderFunc journal_sender)
: host_state_(std::move(host_state)),
power_control_config_(std::move(power_control_config)),
gpio_collector_(gpio_collector),
thread_manager_(std::move(thread_manager)),
power_cycle_timer_(std::move(power_cycle_timer)),
power_ok_watchdog_timer_(std::move(power_ok_watchdog_timer)),
graceful_power_off_timer_(std::move(graceful_power_off_timer)),
warm_reset_check_timer_(std::move(warm_reset_check_timer)),
clock_(clock),
journal_sender_(std::move(journal_sender)) {
if (clock_ != nullptr) {
last_state_change_time_ = clock_->Now();
}
}
void PowerControlGpio::PowerOkHandler(GpioEventType event) {
LOG(INFO) << "Power OK Handler: " << event << " "
<< power_control_config_.power_ok_gpio_name();
absl::MutexLock lock(host_state_mutex_);
absl::MutexLock power_state_callback_lock(power_state_callback_mutex_);
switch (event) {
// assuming asserted now
case GpioEventType::GPIO_EVENT_RISING:
for (const auto& cb : host_power_off_to_on_callbacks_) {
cb(/*setup_run=*/false);
}
switch (host_state_.power_state()) {
case PowerState::POWER_STATE_OFF:
case PowerState::POWER_STATE_CYCLE_OFF:
SetPowerState(PowerState::POWER_STATE_ON);
break;
case PowerState::POWER_STATE_WAIT_FOR_POWER_OK:
SetPowerState(PowerState::POWER_STATE_ON);
power_ok_watchdog_timer_.cancel();
break;
default:
// Do nothing
break;
}
break;
// assuming deasserted now
case GpioEventType::GPIO_EVENT_FALLING:
for (const auto& cb : host_power_on_to_off_callbacks_) {
cb(/*setup_run=*/false);
}
switch (host_state_.power_state()) {
case PowerState::POWER_STATE_CHECK_FOR_WARM_RESET:
warm_reset_check_timer_.cancel();
SetPowerState(PowerState::POWER_STATE_OFF);
break;
case PowerState::POWER_STATE_ON:
case PowerState::POWER_STATE_TRANSITION_TO_OFF:
SetPowerState(PowerState::POWER_STATE_OFF);
break;
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF:
SetPowerState(PowerState::POWER_STATE_CYCLE_OFF);
graceful_power_off_timer_.cancel();
StartPowerCycleTimer();
break;
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF:
SetPowerState(PowerState::POWER_STATE_OFF);
graceful_power_off_timer_.cancel();
break;
case PowerState::POWER_STATE_TRANSITION_TO_CYCLE_OFF:
LOG(INFO) << "Enter POWER_STATE_CYCLE_OFF";
SetPowerState(PowerState::POWER_STATE_CYCLE_OFF);
StartPowerCycleTimer();
break;
default:
// Do nothing
break;
}
break;
default:
// Do nothing
break;
}
}
void PowerControlGpio::PostCompleteHandler(GpioEventType event) {
LOG(INFO) << "Post Complete Handler: " << event << " "
<< power_control_config_.post_complete_gpio_name();
absl::MutexLock lock(host_state_mutex_);
absl::MutexLock os_state_callback_lock(os_state_callback_mutex_);
switch (event) {
// assuming asserted now
case GpioEventType::GPIO_EVENT_RISING:
host_state_.set_os_state(OSState::OS_STATE_STANDBY);
PublishSel("Host OS State: Standby (Post Complete Assert)",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{"GBMC_OS_STATE=Standby"});
for (const auto& cb : host_os_inactive_to_standby_callbacks_) {
cb(/*setup_run=*/false);
}
break;
// assuming deasserted now
case GpioEventType::GPIO_EVENT_FALLING:
host_state_.set_os_state(OSState::OS_STATE_INACTIVE);
PublishSel("Host OS State: Inactive (Post Complete Deassert)",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{"GBMC_OS_STATE=Inactive"});
for (const auto& cb : host_os_standby_to_inactive_callbacks_) {
cb(/*setup_run=*/false);
}
if (host_state_.power_state() == PowerState::POWER_STATE_ON) {
SetPowerState(PowerState::POWER_STATE_CHECK_FOR_WARM_RESET);
StartWarmResetCheckTimer();
};
break;
default:
// Do nothing
break;
}
}
void PowerControlGpio::StartPowerCycleTimer() {
power_cycle_timer_.expires_after(std::chrono::milliseconds(
power_control_config_.timer_map().at("PowerCycleMs")));
power_cycle_timer_.async_wait([this](const boost::system::error_code ec) {
if (ec) {
if (ec != boost::asio::error::operation_aborted) {
LOG(ERROR) << "Power-cycle async_wait failed: " << ec.message();
}
return;
}
LOG(INFO) << "Power-cycle timer expired";
PowerCycleTimerExpired();
});
}
void PowerControlGpio::PowerCycleTimerExpired() {
absl::MutexLock lock(host_state_mutex_);
if (host_state_.power_state() == PowerState::POWER_STATE_CYCLE_OFF) {
LOG(INFO) << "Enter POWER_STATE_WAIT_FOR_POWER_OK";
SetPowerState(PowerState::POWER_STATE_WAIT_FOR_POWER_OK);
StartPowerOKWatchdogTimer();
PowerOn();
}
}
void PowerControlGpio::StartPowerOKWatchdogTimer() {
power_ok_watchdog_timer_.expires_after(std::chrono::milliseconds(
power_control_config_.timer_map().at("PowerOKWatchdogMs")));
power_ok_watchdog_timer_.async_wait([this](
const boost::system::error_code ec) {
if (ec) {
if (ec != boost::asio::error::operation_aborted) {
LOG(ERROR) << "Power-OK watchdog async_wait failed: " << ec.message();
}
return;
}
PowerOkWatchdogTimerExpired();
});
}
void PowerControlGpio::PowerOkWatchdogTimerExpired() {
absl::MutexLock lock(host_state_mutex_);
if (host_state_.power_state() == PowerState::POWER_STATE_WAIT_FOR_POWER_OK) {
PublishSel("Power OK Watchdog Expired",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_WARNING,
{"GBMC_POWER_CONTROL_ERROR_TYPE=PowerOkWatchdogTimeout"});
SetPowerState(PowerState::POWER_STATE_OFF);
}
}
void PowerControlGpio::StartGracefulPowerOffTimer() {
graceful_power_off_timer_.expires_after(std::chrono::seconds(
power_control_config_.timer_map().at("GracefulPowerOffS")));
graceful_power_off_timer_.async_wait([this](
const boost::system::error_code ec) {
if (ec) {
if (ec != boost::asio::error::operation_aborted) {
LOG(ERROR) << "Graceful power-off async_wait failed: " << ec.message();
}
return;
}
GracefulPowerOffTimerExpired();
});
}
void PowerControlGpio::GracefulPowerOffTimerExpired() {
absl::MutexLock lock(host_state_mutex_);
switch (host_state_.power_state()) {
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF:
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF:
PublishSel("Graceful Power Off Timed Out",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_WARNING,
{"GBMC_POWER_CONTROL_ERROR_TYPE=GracefulPowerOffTimeout"});
SetPowerState(PowerState::POWER_STATE_ON);
break;
default:
// Do nothing
break;
}
}
void PowerControlGpio::StartWarmResetCheckTimer() {
warm_reset_check_timer_.expires_after(std::chrono::milliseconds(
power_control_config_.timer_map().at("WarmResetCheckMs")));
warm_reset_check_timer_.async_wait([this](
const boost::system::error_code ec) {
if (ec) {
if (ec != boost::asio::error::operation_aborted) {
LOG(ERROR) << "Warm reset check async_wait failed: " << ec.message();
}
return;
}
WarmResetCheckTimerExpired();
});
}
void PowerControlGpio::WarmResetCheckTimerExpired() {
absl::MutexLock lock(host_state_mutex_);
if (host_state_.power_state() ==
PowerState::POWER_STATE_CHECK_FOR_WARM_RESET) {
SetPowerState(PowerState::POWER_STATE_ON);
}
}
void PowerControlGpio::PowerOn() {
absl::Status status = gpio_collector_->SetActiveForDuration(
power_control_config_.power_out_gpio_name(),
absl::Milliseconds(power_control_config_.timer_map().at("PowerPulseMs")));
if (!status.ok()) {
LOG(ERROR) << "Failed to press power-on GPIO: " << status;
}
}
void PowerControlGpio::ForcePowerOff() {
absl::Status status = gpio_collector_->SetActiveForDuration(
power_control_config_.power_out_gpio_name(),
absl::Milliseconds(
power_control_config_.timer_map().at("ForceOffPulseMs")));
if (!status.ok()) {
LOG(ERROR) << "Failed to press force-power-off GPIO: " << status;
}
}
void PowerControlGpio::GracefulPowerOff() {
absl::Status status = gpio_collector_->SetActiveForDuration(
power_control_config_.power_out_gpio_name(),
absl::Milliseconds(power_control_config_.timer_map().at("PowerPulseMs")));
if (!status.ok()) {
LOG(ERROR) << "Failed to press graceful-power-off GPIO: " << status;
}
}
void PowerControlGpio::Reset() {
absl::Status status = gpio_collector_->SetActiveForDuration(
power_control_config_.reset_out_gpio_name(),
absl::Milliseconds(power_control_config_.timer_map().at("ResetPulseMs")));
if (!status.ok()) {
LOG(ERROR) << "Failed to press reset GPIO: " << status;
}
}
void PowerControlGpio::PowerOnRequest() {
absl::MutexLock lock(host_state_mutex_);
PublishSel("Power On Request",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{"GBMC_POWER_CONTROL_REQUEST_TYPE=PowerOn"});
if (host_state_.power_state() == PowerState::POWER_STATE_OFF) {
SetPowerState(PowerState::POWER_STATE_WAIT_FOR_POWER_OK);
StartPowerOKWatchdogTimer();
PowerOn();
}
}
void PowerControlGpio::PowerOffRequest() {
absl::MutexLock lock(host_state_mutex_);
PublishSel("Power Off Request",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{"GBMC_POWER_CONTROL_REQUEST_TYPE=PowerOff"});
switch (host_state_.power_state()) {
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF:
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF:
SetPowerState(PowerState::POWER_STATE_TRANSITION_TO_OFF);
graceful_power_off_timer_.cancel();
ForcePowerOff();
break;
case PowerState::POWER_STATE_ON:
SetPowerState(PowerState::POWER_STATE_TRANSITION_TO_OFF);
ForcePowerOff();
break;
default:
// Do nothing
break;
}
}
void PowerControlGpio::PowerCycleRequest() {
absl::MutexLock lock(host_state_mutex_);
PublishSel("Power Cycle Request",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{"GBMC_POWER_CONTROL_REQUEST_TYPE=PowerCycle"});
switch (host_state_.power_state()) {
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF:
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF:
SetPowerState(PowerState::POWER_STATE_TRANSITION_TO_CYCLE_OFF);
graceful_power_off_timer_.cancel();
ForcePowerOff();
break;
case PowerState::POWER_STATE_ON:
LOG(INFO) << "Enter TRANSITION_TO_CYCLE_OFF";
SetPowerState(PowerState::POWER_STATE_TRANSITION_TO_CYCLE_OFF);
ForcePowerOff();
break;
default:
// Do nothing
break;
}
}
void PowerControlGpio::ResetRequest() {
absl::MutexLock lock(host_state_mutex_);
PublishSel("Reset Request",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{"GBMC_POWER_CONTROL_REQUEST_TYPE=Reset"});
switch (host_state_.power_state()) {
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF:
case PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF:
SetPowerState(PowerState::POWER_STATE_ON);
graceful_power_off_timer_.cancel();
Reset();
break;
case PowerState::POWER_STATE_ON:
Reset();
break;
default:
// Do nothing
break;
}
}
void PowerControlGpio::GracefulPowerOffRequest() {
absl::MutexLock lock(host_state_mutex_);
PublishSel("Graceful Power Off Request",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{"GBMC_POWER_CONTROL_REQUEST_TYPE=GracefulPowerOff"});
if (host_state_.power_state() == PowerState::POWER_STATE_ON) {
SetPowerState(PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_OFF);
StartGracefulPowerOffTimer();
GracefulPowerOff();
}
}
void PowerControlGpio::GracefulPowerCycleRequest() {
absl::MutexLock lock(host_state_mutex_);
PublishSel("Graceful Power Cycle Request",
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{"GBMC_POWER_CONTROL_REQUEST_TYPE=GracefulPowerCycle"});
if (host_state_.power_state() == PowerState::POWER_STATE_ON) {
SetPowerState(PowerState::POWER_STATE_GRACEFUL_TRANSITION_TO_CYCLE_OFF);
StartGracefulPowerOffTimer();
GracefulPowerOff();
}
}
void PowerControlGpio::SystemResetRequest(ResetType reset_type,
absl::Duration delay) {
LOG(INFO) << "System reset request: " << reset_type << " " << delay;
switch (reset_type) {
case ResetType::RESET_TYPE_FORCE_OFF:
PowerOffRequest();
break;
case ResetType::RESET_TYPE_FORCE_ON:
case ResetType::RESET_TYPE_ON:
PowerOnRequest();
break;
case ResetType::RESET_TYPE_FORCE_RESTART:
ResetRequest();
break;
case ResetType::RESET_TYPE_GRACEFUL_RESTART:
GracefulPowerCycleRequest();
break;
case ResetType::RESET_TYPE_GRACEFUL_SHUTDOWN:
GracefulPowerOffRequest();
break;
case ResetType::RESET_TYPE_POWER_CYCLE:
PowerCycleRequest();
break;
default:
// Do nothing
break;
}
}
nlohmann::json PowerControlGpio::ToJson() {
nlohmann::json json;
absl::MutexLock lock(host_state_mutex_);
json["OSState"] = OSState_Name(host_state_.os_state());
json["PowerState"] = PowerState_Name(host_state_.power_state());
json["LastResetTime"] = absl::FormatTime(
absl::RFC3339_sec, last_state_change_time_, absl::UTCTimeZone());
return json;
}
PowerControlGpio::~PowerControlGpio() {
gpio_collector_ = nullptr;
power_cycle_timer_.cancel();
power_ok_watchdog_timer_.cancel();
graceful_power_off_timer_.cancel();
warm_reset_check_timer_.cancel();
}
void PowerControlGpio::SetPowerState(PowerState state) {
if (host_state_.power_state() != state) {
host_state_.set_power_state(state);
if (clock_ != nullptr) {
last_state_change_time_ = clock_->Now();
}
PowerStateSelInfo sel_info = GetPowerStateSelInfo(state);
PublishSel(absl::StrCat("Host Power State: ", sel_info.message_suffix),
gbmc_sel_framework::EventSeverity::EVENT_SEVERITY_INFO,
{absl::StrCat("GBMC_POWER_STATE=", sel_info.state_value)});
}
}
void PowerControlGpio::PublishSel(
absl::string_view message, gbmc_sel_framework::EventSeverity severity,
absl::Span<const absl::string_view> additional_data) {
gbmc_sel_framework::EventSourceType source_type = GetEventSourceType();
gbmc_sel_framework::EventSourceComponent component =
gbmc_sel_framework::EventSourceComponent::EVENT_SOURCE_COMPONENT_CPU;
gbmc_sel_framework::EventAction action =
gbmc_sel_framework::EventAction::EVENT_ACTION_NULL;
std::vector<absl::string_view> additional_data_views;
additional_data_views.reserve(additional_data.size() + 1);
additional_data_views.emplace_back("GBMC_SYSTEM_EVENT_TYPE=CPUEvent");
for (const auto& data : additional_data) {
additional_data_views.push_back(data);
}
absl::Status status;
if (journal_sender_ != nullptr) {
status = gbmc_sel_framework::PublishGBMCSystemEvent(
source_type, component, severity, action, message,
additional_data_views, [this](const struct iovec* iov, int iovcnt) {
return journal_sender_(iov, iovcnt);
});
} else {
status = gbmc_sel_framework::PublishGBMCSystemEvent(
source_type, component, severity, action, message,
additional_data_views);
}
if (!status.ok()) {
LOG(INFO) << "Failed to publish power control SEL event: " << status;
}
}
gbmc_sel_framework::EventSourceType PowerControlGpio::GetEventSourceType()
const {
if (power_control_config_.host_id() == "system2") {
return gbmc_sel_framework::EventSourceType::EVENT_SOURCE_HPM1;
}
return gbmc_sel_framework::EventSourceType::EVENT_SOURCE_HPM0;
}
void PowerControlGpio::Start() {
absl::MutexLock lock(host_state_mutex_);
absl::MutexLock power_state_callback_lock(power_state_callback_mutex_);
absl::MutexLock os_state_callback_lock(os_state_callback_mutex_);
if (host_state_.power_state() == PowerState::POWER_STATE_ON) {
for (const auto& cb : host_power_off_to_on_callbacks_) {
cb(/*setup_run=*/true);
}
} else if (host_state_.power_state() == PowerState::POWER_STATE_OFF ||
host_state_.power_state() == PowerState::POWER_STATE_CYCLE_OFF ||
host_state_.power_state() ==
PowerState::POWER_STATE_WAIT_FOR_POWER_OK) {
for (const auto& cb : host_power_on_to_off_callbacks_) {
cb(/*setup_run=*/true);
}
}
if (host_state_.os_state() == OSState::OS_STATE_STANDBY) {
for (const auto& cb : host_os_inactive_to_standby_callbacks_) {
cb(/*setup_run=*/true);
}
} else if (host_state_.os_state() == OSState::OS_STATE_INACTIVE) {
for (const auto& cb : host_os_standby_to_inactive_callbacks_) {
cb(/*setup_run=*/true);
}
}
}
void PowerControlGpio::RegisterHostPowerOnToOffCallback(
absl::AnyInvocable<void(bool) const> callback) {
absl::MutexLock lock(power_state_callback_mutex_);
host_power_on_to_off_callbacks_.push_back(std::move(callback));
}
void PowerControlGpio::RegisterHostPowerOffToOnCallback(
absl::AnyInvocable<void(bool) const> callback) {
absl::MutexLock lock(power_state_callback_mutex_);
host_power_off_to_on_callbacks_.push_back(std::move(callback));
}
void PowerControlGpio::RegisterHostOSInactiveToStandbyCallback(
absl::AnyInvocable<void(bool) const> callback) {
absl::MutexLock lock(os_state_callback_mutex_);
host_os_inactive_to_standby_callbacks_.push_back(std::move(callback));
}
void PowerControlGpio::RegisterHostOSStandbyToInactiveCallback(
absl::AnyInvocable<void(bool) const> callback) {
absl::MutexLock lock(os_state_callback_mutex_);
host_os_standby_to_inactive_callbacks_.push_back(std::move(callback));
}
absl::Time PowerControlGpio::GetLastStateChangeTime() {
absl::MutexLock lock(host_state_mutex_);
return last_state_change_time_;
}
PowerControlThreadManager::~PowerControlThreadManager() {
// Stop the scheduler first.
task_scheduler->Stop();
// Finally join all threads.
for (const std::shared_ptr<boost::asio::io_context>& io_context :
io_contexts) {
io_context->stop();
}
for (const std::unique_ptr<ecclesia::ThreadInterface>& thread : threads) {
thread->Join();
}
}
} // namespace milotic_tlbmc