blob: 7039338450939b81758e85d79f3edd84f23b61b5 [file] [log] [blame]
Matt Spinlerd82a5f42017-07-24 13:40:37 -05001/**
2 * Copyright © 2017 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#include <chrono>
17#include <phosphor-logging/log.hpp>
18#include <phosphor-logging/elog.hpp>
19#include <phosphor-logging/elog-errors.hpp>
20#include <xyz/openbmc_project/Common/error.hpp>
21#include <type_traits>
22#include "timer.hpp"
23
24namespace witherspoon
25{
26namespace power
27{
28
29using namespace phosphor::logging;
30using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
31 Error::InternalFailure;
32
33Timer::Timer(event::Event& events,
34 std::function<void()> callbackFunc) :
35 timeEvent(events),
36 callback(callbackFunc),
37 timeout(0)
38{
39 sd_event_source* source = nullptr;
40
41 // Start with an infinite expiration time
42 auto r = sd_event_add_time(timeEvent.get(),
43 &source,
44 CLOCK_MONOTONIC, // Time base
45 UINT64_MAX, // Expire time - way long time
46 0, // Use default event accuracy
47 timeoutHandler, // Callback handler on timeout
48 this); // User data
49 if (r < 0)
50 {
51 log<level::ERR>("Timer::Timer failed call to sd_event_add_time",
52 entry("ERROR=%s", strerror(-r)));
53 elog<InternalFailure>();
54 }
55
56 eventSource.reset(source);
57
58 //Ensure timer isn't running
59 setTimer(SD_EVENT_OFF);
60}
61
62
63Timer::~Timer()
64{
65 setTimer(SD_EVENT_OFF);
66}
67
68
69int Timer::timeoutHandler(sd_event_source* eventSource,
70 uint64_t usec, void* userData)
71{
72 auto timer = static_cast<Timer*>(userData);
73
74 if (timer->type == TimerType::repeating)
75 {
76 //Set the next expiration time
77 timer->setTimeout();
78 }
79
80 timer->callback();
81
82 return 0;
83}
84
85
86std::chrono::microseconds Timer::getTime()
87{
88 using namespace std::chrono;
89 auto now = steady_clock::now().time_since_epoch();
90 return duration_cast<microseconds>(now);
91}
92
93
94void Timer::setTimer(int action)
95{
96 auto r = sd_event_source_set_enabled(eventSource.get(), action);
97 if (r < 0)
98 {
99 log<level::ERR>("Failed call to sd_event_source_set_enabled",
100 entry("ERROR=%s", strerror(-r)),
101 entry("ACTION=%d", action));
102 elog<InternalFailure>();
103 }
104}
105
106
107void Timer::stop()
108{
109 setTimer(SD_EVENT_OFF);
110}
111
112
113bool Timer::running()
114{
115 int status = 0;
116
117 //returns SD_EVENT_OFF, SD_EVENT_ON, or SD_EVENT_ONESHOT
118 auto r = sd_event_source_get_enabled(eventSource.get(), &status);
119 if (r < 0)
120 {
121 log<level::ERR>("Failed call to sd_event_source_get_enabled",
122 entry("ERROR=%s", strerror(-r)));
123 elog<InternalFailure>();
124 }
125
126 return (status != SD_EVENT_OFF);
127}
128
129
130void Timer::setTimeout()
131{
132 //Get the current time and add the delta
133 static_assert(std::is_same<decltype(getTime()),
134 std::chrono::microseconds>::value,
135 "Timer::getTime() is the wrong type");
136 static_assert(std::is_same<decltype(timeout),
137 std::chrono::microseconds>::value,
138 "Timer::timeout is the wrong type");
139
140 auto expireTime = getTime() + timeout;
141
142 //Set the time
143 auto r = sd_event_source_set_time(eventSource.get(), expireTime.count());
144 if (r < 0)
145 {
146 log<level::ERR>("Failed call to sd_event_source_set_time",
147 entry("ERROR=%s", strerror(-r)));
148 elog<InternalFailure>();
149 }
150}
151
152
153void Timer::start(std::chrono::microseconds timeValue,
154 TimerType timerType)
155{
156 type = timerType;
157
158 // Disable the timer
159 setTimer(SD_EVENT_OFF);
160
161 //Rearm the timer
162 timeout = timeValue;
163 setTimeout();
164
165 setTimer((type == TimerType::oneshot) ? SD_EVENT_ONESHOT : SD_EVENT_ON);
166}
167
168
169}
170}