1 #include "button_handler.hpp"
2 
3 #include "settings.hpp"
4 
5 #include <phosphor-logging/log.hpp>
6 #include <xyz/openbmc_project/State/Chassis/server.hpp>
7 #include <xyz/openbmc_project/State/Host/server.hpp>
8 
9 namespace phosphor
10 {
11 namespace button
12 {
13 
14 namespace sdbusRule = sdbusplus::bus::match::rules;
15 using namespace sdbusplus::xyz::openbmc_project::State::server;
16 using namespace phosphor::logging;
17 using sdbusplus::exception::SdBusError;
18 
19 constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
20 constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
21 constexpr auto hostIface = "xyz.openbmc_project.State.Host";
22 constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
23 constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID";
24 constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset";
25 constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
26 constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group";
27 
28 constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
29 constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
30 constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
31 
32 Handler::Handler(sdbusplus::bus::bus& bus) : bus(bus)
33 {
34     try
35     {
36         if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty())
37         {
38             log<level::INFO>("Starting power button handler");
39             powerButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
40                 bus,
41                 sdbusRule::type::signal() + sdbusRule::member("Released") +
42                     sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
43                     sdbusRule::interface(powerButtonIface),
44                 std::bind(std::mem_fn(&Handler::powerPressed), this,
45                           std::placeholders::_1));
46 
47             powerButtonLongPressReleased =
48                 std::make_unique<sdbusplus::bus::match_t>(
49                     bus,
50                     sdbusRule::type::signal() +
51                         sdbusRule::member("PressedLong") +
52                         sdbusRule::path(POWER_DBUS_OBJECT_NAME) +
53                         sdbusRule::interface(powerButtonIface),
54                     std::bind(std::mem_fn(&Handler::longPowerPressed), this,
55                               std::placeholders::_1));
56         }
57     }
58     catch (SdBusError& e)
59     {
60         // The button wasn't implemented
61     }
62 
63     try
64     {
65         if (!getService(ID_DBUS_OBJECT_NAME, idButtonIface).empty())
66         {
67             log<level::INFO>("Registering ID button handler");
68             idButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
69                 bus,
70                 sdbusRule::type::signal() + sdbusRule::member("Released") +
71                     sdbusRule::path(ID_DBUS_OBJECT_NAME) +
72                     sdbusRule::interface(idButtonIface),
73                 std::bind(std::mem_fn(&Handler::idPressed), this,
74                           std::placeholders::_1));
75         }
76     }
77     catch (SdBusError& e)
78     {
79         // The button wasn't implemented
80     }
81 
82     try
83     {
84         if (!getService(RESET_DBUS_OBJECT_NAME, resetButtonIface).empty())
85         {
86             log<level::INFO>("Registering reset button handler");
87             resetButtonReleased = std::make_unique<sdbusplus::bus::match_t>(
88                 bus,
89                 sdbusRule::type::signal() + sdbusRule::member("Released") +
90                     sdbusRule::path(RESET_DBUS_OBJECT_NAME) +
91                     sdbusRule::interface(resetButtonIface),
92                 std::bind(std::mem_fn(&Handler::resetPressed), this,
93                           std::placeholders::_1));
94         }
95     }
96     catch (SdBusError& e)
97     {
98         // The button wasn't implemented
99     }
100 }
101 
102 std::string Handler::getService(const std::string& path,
103                                 const std::string& interface) const
104 {
105     auto method = bus.new_method_call(mapperService, mapperObjPath, mapperIface,
106                                       "GetObject");
107     method.append(path, std::vector{interface});
108     auto result = bus.call(method);
109 
110     std::map<std::string, std::vector<std::string>> objectData;
111     result.read(objectData);
112 
113     return objectData.begin()->first;
114 }
115 
116 bool Handler::poweredOn() const
117 {
118     auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
119     auto method = bus.new_method_call(
120         service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Get");
121     method.append(chassisIface, "CurrentPowerState");
122     auto result = bus.call(method);
123 
124     sdbusplus::message::variant<std::string> state;
125     result.read(state);
126 
127     return Chassis::PowerState::On ==
128            Chassis::convertPowerStateFromString(
129                sdbusplus::message::variant_ns::get<std::string>(state));
130 }
131 
132 void Handler::powerPressed(sdbusplus::message::message& msg)
133 {
134     auto transition = Host::Transition::On;
135 
136     try
137     {
138         if (poweredOn())
139         {
140             transition = Host::Transition::Off;
141         }
142 
143         log<level::INFO>("Handling power button press");
144 
145         sdbusplus::message::variant<std::string> state =
146             convertForMessage(transition);
147 
148         auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
149         auto method = bus.new_method_call(
150             service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
151         method.append(hostIface, "RequestedHostTransition", state);
152 
153         bus.call(method);
154     }
155     catch (SdBusError& e)
156     {
157         log<level::ERR>("Failed power state change on a power button press",
158                         entry("ERROR=%s", e.what()));
159     }
160 }
161 
162 void Handler::longPowerPressed(sdbusplus::message::message& msg)
163 {
164     try
165     {
166         if (!poweredOn())
167         {
168             log<level::INFO>(
169                 "Power is off so ignoring long power button press");
170             return;
171         }
172 
173         log<level::INFO>("Handling long power button press");
174 
175         sdbusplus::message::variant<std::string> state =
176             convertForMessage(Chassis::Transition::Off);
177 
178         auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
179         auto method = bus.new_method_call(
180             service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Set");
181         method.append(chassisIface, "RequestedPowerTransition", state);
182 
183         bus.call(method);
184     }
185     catch (SdBusError& e)
186     {
187         log<level::ERR>("Failed powering off on long power button press",
188                         entry("ERROR=%s", e.what()));
189     }
190 }
191 
192 void Handler::resetPressed(sdbusplus::message::message& msg)
193 {
194     try
195     {
196         if (!poweredOn())
197         {
198             log<level::INFO>("Power is off so ignoring reset button press");
199             return;
200         }
201 
202         log<level::INFO>("Handling reset button press");
203 
204         sdbusplus::message::variant<std::string> state =
205             convertForMessage(Host::Transition::Reboot);
206 
207         auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
208         auto method = bus.new_method_call(
209             service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
210 
211         method.append(hostIface, "RequestedHostTransition", state);
212 
213         bus.call(method);
214     }
215     catch (SdBusError& e)
216     {
217         log<level::ERR>("Failed power state change on a reset button press",
218                         entry("ERROR=%s", e.what()));
219     }
220 }
221 
222 void Handler::idPressed(sdbusplus::message::message& msg)
223 {
224     std::string groupPath{ledGroupBasePath};
225     groupPath += ID_LED_GROUP;
226 
227     auto service = getService(groupPath, ledGroupIface);
228 
229     if (service.empty())
230     {
231         log<level::INFO>("No identify LED group found during ID button press",
232                          entry("GROUP=%s", groupPath.c_str()));
233         return;
234     }
235 
236     try
237     {
238         auto method = bus.new_method_call(service.c_str(), groupPath.c_str(),
239                                           propertyIface, "Get");
240         method.append(ledGroupIface, "Asserted");
241         auto result = bus.call(method);
242 
243         sdbusplus::message::variant<bool> state;
244         result.read(state);
245 
246         state = !sdbusplus::message::variant_ns::get<bool>(state);
247 
248         log<level::INFO>(
249             "Changing ID LED group state on ID LED press",
250             entry("GROUP=%s", groupPath.c_str()),
251             entry("STATE=%d",
252                   sdbusplus::message::variant_ns::get<bool>(state)));
253 
254         method = bus.new_method_call(service.c_str(), groupPath.c_str(),
255                                      propertyIface, "Set");
256 
257         method.append(ledGroupIface, "Asserted", state);
258         result = bus.call(method);
259     }
260     catch (SdBusError& e)
261     {
262         log<level::ERR>("Error toggling ID LED group on ID button press",
263                         entry("ERROR=%s", e.what()));
264     }
265 }
266 } // namespace button
267 } // namespace phosphor
268