Skip to content

Commit 02b6213

Browse files
committed
res_telos_1a2: Add workaround optimized for local FXS stations.
For lines served by analog Digium cards, regardless of the technology behind the FXO port used, the fxodevice option (formerly just 'device') does not work. This seems to be due to the high resistance of the line, which prevents audio from passing through from an FXO port to the line. In reality, the whole premise of using an FXO port the way we are is completely non-standard and outside the scope of what should work, so it's not surprising this can be undefined or varying behavior. However, if the line itself is served by the system, as is likely in the case of a Digium analog card, we don't need to use an FXO port in order to bridge in music on hold; we can simply have Asterisk generate it towards the caller. That is what the new 'fxsdevice' option does; it queues hold on the channel along with the configured music on hold class. This also results in better quality, since it eliminates an additional analog connection and the volume is normal loudness. UpgradeNote: The 'device' option has been renamed to 'fxodevice', and this change is not backwards-compatible. The change was made because, with the addition of the 'fxsdevice' option, having an option simply named 'device' is somewhat ambiguous. Additionally, the serial device no longer has a default, which ensures the module does not load if it is not needed by the user.
1 parent 10df4e7 commit 02b6213

File tree

2 files changed

+135
-32
lines changed

2 files changed

+135
-32
lines changed

configs/samples/res_telos_1a2.conf.sample

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
; Configuration file for Asterisk resource module for Telos 1A2 Module Interface
33

44
;[general]
5-
;device = /dev/ttyS0 ; Serial device to use (at 1200 baud). Default is /dev/ttyS0
5+
;device = /dev/ttyS0 ; Serial device to use (at 1200 baud). There is no default (however, on most systems, the default serial port is /dev/ttyS0)
66

77
;[line1]
88
;line = 1 ; 1A2 line number
9-
;device = DAHDI/1 ; FXO device that can be used to access this line
9+
;fxodevice = DAHDI/1 ; FXO device that can be used to access this line. The line does not need to be served by Asterisk. Music on hold volume is rather low by default with this option.
1010
;moh_class = default ; Music on hold class, to play music on hold when this 1A2 line is on hold (via the FXO device). There is no default.
1111
;hold_context = 1a2hold ; Dialplan context to execute for 1A2 lines on hold. Extension s will be executed at priority 1. If moh_class is also specified, hold_context takes precedence.
12+
; hold_context can only be used with 'fxodevice', not 'fxsdevice'.
1213

1314
;[line2]
1415
;line = 2
15-
;device = DAHDI/2
16+
;fxsdevice = DAHDI/22 ; FXS device that services this line. Music on hold volume and quality is better using fxsdevice as compared to fxodevice; however, the line must be on this system.
1617
;moh_class = default

res/res_telos_1a2.c

Lines changed: 131 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,36 @@
6363
<para>Line number of 1A2 channel (e.g. 1, 2, 3, 4, etc.)</para>
6464
</description>
6565
</configOption>
66-
<configOption name="device">
67-
<synopsis>Asterisk device</synopsis>
66+
<configOption name="fxodevice">
67+
<synopsis>Asterisk FXO device</synopsis>
6868
<description>
6969
<para>Asterisk FXO device corresponding to this 1A2 line.</para>
7070
<para>Music on hold will be streamed onto the line using this FXO port.</para>
71+
<para>This does not require that the FXS line be served by this Asterisk system,
72+
but it may not work with all equipment. If you do not hear music on hold when
73+
putting a call on hold from the 1A2 system, the resistance may be too high
74+
for audio to pass. If the line is served by Asterisk, you can use
75+
<literal>fxsdevice</literal> instead.</para>
76+
<para>Music on hold provided using this option will generally have
77+
very low volume, due to the high resistance of the 1A2 line on hold.
78+
For better quality, use <literal>fxsdevice</literal> if the line is
79+
served by Asterisk.</para>
80+
</description>
81+
</configOption>
82+
<configOption name="fxsdevice">
83+
<synopsis>Asterisk FXS device</synopsis>
84+
<description>
85+
<para>Asterisk FXS device corresponding to this 1A2 line. This is specified
86+
to indicate that this 1A2 line obtains dial tone from this FXS device.</para>
87+
<para>If provided, music on hold will be queued on the active channel
88+
of this device, rather than using the FXO device. This may be necessary
89+
if simply specifying the FXO <literal>device</literal> does not work;
90+
for instance, on Digium analog cards, the resistance may be too high for
91+
music on hold audio to pass while a line is on hold by the 1A2 system.
92+
This option furnishes music on hold directly on the Asterisk channel,
93+
thus resulting in better quality and compatibility. However, this option
94+
only works for lines served by the specified Asterisk FXS device.</para>
95+
<para>Currently, only DAHDI devices are supported for this setting.</para>
7196
</description>
7297
</configOption>
7398
<configOption name="moh_class">
@@ -159,12 +184,14 @@ enum line_state {
159184

160185
struct telos_line {
161186
int lineno; /*!< Line number on telos system */
162-
const char *device; /*!< Asterisk device */
187+
const char *fxodevice; /*!< Asterisk FXO device */
188+
const char *fxsdevice; /*!< Asterisk FXS device */
163189
const char *moh_class; /*!< Music on hold class */
164190
const char *hold_context; /*!< Hold dialplan context */
165191
struct ast_channel *moh_chan; /*!< Music on hold channel */
166192
enum line_state state; /*!< Current line state */
167193
AST_LIST_ENTRY(telos_line) entry; /*!< Next channel */
194+
char fxs_activechan[80]; /*!< Channel on which hold was queued */
168195
char data[];
169196
};
170197

@@ -175,7 +202,7 @@ static int thread_running = 0;
175202

176203
#define MAX_POLL_DELAY 2000
177204

178-
static char serial_device[256] = "/dev/ttyS0"; /* Default serial port on a Linux system (especially if there's only one) */
205+
static char serial_device[256] = "";
179206
static int serial_fd = -1;
180207
static int query_success = 0;
181208
static int unloading = 0;
@@ -276,6 +303,15 @@ static void cleanup_moh_chan(struct ast_channel *chan)
276303
ast_verb(5, "Stopping music on hold for 1A2 line %d\n", t->lineno); \
277304
cleanup_moh_chan(t->moh_chan); \
278305
t->moh_chan = NULL; \
306+
} else if (t->fxs_activechan[0]) { \
307+
struct ast_channel *ochan = ast_channel_get_by_name(t->fxs_activechan); \
308+
if (ochan) { \
309+
ast_queue_unhold(ochan); \
310+
ast_channel_unref(ochan); \
311+
} else { \
312+
ast_debug(2, "Channel %s doesn't exist anymore\n", t->fxs_activechan); \
313+
} \
314+
t->fxs_activechan[0] = '\0'; \
279315
}
280316

281317
static int start_moh(struct telos_line *t)
@@ -290,7 +326,40 @@ static int start_moh(struct telos_line *t)
290326
}
291327

292328
/* We need an FXO device (though not necessarily a DAHDI device) for music on hold */
293-
if (ast_strlen_zero(t->device)) {
329+
if (!ast_strlen_zero(t->fxsdevice)) {
330+
if (!strncasecmp(t->fxsdevice, "DAHDI/", 6)) {
331+
struct ast_channel *chan;
332+
char active_channel[80] = ""; /* AST_MAX_CHANNEL */
333+
char func_args[64];
334+
int dahdichan = atoi(t->fxsdevice + 6);
335+
/* We need to obtain the Asterisk channel that is currently in the "foreground"
336+
* on this DAHDI channel - that would be the owner.
337+
* This is first and foremost because we need an Asterisk channel onto which to queue MOH,
338+
* not just a DAHDI channel, but also so we can stop MOH on the right channel
339+
* if the subchannels switch around while the 1A2 line is on hold. */
340+
snprintf(func_args, sizeof(func_args), "DAHDI_CHANNEL(owner,%d)", dahdichan);
341+
if (ast_func_read(NULL, func_args, active_channel, sizeof(active_channel))) {
342+
ast_log(LOG_WARNING, "Couldn't obtain active Asterisk channel on DAHDI channel %d\n", dahdichan);
343+
return -1;
344+
}
345+
chan = ast_channel_get_by_name(active_channel);
346+
if (chan) {
347+
/* Indicate music on hold by queuing a HOLD on the active channel for this device */
348+
ast_verb(5, "Queued hold on %s using class '%s'\n", active_channel, t->moh_class);
349+
ast_queue_hold(chan, t->moh_class);
350+
ast_channel_unref(chan);
351+
ast_copy_string(t->fxs_activechan, active_channel, sizeof(t->fxs_activechan));
352+
return 0;
353+
} else {
354+
ast_log(LOG_ERROR, "Couldn't find channel %s\n", active_channel);
355+
return -1;
356+
}
357+
} else {
358+
ast_log(LOG_ERROR, "Only DAHDI channels are supported for fxsdevice\n");
359+
return -1;
360+
}
361+
}
362+
if (ast_strlen_zero(t->fxodevice)) {
294363
ast_log(LOG_ERROR, "Music on hold requested for line %d, but no FXO device specified\n", t->lineno);
295364
return -1;
296365
}
@@ -300,10 +369,10 @@ static int start_moh(struct telos_line *t)
300369
STOP_MOH(t); /* If we already have one, for some reason, get rid of it */
301370
}
302371

303-
chandata = ast_strdupa(t->device);
372+
chandata = ast_strdupa(t->fxodevice);
304373
chantech = strsep(&chandata, "/");
305374
if (!chandata) {
306-
ast_log(LOG_ERROR, "No data provided after channel type (%s)\n", t->device);
375+
ast_log(LOG_ERROR, "No data provided after channel type (%s)\n", t->fxodevice);
307376
return -1;
308377
}
309378

@@ -342,7 +411,7 @@ static int start_moh(struct telos_line *t)
342411
desc = "Unknown";
343412
break;
344413
}
345-
ast_log(LOG_WARNING, "Couldn't dial %s (%s)\n", t->device, desc);
414+
ast_log(LOG_WARNING, "Couldn't dial %s (%s)\n", t->fxodevice, desc);
346415
} else {
347416
ast_channel_unlock(t->moh_chan); /* Was returned locked and reference bumped */
348417
ast_verb(5, "%s (%s) for 1A2 line %d\n", !ast_strlen_zero(t->hold_context) ? "Launched hold dialplan" : "Started music on hold", S_OR(t->hold_context, t->moh_class), t->lineno);
@@ -707,10 +776,11 @@ static char *handle_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_
707776

708777
ast_cli(a->fd, "Serial device is %s (fd %d)\n", serial_device, serial_fd);
709778
ast_cli(a->fd, "Serial status is %s\n", thread_running ? query_success ? "RUNNING" : "STALLED" : "ABORTED");
710-
ast_cli(a->fd, "%4s %-8s %17s %20s %s\n", "Line", "State", "Associated Device", "MOH Class", "Hold Context");
779+
ast_cli(a->fd, "%4s %-8s %17s %17s %20s %s\n", "Line", "State", "FXO Device", "FXS Device/Chan", "MOH Class", "Hold Context");
711780
AST_RWLIST_RDLOCK(&lines);
712781
AST_RWLIST_TRAVERSE(&lines, t, entry) {
713-
ast_cli(a->fd, "%4d %-8s %17s %20s %s\n", t->lineno, state_str_cli(t->state), S_OR(t->device, ""), S_OR(t->moh_class, ""), S_OR(t->hold_context, ""));
782+
const char *fxschannel = S_OR(t->fxs_activechan, S_OR(t->fxsdevice, "")); /* If we have an active FXS channel, use that as it's more specific, otherwise just the device itself */
783+
ast_cli(a->fd, "%4d %-8s %17s %17s %20s %s\n", t->lineno, state_str_cli(t->state), S_OR(t->fxodevice, ""), fxschannel, S_OR(t->moh_class, ""), S_OR(t->hold_context, ""));
714784
}
715785
AST_RWLIST_UNLOCK(&lines);
716786

@@ -782,16 +852,22 @@ static int load_config(void)
782852
} else { /* it's a line definition */
783853
struct telos_line *t;
784854
char *data;
785-
const char *device = NULL, *moh_class = NULL, *hold_context = NULL;
855+
const char *fxodevice = NULL, *fxsdevice = NULL, *moh_class = NULL, *hold_context = NULL;
786856
for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
787857
if (!strcasecmp(var->name, "line")) {
788858
if (ast_str_to_int(var->value, &tmp)) {
789859
ast_log(LOG_WARNING, "Invalid line number '%s'\n", var->value);
790860
ast_config_destroy(cfg);
791861
return -1;
792862
}
793-
} else if (!strcasecmp(var->name, "device")) {
794-
device = var->value;
863+
} else if (!strcasecmp(var->name, "fxodevice")) {
864+
fxodevice = var->value;
865+
} else if (!strcasecmp(var->name, "fxsdevice")) {
866+
if (strncasecmp(var->value, "DAHDI/", 6)) {
867+
ast_log(LOG_WARNING, "Only DAHDI devices are supported for fxsdevice\n");
868+
continue;
869+
}
870+
fxsdevice = var->value;
795871
} else if (!strcasecmp(var->name, "moh_class")) {
796872
moh_class = var->value;
797873
} else if (!strcasecmp(var->name, "hold_context")) {
@@ -806,39 +882,71 @@ static int load_config(void)
806882
return -1;
807883
}
808884
}
809-
t = ast_calloc(1, sizeof(*t) + (device ? strlen(device) + 1 : 0) + (moh_class ? strlen(moh_class) + 1 : 0) + (hold_context ? strlen(hold_context) + 1 : 0));
885+
t = ast_calloc(1, sizeof(*t) + (fxodevice ? strlen(fxodevice) + 1 : 0) + (fxsdevice ? strlen(fxsdevice) + 1 : 0)
886+
+ (moh_class ? strlen(moh_class) + 1 : 0) + (hold_context ? strlen(hold_context) + 1 : 0));
810887
if (!t) {
811888
ast_config_destroy(cfg);
812889
return -1;
813890
}
814891
t->lineno = tmp;
815892
data = t->data;
816-
if (!ast_strlen_zero(device)) {
817-
strcpy(data, device); /* Safe */
818-
t->device = data;
819-
data += strlen(device) + 1;
893+
if (!ast_strlen_zero(fxodevice)) {
894+
strcpy(data, fxodevice); /* Safe */
895+
t->fxodevice = data;
896+
data += strlen(fxodevice) + 1;
897+
}
898+
if (!ast_strlen_zero(fxsdevice)) {
899+
strcpy(data, fxsdevice); /* Safe */
900+
t->fxsdevice = data;
901+
data += strlen(fxsdevice) + 1;
820902
}
821903
if (!ast_strlen_zero(moh_class)) {
822-
strcpy(data, moh_class);
904+
strcpy(data, moh_class); /* Safe */
823905
t->moh_class = data;
824906
data += strlen(moh_class) + 1;
825907
}
826908
if (!ast_strlen_zero(hold_context)) {
827-
strcpy(data, hold_context);
909+
strcpy(data, hold_context); /* Safe */
828910
t->hold_context = data;
829911
}
912+
if (ast_strlen_zero(fxodevice) && !ast_strlen_zero(hold_context)) {
913+
ast_log(LOG_ERROR, "The 'hold_context' setting requires 'fxodevice' to be set\n");
914+
}
915+
if (!ast_strlen_zero(fxsdevice) && ast_strlen_zero(moh_class)) {
916+
ast_log(LOG_WARNING, "The 'fxsdevice' setting requires 'moh_class' to be set to customize music on hold\n");
917+
}
830918
AST_RWLIST_INSERT_TAIL(&lines, t, entry);
831919
}
832920
}
833921

834922
ast_config_destroy(cfg);
923+
924+
/* This option intentionally has no default,
925+
* so that if the stock config file has not been modified,
926+
* we assume this module isn't needed and decline to load,
927+
* since we can't do anything useful without the associated equipment. */
928+
if (ast_strlen_zero(serial_device)) {
929+
ast_log(LOG_NOTICE, "No serial device specified, declining to load\n");
930+
return -1;
931+
}
932+
835933
return 0;
836934
}
837935

838-
static int unload_module(void)
936+
static void free_lines(void)
839937
{
840938
struct telos_line *t;
841939

940+
AST_RWLIST_WRLOCK(&lines);
941+
while ((t = AST_RWLIST_REMOVE_HEAD(&lines, entry))) {
942+
STOP_MOH(t);
943+
ast_free(t);
944+
}
945+
AST_RWLIST_UNLOCK(&lines);
946+
}
947+
948+
static int unload_module(void)
949+
{
842950
unloading = 1;
843951
ast_cli_unregister_multiple(telos_cli, ARRAY_LEN(telos_cli));
844952
ast_custom_function_unregister(&acf_telos);
@@ -857,13 +965,7 @@ static int unload_module(void)
857965
serial_fd = -1;
858966
}
859967

860-
AST_RWLIST_WRLOCK(&lines);
861-
while ((t = AST_RWLIST_REMOVE_HEAD(&lines, entry))) {
862-
STOP_MOH(t);
863-
ast_free(t);
864-
}
865-
AST_RWLIST_UNLOCK(&lines);
866-
968+
free_lines();
867969
return 0;
868970
}
869971

@@ -873,7 +975,7 @@ static int load_module(void)
873975
struct termios attr;
874976

875977
if (load_config()) {
876-
unload_module();
978+
free_lines();
877979
return AST_MODULE_LOAD_DECLINE;
878980
}
879981

0 commit comments

Comments
 (0)