blob: 8f1129b431e3d95324700a135aa1b6ed05a28b4d [file] [log] [blame]
/**
* Copyright © 2025 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "chassis.hpp"
namespace phosphor::power::sequencer
{
std::tuple<bool, std::string> Chassis::canSetPowerState(
PowerState newPowerState)
{
verifyMonitoringInitialized();
try
{
if (powerState && (powerState == newPowerState))
{
return {false, "Chassis is already at requested state"};
}
if (!isPresent())
{
return {false, "Chassis is not present"};
}
// Do not allow power on for chassis in hardware isolation; power off OK
if (!isEnabled() && (newPowerState == PowerState::on))
{
return {false, "Chassis is not enabled"};
}
if (!isInputPowerGood())
{
return {false, "Chassis does not have input power"};
}
// Check Available last. This D-Bus property is based on a list of
// factors including some of the preceding properties.
if (!isAvailable())
{
return {false, "Chassis is not available"};
}
}
catch (const std::exception& e)
{
return {false,
std::format("Error determining chassis status: {}", e.what())};
}
return {true, ""};
}
void Chassis::setPowerState(PowerState newPowerState, Services& services)
{
verifyMonitoringInitialized();
auto [canSet, reason] = canSetPowerState(newPowerState);
if (!canSet)
{
throw std::runtime_error{
std::format("Unable to set chassis {} to state {}: {}", number,
PowerInterface::toString(newPowerState), reason)};
}
powerState = newPowerState;
if (powerState == PowerState::on)
{
powerOn(services);
}
else
{
powerOff(services);
}
}
void Chassis::monitor(Services& services)
{
verifyMonitoringInitialized();
if (!isPresent() || !isInputPowerGood())
{
powerState = PowerState::off;
powerGood = PowerGood::off;
closeDevices();
return;
}
if (isPresent() && isAvailable() && isInputPowerGood())
{
readPowerGood(services);
setInitialPowerStateIfNeeded();
}
}
void Chassis::closeDevices()
{
for (auto& powerSequencer : powerSequencers)
{
try
{
if (powerSequencer->isOpen())
{
powerSequencer->close();
}
}
catch (...)
{
// Ignore errors; often called when chassis goes missing/unavailable
}
}
}
void Chassis::readPowerGood(Services& services)
{
// Count the number of power sequencer devices with power good on and off
size_t powerGoodOnCount{0}, powerGoodOffCount{0};
for (auto& powerSequencer : powerSequencers)
{
try
{
openDeviceIfNeeded(*powerSequencer, services);
if (powerSequencer->getPowerGood())
{
++powerGoodOnCount;
}
else
{
++powerGoodOffCount;
}
}
catch (...)
{}
}
if (powerGoodOnCount == powerSequencers.size())
{
// All devices have power good on; set chassis power good to on
powerGood = PowerGood::on;
}
else if (powerGoodOffCount == powerSequencers.size())
{
// All devices have power good off; set chassis power good to off
powerGood = PowerGood::off;
}
}
void Chassis::setInitialPowerStateIfNeeded()
{
if (!powerState)
{
if (powerGood)
{
if (powerGood == PowerGood::off)
{
powerState = PowerState::off;
}
else
{
powerState = PowerState::on;
}
}
}
}
void Chassis::powerOn(Services& services)
{
std::string error{};
for (auto& powerSequencer : powerSequencers)
{
try
{
openDeviceIfNeeded(*powerSequencer, services);
powerSequencer->powerOn();
}
catch (const std::exception& e)
{
// Catch and save error so we can power on any remaining devices
error =
std::format("Unable to power on device {} in chassis {}: {}",
powerSequencer->getName(), number, e.what());
}
}
if (!error.empty())
{
throw std::runtime_error{error};
}
}
void Chassis::powerOff(Services& services)
{
std::string error{};
for (auto& powerSequencer : powerSequencers)
{
try
{
openDeviceIfNeeded(*powerSequencer, services);
powerSequencer->powerOff();
}
catch (const std::exception& e)
{
// Catch and save error so we can power off any remaining devices
error =
std::format("Unable to power off device {} in chassis {}: {}",
powerSequencer->getName(), number, e.what());
}
}
if (!error.empty())
{
throw std::runtime_error{error};
}
}
} // namespace phosphor::power::sequencer