1 #include "host_then_chassis_poweroff.hpp" 2 3 #include "config.hpp" 4 #include "power_button_profile_factory.hpp" 5 6 #include <phosphor-logging/lg2.hpp> 7 #include <xyz/openbmc_project/State/BMC/server.hpp> 8 #include <xyz/openbmc_project/State/Chassis/server.hpp> 9 10 namespace phosphor::button 11 { 12 13 // Register the profile with the factory. 14 static PowerButtonProfileRegister<HostThenChassisPowerOff> profileRegister; 15 16 namespace service 17 { 18 constexpr auto bmcState = "xyz.openbmc_project.State.BMC"; 19 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis0"; 20 constexpr auto hostState = "xyz.openbmc_project.State.Host"; 21 } // namespace service 22 23 namespace object_path 24 { 25 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0"; 26 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0"; 27 constexpr auto hostState = "/xyz/openbmc_project/state/host0"; 28 } // namespace object_path 29 30 namespace interface 31 { 32 constexpr auto property = "org.freedesktop.DBus.Properties"; 33 constexpr auto bmcState = "xyz.openbmc_project.State.BMC"; 34 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis"; 35 constexpr auto hostState = "xyz.openbmc_project.State.Host"; 36 } // namespace interface 37 38 using namespace sdbusplus::xyz::openbmc_project::State::server; 39 40 void HostThenChassisPowerOff::pressed() 41 { 42 lg2::info("Power button pressed"); 43 44 try 45 { 46 // If power not on - power on 47 if (!isPoweredOn()) 48 { 49 if (!isBmcReady()) 50 { 51 lg2::error("BMC not at ready state yet, cannot power on"); 52 return; 53 } 54 55 state = PowerOpState::powerOnPress; 56 powerOn(); 57 return; 58 } 59 } 60 catch (const sdbusplus::exception_t& e) 61 { 62 lg2::error( 63 "Exception while processing power button press. Cannot continue"); 64 return; 65 } 66 67 // Power is on ... 68 69 if (state == PowerOpState::buttonNotPressed) 70 { 71 lg2::info("Starting countdown to power off"); 72 state = PowerOpState::buttonPressed; 73 setHostOffTime(); 74 timer.restart(pollInterval); 75 } 76 77 // Button press during host off to chassis off window. 78 // Causes a chassis power off. 79 else if (state == PowerOpState::buttonReleasedHostToChassisOffWindow) 80 { 81 lg2::info("Starting chassis power off due to button press"); 82 state = PowerOpState::chassisOffStarted; 83 timer.setEnabled(false); 84 chassisPowerOff(); 85 } 86 } 87 88 void HostThenChassisPowerOff::released(uint64_t /*pressTimeMS*/) 89 { 90 lg2::info("Power button released"); 91 92 // Button released in the host to chassis off window. 93 // Timer continues to run in case button is pressed again 94 // in the window. 95 if (state == PowerOpState::buttonPressedHostOffStarted) 96 { 97 state = PowerOpState::buttonReleasedHostToChassisOffWindow; 98 return; 99 } 100 101 state = PowerOpState::buttonNotPressed; 102 timer.setEnabled(false); 103 } 104 105 void HostThenChassisPowerOff::timerHandler() 106 { 107 const auto now = std::chrono::steady_clock::now(); 108 109 if ((state == PowerOpState::buttonPressed) && (now >= hostOffTime)) 110 { 111 // Start the host power off and start the chassis 112 // power off countdown. 113 state = PowerOpState::buttonPressedHostOffStarted; 114 setChassisOffTime(); 115 hostPowerOff(); 116 } 117 else if ((state == PowerOpState::buttonPressedHostOffStarted) && 118 (now >= chassisOffTime)) 119 { 120 // Button still pressed and it passed the chassis off time. 121 // Issue the chassis power off. 122 state = PowerOpState::chassisOffStarted; 123 timer.setEnabled(false); 124 chassisPowerOff(); 125 } 126 } 127 128 void HostThenChassisPowerOff::hostTransition(Host::Transition transition) 129 { 130 try 131 { 132 std::variant<std::string> state = convertForMessage(transition); 133 134 lg2::info("Power button action requesting host transition of {TRANS}", 135 "TRANS", std::get<std::string>(state)); 136 137 auto method = bus.new_method_call(service::hostState, 138 object_path::hostState, 139 interface::property, "Set"); 140 method.append(interface::hostState, "RequestedHostTransition", state); 141 142 bus.call(method); 143 } 144 catch (const sdbusplus::exception_t& e) 145 { 146 lg2::error("Failed requesting host transition {TRANS}: {ERROR}", 147 "TRANS", convertForMessage(transition), "ERROR", e); 148 } 149 } 150 151 void HostThenChassisPowerOff::powerOn() 152 { 153 hostTransition(Host::Transition::On); 154 } 155 156 void HostThenChassisPowerOff::hostPowerOff() 157 { 158 hostTransition(Host::Transition::Off); 159 } 160 161 void HostThenChassisPowerOff::chassisPowerOff() 162 { 163 lg2::info("Power button action requesting chassis power off"); 164 165 try 166 { 167 std::variant<std::string> state = 168 convertForMessage(Chassis::Transition::Off); 169 170 auto method = bus.new_method_call(service::chassisState, 171 object_path::chassisState, 172 interface::property, "Set"); 173 method.append(interface::chassisState, "RequestedPowerTransition", 174 state); 175 176 bus.call(method); 177 } 178 catch (const sdbusplus::exception_t& e) 179 { 180 lg2::error("Failed requesting chassis off: {ERROR}", "ERROR", e); 181 } 182 } 183 184 bool HostThenChassisPowerOff::isPoweredOn() const 185 { 186 Chassis::PowerState chassisState; 187 188 try 189 { 190 auto method = bus.new_method_call(service::chassisState, 191 object_path::chassisState, 192 interface::property, "Get"); 193 method.append(interface::chassisState, "CurrentPowerState"); 194 auto result = bus.call(method); 195 196 std::variant<std::string> state; 197 result.read(state); 198 199 chassisState = 200 Chassis::convertPowerStateFromString(std::get<std::string>(state)); 201 } 202 catch (const sdbusplus::exception_t& e) 203 { 204 lg2::error("Failed checking if chassis is on: {ERROR}", "ERROR", e); 205 throw; 206 } 207 208 return chassisState == Chassis::PowerState::On; 209 } 210 211 bool HostThenChassisPowerOff::isBmcReady() const 212 { 213 BMC::BMCState bmcState; 214 215 try 216 { 217 auto method = bus.new_method_call(service::bmcState, 218 object_path::bmcState, 219 interface::property, "Get"); 220 method.append(interface::bmcState, "CurrentBMCState"); 221 auto result = bus.call(method); 222 223 std::variant<std::string> state; 224 result.read(state); 225 226 bmcState = BMC::convertBMCStateFromString(std::get<std::string>(state)); 227 } 228 catch (const sdbusplus::exception_t& e) 229 { 230 lg2::error("Failed reading BMC state interface: {}", "ERROR", e); 231 throw; 232 } 233 234 return bmcState == BMC::BMCState::Ready; 235 } 236 } // namespace phosphor::button 237