1 #pragma once 2 3 #include "sdbusplus.hpp" 4 5 #include <fmt/format.h> 6 7 #include <phosphor-logging/log.hpp> 8 #include <xyz/openbmc_project/State/Host/server.hpp> 9 10 #include <functional> 11 12 using HostState = 13 sdbusplus::xyz::openbmc_project::State::server::Host::HostState; 14 15 namespace phosphor::fan 16 { 17 18 /** 19 * @class PowerState 20 * 21 * This class provides an interface to check the current power state, 22 * and to register a function that gets called when there is a power 23 * state change. A callback can be passed in using the constructor, 24 * or can be added later using addCallback(). 25 * 26 * Different architectures may have different ways of considering 27 * power to be on, such as a pgood property on the 28 * org.openbmc.Control.Power interface, or the CurrentPowerState 29 * property on the State.Chassis interface, so those details will 30 * be in a derived class. 31 */ 32 class PowerState 33 { 34 public: 35 using StateChangeFunc = std::function<void(bool)>; 36 37 virtual ~PowerState() = default; 38 PowerState(const PowerState&) = delete; 39 PowerState& operator=(const PowerState&) = delete; 40 PowerState(PowerState&&) = delete; 41 PowerState& operator=(PowerState&&) = delete; 42 43 /** 44 * @brief Constructor 45 * 46 * @param[in] bus - The D-Bus bus connection object 47 * @param[in] callback - The function that should be run when 48 * the power state changes 49 */ 50 PowerState(sdbusplus::bus::bus& bus, StateChangeFunc callback) : _bus(bus) 51 { 52 _callbacks.emplace("default", std::move(callback)); 53 } 54 55 /** 56 * @brief Constructor 57 * 58 * Callbacks can be added with addCallback(). 59 */ 60 PowerState() : _bus(util::SDBusPlus::getBus()) 61 {} 62 63 /** 64 * @brief Adds a function to call when the power state changes 65 * 66 * @param[in] - Any unique name, so the callback can be removed later 67 * if desired. 68 * @param[in] callback - The function that should be run when 69 * the power state changes 70 */ 71 void addCallback(const std::string& name, StateChangeFunc callback) 72 { 73 _callbacks.emplace(name, std::move(callback)); 74 } 75 76 /** 77 * @brief Remove the callback so it is no longer called 78 * 79 * @param[in] name - The name used when it was added. 80 */ 81 void deleteCallback(const std::string& name) 82 { 83 _callbacks.erase(name); 84 } 85 86 /** 87 * @brief Says if power is on 88 * 89 * @return bool - The power state 90 */ 91 bool isPowerOn() const 92 { 93 return _powerState; 94 } 95 96 protected: 97 /** 98 * @brief Called by derived classes to set the power state value 99 * 100 * Will call the callback functions if the state changed. 101 * 102 * @param[in] state - The new power state 103 */ 104 void setPowerState(bool state) 105 { 106 if (state != _powerState) 107 { 108 _powerState = state; 109 for (const auto& [name, callback] : _callbacks) 110 { 111 callback(_powerState); 112 } 113 } 114 } 115 116 /** 117 * @brief Reference to the D-Bus connection object. 118 */ 119 sdbusplus::bus::bus& _bus; 120 121 /** 122 * @brief The power state value 123 */ 124 bool _powerState = false; 125 126 private: 127 /** 128 * @brief The callback functions to run when the power state changes 129 */ 130 std::map<std::string, StateChangeFunc> _callbacks; 131 }; 132 133 /** 134 * @class PGoodState 135 * 136 * This class implements the PowerState API by looking at the 'pgood' 137 * property on the org.openbmc.Control.Power interface. 138 */ 139 class PGoodState : public PowerState 140 { 141 public: 142 virtual ~PGoodState() = default; 143 PGoodState(const PGoodState&) = delete; 144 PGoodState& operator=(const PGoodState&) = delete; 145 PGoodState(PGoodState&&) = delete; 146 PGoodState& operator=(PGoodState&&) = delete; 147 148 PGoodState() : 149 PowerState(), _match(_bus, 150 sdbusplus::bus::match::rules::propertiesChanged( 151 _pgoodPath, _pgoodInterface), 152 [this](auto& msg) { this->pgoodChanged(msg); }) 153 { 154 readPGood(); 155 } 156 157 /** 158 * @brief Constructor 159 * 160 * @param[in] bus - The D-Bus bus connection object 161 * @param[in] callback - The function that should be run when 162 * the power state changes 163 */ 164 PGoodState(sdbusplus::bus::bus& bus, StateChangeFunc func) : 165 PowerState(bus, func), 166 _match(_bus, 167 sdbusplus::bus::match::rules::propertiesChanged(_pgoodPath, 168 _pgoodInterface), 169 [this](auto& msg) { this->pgoodChanged(msg); }) 170 { 171 readPGood(); 172 } 173 174 /** 175 * @brief PropertiesChanged callback for the PGOOD property. 176 * 177 * Will call the registered callback function if necessary. 178 * 179 * @param[in] msg - The payload of the propertiesChanged signal 180 */ 181 void pgoodChanged(sdbusplus::message::message& msg) 182 { 183 std::string interface; 184 std::map<std::string, std::variant<int32_t>> properties; 185 186 msg.read(interface, properties); 187 188 auto pgoodProp = properties.find(_pgoodProperty); 189 if (pgoodProp != properties.end()) 190 { 191 auto pgood = std::get<int32_t>(pgoodProp->second); 192 setPowerState(pgood); 193 } 194 } 195 196 private: 197 /** 198 * @brief Reads the PGOOD property from D-Bus and saves it. 199 */ 200 void readPGood() 201 { 202 try 203 { 204 auto pgood = util::SDBusPlus::getProperty<int32_t>( 205 _bus, _pgoodPath, _pgoodInterface, _pgoodProperty); 206 207 _powerState = static_cast<bool>(pgood); 208 } 209 catch (const util::DBusServiceError& e) 210 { 211 // Wait for propertiesChanged signal when service starts 212 } 213 } 214 215 /** @brief D-Bus path constant */ 216 const std::string _pgoodPath{"/org/openbmc/control/power0"}; 217 218 /** @brief D-Bus interface constant */ 219 const std::string _pgoodInterface{"org.openbmc.control.Power"}; 220 221 /** @brief D-Bus property constant */ 222 const std::string _pgoodProperty{"pgood"}; 223 224 /** @brief The propertiesChanged match */ 225 sdbusplus::bus::match::match _match; 226 }; 227 228 /** 229 * @class HostPowerState 230 * 231 * This class implements the PowerState API by looking at the 'powerState' 232 * property on the phosphor virtual sensor interface. 233 */ 234 class HostPowerState : public PowerState 235 { 236 public: 237 virtual ~HostPowerState() = default; 238 HostPowerState(const HostPowerState&) = delete; 239 HostPowerState& operator=(const HostPowerState&) = delete; 240 HostPowerState(HostPowerState&&) = delete; 241 HostPowerState& operator=(HostPowerState&&) = delete; 242 243 HostPowerState() : 244 PowerState(), 245 _match(_bus, 246 sdbusplus::bus::match::rules::propertiesChangedNamespace( 247 _hostStatePath, _hostStateInterface), 248 [this](auto& msg) { this->hostStateChanged(msg); }) 249 { 250 readHostState(); 251 } 252 253 /** 254 * @brief Constructor 255 * 256 * @param[in] bus - The D-Bus bus connection object 257 * @param[in] callback - The function that should be run when 258 * the power state changes 259 */ 260 HostPowerState(sdbusplus::bus::bus& bus, StateChangeFunc func) : 261 PowerState(bus, func), 262 _match(_bus, 263 sdbusplus::bus::match::rules::propertiesChangedNamespace( 264 _hostStatePath, _hostStateInterface), 265 [this](auto& msg) { this->hostStateChanged(msg); }) 266 { 267 readHostState(); 268 } 269 270 /** 271 * @brief PropertiesChanged callback for the CurrentHostState property. 272 * 273 * Will call the registered callback function if necessary. 274 * 275 * @param[in] msg - The payload of the propertiesChanged signal 276 */ 277 void hostStateChanged(sdbusplus::message::message& msg) 278 { 279 std::string interface; 280 std::map<std::string, std::variant<std::string>> properties; 281 std::vector<HostState> hostPowerStates; 282 283 msg.read(interface, properties); 284 285 auto hostStateProp = properties.find(_hostStateProperty); 286 if (hostStateProp != properties.end()) 287 { 288 auto currentHostState = 289 sdbusplus::message::convert_from_string<HostState>( 290 std::get<std::string>(hostStateProp->second)); 291 292 if (!currentHostState) 293 { 294 throw sdbusplus::exception::InvalidEnumString(); 295 } 296 HostState hostState = *currentHostState; 297 298 hostPowerStates.emplace_back(hostState); 299 setHostPowerState(hostPowerStates); 300 } 301 } 302 303 private: 304 void setHostPowerState(std::vector<HostState>& hostPowerStates) 305 { 306 bool powerStateflag = false; 307 for (const auto& powerState : hostPowerStates) 308 { 309 if (powerState == HostState::Standby || 310 powerState == HostState::Running || 311 powerState == HostState::TransitioningToRunning || 312 powerState == HostState::Quiesced || 313 powerState == HostState::DiagnosticMode) 314 { 315 powerStateflag = true; 316 break; 317 } 318 } 319 setPowerState(powerStateflag); 320 } 321 322 /** 323 * @brief Reads the CurrentHostState property from D-Bus and saves it. 324 */ 325 void readHostState() 326 { 327 328 std::string hostStatePath; 329 std::string hostStateService; 330 std::string hostService = "xyz.openbmc_project.State.Host"; 331 std::vector<HostState> hostPowerStates; 332 333 int32_t depth = 0; 334 const std::string path = "/"; 335 336 auto mapperResponse = util::SDBusPlus::getSubTreeRaw( 337 _bus, path, _hostStateInterface, depth); 338 339 for (const auto& path : mapperResponse) 340 { 341 for (const auto& service : path.second) 342 { 343 hostStateService = service.first; 344 345 if (hostStateService.find(hostService) != std::string::npos) 346 { 347 hostStatePath = path.first; 348 349 auto currentHostState = 350 util::SDBusPlus::getProperty<HostState>( 351 hostStateService, hostStatePath, 352 _hostStateInterface, _hostStateProperty); 353 354 hostPowerStates.emplace_back(currentHostState); 355 } 356 } 357 } 358 setHostPowerState(hostPowerStates); 359 } 360 361 const std::string _hostStatePath{"/xyz/openbmc_project/state"}; 362 363 /** @brief D-Bus interface constant */ 364 const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"}; 365 366 /** @brief D-Bus property constant */ 367 const std::string _hostStateProperty{"CurrentHostState"}; 368 369 /** @brief The propertiesChanged match */ 370 sdbusplus::bus::match::match _match; 371 }; 372 373 } // namespace phosphor::fan 374