blob: 7039338450939b81758e85d79f3edd84f23b61b5 [file] [log] [blame]
/**
* Copyright © 2017 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <chrono>
#include <phosphor-logging/log.hpp>
#include <phosphor-logging/elog.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
#include <type_traits>
#include "timer.hpp"
namespace witherspoon
{
namespace power
{
using namespace phosphor::logging;
using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
Error::InternalFailure;
Timer::Timer(event::Event& events,
std::function<void()> callbackFunc) :
timeEvent(events),
callback(callbackFunc),
timeout(0)
{
sd_event_source* source = nullptr;
// Start with an infinite expiration time
auto r = sd_event_add_time(timeEvent.get(),
&source,
CLOCK_MONOTONIC, // Time base
UINT64_MAX, // Expire time - way long time
0, // Use default event accuracy
timeoutHandler, // Callback handler on timeout
this); // User data
if (r < 0)
{
log<level::ERR>("Timer::Timer failed call to sd_event_add_time",
entry("ERROR=%s", strerror(-r)));
elog<InternalFailure>();
}
eventSource.reset(source);
//Ensure timer isn't running
setTimer(SD_EVENT_OFF);
}
Timer::~Timer()
{
setTimer(SD_EVENT_OFF);
}
int Timer::timeoutHandler(sd_event_source* eventSource,
uint64_t usec, void* userData)
{
auto timer = static_cast<Timer*>(userData);
if (timer->type == TimerType::repeating)
{
//Set the next expiration time
timer->setTimeout();
}
timer->callback();
return 0;
}
std::chrono::microseconds Timer::getTime()
{
using namespace std::chrono;
auto now = steady_clock::now().time_since_epoch();
return duration_cast<microseconds>(now);
}
void Timer::setTimer(int action)
{
auto r = sd_event_source_set_enabled(eventSource.get(), action);
if (r < 0)
{
log<level::ERR>("Failed call to sd_event_source_set_enabled",
entry("ERROR=%s", strerror(-r)),
entry("ACTION=%d", action));
elog<InternalFailure>();
}
}
void Timer::stop()
{
setTimer(SD_EVENT_OFF);
}
bool Timer::running()
{
int status = 0;
//returns SD_EVENT_OFF, SD_EVENT_ON, or SD_EVENT_ONESHOT
auto r = sd_event_source_get_enabled(eventSource.get(), &status);
if (r < 0)
{
log<level::ERR>("Failed call to sd_event_source_get_enabled",
entry("ERROR=%s", strerror(-r)));
elog<InternalFailure>();
}
return (status != SD_EVENT_OFF);
}
void Timer::setTimeout()
{
//Get the current time and add the delta
static_assert(std::is_same<decltype(getTime()),
std::chrono::microseconds>::value,
"Timer::getTime() is the wrong type");
static_assert(std::is_same<decltype(timeout),
std::chrono::microseconds>::value,
"Timer::timeout is the wrong type");
auto expireTime = getTime() + timeout;
//Set the time
auto r = sd_event_source_set_time(eventSource.get(), expireTime.count());
if (r < 0)
{
log<level::ERR>("Failed call to sd_event_source_set_time",
entry("ERROR=%s", strerror(-r)));
elog<InternalFailure>();
}
}
void Timer::start(std::chrono::microseconds timeValue,
TimerType timerType)
{
type = timerType;
// Disable the timer
setTimer(SD_EVENT_OFF);
//Rearm the timer
timeout = timeValue;
setTimeout();
setTimer((type == TimerType::oneshot) ? SD_EVENT_ONESHOT : SD_EVENT_ON);
}
}
}