1 /**
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
19 namespace phosphor::power::sequencer
20 {
21
canSetPowerState(PowerState newPowerState)22 std::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
setPowerState(PowerState newPowerState,Services & services)65 void 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
monitor(Services & services)87 void 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
closeDevices()106 void 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
readPowerGood(Services & services)124 void 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
setInitialPowerStateIfNeeded()158 void 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
powerOn(Services & services)176 void 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
powerOff(Services & services)201 void 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
227