- add get_max_rate timing API call
[asterisk/asterisk.git] / main / timing.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Digium, Inc.
5  *
6  * Kevin P. Fleming <kpfleming@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Timing source management
22  *
23  * \author Kevin P. Fleming <kpfleming@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/_private.h"
31
32 #include "asterisk/timing.h"
33 #include "asterisk/lock.h"
34 #include "asterisk/cli.h"
35 #include "asterisk/utils.h"
36 #include "asterisk/time.h"
37
38 AST_RWLOCK_DEFINE_STATIC(lock);
39
40 static struct ast_timing_functions timer_funcs;
41
42 void *ast_install_timing_functions(struct ast_timing_functions *funcs)
43 {
44         if (!funcs->timer_open ||
45             !funcs->timer_close ||
46                 !funcs->timer_set_rate ||
47             !funcs->timer_ack ||
48             !funcs->timer_get_event ||
49                 !funcs->timer_get_max_rate ||
50             !funcs->timer_enable_continuous ||
51             !funcs->timer_disable_continuous) {
52                 return NULL;
53         }
54
55         ast_rwlock_wrlock(&lock);
56
57         if (timer_funcs.timer_open) {
58                 ast_rwlock_unlock(&lock);
59                 ast_log(LOG_NOTICE, "Multiple timing modules are loaded.  You should only load one.\n");
60                 return NULL;
61         }
62         
63         timer_funcs = *funcs;
64
65         ast_rwlock_unlock(&lock);
66
67         return &timer_funcs;
68 }
69
70 void ast_uninstall_timing_functions(void *handle)
71 {
72         ast_rwlock_wrlock(&lock);
73
74         if (handle != &timer_funcs) {
75                 ast_rwlock_unlock(&lock);
76                 return;
77         }
78
79         memset(&timer_funcs, 0, sizeof(timer_funcs));
80
81         ast_rwlock_unlock(&lock);
82 }
83
84 int ast_timer_open(void)
85 {
86         int timer;
87
88         ast_rwlock_rdlock(&lock);
89
90         if (!timer_funcs.timer_open) {
91                 ast_rwlock_unlock(&lock);
92                 return -1;
93         }
94
95         timer = timer_funcs.timer_open();
96
97         ast_rwlock_unlock(&lock);
98
99         return timer;
100 }
101
102 void ast_timer_close(int timer)
103 {
104         ast_rwlock_rdlock(&lock);
105
106         if (!timer_funcs.timer_close) {
107                 ast_rwlock_unlock(&lock);
108                 return;
109         }
110
111         timer_funcs.timer_close(timer);
112
113         ast_rwlock_unlock(&lock);
114 }
115
116 int ast_timer_set_rate(int handle, unsigned int rate)
117 {
118         int res;
119
120         ast_rwlock_rdlock(&lock);
121
122         if (!timer_funcs.timer_set_rate) {
123                 ast_rwlock_unlock(&lock);
124                 return -1;
125         }
126
127         res = timer_funcs.timer_set_rate(handle, rate);
128
129         ast_rwlock_unlock(&lock);
130
131         return res;
132 }
133
134 void ast_timer_ack(int handle, unsigned int quantity)
135 {
136         ast_rwlock_rdlock(&lock);
137
138         if (!timer_funcs.timer_ack) {
139                 ast_rwlock_unlock(&lock);
140                 return;
141         }
142
143         timer_funcs.timer_ack(handle, quantity);
144
145         ast_rwlock_unlock(&lock);
146 }
147
148 int ast_timer_enable_continuous(int handle)
149 {
150         int result;
151
152         ast_rwlock_rdlock(&lock);
153
154         if (!timer_funcs.timer_enable_continuous) {
155                 ast_rwlock_unlock(&lock);
156                 return -1;
157         }
158
159         result = timer_funcs.timer_enable_continuous(handle);
160
161         ast_rwlock_unlock(&lock);
162
163         return result;
164 }
165
166 int ast_timer_disable_continuous(int handle)
167 {
168         int result;
169
170         ast_rwlock_rdlock(&lock);
171
172         if (!timer_funcs.timer_disable_continuous) {
173                 ast_rwlock_unlock(&lock);
174                 return -1;
175         }
176
177         result = timer_funcs.timer_disable_continuous(handle);
178
179         ast_rwlock_unlock(&lock);
180
181         return result;
182 }
183
184 enum ast_timing_event ast_timer_get_event(int handle)
185 {
186         enum ast_timing_event result;
187
188         ast_rwlock_rdlock(&lock);
189
190         if (!timer_funcs.timer_get_event) {
191                 ast_rwlock_unlock(&lock);
192                 return -1;
193         }
194
195         result = timer_funcs.timer_get_event(handle);
196
197         ast_rwlock_unlock(&lock);
198
199         return result;
200 }
201
202 unsigned int ast_timer_get_max_rate(int handle)
203 {
204         unsigned int res;
205
206         ast_rwlock_rdlock(&lock);
207
208         res = timer_funcs.timer_get_max_rate(handle);
209
210         ast_rwlock_unlock(&lock);
211
212         return res;
213 }
214
215 static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
216 {
217         int fd, count = 0;
218         struct timeval start, end;
219         unsigned int test_rate = 50;
220
221         switch (cmd) {
222         case CLI_INIT:
223                 e->command = "timing test";
224                 e->usage = "Usage: timing test <rate>\n"
225                            "   Test a timer with a specified rate, 100/sec by default.\n"
226                            "";
227                 return NULL;
228         case CLI_GENERATE:
229                 return NULL;
230         }
231
232         if (a->argc != 2 && a->argc != 3) {
233                 return CLI_SHOWUSAGE;
234         }
235
236         if (a->argc == 3) {
237                 unsigned int rate;
238                 if (sscanf(a->argv[2], "%u", &rate) == 1) {
239                         test_rate = rate;
240                 } else {
241                         ast_cli(a->fd, "Invalid rate '%s', using default of %u\n", a->argv[2], test_rate);      
242                 }
243         }
244
245         ast_cli(a->fd, "Attempting to test a timer with %u ticks per second ...\n", test_rate);
246
247         if ((fd = ast_timer_open()) == -1) {
248                 ast_cli(a->fd, "Failed to open timing fd\n");
249                 return CLI_FAILURE;
250         }
251
252         start = ast_tvnow();
253
254         ast_timer_set_rate(fd, test_rate);
255
256         while (ast_tvdiff_ms((end = ast_tvnow()), start) < 1000) {
257                 int res;
258                 struct pollfd pfd = {
259                         .fd = fd,
260                         .events = POLLIN | POLLPRI,
261                 };
262
263                 res = poll(&pfd, 1, 100);
264
265                 if (res == 1) {
266                         count++;
267                         ast_timer_ack(fd, 1);
268                 } else if (!res) {
269                         ast_cli(a->fd, "poll() timed out!  This is bad.\n");
270                 } else if (errno != EAGAIN && errno != EINTR) {
271                         ast_cli(a->fd, "poll() returned error: %s\n", strerror(errno));
272                 }
273         }
274
275         ast_timer_close(fd);
276
277         ast_cli(a->fd, "It has been %d milliseconds, and we got %d timer ticks\n", 
278                 ast_tvdiff_ms(end, start), count);
279
280         return CLI_SUCCESS;
281 }
282
283 static struct ast_cli_entry cli_timing[] = {
284         AST_CLI_DEFINE(timing_test, "Run a timing test"),
285 };
286
287 int ast_timing_init(void)
288 {
289         return ast_cli_register_multiple(cli_timing, ARRAY_LEN(cli_timing));
290 }