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