freetdm: Added new OOB event FTDM_OOB_POLARITY_REVERSE

Added new channel command FTDM_COMMAND_SET_POLARITY
         ftmod_wanpipe - Added support to enqueue polarity events on FXO lines
	 ftmod_wanpipe - Added support to set polarity on FXS lines
	 ftmod_analog - Added support to answer and hangup FXO lines on polarity reverse
	 ftmod_analog - Added support to reverse polarity in the FXS line on answer and hangup
	 mod_freetdm - Added parameters answer-on-polarity, hangup-on-polarity and polarity-delay
                       to enable those analog features
This commit is contained in:
Moises Silva 2010-12-21 15:11:22 -05:00
parent 092d22a214
commit cdfa8bf7ae
9 changed files with 277 additions and 35 deletions

View File

@ -12,31 +12,81 @@ with the signaling protocols that you can run on top of your I/O interfaces.
<settings> <settings>
<param name="debug" value="0"/> <param name="debug" value="0"/>
<!--<param name="hold-music" value="$${moh_uri}"/>--> <!--<param name="hold-music" value="$${moh_uri}"/>-->
<!-- Analog global options (they apply to all spans)
Remember you can only choose between either call-swap
or 3-way, not both!
-->
<!--<param name="enable-analog-option" value="call-swap"/>--> <!--<param name="enable-analog-option" value="call-swap"/>-->
<!--<param name="enable-analog-option" value="3-way"/>--> <!--<param name="enable-analog-option" value="3-way"/>-->
</settings> </settings>
<!-- Sample analog configuration --> <!-- Sample analog configuration (The analog_spans tag is for ftmod_analog) -->
<analog_spans> <analog_spans>
<!-- The span name must match the name in your freetdm.conf --> <!-- The span name must match the name in your freetdm.conf -->
<span name="myAnalog"> <span name="myAnalog">
<!--<param name="hold-music" value="$${moh_uri}"/>--> <!--<param name="hold-music" value="$${moh_uri}"/>-->
<!--<param name="enable-analog-option" value="call-swap"/>--> <!--
<!--<param name="enable-analog-option" value="3-way"/>--> 3-way allows you to flash your FXS line and dial
another number and put all the parties in a conference
call-swap allows you to flash your FXS line and swap
between one call and another
Remember you can only choose between either call-swap
or 3-way, not both!
<param name="enable-analog-option" value="call-swap"/>
<param name="enable-analog-option" value="3-way"/>
-->
<!-- Tones are defined in tones.conf
This setting is very important for analog lines to
work properly
-->
<param name="tonegroup" value="us"/> <param name="tonegroup" value="us"/>
<!-- How much time to wait for digits (in FXS lines) -->
<param name="digit-timeout" value="2000"/> <param name="digit-timeout" value="2000"/>
<!-- Maximum number of digits to wait for (in FXS lines) -->
<param name="max-digits" value="11"/> <param name="max-digits" value="11"/>
<param name="dialplan" value="XML"/>
<param name="context" value="default"/> <!-- whether you want to wait for caller id -->
<param name="enable-callerid" value="true"/> <param name="enable-callerid" value="true"/>
<!-- whether you want to enable callwaiting feature -->
<!--<param name="callwaiting" value="true"/>-->
<!-- whether you want to answer/hangup on polarity reverse for outgoing calls in FXO devices
and send polarity reverse on answer/hangup for incoming calls in FXS devices -->
<!--<param name="answer-polarity-reverse" value="false"/>-->
<!--<param name="hangup-polarity-reverse" value="false"/>-->
<!--
Minimum delay (in milliseconds) required between an answer polarity reverse
and hangup polarity reverse in order to assume the second polarity reverse is a real hangup
<param name="polarity-delay" value="600"/>
-->
<!-- regex to stop dialing when it matches --> <!-- regex to stop dialing when it matches -->
<!--<param name="dial-regex" value="5555"/>--> <!--<param name="dial-regex" value="5555"/>-->
<!-- regex to stop dialing when it does not match --> <!-- regex to stop dialing when it does not match -->
<!--<param name="fail-dial-regex" value="^5"/>--> <!--<param name="fail-dial-regex" value="^5"/>-->
<!-- FreeSWITCH dialplan type and context to send the calls -->
<param name="dialplan" value="XML"/>
<param name="context" value="default"/>
</span> </span>
</analog_spans> </analog_spans>
<!-- openr2 (MFC-R2 signaling) spans <!--
openr2 (MFC-R2 signaling) spans (ftmod_r2)
In order to use this type of spans your FreeTDM must have been compiled with ftmod_r2 module.
The module is compiled if the openr2 library is present when running the ./configure script
in the FreeTDM source code
MFC-R2 signaling has lots of variants from country to country and even sometimes MFC-R2 signaling has lots of variants from country to country and even sometimes
minor variants inside the same country. The only mandatory parameters here are: minor variants inside the same country. The only mandatory parameters here are:
variant, but typically you also want to set max_ani and max_dnis. variant, but typically you also want to set max_ani and max_dnis.
@ -46,6 +96,7 @@ with the signaling protocols that you can run on top of your I/O interfaces.
best defaults for your country. If you want to contribute your configs for a particular best defaults for your country. If you want to contribute your configs for a particular
country send them to the e-mail of the primary OpenR2 developer that you can find in the country send them to the e-mail of the primary OpenR2 developer that you can find in the
AUTHORS file of the OpenR2 package, they will be added to the samples directory of openr2. AUTHORS file of the OpenR2 package, they will be added to the samples directory of openr2.
--> -->
<r2_spans> <r2_spans>
<span name="wp1" cfgprofile="testr2"> <span name="wp1" cfgprofile="testr2">

View File

@ -2717,6 +2717,9 @@ static switch_status_t load_config(void)
char *hold_music = NULL; char *hold_music = NULL;
char *fail_dial_regex = NULL; char *fail_dial_regex = NULL;
const char *enable_callerid = "true"; const char *enable_callerid = "true";
const char *answer_polarity = "false";
const char *hangup_polarity = "false";
int polarity_delay = 600;
int callwaiting = 1; int callwaiting = 1;
uint32_t span_id = 0, to = 0, max = 0; uint32_t span_id = 0, to = 0, max = 0;
@ -2788,6 +2791,12 @@ static switch_status_t load_config(void)
dial_regex = val; dial_regex = val;
} else if (!strcasecmp(var, "enable-callerid")) { } else if (!strcasecmp(var, "enable-callerid")) {
enable_callerid = val; enable_callerid = val;
} else if (!strcasecmp(var, "answer-polarity-reverse")) {
answer_polarity = val;
} else if (!strcasecmp(var, "hangup-polarity-reverse")) {
hangup_polarity = val;
} else if (!strcasecmp(var, "polarity-delay")) {
polarity_delay = atoi(val);
} else if (!strcasecmp(var, "fail-dial-regex")) { } else if (!strcasecmp(var, "fail-dial-regex")) {
fail_dial_regex = val; fail_dial_regex = val;
} else if (!strcasecmp(var, "hold-music")) { } else if (!strcasecmp(var, "hold-music")) {
@ -2848,6 +2857,9 @@ static switch_status_t load_config(void)
"max_dialstr", &max, "max_dialstr", &max,
"hotline", hotline ? hotline : "", "hotline", hotline ? hotline : "",
"enable_callerid", enable_callerid, "enable_callerid", enable_callerid,
"answer_polarity_reverse", answer_polarity,
"hangup_polarity_reverse", hangup_polarity,
"polarity_delay", &polarity_delay,
"callwaiting", &callwaiting, "callwaiting", &callwaiting,
FTDM_TAG_END) != FTDM_SUCCESS) { FTDM_TAG_END) != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span)); ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span));

View File

@ -1541,6 +1541,7 @@ end:
ftdm_mutex_unlock(ftdmchan->span->mutex); ftdm_mutex_unlock(ftdmchan->span->mutex);
} else { } else {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
goto done;
} }
/* there is an inherent race here between set and check of the change flag but we do not care because /* there is an inherent race here between set and check of the change flag but we do not care because
@ -1570,7 +1571,7 @@ end:
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n",
ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
} }
done:
return ok ? FTDM_SUCCESS : FTDM_FAIL; return ok ? FTDM_SUCCESS : FTDM_FAIL;
} }
@ -2289,8 +2290,15 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c
ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1); ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1);
} else { } else {
/* the signaling stack did not touch the state, /* the signaling stack did not touch the state,
* core is responsible from clearing flags and stuff */ * core is responsible from clearing flags and stuff, however, because ftmod_analog
ftdm_channel_close(&chan); * is a bitch in a serious need of refactoring, we also check whether the channel is open
* to avoid an spurious warning about the channel not being open. This is because ftmod_analog
* does not follow our convention of sending SIGEVENT_STOP and waiting for the user to move
* to HANGUP (implicitly through ftdm_channel_call_hangup(), as soon as ftmod_analog is fixed
* this check can be removed */
if (ftdm_test_flag(chan, FTDM_CHANNEL_OPEN)) {
ftdm_channel_close(&chan);
}
} }
return FTDM_SUCCESS; return FTDM_SUCCESS;
} }
@ -3933,7 +3941,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *dat
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "cannot write in channel not open\n"); ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot write in channel not open\n");
snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open"); snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open");
status = FTDM_FAIL; status = FTDM_FAIL;
goto done; goto done;

View File

@ -37,7 +37,9 @@
typedef enum { typedef enum {
FTDM_ANALOG_RUNNING = (1 << 0), FTDM_ANALOG_RUNNING = (1 << 0),
FTDM_ANALOG_CALLERID = (1 << 1) FTDM_ANALOG_CALLERID = (1 << 1),
FTDM_ANALOG_ANSWER_POLARITY_REVERSE = (1 << 2),
FTDM_ANALOG_HANGUP_POLARITY_REVERSE = (1 << 3)
} ftdm_analog_flag_t; } ftdm_analog_flag_t;
#define FTDM_MAX_HOTLINE_STR 20 #define FTDM_MAX_HOTLINE_STR 20
@ -47,11 +49,13 @@ struct ftdm_analog_data {
uint32_t flags; uint32_t flags;
uint32_t max_dialstr; uint32_t max_dialstr;
uint32_t wait_dialtone_timeout; uint32_t wait_dialtone_timeout;
uint32_t polarity_delay;
uint32_t digit_timeout; uint32_t digit_timeout;
char hotline[FTDM_MAX_HOTLINE_STR]; char hotline[FTDM_MAX_HOTLINE_STR];
}; };
/* Analog flags to be set in the sflags (signaling flags) channel memeber */
#define AF_POLARITY_REVERSE (1 << 0)
static void *ftdm_analog_run(ftdm_thread_t *me, void *obj); static void *ftdm_analog_run(ftdm_thread_t *me, void *obj);
typedef struct ftdm_analog_data ftdm_analog_data_t; typedef struct ftdm_analog_data ftdm_analog_data_t;

View File

@ -184,6 +184,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span)
uint32_t digit_timeout = 10; uint32_t digit_timeout = 10;
uint32_t wait_dialtone_timeout = 30000; uint32_t wait_dialtone_timeout = 30000;
uint32_t max_dialstr = MAX_DTMF; uint32_t max_dialstr = MAX_DTMF;
uint32_t polarity_delay = 600;
const char *var, *val; const char *var, *val;
int *intval; int *intval;
uint32_t flags = FTDM_ANALOG_CALLERID; uint32_t flags = FTDM_ANALOG_CALLERID;
@ -236,6 +237,29 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span)
} else { } else {
flags &= ~FTDM_ANALOG_CALLERID; flags &= ~FTDM_ANALOG_CALLERID;
} }
} else if (!strcasecmp(var, "answer_polarity_reverse")) {
if (!(val = va_arg(ap, char *))) {
break;
}
if (ftdm_true(val)) {
flags |= FTDM_ANALOG_ANSWER_POLARITY_REVERSE;
} else {
flags &= ~FTDM_ANALOG_ANSWER_POLARITY_REVERSE;
}
} else if (!strcasecmp(var, "hangup_polarity_reverse")) {
if (!(val = va_arg(ap, char *))) {
break;
}
if (ftdm_true(val)) {
flags |= FTDM_ANALOG_HANGUP_POLARITY_REVERSE;
} else {
flags &= ~FTDM_ANALOG_HANGUP_POLARITY_REVERSE;
}
} else if (!strcasecmp(var, "polarity_delay")) {
if (!(intval = va_arg(ap, int *))) {
break;
}
polarity_delay = *intval;
} else if (!strcasecmp(var, "callwaiting")) { } else if (!strcasecmp(var, "callwaiting")) {
if (!(intval = va_arg(ap, int *))) { if (!(intval = va_arg(ap, int *))) {
break; break;
@ -276,6 +300,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span)
analog_data->flags = flags; analog_data->flags = flags;
analog_data->digit_timeout = digit_timeout; analog_data->digit_timeout = digit_timeout;
analog_data->wait_dialtone_timeout = wait_dialtone_timeout; analog_data->wait_dialtone_timeout = wait_dialtone_timeout;
analog_data->polarity_delay = polarity_delay;
analog_data->max_dialstr = max_dialstr; analog_data->max_dialstr = max_dialstr;
span->signal_cb = sig_cb; span->signal_cb = sig_cb;
strncpy(analog_data->hotline, hotline, sizeof(analog_data->hotline)); strncpy(analog_data->hotline, hotline, sizeof(analog_data->hotline));
@ -399,6 +424,7 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
ftdm_analog_data_t *analog_data = ftdmchan->span->signal_data; ftdm_analog_data_t *analog_data = ftdmchan->span->signal_data;
ftdm_channel_t *closed_chan; ftdm_channel_t *closed_chan;
uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = analog_data->wait_dialtone_timeout; uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = analog_data->wait_dialtone_timeout;
uint32_t answer_on_polarity_counter = 0;
ftdm_sigmsg_t sig; ftdm_sigmsg_t sig;
ftdm_status_t status; ftdm_status_t status;
@ -470,7 +496,12 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
if (ftdmchan->needed_tones[FTDM_TONEMAP_DIAL]) { if (ftdmchan->needed_tones[FTDM_TONEMAP_DIAL]) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY);
} else { } else {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); /* do not go up if we're waiting for polarity reversal */
if (ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE)) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
} else {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP);
}
} }
} }
} }
@ -561,8 +592,30 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
} }
case FTDM_CHANNEL_STATE_UP: case FTDM_CHANNEL_STATE_UP:
case FTDM_CHANNEL_STATE_RING: case FTDM_CHANNEL_STATE_RING:
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
{ {
ftdm_sleep(interval); if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) &&
ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA &&
ftdm_test_sflag(ftdmchan, AF_POLARITY_REVERSE)) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_NOTICE, "Answering on polarity reverse\n");
ftdm_clear_sflag(ftdmchan, AF_POLARITY_REVERSE);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP);
answer_on_polarity_counter = state_counter;
} else if (ftdmchan->state == FTDM_CHANNEL_STATE_UP
&& ftdm_test_sflag(ftdmchan, AF_POLARITY_REVERSE)){
/* if this polarity reverse is close to the answer polarity reverse, ignore it */
if (answer_on_polarity_counter
&& (state_counter - answer_on_polarity_counter) > analog_data->polarity_delay) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_NOTICE, "Hanging up on polarity reverse\n");
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING,
"Not hanging up on polarity reverse, too close to Answer reverse\n");
}
ftdm_clear_sflag(ftdmchan, AF_POLARITY_REVERSE);
} else {
ftdm_sleep(interval);
}
continue; continue;
} }
break; break;
@ -615,6 +668,18 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
sig.event_id = FTDM_SIGEVENT_UP; sig.event_id = FTDM_SIGEVENT_UP;
} }
if (ftdmchan->type == FTDM_CHAN_TYPE_FXS &&
!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) &&
ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE)) {
ftdm_polarity_t polarity = FTDM_POLARITY_REVERSE;
if (ftdmchan->polarity != FTDM_POLARITY_FORWARD) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Polarity is already reversed on answer??\n");
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Reversing polarity on answer\n");
ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_POLARITY, &polarity);
}
}
ftdm_span_send_signal(ftdmchan->span, &sig); ftdm_span_send_signal(ftdmchan->span, &sig);
continue; continue;
} }
@ -639,6 +704,22 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
continue; continue;
} }
break; break;
case FTDM_CHANNEL_STATE_HANGUP:
/* this state is only used when the user hangup, if the device hang up (onhook) we currently
* go straight to DOWN. If we ever change this (as other signaling modules do) by using this
* state for both user and device hangup, we should check here for the type of hangup since
* some actions (polarity reverse) do not make sense if the device hung up */
if (ftdmchan->type == FTDM_CHAN_TYPE_FXS &&
ftdmchan->last_state == FTDM_CHANNEL_STATE_UP &&
ftdm_test_flag(analog_data, FTDM_ANALOG_HANGUP_POLARITY_REVERSE)) {
ftdm_polarity_t polarity = ftdmchan->polarity == FTDM_POLARITY_REVERSE
? FTDM_POLARITY_FORWARD : FTDM_POLARITY_REVERSE;
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Reversing polarity on hangup\n");
ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_POLARITY, &polarity);
}
break;
case FTDM_CHANNEL_STATE_DOWN: case FTDM_CHANNEL_STATE_DOWN:
{ {
sig.event_id = FTDM_SIGEVENT_STOP; sig.event_id = FTDM_SIGEVENT_STOP;
@ -847,6 +928,9 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
done: done:
closed_chan = ftdmchan;
ftdm_channel_lock(closed_chan);
if (ftdmchan->type == FTDM_CHAN_TYPE_FXO && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK)) { if (ftdmchan->type == FTDM_CHAN_TYPE_FXO && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK)) {
ftdm_channel_command(ftdmchan, FTDM_COMMAND_ONHOOK, NULL); ftdm_channel_command(ftdmchan, FTDM_COMMAND_ONHOOK, NULL);
@ -857,7 +941,8 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
} }
closed_chan = ftdmchan; ftdm_clear_sflag(ftdmchan, AF_POLARITY_REVERSE);
ftdm_channel_close(&ftdmchan); ftdm_channel_close(&ftdmchan);
ftdm_channel_command(closed_chan, FTDM_COMMAND_SET_NATIVE_CODEC, NULL); ftdm_channel_command(closed_chan, FTDM_COMMAND_SET_NATIVE_CODEC, NULL);
@ -875,8 +960,11 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
} }
ftdm_log_chan(closed_chan, FTDM_LOG_DEBUG, "ANALOG CHANNEL %d:%d thread ended.\n", closed_chan->span_id, closed_chan->chan_id); ftdm_log_chan(closed_chan, FTDM_LOG_DEBUG, "ANALOG CHANNEL %d:%d thread ended.\n", closed_chan->span_id, closed_chan->chan_id);
ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD); ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD);
ftdm_channel_unlock(closed_chan);
return NULL; return NULL;
} }
@ -903,6 +991,19 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
ftdm_mutex_lock(event->channel->mutex); ftdm_mutex_lock(event->channel->mutex);
locked++; locked++;
/* MAINTENANCE WARNING:
* 1. Be aware you are working on the locked channel
* 2. We should not be calling ftdm_span_send_signal or ftdm_set_state when there is already a channel thread running
* however, since this is old code I am not changing it now, but new code should adhere to that convention
* otherwise, we have possible races where we compete with the user for state changes, ie, the user requests
* a state change and then we process an event, the state change from the user is pending so our ftdm_set_state
* operation will fail. In cases where we win the race, our state change will be accepted but if a user requests
* a state change before the state change we requested here is processed by the channel thread, we'll end up
* rejecting the user request.
*
* See docs/locking.txt for further information about what guarantees should signaling modules provide when
* locking/unlocking a channel
* */
switch(event->enum_id) { switch(event->enum_id) {
case FTDM_OOB_RING_START: case FTDM_OOB_RING_START:
{ {
@ -940,7 +1041,11 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
} }
ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_DOWN); ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_DOWN);
} }
if (event->channel->type == FTDM_CHAN_TYPE_FXS) {
/* we always return to forward when the device goes onhook */
ftdm_polarity_t forward_polarity = FTDM_POLARITY_FORWARD;
ftdm_channel_command(event->channel, FTDM_COMMAND_SET_POLARITY, &forward_polarity);
}
} }
break; break;
case FTDM_OOB_FLASH: case FTDM_OOB_FLASH:
@ -1004,6 +1109,35 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
ftdm_span_send_signal(span, &sig); ftdm_span_send_signal(span, &sig);
} }
break; break;
case FTDM_OOB_POLARITY_REVERSE:
{
if (event->channel->type != FTDM_CHAN_TYPE_FXO) {
ftdm_log_chan_msg(event->channel, FTDM_LOG_WARNING,
"Ignoring polarity reversal, this should not happen in non-FXO channels!\n");
break;
}
if (!ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD) &&
ftdm_test_flag(event->channel, FTDM_CHANNEL_OFFHOOK)) {
ftdm_log_chan_msg(event->channel, FTDM_LOG_WARNING,
"Forcing onhook in channel not in thread after polarity reversal\n");
ftdm_channel_command(event->channel, FTDM_COMMAND_ONHOOK, NULL);
break;
}
if (!ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE)
&& !ftdm_test_flag(analog_data, FTDM_ANALOG_HANGUP_POLARITY_REVERSE)) {
ftdm_log_chan_msg(event->channel, FTDM_LOG_DEBUG,
"Ignoring polarity reversal because this channel is not configured for it\n");
break;
}
if (event->channel->state == FTDM_CHANNEL_STATE_DOWN) {
ftdm_log_chan_msg(event->channel, FTDM_LOG_DEBUG,
"Ignoring polarity reversal because this channel is down\n");
break;
}
/* we have a good channel, set the polarity flag and let the channel thread deal with it */
ftdm_set_sflag(event->channel, AF_POLARITY_REVERSE);
}
break;
default: default:
{ {
ftdm_log_chan(event->channel, FTDM_LOG_DEBUG, "Ignoring event [%s] in state [%s]\n", ftdm_oob_event2str(event->enum_id), ftdm_channel_state2str(event->channel->state)); ftdm_log_chan(event->channel, FTDM_LOG_DEBUG, "Ignoring event [%s] in state [%s]\n", ftdm_oob_event2str(event->enum_id), ftdm_channel_state2str(event->channel->state));

View File

@ -784,17 +784,27 @@ static FIO_COMMAND_FUNCTION(wanpipe_command)
err = sangoma_set_tx_queue_sz(ftdmchan->sockfd, &tdm_api, queue_size); err = sangoma_set_tx_queue_sz(ftdmchan->sockfd, &tdm_api, queue_size);
} }
break; break;
case FTDM_COMMAND_SET_POLARITY:
{
ftdm_polarity_t polarity = FTDM_COMMAND_OBJ_INT;
err = sangoma_tdm_set_polarity(ftdmchan->sockfd, &tdm_api, polarity);
if (!err) {
ftdmchan->polarity = polarity;
}
}
break;
default: default:
err = FTDM_NOTIMPL; err = FTDM_NOTIMPL;
break; break;
}; };
if (err) { if (err) {
snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); int myerrno = errno;
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Wanpipe failed to execute command %d: %s\n", command, strerror(myerrno));
errno = myerrno;
return err; return err;
} }
return FTDM_SUCCESS; return FTDM_SUCCESS;
} }
@ -1237,8 +1247,9 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event)
wanpipe_tdm_api_t tdm_api; wanpipe_tdm_api_t tdm_api;
ftdm_span_t *span = ftdmchan->span; ftdm_span_t *span = ftdmchan->span;
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) {
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT); ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT);
}
memset(&tdm_api, 0, sizeof(tdm_api)); memset(&tdm_api, 0, sizeof(tdm_api));
status = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api); status = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api);
@ -1251,7 +1262,7 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event)
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "read wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "read wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type);
switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) { switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) {
case WP_TDMAPI_EVENT_LINK_STATUS: case WP_API_EVENT_LINK_STATUS:
{ {
switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) { switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) {
case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED: case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED:
@ -1264,7 +1275,7 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event)
} }
break; break;
case WP_TDMAPI_EVENT_RXHOOK: case WP_API_EVENT_RXHOOK:
{ {
if (ftdmchan->type == FTDM_CHAN_TYPE_FXS) { if (ftdmchan->type == FTDM_CHAN_TYPE_FXS) {
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_OFFHOOK : FTDM_OOB_ONHOOK; event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_OFFHOOK : FTDM_OOB_ONHOOK;
@ -1300,26 +1311,26 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event)
} }
} }
break; break;
case WP_TDMAPI_EVENT_RING_DETECT: case WP_API_EVENT_RING_DETECT:
{ {
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_RING_START : FTDM_OOB_RING_STOP; event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_RING_START : FTDM_OOB_RING_STOP;
} }
break; break;
/* /*
disabled this ones when configuring, we don't need them, do we? disabled this ones when configuring, we don't need them, do we?
case WP_TDMAPI_EVENT_RING_TRIP_DETECT: case WP_API_EVENT_RING_TRIP_DETECT:
{ {
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_ONHOOK : FTDM_OOB_OFFHOOK; event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_ONHOOK : FTDM_OOB_OFFHOOK;
} }
break; break;
*/ */
case WP_TDMAPI_EVENT_RBS: case WP_API_EVENT_RBS:
{ {
event_id = FTDM_OOB_CAS_BITS_CHANGE; event_id = FTDM_OOB_CAS_BITS_CHANGE;
ftdmchan->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits); ftdmchan->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits);
} }
break; break;
case WP_TDMAPI_EVENT_DTMF: case WP_API_EVENT_DTMF:
{ {
char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 }; char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 };
event_id = FTDM_OOB_NOOP; event_id = FTDM_OOB_NOOP;
@ -1342,12 +1353,18 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event)
} }
} }
break; break;
case WP_TDMAPI_EVENT_ALARM: case WP_API_EVENT_ALARM:
{ {
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm);
event_id = FTDM_OOB_ALARM_TRAP; event_id = FTDM_OOB_ALARM_TRAP;
} }
break; break;
case WP_API_EVENT_POLARITY_REVERSE:
{
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Got polarity reverse\n");
event_id = FTDM_OOB_POLARITY_REVERSE;
}
break;
default: default:
{ {
ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unhandled wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unhandled wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type);
@ -1423,7 +1440,7 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event)
ftdm_log_chan(span->channels[i], FTDM_LOG_DEBUG, "read wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); ftdm_log_chan(span->channels[i], FTDM_LOG_DEBUG, "read wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type);
switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) { switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) {
case WP_TDMAPI_EVENT_LINK_STATUS: case WP_API_EVENT_LINK_STATUS:
{ {
switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) { switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) {
case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED: case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED:
@ -1436,7 +1453,7 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event)
} }
break; break;
case WP_TDMAPI_EVENT_RXHOOK: case WP_API_EVENT_RXHOOK:
{ {
if (span->channels[i]->type == FTDM_CHAN_TYPE_FXS) { if (span->channels[i]->type == FTDM_CHAN_TYPE_FXS) {
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_OFFHOOK : FTDM_OOB_ONHOOK; event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_OFFHOOK : FTDM_OOB_ONHOOK;
@ -1472,26 +1489,26 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event)
} }
} }
break; break;
case WP_TDMAPI_EVENT_RING_DETECT: case WP_API_EVENT_RING_DETECT:
{ {
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_RING_START : FTDM_OOB_RING_STOP; event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_RING_START : FTDM_OOB_RING_STOP;
} }
break; break;
/* /*
disabled this ones when configuring, we don't need them, do we? disabled this ones when configuring, we don't need them, do we?
case WP_TDMAPI_EVENT_RING_TRIP_DETECT: case WP_API_EVENT_RING_TRIP_DETECT:
{ {
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_ONHOOK : FTDM_OOB_OFFHOOK; event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_ONHOOK : FTDM_OOB_OFFHOOK;
} }
break; break;
*/ */
case WP_TDMAPI_EVENT_RBS: case WP_API_EVENT_RBS:
{ {
event_id = FTDM_OOB_CAS_BITS_CHANGE; event_id = FTDM_OOB_CAS_BITS_CHANGE;
span->channels[i]->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits); span->channels[i]->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits);
} }
break; break;
case WP_TDMAPI_EVENT_DTMF: case WP_API_EVENT_DTMF:
{ {
char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 }; char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 };
event_id = FTDM_OOB_NOOP; event_id = FTDM_OOB_NOOP;
@ -1514,12 +1531,18 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event)
} }
} }
break; break;
case WP_TDMAPI_EVENT_ALARM: case WP_API_EVENT_ALARM:
{ {
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm);
event_id = FTDM_OOB_ALARM_TRAP; event_id = FTDM_OOB_ALARM_TRAP;
} }
break; break;
case WP_API_EVENT_POLARITY_REVERSE:
{
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Got polarity reverse\n");
event_id = FTDM_OOB_POLARITY_REVERSE;
}
break;
default: default:
{ {
ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unhandled wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unhandled wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type);

View File

@ -557,8 +557,14 @@ typedef enum {
FTDM_COMMAND_COUNT, FTDM_COMMAND_COUNT,
FTDM_COMMAND_SET_RX_QUEUE_SIZE, FTDM_COMMAND_SET_RX_QUEUE_SIZE,
FTDM_COMMAND_SET_TX_QUEUE_SIZE, FTDM_COMMAND_SET_TX_QUEUE_SIZE,
FTDM_COMMAND_SET_POLARITY,
} ftdm_command_t; } ftdm_command_t;
typedef enum {
FTDM_POLARITY_FORWARD = 0,
FTDM_POLARITY_REVERSE = 1
} ftdm_polarity_t;
/*! \brief Custom memory handler hooks. Not recommended to use unless you need memory allocation customizations */ /*! \brief Custom memory handler hooks. Not recommended to use unless you need memory allocation customizations */
typedef void *(*ftdm_malloc_func_t)(void *pool, ftdm_size_t len); typedef void *(*ftdm_malloc_func_t)(void *pool, ftdm_size_t len);
typedef void *(*ftdm_calloc_func_t)(void *pool, ftdm_size_t elements, ftdm_size_t len); typedef void *(*ftdm_calloc_func_t)(void *pool, ftdm_size_t elements, ftdm_size_t len);

View File

@ -143,7 +143,9 @@ extern "C" {
\return true value if the object has the flags defined \return true value if the object has the flags defined
*/ */
#define ftdm_test_flag(obj, flag) ((obj)->flags & flag) #define ftdm_test_flag(obj, flag) ((obj)->flags & flag)
/*!< Physical (IO) module specific flags */
#define ftdm_test_pflag(obj, flag) ((obj)->pflags & flag) #define ftdm_test_pflag(obj, flag) ((obj)->pflags & flag)
/*!< signaling module specific flags */
#define ftdm_test_sflag(obj, flag) ((obj)->sflags & flag) #define ftdm_test_sflag(obj, flag) ((obj)->sflags & flag)
#define ftdm_set_alarm_flag(obj, flag) (obj)->alarm_flags |= (flag) #define ftdm_set_alarm_flag(obj, flag) (obj)->alarm_flags |= (flag)
@ -456,6 +458,7 @@ struct ftdm_channel {
ftdm_fsk_data_state_t fsk; ftdm_fsk_data_state_t fsk;
uint8_t fsk_buf[80]; uint8_t fsk_buf[80];
uint32_t ring_count; uint32_t ring_count;
ftdm_polarity_t polarity;
/* Private I/O data. Do not touch unless you are an I/O module */ /* Private I/O data. Do not touch unless you are an I/O module */
void *io_data; void *io_data;
/* Private signaling data. Do not touch unless you are a signaling module */ /* Private signaling data. Do not touch unless you are a signaling module */

View File

@ -123,6 +123,7 @@ typedef enum {
FTDM_STR2ENUM_P(ftdm_str2ftdm_analog_start_type, ftdm_analog_start_type2str, ftdm_analog_start_type_t) FTDM_STR2ENUM_P(ftdm_str2ftdm_analog_start_type, ftdm_analog_start_type2str, ftdm_analog_start_type_t)
typedef enum { typedef enum {
FTDM_OOB_NOOP,
FTDM_OOB_ONHOOK, FTDM_OOB_ONHOOK,
FTDM_OOB_OFFHOOK, FTDM_OOB_OFFHOOK,
FTDM_OOB_WINK, FTDM_OOB_WINK,
@ -131,11 +132,11 @@ typedef enum {
FTDM_OOB_RING_STOP, FTDM_OOB_RING_STOP,
FTDM_OOB_ALARM_TRAP, FTDM_OOB_ALARM_TRAP,
FTDM_OOB_ALARM_CLEAR, FTDM_OOB_ALARM_CLEAR,
FTDM_OOB_NOOP,
FTDM_OOB_CAS_BITS_CHANGE, FTDM_OOB_CAS_BITS_CHANGE,
FTDM_OOB_POLARITY_REVERSE,
FTDM_OOB_INVALID FTDM_OOB_INVALID
} ftdm_oob_event_t; } ftdm_oob_event_t;
#define OOB_STRINGS "ONHOOK", "OFFHOOK", "WINK", "FLASH", "RING_START", "RING_STOP", "ALARM_TRAP", "ALARM_CLEAR", "NOOP", "CAS_BITS_CHANGE", "INVALID" #define OOB_STRINGS "NOOP", "ONHOOK", "OFFHOOK", "WINK", "FLASH", "RING_START", "RING_STOP", "ALARM_TRAP", "ALARM_CLEAR", "CAS_BITS_CHANGE", "POLARITY_REVERSE", "INVALID"
FTDM_STR2ENUM_P(ftdm_str2ftdm_oob_event, ftdm_oob_event2str, ftdm_oob_event_t) FTDM_STR2ENUM_P(ftdm_str2ftdm_oob_event, ftdm_oob_event2str, ftdm_oob_event_t)
/*! \brief Event types */ /*! \brief Event types */