894 lines
24 KiB
C
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_ */
|
|
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
|