1 /**
2  * Copyright © 2021 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "power_interface.hpp"
18 
19 #include "types.hpp"
20 
21 #include <fmt/format.h>
22 
23 #include <phosphor-logging/log.hpp>
24 #include <sdbusplus/exception.hpp>
25 #include <sdbusplus/sdbus.hpp>
26 #include <sdbusplus/server.hpp>
27 
28 #include <string>
29 #include <tuple>
30 
31 using namespace phosphor::logging;
32 
33 namespace phosphor::power::sequencer
34 {
35 
36 PowerInterface::PowerInterface(sdbusplus::bus::bus& bus, const char* path) :
37     _serverInterface(bus, path, POWER_IFACE, _vtable, this)
38 {}
39 
40 int PowerInterface::callbackGetPgood(sd_bus* /*bus*/, const char* /*path*/,
41                                      const char* /*interface*/,
42                                      const char* /*property*/,
43                                      sd_bus_message* msg, void* context,
44                                      sd_bus_error* error)
45 {
46     if (msg != nullptr && context != nullptr)
47     {
48         try
49         {
50             auto pwrObj = static_cast<PowerInterface*>(context);
51             int pgood = pwrObj->getPgood();
52             log<level::INFO>(
53                 fmt::format("callbackGetPgood: {}", pgood).c_str());
54 
55             sdbusplus::message::message(msg).append(pgood);
56         }
57         catch (sdbusplus::exception_t& e)
58         {
59             return sd_bus_error_set(error, e.name(), e.description());
60         }
61     }
62     else
63     {
64         // The message or context were null
65         log<level::ERR>("Unable to service get pgood property callback");
66         return -1;
67     }
68 
69     return 1;
70 }
71 
72 int PowerInterface::callbackGetPgoodTimeout(sd_bus* /*bus*/,
73                                             const char* /*path*/,
74                                             const char* /*interface*/,
75                                             const char* /*property*/,
76                                             sd_bus_message* msg, void* context,
77                                             sd_bus_error* error)
78 {
79     if (msg != nullptr && context != nullptr)
80     {
81         try
82         {
83             auto pwrObj = static_cast<PowerInterface*>(context);
84             int timeout = pwrObj->getPgoodTimeout();
85             log<level::INFO>(
86                 fmt::format("callbackGetPgoodTimeout: {}", timeout).c_str());
87 
88             sdbusplus::message::message(msg).append(timeout);
89         }
90         catch (sdbusplus::exception_t& e)
91         {
92             return sd_bus_error_set(error, e.name(), e.description());
93         }
94     }
95     else
96     {
97         // The message or context were null
98         log<level::ERR>(
99             "Unable to service get pgood timeout property callback");
100         return -1;
101     }
102 
103     return 1;
104 }
105 
106 int PowerInterface::callbackGetPowerState(sd_bus_message* msg, void* context,
107                                           sd_bus_error* error)
108 {
109     if (msg != nullptr && context != nullptr)
110     {
111         try
112         {
113             auto pwrObj = static_cast<PowerInterface*>(context);
114             // Return the current power state of the GPIO, rather than the last
115             // requested power state change
116             int pgood = pwrObj->getPgood();
117             log<level::INFO>(
118                 fmt::format("callbackGetPowerState: {}", pgood).c_str());
119 
120             auto reply = sdbusplus::message::message(msg).new_method_return();
121             reply.append(pgood);
122             reply.method_return();
123         }
124         catch (sdbusplus::exception_t& e)
125         {
126             return sd_bus_error_set(error, e.name(), e.description());
127         }
128     }
129     else
130     {
131         // The message or context were null
132         log<level::ERR>("Unable to service getPowerState method callback");
133         return -1;
134     }
135 
136     return 1;
137 }
138 
139 int PowerInterface::callbackSetPgoodTimeout(sd_bus* /*bus*/,
140                                             const char* /*path*/,
141                                             const char* /*interface*/,
142                                             const char* /*property*/,
143                                             sd_bus_message* msg, void* context,
144                                             sd_bus_error* error)
145 {
146     if (msg != nullptr && context != nullptr)
147     {
148         try
149         {
150             auto m = sdbusplus::message::message(msg);
151 
152             int timeout{};
153             m.read(timeout);
154 
155             auto pwrObj = static_cast<PowerInterface*>(context);
156             log<level::INFO>(
157                 fmt::format("callbackSetPgoodTimeout: {}", timeout).c_str());
158             pwrObj->setPgoodTimeout(timeout);
159         }
160         catch (sdbusplus::exception_t& e)
161         {
162             return sd_bus_error_set(error, e.name(), e.description());
163         }
164     }
165     else
166     {
167         // The message or context were null
168         log<level::ERR>(
169             "Unable to service set pgood timeout property callback");
170         return -1;
171     }
172 
173     return 1;
174 }
175 
176 int PowerInterface::callbackGetState(sd_bus* /*bus*/, const char* /*path*/,
177                                      const char* /*interface*/,
178                                      const char* /*property*/,
179                                      sd_bus_message* msg, void* context,
180                                      sd_bus_error* error)
181 {
182     if (msg != nullptr && context != nullptr)
183     {
184         try
185         {
186             auto pwrObj = static_cast<PowerInterface*>(context);
187             int state = pwrObj->getState();
188             log<level::INFO>(
189                 fmt::format("callbackGetState: {}", state).c_str());
190 
191             sdbusplus::message::message(msg).append(state);
192         }
193         catch (sdbusplus::exception_t& e)
194         {
195             return sd_bus_error_set(error, e.name(), e.description());
196         }
197     }
198     else
199     {
200         // The message or context were null
201         log<level::ERR>("Unable to service get state property callback");
202         return -1;
203     }
204 
205     return 1;
206 }
207 
208 int PowerInterface::callbackSetPowerState(sd_bus_message* msg, void* context,
209                                           sd_bus_error* error)
210 {
211     if (msg != nullptr && context != nullptr)
212     {
213         try
214         {
215             auto m = sdbusplus::message::message(msg);
216 
217             int state{};
218             m.read(state);
219 
220             if (state != 1 && state != 0)
221             {
222                 return sd_bus_error_set(error,
223                                         "org.openbmc.ControlPower.Error.Failed",
224                                         "Invalid power state");
225             }
226 
227             auto pwrObj = static_cast<PowerInterface*>(context);
228             log<level::INFO>(
229                 fmt::format("callbackSetPowerState: {}", state).c_str());
230             pwrObj->setState(state);
231 
232             m.new_method_return().method_return();
233         }
234         catch (sdbusplus::exception_t& e)
235         {
236             return sd_bus_error_set(error, e.name(), e.description());
237         }
238     }
239     else
240     {
241         // The message or context were null
242         log<level::ERR>("Unable to service setPowerState method callback");
243         return -1;
244     }
245 
246     return 1;
247 }
248 
249 void PowerInterface::emitPowerGoodSignal()
250 {
251     log<level::INFO>("emitPowerGoodSignal");
252     _serverInterface.new_signal("PowerGood").signal_send();
253 }
254 
255 void PowerInterface::emitPowerLostSignal()
256 {
257     log<level::INFO>("emitPowerLostSignal");
258     _serverInterface.new_signal("PowerLost").signal_send();
259 }
260 
261 void PowerInterface::emitPropertyChangedSignal(const char* property)
262 {
263     log<level::INFO>(
264         fmt::format("emitPropertyChangedSignal: {}", property).c_str());
265     _serverInterface.property_changed(property);
266 }
267 
268 const sdbusplus::vtable::vtable_t PowerInterface::_vtable[] = {
269     sdbusplus::vtable::start(),
270     // Method setPowerState takes an int parameter and returns void
271     sdbusplus::vtable::method("setPowerState", "i", "", callbackSetPowerState),
272     // Method getPowerState takes no parameters and returns int
273     sdbusplus::vtable::method("getPowerState", "", "i", callbackGetPowerState),
274     // Signal PowerGood
275     sdbusplus::vtable::signal("PowerGood", ""),
276     // Signal PowerLost
277     sdbusplus::vtable::signal("PowerLost", ""),
278     // Property pgood is type int, read only, and uses the emits_change flag
279     sdbusplus::vtable::property("pgood", "i", callbackGetPgood,
280                                 sdbusplus::vtable::property_::emits_change),
281     // Property state is type int, read only, and uses the emits_change flag
282     sdbusplus::vtable::property("state", "i", callbackGetState,
283                                 sdbusplus::vtable::property_::emits_change),
284     // Property pgood_timeout is type int, read write, and uses the emits_change
285     // flag
286     sdbusplus::vtable::property("pgood_timeout", "i", callbackGetPgoodTimeout,
287                                 callbackSetPgoodTimeout,
288                                 sdbusplus::vtable::property_::emits_change),
289     sdbusplus::vtable::end()};
290 
291 } // namespace phosphor::power::sequencer
292