blob: 6332a26167ac762b59cd625ef1e4db0c81ff322d [file] [log] [blame]
/*
*
* Copyright (c) 2014-2017 Nest Labs, Inc.
* All rights reserved.
*
* 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.
*/
/**
* @file
* This file contains a sample implementation of platform-provided timing routines
* under the Weave::Platform::Time namespace. This sample implementation provides
* extra test hooks.
*/
// __STDC_LIMIT_MACROS must be defined for UINT8_MAX to be defined for pre-C++11 clib
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif // __STDC_LIMIT_MACROS
// __STDC_CONSTANT_MACROS must be defined for INT64_C and UINT64_C to be defined for pre-C++11 clib
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif // __STDC_CONSTANT_MACROS
// it is important for this first inclusion of stdint.h to have all the right switches turned ON
#include <stdint.h>
// __STDC_FORMAT_MACROS must be defined for PRIX64 to be defined for pre-C++11 clib
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif // __STDC_FORMAT_MACROS
// it is important for this first inclusion of inttypes.h to have all the right switches turned ON
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
// we might have to use gettimeofday for remote proximiation of clock_gettime(CLOCK_BOOTTIME)
#include <sys/time.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include "TestPlatformTime.h"
#if WEAVE_CONFIG_TIME
using namespace nl::Weave::Profiles::Time;
namespace nl {
namespace Weave {
namespace MockPlatform {
/**
* Offset to system time for testing purposes
*/
timesync_t gTestOffsetToSystemTime_usec;
/**
* The Platform::Time external functions are decoupled from their
* implementation using this structure of function pointers.
* This allows test applications to override the implementation of
* any of the functions with a private one.
*/
struct TestPlatformTimeFns gTestPlatformTimeFns = {
nl::Weave::MockPlatform::GetMonotonicRawTime,
nl::Weave::MockPlatform::GetSystemTime,
nl::Weave::MockPlatform::GetSystemTimeMs,
nl::Weave::MockPlatform::GetSleepCompensatedMonotonicTime,
nl::Weave::MockPlatform::SetSystemTime
};
#if HAVE_CLOCK_GETTIME && HAVE_DECL_CLOCK_BOOTTIME
/**
* Convert timespec, the data structure used in clock_gettime, to timesync_t, which we use
*
* @param[in] src The timespec instance
* @param[out] p_dst_usec Pointer to the destination timesync_t instance
*
* @return WEAVE_ERROR_INVALID_ARGUMENT is the input is out of range
*/
static WEAVE_ERROR ConvertTimespecToTimesync(const timespec & src, timesync_t * const p_dst_usec)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if ((src.tv_nsec < 0) || (src.tv_nsec > 1000000000LL) || (src.tv_sec < 0) || (src.tv_sec > MAX_TIMESYNC_SEC))
{
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
}
*p_dst_usec = timesync_t(src.tv_sec) * 1000000;
*p_dst_usec += (src.tv_nsec / 1000);
exit:
WeaveLogFunctError(err);
return err;
}
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetSleepCompensatedMonotonicTime
*/
WEAVE_ERROR GetSleepCompensatedMonotonicTime(timesync_t * const p_timestamp_usec)
{
const int clock_id = CLOCK_BOOTTIME;
WEAVE_ERROR err = WEAVE_NO_ERROR;
timespec ts_now;
if (0 != clock_gettime(clock_id, &ts_now))
{
err = nl::Weave::System::MapErrorPOSIX(errno);
goto exit;
}
err = ConvertTimespecToTimesync(ts_now, p_timestamp_usec);
SuccessOrExit(err);
exit:
WeaveLogFunctError(err);
return err;
}
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetMonotonicRawTime
*/
WEAVE_ERROR GetMonotonicRawTime(timesync_t * const p_timestamp_usec)
{
// monotonic_raw is slightly more stable than monotonic, as it is not corrected/adjusted
#if defined(CLOCK_MONOTONIC_RAW)
const int clock_id = CLOCK_MONOTONIC_RAW;
#else
const int clock_id = CLOCK_MONOTONIC;
#endif
WEAVE_ERROR err = WEAVE_NO_ERROR;
timespec ts_now;
if (0 != clock_gettime(clock_id, &ts_now))
{
err = nl::Weave::System::MapErrorPOSIX(errno);
goto exit;
}
err = ConvertTimespecToTimesync(ts_now, p_timestamp_usec);
SuccessOrExit(err);
exit:
WeaveLogFunctError(err);
return err;
}
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetSystemTime
*/
WEAVE_ERROR GetSystemTime(timesync_t * const p_timestamp_usec)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
static timesync_t offsetMonotonicRawToSystemTime = TIMESYNC_INVALID;
timesync_t rawNow_usec;
err = nl::Weave::Platform::Time::GetMonotonicRawTime(&rawNow_usec);
SuccessOrExit(err);
WeaveLogProgress(TimeService, "Now (monotonic raw): %" PRId64 " usec", rawNow_usec);
if (TIMESYNC_INVALID == offsetMonotonicRawToSystemTime)
{
timespec ts_now;
if (0 != clock_gettime(CLOCK_REALTIME, &ts_now))
{
err = nl::Weave::System::MapErrorPOSIX(errno);
goto exit;
}
err = ConvertTimespecToTimesync(ts_now, p_timestamp_usec);
SuccessOrExit(err);
offsetMonotonicRawToSystemTime = *p_timestamp_usec - rawNow_usec;
}
else
{
*p_timestamp_usec = rawNow_usec + offsetMonotonicRawToSystemTime;
}
// apply fake offset for testing purpose
*p_timestamp_usec += gTestOffsetToSystemTime_usec;
WeaveLogProgress(TimeService, "Mock offset: %" PRId64 " usec", gTestOffsetToSystemTime_usec);
WeaveLogProgress(TimeService, "Mock System Time %f sec", (*p_timestamp_usec) * 1e-6);
exit:
WeaveLogFunctError(err);
return err;
}
#elif HAVE_GETTIMEOFDAY // HAVE_CLOCK_GETTIME && HAVE_DECL_CLOCK_BOOTTIME
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetSleepCompensatedMonotonicTime
*/
WEAVE_ERROR GetSleepCompensatedMonotonicTime(timesync_t * const p_timestamp_usec)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
timeval ts_now;
if (0 != gettimeofday(&ts_now, NULL))
{
err = nl::Weave::System::MapErrorPOSIX(errno);
goto exit;
}
*p_timestamp_usec = timesync_t(ts_now.tv_sec) * 1000000;
*p_timestamp_usec += ts_now.tv_usec;
exit:
WeaveLogFunctError(err);
return err;
}
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetMonotonicRawTime
*/
WEAVE_ERROR GetMonotonicRawTime(timesync_t * const p_timestamp_usec)
{
return nl::Weave::MockPlatform::gTestPlatformTimeFns.GetSleepCompensatedMonotonicTime(p_timestamp_usec);
}
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetSystemTime
*/
WEAVE_ERROR GetSystemTime(timesync_t * const p_timestamp_usec)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
err = gTestPlatformTimeFns.GetSleepCompensatedMonotonicTime(p_timestamp_usec);
SuccessOrExit(err);
// apply fake offset for testing purpose
*p_timestamp_usec += gTestOffsetToSystemTime_usec;
WeaveLogProgress(TimeService, "Mock System Time %f sec", (*p_timestamp_usec) * 1e-6);
exit:
WeaveLogFunctError(err);
return err;
}
#else // HAVE_CLOCK_GETTIME && HAVE_DECL_CLOCK_BOOTTIME
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetSleepCompensatedMonotonicTime
*/
WEAVE_ERROR GetSleepCompensatedMonotonicTime(timesync_t * const p_timestamp_usec)
{
return WEAVE_ERROR_NOT_IMPLEMENTED;
}
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetMonotonicRawTime
*/
WEAVE_ERROR GetMonotonicRawTime(timesync_t * const p_timestamp_usec)
{
return WEAVE_ERROR_NOT_IMPLEMENTED;
}
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetSystemTime
*/
WEAVE_ERROR GetSystemTime(timesync_t * const p_timestamp_usec)
{
return WEAVE_ERROR_NOT_IMPLEMENTED;
}
#endif // HAVE_CLOCK_GETTIME && HAVE_DECL_CLOCK_BOOTTIME
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::GetSystemTimeMs
*/
WEAVE_ERROR GetSystemTimeMs(timesync_t * const p_timestamp_msec)
{
return WEAVE_ERROR_NOT_IMPLEMENTED;
}
/**
* Mock platform internal implementation of @see nl::Weave::Platform::Time::SetSystemTime
*/
WEAVE_ERROR SetSystemTime(const timesync_t timestamp_usec)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
timesync_t modifiedSystemTime_usec = 0;
if ((timestamp_usec < 0) || (timestamp_usec & MASK_INVALID_TIMESYNC))
{
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
}
// apply correction to the mock system time
err = gTestPlatformTimeFns.GetSystemTime(&modifiedSystemTime_usec);
SuccessOrExit(err);
gTestOffsetToSystemTime_usec += (timestamp_usec - modifiedSystemTime_usec);
WeaveLogProgress(TimeService, "Correction to system time %f sec", (timestamp_usec - modifiedSystemTime_usec) * 1e-6);
exit:
WeaveLogFunctError(err);
return err;
}
} // namespace MockPlatform
} // namespace Weave
} // namespace nl
WEAVE_ERROR nl::Weave::Platform::Time::GetMonotonicRawTime(Profiles::Time::timesync_t * const p_timestamp_usec)
{
return nl::Weave::MockPlatform::gTestPlatformTimeFns.GetMonotonicRawTime(p_timestamp_usec);
}
WEAVE_ERROR nl::Weave::Platform::Time::GetSystemTime(Profiles::Time::timesync_t * const p_timestamp_usec)
{
return nl::Weave::MockPlatform::gTestPlatformTimeFns.GetSystemTime(p_timestamp_usec);
}
WEAVE_ERROR nl::Weave::Platform::Time::GetSystemTimeMs(Profiles::Time::timesync_t * const p_timestamp_msec)
{
return nl::Weave::MockPlatform::gTestPlatformTimeFns.GetSystemTimeMs(p_timestamp_msec);
}
WEAVE_ERROR nl::Weave::Platform::Time::SetSystemTime(const Profiles::Time::timesync_t timestamp_usec)
{
return nl::Weave::MockPlatform::gTestPlatformTimeFns.SetSystemTime(timestamp_usec);
}
WEAVE_ERROR nl::Weave::Platform::Time::GetSleepCompensatedMonotonicTime(Profiles::Time::timesync_t * const p_timestamp_usec)
{
return nl::Weave::MockPlatform::gTestPlatformTimeFns.GetSleepCompensatedMonotonicTime(p_timestamp_usec);
}
#endif // WEAVE_CONFIG_TIME