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