2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005, Christian Richter
6 * Christian Richter <crich@beronet.com>
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.
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.
23 * \brief chan_misdn configuration management
24 * \author Christian Richter <crich@beronet.com>
26 * \ingroup channel_drivers
31 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
38 #include "chan_misdn_config.h"
40 #include "asterisk/config.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/strings.h"
46 #include "asterisk/utils.h"
48 #define AST_LOAD_CFG ast_config_load
49 #define AST_DESTROY_CFG ast_config_destroy
51 #define NO_DEFAULT "<>"
56 #define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec))
57 #define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec))
70 struct msn_list
*next
;
81 struct misdn_cfg_spec
{
82 char name
[BUFFERSIZE
];
83 enum misdn_cfg_elements elem
;
84 enum misdn_cfg_type type
;
87 char desc
[BUFFERSIZE
];
91 static const char ports_description
[] =
92 "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order).";
94 static const struct misdn_cfg_spec port_spec
[] = {
95 { "name", MISDN_CFG_GROUPNAME
, MISDN_CTYPE_STR
, "default", NONE
,
96 "Name of the portgroup." },
97 { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS
, MISDN_CTYPE_STR
, "all", NONE
,
98 "Here you can define which bearers should be allowed." },
99 { "rxgain", MISDN_CFG_RXGAIN
, MISDN_CTYPE_INT
, "0", NONE
,
100 "Set this between -8 and 8 to change the RX Gain." },
101 { "txgain", MISDN_CFG_TXGAIN
, MISDN_CTYPE_INT
, "0", NONE
,
102 "Set this between -8 and 8 to change the TX Gain." },
103 { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL
, MISDN_CTYPE_BOOL
, "no", NONE
,
104 "Some telcos espacially in NL seem to need this set to yes,\n"
105 "\talso in switzerland this seems to be important." },
106 { "far_alerting", MISDN_CFG_FAR_ALERTING
, MISDN_CTYPE_BOOL
, "no", NONE
,
107 "If we should generate ringing for chan_sip and others." },
108 { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK
, MISDN_CTYPE_BOOL
, "no", NONE
,
109 "This option defines, if chan_misdn should check the L1 on a PMP\n"
110 "\tbefore makeing a group call on it. The L1 may go down for PMP Ports\n"
111 "\tso we might need this.\n"
112 "\tBut be aware! a broken or plugged off cable might be used for a group call\n"
113 "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n"
114 "\tbecause of a lost Link or because the Provider shut it down..." },
115 { "block_on_alarm", MISDN_CFG_ALARM_BLOCK
, MISDN_CTYPE_BOOL
, "no", NONE
,
116 "Block this port if we have an alarm on it."
118 { "hdlc", MISDN_CFG_HDLC
, MISDN_CTYPE_BOOL
, "no", NONE
,
119 "Set this to yes, if you want to bridge a mISDN data channel to\n"
120 "\tanother channel type or to an application." },
121 { "context", MISDN_CFG_CONTEXT
, MISDN_CTYPE_STR
, "default", NONE
,
122 "Context to use for incoming calls." },
123 { "language", MISDN_CFG_LANGUAGE
, MISDN_CTYPE_STR
, "en", NONE
,
125 { "musicclass", MISDN_CFG_MUSICCLASS
, MISDN_CTYPE_STR
, "default", NONE
,
126 "Sets the musiconhold class." },
127 { "callerid", MISDN_CFG_CALLERID
, MISDN_CTYPE_STR
, "", NONE
,
128 "Sets the caller ID." },
129 { "method", MISDN_CFG_METHOD
, MISDN_CTYPE_STR
, "standard", NONE
,
130 "Sets the method to use for channel selection:\n"
131 "\t standard - always choose the first free channel with the lowest number\n"
132 "\t round_robin - use the round robin algorithm to select a channel. use this\n"
133 "\t if you want to balance your load." },
134 { "dialplan", MISDN_CFG_DIALPLAN
, MISDN_CTYPE_INT
, "0", NONE
,
135 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
137 "\tThere are different types of the dialplan:\n"
139 "\tdialplan -> outgoing Number\n"
140 "\tlocaldialplan -> callerid\n"
141 "\tcpndialplan -> connected party number\n"
143 "\tdialplan options:\n"
146 "\t1 - International\n"
150 "\tThis setting is used for outgoing calls." },
151 { "localdialplan", MISDN_CFG_LOCALDIALPLAN
, MISDN_CTYPE_INT
, "0", NONE
,
152 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
154 "\tThere are different types of the dialplan:\n"
156 "\tdialplan -> outgoing Number\n"
157 "\tlocaldialplan -> callerid\n"
158 "\tcpndialplan -> connected party number\n"
160 "\tdialplan options:\n"
163 "\t1 - International\n"
167 "\tThis setting is used for outgoing calls" },
168 { "cpndialplan", MISDN_CFG_CPNDIALPLAN
, MISDN_CTYPE_INT
, "0", NONE
,
169 "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n"
171 "\tThere are different types of the dialplan:\n"
173 "\tdialplan -> outgoing Number\n"
174 "\tlocaldialplan -> callerid\n"
175 "\tcpndialplan -> connected party number\n"
177 "\tdialplan options:\n"
180 "\t1 - International\n"
184 "\tThis setting is used for outgoing calls." },
185 { "nationalprefix", MISDN_CFG_NATPREFIX
, MISDN_CTYPE_STR
, "0", NONE
,
186 "Prefix for national, this is put before the\n"
187 "\toad if an according dialplan is set by the other end." },
188 { "internationalprefix", MISDN_CFG_INTERNATPREFIX
, MISDN_CTYPE_STR
, "00", NONE
,
189 "Prefix for international, this is put before the\n"
190 "\toad if an according dialplan is set by the other end." },
191 { "presentation", MISDN_CFG_PRES
, MISDN_CTYPE_INT
, "-1", NONE
,
192 "These (presentation and screen) are the exact isdn screening and presentation\n"
194 "\tIf -1 is given for both values, the presentation indicators are used from\n"
195 "\tAsterisks SetCallerPres application.\n"
197 "\tscreen=0, presentation=0 -> callerid presented not screened\n"
198 "\tscreen=1, presentation=1 -> callerid presented but screened (the remote end doesn't see it!)" },
199 { "screen", MISDN_CFG_SCREEN
, MISDN_CTYPE_INT
, "-1", NONE
,
200 "These (presentation and screen) are the exact isdn screening and presentation\n"
202 "\tIf -1 is given for both values, the presentation indicators are used from\n"
203 "\tAsterisks SetCallerPres application.\n"
205 "\tscreen=0, presentation=0 -> callerid presented not screened\n"
206 "\tscreen=1, presentation=1 -> callerid presented but screened (the remote end doesn't see it!)" },
207 { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE
, MISDN_CTYPE_BOOL
, "no", NONE
,
208 "Enable this to get into the s dialplan-extension.\n"
209 "\tThere you can use DigitTimeout if you can't or don't want to use\n"
210 "\tisdn overlap dial.\n"
211 "\tNOTE: This will jump into the s extension for every exten!" },
212 { "nodialtone", MISDN_CFG_NODIALTONE
, MISDN_CTYPE_BOOL
, "no", NONE
,
213 "Enable this to prevent chan_misdn to generate the dialtone\n"
214 "\tThis makes only sense together with the always_immediate=yes option\n"
215 "\tto generate your own dialtone with Playtones or so."},
216 { "immediate", MISDN_CFG_IMMEDIATE
, MISDN_CTYPE_BOOL
, "no", NONE
,
217 "Enable this if you want callers which called exactly the base\n"
218 "\tnumber (so no extension is set) to jump into the s extension.\n"
219 "\tIf the user dials something more, it jumps to the correct extension\n"
221 { "senddtmf", MISDN_CFG_SENDDTMF
, MISDN_CTYPE_BOOL
, "no", NONE
,
222 "Enable this if we should produce DTMF Tones ourselves." },
223 { "astdtmf", MISDN_CFG_ASTDTMF
, MISDN_CTYPE_BOOL
, "no", NONE
,
224 "Enable this if you want to use the Asterisk dtmf detector\n"
225 "instead of the mISDN_dsp/hfcmulti one."
227 { "hold_allowed", MISDN_CFG_HOLD_ALLOWED
, MISDN_CTYPE_BOOL
, "no", NONE
,
228 "Enable this to have support for hold and retrieve." },
229 { "early_bconnect", MISDN_CFG_EARLY_BCONNECT
, MISDN_CTYPE_BOOL
, "yes", NONE
,
230 "Disable this if you don't mind correct handling of Progress Indicators." },
231 { "incoming_early_audio", MISDN_CFG_INCOMING_EARLY_AUDIO
, MISDN_CTYPE_BOOL
, "no", NONE
,
232 "Turn this on if you like to send Tone Indications to a Incoming\n"
233 "\tisdn channel on a TE Port. Rarely used, only if the Telco allows\n"
234 "\tyou to send indications by yourself, normally the Telco sends the\n"
235 "\tindications to the remote party." },
236 { "echocancel", MISDN_CFG_ECHOCANCEL
, MISDN_CTYPE_BOOLINT
, "0", 128,
237 "This enables echocancellation, with the given number of taps.\n"
238 "\tBe aware, move this setting only to outgoing portgroups!\n"
239 "\tA value of zero turns echocancellation off.\n"
241 "\tPossible values are: 0,32,64,128,256,yes(=128),no(=0)" },
243 { "pipeline", MISDN_CFG_PIPELINE
, MISDN_CTYPE_STR
, NO_DEFAULT
, NONE
,
244 "Set the configuration string for the mISDN dsp pipeline.\n"
246 "\tExample for enabling the mg2 echo cancellation module with deftaps\n"
248 "\t\tmg2ec(deftaps=128)" },
251 { "bnechocancel", MISDN_CFG_BNECHOCANCEL
, MISDN_CTYPE_BOOLINT
, "yes", 64,
252 "echotail in ms (1-200)\n"},
253 { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL
, MISDN_CTYPE_INT
, "0", NONE
,
255 { "bnec_nlp", MISDN_CFG_BNEC_NLP
, MISDN_CTYPE_BOOL
, "yes", NONE
,
256 "Nonlinear Processing (much faster adaption)"},
257 { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF
, MISDN_CTYPE_BOOL
, "no", NONE
,
258 "ZeroCoeffeciens\n"},
259 { "bnec_tonedisabler", MISDN_CFG_BNEC_TD
, MISDN_CTYPE_BOOL
, "no", NONE
,
261 { "bnec_adaption", MISDN_CFG_BNEC_ADAPT
, MISDN_CTYPE_INT
, "1", NONE
,
262 "Adaption mode (0=no,1=full,2=fast)\n"},
264 { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS
, MISDN_CTYPE_BOOL
, "0", NONE
,
265 "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n"
266 "\tthis requests additional Infos, so we can waitfordigits without much\n"
267 "\tissues. This works only for PTP Ports" },
268 { "noautorespond_on_setup", MISDN_CFG_NOAUTORESPOND_ON_SETUP
, MISDN_CTYPE_BOOL
, "0", NONE
,
269 "Do not send SETUP_ACKNOWLEDGE or PROCEEDING automatically to the calling Party.\n"
270 "Instead we directly jump into the dialplan. This might be useful for fast call\n"
271 "rejection, or for some broken switches, that need hangup causes like busy in the.\n"
272 "RELEASE_COMPLETE Message, instead of the DISCONNECT Message.\n"},
273 { "jitterbuffer", MISDN_CFG_JITTERBUFFER
, MISDN_CTYPE_INT
, "4000", NONE
,
274 "The jitterbuffer." },
275 { "jitterbuffer_upper_threshold", MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD
, MISDN_CTYPE_INT
, "0", NONE
,
276 "Change this threshold to enable dejitter functionality." },
277 { "callgroup", MISDN_CFG_CALLGROUP
, MISDN_CTYPE_ASTGROUP
, NO_DEFAULT
, NONE
,
279 { "pickupgroup", MISDN_CFG_PICKUPGROUP
, MISDN_CTYPE_ASTGROUP
, NO_DEFAULT
, NONE
,
281 { "max_incoming", MISDN_CFG_MAX_IN
, MISDN_CTYPE_INT
, "-1", NONE
,
282 "Defines the maximum amount of incoming calls per port for this group.\n"
283 "\tCalls which exceed the maximum will be marked with the channel varible\n"
284 "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" },
285 { "max_outgoing", MISDN_CFG_MAX_OUT
, MISDN_CTYPE_INT
, "-1", NONE
,
286 "Defines the maximum amount of outgoing calls per port for this group\n"
287 "\texceeding calls will be rejected" },
289 { "reject_cause", MISDN_CFG_REJECT_CAUSE
, MISDN_CTYPE_INT
, "21", NONE
,
290 "Defines the cause with which a 3. call is rejected on PTMP BRI."},
291 { "faxdetect", MISDN_CFG_FAXDETECT
, MISDN_CTYPE_STR
, "no", NONE
,
292 "Setup fax detection:\n"
293 "\t no - no fax detection\n"
294 "\t incoming - fax detection for incoming calls\n"
295 "\t outgoing - fax detection for outgoing calls\n"
296 "\t both - fax detection for incoming and outgoing calls\n"
297 "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n"
298 "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." },
299 { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT
, MISDN_CTYPE_INT
, "5", NONE
,
300 "Number of seconds the fax detection should do its job. After the given period of time,\n"
301 "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n"
302 "\tSet this to 0 if you don't want a timeout (never stop detecting)." },
303 { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT
, MISDN_CTYPE_STR
, NO_DEFAULT
, NONE
,
304 "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." },
305 { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT
, MISDN_CTYPE_BOOLINT
, "0", 4,
306 "Watches the layer 1. If the layer 1 is down, it tries to\n"
307 "\tget it up. The timeout is given in seconds. with 0 as value it\n"
308 "\tdoes not watch the l1 at all\n"
310 "\tThis option is only read at loading time of chan_misdn, which\n"
311 "\tmeans you need to unload and load chan_misdn to change the value,\n"
312 "\tan Asterisk restart should do the trick." },
313 { "overlapdial", MISDN_CFG_OVERLAP_DIAL
, MISDN_CTYPE_BOOLINT
, "0", 4,
314 "Enables overlap dial for the given amount of seconds.\n"
315 "\tPossible values are positive integers or:\n"
316 "\t yes (= 4 seconds)\n"
317 "\t no (= 0 seconds = disabled)" },
318 { "nttimeout", MISDN_CFG_NTTIMEOUT
, MISDN_CTYPE_BOOL
, "no", NONE
,
319 "Set this to yes if you want calls disconnected in overlap mode"
320 "when a timeout happens.\n"},
321 { "bridging", MISDN_CFG_BRIDGING
, MISDN_CTYPE_BOOL
, "yes", NONE
,
322 "Set this to yes/no, default is yes.\n"
323 "This can be used to have bridging enabled in general and to\n"
324 "disable it for specific ports. It makes sense to disable\n"
325 "bridging on NT Port where you plan to use the HOLD/RETRIEVE\n"
326 "features with ISDN phones.\n"
328 { "msns", MISDN_CFG_MSNS
, MISDN_CTYPE_MSNLIST
, "*", NONE
,
329 "MSN's for TE ports, listen on those numbers on the above ports, and\n"
330 "\tindicate the incoming calls to Asterisk.\n"
331 "\tHere you can give a comma seperated list, or simply an '*' for any msn." },
334 static const struct misdn_cfg_spec gen_spec
[] = {
335 { "debug", MISDN_GEN_DEBUG
, MISDN_CTYPE_INT
, "0", NONE
,
336 "Sets the debugging flag:\n"
338 "\t1 - mISDN Messages and * - Messages, and * - State changes\n"
339 "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n"
340 "\t3 - very Verbose, the above + lots of Driver specific infos\n"
341 "\t4 - even more Verbose than 3" },
343 { "misdn_init", MISDN_GEN_MISDN_INIT
, MISDN_CTYPE_STR
, "/etc/misdn-init.conf", NONE
,
344 "Set the path to the misdn-init.conf (for nt_ptp mode checking)." },
346 { "tracefile", MISDN_GEN_TRACEFILE
, MISDN_CTYPE_STR
, "/var/log/asterisk/misdn.log", NONE
,
347 "Set the path to the massively growing trace file, if you want that." },
348 { "bridging", MISDN_GEN_BRIDGING
, MISDN_CTYPE_BOOL
, "yes", NONE
,
349 "Set this to yes if you want mISDN_dsp to bridge the calls in HW." },
350 { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE
, MISDN_CTYPE_BOOL
, "yes", NONE
,
351 "Stops dialtone after getting first digit on NT Port." },
352 { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN
, MISDN_CTYPE_BOOL
, "yes", NONE
,
353 "Wether to append overlapdialed Digits to Extension or not." },
354 { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT
, MISDN_CTYPE_BOOL
, "no", NONE
,
355 "Wether to look out for dynamic crypting attempts." },
356 { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX
, MISDN_CTYPE_STR
, NO_DEFAULT
, NONE
,
357 "What is used for crypting Protocol." },
358 { "crypt_keys", MISDN_GEN_CRYPT_KEYS
, MISDN_CTYPE_STR
, NO_DEFAULT
, NONE
,
359 "Keys for cryption, you reference them in the dialplan\n"
360 "\tLater also in dynamic encr." },
361 { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS
, MISDN_CTYPE_BOOL
, "no", NONE
,
362 "avoid dropping calls if the L2 goes down. some nortel pbx\n"
363 "do put down the L2/L1 for some milliseconds even if there\n"
364 "are running calls. with this option you can avoid dropping them\n" },
365 { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS
, MISDN_CTYPE_INT
, "0", NONE
,
366 "No description yet."},
367 { "ntdebugfile", MISDN_GEN_NTDEBUGFILE
, MISDN_CTYPE_STR
, "/var/log/misdn-nt.log", NONE
,
368 "No description yet." }
372 /* array of port configs, default is at position 0. */
373 static union misdn_cfg_pt
**port_cfg
;
374 /* max number of available ports, is set on init */
375 static int max_ports
;
377 static union misdn_cfg_pt
*general_cfg
;
378 /* storing the ptp flag separated to save memory */
380 /* maps enum config elements to array positions */
383 static ast_mutex_t config_mutex
;
385 #define CLI_ERROR(name, value, section) ({ \
386 ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \
387 "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \
390 static int _enum_array_map (void)
394 for (i
= MISDN_CFG_FIRST
+ 1; i
< MISDN_CFG_LAST
; ++i
) {
395 if (i
== MISDN_CFG_PTP
)
398 for (j
= 0; j
< NUM_PORT_ELEMENTS
; ++j
) {
399 if (port_spec
[j
].elem
== i
) {
406 ast_log(LOG_WARNING
, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i
);
410 for (i
= MISDN_GEN_FIRST
+ 1; i
< MISDN_GEN_LAST
; ++i
) {
412 for (j
= 0; j
< NUM_GEN_ELEMENTS
; ++j
) {
413 if (gen_spec
[j
].elem
== i
) {
420 ast_log(LOG_WARNING
, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i
);
427 static int get_cfg_position (char *name
, int type
)
433 for (i
= 0; i
< NUM_PORT_ELEMENTS
; ++i
) {
434 if (!strcasecmp(name
, port_spec
[i
].name
))
439 for (i
= 0; i
< NUM_GEN_ELEMENTS
; ++i
) {
440 if (!strcasecmp(name
, gen_spec
[i
].name
))
448 static inline void misdn_cfg_lock (void)
450 ast_mutex_lock(&config_mutex
);
453 static inline void misdn_cfg_unlock (void)
455 ast_mutex_unlock(&config_mutex
);
458 static void _free_msn_list (struct msn_list
* iter
)
461 _free_msn_list(iter
->next
);
467 static void _free_port_cfg (void)
470 int gn
= map
[MISDN_CFG_GROUPNAME
];
471 union misdn_cfg_pt
* free_list
[max_ports
+ 2];
473 memset(free_list
, 0, sizeof(free_list
));
474 free_list
[0] = port_cfg
[0];
475 for (i
= 1; i
<= max_ports
; ++i
) {
476 if (port_cfg
[i
][gn
].str
) {
477 /* we always have a groupname in the non-default case, so this is fine */
478 for (j
= 1; j
<= max_ports
; ++j
) {
479 if (free_list
[j
] && free_list
[j
][gn
].str
== port_cfg
[i
][gn
].str
)
481 else if (!free_list
[j
]) {
482 free_list
[j
] = port_cfg
[i
];
488 for (j
= 0; free_list
[j
]; ++j
) {
489 for (i
= 0; i
< NUM_PORT_ELEMENTS
; ++i
) {
490 if (free_list
[j
][i
].any
) {
491 if (port_spec
[i
].type
== MISDN_CTYPE_MSNLIST
)
492 _free_msn_list(free_list
[j
][i
].ml
);
494 free(free_list
[j
][i
].any
);
500 static void _free_general_cfg (void)
504 for (i
= 0; i
< NUM_GEN_ELEMENTS
; i
++)
505 if (general_cfg
[i
].any
)
506 free(general_cfg
[i
].any
);
509 void misdn_cfg_get (int port
, enum misdn_cfg_elements elem
, void *buf
, int bufsize
)
513 if ((elem
< MISDN_CFG_LAST
) && !misdn_cfg_is_port_valid(port
)) {
514 memset(buf
, 0, bufsize
);
515 ast_log(LOG_WARNING
, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port
);
520 if (elem
== MISDN_CFG_PTP
) {
521 if (!memcpy(buf
, &ptp
[port
], (bufsize
> ptp
[port
]) ? sizeof(ptp
[port
]) : bufsize
))
522 memset(buf
, 0, bufsize
);
524 if ((place
= map
[elem
]) < 0) {
525 memset (buf
, 0, bufsize
);
526 ast_log(LOG_WARNING
, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem
);
528 if (elem
< MISDN_CFG_LAST
) {
529 switch (port_spec
[place
].type
) {
530 case MISDN_CTYPE_STR
:
531 if (port_cfg
[port
][place
].str
) {
532 if (!memccpy(buf
, port_cfg
[port
][place
].str
, 0, bufsize
))
534 } else if (port_cfg
[0][place
].str
) {
535 if (!memccpy(buf
, port_cfg
[0][place
].str
, 0, bufsize
))
538 memset(buf
, 0, bufsize
);
541 if (port_cfg
[port
][place
].any
)
542 memcpy(buf
, port_cfg
[port
][place
].any
, bufsize
);
543 else if (port_cfg
[0][place
].any
)
544 memcpy(buf
, port_cfg
[0][place
].any
, bufsize
);
546 memset(buf
, 0, bufsize
);
549 switch (gen_spec
[place
].type
) {
550 case MISDN_CTYPE_STR
:
551 if (!general_cfg
[place
].str
|| !memccpy(buf
, general_cfg
[place
].str
, 0, bufsize
))
555 if (general_cfg
[place
].any
)
556 memcpy(buf
, general_cfg
[place
].any
, bufsize
);
558 memset(buf
, 0, bufsize
);
566 enum misdn_cfg_elements
misdn_cfg_get_elem (char *name
)
570 /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
571 if (!strcmp(name
, "ports"))
572 return MISDN_CFG_GROUPNAME
;
573 if (!strcmp(name
, "name"))
574 return MISDN_CFG_FIRST
;
576 pos
= get_cfg_position (name
, PORT_CFG
);
578 return port_spec
[pos
].elem
;
580 pos
= get_cfg_position (name
, GEN_CFG
);
582 return gen_spec
[pos
].elem
;
584 return MISDN_CFG_FIRST
;
587 void misdn_cfg_get_name (enum misdn_cfg_elements elem
, void *buf
, int bufsize
)
589 struct misdn_cfg_spec
*spec
= NULL
;
590 int place
= map
[elem
];
593 if (elem
== MISDN_CFG_PTP
) {
598 /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
599 if (elem
== MISDN_CFG_GROUPNAME
) {
600 if (!snprintf(buf
, bufsize
, "ports"))
605 if ((elem
> MISDN_CFG_FIRST
) && (elem
< MISDN_CFG_LAST
))
606 spec
= (struct misdn_cfg_spec
*)port_spec
;
607 else if ((elem
> MISDN_GEN_FIRST
) && (elem
< MISDN_GEN_LAST
))
608 spec
= (struct misdn_cfg_spec
*)gen_spec
;
610 if (!spec
|| !memccpy(buf
, spec
[place
].name
, 0, bufsize
))
614 void misdn_cfg_get_desc (enum misdn_cfg_elements elem
, void *buf
, int bufsize
, void *buf_default
, int bufsize_default
)
616 int place
= map
[elem
];
617 struct misdn_cfg_spec
*spec
= NULL
;
619 /* here comes a hack to replace the (not existing) "name" elemet with the "ports" element */
620 if (elem
== MISDN_CFG_GROUPNAME
) {
621 if (!memccpy(buf
, ports_description
, 0, bufsize
))
623 if (buf_default
&& bufsize_default
)
624 memset(buf_default
, 0, 1);
628 if ((elem
> MISDN_CFG_FIRST
) && (elem
< MISDN_CFG_LAST
))
629 spec
= (struct misdn_cfg_spec
*)port_spec
;
630 else if ((elem
> MISDN_GEN_FIRST
) && (elem
< MISDN_GEN_LAST
))
631 spec
= (struct misdn_cfg_spec
*)gen_spec
;
633 if (!spec
|| !spec
[place
].desc
)
636 if (!memccpy(buf
, spec
[place
].desc
, 0, bufsize
))
638 if (buf_default
&& bufsize
) {
639 if (!strcmp(spec
[place
].def
, NO_DEFAULT
))
640 memset(buf_default
, 0, 1);
641 else if (!memccpy(buf_default
, spec
[place
].def
, 0, bufsize_default
))
642 memset(buf_default
, 0, 1);
647 int misdn_cfg_is_msn_valid (int port
, char* msn
)
650 struct msn_list
*iter
;
652 if (!misdn_cfg_is_port_valid(port
)) {
653 ast_log(LOG_WARNING
, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port
);
658 if (port_cfg
[port
][map
[MISDN_CFG_MSNS
]].ml
)
659 iter
= port_cfg
[port
][map
[MISDN_CFG_MSNS
]].ml
;
661 iter
= port_cfg
[0][map
[MISDN_CFG_MSNS
]].ml
;
662 for (; iter
; iter
= iter
->next
)
663 if (*(iter
->msn
) == '*' || ast_extension_match(iter
->msn
, msn
)) {
672 int misdn_cfg_is_port_valid (int port
)
674 int gn
= map
[MISDN_CFG_GROUPNAME
];
676 return (port
>= 1 && port
<= max_ports
&& port_cfg
[port
][gn
].str
);
679 int misdn_cfg_is_group_method (char *group
, enum misdn_cfg_method meth
)
686 method
= port_cfg
[0][map
[MISDN_CFG_METHOD
]].str
;
688 for (i
= 1; i
<= max_ports
; i
++) {
689 if (port_cfg
[i
] && port_cfg
[i
][map
[MISDN_CFG_GROUPNAME
]].str
) {
690 if (!strcasecmp(port_cfg
[i
][map
[MISDN_CFG_GROUPNAME
]].str
, group
))
691 method
= (port_cfg
[i
][map
[MISDN_CFG_METHOD
]].str
?
692 port_cfg
[i
][map
[MISDN_CFG_METHOD
]].str
: port_cfg
[0][map
[MISDN_CFG_METHOD
]].str
);
698 case METHOD_STANDARD
: re
= !strcasecmp(method
, "standard");
700 case METHOD_ROUND_ROBIN
: re
= !strcasecmp(method
, "round_robin");
702 case METHOD_STANDARD_DEC
: re
= !strcasecmp(method
, "standard_dec");
711 void misdn_cfg_get_ports_string (char *ports
)
715 int gn
= map
[MISDN_CFG_GROUPNAME
];
720 for (i
= 1; i
<= max_ports
; i
++) {
721 if (port_cfg
[i
][gn
].str
) {
723 sprintf(tmp
, "%dptp,", i
);
725 sprintf(tmp
, "%d,", i
);
731 if ((l
= strlen(ports
)))
735 void misdn_cfg_get_config_string (int port
, enum misdn_cfg_elements elem
, char* buf
, int bufsize
)
738 char tempbuf
[BUFFERSIZE
] = "";
739 struct msn_list
*iter
;
741 if ((elem
< MISDN_CFG_LAST
) && !misdn_cfg_is_port_valid(port
)) {
743 ast_log(LOG_WARNING
, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port
);
750 if (elem
== MISDN_CFG_PTP
) {
751 snprintf(buf
, bufsize
, " -> ptp: %s", ptp
[port
] ? "yes" : "no");
753 else if (elem
> MISDN_CFG_FIRST
&& elem
< MISDN_CFG_LAST
) {
754 switch (port_spec
[place
].type
) {
755 case MISDN_CTYPE_INT
:
756 case MISDN_CTYPE_BOOLINT
:
757 if (port_cfg
[port
][place
].num
)
758 snprintf(buf
, bufsize
, " -> %s: %d", port_spec
[place
].name
, *port_cfg
[port
][place
].num
);
759 else if (port_cfg
[0][place
].num
)
760 snprintf(buf
, bufsize
, " -> %s: %d", port_spec
[place
].name
, *port_cfg
[0][place
].num
);
762 snprintf(buf
, bufsize
, " -> %s:", port_spec
[place
].name
);
764 case MISDN_CTYPE_BOOL
:
765 if (port_cfg
[port
][place
].num
)
766 snprintf(buf
, bufsize
, " -> %s: %s", port_spec
[place
].name
, *port_cfg
[port
][place
].num
? "yes" : "no");
767 else if (port_cfg
[0][place
].num
)
768 snprintf(buf
, bufsize
, " -> %s: %s", port_spec
[place
].name
, *port_cfg
[0][place
].num
? "yes" : "no");
770 snprintf(buf
, bufsize
, " -> %s:", port_spec
[place
].name
);
772 case MISDN_CTYPE_ASTGROUP
:
773 if (port_cfg
[port
][place
].grp
)
774 snprintf(buf
, bufsize
, " -> %s: %s", port_spec
[place
].name
,
775 ast_print_group(tempbuf
, sizeof(tempbuf
), *port_cfg
[port
][place
].grp
));
776 else if (port_cfg
[0][place
].grp
)
777 snprintf(buf
, bufsize
, " -> %s: %s", port_spec
[place
].name
,
778 ast_print_group(tempbuf
, sizeof(tempbuf
), *port_cfg
[0][place
].grp
));
780 snprintf(buf
, bufsize
, " -> %s:", port_spec
[place
].name
);
782 case MISDN_CTYPE_MSNLIST
:
783 if (port_cfg
[port
][place
].ml
)
784 iter
= port_cfg
[port
][place
].ml
;
786 iter
= port_cfg
[0][place
].ml
;
788 for (; iter
; iter
= iter
->next
)
789 sprintf(tempbuf
, "%s%s, ", tempbuf
, iter
->msn
);
790 tempbuf
[strlen(tempbuf
)-2] = 0;
792 snprintf(buf
, bufsize
, " -> msns: %s", *tempbuf
? tempbuf
: "none");
794 case MISDN_CTYPE_STR
:
795 if ( port_cfg
[port
][place
].str
) {
796 snprintf(buf
, bufsize
, " -> %s: %s", port_spec
[place
].name
, port_cfg
[port
][place
].str
);
797 } else if (port_cfg
[0][place
].str
) {
798 snprintf(buf
, bufsize
, " -> %s: %s", port_spec
[place
].name
, port_cfg
[0][place
].str
);
800 snprintf(buf
, bufsize
, " -> %s:", port_spec
[place
].name
);
804 } else if (elem
> MISDN_GEN_FIRST
&& elem
< MISDN_GEN_LAST
) {
805 switch (gen_spec
[place
].type
) {
806 case MISDN_CTYPE_INT
:
807 case MISDN_CTYPE_BOOLINT
:
808 if (general_cfg
[place
].num
)
809 snprintf(buf
, bufsize
, " -> %s: %d", gen_spec
[place
].name
, *general_cfg
[place
].num
);
811 snprintf(buf
, bufsize
, " -> %s:", gen_spec
[place
].name
);
813 case MISDN_CTYPE_BOOL
:
814 if (general_cfg
[place
].num
)
815 snprintf(buf
, bufsize
, " -> %s: %s", gen_spec
[place
].name
, *general_cfg
[place
].num
? "yes" : "no");
817 snprintf(buf
, bufsize
, " -> %s:", gen_spec
[place
].name
);
819 case MISDN_CTYPE_STR
:
820 if ( general_cfg
[place
].str
) {
821 snprintf(buf
, bufsize
, " -> %s: %s", gen_spec
[place
].name
, general_cfg
[place
].str
);
823 snprintf(buf
, bufsize
, " -> %s:", gen_spec
[place
].name
);
827 snprintf(buf
, bufsize
, " -> type of %s not handled yet", gen_spec
[place
].name
);
832 ast_log(LOG_WARNING
, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem
);
837 int misdn_cfg_get_next_port (int port
)
840 int gn
= map
[MISDN_CFG_GROUPNAME
];
843 for (port
++; port
<= max_ports
; port
++) {
844 if (port_cfg
[port
][gn
].str
) {
854 int misdn_cfg_get_next_port_spin (int port
)
856 int p
= misdn_cfg_get_next_port(port
);
857 return (p
> 0) ? p
: misdn_cfg_get_next_port(0);
860 static int _parse (union misdn_cfg_pt
*dest
, char *value
, enum misdn_cfg_type type
, int boolint_def
)
867 case MISDN_CTYPE_STR
:
868 if ((len
= strlen(value
))) {
869 dest
->str
= (char *)malloc((len
+ 1) * sizeof(char));
870 strncpy(dest
->str
, value
, len
);
873 dest
->str
= (char *)malloc( sizeof(char));
877 case MISDN_CTYPE_INT
:
880 if (strchr(value
,'x'))
884 if (sscanf(value
, pat
, &tmp
)) {
885 dest
->num
= (int *)malloc(sizeof(int));
886 memcpy(dest
->num
, &tmp
, sizeof(int));
891 case MISDN_CTYPE_BOOL
:
892 dest
->num
= (int *)malloc(sizeof(int));
893 *(dest
->num
) = (ast_true(value
) ? 1 : 0);
895 case MISDN_CTYPE_BOOLINT
:
896 dest
->num
= (int *)malloc(sizeof(int));
897 if (sscanf(value
, "%d", &tmp
)) {
898 memcpy(dest
->num
, &tmp
, sizeof(int));
900 *(dest
->num
) = (ast_true(value
) ? boolint_def
: 0);
903 case MISDN_CTYPE_MSNLIST
:
904 for (valtmp
= strsep(&value
, ","); valtmp
; valtmp
= strsep(&value
, ",")) {
905 if ((len
= strlen(valtmp
))) {
906 struct msn_list
*ml
= (struct msn_list
*)malloc(sizeof(struct msn_list
));
907 ml
->msn
= (char *)calloc(len
+1, sizeof(char));
908 strncpy(ml
->msn
, valtmp
, len
);
914 case MISDN_CTYPE_ASTGROUP
:
915 dest
->grp
= (ast_group_t
*)malloc(sizeof(ast_group_t
));
916 *(dest
->grp
) = ast_get_group(value
);
923 static void _build_general_config (struct ast_variable
*v
)
927 for (; v
; v
= v
->next
) {
928 if (((pos
= get_cfg_position(v
->name
, GEN_CFG
)) < 0) ||
929 (_parse(&general_cfg
[pos
], v
->value
, gen_spec
[pos
].type
, gen_spec
[pos
].boolint_def
) < 0))
930 CLI_ERROR(v
->name
, v
->value
, "general");
934 static void _build_port_config (struct ast_variable
*v
, char *cat
)
937 union misdn_cfg_pt cfg_tmp
[NUM_PORT_ELEMENTS
];
938 int cfg_for_ports
[max_ports
+ 1];
943 memset(cfg_tmp
, 0, sizeof(cfg_tmp
));
944 memset(cfg_for_ports
, 0, sizeof(cfg_for_ports
));
946 if (!strcasecmp(cat
, "default")) {
947 cfg_for_ports
[0] = 1;
950 if (((pos
= get_cfg_position("name", PORT_CFG
)) < 0) ||
951 (_parse(&cfg_tmp
[pos
], cat
, port_spec
[pos
].type
, port_spec
[pos
].boolint_def
) < 0)) {
952 CLI_ERROR(v
->name
, v
->value
, cat
);
956 for (; v
; v
= v
->next
) {
957 if (!strcasecmp(v
->name
, "ports")) {
959 char ptpbuf
[BUFFERSIZE
] = "";
961 for (token
= strsep(&v
->value
, ","); token
; token
= strsep(&v
->value
, ","), *ptpbuf
= 0) {
964 if (sscanf(token
, "%d-%d%s", &start
, &end
, ptpbuf
) >= 2) {
965 for (; start
<= end
; start
++) {
966 if (start
<= max_ports
&& start
> 0) {
967 cfg_for_ports
[start
] = 1;
968 ptp
[start
] = (strstr(ptpbuf
, "ptp")) ? 1 : 0;
970 CLI_ERROR(v
->name
, v
->value
, cat
);
973 if (sscanf(token
, "%d%s", &start
, ptpbuf
)) {
974 if (start
<= max_ports
&& start
> 0) {
975 cfg_for_ports
[start
] = 1;
976 ptp
[start
] = (strstr(ptpbuf
, "ptp")) ? 1 : 0;
978 CLI_ERROR(v
->name
, v
->value
, cat
);
980 CLI_ERROR(v
->name
, v
->value
, cat
);
984 if (((pos
= get_cfg_position(v
->name
, PORT_CFG
)) < 0) ||
985 (_parse(&cfg_tmp
[pos
], v
->value
, port_spec
[pos
].type
, port_spec
[pos
].boolint_def
) < 0))
986 CLI_ERROR(v
->name
, v
->value
, cat
);
990 for (i
= 0; i
< (max_ports
+ 1); ++i
) {
991 if (cfg_for_ports
[i
]) {
992 memcpy(port_cfg
[i
], cfg_tmp
, sizeof(cfg_tmp
));
997 void misdn_cfg_update_ptp (void)
1000 char misdn_init
[BUFFERSIZE
];
1001 char line
[BUFFERSIZE
];
1003 char *tok
, *p
, *end
;
1006 misdn_cfg_get(0, MISDN_GEN_MISDN_INIT
, &misdn_init
, sizeof(misdn_init
));
1008 if (!ast_strlen_zero(misdn_init
)) {
1009 fp
= fopen(misdn_init
, "r");
1011 while(fgets(line
, sizeof(line
), fp
)) {
1012 if (!strncmp(line
, "nt_ptp", 6)) {
1013 for (tok
= strtok_r(line
,",=", &p
);
1015 tok
= strtok_r(NULL
,",=", &p
)) {
1016 port
= strtol(tok
, &end
, 10);
1017 if (end
!= tok
&& misdn_cfg_is_port_valid(port
)) {
1027 ast_log(LOG_WARNING
,"Couldn't open %s: %s\n", misdn_init
, strerror(errno
));
1036 for (i
= 1; i
<= max_ports
; ++i
) {
1037 snprintf(filename
, sizeof(filename
), "/sys/class/mISDN-stacks/st-%08x/protocol", i
<< 8);
1038 fp
= fopen(filename
, "r");
1040 ast_log(LOG_WARNING
, "Could not open %s: %s\n", filename
, strerror(errno
));
1043 if (fscanf(fp
, "0x%08x", &proto
) != 1)
1044 ast_log(LOG_WARNING
, "Could not parse contents of %s!\n", filename
);
1046 ptp
[i
] = proto
& 1<<5 ? 1 : 0;
1052 static void _fill_defaults (void)
1056 for (i
= 0; i
< NUM_PORT_ELEMENTS
; ++i
) {
1057 if (!port_cfg
[0][i
].any
&& strcasecmp(port_spec
[i
].def
, NO_DEFAULT
))
1058 _parse(&(port_cfg
[0][i
]), (char *)port_spec
[i
].def
, port_spec
[i
].type
, port_spec
[i
].boolint_def
);
1060 for (i
= 0; i
< NUM_GEN_ELEMENTS
; ++i
) {
1061 if (!general_cfg
[i
].any
&& strcasecmp(gen_spec
[i
].def
, NO_DEFAULT
))
1062 _parse(&(general_cfg
[i
]), (char *)gen_spec
[i
].def
, gen_spec
[i
].type
, gen_spec
[i
].boolint_def
);
1066 void misdn_cfg_reload (void)
1071 void misdn_cfg_destroy (void)
1076 _free_general_cfg();
1084 ast_mutex_destroy(&config_mutex
);
1087 int misdn_cfg_init (int this_max_ports
)
1089 char config
[] = "misdn.conf";
1092 struct ast_config
*cfg
;
1093 struct ast_variable
*v
;
1095 if (!(cfg
= AST_LOAD_CFG(config
))) {
1096 ast_log(LOG_WARNING
, "missing file: misdn.conf\n");
1100 ast_mutex_init(&config_mutex
);
1104 if (this_max_ports
) {
1105 /* this is the first run */
1106 max_ports
= this_max_ports
;
1107 map
= (int *)calloc(MISDN_GEN_LAST
+ 1, sizeof(int));
1108 if (_enum_array_map())
1110 p
= (char *)calloc(1, (max_ports
+ 1) * sizeof(union misdn_cfg_pt
*)
1111 + (max_ports
+ 1) * NUM_PORT_ELEMENTS
* sizeof(union misdn_cfg_pt
));
1112 port_cfg
= (union misdn_cfg_pt
**)p
;
1113 p
+= (max_ports
+ 1) * sizeof(union misdn_cfg_pt
*);
1114 for (i
= 0; i
<= max_ports
; ++i
) {
1115 port_cfg
[i
] = (union misdn_cfg_pt
*)p
;
1116 p
+= NUM_PORT_ELEMENTS
* sizeof(union misdn_cfg_pt
);
1118 general_cfg
= (union misdn_cfg_pt
*)calloc(1, sizeof(union misdn_cfg_pt
*) * NUM_GEN_ELEMENTS
);
1119 ptp
= (int *)calloc(max_ports
+ 1, sizeof(int));
1124 _free_general_cfg();
1125 memset(port_cfg
[0], 0, NUM_PORT_ELEMENTS
* sizeof(union misdn_cfg_pt
) * (max_ports
+ 1));
1126 memset(general_cfg
, 0, sizeof(union misdn_cfg_pt
*) * NUM_GEN_ELEMENTS
);
1127 memset(ptp
, 0, sizeof(int) * (max_ports
+ 1));
1130 cat
= ast_category_browse(cfg
, NULL
);
1133 v
= ast_variable_browse(cfg
, cat
);
1134 if (!strcasecmp(cat
, "general")) {
1135 _build_general_config(v
);
1137 _build_port_config(v
, cat
);
1139 cat
= ast_category_browse(cfg
, cat
);
1145 AST_DESTROY_CFG(cfg
);