xbee_stat: replace printf printf_P
[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 #include <aversive.h>
41 #include <aversive/pgmspace.h>
42
43 #include "callout.h"
44
45 #ifdef CALLOUT_STATS
46 #define __TIMER_STAT_ADD(cm, field, x) cm->stats.field += x
47 #else
48 #define __TIMER_STAT_ADD(cm, field, x) do { } while(0)
49 #endif
50
51 #ifdef CALLOUT_DEBUG
52 #define callout_dprintf_P(fmt, ...) printf_P(PSTR("%s(): " fmt), __FUNCTION__, \
53                                          __VA_ARGS__)
54 #else
55 #define callout_dprintf_P(...) do { } while (0)
56 #endif
57
58 /* Initialize a callout manager */
59 int
60 callout_manager_init(struct callout_manager *cm, get_time_t *get_time)
61 {
62         if (get_time == NULL)
63                 return -1;
64         memset(cm, 0, sizeof(*cm));
65         cm->get_time = get_time;
66         TAILQ_INIT(&cm->pending_list);
67         return 0;
68 }
69
70 /* Initialize the timer handle tim for use */
71 void
72 callout_init(struct callout *tim)
73 {
74         memset(tim, 0, sizeof(*tim));
75 }
76
77 /*
78  * add in list (timer must not already be in a list)
79  */
80 static void
81 callout_add(struct callout_manager *cm, struct callout *tim)
82 {
83         struct callout *t;
84
85         callout_dprintf_P(PSTR("cm=%p tim=%p\r\n"), cm, tim);
86
87         /* list is empty */
88         if (TAILQ_EMPTY(&cm->pending_list)) {
89                 TAILQ_INSERT_HEAD(&cm->pending_list, tim, next);
90                 return;
91         }
92
93         /* 'tim' expires before first entry */
94         t = TAILQ_FIRST(&cm->pending_list);
95         if ((int16_t)(tim->expire - t->expire) < 0) {
96                 TAILQ_INSERT_HEAD(&cm->pending_list, tim, next);
97                 return;
98         }
99
100         /* find an element that will expire after 'tim' */
101         TAILQ_FOREACH(t, &cm->pending_list, next) {
102                 if ((int16_t)(tim->expire - t->expire) < 0) {
103                         TAILQ_INSERT_BEFORE(t, tim, next);
104                         return;
105                 }
106         }
107
108         /* not found, insert at the end of the list */
109         TAILQ_INSERT_TAIL(&cm->pending_list, tim, next);
110 }
111
112 /*
113  * del from list (timer must be in a list)
114  */
115 static void
116 callout_del(struct callout_manager *cm, struct callout *tim)
117 {
118         callout_dprintf_P(PSTR("cm=%p tim=%p\r\n"), cm, tim);
119         TAILQ_REMOVE(&cm->pending_list, tim, next);
120 }
121
122 /* Reset and start the timer associated with the timer handle tim */
123 static int
124 __callout_reset(struct callout_manager *cm, struct callout *tim, uint16_t expire,
125                 uint16_t period, callout_cb_t fct, void *arg)
126 {
127         callout_dprintf_P(PSTR("cm=%p tim=%p expire=%d period=%d\r\n"),
128                           cm, tim, expire, period);
129
130         __TIMER_STAT_ADD(cm, reset, 1);
131         cm->updated = 1;
132
133         /* remove it from list */
134         if (tim->scheduled == 1 && tim->running == 0) {
135                 callout_del(cm, tim);
136                 __TIMER_STAT_ADD(cm, pending, -1);
137         }
138
139         tim->period = period;
140         tim->expire = expire;
141         tim->f = fct;
142         tim->arg = arg;
143         tim->scheduled = 1;
144         tim->running = 0;
145
146         __TIMER_STAT_ADD(cm, pending, 1);
147         callout_add(cm, tim);
148
149         return 0;
150 }
151
152 /* Reset and start the timer associated with the timer handle tim */
153 int
154 callout_reset(struct callout_manager *cm, struct callout *tim, uint16_t ticks,
155               enum callout_type type, callout_cb_t fct, void *arg)
156 {
157         uint16_t cur_time = cm->get_time();
158         return __callout_reset(cm, tim, ticks + cur_time,
159                                type == PERIODICAL ? ticks : 0, fct, arg);
160 }
161
162 /* Stop the timer associated with the timer handle tim */
163 void
164 callout_stop(struct callout_manager *cm, struct callout *tim)
165 {
166         callout_dprintf_P(PSTR("cm=%p tim=%p\r\n"), cm, tim);
167
168         __TIMER_STAT_ADD(cm, stop, 1);
169         cm->updated = 1;
170
171         /* remove it from list */
172         if (tim->scheduled == 1 && tim->running == 0) {
173                 callout_del(cm, tim);
174                 __TIMER_STAT_ADD(cm, pending, -1);
175         }
176 }
177
178 /* Test the PENDING status of the timer handle tim */
179 int
180 callout_pending(struct callout *tim)
181 {
182         return tim->scheduled == 1;
183 }
184
185 /* must be called periodically, run all timer that expired */
186 void callout_manage(struct callout_manager *cm)
187 {
188         struct callout_list expired_list;
189         struct callout *tim;
190         uint16_t cur_time = cm->get_time();
191
192         callout_dprintf_P(PSTR("cm=%p\r\n"), cm);
193
194         TAILQ_INIT(&expired_list);
195         __TIMER_STAT_ADD(cm, manage, 1);
196
197         /* move all expired timers in a local list */
198         while (!TAILQ_EMPTY(&cm->pending_list)) {
199                 tim = TAILQ_FIRST(&cm->pending_list);
200
201                 if ((int16_t)(cur_time - tim->expire) < 0)
202                         break;
203
204                 TAILQ_REMOVE(&cm->pending_list, tim, next);
205                 TAILQ_INSERT_TAIL(&expired_list, tim, next);
206         }
207
208         /* for each timer of 'expired' list, execute callback */
209         while (!TAILQ_EMPTY(&expired_list)) {
210                 tim = TAILQ_FIRST(&expired_list);
211                 TAILQ_REMOVE(&expired_list, tim, next);
212
213                 cm->updated = 0;
214
215                 /* execute callback function with list unlocked */
216                 __TIMER_STAT_ADD(cm, pending, -1);
217                 __TIMER_STAT_ADD(cm, running, 1);
218                 tim->running = 1;
219                 tim->f(cm, tim, tim->arg);
220                 __TIMER_STAT_ADD(cm, running, -1);
221
222                 /* the timer was stopped or reloaded by the callback
223                  * function, we have nothing to do here */
224                 if (cm->updated == 1)
225                         continue;
226
227                 tim->running = 0;
228                 tim->scheduled = 0;
229
230                 /* if timer type is periodical, reschedule */
231                 if (tim->period != 0) {
232                         __callout_reset(cm, tim, cur_time + tim->period,
233                                         tim->period, tim->f, tim->arg);
234                 }
235         }
236 }
237
238 /* dump statistics about timers */
239 void callout_dump_stats(struct callout_manager *cm)
240 {
241 #ifdef CALLOUT_STATS
242         printf_P(PSTR("Timer statistics:\r\n"));
243         printf_P(PSTR("  reset = %d\r\n"), cm->stats.reset);
244         printf_P(PSTR("  stop = %d\r\n"), cm->stats.stop);
245         printf_P(PSTR("  manage = %d\r\n"), cm->stats.manage);
246         printf_P(PSTR("  pending = %d\r\n"), cm->stats.pending);
247         printf_P(PSTR("  running = %d\r\n"), cm->stats.running);
248 #else
249         printf_P(PSTR("No timer statistics, CALLOUT_STATS is disabled\r\n"));
250 #endif
251 }
252
253 #if 0
254
255 /******************************/
256
257 #include <sys/time.h>
258 #include <unistd.h>
259
260 static uint16_t get_time(void)
261 {
262         struct timeval tv;
263
264         gettimeofday(&tv, NULL);
265         return tv.tv_sec;
266 }
267
268 static void cb1(struct callout_manager *cm, struct callout *tim, void *arg);
269 static void cb2(struct callout_manager *cm, struct callout *tim, void *arg);
270 static void cb3(struct callout_manager *cm, struct callout *tim, void *arg);
271
272 static void cb1(struct callout_manager *cm, struct callout *tim, void *arg)
273 {
274         static int cnt;
275         arg = arg; /* silent compiler */
276
277         printf_P(PSTR("cb1\r\n"));
278         callout_dump_stats(cm);
279         if (++cnt >= 4)
280                 callout_stop(cm, tim);
281 }
282
283 static void cb2(struct callout_manager *cm, struct callout *tim, void *arg)
284 {
285         static int cnt;
286         struct callout *t3 = arg;
287
288         printf_P(PSTR("cb2\r\n"));
289         if (++cnt < 3)
290                 callout_reset(cm, tim, 5, SINGLE, cb2, arg);
291         else
292                 callout_reset(cm, t3, 1, SINGLE, cb3, NULL);
293 }
294
295 static void cb3(struct callout_manager *cm, struct callout *tim, void *arg)
296 {
297         cm = cm; /* silent compiler */
298         tim = tim; /* silent compiler */
299         arg = arg; /* silent compiler */
300
301         printf_P(PSTR("cb3\r\n"));
302 }
303
304 int main(void)
305 {
306         struct callout_manager cm;
307         struct callout t1, t2, t3;
308         int i;
309
310         if (callout_manager_init(&cm, get_time) < 0)
311                 return -1;
312
313         callout_init(&t1);
314         callout_init(&t2);
315         callout_init(&t3);
316
317         callout_reset(&cm, &t1, 3, PERIODICAL, cb1, NULL);
318         callout_reset(&cm, &t2, 5, SINGLE, cb2, &t3);
319
320         for (i = 0; i < 18; i++) {
321                 callout_manage(&cm);
322                 sleep(1);
323         }
324
325         callout_dump_stats(&cm);
326         return 0;
327 }
328
329 #endif