1 /*
2 // Copyright (c) 2018 Intel 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 #pragma once
18 #include "common.hpp"
19 #include "gpio.hpp"
20 #include "xyz/openbmc_project/Chassis/Buttons/Power/server.hpp"
21 #include "xyz/openbmc_project/Chassis/Common/error.hpp"
22 
23 #include <unistd.h>
24 
25 #include <chrono>
26 #include <phosphor-logging/elog-errors.hpp>
27 
28 const static constexpr char* POWER_BUTTON = "POWER_BUTTON";
29 
30 struct PowerButton
31     : sdbusplus::server::object::object<
32           sdbusplus::xyz::openbmc_project::Chassis::Buttons::server::Power>
33 {
34 
35     PowerButton(sdbusplus::bus::bus& bus, const char* path, EventPtr& event,
36                 sd_event_io_handler_t handler = PowerButton::EventHandler) :
37         sdbusplus::server::object::object<
38             sdbusplus::xyz::openbmc_project::Chassis::Buttons::server::Power>(
39             bus, path),
40         fd(-1), bus(bus), event(event), callbackHandler(handler)
41     {
42 
43         int ret = -1;
44 
45         // config gpio
46         ret = ::configGpio(POWER_BUTTON, &fd, bus);
47         if (ret < 0)
48         {
49             phosphor::logging::log<phosphor::logging::level::ERR>(
50                 "POWER_BUTTON: failed to config GPIO");
51             throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
52                 IOError();
53         }
54 
55         char buf;
56         ::read(fd, &buf, sizeof(buf));
57 
58         ret = sd_event_add_io(event.get(), nullptr, fd, EPOLLPRI,
59                               callbackHandler, this);
60         if (ret < 0)
61         {
62             phosphor::logging::log<phosphor::logging::level::ERR>(
63                 "POWER_BUTTON: failed to add to event loop");
64             ::closeGpio(fd);
65             throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
66                 IOError();
67         }
68     }
69 
70     ~PowerButton()
71     {
72         ::closeGpio(fd);
73     }
74 
75     void simPress() override;
76     void simLongPress() override;
77 
78     static const char* getGpioName()
79     {
80         return POWER_BUTTON;
81     }
82 
83     void updatePressedTime()
84     {
85         pressedTime = std::chrono::steady_clock::now();
86     }
87 
88     auto getPressTime() const
89     {
90         return pressedTime;
91     }
92 
93     static int EventHandler(sd_event_source* es, int fd, uint32_t revents,
94                             void* userdata)
95     {
96 
97         int n = -1;
98         char buf = '0';
99 
100         if (!userdata)
101         {
102             phosphor::logging::log<phosphor::logging::level::ERR>(
103                 "POWER_BUTTON: userdata null!");
104             throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
105                 IOError();
106         }
107 
108         PowerButton* powerButton = static_cast<PowerButton*>(userdata);
109 
110         if (!powerButton)
111         {
112             phosphor::logging::log<phosphor::logging::level::ERR>(
113                 "POWER_BUTTON: null pointer!");
114             throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
115                 IOError();
116         }
117 
118         n = ::lseek(fd, 0, SEEK_SET);
119 
120         if (n < 0)
121         {
122             phosphor::logging::log<phosphor::logging::level::ERR>(
123                 "POWER_BUTTON: lseek error!");
124             throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
125                 IOError();
126         }
127 
128         n = ::read(fd, &buf, sizeof(buf));
129         if (n < 0)
130         {
131             phosphor::logging::log<phosphor::logging::level::ERR>(
132                 "POWER_BUTTON: read error!");
133             throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
134                 IOError();
135         }
136 
137         if (buf == '0')
138         {
139             phosphor::logging::log<phosphor::logging::level::DEBUG>(
140                 "POWER_BUTTON: pressed");
141 
142             powerButton->updatePressedTime();
143             // emit pressed signal
144             powerButton->pressed();
145         }
146         else
147         {
148             phosphor::logging::log<phosphor::logging::level::DEBUG>(
149                 "POWER_BUTTON: released");
150 
151             auto now = std::chrono::steady_clock::now();
152             auto d = std::chrono::duration_cast<std::chrono::milliseconds>(
153                 now - powerButton->getPressTime());
154 
155             if (d > std::chrono::milliseconds(LONG_PRESS_TIME_MS))
156             {
157                 powerButton->pressedLong();
158             }
159             else
160             {
161                 // released
162                 powerButton->released();
163             }
164         }
165 
166         return 0;
167     }
168 
169   private:
170     int fd;
171     sdbusplus::bus::bus& bus;
172     EventPtr& event;
173     sd_event_io_handler_t callbackHandler;
174     decltype(std::chrono::steady_clock::now()) pressedTime;
175 };
176