timing: Improve performance for most timing implementations.
[asterisk/asterisk.git] / res / res_timing_dahdi.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Digium, Inc.
5  *
6  * Russell Bryant <russell@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 /*! 
20  * \file
21  * \author Russell Bryant <russell@digium.com>
22  *
23  * \brief DAHDI timing interface 
24  */
25
26 /*** MODULEINFO
27         <depend>dahdi</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <math.h>
39
40 #include <dahdi/user.h>
41
42 #include "asterisk/module.h"
43 #include "asterisk/timing.h"
44 #include "asterisk/utils.h"
45
46 static void *timing_funcs_handle;
47
48 static void *dahdi_timer_open(void);
49 static void dahdi_timer_close(void *data);
50 static int dahdi_timer_set_rate(void *data, unsigned int rate);
51 static int dahdi_timer_ack(void *data, unsigned int quantity);
52 static int dahdi_timer_enable_continuous(void *data);
53 static int dahdi_timer_disable_continuous(void *data);
54 static enum ast_timer_event dahdi_timer_get_event(void *data);
55 static unsigned int dahdi_timer_get_max_rate(void *data);
56 static int dahdi_timer_fd(void *data);
57
58 static struct ast_timing_interface dahdi_timing = {
59         .name = "DAHDI",
60         .priority = 100,
61         .timer_open = dahdi_timer_open,
62         .timer_close = dahdi_timer_close,
63         .timer_set_rate = dahdi_timer_set_rate,
64         .timer_ack = dahdi_timer_ack,
65         .timer_enable_continuous = dahdi_timer_enable_continuous,
66         .timer_disable_continuous = dahdi_timer_disable_continuous,
67         .timer_get_event = dahdi_timer_get_event,
68         .timer_get_max_rate = dahdi_timer_get_max_rate,
69         .timer_fd = dahdi_timer_fd,
70 };
71
72 struct dahdi_timer {
73         int fd;
74 };
75
76 static void *dahdi_timer_open(void)
77 {
78         struct dahdi_timer *timer;
79
80         if (!(timer = ast_calloc(1, sizeof(*timer)))) {
81                 return NULL;
82         }
83
84         if ((timer->fd = open("/dev/dahdi/timer", O_RDWR)) < 0) {
85                 ast_log(LOG_ERROR, "Failed to create dahdi timer: %s\n", strerror(errno));
86                 ast_free(timer);
87                 return NULL;
88         }
89
90         return timer;
91 }
92
93 static void dahdi_timer_close(void *data)
94 {
95         struct dahdi_timer *timer = data;
96
97         close(timer->fd);
98         ast_free(timer);
99 }
100
101 static int dahdi_timer_set_rate(void *data, unsigned int rate)
102 {
103         struct dahdi_timer *timer = data;
104         int samples;
105
106         /* DAHDI timers are configured using a number of samples,
107          * based on an 8 kHz sample rate. */
108         samples = (unsigned int) roundf((8000.0 / ((float) rate)));
109
110         if (ioctl(timer->fd, DAHDI_TIMERCONFIG, &samples)) {
111                 ast_log(LOG_ERROR, "Failed to configure DAHDI timing fd for %u sample timer ticks\n",
112                         samples);
113                 return -1;
114         }
115
116         return 0;
117 }
118
119 static int dahdi_timer_ack(void *data, unsigned int quantity)
120 {
121         struct dahdi_timer *timer = data;
122
123         return ioctl(timer->fd, DAHDI_TIMERACK, &quantity) ? -1 : 0;
124 }
125
126 static int dahdi_timer_enable_continuous(void *data)
127 {
128         struct dahdi_timer *timer = data;
129         int flags = 1;
130
131         return ioctl(timer->fd, DAHDI_TIMERPING, &flags) ? -1 : 0;
132 }
133
134 static int dahdi_timer_disable_continuous(void *data)
135 {
136         struct dahdi_timer *timer = data;
137         int flags = -1;
138
139         return ioctl(timer->fd, DAHDI_TIMERPONG, &flags) ? -1 : 0;
140 }
141
142 static enum ast_timer_event dahdi_timer_get_event(void *data)
143 {
144         struct dahdi_timer *timer = data;
145         int res;
146         int event;
147
148         res = ioctl(timer->fd, DAHDI_GETEVENT, &event);
149
150         if (res) {
151                 event = DAHDI_EVENT_TIMER_EXPIRED;
152         }
153
154         switch (event) {
155         case DAHDI_EVENT_TIMER_PING:
156                 return AST_TIMING_EVENT_CONTINUOUS;
157         case DAHDI_EVENT_TIMER_EXPIRED:
158         default:
159                 return AST_TIMING_EVENT_EXPIRED;        
160         }
161 }
162
163 static unsigned int dahdi_timer_get_max_rate(void *data)
164 {
165         return 1000;
166 }
167
168 static int dahdi_timer_fd(void *data)
169 {
170         struct dahdi_timer *timer = data;
171
172         return timer->fd;
173 }
174
175 #define SEE_TIMING "For more information on Asterisk timing modules, including ways to potentially fix this problem, please see https://wiki.asterisk.org/wiki/display/AST/Timing+Interfaces\n"
176
177 static int dahdi_test_timer(void)
178 {
179         int fd;
180         int x = 160;
181         
182         fd = open("/dev/dahdi/timer", O_RDWR);
183
184         if (fd < 0) {
185                 return -1;
186         }
187
188         if (ioctl(fd, DAHDI_TIMERCONFIG, &x)) {
189                 ast_log(LOG_ERROR, "You have DAHDI built and drivers loaded, but the DAHDI timer test failed to set DAHDI_TIMERCONFIG to %d.\n" SEE_TIMING, x);
190                 close(fd);
191                 return -1;
192         }
193
194         if ((x = ast_wait_for_input(fd, 300)) < 0) {
195                 ast_log(LOG_ERROR, "You have DAHDI built and drivers loaded, but the DAHDI timer could not be polled during the DAHDI timer test.\n" SEE_TIMING);
196                 close(fd);
197                 return -1;
198         }
199
200         if (!x) {
201                 const char dahdi_timer_error[] = {
202                         "Asterisk has detected a problem with your DAHDI configuration and will shutdown for your protection.  You have options:"
203                         "\n\t1. You only have to compile DAHDI support into Asterisk if you need it.  One option is to recompile without DAHDI support."
204                         "\n\t2. You only have to load DAHDI drivers if you want to take advantage of DAHDI services.  One option is to unload DAHDI modules if you don't need them."
205                         "\n\t3. If you need DAHDI services, you must correctly configure DAHDI."
206                 };
207                 ast_log(LOG_ERROR, "%s\n" SEE_TIMING, dahdi_timer_error);
208                 usleep(100);
209                 close(fd);
210                 return -1;
211         }
212
213         close(fd);
214
215         return 0;
216 }
217
218 static int load_module(void)
219 {
220         if (dahdi_test_timer()) {
221                 return AST_MODULE_LOAD_DECLINE;
222         }
223
224         return (timing_funcs_handle = ast_register_timing_interface(&dahdi_timing)) ?
225                 AST_MODULE_LOAD_SUCCESS : AST_MODULE_LOAD_DECLINE;
226 }
227
228 static int unload_module(void)
229 {
230         if (timing_funcs_handle) {
231                 return ast_unregister_timing_interface(timing_funcs_handle);
232         }
233
234         return 0;
235 }
236
237 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "DAHDI Timing Interface",
238                 .load = load_module,
239                 .unload = unload_module,
240                 .load_pri = AST_MODPRI_TIMING,
241                 );