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 = US_TIMER_INTERVAL;
  85 
  86         signal(SIGALRM, catch_alarm);
  87         setitimer(ITIMER_REAL, &itimer, 0);
  88 }
  89 
  90 void 
  91 timerUpdate(IntervalTimer * itimer)
  92 {
  93 
  94         int i, delta;
  95 
  96         /*
  97          * latch how many ticks we got since we were last called
  98          * remember that catch_alarm() is totally asynchronous to this timerUpdate()
  99          */
 100         delta = elapsed;
 101         elapsed = 0;
 102 
 103         if (delta <= 0)
 104                 return;
 105 
 106         /*
 107          * if time actually passed, then decrease every timer left
 108          * the one(s) that went to zero or negative are:
 109          *  a) rearmed at the original time (ignoring the time that may have passed ahead)
 110          *  b) have their expiration latched until timerExpired() is called
 111          */
 112         for (i = 0; i < TIMER_ARRAY_SIZE; ++i) {
 113                 if ((itimer[i].interval) > 0 && ((itimer[i].left) -= delta) 
 114                     <= 0) {
 115                         itimer[i].left = itimer[i].interval;
 116                         itimer[i].expire = TRUE;
 117                         DBG2("TimerUpdate:    Timer %u has now expired.   (Re-armed again with interval %d, left %d)\n", i, itimer[i].interval, itimer[i].left );
 118                 }
 119         }
 120 
 121 }
 122 
 123 void 
 124 timerStop(UInteger16 index, IntervalTimer * itimer)
 125 {
 126         if (index >= TIMER_ARRAY_SIZE)
 127                 return;
 128 
 129         itimer[index].interval = 0;
 130         DBG2("timerStop:      Stopping timer %d.   (New interval: %d; New left: %d)\n", index, itimer[index].left , itimer[index].interval);
 131 }
 132 
 133 void 
 134 timerStart(UInteger16 index, float interval, IntervalTimer * itimer)
 135 {
 136         if (index >= TIMER_ARRAY_SIZE)
 137                 return;
 138 
 139         itimer[index].expire = FALSE;
 140 
 141 
 142         /*
 143          *  US_TIMER_INTERVAL defines the minimum interval between sigalarms.
 144          *  timerStart has a float parameter for the interval, which is casted to integer.
 145          *  very small amounts are forced to expire ASAP by setting the interval to 1
 146          */
 147         itimer[index].left = (int)((interval * 1E6) / US_TIMER_INTERVAL);
 148         if(itimer[index].left == 0){
 149                 /*
 150                  * the interval is too small, raise it to 1 to make sure it expires ASAP
 151                  * Timer cancelation is done explicitelly with stopTimer()
 152                  */ 
 153                 itimer[index].left = 1;
 154 
 155                 static int operator_warned_interval_too_small = 0;
 156                 if(!operator_warned_interval_too_small){
 157                         operator_warned_interval_too_small = 1;
 158                         /*
 159                          * using random uniform timers it is pratically guarantted that we hit the possible minimum timer.
 160                          * This is because of the current timer model based on periodic sigalarm, irrespective if the next
 161                          * event is close or far away in time.
 162                          *
 163                          * A solution would be to recode this whole module with a calendar queue, while keeping the same API:
 164                          * E.g.: http://www.isi.edu/nsnam/ns/doc/node35.html
 165                          *
 166                          * Having events that expire immediatly (ie, delayreq invocations using random timers) can lead to
 167                          * messages appearing in unexpected ordering, so the protocol implementation must check more conditions
 168                          * and not assume a certain ususal ordering
 169                          */
 170                         DBG("Timer would be issued immediatly. Please raise dep/timer.c:US_TIMER_INTERVAL to hold %.2fs\n",
 171                                 interval
 172                         );
 173                         
 174                 }
 175         }
 176         itimer[index].interval = itimer[index].left;
 177 
 178         DBG2("timerStart:     Set timer %d to %f.  New interval: %d; new left: %d\n", index, interval, itimer[index].left , itimer[index].interval);
 179 }
 180 
 181 
 182 
 183 /*
 184  * This function arms the timer with a uniform range, as requested by page 105 of the standard (for sending delayReqs.)
 185  * actual time will be U(0, interval * 2.0);
 186  *
 187  * PTPv1 algorithm was:
 188  *    ptpClock->R = getRand(&ptpClock->random_seed) % (PTP_DELAY_REQ_INTERVAL - 2) + 2;
 189  *    R is the number of Syncs to be received, before sending a new request
 190  * 
 191  */ 
 192 void timerStart_random(UInteger16 index, float interval, IntervalTimer * itimer)
 193 {
 194         float new_value;
 195 
 196         new_value = getRand() * interval * 2.0;
 197         DBG2(" timerStart_random: requested %.2f, got %.2f\n", interval, new_value);
 198         
 199         timerStart(index, new_value, itimer);
 200 }
 201 
 202 
 203 
 204 Boolean 
 205 timerExpired(UInteger16 index, IntervalTimer * itimer)
 206 {
 207         timerUpdate(itimer);
 208 
 209         if (index >= TIMER_ARRAY_SIZE)
 210                 return FALSE;
 211 
 212         if (!itimer[index].expire)
 213                 return FALSE;
 214 
 215         itimer[index].expire = FALSE;
 216 
 217 
 218         DBG2("timerExpired:   Timer %d expired, taking actions.   current interval: %d; current left: %d\n", index, itimer[index].left , itimer[index].interval);
 219 
 220         return TRUE;
 221 }
 222 
 223 Boolean 
 224 timerStopped(UInteger16 index, IntervalTimer * itimer)
 225 {
 226         timerUpdate(itimer);
 227 
 228         if (index >= TIMER_ARRAY_SIZE)
 229                 return FALSE;
 230 
 231         if (itimer[index].interval == 0) {
 232                 return TRUE;
 233         DBG2("timerStopped:   Timer %d is stopped\n", index);
 234         }
 235 
 236         return FALSE;
 237 
 238 }
 239 
 240 Boolean 
 241 timerRunning(UInteger16 index, IntervalTimer * itimer)
 242 {
 243         timerUpdate(itimer);
 244 
 245         if (index >= TIMER_ARRAY_SIZE)
 246                 return FALSE;
 247 
 248         if ((itimer[index].interval != 0) &&
 249             (itimer[index].expire == FALSE)) {
 250                 return TRUE;
 251         DBG2("timerRunning:   Timer %d is running\n", index);
 252         }
 253 
 254         return FALSE;
 255 
 256 }
 257