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