]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - apps/tidep0084.git/blob - tutorials/generic_sensor_tutorial/tutorial/SensorToCloud/components/common/src/timer_cb.c
Added Support for Generic Sensor Tutorial which provides instructions on how to add...
[apps/tidep0084.git] / tutorials / generic_sensor_tutorial / tutorial / SensorToCloud / components / common / src / timer_cb.c
1 /******************************************************************************
2  @file timer_cb.c
4  @brief TIMAC 2.0 API Callback timer abstraction
6  Group: WCS LPC
7  $Target Devices: Linux: AM335x, Embedded Devices: CC1310, CC1350$
9  ******************************************************************************
10  $License: BSD3 2016 $
11   
12    Copyright (c) 2015, Texas Instruments Incorporated
13    All rights reserved.
14   
15    Redistribution and use in source and binary forms, with or without
16    modification, are permitted provided that the following conditions
17    are met:
18   
19    *  Redistributions of source code must retain the above copyright
20       notice, this list of conditions and the following disclaimer.
21   
22    *  Redistributions in binary form must reproduce the above copyright
23       notice, this list of conditions and the following disclaimer in the
24       documentation and/or other materials provided with the distribution.
25   
26    *  Neither the name of Texas Instruments Incorporated nor the names of
27       its contributors may be used to endorse or promote products derived
28       from this software without specific prior written permission.
29   
30    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
32    THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
35    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
36    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
37    OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
38    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
39    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
40    EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41  ******************************************************************************
42  $Release Name: TI-15.4Stack Linux x64 SDK$
43  $Release Date: Jun 28, 2017 (2.02.00.03)$
44  *****************************************************************************/
46 #include "compiler.h"
47 #include "timer.h"
48 #include "compiler.h"
49 #include "log.h"
50 #include "mutex.h"
51 #include "threads.h"
52 #include "ti_semaphore.h"
54 #include <malloc.h>
55 #include <string.h>
57 /*
58  * @var timer_sem_id
59  * @brief Timer thread sleeps on this
60  */
61 static intptr_t timer_sem_id;
63 /*
64  * @var timer_mutex_id
65  * @brief Controls access to the timer linked list
66  */
67 static intptr_t timer_mutex_id;
69 /*
70  * @var timer_thread_id
71  * @brief Timer thread id
72  */
73 static intptr_t timer_thread_id;
75 /*
76  * @var timer_check
77  * @brief used to verify the timer_cb_structure is valid or not
78  */
79 static const int timer_check = 'T';
81 /*
82  * @struct timer_cb_details
83  * @brief Details about a timer with a callback.
84  */
85 struct timer_cb_details {
86     /*! points to timer_check if this is a valid timer */
87     const int *check_ptr;
89     /*! debug name, could be null */
90     const char *dbg_name;
92     /*! time when this expires */
93     uint32_t expire_time;
95     /*! Is this timer periodic? */
96     bool     periodic;
98     /*! what is this timer period? */
99     uint32_t period_mSec;
101     /*! callback for timer */
102     timer_callback_fn *callback;
104     /*! parameter for the timer */
105     intptr_t           cookie;
107     /* next timer in the list */
108     struct timer_cb_details *pNext;
109 };
111 /*
112  * @var pTMR_InService
113  * @brief This is the current active timer being serviced.
114  *
115  */
116 static  struct timer_cb_details *  pTMR_InService;;
118 /*
119  * @var pTMR_List
120  * @brief The list of all active timers
121  */
122 static struct timer_cb_details *pTMR_ActiveList;
124 /*
125  * @var timer_done_once
126  * @brief  Initialize the timer callback code once
127  */
128 static int timer_done_once = 0;
130 /*!
131  * @var timer_cb_h2tmr
132  * @param h - possible timer pointer to convert
133  * @returns pointer, or NULL on error
134  */
135 static struct timer_cb_details *timer_cb_h2tmr(intptr_t h)
137     struct timer_cb_details *pTMR;
139     pTMR = (struct timer_cb_details *)(h);
140     if(pTMR)
141     {
142         if(pTMR->check_ptr != &(timer_check))
143         {
144             LOG_printf(LOG_ERROR, "not a timer: %p\n", (void *)(pTMR));
145             pTMR = NULL;
146         }
147     }
148     return (pTMR);
151 /*!
152  * @brief Lock the timer list
153  */
154 static void timer_list_lock(void)
156     MUTEX_lock(timer_mutex_id, -1);
159 /*!
160  * @brief UnLock the timer list
161  */
162 static void timer_list_unlock(void)
164     MUTEX_unLock(timer_mutex_id);
167 /*!
168  * @brief Insert this timer into the list at the proper location
169  */
170 static void _timer_insert(struct timer_cb_details *pTMR)
172     struct timer_cb_details **ppTMR;
173     int tval;
175     /* determine when this timer will expire
176      * NOTE:
177      *  This code has a limitation :-(
178      *  It will slowly skew perodic timers
179      *  in real hardware we might choose
180      *  to do this a differnet way that
181      *  will not skew ...
182      *
183      * -------
184      *  Example:
185      *   PEROID = 10
186      *   Actual 1st service occurs at 11
187      *   Resceduled at 12 mSec
188      *   Next schedule will occur at 22mSec
189      *   And the next.. might be off by a few mSecs
190      * -------
192      * Determine when this will expire
193     */
194     pTMR->expire_time = TIMER_getNow() + pTMR->period_mSec;
196     /* hunt the list for its insertion position */
197     ppTMR = &(pTMR_ActiveList);
199     /* While we have a list.. */
200     while(*ppTMR)
201     {
203         /* where does it go? */
204         tval = (int)(pTMR->expire_time - (*ppTMR)->expire_time);
206         if(tval <= 0)
207         {
208             /* it goes here (before) this one */
209             break;
210         }
211         /* Try the next one */
212         ppTMR = &((*ppTMR)->pNext);
213     }
215     /* insert here, before the next one */
216     pTMR->pNext = (*ppTMR);
217     *ppTMR = pTMR;
220 /*
221  * @brief This is the timer thread (we don't use signals)
222  * @param cookie - not used in this code
223  * @return not really used
224  *
225  * This implimentation is based on a semaphore timeout
226  * We choose this implimentation so as to avoid
227  */
228 static void real_timer_thread_func(void)
230     uint32_t tnow;
231     int timeout_mSecs;
232     struct timer_cb_details *pTMR;
234     timer_list_lock();
236     for(;;)
237     {
238         pTMR = pTMR_ActiveList;
239         /* Do we have a timer list? */
240         if(pTMR)
241         {
242             /* Check the head item */
243             tnow = TIMER_getNow();
244             tnow = pTMR->expire_time - tnow;
245             timeout_mSecs = (int)(tnow);
246         }
247         else
248         {
249             /* no list, so sleep for a minute */
250             timeout_mSecs = 60 * 1000;
251         }
253         /* Should we sleep? */
254         if(timeout_mSecs > 0)
255         {
256             timer_list_unlock();
257             SEMAPHORE_waitWithTimeout(timer_sem_id, timeout_mSecs);
258             timer_list_lock();
259             /* Try again */
260             continue;
261         }
263         /* Timer at head has expired */
264         /* head item is now the timer "in-service" */
266         /* Remove head item from list.. */
267         pTMR_ActiveList = pTMR->pNext;
268         pTMR->pNext = NULL;
270         /* NOTE the currently active timer */
271         pTMR_InService = pTMR;
273         /* call the expired timer */
274         /* we unlock it during this process */
275         timer_list_unlock();
276         (*(pTMR->callback))((intptr_t)(pTMR), pTMR->cookie);
277         timer_list_lock();
279         /*
280          * This can happen during the callback:
281          *
282          * Option 1:
283          *   Nothing happens
284          *   If it is perodic, below we will reschedule
285          *   otherwise the app will delete it when it is not needed again
286          *
287          * Option 2: (this is the problem)
288          *   The app decides to delete it.
289          *   If this happens, we will have NULLed
290          *   the pTMR_InService variable
291         */
293         /* Reclaim our pointer */
294         pTMR = pTMR_InService;
295         pTMR_InService = NULL;
297         /* Determine: Was it deleted? */
298         if(pTMR)
299         {
300             /* No it was not.. */
301             if(pTMR->periodic)
302             {
303                 _timer_insert(pTMR);
304             }
305         }
306         /* continue around */
307     } /* for(;;) loop */
310 /* this function exists to remove a Visual Studio C4702 warning ... */
311 static intptr_t timer_thread_func(intptr_t cookie)
313     (void)cookie;
314     real_timer_thread_func();
315     return (0);
318 /*
319  * @brief Code to initialize the timer module
320  * @return void
321  */
322 static void timer_once_routine(void)
324     if(timer_done_once)
325     {
326         return;
327     }
328     timer_done_once = 1;
330     timer_mutex_id = MUTEX_create("timer-mutex");
331     /* Lock asap ... */
332     timer_list_lock();
334     /* create our semaphore, preloaded */
335     /* we initalize the semaphore with 1 so it runs */
336     /* when the thread starts */
337     timer_sem_id = SEMAPHORE_create("timer-semaphore", 1);
338     /* we create the thread *LAST* */
339     timer_thread_id = THREAD_create("timer-thread",
340                                      timer_thread_func,
341                                      0,
342                                      THREAD_FLAGS_DEFAULT);
343     /* Then release once everything is done */
344     timer_list_unlock();
347 /*
348  * Create a callback timer
349  *
350  * Public function defined in timer.h
351  */
352 intptr_t TIMER_CB_create(const char *dbg_name,
353                           timer_callback_fn *pCbFunc,
354                           intptr_t cookie,
355                           uint32_t period_mSec,
356                           bool periodic)
358     struct timer_cb_details *pTMR;
360     timer_once_routine();
362     /* Create our new timer */
363     pTMR = (struct timer_cb_details *)calloc(1, sizeof(*pTMR));
364     if(pTMR == NULL)
365     {
366         LOG_printf(LOG_ERROR, "no memory for timer!\n");
367         return (0);
368     }
370     memset((void *)(pTMR), 0, sizeof(*pTMR));
371     if(dbg_name == NULL)
372     {
373         dbg_name = "tmr-no-name";
374     }
375     pTMR->dbg_name = strdup(dbg_name);
376     /* we don't do anything about errors here */
378     pTMR->check_ptr   = &timer_check;
379     pTMR->callback    = pCbFunc;
380     pTMR->cookie      = cookie;
381     pTMR->periodic    = periodic;
382     /* Disallow 0 [continous] timers */
383     if(period_mSec == 0)
384     {
385         period_mSec = 1;
386     }
387     pTMR->period_mSec = period_mSec;
389     timer_list_lock();
390     _timer_insert(pTMR);
391     timer_list_unlock();
393     /* wake up the timer thread
394      * so it will check the list */
395     SEMAPHORE_put(timer_sem_id);
397     return ((intptr_t)(pTMR));
400 /*
401  * Given a callback timer handle, destroy it
402  *
403  * Public function defined in timer.h
404  */
405 void TIMER_CB_destroy(intptr_t h)
407     struct timer_cb_details *pTMR;
408     struct timer_cb_details **ppTMR;
410     pTMR = timer_cb_h2tmr(h);
411     if(pTMR == NULL)
412     {
413         return;
414     }
416     /* Be safe, lock down */
417     timer_list_lock();
419     /* are we deleting the timer that is in the callback? */
420     if(pTMR == pTMR_InService)
421     {
422         /* yes .. so zap this */
423         /* otherwise we might try */
424         /* to reschedule it when */
425         /* the callback returns */
426         pTMR_InService = NULL;
427     }
429     /* Search the timer list for this one */
430     ppTMR = &(pTMR_ActiveList);
432     /* while we have items in the active list */
433     while(*ppTMR)
434     {
435         /* If this is the matching timer? */
436         if((*ppTMR) == pTMR)
437         {
438             /* YES! */
439             /* remove this from the list */
440             *ppTMR = pTMR->pNext;
441             pTMR->pNext = NULL;
442             /* and stop looking */
443             break;
444         }
445         else
446         {
447             /* try next one in the list */
448             ppTMR = &((*ppTMR)->pNext);
449         }
450     }
451     timer_list_unlock();
453     if(pTMR->dbg_name)
454     {
455         free_const((const void *)(pTMR->dbg_name));
456     }
457     memset(((void *)(pTMR)), 0, sizeof(*pTMR));
458     free((void *)(pTMR));
462 /*
463  * Given a callback timer handle, determine if the handle is still valid or not
464  *
465  * Public function defined in timer.h
466  */
467 bool TIMER_CB_isValid(intptr_t h)
469     return (!!(timer_cb_h2tmr(h) != NULL));
472 /*
473  * Given a callback timer handle, return how much time remains before it expires
474  *
475  * Public function defined in timer.h
476  */
477 int TIMER_CB_getRemain(intptr_t h)
479     uint32_t tnow;
480     struct timer_cb_details *pTMR;
482     pTMR = timer_cb_h2tmr(h);
483     if(pTMR)
484     {
485         tnow = TIMER_getNow();
486         tnow = pTMR->expire_time - tnow;
487         return ((int)(tnow));
488     }
489     else
490     {
491         return (-1);
492     }
495 /*
496  *  ========================================
497  *  Texas Instruments Micro Controller Style
498  *  ========================================
499  *  Local Variables:
500  *  mode: c
501  *  c-file-style: "bsd"
502  *  tab-width: 4
503  *  c-basic-offset: 4
504  *  indent-tabs-mode: nil
505  *  End:
506  *  vim:set  filetype=c tabstop=4 shiftwidth=4 expandtab=true
507  */