]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
OPENZAP-238: [freetdm] Enable GSM immediate forwarding logic
authorMoises Silva <moy@sangoma.com>
Fri, 8 Aug 2014 06:38:04 +0000 (02:38 -0400)
committerMoises Silva <moy@sangoma.com>
Fri, 13 Nov 2015 07:05:53 +0000 (02:05 -0500)
Use the new parameter immediate-forwarding-numbers to configure
immediate forwarding logic that emulates hunt groups

The parameter syntax is:

[<span-name>:]<number>

Multiple elements can be specified separated by commas

If the <span-name> is specified, the span will be checked for
availability, if available, its number will be selected for
forwarding, otherwise next number will be checked

Forwarding is enabled as soon as a channel is answered and its
disabled when the channel is hung up

libs/freetdm/conf/freetdm.conf.xml
libs/freetdm/src/ftmod/ftmod_gsm/ftmod_gsm.c

index dcbaafe2db86a8b9fc1e5fd0fec72a3066acc900..9b9912006d315ebc26c59df5ddc2424728a952a8 100644 (file)
@@ -571,5 +571,59 @@ with the signaling protocols that you can run on top of your I/O interfaces.
                        <param name="context" value="default"/>
                </span>
        </pritap_spans>
+
+       <!--
+       GSM spans (libwat must be installed when configuring the freetdm build)
+       -->
+       <gsm_spans>
+               <span name="gsm01">
+                       <!-- where to send inbound calls to -->
+                       <param name="dialplan" value="XML" />
+                       <param name="context" value="module1" />
+
+                       <!--
+                               GSM module type
+                               Accepted values: "telit", "telit-gc864", "telit-he910", "telit-cc864", "telit-de910", "motorola"
+                       -->
+                       <param name="moduletype" value="telit-gc864" />
+
+                       <!--
+                               Debug mask (accepted values: all, uart_raw, uart_raw, call_state, span_state, at_parse, at_handle, sms_encode, sms_decode, none)
+                               comma-separated values are accepted to combine those levels
+                       -->
+                       <param name="debug" value="all" />
+
+                       <!--
+                               Whether to enable HW DTMF in the hardware module
+                               Accepted values: true, generate, detect, false
+                       -->
+                       <param name="hwdtmf" value="true" />
+
+                       <!--
+                               This enables conditional forwarding on module startup
+                       -->
+                       <!-- <param name="conditional-forwarding-prefix" value="*71" /> -->
+                       <!-- <param name="conditional-forwarding-number" value="123456789" /> -->
+
+                       <!--
+                               This enables immediate forwarding logic to simulate hunt groups
+                               The syntax for immediate-forwarding-numbers ia a comma-separated
+                               list of elements in the form [<span-name>:]<number>
+
+                               if span-name is specified, the span will be checked for availability
+                               before enabling forwarding to that span number
+
+                               The span-name must be a defined freetdm span name
+
+                               If the span-name is not specified then only first number specified is used for
+                               forwarding whenever this span is busy
+                       -->
+                       <!-- <param name="immediate-forwarding-prefix" value="*72" /> -->
+                       <!-- <param name="immediate-forwarding-numbers" value="gsm02:123456789" /> -->
+
+                       <!-- Number to dial to disable forwarding when the call ends (if immediate forwarding was enabled) -->
+                       <!-- <param name="disable-forwarding-number" value="*73" /> -->
+               </span>
+       </gsm_spans>
 </configuration>
 
index 974ef2c5162856d2720b5977e41a8773f8f859c1..9e136313df3f8c5adaf64215cf9ca04d72b53f3c 100755 (executable)
@@ -106,10 +106,19 @@ typedef struct ftdm_gsm_span_data_s {
        ftdm_channel_t *bchan;
        int32_t call_id;
        uint32_t sms_id;
-       char conditional_forward_number[255];
+       char conditional_forward_prefix[10];
+       char conditional_forward_number[50];
+       char immediate_forward_prefix[10];
+       struct {
+               char number[50];
+               char span[50];
+       } immediate_forward_numbers[10];
+       char disable_forward_number[50];
        ftdm_sched_t *sched;
        ftdm_timer_id_t conditional_forwarding_timer;
-       ftdm_bool_t init_forwarding;
+       ftdm_timer_id_t immediate_forwarding_timer;
+       ftdm_bool_t init_conditional_forwarding;
+       ftdm_bool_t startup_forwarding_disabled;
 } ftdm_gsm_span_data_t;
 
 // command handler function type.
@@ -230,9 +239,11 @@ done:
 
 static void ftdm_gsm_enable_conditional_forwarding(void *data)
 {
+       char number[255];
        ftdm_gsm_span_data_t *gsm_data = data;
-       ftdm_log_chan(gsm_data->bchan, FTDM_LOG_NOTICE, "Enabling conditional forwarding to %s\n", gsm_data->conditional_forward_number);
-       ftdm_gsm_make_raw_call(data, gsm_data->conditional_forward_number);
+       snprintf(number, sizeof(number), "%s%s", gsm_data->conditional_forward_prefix, gsm_data->conditional_forward_number);
+       ftdm_log_chan(gsm_data->bchan, FTDM_LOG_INFO, "Enabling conditional forwarding to %s\n", number);
+       ftdm_gsm_make_raw_call(data, number);
 }
 
 static void on_wat_span_status(unsigned char span_id, wat_span_status_t *status)
@@ -258,12 +269,12 @@ static void on_wat_span_status(unsigned char span_id, wat_span_status_t *status)
                        } else {
                                ftdm_log_chan_msg(gsm_data->bchan, FTDM_LOG_INFO, "Signaling is now down\n");
                        }
-                       if (gsm_data->init_forwarding == FTDM_TRUE && !ftdm_strlen_zero_buf(gsm_data->conditional_forward_number)) {
-                               ftdm_sched_timer(gsm_data->sched, "conditional_forwarding_delay", 500,
+                       if (gsm_data->init_conditional_forwarding == FTDM_TRUE && !ftdm_strlen_zero_buf(gsm_data->conditional_forward_number)) {
+                               ftdm_sched_timer(gsm_data->sched, "conditional_forwarding_delay", 1000,
                                                ftdm_gsm_enable_conditional_forwarding,
                                                gsm_data,
                                                &gsm_data->conditional_forwarding_timer);
-                               gsm_data->init_forwarding = FTDM_FALSE;
+                               gsm_data->init_conditional_forwarding = FTDM_FALSE;
                        }
                }
                break;
@@ -754,136 +765,164 @@ static ftdm_state_map_t gsm_state_map = {
        }
 };
 
+#define immediate_forward_enabled(gsm_data) !ftdm_strlen_zero_buf(gsm_data->immediate_forward_numbers[0].number)
+
+static void perform_enable_immediate_forward(void *data)
+{
+       ftdm_span_t *fwd_span = NULL;
+       ftdm_gsm_span_data_t *fwd_gsm_data = NULL;
+       char *fwd_span_name = NULL;
+       char *number = NULL;
+       char cmd[100];
+       int i = 0;
+       ftdm_channel_t *ftdmchan = data;
+       ftdm_gsm_span_data_t *gsm_data = ftdmchan->span->signal_data;
+
+       for (i = 0; i < ftdm_array_len(gsm_data->immediate_forward_numbers); i++) {
+               fwd_span_name = gsm_data->immediate_forward_numbers[i].span;
+               fwd_span = NULL;
+               if (!ftdm_strlen_zero_buf(fwd_span_name) &&
+                   ftdm_span_find_by_name(fwd_span_name, &fwd_span) != FTDM_SUCCESS) {
+                       continue;
+               }
+               fwd_gsm_data = fwd_span ? fwd_span->signal_data : NULL;
+               if (fwd_gsm_data && fwd_gsm_data->call_id) {
+                       /* span busy, do not forward here */
+                       continue;
+               }
+               number = gsm_data->immediate_forward_numbers[i].number;
+               break;
+       }
+
+       if (!number) {
+               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "No numbers available to enable immediate forwarding\n");
+               return;
+       }
+
+       ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Enabling immediate forwarding to %s\n", number);
+       snprintf(cmd, sizeof(cmd), "ATD%s%s", gsm_data->immediate_forward_prefix, number);
+       wat_cmd_req(ftdmchan->span->span_id, cmd, NULL, NULL);
+}
+
+static __inline__ void enable_immediate_forward(ftdm_channel_t *ftdmchan)
+{
+       ftdm_gsm_span_data_t *gsm_data = ftdmchan->span->signal_data;
+       ftdm_sched_timer(gsm_data->sched, "immediate_forwarding_delay", 1000,
+                       perform_enable_immediate_forward,
+                       ftdmchan,
+                       &gsm_data->immediate_forwarding_timer);
+}
+
+static __inline__ void disable_all_forwarding(ftdm_channel_t *ftdmchan)
+{
+       char cmd[100];
+       ftdm_gsm_span_data_t *gsm_data = ftdmchan->span->signal_data;
+
+       if (ftdm_strlen_zero_buf(gsm_data->disable_forward_number)) {
+               return;
+       }
+
+       snprintf(cmd, sizeof(cmd), "ATD%s", gsm_data->disable_forward_number);
+       ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Disabling GSM immediate forward dialing %s\n", gsm_data->disable_forward_number);
+       wat_cmd_req(ftdmchan->span->span_id, cmd, NULL, NULL);
+}
+
 static ftdm_status_t ftdm_gsm_state_advance(ftdm_channel_t *ftdmchan)
 {
+       ftdm_gsm_span_data_t *gsm_data = ftdmchan->span->signal_data;
+
+       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing GSM state handler for %s\n", ftdm_channel_state2str(ftdmchan->state));
 
-       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state));
-       
        ftdm_channel_complete_state(ftdmchan);
 
-               switch (ftdmchan->state) {
+       switch (ftdmchan->state) {
 
-               /* starting an incoming call */
-               case FTDM_CHANNEL_STATE_COLLECT: 
-                       {
-                               
-                               
-                       }
-                       break;
+       /* starting an outgoing call */
+       case FTDM_CHANNEL_STATE_DIALING:
+               {
+                       uint32_t interval = 0;
+                       wat_con_event_t con_event;
 
-                       /* starting an outgoing call */
-               case FTDM_CHANNEL_STATE_DIALING:
-                       {
-                               uint32_t interval = 0;
-                               
-                               ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting outgoing call with interval %d\n", interval);
-
-                               {
-
-                                       ftdm_gsm_span_data_t *gsm_data;
-                                       wat_con_event_t con_event;
-                                       gsm_data = ftdmchan->span->signal_data;
-                                       gsm_data->call_id = GSM_OUTBOUND_CALL_ID;
-                                       memset(&con_event, 0, sizeof(con_event));
-                                       ftdm_set_string(con_event.called_num.digits, ftdmchan->caller_data.dnis.digits);
-                                       ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s\n", con_event.called_num.digits);
-                                       wat_con_req(ftdmchan->span->span_id, gsm_data->call_id , &con_event);
-                                       SEND_STATE_SIGNAL(FTDM_SIGEVENT_DIALING);
-                                       
-                                       
-                               }
+                       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting outgoing call with interval %d\n", interval);
 
-                               
-                       }
-                       break;
+                       gsm_data->call_id = GSM_OUTBOUND_CALL_ID;
+                       memset(&con_event, 0, sizeof(con_event));
+                       ftdm_set_string(con_event.called_num.digits, ftdmchan->caller_data.dnis.digits);
+                       ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s\n", con_event.called_num.digits);
+                       wat_con_req(ftdmchan->span->span_id, gsm_data->call_id , &con_event);
 
-                       /* incoming call was offered */
-               case FTDM_CHANNEL_STATE_RING:
+                       SEND_STATE_SIGNAL(FTDM_SIGEVENT_DIALING);
+               }
+               break;
 
+       /* incoming call was offered */
+       case FTDM_CHANNEL_STATE_RING:
+               {
                        /* notify the user about the new call */
-
-                       ftdm_log(FTDM_LOG_INFO, "Answering Incomming Call\r\n");
+                       ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Inbound call detected\n");
                        SEND_STATE_SIGNAL(FTDM_SIGEVENT_START);
-                       
-                       break;
-
-                       /* the call is making progress */
-               case FTDM_CHANNEL_STATE_PROGRESS:
-               case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
-                       {
-                               SEND_STATE_SIGNAL(FTDM_SIGEVENT_PROGRESS_MEDIA);
-                               ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP);
-                       }
-                       break;
+               }
+               break;
 
-                       /* the call was answered */
-               case FTDM_CHANNEL_STATE_UP:
-                       {
-                               if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-                               
-                                       SEND_STATE_SIGNAL(FTDM_SIGEVENT_UP);
-                               }
-                               else {
-                                               ftdm_gsm_span_data_t *gsm_data;
-                                               gsm_data = ftdmchan->span->signal_data;
-                                               wat_con_cfm(ftdmchan->span->span_id, gsm_data->call_id); 
-                               }
-                               
-                               
-                       }
-                       break;
-
-                       /* just got hangup */
-               case FTDM_CHANNEL_STATE_HANGUP:
-                       {
-                               ftdm_gsm_span_data_t *gsm_data;
-                               gsm_data = ftdmchan->span->signal_data;
-                               wat_rel_req(ftdmchan->span->span_id, gsm_data->call_id);
-                               gsm_data->call_id = 0;
-                               SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
-                       }
-                       break;
+       /* the call is making progress */
+       case FTDM_CHANNEL_STATE_PROGRESS:
+       case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
+               {
+                       SEND_STATE_SIGNAL(FTDM_SIGEVENT_PROGRESS_MEDIA);
+                       ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP);
+               }
+               break;
 
-               case FTDM_CHANNEL_STATE_TERMINATING:
-                       {
-                               SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
+       /* the call was answered */
+       case FTDM_CHANNEL_STATE_UP:
+               {
+                       if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
+                               SEND_STATE_SIGNAL(FTDM_SIGEVENT_UP);
+                       } else {
+                               wat_con_cfm(ftdmchan->span->span_id, gsm_data->call_id);
                        }
-                       break;
-
-                       /* finished call for good */
-               case FTDM_CHANNEL_STATE_DOWN: 
-                       {
-                               ftdm_channel_t *closed_chan;
-                               closed_chan = ftdmchan;
-                               ftdm_channel_close(&closed_chan);
-                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "State processing ended.\n");
-                               SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
+                       if (immediate_forward_enabled(gsm_data)) {
+                               enable_immediate_forward(ftdmchan);
                        }
-                       break;
+               }
+               break;
 
-                       /* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */
-               case FTDM_CHANNEL_STATE_RINGING: 
-                       ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n");
-                       SEND_STATE_SIGNAL(FTDM_SIGEVENT_RINGING);
-                               
-                       break;
+       /* just got hangup */
+       case FTDM_CHANNEL_STATE_HANGUP:
+               {
+                       wat_rel_req(ftdmchan->span->span_id, gsm_data->call_id);
+                       SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
+               }
+               break;
 
-                       /* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */
-               case FTDM_CHANNEL_STATE_RESET:
-                       {
-                               ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n");
-                               ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
+       /* finished call for good */
+       case FTDM_CHANNEL_STATE_DOWN:
+               {
+                       ftdm_channel_t *closed_chan;
+                       gsm_data->call_id = 0;
+                       closed_chan = ftdmchan;
+                       ftdm_channel_close(&closed_chan);
+                       ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "State processing ended.\n");
+                       SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
+                       if (immediate_forward_enabled(gsm_data)) {
+                               disable_all_forwarding(ftdmchan);
                        }
-                       break;
+               }
+               break;
 
-               default:
-                       {
-                               ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state));
-                       }
-                       break;
-       }
+       /* Outbound call is ringing */
+       case FTDM_CHANNEL_STATE_RINGING:
+               {
+                       SEND_STATE_SIGNAL(FTDM_SIGEVENT_RINGING);
+               }
+               break;
 
+       default:
+               {
+                       ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state: %s\n", ftdm_channel_state2str(ftdmchan->state));
+               }
+               break;
+       }
        
        return FTDM_SUCCESS;
 }
@@ -1067,7 +1106,50 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_gsm_configure_span_signaling)
                        ftdm_log(FTDM_LOG_DEBUG, "Configuring GSM span %s with hardware dtmf %s\n", span->name, val);
                } else if (!strcasecmp(var, "conditional-forwarding-number")) {
                        ftdm_set_string(gsm_data->conditional_forward_number, val);
-                       gsm_data->init_forwarding = FTDM_TRUE;
+                       gsm_data->init_conditional_forwarding = FTDM_TRUE;
+               } else if (!strcasecmp(var, "conditional-forwarding-prefix")) {
+                       ftdm_set_string(gsm_data->conditional_forward_prefix, val);
+               } else if (!strcasecmp(var, "immediate-forwarding-numbers")) {
+                       char *state = NULL;
+                       char *span_end = NULL;
+                       char *number = NULL;
+                       char *span_name = NULL;
+                       int f = 0;
+                       char *valdup = ftdm_strdup(val);
+                       char *s = valdup;
+
+                       if (!ftdm_strlen_zero_buf(gsm_data->immediate_forward_numbers[0].number)) {
+                               ftdm_log(FTDM_LOG_ERROR, "immediate-forwarding-numbers already parsed! failed to parse: %s\n", val);
+                               goto ifn_parse_done;
+                       }
+
+                       /* The string must be in the form [<span>:]<number>, optionally multiple elements separated by comma */
+                       while ((number = strtok_r(s, ",", &state))) {
+                               if (f == ftdm_array_len(gsm_data->immediate_forward_numbers)) {
+                                       ftdm_log(FTDM_LOG_ERROR, "Max number (%d) of immediate forwarding numbers reached!\n", f);
+                                       break;
+                               }
+
+                               s = NULL;
+                               span_end = strchr(number, ':');
+                               if (span_end) {
+                                       *span_end = '\0';
+                                       span_name = number;
+                                       number = (span_end + 1);
+                                       ftdm_set_string(gsm_data->immediate_forward_numbers[f].span, span_name);
+                                       ftdm_log(FTDM_LOG_DEBUG, "Parsed immediate forwarding to span %s number %s\n", span_name, number);
+                               } else {
+                                       ftdm_log(FTDM_LOG_DEBUG, "Parsed immediate forwarding to number %s\n", number);
+                               }
+                               ftdm_set_string(gsm_data->immediate_forward_numbers[f].number, number);
+                               f++;
+                       }
+ifn_parse_done:
+                       ftdm_safe_free(valdup);
+               } else if (!strcasecmp(var, "immediate-forwarding-prefix")) {
+                       ftdm_set_string(gsm_data->immediate_forward_prefix, val);
+               } else if (!strcasecmp(var, "disable-forwarding-number")) {
+                       ftdm_set_string(gsm_data->disable_forward_number, val);
                } else {
                        ftdm_log(FTDM_LOG_ERROR, "Ignoring unknown GSM parameter '%s'", var);
                }