/** * 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 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