blob: 8f1129b431e3d95324700a135aa1b6ed05a28b4d [file] [log] [blame]
Shawn McCarneyb5330162025-12-24 14:37:45 -06001/**
2 * Copyright © 2025 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "chassis.hpp"
18
19namespace phosphor::power::sequencer
20{
21
22std::tuple<bool, std::string> Chassis::canSetPowerState(
23 PowerState newPowerState)
24{
25 verifyMonitoringInitialized();
26 try
27 {
28 if (powerState && (powerState == newPowerState))
29 {
30 return {false, "Chassis is already at requested state"};
31 }
32
33 if (!isPresent())
34 {
35 return {false, "Chassis is not present"};
36 }
37
38 // Do not allow power on for chassis in hardware isolation; power off OK
39 if (!isEnabled() && (newPowerState == PowerState::on))
40 {
41 return {false, "Chassis is not enabled"};
42 }
43
44 if (!isInputPowerGood())
45 {
46 return {false, "Chassis does not have input power"};
47 }
48
49 // Check Available last. This D-Bus property is based on a list of
50 // factors including some of the preceding properties.
51 if (!isAvailable())
52 {
53 return {false, "Chassis is not available"};
54 }
55 }
56 catch (const std::exception& e)
57 {
58 return {false,
59 std::format("Error determining chassis status: {}", e.what())};
60 }
61
62 return {true, ""};
63}
64
65void Chassis::setPowerState(PowerState newPowerState, Services& services)
66{
67 verifyMonitoringInitialized();
68 auto [canSet, reason] = canSetPowerState(newPowerState);
69 if (!canSet)
70 {
71 throw std::runtime_error{
72 std::format("Unable to set chassis {} to state {}: {}", number,
73 PowerInterface::toString(newPowerState), reason)};
74 }
75
76 powerState = newPowerState;
77 if (powerState == PowerState::on)
78 {
79 powerOn(services);
80 }
81 else
82 {
83 powerOff(services);
84 }
85}
86
87void Chassis::monitor(Services& services)
88{
89 verifyMonitoringInitialized();
90
91 if (!isPresent() || !isInputPowerGood())
92 {
93 powerState = PowerState::off;
94 powerGood = PowerGood::off;
95 closeDevices();
96 return;
97 }
98
99 if (isPresent() && isAvailable() && isInputPowerGood())
100 {
101 readPowerGood(services);
102 setInitialPowerStateIfNeeded();
103 }
104}
105
106void Chassis::closeDevices()
107{
108 for (auto& powerSequencer : powerSequencers)
109 {
110 try
111 {
112 if (powerSequencer->isOpen())
113 {
114 powerSequencer->close();
115 }
116 }
117 catch (...)
118 {
119 // Ignore errors; often called when chassis goes missing/unavailable
120 }
121 }
122}
123
124void Chassis::readPowerGood(Services& services)
125{
126 // Count the number of power sequencer devices with power good on and off
127 size_t powerGoodOnCount{0}, powerGoodOffCount{0};
128 for (auto& powerSequencer : powerSequencers)
129 {
130 try
131 {
132 openDeviceIfNeeded(*powerSequencer, services);
133 if (powerSequencer->getPowerGood())
134 {
135 ++powerGoodOnCount;
136 }
137 else
138 {
139 ++powerGoodOffCount;
140 }
141 }
142 catch (...)
143 {}
144 }
145
146 if (powerGoodOnCount == powerSequencers.size())
147 {
148 // All devices have power good on; set chassis power good to on
149 powerGood = PowerGood::on;
150 }
151 else if (powerGoodOffCount == powerSequencers.size())
152 {
153 // All devices have power good off; set chassis power good to off
154 powerGood = PowerGood::off;
155 }
156}
157
158void Chassis::setInitialPowerStateIfNeeded()
159{
160 if (!powerState)
161 {
162 if (powerGood)
163 {
164 if (powerGood == PowerGood::off)
165 {
166 powerState = PowerState::off;
167 }
168 else
169 {
170 powerState = PowerState::on;
171 }
172 }
173 }
174}
175
176void Chassis::powerOn(Services& services)
177{
178 std::string error{};
179 for (auto& powerSequencer : powerSequencers)
180 {
181 try
182 {
183 openDeviceIfNeeded(*powerSequencer, services);
184 powerSequencer->powerOn();
185 }
186 catch (const std::exception& e)
187 {
188 // Catch and save error so we can power on any remaining devices
189 error =
190 std::format("Unable to power on device {} in chassis {}: {}",
191 powerSequencer->getName(), number, e.what());
192 }
193 }
194
195 if (!error.empty())
196 {
197 throw std::runtime_error{error};
198 }
199}
200
201void Chassis::powerOff(Services& services)
202{
203 std::string error{};
204 for (auto& powerSequencer : powerSequencers)
205 {
206 try
207 {
208 openDeviceIfNeeded(*powerSequencer, services);
209 powerSequencer->powerOff();
210 }
211 catch (const std::exception& e)
212 {
213 // Catch and save error so we can power off any remaining devices
214 error =
215 std::format("Unable to power off device {} in chassis {}: {}",
216 powerSequencer->getName(), number, e.what());
217 }
218 }
219
220 if (!error.empty())
221 {
222 throw std::runtime_error{error};
223 }
224}
225
226} // namespace phosphor::power::sequencer