blob: d371c980707f2f5d02674164fb4a428a39c6c37a [file] [log] [blame]
Matt Spinler2de67cf2017-04-27 11:07:53 -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 <type_traits>
19#include "timer.hpp"
20
21namespace phosphor
22{
23namespace fan
24{
25namespace util
26{
27
28using namespace phosphor::logging;
29
Matt Spinlere824f982017-05-11 10:07:55 -050030Timer::Timer(phosphor::fan::event::EventPtr& events,
Matt Spinler2de67cf2017-04-27 11:07:53 -050031 std::function<void()> callbackFunc) :
32 timeEvent(events),
33 callback(callbackFunc),
34 timeout(0)
35{
36 sd_event_source* source = nullptr;
37
38 // Start with an infinite expiration time
39 auto r = sd_event_add_time(timeEvent.get(),
40 &source,
41 CLOCK_MONOTONIC, // Time base
42 UINT64_MAX, // Expire time - way long time
43 0, // Use default event accuracy
44 timeoutHandler, // Callback handler on timeout
45 this); // User data
46 if (r < 0)
47 {
48 log<level::ERR>("Timer::Timer failed call to sd_event_add_time",
49 entry("ERROR=%s", strerror(-r)));
50 //TODO openbmc/openbmc#1555 throw an elog
51 throw std::runtime_error("Timer initialization failed");
52 }
53
54 eventSource.reset(source);
55
56 //Ensure timer isn't running
57 setTimer(SD_EVENT_OFF);
58}
59
60
61Timer::~Timer()
62{
63 setTimer(SD_EVENT_OFF);
64}
65
66
67int Timer::timeoutHandler(sd_event_source* eventSource,
68 uint64_t usec, void* userData)
69{
70 auto timer = static_cast<Timer*>(userData);
71
72 if (timer->type == TimerType::repeating)
73 {
74 //Set the next expiration time
75 timer->setTimeout();
76 }
77
78 timer->callback();
79
80 return 0;
81}
82
83
84std::chrono::microseconds Timer::getTime()
85{
86 using namespace std::chrono;
87 auto now = steady_clock::now().time_since_epoch();
88 return duration_cast<microseconds>(now);
89}
90
91
92void Timer::setTimer(int action)
93{
94 auto r = sd_event_source_set_enabled(eventSource.get(), action);
95 if (r < 0)
96 {
97 log<level::ERR>("Failed call to sd_event_source_set_enabled",
98 entry("ERROR=%s", strerror(-r)),
99 entry("ACTION=%d", action));
100 //TODO openbmc/openbmc#1555 throw an elog
101 throw std::runtime_error("Failed call to sd_event_source_set_enabled");
102 }
103}
104
105
106void Timer::stop()
107{
108 setTimer(SD_EVENT_OFF);
109}
110
111
112bool Timer::running()
113{
114 int status = 0;
115
116 //returns SD_EVENT_OFF, SD_EVENT_ON, or SD_EVENT_ONESHOT
117 auto r = sd_event_source_get_enabled(eventSource.get(), &status);
118 if (r < 0)
119 {
120 log<level::ERR>("Failed call to sd_event_source_get_enabled",
121 entry("ERROR=%s", strerror(-r)));
122 //TODO openbmc/openbmc#1555 throw an elog
123 throw std::runtime_error("Failed call to sd_event_source_get_enabled");
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 //TODO openbmc/openbmc#1555 throw an elog
149 throw std::runtime_error("Failed call to sd_event_source_set_time");
150 }
151}
152
153
154void Timer::start(std::chrono::microseconds timeValue,
155 TimerType timerType)
156{
157 type = timerType;
158
159 // Disable the timer
160 setTimer(SD_EVENT_OFF);
161
162 //Rearm the timer
163 timeout = timeValue;
164 setTimeout();
165
166 setTimer((type == TimerType::oneshot) ? SD_EVENT_ONESHOT : SD_EVENT_ON);
167}
168
169
170}
171}
172}