Files
Chromebook-Device-Nyan-NVID…/drivers/platform/x86/intel_dptf/esif_ccb_timer.h
2025-04-06 23:50:55 -05:00

894 lines
24 KiB
C

/*******************************************************************************
** This file is provided under a dual BSD/GPLv2 license. When using or
** redistributing this file, you may do so under either license.
**
** GPL LICENSE SUMMARY
**
** Copyright (c) 2013 Intel Corporation All Rights Reserved
**
** This program is free software; you can redistribute it and/or modify it under
** the terms of version 2 of the GNU General Public License as published by the
** Free Software Foundation.
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
** details.
**
** You should have received a copy of the GNU General Public License along with
** this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
** The full GNU General Public License is included in this distribution in the
** file called LICENSE.GPL.
**
** BSD LICENSE
**
** Copyright (c) 2013 Intel Corporation All Rights Reserved
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
**
** * Redistributions of source code must retain the above copyright notice, this
** list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright notice,
** this list of conditions and the following disclaimer in the documentation
** and/or other materials provided with the distribution.
** * Neither the name of Intel Corporation nor the names of its contributors may
** be used to endorse or promote products derived from this software without
** specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
**
*******************************************************************************/
#ifndef _ESIF_CCB_TIMER_H_
#define _ESIF_CCB_TIMER_H_
#include "esif_ccb_lock.h"
#include "esif_ccb_sem.h"
#ifdef ESIF_ATTR_OS_WINDOWS
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
/*
* Coalescable timer options
*/
#define TOLERABLE_DELAY_DIVISOR 10 /* timeout divisor */
#define TOLERABLE_DELAY_INCREMENT 50 /* ms */
#define DUE_TIME_MS_CONV_FACTOR 10000
#endif
#endif
/******************************************************************************
* KERNEL TIMER
******************************************************************************/
#ifdef ESIF_ATTR_KERNEL
/* Timer Callback Function */
typedef void (*esif_ccb_timer_cb)(void *context);
/*
* Timer Is Defined to be opaue here to acoomondate different
* OS and implementation approaches for timers
*/
#ifdef ESIF_ATTR_OS_LINUX
typedef struct {
struct delayed_work work;
esif_ccb_timer_cb function_ptr;
esif_ccb_low_priority_thread_lock_t context_lock;
u32 exit_flag;
u32 timer_period_msec;
u32 periodic_flag;
void *context_ptr;
} esif_ccb_timer_t;
#endif /* ESIF_ATTR_OS_LINUX */
#ifdef ESIF_ATTR_OS_WINDOWS
typedef struct {
esif_ccb_timer_cb function_ptr;
esif_ccb_low_priority_thread_lock_t context_lock;
u32 exit_flag;
esif_ccb_time_t timer_period_msec;
u32 periodic_flag;
void *context_ptr;
} esif_ccb_timer_context_t;
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
typedef struct esif_ccb_timer {
KTIMER timer;
KDPC dpc;
esif_ccb_timer_context_t timer_context;
} esif_ccb_timer_t;
typedef struct esif_ccb_work_item_context {
void *ptr;
} esif_ccb_work_item_context_t;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(esif_ccb_work_item_context_t,
esif_ccb_get_work_item_context)
#else
typedef WDFTIMER esif_ccb_timer_t;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(esif_ccb_timer_context_t,
esif_ccb_get_timer_context)
#endif
#endif /* ESIF_ATTR_OS_WINDOWS */
static enum esif_rc esif_ccb_timer_set_msec(
esif_ccb_timer_t *timer_ptr,
esif_ccb_time_t timeout,
esif_flags_t periodic,
esif_ccb_timer_cb function_ptr,
void *context_ptr
);
#ifdef ESIF_ATTR_OS_LINUX
/* Timer Callback Wrapper Find And Fire Function */
static ESIF_INLINE void esif_ccb_timer_cb_wrapper(struct work_struct *work)
{
esif_ccb_timer_t *timer_ptr = container_of(
(struct delayed_work *)work,
esif_ccb_timer_t,
work);
TIMER_DEBUG("%s: timer fired!!!!!\n", ESIF_FUNC);
if (NULL == timer_ptr)
goto exit;
esif_ccb_low_priority_thread_read_lock(&timer_ptr->context_lock);
if (!timer_ptr->exit_flag) {
timer_ptr->function_ptr(timer_ptr->context_ptr);
/* RESET The Timer */
if (timer_ptr->periodic_flag) {
esif_ccb_timer_set_msec(timer_ptr,
timer_ptr->timer_period_msec,
ESIF_TRUE,
timer_ptr->function_ptr,
timer_ptr->context_ptr);
}
}
esif_ccb_low_priority_thread_read_unlock(&timer_ptr->context_lock);
exit:
;
}
#endif /* ESIF_ATTR_OS_LINUX */
#ifdef ESIF_ATTR_OS_WINDOWS
/* Timer Callback Wrapper Find And Fire Function */
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
static KDEFERRED_ROUTINE esif_ccb_timer_dpc;
static EVT_WDF_WORKITEM esif_ccb_timer_cb_wrapper;
static void esif_ccb_timer_cb_wrapper(
WDFWORKITEM work_item
)
{
esif_ccb_work_item_context_t *work_item_context_ptr = NULL;
esif_ccb_timer_t *timer_ptr = NULL;
esif_ccb_timer_context_t *timer_context_ptr = NULL;
TIMER_DEBUG("%s: timer fired!!!!!\n", ESIF_FUNC);
work_item_context_ptr = esif_ccb_get_work_item_context(work_item);
if(NULL == work_item_context_ptr) {
goto exit;
}
timer_ptr = (esif_ccb_timer_t *)work_item_context_ptr->ptr;
if(NULL == timer_ptr) {
goto exit;
}
timer_context_ptr = &timer_ptr->timer_context;
if (NULL == timer_context_ptr)
goto exit;
esif_ccb_low_priority_thread_read_lock(
&timer_context_ptr->context_lock);
if (!timer_context_ptr->exit_flag) {
timer_context_ptr->function_ptr(timer_context_ptr->context_ptr);
/* RESET The Timer */
if (timer_context_ptr->periodic_flag) {
esif_ccb_timer_set_msec(
timer_ptr,
timer_context_ptr->timer_period_msec,
ESIF_TRUE,
timer_context_ptr->function_ptr,
timer_context_ptr->context_ptr);
}
}
esif_ccb_low_priority_thread_read_unlock(
&timer_context_ptr->context_lock);
exit:
WdfObjectDelete(work_item);
}
static void esif_ccb_timer_dpc (
struct _KDPC *Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2
)
{
NTSTATUS status = STATUS_SUCCESS;
esif_ccb_work_item_context_t *work_item_context_ptr = NULL;
WDF_WORKITEM_CONFIG workItemConfig = {0};
WDF_OBJECT_ATTRIBUTES workItemAttribs = {0};
WDFWORKITEM workItem = NULL;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
WDF_WORKITEM_CONFIG_INIT(&workItemConfig, esif_ccb_timer_cb_wrapper);
workItemConfig.AutomaticSerialization = TRUE;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&workItemAttribs,
esif_ccb_work_item_context_t);
workItemAttribs.ParentObject = g_wdf_ipc_queue_handle;
status = WdfWorkItemCreate(&workItemConfig, &workItemAttribs, &workItem);
if (NT_SUCCESS(status)) {
work_item_context_ptr = esif_ccb_get_work_item_context(workItem);
work_item_context_ptr->ptr = DeferredContext;
WdfWorkItemEnqueue(workItem);
}
}
#else /* NOT ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
EVT_WDF_TIMER esif_ccb_timer_cb_wrapper;
ESIF_INLINE void esif_ccb_timer_cb_wrapper(WDFTIMER timer)
{
esif_ccb_timer_context_t *timer_context_ptr =
esif_ccb_get_timer_context(timer);
TIMER_DEBUG("%s: timer fired!!!!!\n", ESIF_FUNC);
if (NULL == timer_context_ptr)
goto exit;
esif_ccb_low_priority_thread_read_lock(
&timer_context_ptr->context_lock);
if (!timer_context_ptr->exit_flag) {
timer_context_ptr->function_ptr(timer_context_ptr->context_ptr);
/* RESET The Timer */
if (timer_context_ptr->periodic_flag) {
esif_ccb_timer_set_msec(&timer,
timer_context_ptr->timer_period_msec,
ESIF_TRUE,
timer_context_ptr->function_ptr,
timer_context_ptr->context_ptr);
}
}
esif_ccb_low_priority_thread_read_unlock(
&timer_context_ptr->context_lock);
exit:
(0);
}
#endif /* NOT ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
#endif /* ESIF_ATTR_OS_WINDOWS */
/* Timer Initialize */
static ESIF_INLINE
enum esif_rc esif_ccb_timer_init(esif_ccb_timer_t *timer_ptr)
{
enum esif_rc rc = ESIF_E_UNSPECIFIED;
#ifdef ESIF_ATTR_OS_LINUX
TIMER_DEBUG("%s: timer %p\n", ESIF_FUNC, timer_ptr);
INIT_DELAYED_WORK(&timer_ptr->work, NULL);
esif_ccb_low_priority_thread_lock_init(&timer_ptr->context_lock);
timer_ptr->exit_flag = FALSE;
rc = ESIF_OK;
#endif
#ifdef ESIF_ATTR_OS_WINDOWS
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
esif_ccb_memset(timer_ptr, 0, sizeof(*timer_ptr));
KeInitializeDpc(&timer_ptr->dpc, esif_ccb_timer_dpc, timer_ptr);
KeInitializeTimer(&timer_ptr->timer);
esif_ccb_low_priority_thread_lock_init(
&timer_ptr->timer_context.context_lock);
timer_ptr->timer_context.exit_flag = FALSE;
TIMER_DEBUG("%s: timer %p\n", ESIF_FUNC, timer_ptr);
rc = ESIF_OK;
#else /* NOT ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
NTSTATUS status;
WDF_TIMER_CONFIG timer_config = {0};
WDF_OBJECT_ATTRIBUTES timer_attributes = {0};
esif_ccb_timer_context_t *timer_context_ptr = NULL;
TIMER_DEBUG("%s: timer %p\n", ESIF_FUNC, timer_ptr);
WDF_TIMER_CONFIG_INIT(&timer_config, esif_ccb_timer_cb_wrapper);
timer_config.AutomaticSerialization = TRUE;
WDF_OBJECT_ATTRIBUTES_INIT(&timer_attributes);
timer_attributes.ParentObject = g_wdf_ipc_queue_handle;
timer_attributes.ExecutionLevel = WdfExecutionLevelPassive;
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&timer_attributes,
esif_ccb_timer_context_t);
status = WdfTimerCreate(&timer_config, &timer_attributes, timer_ptr);
TIMER_DEBUG("%s: timer %p status %08x\n", ESIF_FUNC, timer_ptr, status);
if (!NT_SUCCESS(status))
goto exit;
timer_context_ptr = esif_ccb_get_timer_context(*timer_ptr);
if (timer_context_ptr == NULL)
goto exit;
esif_ccb_low_priority_thread_lock_init(&timer_context_ptr->context_lock);
timer_context_ptr->exit_flag = FALSE;
rc = ESIF_OK;
exit:
#endif /* NOT ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
#endif /* ESIF_ATTR_OS_WINDOWS */
return rc;
}
/* Timer Set */
static ESIF_INLINE enum esif_rc esif_ccb_timer_set_msec(
esif_ccb_timer_t *timer_ptr,
esif_ccb_time_t timeout,
esif_flags_t periodic,
esif_ccb_timer_cb function_ptr,
void *context_ptr
)
{
enum esif_rc rc = ESIF_E_UNSPECIFIED;
#ifdef ESIF_ATTR_OS_LINUX
u64 x;
u64 y;
u8 result;
#
timer_ptr->periodic_flag = periodic;
timer_ptr->timer_period_msec = timeout;
timer_ptr->function_ptr = function_ptr;
timer_ptr->context_ptr = context_ptr;
TIMER_DEBUG("%s: timer %p timeout %u\n",
ESIF_FUNC,
timer_ptr,
(int)timeout);
PREPARE_DELAYED_WORK(&timer_ptr->work, esif_ccb_timer_cb_wrapper);
/* Keep 32 bit Linux Happy */
x = timeout * HZ;
y = 1000;
do_div(x, y);
result = schedule_delayed_work(&timer_ptr->work, x);
if (ESIF_TRUE == result)
rc = ESIF_OK;
#endif
#ifdef ESIF_ATTR_OS_WINDOWS
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
esif_ccb_timer_context_t *timer_context_ptr = NULL;
LARGE_INTEGER due_time = {0LL};
u32 tolerable_delay = 0;
TIMER_DEBUG("%s: timer %p timeout %u\n", ESIF_FUNC, timer_ptr, timeout);
if(NULL == timer_ptr) {
rc = ESIF_E_PARAMETER_IS_NULL;
goto exit;
}
timer_context_ptr = &timer_ptr->timer_context;
timer_context_ptr->function_ptr = function_ptr;
timer_context_ptr->context_ptr = context_ptr;
timer_context_ptr->periodic_flag = periodic;
timer_context_ptr->timer_period_msec = timeout;
timer_context_ptr->exit_flag = FALSE;
/*
* Calculate the delay that can be tolerated, with a minimum of at
* least 1 TOLERABLE_DELAY_INCREMENT.
*/
tolerable_delay = (u32) timeout / TOLERABLE_DELAY_DIVISOR;
tolerable_delay = (tolerable_delay / TOLERABLE_DELAY_INCREMENT) + 1;
tolerable_delay *= TOLERABLE_DELAY_INCREMENT;
due_time.QuadPart = (LONGLONG)timeout * -10000;
KeSetCoalescableTimer(&timer_ptr->timer,
due_time,
0,
tolerable_delay,
&timer_ptr->dpc);
rc = ESIF_OK;
exit:
#else /* NOT ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
esif_ccb_timer_context_t *timer_context_ptr = NULL;
BOOLEAN status;
TIMER_DEBUG("%s: timer %p timeout %u\n", ESIF_FUNC, timer_ptr, timeout);
if (NULL == timer_ptr) {
rc = ESIF_E_PARAMETER_IS_NULL;
goto exit;
}
/* Set Context */
timer_context_ptr = esif_ccb_get_timer_context(*timer_ptr);
if (NULL != timer_context_ptr) {
timer_context_ptr->timer_period_msec = timeout;
timer_context_ptr->periodic_flag = periodic;
timer_context_ptr->function_ptr = function_ptr;
timer_context_ptr->context_ptr = context_ptr;
timer_context_ptr->exit_flag = FALSE;
}
/* Finally Start Timer */
status = WdfTimerStart(*timer_ptr, (LONG)timeout * -10000);
TIMER_DEBUG("%s: timer %p status %08x\n", ESIF_FUNC, timer_ptr, status);
if (TRUE == status)
rc = ESIF_OK;
exit:
#endif /* NOT ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
#endif /* ESIF_ATTR_OS_WINDOWS */
return rc;
}
/* Timer Stop And Destory */
static ESIF_INLINE
enum esif_rc esif_ccb_timer_kill(esif_ccb_timer_t *timer)
{
enum esif_rc rc = ESIF_E_UNSPECIFIED;
#ifdef ESIF_ATTR_OS_LINUX
TIMER_DEBUG("%s: timer %p\n", ESIF_FUNC, timer);
esif_ccb_low_priority_thread_write_lock(&timer->context_lock);
timer->exit_flag = TRUE;
esif_ccb_low_priority_thread_write_unlock(&timer->context_lock);
if (ESIF_TRUE == cancel_delayed_work(&timer->work))
rc = ESIF_OK;
#endif
#ifdef ESIF_ATTR_OS_WINDOWS
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
esif_ccb_timer_context_t *timer_context_ptr = NULL;
TIMER_DEBUG("%s: timer %p\n", ESIF_FUNC, timer);
if(NULL == timer) {
goto exit;
}
timer_context_ptr = &timer->timer_context;
if (timer_context_ptr != NULL) {
esif_ccb_low_priority_thread_write_lock(
&timer_context_ptr->context_lock);
timer_context_ptr->exit_flag = TRUE;
esif_ccb_low_priority_thread_write_unlock(
&timer_context_ptr->context_lock);
}
KeCancelTimer(&timer->timer);
exit:
rc = ESIF_OK;
#else /* NOT ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
esif_ccb_timer_context_t *timer_context_ptr = NULL;
TIMER_DEBUG("%s: timer %p\n", ESIF_FUNC, timer);
timer_context_ptr = esif_ccb_get_timer_context(*timer);
if (timer_context_ptr != NULL) {
esif_ccb_low_priority_thread_write_lock(
&timer_context_ptr->context_lock);
timer_context_ptr->exit_flag = TRUE;
esif_ccb_low_priority_thread_write_unlock(
&timer_context_ptr->context_lock);
}
/* WDF Framework Will Cleanup */
WdfTimerStop(*timer, FALSE);
rc = ESIF_OK;
#endif /* NOT ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
#endif /* ESIF_ATTR_OS_WINDOWS */
return rc;
}
#endif /* ESIF_ATTR_KERNEL*/
/******************************************************************************
* USER TIMER
******************************************************************************/
#ifdef ESIF_ATTR_USER
#include "esif.h"
/* OS Agnostic Callback Function */
typedef void (ESIF_CALLCONV *esif_ccb_timer_cb)(const void *context_ptr);
#pragma pack(push, 1)
/* OS Agnostic Timer Context */
typedef struct esif_ccb_timer_ctx {
esif_ccb_timer_cb cb_func; /* Call Back Function */
void *cb_context_ptr; /* Call Back Function Context */
} esif_ccb_timer_ctx_t;
#pragma pack(pop)
#ifdef ESIF_ATTR_OS_LINUX
#include <signal.h>
#pragma pack(push,1)
/* Linux Timer */
typedef struct esif_ccb_timer {
timer_t timer; /* Linux specific timer */
esif_ccb_timer_ctx_t *timer_ctx_ptr; /* OS Agnostic timer context */
esif_ccb_mutex_t context_lock; /* Call back function lock */
} esif_ccb_timer_t;
#pragma pack(pop)
/*
* Each OS Expects its own CALLBACK primitive we use this
* wrapper function to normalize the parameters and and
* ultimately call our func_ptr and func_context originally
* provided to the timer. By example Linux expects a union
* here that contains our timer context poiner which in turn
* contains our function and context for the function.
*/
/* Linux Timer Behavior Note
* Reference : http://man7.org/linux/man-pages/man2/timer_create.2.html
*
* Because of the way POSIX timer callbacks are handled, it is possible
* that the OS may have a pending timer notification for a timer that
* was cancelled, reinitialized, and reset from a user space app. In
* that instance, if you are using the same timer repeatedly you may see
* 2 callbacks for a single timer due to this pending signal issue.
*/
static ESIF_INLINE void esif_ccb_timer_cb_wrapper(
const union sigval sv
)
{
esif_ccb_timer_t* timer_ptr = (esif_ccb_timer_t *)sv.sival_ptr;
ESIF_ASSERT(timer_ptr->timer_ctx_ptr != NULL);
ESIF_ASSERT(timer_ptr->timer_ctx_ptr->cb_func != NULL);
ESIF_ASSERT(timer_ptr->timer_ctx_ptr->cb_context_ptr != NULL);
esif_ccb_mutex_lock(&(timer_ptr->context_lock));
if (NULL != timer_ptr->timer_ctx_ptr &&
NULL != timer_ptr->timer_ctx_ptr->cb_func &&
NULL != timer_ptr->timer_ctx_ptr->cb_context_ptr) {
timer_ptr->timer_ctx_ptr->cb_func(
timer_ptr->timer_ctx_ptr->cb_context_ptr);
}
esif_ccb_mutex_unlock(&(timer_ptr->context_lock));
}
#endif /* ESIF_ATTR_OS_LINUX */
#ifdef ESIF_ATTR_OS_WINDOWS
#include <WinBase.h>
#pragma pack(push,1)
/* Windows Timer */
typedef struct esif_ccb_timer {
HANDLE timer; /* Windows specific timer */
HANDLE timer_wait_handle;
esif_ccb_timer_ctx_t *timer_ctx_ptr; /* OS Agnostic timer context */
} esif_ccb_timer_t;
#pragma pack(pop)
/*
* Each OS Expects its own CALLBACK primitive we use this
* wrapper function to normalize the parameters and and
* ultimately call our func_ptr and func_context originally
* provided to the timer. By example Windows expects two
* parameters of which we only use one. The other one is
* always true.
*/
static ESIF_INLINE void NTAPI esif_ccb_timer_cb_wrapper(
void *context_ptr,
BOOLEAN notUsed
)
{
esif_ccb_timer_ctx_t *timer_ctx_ptr =
(esif_ccb_timer_ctx_t *)context_ptr;
ESIF_ASSERT(timer_ctx_ptr != NULL);
ESIF_ASSERT(timer_ctx_ptr->cb_func != NULL);
ESIF_ASSERT(timer_ctx_ptr->cb_context_ptr != NULL);
UNREFERENCED_PARAMETER(notUsed);
if (NULL != timer_ctx_ptr &&
NULL != timer_ctx_ptr->cb_func &&
NULL != timer_ctx_ptr->cb_context_ptr) {
timer_ctx_ptr->cb_func(timer_ctx_ptr->cb_context_ptr);
}
}
#endif /* ESIF_ATTR_OS_WINDOWS */
static ESIF_INLINE eEsifError esif_ccb_timer_init(
esif_ccb_timer_t *timer_ptr, /* Our Timer */
const esif_ccb_timer_cb function_ptr, /* Callback when timer fires */
void *context_ptr /* Callback context if any */
)
{
eEsifError rc = ESIF_E_UNSPECIFIED;
#ifdef ESIF_ATTR_OS_LINUX
struct sigevent se;
pthread_attr_t attr;
pthread_attr_init(&attr);
#endif
ESIF_ASSERT(timer_ptr != NULL);
ESIF_ASSERT(function_ptr != NULL);
if (NULL == timer_ptr) {
rc = ESIF_E_PARAMETER_IS_NULL;
goto exit;
}
esif_ccb_memset(timer_ptr, 0, sizeof(*timer_ptr));
#ifdef ESIF_ATTR_OS_WINDOWS
timer_ptr->timer = CreateWaitableTimer(NULL, TRUE, NULL);
if (NULL == timer_ptr->timer)
goto exit;
rc = ESIF_OK;
#endif
/* Allocate and setup new context */
timer_ptr->timer_ctx_ptr = (esif_ccb_timer_ctx_t *)
esif_ccb_malloc(sizeof(*timer_ptr->timer_ctx_ptr));
if (NULL == timer_ptr->timer_ctx_ptr) {
rc = ESIF_E_NO_MEMORY;
goto exit;
}
/* Store state for timer set */
timer_ptr->timer_ctx_ptr->cb_func = function_ptr;
timer_ptr->timer_ctx_ptr->cb_context_ptr = context_ptr;
#ifdef ESIF_ATTR_OS_LINUX
/* Initialize the callback lock and exit flag */
esif_ccb_mutex_init(&(timer_ptr->context_lock));
se.sigev_notify = SIGEV_THREAD;
se.sigev_notify_function = esif_ccb_timer_cb_wrapper;
se.sigev_value.sival_ptr = timer_ptr;
se.sigev_notify_attributes = &attr;
if (0 == timer_create(CLOCK_REALTIME, &se,
(timer_t *)&timer_ptr->timer)) {
rc = ESIF_OK;
} else {
esif_ccb_free(timer_ptr->timer_ctx_ptr );
timer_ptr->timer_ctx_ptr = NULL;
}
#endif
exit:
return rc;
}
static ESIF_INLINE eEsifError esif_ccb_timer_set_msec(
esif_ccb_timer_t *timer_ptr, /* Our Timer */
const esif_ccb_time_t timeout /* Timeout in msec */
)
{
eEsifError rc = ESIF_E_UNSPECIFIED;
#ifdef ESIF_ATTR_OS_LINUX
struct itimerspec its;
u64 freq_nanosecs = timeout * 1000 * 1000; /* convert msec to nsec */
#endif
#ifdef ESIF_ATTR_OS_WINDOWS
u32 ret_val = 0;
u32 wait_timeout = (u32) timeout;
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
u32 tolerable_delay = 0;
LARGE_INTEGER due_time = {0};
#endif
#endif /* ESIF_ATTR_OS_WINDOWS */
ESIF_ASSERT(timer_ptr != NULL);
if (NULL == timer_ptr) {
rc = ESIF_E_NO_MEMORY;
goto exit;
}
#ifdef ESIF_ATTR_OS_WINDOWS
if (NULL == timer_ptr->timer)
goto exit;
if (NULL != timer_ptr->timer_wait_handle) {
UnregisterWaitEx(timer_ptr->timer_wait_handle,
INVALID_HANDLE_VALUE);
timer_ptr->timer_wait_handle = NULL;
}
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
/*
* Calculate the delay that can be tolerated, with a minimum of at
* least 1 TOLERABLE_DELAY_INCREMENT.
*/
tolerable_delay = (u32) timeout / TOLERABLE_DELAY_DIVISOR;
tolerable_delay = (tolerable_delay / TOLERABLE_DELAY_INCREMENT) + 1;
tolerable_delay *= TOLERABLE_DELAY_INCREMENT;
/*
* For the coalescable timer, we use the timeout of the timer; not
* the timeout of the waiting thread.
*/
wait_timeout = INFINITE;
due_time.QuadPart = -1LL * timeout * DUE_TIME_MS_CONV_FACTOR;
ret_val = SetWaitableTimerEx(timer_ptr->timer,
&due_time,
0,
NULL,
NULL,
NULL,
tolerable_delay);
if (!ret_val)
goto exit;
#endif /* ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS */
ret_val = RegisterWaitForSingleObject(&timer_ptr->timer_wait_handle,
timer_ptr->timer,
esif_ccb_timer_cb_wrapper,
timer_ptr->timer_ctx_ptr,
(u32)wait_timeout,
WT_EXECUTEONLYONCE);
if (!ret_val) {
#ifdef ESIF_FEAT_OPT_USE_COALESCABLE_TIMERS
CancelWaitableTimer(timer_ptr->timer);
#endif
goto exit;
}
rc = ESIF_OK;
#endif /* ESIF_ATTR_OS_WINDOWS */
#ifdef ESIF_ATTR_OS_LINUX
its.it_value.tv_sec = freq_nanosecs / 1000000000;
its.it_value.tv_nsec = freq_nanosecs % 1000000000;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
if (0 == timer_settime(timer_ptr->timer, 0, &its, NULL))
rc = ESIF_OK;
#endif /* ESIF_ATTR_OS_LINUX */
exit:
return rc;
}
static ESIF_INLINE eEsifError esif_ccb_timer_kill(
esif_ccb_timer_t *timer_ptr /* Our Timer */
)
{
eEsifError rc = ESIF_E_UNSPECIFIED;
if (NULL == timer_ptr)
goto exit;
#ifdef ESIF_ATTR_OS_WINDOWS
if (NULL != timer_ptr->timer_wait_handle) {
UnregisterWaitEx(timer_ptr->timer_wait_handle,
INVALID_HANDLE_VALUE );
timer_ptr->timer_wait_handle = NULL;
}
if (timer_ptr->timer != NULL) {
CloseHandle(timer_ptr->timer);
timer_ptr->timer = NULL;
}
rc = ESIF_OK;
#endif
#ifdef ESIF_ATTR_OS_LINUX
esif_ccb_mutex_lock(&(timer_ptr->context_lock));
if (0 == timer_delete(timer_ptr->timer))
rc = ESIF_OK;
#endif
if (timer_ptr->timer_ctx_ptr != NULL) {
esif_ccb_free(timer_ptr->timer_ctx_ptr);
timer_ptr->timer_ctx_ptr = NULL;
}
#ifdef ESIF_ATTR_OS_LINUX
esif_ccb_mutex_unlock(&(timer_ptr->context_lock));
#endif
exit:
return rc;
}
#endif /* ESIF_ATTR_USER */
#endif /* _ESIF_CCB_TIMER_H_ */
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/