1 /*-
   2  * Copyright (c) 2011-2012 George V. Neville-Neil,
   3  *                         Steven Kreuzer, 
   4  *                         Martin Burnicki, 
   5  *                         Jan Breuer,
   6  *                         Gael Mace, 
   7  *                         Alexandre Van Kempen,
   8  *                         Inaqui Delgado,
   9  *                         Rick Ratzel,
  10  *                         National Instruments.
  11  * Copyright (c) 2009-2010 George V. Neville-Neil, 
  12  *                         Steven Kreuzer, 
  13  *                         Martin Burnicki, 
  14  *                         Jan Breuer,
  15  *                         Gael Mace, 
  16  *                         Alexandre Van Kempen
  17  *
  18  * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
  19  *
  20  * All Rights Reserved
  21  * 
  22  * Redistribution and use in source and binary forms, with or without
  23  * modification, are permitted provided that the following conditions are
  24  * met:
  25  * 1. Redistributions of source code must retain the above copyright notice,
  26  *    this list of conditions and the following disclaimer.
  27  * 2. Redistributions in binary form must reproduce the above copyright
  28  *    notice, this list of conditions and the following disclaimer in the
  29  *    documentation and/or other materials provided with the distribution.
  30  * 
  31  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  32  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  33  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  34  * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
  35  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  36  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  37  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  38  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  39  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  40  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  41  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42  */
  43 
  44 /**
  45  * @file   timer.c
  46  * @date   Wed Jun 23 09:41:26 2010
  47  * 
  48  * @brief  The timers which run the state machine.
  49  * 
  50  * Timers in the PTP daemon are run off of the signal system.  
  51  */
  52 
  53 #include "../ptpd.h"
  54 
  55 #define US_TIMER_INTERVAL (62500)
  56 volatile unsigned int elapsed;
  57 
  58 /*
  59  * original code calls sigalarm every fixed 1ms. This highly pollutes the debug_log, and causes more interrupted instructions
  60  * This was later modified to have a fixed granularity of 1s.
  61  *
  62  * Currently this has a configured granularity, and timerStart() guarantees that clocks expire ASAP when the granularity is too small.
  63  * Timers must now be explicitelly canceled with timerStop (instead of timerStart(0.0))
  64  */
  65 
  66 void 
  67 catch_alarm(int sig)
  68 {
  69         elapsed++;
  70         /* be sure to NOT call DBG in asynchronous handlers! */
  71 }
  72 
  73 void 
  74 initTimer(void)
  75 {
  76         struct itimerval itimer;
  77 
  78         DBG("initTimer\n");
  79 
  80         signal(SIGALRM, SIG_IGN);
  81 
  82         elapsed = 0;
  83         itimer.it_value.tv_sec = itimer.it_interval.tv_sec = 0;
  84         itimer.it_value.tv_usec = itimer.it_interval.tv_usec =
  85             US_TIMER_INTERVAL;
  86 
  87 #ifdef __sun
  88         sigset(SIGALRM, catch_alarm);
  89 #else
  90         signal(SIGALRM, catch_alarm);
  91 #endif
  92         setitimer(ITIMER_REAL, &itimer, 0);
  93 }
  94 
  95 void 
  96 timerUpdate(IntervalTimer * itimer)
  97 {
  98 
  99         int i, delta;
 100 
 101         /*
 102          * latch how many ticks we got since we were last called
 103          * remember that catch_alarm() is totally asynchronous to this timerUpdate()
 104          */
 105         delta = elapsed;
 106         elapsed = 0;
 107 
 108         if (delta <= 0)
 109                 return;
 110 
 111         /*
 112          * if time actually passed, then decrease every timer left
 113          * the one(s) that went to zero or negative are:
 114          *  a) rearmed at the original time (ignoring the time that may have passed ahead)
 115          *  b) have their expiration latched until timerExpired() is called
 116          */
 117         for (i = 0; i < TIMER_ARRAY_SIZE; ++i) {
 118                 if ((itimer[i].interval) > 0 && ((itimer[i].left) -= delta) 
 119                     <= 0) {
 120                         itimer[i].left = itimer[i].interval;
 121                         itimer[i].expire = TRUE;
 122                         DBG2("TimerUpdate:    Timer %u has now expired.   (Re-armed again with interval %d, left %d)\n", i, itimer[i].interval, itimer[i].left );
 123                 }
 124         }
 125 
 126 }
 127 
 128 void 
 129 timerStop(UInteger16 index, IntervalTimer * itimer)
 130 {
 131         if (index >= TIMER_ARRAY_SIZE)
 132                 return;
 133 
 134         itimer[index].interval = 0;
 135         DBG2("timerStop:      Stopping timer %d.   (New interval: %d; New left: %d)\n", index, itimer[index].left , itimer[index].interval);
 136 }
 137 
 138 void 
 139 timerStart(UInteger16 index, float interval, IntervalTimer * itimer)
 140 {
 141         if (index >= TIMER_ARRAY_SIZE)
 142                 return;
 143 
 144         itimer[index].expire = FALSE;
 145 
 146 
 147         /*
 148          *  US_TIMER_INTERVAL defines the minimum interval between sigalarms.
 149          *  timerStart has a float parameter for the interval, which is casted to integer.
 150          *  very small amounts are forced to expire ASAP by setting the interval to 1
 151          */
 152         itimer[index].left = (int)((interval * 1E6) / US_TIMER_INTERVAL);
 153         if(itimer[index].left == 0){
 154                 /*
 155                  * the interval is too small, raise it to 1 to make sure it expires ASAP
 156                  * Timer cancelation is done explicitelly with stopTimer()
 157                  */ 
 158                 itimer[index].left = 1;
 159 
 160                 static int operator_warned_interval_too_small = 0;
 161                 if(!operator_warned_interval_too_small){
 162                         operator_warned_interval_too_small = 1;
 163                         /*
 164                          * using random uniform timers it is pratically guarantted that we hit the possible minimum timer.
 165                          * This is because of the current timer model based on periodic sigalarm, irrespective if the next
 166                          * event is close or far away in time.
 167                          *
 168                          * A solution would be to recode this whole module with a calendar queue, while keeping the same API:
 169                          * E.g.: http://www.isi.edu/nsnam/ns/doc/node35.html
 170                          *
 171                          * Having events that expire immediatly (ie, delayreq invocations using random timers) can lead to
 172                          * messages appearing in unexpected ordering, so the protocol implementation must check more conditions
 173                          * and not assume a certain ususal ordering
 174                          */
 175                         DBG("Timer would be issued immediatly. Please raise dep/timer.c:US_TIMER_INTERVAL to hold %.2fs\n",
 176                                 interval
 177                         );
 178                         
 179                 }
 180         }
 181         itimer[index].interval = itimer[index].left;
 182 
 183         DBG2("timerStart:     Set timer %d to %f.  New interval: %d; new left: %d\n", index, interval, itimer[index].left , itimer[index].interval);
 184 }
 185 
 186 
 187 
 188 /*
 189  * This function arms the timer with a uniform range, as requested by page 105 of the standard (for sending delayReqs.)
 190  * actual time will be U(0, interval * 2.0);
 191  *
 192  * PTPv1 algorithm was:
 193  *    ptpClock->R = getRand(&ptpClock->random_seed) % (PTP_DELAY_REQ_INTERVAL - 2) + 2;
 194  *    R is the number of Syncs to be received, before sending a new request
 195  * 
 196  */ 
 197 void timerStart_random(UInteger16 index, float interval, IntervalTimer * itimer)
 198 {
 199         float new_value;
 200 
 201         new_value = getRand() * interval * 2.0;
 202         DBG2(" timerStart_random: requested %.2f, got %.2f\n", interval, new_value);
 203         
 204         timerStart(index, new_value, itimer);
 205 }
 206 
 207 
 208 
 209 Boolean 
 210 timerExpired(UInteger16 index, IntervalTimer * itimer)
 211 {
 212         timerUpdate(itimer);
 213 
 214         if (index >= TIMER_ARRAY_SIZE)
 215                 return FALSE;
 216 
 217         if (!itimer[index].expire)
 218                 return FALSE;
 219 
 220         itimer[index].expire = FALSE;
 221 
 222 
 223         DBG2("timerExpired:   Timer %d expired, taking actions.   current interval: %d; current left: %d\n", index, itimer[index].left , itimer[index].interval);
 224 
 225         return TRUE;
 226 }
 227 
 228 Boolean 
 229 timerStopped(UInteger16 index, IntervalTimer * itimer)
 230 {
 231         timerUpdate(itimer);
 232 
 233         if (index >= TIMER_ARRAY_SIZE)
 234                 return FALSE;
 235 
 236         if (itimer[index].interval == 0) {
 237                 return TRUE;
 238         DBG2("timerStopped:   Timer %d is stopped\n", index);
 239         }
 240 
 241         return FALSE;
 242 
 243 }
 244 
 245 Boolean 
 246 timerRunning(UInteger16 index, IntervalTimer * itimer)
 247 {
 248         timerUpdate(itimer);
 249 
 250         if (index >= TIMER_ARRAY_SIZE)
 251                 return FALSE;
 252 
 253         if ((itimer[index].interval != 0) &&
 254             (itimer[index].expire == FALSE)) {
 255                 return TRUE;
 256         DBG2("timerRunning:   Timer %d is running\n", index);
 257         }
 258 
 259         return FALSE;
 260 
 261 }
 262