at query is not NULL when receiving xmit status
[protos/xbee-avr.git] / callout.c
1 /*-
2  * Copyright (c) <2010>, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in
14  *   the documentation and/or other materials provided with the
15  *   distribution.
16  *
17  * - Neither the name of Intel Corporation nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
32  * OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #include <string.h>
36 #include <stdio.h>
37 #include <stdint.h>
38 #include <aversive/queue.h>
39 #include <inttypes.h>
40
41 #include "callout.h"
42
43 #ifdef CALLOUT_STATS
44 #define __TIMER_STAT_ADD(cm, field, x) cm->stats.field += x
45 #else
46 #define __TIMER_STAT_ADD(cm, field, x) do { } while(0)
47 #endif
48
49 #ifdef CALLOUT_DEBUG
50 #define callout_dprintf(fmt, ...) printf("%s(): " fmt, __FUNCTION__, \
51                                          __VA_ARGS__)
52 #else
53 #define callout_dprintf(...) do { } while (0)
54 #endif
55
56 /* Initialize a callout manager */
57 int
58 callout_manager_init(struct callout_manager *cm, get_time_t *get_time)
59 {
60         if (get_time == NULL)
61                 return -1;
62         memset(cm, 0, sizeof(*cm));
63         cm->get_time = get_time;
64         TAILQ_INIT(&cm->pending_list);
65         return 0;
66 }
67
68 /* Initialize the timer handle tim for use */
69 void
70 callout_init(struct callout *tim)
71 {
72         memset(tim, 0, sizeof(*tim));
73 }
74
75 /*
76  * add in list (timer must not already be in a list)
77  */
78 static void
79 callout_add(struct callout_manager *cm, struct callout *tim)
80 {
81         struct callout *t;
82
83         callout_dprintf("cm=%p tim=%p\n", cm, tim);
84
85         /* list is empty */
86         if (TAILQ_EMPTY(&cm->pending_list)) {
87                 TAILQ_INSERT_HEAD(&cm->pending_list, tim, next);
88                 return;
89         }
90
91         /* 'tim' expires before first entry */
92         t = TAILQ_FIRST(&cm->pending_list);
93         if ((int16_t)(tim->expire - t->expire) < 0) {
94                 TAILQ_INSERT_HEAD(&cm->pending_list, tim, next);
95                 return;
96         }
97
98         /* find an element that will expire after 'tim' */
99         TAILQ_FOREACH(t, &cm->pending_list, next) {
100                 if ((int16_t)(tim->expire - t->expire) < 0) {
101                         TAILQ_INSERT_BEFORE(t, tim, next);
102                         return;
103                 }
104         }
105
106         /* not found, insert at the end of the list */
107         TAILQ_INSERT_TAIL(&cm->pending_list, tim, next);
108 }
109
110 /*
111  * del from list (timer must be in a list)
112  */
113 static void
114 callout_del(struct callout_manager *cm, struct callout *tim)
115 {
116         callout_dprintf("cm=%p tim=%p\n", cm, tim);
117         TAILQ_REMOVE(&cm->pending_list, tim, next);
118 }
119
120 /* Reset and start the timer associated with the timer handle tim */
121 static int
122 __callout_reset(struct callout_manager *cm, struct callout *tim, uint16_t expire,
123                 uint16_t period, callout_cb_t fct, void *arg)
124 {
125         callout_dprintf("cm=%p tim=%p expire=%d period=%d\n",
126                         cm, tim, expire, period);
127
128         __TIMER_STAT_ADD(cm, reset, 1);
129         cm->updated = 1;
130
131         /* remove it from list */
132         if (tim->scheduled == 1 && tim->running == 0) {
133                 callout_del(cm, tim);
134                 __TIMER_STAT_ADD(cm, pending, -1);
135         }
136
137         tim->period = period;
138         tim->expire = expire;
139         tim->f = fct;
140         tim->arg = arg;
141         tim->scheduled = 1;
142         tim->running = 0;
143
144         __TIMER_STAT_ADD(cm, pending, 1);
145         callout_add(cm, tim);
146
147         return 0;
148 }
149
150 /* Reset and start the timer associated with the timer handle tim */
151 int
152 callout_reset(struct callout_manager *cm, struct callout *tim, uint16_t ticks,
153               enum callout_type type, callout_cb_t fct, void *arg)
154 {
155         uint16_t cur_time = cm->get_time();
156         return __callout_reset(cm, tim, ticks + cur_time,
157                                type == PERIODICAL ? ticks : 0, fct, arg);
158 }
159
160 /* Stop the timer associated with the timer handle tim */
161 void
162 callout_stop(struct callout_manager *cm, struct callout *tim)
163 {
164         callout_dprintf("cm=%p tim=%p\n", cm, tim);
165
166         __TIMER_STAT_ADD(cm, stop, 1);
167         cm->updated = 1;
168
169         /* remove it from list */
170         if (tim->scheduled == 1 && tim->running == 0) {
171                 callout_del(cm, tim);
172                 __TIMER_STAT_ADD(cm, pending, -1);
173         }
174 }
175
176 /* Test the PENDING status of the timer handle tim */
177 int
178 callout_pending(struct callout *tim)
179 {
180         return tim->scheduled == 1;
181 }
182
183 /* must be called periodically, run all timer that expired */
184 void callout_manage(struct callout_manager *cm)
185 {
186         struct callout_list expired_list;
187         struct callout *tim;
188         uint16_t cur_time = cm->get_time();
189
190         callout_dprintf("cm=%p\n", cm);
191
192         TAILQ_INIT(&expired_list);
193         __TIMER_STAT_ADD(cm, manage, 1);
194
195         /* move all expired timers in a local list */
196         while (!TAILQ_EMPTY(&cm->pending_list)) {
197                 tim = TAILQ_FIRST(&cm->pending_list);
198
199                 if ((int16_t)(cur_time - tim->expire) < 0)
200                         break;
201
202                 TAILQ_REMOVE(&cm->pending_list, tim, next);
203                 TAILQ_INSERT_TAIL(&expired_list, tim, next);
204         }
205
206         /* for each timer of 'expired' list, execute callback */
207         while (!TAILQ_EMPTY(&expired_list)) {
208                 tim = TAILQ_FIRST(&expired_list);
209                 TAILQ_REMOVE(&expired_list, tim, next);
210
211                 cm->updated = 0;
212
213                 /* execute callback function with list unlocked */
214                 __TIMER_STAT_ADD(cm, pending, -1);
215                 __TIMER_STAT_ADD(cm, running, 1);
216                 tim->running = 1;
217                 tim->f(cm, tim, tim->arg);
218                 __TIMER_STAT_ADD(cm, running, -1);
219
220                 /* the timer was stopped or reloaded by the callback
221                  * function, we have nothing to do here */
222                 if (cm->updated == 1)
223                         continue;
224
225                 tim->running = 0;
226                 tim->scheduled = 0;
227
228                 /* if timer type is periodical, reschedule */
229                 if (tim->period != 0) {
230                         __callout_reset(cm, tim, cur_time + tim->period,
231                                         tim->period, tim->f, tim->arg);
232                 }
233         }
234 }
235
236 /* dump statistics about timers */
237 void callout_dump_stats(struct callout_manager *cm)
238 {
239 #ifdef CALLOUT_STATS
240         printf("Timer statistics:\n");
241         printf("  reset = %d\n", cm->stats.reset);
242         printf("  stop = %d\n", cm->stats.stop);
243         printf("  manage = %d\n", cm->stats.manage);
244         printf("  pending = %d\n", cm->stats.pending);
245         printf("  running = %d\n", cm->stats.running);
246 #else
247         printf("No timer statistics, CALLOUT_STATS is disabled\n");
248 #endif
249 }
250
251 #if 0
252
253 /******************************/
254
255 #include <sys/time.h>
256 #include <unistd.h>
257
258 static uint16_t get_time(void)
259 {
260         struct timeval tv;
261
262         gettimeofday(&tv, NULL);
263         return tv.tv_sec;
264 }
265
266 static void cb1(struct callout_manager *cm, struct callout *tim, void *arg);
267 static void cb2(struct callout_manager *cm, struct callout *tim, void *arg);
268 static void cb3(struct callout_manager *cm, struct callout *tim, void *arg);
269
270 static void cb1(struct callout_manager *cm, struct callout *tim, void *arg)
271 {
272         static int cnt;
273         arg = arg; /* silent compiler */
274
275         printf("cb1\n");
276         callout_dump_stats(cm);
277         if (++cnt >= 4)
278                 callout_stop(cm, tim);
279 }
280
281 static void cb2(struct callout_manager *cm, struct callout *tim, void *arg)
282 {
283         static int cnt;
284         struct callout *t3 = arg;
285
286         printf("cb2\n");
287         if (++cnt < 3)
288                 callout_reset(cm, tim, 5, SINGLE, cb2, arg);
289         else
290                 callout_reset(cm, t3, 1, SINGLE, cb3, NULL);
291 }
292
293 static void cb3(struct callout_manager *cm, struct callout *tim, void *arg)
294 {
295         cm = cm; /* silent compiler */
296         tim = tim; /* silent compiler */
297         arg = arg; /* silent compiler */
298
299         printf("cb3\n");
300 }
301
302 int main(void)
303 {
304         struct callout_manager cm;
305         struct callout t1, t2, t3;
306         int i;
307
308         if (callout_manager_init(&cm, get_time) < 0)
309                 return -1;
310
311         callout_init(&t1);
312         callout_init(&t2);
313         callout_init(&t3);
314
315         callout_reset(&cm, &t1, 3, PERIODICAL, cb1, NULL);
316         callout_reset(&cm, &t2, 5, SINGLE, cb2, &t3);
317
318         for (i = 0; i < 18; i++) {
319                 callout_manage(&cm);
320                 sleep(1);
321         }
322
323         callout_dump_stats(&cm);
324         return 0;
325 }
326
327 #endif