1/* 2 * bcmseclib_timer.c -- timer library 3 * 4 * Copyright (C) 2014, Broadcom Corporation 5 * All Rights Reserved. 6 * 7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; 8 * the contents of this file may not be disclosed to third parties, copied 9 * or duplicated in any form, in whole or in part, without the prior 10 * written permission of Broadcom Corporation. 11 * 12 * $Id: bcmseclib_timer.c,v 1.7 2011-01-11 19:03:26 $ 13 */ 14 15#include <stdio.h> 16#include <stdlib.h> 17#include <string.h> 18#include <stdlib.h> 19#include <typedefs.h> 20#include <bcm_osl.h> 21#include <bcm_llist.h> 22#include <bcmseclib_timer.h> 23#include <bcmseclib_timer_os.h> 24#include <debug.h> 25 26/* Timer manager. */ 27struct bcmseclib_timer_mgr 28{ 29 bcmseclib_timer_t *timerlist; 30 bcmseclib_timer_t *timer_list_mark; 31 bcmseclib_timer_t *timer_freelist; 32 int maxtimers; 33}; 34 35/* Global timer manager. */ 36static bcmseclib_timer_mgr_t *g_timer_mgr = NULL; 37 38static int bcmseclib_set_expiration(uint time, exp_time_t * expiry_time); 39static int bcmseclib_compare_times(exp_time_t *t1, exp_time_t *t2); 40static bool bcmseclib_has_timer_expired(exp_time_t *t); 41static void bcmseclib_translate_expiry_to_absolute(exp_time_t *t); 42 43 44/* Init the timer library 45 * Allocate memory, init free list 46 * Return zero for success, non-zero otherwise 47 */ 48int 49bcmseclib_init_timer_utilities_ex(int ntimers, bcmseclib_timer_mgr_t **mgrp) 50{ 51 int i; 52 bcmseclib_timer_mgr_t *mgr; 53 54 mgr = (bcmseclib_timer_mgr_t *) OS_MALLOC(sizeof(bcmseclib_timer_mgr_t)); 55 if (mgr == NULL) { 56 return (-1); 57 } 58 memset(mgr, 0, sizeof(bcmseclib_timer_mgr_t)); 59 60 mgr->maxtimers = ntimers; 61 mgr->timer_freelist = (bcmseclib_timer_t *) OS_MALLOC(ntimers * sizeof(bcmseclib_timer_t)); 62 memset(mgr->timer_freelist, 0, ntimers * sizeof(bcmseclib_timer_t)); 63 64 /* save, we'll need this to cleanup when we shutdown */ 65 mgr->timer_list_mark = mgr->timer_freelist; 66 67 for (i = 0; i < (ntimers-1); i++) 68 mgr->timer_freelist[i].next = &mgr->timer_freelist[i+1]; 69 70 mgr->timer_freelist[i].next = NULL; 71 72 if (mgrp == NULL) { 73 mgrp = &g_timer_mgr; 74 } 75 *mgrp = mgr; 76 return 0; 77} 78/* Clean it up: 79 * Free the allocated memory 80 * All users of timer utilities should have freed their timers 81 * via bcmseclib_free_timer() before this 82 */ 83int 84bcmseclib_deinit_timer_utilities_ex(bcmseclib_timer_mgr_t *mgr) 85{ 86 if (mgr == NULL) { 87 mgr = g_timer_mgr; 88 } 89 90 OS_FREE(mgr->timer_list_mark); 91 OS_FREE(mgr); 92 mgr = NULL; 93 94 return 0; 95} 96 97/* Setup an existing timer with specified parms 98 * and add it to the active list 99 * If it's already on the active list relocate it 100 * to the appropriate position 101 */ 102void 103bcmseclib_add_timer(bcmseclib_timer_t *t, uint ms, bool periodic) 104{ 105 bcmseclib_timer_t *plist, *pprev; 106 bcmseclib_timer_mgr_t *mgr; 107 char *funstr = "bcmseclib_add_timer"; 108 109 (void) funstr; 110 PRINT_TRACE(("%s(0x%x): duration %d periodic %d\n", funstr, (int)t->mgr, ms, periodic)); 111 112 mgr = t->mgr; 113 114 /* If already in active list: remove it! */ 115 /* this call may fail: ok, just means t was not on list */ 116 bcm_llist_del_member(&mgr->timerlist, t); 117 118 /* set time value and periodic flag */ 119 t->ms = ms; 120 t->periodic = periodic; 121 bcmseclib_set_expiration(ms, &t->expiry_time); 122 123 /* place in correct position (by ascending expiry time) in active list */ 124 125 /* if list is NULL, we're the one & only */ 126 if (mgr->timerlist == NULL) { 127 t->next = NULL; 128 mgr->timerlist = t; 129 goto done; 130 } 131 132 /* walk list until we find a time greater than ours, insert before that member */ 133 pprev = NULL; 134 for (plist = mgr->timerlist; plist; ) { 135 if (bcmseclib_compare_times(&t->expiry_time, &plist->expiry_time) <= 0) { 136 t->next = plist; 137 if (pprev == NULL) 138 mgr->timerlist = t; 139 else 140 pprev->next = t; 141 break; 142 } 143 pprev = plist; 144 plist = plist->next; 145 } 146 147 /* end of list */ 148 if (plist == NULL) { 149 pprev->next = t; 150 t->next = NULL; 151 } 152 153done: 154 return; 155} 156 157/* De-activate timer: remove from active list, but maintain settings, 158 * application is maintaining ownership 159 */ 160bool 161bcmseclib_del_timer(bcmseclib_timer_t *t) 162{ 163 bcmseclib_timer_mgr_t *mgr = t->mgr; 164 165 /* Unlink from active list */ 166 /* this call may fail: ok, just means t was not on list */ 167 if (bcm_llist_del_member(&mgr->timerlist, t) == 0) 168 return TRUE; 169 170 return FALSE; 171} 172 173/* Application is surrendering this timer: 174 * Remove from active list and clear all members 175 */ 176void 177bcmseclib_free_timer(bcmseclib_timer_t *t) 178{ 179 bcmseclib_timer_mgr_t *mgr; 180 181 if (t == NULL) 182 return; 183 184 mgr = t->mgr; 185 186 /* unlink from active list if necessary */ 187 bcm_llist_del_member(&mgr->timerlist, t); 188 189 /* place in free list */ 190 t->next = mgr->timer_freelist; 191 mgr->timer_freelist = t; 192} 193 194/* Create the data structures, fill in callback args, but do NOT 195 * add to active list 196 */ 197bcmseclib_timer_t * 198bcmseclib_init_timer_ex(bcmseclib_timer_mgr_t *mgr, void (*fn)(void *arg), void *arg, const char *name) 199{ 200 bcmseclib_timer_t *pnew; 201 char *funstr = "bcmseclib_init_timer"; 202 203 if (mgr == NULL) { 204 mgr = g_timer_mgr; 205 } 206 207 /* Find a free timer, if none complain and return NULL */ 208 if (mgr->timer_freelist == NULL) { 209 PRINT_ERR(("%s: No timer blocks availavble\n", funstr)); 210 return NULL; 211 } 212 213 pnew = mgr->timer_freelist; 214 mgr->timer_freelist = pnew->next; 215 pnew->next = NULL; 216 217 /* Fill in cb fun, arg, name */ 218 ASSERT(fn); 219 if (fn == NULL) { 220 PRINT_ERR(("%s: NULL cb function arg!\n", funstr)); 221 return NULL; 222 } 223 pnew->fn = fn; 224 pnew->arg = arg; 225 pnew->mgr = mgr; 226 227#ifdef BCMDBG 228 if ((pnew->name = OS_MALLOC(strlen(name) + 1))) 229 strcpy(pnew->name, name); 230#endif 231 232 /* return pointer to timer */ 233 return pnew; 234} 235 236/* Check the active timer list 237 * Return: 238 * TRUE if we've got a timeout to consider, set value of t appropriately 239 * FALSE otherwise, value of t irrelevant 240 * Primary user should be the select/waitfor loop in the dispatcher. 241 */ 242bool 243bcmseclib_get_timeout_ex(bcmseclib_timer_mgr_t *mgr, exp_time_t *t) 244{ 245 char *funstr = "bcmseclib_get_timeout"; 246 247 if (mgr == NULL) { 248 mgr = g_timer_mgr; 249 } 250 251 /* no timers pending */ 252 if (mgr->timerlist == NULL) 253 return FALSE; 254 255 if (t == NULL) { 256 PRINT_ERR(("%s: can't get timeout into null pointer\n", funstr)); 257 return FALSE; 258 } 259 260 memcpy(t, &mgr->timerlist->expiry_time, sizeof(exp_time_t)); 261 262 /* Translate expiry time which is a literal time of day to 263 * time remaining to expiry for use by select/waitfor functions 264 */ 265 bcmseclib_translate_expiry_to_absolute(t); 266 267 return TRUE; 268} 269 270/* Check the timer list, process expirations */ 271void 272bcmseclib_process_timer_expiry_ex(bcmseclib_timer_mgr_t *mgr) 273{ 274 bcmseclib_timer_t *ptimer; 275 char *funstr = "bcmseclib_process_timer_expiry"; 276 277 if (mgr == NULL) { 278 mgr = g_timer_mgr; 279 } 280 281 ptimer = mgr->timerlist; 282 if (ptimer == NULL) 283 return; 284 285 /* process all expired timers */ 286 for (; ptimer && bcmseclib_has_timer_expired(&ptimer->expiry_time); ptimer = mgr->timerlist) { 287 /* Remove from the active list */ 288 /* Caution: this modifies timerlist: recheck against NULL afterwards */ 289 bcmseclib_del_timer(ptimer); 290 291 /* add timer back if periodic */ 292 /* Caution: do this before the cb is fired in case it is delete 293 * in the handler 294 */ 295 if (ptimer->periodic) 296 bcmseclib_add_timer(ptimer, ptimer->ms, ptimer->periodic); 297 298 /* exec cb function */ 299 if (!ptimer->fn) { 300 PRINT_ERR(("%s: NULL cb function\n", funstr)); 301 } else { 302 303 (*ptimer->fn)(ptimer->arg); 304 } 305 } 306} 307 308/* Theory of operation: 309 * All timers that are added specify their expiration time in milliseconds (msec) 310 * relative to now. This 'msec' expiration time is converted to an exp_time_t 311 * structure that stores the expiry time relative to a fixed reference point. 312 * The resulting expiration time can be compared to the current time (relative 313 * to the fixed reference point) to determine if the timer has expired. 314 */ 315 316/* Convert the expiration time (msec) to an exp_time_t structure that 317 * stores the expiry time relative to a fixed reference point. 318 */ 319static int 320bcmseclib_set_expiration(uint msec, exp_time_t * expiry_time) 321{ 322 bcmseclib_time_t now; 323 int sec = msec/1000; 324 int usec = (msec % 1000) * 1000; 325 326 memset(&now, 0, sizeof(now)); 327 bcmseclib_os_get_time(&now); 328 329 expiry_time->sec = now.sec + sec; 330 expiry_time->usec = now.usec + usec; 331 332 /* Handle overflow of usec. */ 333 while (expiry_time->usec >= 1000000) { 334 expiry_time->sec += 1; 335 expiry_time->usec -= 1000000; 336 } 337 338 return 0; 339} 340 341/* Return: 342 * < 0 for t1 < t2 343 * 0 for t1 == t2 344 * > 0 for t1 > t2 345 * 346 * Where: "less than" means sooner, "greater than" means later 347 */ 348static int 349bcmseclib_compare_times(exp_time_t *t1, exp_time_t *t2) 350{ 351 352 if (t1->sec > t2->sec) 353 return 1; 354 355 if (t1->sec < t2->sec) 356 return -1; 357 358 /* sec parts are equal */ 359 if (t1->usec > t2->usec) 360 return 1; 361 if (t1->usec < t2->usec) 362 return -1; 363 364 return 0; 365} 366 367/* Return: 368 * TRUE if time has expired 369 * FALSE otherwise 370 * TODO: should we advance slightly, ie return TRUE if timer will 371 * expire "soon"? 372 */ 373static bool 374bcmseclib_has_timer_expired(exp_time_t *t) 375{ 376 bcmseclib_time_t now; 377 378 memset(&now, 0, sizeof(now)); 379 bcmseclib_os_get_time(&now); 380 381 if (bcmseclib_compare_times(&now, t) >= 0) 382 return TRUE; 383 384 return FALSE; 385} 386 387/* Input: time value in t which stores the expiry time relative to a fixed reference point. 388 * Output: time remaining to expiration as absolute number of sec/usec relative to now. 389 */ 390static void 391bcmseclib_translate_expiry_to_absolute(exp_time_t *t) 392{ 393 bcmseclib_time_t now; 394 395 memset(&now, 0, sizeof(now)); 396 bcmseclib_os_get_time(&now); 397 if (bcmseclib_compare_times(&now, t) >= 0) { 398 t->sec = t->usec = 0; 399 return; 400 } 401 402 PRINT_TRACE(("t->sec %ld t->usec %ld now.sec %ld now.usec %ld\n", 403 t->sec, t->usec, now.sec, now.usec)); 404 405 406 t->sec -= now.sec; 407 t->usec -= now.usec; 408 409 /* Handle underflow. */ 410 if (t->usec < 0) { 411 t->sec -= 1; 412 t->usec += 1000000; 413 } 414 415 ASSERT(t->sec >= 0); 416 ASSERT(t->usec >= 0); 417 418} 419