| /****************************************************************************** |
| |
| Copyright (c) 2014-2015 Lantiq Deutschland GmbH |
| Copyright (c) 2015-2016 Lantiq Beteiligungs-GmbH & Co.KG |
| Copyright 2016, Intel Corporation. |
| |
| For licensing information, see the file 'LICENSE' in the root folder of |
| this software module. |
| |
| ******************************************************************************/ |
| |
| /** |
| \file dxs_timer.c |
| This file contains the implementation of timers used in DXS driver. |
| */ |
| |
| /* ========================================================================= */ |
| /* Includes */ |
| /* ========================================================================= */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <pthread.h> |
| #include <time.h> |
| #include <sys/time.h> |
| #include <signal.h> |
| #include <semaphore.h> |
| #include <errno.h> |
| |
| #include "dxs_timer.h" |
| |
| /* ========================================================================= */ |
| /* Macro definitions */ |
| /* ========================================================================= */ |
| |
| /** default timeout thread poll time (in milliseconds), |
| this is the polling time used to check if new entiries/events were added */ |
| #ifndef TIMEOUT_THREAD_POLL_TIME |
| #define TIMEOUT_THREAD_POLL_TIME 50 |
| #endif |
| |
| /** get pointer to payload of given entry */ |
| #define list_entry_data(ENTRY) ((void *)((char *)ENTRY + sizeof(struct list_entry))) |
| |
| /** check if list is empty */ |
| #define is_list_empty(LIST) \ |
| (((LIST)->first_element.next == &(LIST)->first_element) ? DXS_TM_TRUE : DXS_TM_FALSE) |
| #define foreach_list_entry_safe_ll(PLIST, ENTRY, NEXT_ENTRY) \ |
| for ((ENTRY) = (PLIST)->next, (NEXT_ENTRY) = (ENTRY)->next; \ |
| (ENTRY)->next != (PLIST)->next; \ |
| (ENTRY) = (NEXT_ENTRY), (NEXT_ENTRY) = (ENTRY)->next) |
| #define foreach_list_entry(PLIST, ENTRY) \ |
| for ((ENTRY) = (PLIST)->first_element.next; \ |
| (ENTRY)->next != (PLIST)->first_element.next; \ |
| (ENTRY) = (ENTRY)->next) |
| |
| /* ========================================================================= */ |
| /* Type definitions */ |
| /* ========================================================================= */ |
| |
| /** List entry */ |
| struct list_entry { |
| /** Previous list entry */ |
| struct list_entry *next; |
| /** Next list entry */ |
| struct list_entry *prev; |
| }; |
| |
| /** List structure */ |
| struct list { |
| /** Used list entries, this first element is empty |
| and can be used only to pint to other elements */ |
| struct list_entry first_element; |
| /** Size of list entry data */ |
| size_t payload_size; |
| /** List lock */ |
| sem_t lock; |
| }; |
| |
| /** timeout descriptor structure */ |
| struct timeout { |
| /** set DXS_TM_TRUE if handler should be called periodically, |
| otherwise set DXS_TM_FALSE */ |
| DXS_TM_BOOL_t bPeriodical; |
| /** timeout argument */ |
| unsigned long arg1; |
| /** timeout handler */ |
| TIMER_ENTRY handler; |
| }; |
| |
| /** timeout list entry */ |
| struct timeout_list_entry { |
| /** Time when the timeout becomes active (in milliseconds) */ |
| time_t timeout_time; |
| /** Time to wait from setting the timeout, used for periodic events */ |
| time_t time_in; |
| /** timeout descriptor */ |
| struct timeout timeout; |
| }; |
| |
| /** timeout control structure */ |
| struct DXS_TM_Context { |
| /* DXS_TM_TRUE if timers were initialized */ |
| DXS_TM_BOOL_t bTimersInialized; |
| /** timeout list */ |
| struct list timeout_list; |
| /** Timeout thread control structure */ |
| pthread_t timeout_thread_ctrl; |
| }; |
| |
| /* ========================================================================== */ |
| /* Global variables */ |
| /* ========================================================================== */ |
| |
| /** \todo deleting/clean up of timers is missing for user space, it should |
| be called upon closing the tapi thread or even maybe for dev stop */ |
| /** control structure for timers in user's space */ |
| struct DXS_TM_Context G_timers /* = {.bTimersInialized = DXS_TM_FALSE} */; |
| static volatile sig_atomic_t timer_thr_exit = 0; |
| |
| /* ========================================================================== */ |
| /* Local functions */ |
| /* ========================================================================== */ |
| |
| static int32_t DXS_TM_list_init(struct list *list, size_t payload_size); |
| static void DXS_TM_list_entry_free(struct list *list, |
| struct list_entry *entry); |
| static int32_t DXS_TM_lockless_event_remove(struct DXS_TM_Context *context, |
| struct list_entry *entry_to_remove); |
| static int32_t DXS_TM_timeout_event_remove(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry); |
| static void DXS_TM_timeout_event_stop(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry); |
| static struct list_entry *DXS_TM_next_active_event_get(struct DXS_TM_Context *context, |
| struct timeout *timeout, |
| time_t * time_to_next_entry); |
| static void DXS_TM_list_entry_remove(struct list *list, |
| struct list_entry *entry); |
| static void DXS_TM_lockless_event_stop(struct DXS_TM_Context *context, |
| struct list_entry *entry_to_stop); |
| static void *DXS_TM_timeout_thread_main(void *arg); |
| static void DXS_TM_list_delete(struct list *list); |
| static int32_t DXS_TM_timeout_init(struct DXS_TM_Context *context); |
| static struct list_entry *DXS_TM_event_entry_create(struct DXS_TM_Context *context, |
| const struct timeout *timeout); |
| static struct list_entry *DXS_TM_timeout_event_create(struct DXS_TM_Context *context, |
| TIMER_ENTRY handler, |
| unsigned long arg1); |
| static int32_t DXS_TM_timeout_event_start(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry, |
| time_t timeout_time, DXS_TM_BOOL_t bPeriodical); |
| static int32_t DXS_TM_event_entry_start(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry, |
| time_t timeout_time, |
| DXS_TM_BOOL_t bPeriodical); |
| static int32_t DXS_TM_lockless_event_entry_start(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry, |
| time_t timeout_time); |
| static void DXS_TM_list_entry_add_before(struct list *list, |
| struct list_entry *entry, |
| struct list_entry *new_entry); |
| static void DXS_TM_list_entry_add_tail(struct list *list, |
| struct list_entry *new_entry); |
| static void DXS_TM_list_entry_add_after(struct list *list, |
| struct list_entry *entry, |
| struct list_entry *new_entry); |
| |
| /** |
| Sleep for defined time period. |
| |
| \param pTVal pointer to timespec structure |
| |
| \return |
| - none |
| */ |
| static void dxs_nanosleep(struct timespec *pTVal) |
| { |
| while (1) |
| { |
| int rval = nanosleep(pTVal, pTVal); |
| if (rval == 0) |
| { |
| return; |
| } |
| else |
| { |
| if (errno == EINTR) |
| continue; |
| else |
| return; |
| } |
| } |
| } |
| |
| /** |
| Sleep for an interval in ms. |
| |
| \param sleepTime_ms sleep time in ms |
| |
| \return |
| - none |
| */ |
| static void dxs_MSecSleep(time_t sleepTime_ms) |
| { |
| struct timespec tv; |
| tv.tv_sec = sleepTime_ms/1000; |
| tv.tv_nsec = (long) ((sleepTime_ms - (tv.tv_sec * 1000)) * 1000 * 1000); |
| |
| dxs_nanosleep(&tv); |
| |
| return; |
| } |
| |
| /** |
| Elapsed time in ms after given timestamp. |
| |
| \param refTime_ms reference time in ms |
| |
| \return |
| - elapsed time in ms as time_t value |
| */ |
| static time_t dxs_ElapsedTimeMSecGet( |
| time_t refTime_ms) |
| { |
| struct timeval tv = {0}; |
| time_t nTime_ms = 0; |
| |
| gettimeofday(&tv, NULL); |
| nTime_ms = (time_t)(tv.tv_sec*1000 + (tv.tv_usec) / 1000); |
| |
| if ( (refTime_ms == 0) || (refTime_ms > nTime_ms) ) |
| { |
| return nTime_ms; |
| } |
| |
| return (nTime_ms - refTime_ms); |
| } |
| |
| /** |
| Create new list_entry element and assign a callback function to it. |
| |
| \param context pointer to tapi timer context structure |
| \param handler pointer to callback function |
| \param arg1 private data for the callback, can be pointer or int |
| |
| \return |
| - pointer to newly allocated list_entry |
| - NULL in case of error, return value of DXS_TM_event_entry_create() |
| */ |
| static struct list_entry *DXS_TM_timeout_event_create(struct DXS_TM_Context *context, |
| TIMER_ENTRY handler, |
| unsigned long arg1) |
| { |
| struct timeout timeout; |
| |
| timeout.handler = handler; |
| timeout.arg1 = arg1; |
| timeout.bPeriodical = DXS_TM_FALSE; |
| |
| return DXS_TM_event_entry_create(context, &timeout); |
| } |
| |
| /** |
| Create new list_entry element, allocate memory and copy timeout data. |
| |
| \param context pointer to tapi timer context structure |
| \param timeout pointer to timeout stucture |
| |
| \return |
| - pointer to newly allocated list_entry |
| - NULL in case of error |
| */ |
| static struct list_entry *DXS_TM_event_entry_create(struct DXS_TM_Context *context, |
| const struct timeout *timeout) |
| { |
| struct list_entry *new_entry; |
| struct timeout_list_entry *new_timeout_entry; |
| |
| if (sem_wait(&context->timeout_list.lock)) |
| return NULL; |
| /* allocate memory for list entry with payload */ |
| new_entry = malloc(sizeof(struct list_entry) + context->timeout_list.payload_size); |
| if (!new_entry) { |
| sem_post(&context->timeout_list.lock); |
| return NULL; |
| } |
| |
| new_timeout_entry = list_entry_data(new_entry); |
| |
| new_timeout_entry->timeout = *timeout; |
| |
| sem_post(&context->timeout_list.lock); |
| |
| return new_entry; |
| } |
| |
| /** |
| Initialize DXS_TM_Context structure and its list and start timeout control |
| thread. |
| |
| \param context pointer to tapi timer context structure |
| |
| \return |
| - DXS_TM_SUCCESS or DXS_TM_ERROR in case of errors |
| */ |
| static int32_t DXS_TM_timeout_init(struct DXS_TM_Context *context) |
| { |
| int32_t error; |
| |
| error = DXS_TM_list_init(&context->timeout_list, sizeof(struct timeout_list_entry)); |
| |
| if (error) |
| { |
| fprintf( stderr, |
| ("DXS_API: DXS_TM_list_init() failed for timer Initialization\n")); |
| return error; |
| } |
| error = (DXS_TM_SUCCESS == pthread_create(&context->timeout_thread_ctrl, |
| NULL, |
| DXS_TM_timeout_thread_main, |
| (void *)context)) ? DXS_TM_SUCCESS : DXS_TM_ERROR; |
| |
| if (error) |
| { |
| fprintf( stderr, |
| ("DXS_API: Thread init failed for timer Initialization\n")); |
| DXS_TM_list_delete(&context->timeout_list); |
| return error; |
| } |
| return DXS_TM_SUCCESS; |
| } |
| |
| /** |
| Release memory and other resources used. |
| |
| \param list pointer to list |
| |
| */ |
| static void DXS_TM_list_delete(struct list *list) |
| { |
| struct list_entry *entry, *tmp_entry; |
| |
| foreach_list_entry_safe_ll(&list->first_element, entry, tmp_entry) |
| { |
| free(entry); |
| } |
| /* if list is empty fileds next and prev should point first_element */ |
| list->first_element.next = &list->first_element; |
| list->first_element.prev = &list->first_element; |
| (void)sem_destroy(&list->lock); |
| } |
| |
| /* |
| * exit from timer thread |
| */ |
| void terminate_timer_thread(int sig) |
| { |
| timer_thr_exit = 1; |
| } |
| |
| /** timeout events handling thread |
| |
| \param[in] arg Thread arguments |
| |
| \return 0 |
| */ |
| static void *DXS_TM_timeout_thread_main(void *arg) |
| { |
| struct DXS_TM_Context *context = (struct DXS_TM_Context *)arg; |
| struct timeout timer = {0}; |
| struct list_entry *timer_to_execute_entry = NULL; |
| time_t wait_time; |
| struct sigaction act = {0}; |
| int32_t ret; |
| |
| act.sa_handler = terminate_timer_thread; |
| sigaction(SIGTERM, &act, NULL); |
| |
| /* while thread is running */ |
| while (!timer_thr_exit) |
| { |
| /* wait for message in FIFO */ |
| while (!timer_thr_exit) |
| { |
| wait_time = 0; |
| ret = sem_wait(&context->timeout_list.lock); |
| if (ret) |
| break; |
| timer_to_execute_entry = DXS_TM_next_active_event_get(context, &timer, |
| &wait_time); |
| sem_post(&context->timeout_list.lock); |
| if (timer_to_execute_entry == NULL) |
| { |
| /* if time to the next timeout is longer than the poll time, |
| then wait only TIMEOUT_THREAD_POLL_TIME ms */ |
| if ((wait_time == 0) || (wait_time > TIMEOUT_THREAD_POLL_TIME)) |
| { |
| wait_time = TIMEOUT_THREAD_POLL_TIME; |
| } |
| /** \todo replace sleep with select, for this some sync mechanism |
| with add/start entry is needed, for example select could wait |
| for a pipe fd being readable, each function for add/start entry |
| would write to this pipe, this could replace the polling, |
| if above is implemented then wait_time could be set to exact |
| time to next timeout or wait forever untill and event/entry |
| is added */ |
| dxs_MSecSleep((time_t) wait_time); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| #if 0 |
| /* check if we are shutting down */ |
| if (thr_params->bShutDown == DXS_TM_TRUE |
| || thr_params->bRunning == DXS_TM_FALSE) |
| { |
| break; |
| } |
| #endif |
| |
| ret = sem_wait(&context->timeout_list.lock); |
| if (ret) |
| { |
| break; |
| } |
| else |
| { |
| struct timeout_list_entry *periodical_timeout_entry; |
| time_t time_in; |
| |
| periodical_timeout_entry = list_entry_data(timer_to_execute_entry); |
| DXS_TM_lockless_event_stop(context, timer_to_execute_entry); |
| if (periodical_timeout_entry && timer.bPeriodical) |
| { |
| /* for periodical events get time to next timeout and add new timeout */ |
| time_in = periodical_timeout_entry->time_in; |
| (void) DXS_TM_lockless_event_entry_start(context, timer_to_execute_entry, |
| time_in); |
| } |
| } |
| sem_post(&context->timeout_list.lock); |
| if (timer.handler != NULL) |
| { |
| (void) timer.handler((Timer_ID) timer_to_execute_entry, (void *) timer.arg1); |
| } |
| else |
| { |
| fprintf( stderr, |
| ("DXS_TM_timeout_thread_main - ERROR: found event without timer handler")); |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| Lockless version of DXS_TM_timeout_event_remove. |
| |
| \param context pointer to tapi timer context structure |
| \param entry_to_stop pointer to tapi entry structure |
| |
| \return |
| - DXS_TM_SUCCESS |
| */ |
| static void DXS_TM_lockless_event_stop(struct DXS_TM_Context *context, |
| struct list_entry *entry_to_stop) |
| { |
| struct list_entry *current_entry; |
| |
| if (is_list_empty(&context->timeout_list)) |
| { |
| return; |
| } |
| foreach_list_entry(&context->timeout_list, current_entry) |
| { |
| if (current_entry == entry_to_stop) |
| { |
| DXS_TM_list_entry_remove(&context->timeout_list, current_entry); |
| return; |
| } |
| } |
| return; |
| } |
| |
| /** |
| Remove entry from the list. |
| |
| \param list pointer to list structure |
| \param entry pointer to entry structure that will be removed |
| */ |
| static void DXS_TM_list_entry_remove(struct list *list, |
| struct list_entry *entry) |
| { |
| entry->next->prev = entry->prev; |
| entry->prev->next = entry->next; |
| entry->prev = NULL; |
| entry->next = NULL; |
| if (list->first_element.next == entry) |
| { |
| list->first_element.next = &list->first_element; |
| } |
| } |
| |
| /** Get next timeouted event |
| |
| \param[in] context timer context pointer |
| \param[out] timeout Returns timeout descriptor |
| \param[out] time_to_next_entry time till the next timeout expires, |
| set only if no entry was found |
| |
| \return first entry on the list for which timeout expired or NULL |
| */ |
| static struct list_entry *DXS_TM_next_active_event_get(struct DXS_TM_Context *context, |
| struct timeout *timeout, |
| time_t * time_to_next_entry) |
| { |
| struct timeout_list_entry *first_entry; |
| time_t currentTime; |
| |
| if (is_list_empty(&context->timeout_list)) { |
| return NULL; |
| } |
| |
| /* get the first entry from the list, |
| should be the one for which timeout expires first */ |
| first_entry = list_entry_data(context->timeout_list.first_element.next); |
| if (!first_entry) |
| { |
| return NULL; |
| } |
| currentTime = (time_t)dxs_ElapsedTimeMSecGet(0); |
| if (first_entry->timeout_time <= currentTime) |
| { |
| /* timeout occured for this entry */ |
| *timeout = first_entry->timeout; |
| return context->timeout_list.first_element.next; |
| } |
| else |
| { |
| /* get time to next timeout */ |
| *time_to_next_entry = first_entry->timeout_time - currentTime; |
| } |
| return NULL; |
| } |
| |
| /** Stop handling of given entry |
| |
| \param[in] context timer context pointer |
| \param[in] timer_entry timer entry to stop |
| |
| \return always DXS_TM_SUCCESS as DXS_TM_lockless_event_stop does |
| */ |
| static void DXS_TM_timeout_event_stop(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry) |
| { |
| struct timeout_list_entry *timeout_entry_to_start; |
| |
| sem_wait(&context->timeout_list.lock); |
| timeout_entry_to_start = list_entry_data(timer_entry); |
| timeout_entry_to_start->timeout.bPeriodical = DXS_TM_FALSE; |
| DXS_TM_lockless_event_stop(context, timer_entry); |
| sem_post(&context->timeout_list.lock); |
| |
| return; |
| } |
| |
| /** Remove given entry from the list |
| |
| \param[in] context timer context pointer |
| \param[in] timer_entry timer entry to remove |
| |
| \return always DXS_TM_SUCCESS as DXS_TM_lockless_event_remove does |
| */ |
| static int32_t DXS_TM_timeout_event_remove(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry) |
| { |
| int32_t error; |
| |
| sem_wait(&context->timeout_list.lock); |
| error = DXS_TM_lockless_event_remove(context, timer_entry); |
| sem_post(&context->timeout_list.lock); |
| |
| return error; |
| } |
| |
| /** Lockless version of DXS_TM_timeout_event_remove |
| |
| \param[in] context timer context pointer |
| \param[in] entry_to_remove entry to remove |
| |
| \return always DXS_TM_SUCCESS |
| */ |
| static int32_t DXS_TM_lockless_event_remove(struct DXS_TM_Context *context, |
| struct list_entry *entry_to_remove) |
| { |
| struct list_entry *current_entry; |
| |
| if (is_list_empty(&context->timeout_list)) |
| { |
| return DXS_TM_SUCCESS; |
| } |
| foreach_list_entry(&context->timeout_list, current_entry) |
| { |
| if (current_entry == entry_to_remove) |
| { |
| DXS_TM_list_entry_free(&context->timeout_list, current_entry); |
| return DXS_TM_SUCCESS; |
| } |
| } |
| return DXS_TM_SUCCESS; |
| } |
| |
| /** Release memory allocated for given entry |
| |
| \param[in] list pointeer to the list - unused |
| \param[in] entry entry to remove and free memory |
| */ |
| static void DXS_TM_list_entry_free(struct list *list, |
| struct list_entry *entry) |
| { |
| if ((NULL != entry->next) && (NULL != entry->prev)) |
| { |
| entry->next->prev = entry->prev; |
| entry->prev->next = entry->next; |
| } |
| free(entry); |
| } |
| |
| /** Release memory allocated for given entry |
| |
| \param[in] list pointeer to the list |
| \param[in] payload_size size of memory in bytes needed to hold the entry data |
| |
| \return value sem_init() call |
| */ |
| static int32_t DXS_TM_list_init(struct list *list, size_t payload_size) |
| { |
| /* for first element next and prev point to first element if list is empty */ |
| list->first_element.next = &list->first_element; |
| list->first_element.prev = &list->first_element; |
| list->payload_size = payload_size; |
| /* get the lock */ |
| return sem_init(&list->lock, 0, 1); |
| } |
| |
| /** Start handling of given timeout entry |
| |
| \param[in] context timer context pointer |
| \param[in] timer_entry timer entry to start |
| \param[in] timeout_time timoeout time for new timer entry |
| \param[in] bPeriodical if DXS_TM_TRUE, then timeout for event will be |
| checked periodicaly to execute the handler |
| |
| \return always DXS_TM_SUCCESS as DXS_TM_event_entry_start does |
| */ |
| static int32_t DXS_TM_timeout_event_start(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry, |
| time_t timeout_time, DXS_TM_BOOL_t bPeriodical) |
| { |
| /** \todo add a check if timers are initialized */ |
| return DXS_TM_event_entry_start(context, timer_entry, timeout_time, bPeriodical); |
| } |
| |
| /** Start handling of given timeout entry |
| |
| \param[in] context timer context pointer |
| \param[in] timer_entry timer entry to start |
| \param[in] timeout_time timoeout time for new timer entry |
| \param[in] bPeriodical if DXS_TM_TRUE, then timeout for event will be |
| checked periodicaly to execute the handler |
| |
| \return always DXS_TM_SUCCESS |
| */ |
| static int32_t DXS_TM_event_entry_start(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry, |
| time_t timeout_time, |
| DXS_TM_BOOL_t bPeriodical) |
| { |
| struct timeout_list_entry *timeout_entry_to_start; |
| int32_t ret; |
| |
| sem_wait(&context->timeout_list.lock); |
| timeout_entry_to_start = list_entry_data(timer_entry); |
| timeout_entry_to_start->timeout.bPeriodical = bPeriodical; |
| ret = DXS_TM_lockless_event_entry_start(context, timer_entry, timeout_time); |
| sem_post(&context->timeout_list.lock); |
| |
| return ret; |
| } |
| |
| /** Add timeout event |
| |
| \param[in] context timer context pointer |
| \param[in] timer_entry timer entry to start |
| \param[in] timeout_time timoeout time for new timer entry (in ms) |
| |
| \return always DXS_TM_SUCCESS |
| */ |
| static int32_t DXS_TM_lockless_event_entry_start(struct DXS_TM_Context *context, |
| struct list_entry *timer_entry, |
| time_t timeout_time) |
| { |
| struct list_entry *current_entry; |
| struct timeout_list_entry *timeout_entry, *timeout_entry_to_start; |
| DXS_TM_BOOL_t added = DXS_TM_FALSE; |
| |
| if (timer_entry == NULL) |
| return DXS_TM_ERROR; |
| timeout_entry_to_start = list_entry_data(timer_entry); |
| if (timeout_entry_to_start == NULL) |
| return DXS_TM_ERROR; |
| /* get timeout time to compare with current time read |
| with dxs_ElapsedTimeMSecGet(0) */ |
| timeout_entry_to_start->timeout_time = dxs_ElapsedTimeMSecGet(0) + timeout_time; |
| timeout_entry_to_start->time_in = timeout_time; |
| foreach_list_entry(&context->timeout_list, current_entry) |
| { |
| timeout_entry = list_entry_data(current_entry); |
| if (timeout_entry->timeout_time > |
| timeout_entry_to_start->timeout_time) |
| { |
| DXS_TM_list_entry_add_before(&context->timeout_list, current_entry, |
| timer_entry); |
| added = DXS_TM_TRUE; |
| break; |
| } |
| } |
| if (!added) |
| { |
| DXS_TM_list_entry_add_tail(&context->timeout_list, timer_entry); |
| } |
| return DXS_TM_SUCCESS; |
| } |
| |
| /** Add add new entry before entry that is already on the list |
| |
| \param[in] list the list - unused |
| \param[in] entry entry from the list |
| \param[in] new_entry entry to add |
| */ |
| static void DXS_TM_list_entry_add_before(struct list *list, |
| struct list_entry *entry, |
| struct list_entry *new_entry) |
| { |
| entry->prev->next = new_entry; |
| new_entry->prev = entry->prev; |
| new_entry->next = entry; |
| entry->prev = new_entry; |
| } |
| |
| /** Add add new entry at the end of the list |
| |
| \param[in] list the list |
| \param[in] entry entry to add |
| */ |
| static void DXS_TM_list_entry_add_tail(struct list *list, |
| struct list_entry *new_entry) |
| { |
| DXS_TM_list_entry_add_after(list, list->first_element.prev, new_entry); |
| } |
| |
| /** Add add new entry after entry that is already on the list |
| |
| \param[in] list the list - unused |
| \param[in] entry entry from the list |
| \param[in] new_entry entry to add |
| */ |
| static void DXS_TM_list_entry_add_after(struct list *list, |
| struct list_entry *entry, |
| struct list_entry *new_entry) |
| { |
| entry->next->prev = new_entry; |
| new_entry->next = entry->next; |
| entry->next = new_entry; |
| new_entry->prev = entry; |
| } |
| |
| /* ========================================================================== */ |
| /* Public functions */ |
| /* ========================================================================== */ |
| |
| /** |
| Create a timer. |
| |
| \param pTimerEntry Function pointer to the call back function. |
| \param nArgument Pointer to DXS channel structure. |
| |
| \return |
| Timer_ID Pointer to internal timer structure. |
| |
| \remarks |
| Initialize a task queue which will be scheduled once a timer interrupt occurs |
| to execute the appropriate operation in a process context, process in which |
| semaphores ... are allowed. |
| Please notice that this task has to run under the keventd process, in which |
| it can be executed thousands of times within a single timer tick. |
| */ |
| Timer_ID DXS_TimerCreate(TIMER_ENTRY pTimerEntry, void *nArgument) |
| { |
| if (DXS_TM_TRUE != G_timers.bTimersInialized) |
| { |
| /* timeout thread needs to be started only once */ |
| G_timers.bTimersInialized = DXS_TM_TRUE; |
| DXS_TM_timeout_init(&G_timers); |
| } |
| return (Timer_ID) DXS_TM_timeout_event_create(&G_timers, pTimerEntry, |
| (unsigned long)nArgument); |
| } |
| |
| /** |
| Sets a timer to the specified time and starts it. It can be choose if the |
| timer starts periodically. |
| |
| \param Timer_ID Pointer to internal timer structure. |
| \param nTime Time in ms. |
| \param bPeriodically Starts the timer periodically or not. |
| \param bRestart Restart the timer or normal start. |
| |
| \return |
| Returns an error code: DXS_TM_TRUE / DXS_TM_FALSE |
| */ |
| DXS_TM_BOOL_t DXS_TimerSet(Timer_ID Timer, uint32_t nTime, DXS_TM_BOOL_t bPeriodically, |
| DXS_TM_BOOL_t bRestart) |
| { |
| /** \todo add a check if timers are initialized */ |
| if (bRestart == DXS_TM_TRUE) |
| { |
| DXS_TM_timeout_event_stop(&G_timers, (struct list_entry *) Timer); |
| } |
| |
| if (DXS_TM_timeout_event_start(&G_timers, |
| (struct list_entry *) Timer, |
| (time_t) nTime, |
| bPeriodically) != 0) |
| { |
| fprintf ( stderr, ("DXS_TimerSet: failed to start timer\n")); |
| return DXS_TM_FALSE; |
| } |
| |
| return DXS_TM_TRUE; |
| } |
| |
| /** |
| Stop a timer. |
| |
| \param Timer_ID Pointer to internal timer structure. |
| |
| \return |
| Returns an error code: DXS_TM_TRUE / DXS_TM_FALSE |
| */ |
| DXS_TM_BOOL_t DXS_TimerStop(Timer_ID Timer) |
| { |
| if(Timer) |
| DXS_TM_timeout_event_stop(&G_timers, (struct list_entry *) Timer); |
| |
| return DXS_TM_TRUE; |
| } |
| |
| /** |
| Delete a timer. |
| |
| \param Timer_ID Pointer to internal timer structure. |
| |
| \return |
| Returns an error code: DXS_TM_TRUE / DXS_TM_FALSE |
| */ |
| DXS_TM_BOOL_t DXS_TimerDestroy(Timer_ID Timer) |
| { |
| if (Timer == NULL) |
| return DXS_TM_FALSE; |
| |
| DXS_TimerStop(Timer); |
| |
| /* free memory */ |
| (void) DXS_TM_timeout_event_remove(&G_timers, (struct list_entry *)Timer); |
| |
| return DXS_TM_TRUE; |
| } |