root/sample/boost/ftdmstart.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. interrupt_requested
  2. schedule_timer
  3. run_timers
  4. release_timers
  5. send_hangup
  6. send_answer
  7. send_progress
  8. FIO_SIGNAL_CB_FUNCTION
  9. place_call
  10. main

   1 /*
   2  * Copyright (c) 2010, Sangoma Technologies
   3  * Moises Silva <moy@sangoma.com>
   4  * All rights reserved.
   5  * 
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  * 
  10  * * Redistributions of source code must retain the above copyright
  11  * notice, this list of conditions and the following disclaimer.
  12  * 
  13  * * Redistributions in binary form must reproduce the above copyright
  14  * notice, this list of conditions and the following disclaimer in the
  15  * documentation and/or other materials provided with the distribution.
  16  * 
  17  * * Neither the name of the original author; nor the names of any contributors
  18  * may be used to endorse or promote products derived from this software
  19  * without specific prior written permission.
  20  * 
  21  * 
  22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
  26  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33  */
  34 
  35 /**
  36  * Sample program for the boost signaling absraction.
  37  * Usage: testboostalone <spanno-1> <spanno-2> ... -d [number-to-dial-if-any]
  38  * compile this program linking to the freetdm library (ie -lfreetdm)
  39  **/
  40 
  41 #ifndef __linux__
  42 #define _CRT_SECURE_NO_WARNINGS 1
  43 #endif
  44 
  45 #include <signal.h>
  46 
  47 #include "freetdm.h"
  48 #include <stdlib.h>
  49 #include <string.h>
  50 #include <errno.h>
  51 
  52 
  53 /* arbitrary limit for max calls in this sample program */
  54 #define MAX_CALLS 255
  55 
  56 /* some timers (in seconds) to fake responses in incoming calls */
  57 #define PROGRESS_TIMER 1
  58 #define ANSWER_TIMER 5
  59 #define HANGUP_TIMER 15
  60 
  61 /* simple variable used to stop the application */
  62 static int app_running = 0;
  63 
  64 typedef void (*expired_function_t)(ftdm_channel_t *channel);
  65 typedef struct dummy_timer_s {
  66         int time;
  67         ftdm_channel_t *channel;
  68         expired_function_t expired;
  69 } dummy_timer_t;
  70 
  71 /* dummy second resolution timers */
  72 static dummy_timer_t g_timers[MAX_CALLS];
  73 
  74 /* mutex to protect the timers (both, the test thread and the signaling thread may modify them) */
  75 static ftdm_mutex_t *g_schedule_mutex;
  76 
  77 /* unique outgoing channel */
  78 static ftdm_channel_t *g_outgoing_channel = NULL;
  79 
  80 static void interrupt_requested(int signal)
  81 {
  82         app_running = 0;
  83 }
  84 
  85 static void schedule_timer(ftdm_channel_t *channel, int sec, expired_function_t expired)
  86 {
  87         int i;
  88         ftdm_mutex_lock(g_schedule_mutex);
  89         for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) {
  90                 /* check the timer slot is free to use */
  91                 if (!g_timers[i].time) {
  92                         g_timers[i].time = sec;
  93                         g_timers[i].channel = channel;
  94                         g_timers[i].expired = expired;
  95                         ftdm_mutex_unlock(g_schedule_mutex);
  96                         return;
  97                 }
  98         }
  99         ftdm_log(FTDM_LOG_ERROR, "Failed to schedule timer\n");
 100         ftdm_mutex_unlock(g_schedule_mutex);
 101 }
 102 
 103 static void run_timers(void)
 104 {
 105         int i;
 106         void *channel;
 107         expired_function_t expired_func = NULL;
 108         ftdm_mutex_lock(g_schedule_mutex);
 109         for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) {
 110                 /* if there's time left, decrement */
 111                 if (g_timers[i].time) {
 112                         g_timers[i].time--;
 113                 }
 114 
 115                 /* if time expired and we have an expired function, call it */
 116                 if (!g_timers[i].time && g_timers[i].expired) {
 117                         expired_func = g_timers[i].expired;
 118                         channel = g_timers[i].channel;
 119                         memset(&g_timers[i], 0, sizeof(g_timers[i]));
 120                         expired_func(channel);
 121                 }
 122         }
 123         ftdm_mutex_unlock(g_schedule_mutex);
 124 }
 125 
 126 static void release_timers(ftdm_channel_t *channel)
 127 {
 128         int i;
 129         ftdm_mutex_lock(g_schedule_mutex);
 130         for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) {
 131                 /* clear any timer belonging to the given channel */
 132                 if (g_timers[i].channel == channel) {
 133                         memset(&g_timers[i], 0, sizeof(g_timers[i]));
 134                 }
 135         }
 136         ftdm_mutex_unlock(g_schedule_mutex);
 137 }
 138 
 139 /*  hangup the call */ 
 140 static void send_hangup(ftdm_channel_t *channel)
 141 {
 142         int spanid = ftdm_channel_get_span_id(channel);
 143         int chanid = ftdm_channel_get_id(channel);
 144         ftdm_log(FTDM_LOG_NOTICE, "-- Requesting hangup in channel %d:%d\n", spanid, chanid);
 145         ftdm_channel_call_hangup(channel);
 146 }
 147 
 148 /*  send answer for an incoming call */ 
 149 static void send_answer(ftdm_channel_t *channel)
 150 {
 151          /* we move the channel signaling state machine to UP (answered) */
 152         int spanid = ftdm_channel_get_span_id(channel);
 153         int chanid = ftdm_channel_get_id(channel);
 154         ftdm_log(FTDM_LOG_NOTICE, "-- Requesting answer in channel %d:%d\n", spanid, chanid);
 155         ftdm_channel_call_answer(channel);
 156         schedule_timer(channel, HANGUP_TIMER, send_hangup);
 157 }
 158 
 159 /* send progress for an incoming */
 160 static void send_progress(ftdm_channel_t *channel)
 161 {
 162          /* we move the channel signaling state machine to UP (answered) */
 163         int spanid = ftdm_channel_get_span_id(channel);
 164         int chanid = ftdm_channel_get_id(channel);
 165         ftdm_log(FTDM_LOG_NOTICE, "-- Requesting progress\n", spanid, chanid);
 166         ftdm_channel_call_indicate(channel, FTDM_CHANNEL_INDICATE_PROGRESS);
 167         schedule_timer(channel, ANSWER_TIMER, send_answer);
 168 }
 169 
 170 /* This function will be called in an undetermined signaling thread, you must not do 
 171  * any blocking operations here or the signaling stack may delay other call event processing 
 172  * The arguments for this function are defined in FIO_SIGNAL_CB_FUNCTION prototype, I just
 173  * name them here for your convenience:
 174  * ftdm_sigmsg_t *sigmsg
 175  * - The sigmsg structure contains the ftdm_channel structure that represents the channel where
 176  * the event occurred and the event_id of the signaling event that just occurred.
 177  * */
 178 static FIO_SIGNAL_CB_FUNCTION(on_signaling_event)
 179 {
 180         switch (sigmsg->event_id) {
 181         /* This event signals the start of an incoming call */
 182         case FTDM_SIGEVENT_START:
 183                 ftdm_log(FTDM_LOG_NOTICE, "Incoming call received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id);
 184                 schedule_timer(sigmsg->channel, PROGRESS_TIMER, send_progress);
 185                 break;
 186         /* This event signals progress on an outgoing call */
 187         case FTDM_SIGEVENT_PROGRESS_MEDIA:
 188                 ftdm_log(FTDM_LOG_NOTICE, "Progress message received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id);
 189                 break;
 190         /* This event signals answer in an outgoing call */
 191         case FTDM_SIGEVENT_UP:
 192                 ftdm_log(FTDM_LOG_NOTICE, "Answer received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id);
 193                 /* now the channel is answered and we can use 
 194                  * ftdm_channel_wait() to wait for input/output in a channel (equivalent to poll() or select())
 195                  * ftdm_channel_read() to read available data in a channel
 196                  * ftdm_channel_write() to write to the channel */
 197                 break;
 198         /* This event signals hangup from the other end */
 199         case FTDM_SIGEVENT_STOP:
 200                 ftdm_log(FTDM_LOG_NOTICE, "Hangup received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id);
 201                 if (g_outgoing_channel == sigmsg->channel) {
 202                         g_outgoing_channel = NULL;
 203                 }
 204                 /* release any timer for this channel */
 205                 release_timers(sigmsg->channel);
 206                 /* acknowledge the hangup */
 207                 ftdm_channel_call_hangup(sigmsg->channel);
 208                 break;
 209         default:
 210                 ftdm_log(FTDM_LOG_WARNING, "Unhandled event %s in channel %d:%d\n", ftdm_signal_event2str(sigmsg->event_id), 
 211                                 sigmsg->span_id, sigmsg->chan_id);
 212                 break;
 213         }
 214         return FTDM_SUCCESS;
 215 }
 216 
 217 static void place_call(const ftdm_span_t *span, const char *number)
 218 {
 219         ftdm_channel_t *ftdmchan = NULL;
 220         ftdm_caller_data_t caller_data = {{ 0 }};
 221         ftdm_status_t status = FTDM_FAIL;
 222 
 223         /* set destiny number */
 224         ftdm_set_string(caller_data.dnis.digits, number);
 225 
 226         /* set callerid */
 227         ftdm_set_string(caller_data.cid_name, "testsangomaboost");
 228         ftdm_set_string(caller_data.cid_num.digits, "1234");
 229 
 230         /* request to search for an outgoing channel top down with the given caller data.
 231          * it is also an option to use ftdm_channel_open_by_group to let freetdm hunt
 232          * an available channel in a given group instead of per span
 233          * */
 234         status = ftdm_channel_open_by_span(ftdm_span_get_id(span), FTDM_TOP_DOWN, &caller_data, &ftdmchan);
 235         if (status != FTDM_SUCCESS) {
 236                 ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n");
 237                 return;
 238         }
 239 
 240         g_outgoing_channel = ftdmchan;
 241 
 242         /* set the caller data for the outgoing channel */
 243         ftdm_channel_set_caller_data(ftdmchan, &caller_data);
 244 
 245         status = ftdm_channel_call_place(ftdmchan);
 246         if (status != FTDM_SUCCESS) {
 247                 ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n");
 248                 return;
 249         }
 250 
 251         /* this is required to initialize the outgoing channel */
 252         ftdm_channel_init(ftdmchan);
 253 }
 254 
 255 #define ARRLEN(arr) (sizeof(arr)/sizeof(arr[0]))
 256 int main(int argc, char *argv[])
 257 {
 258         /* span names can be any null-terminated string, does not need to be a wanpipe port */
 259         int span_numbers[32];
 260         char span_names[ARRLEN(span_numbers)][ARRLEN(span_numbers)];
 261         const char *spanname = NULL;
 262         char wpchans[25];
 263         unsigned configured = 0;
 264         int i, spanno;
 265         int numspans = 0;
 266         ftdm_status_t status;
 267         ftdm_span_t *span_list[ARRLEN(span_numbers)];
 268         ftdm_span_t *span;
 269         ftdm_channel_config_t chan_config;
 270         ftdm_conf_parameter_t parameters[20];
 271         char *todial = NULL;
 272         int32_t ticks = 0;
 273 
 274         /* register a handler to shutdown things properly */
 275 #ifdef _WIN64
 276         // still trying to figure this one out otherwise triggers error
 277         if (signal(SIGINT, interrupt_requested) < 0) {
 278 #else
 279         if (signal(SIGINT, interrupt_requested) == SIG_ERR) {
 280 #endif
 281                 fprintf(stderr, "Could not set the SIGINT signal handler: %s\n", strerror(errno));
 282                 exit(-1);
 283         }
 284 
 285         for (i = 1; i < argc; i++) {
 286                 if (argv[i][0] == '-' && argv[i][1] == 'd') {
 287                         i++;
 288                         if (i >= argc) {
 289                                 fprintf(stderr, "Error, -d specified but no number to dial!\n");
 290                                 exit(1);
 291                         }
 292                         todial = argv[i];
 293                         if (!strlen(todial)) {
 294                                 todial = NULL;
 295                         }
 296                         printf("Number to dial: %s\n", todial);
 297                         continue;
 298                 }
 299                 spanno = atoi(argv[i]);
 300                 span_numbers[numspans] = spanno;
 301                 snprintf(span_names[numspans], sizeof(span_names[numspans]), "wanpipe%d", spanno);
 302                 numspans++;
 303         }
 304 
 305         if (!numspans) {
 306                 fprintf(stderr, "please specify a at least 1 wanpipe port number\n");
 307                 exit(-1);
 308         }
 309 
 310         /* clear any outstanding timers */
 311         memset(&g_timers, 0, sizeof(g_timers));
 312 
 313         /* set the logging level to use */
 314         ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
 315 
 316         /* Initialize the FTDM library */
 317         if (ftdm_global_init() != FTDM_SUCCESS) {
 318                 fprintf(stderr, "Error loading FreeTDM\n");
 319                 exit(-1);
 320         }
 321 
 322         /* create the schedule mutex */
 323         ftdm_mutex_create(&g_schedule_mutex);
 324 
 325         /* now we can start creating spans */
 326         memset(&chan_config, 0, sizeof(chan_config));
 327         strncpy(chan_config.group_name, "mygroup", sizeof(chan_config.group_name)-1);
 328         chan_config.group_name[sizeof(chan_config.group_name)-1] = 0;
 329         for (i = 0; i < numspans; i++) {
 330                 spanname = span_names[i];
 331                 /* "wanpipe" is the special I/O identifier for Sangoma devices */
 332                 ftdm_log(FTDM_LOG_NOTICE, "Creating span %s\n", spanname);
 333                 status = ftdm_span_create("wanpipe", spanname, &span_list[i]);
 334                 if (status != FTDM_SUCCESS) {
 335                         ftdm_log(FTDM_LOG_CRIT, "Failed to create span %s\n", spanname);
 336                         goto done;
 337                 }
 338                 span = span_list[i];
 339                 spanno = span_numbers[i];
 340 
 341                 /* set the trunk type for the span */
 342                 ftdm_span_set_trunk_type(span_list[i], FTDM_TRUNK_T1);
 343                 
 344                 /* configure B channels (syntax for wanpipe channels is span:low_chan-high_chan) */
 345                 chan_config.type = FTDM_CHAN_TYPE_B;
 346                 snprintf(wpchans, sizeof(wpchans), "%d:1-23", spanno);
 347                 ftdm_configure_span_channels(span, wpchans, &chan_config, &configured);
 348                 ftdm_log(FTDM_LOG_NOTICE, "registered %d b channels\n", configured);
 349         }
 350 
 351         /* At this point FreeTDM is ready to be used, the spans defined in freetdm.conf have the basic I/O board configuration
 352          * but no telephony signaling configuration at all. */
 353         ftdm_log(FTDM_LOG_NOTICE, "FreeTDM loaded ...\n");
 354 
 355         /* now we can start configuring signaling for the previously created spans */
 356         for (i = 0; i < numspans; i++) {
 357                 spanname = span_names[i];
 358 
 359                 /* Retrieve a span by name (as specified in ftdm_span_create()) */
 360                 if (ftdm_span_find_by_name(spanname, &span) != FTDM_SUCCESS) {
 361                         ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", ftdm_span_get_name(span));
 362                         goto done;
 363                 }
 364 
 365                 /* prepare the configuration parameters that will be sent down to the signaling stack, the array of paramters must be terminated by an 
 366                  * array element with a null .var member */
 367 
 368                 /* for sangoma_boost signaling (abstraction signaling used by Sangoma for PRI, BRI and SS7) the first parameter you must send
 369                  * is sigmod, which must be either sangoma_prid, if you have the PRI stack available, or sangoma_brid for the BRI stack */
 370                 parameters[0].var = "sigmod";   
 371                 parameters[0].val = "sangoma_prid";     
 372 
 373                 /* following parameters are signaling stack specific, this ones are for PRI */
 374                 parameters[1].var = "switchtype";
 375                 parameters[1].val = "national";
 376 
 377                 parameters[2].var = "signalling";
 378                 parameters[2].val = "pri_cpe";
 379 
 380                 /*
 381                  * parameters[3].var = "nfas_primary";
 382                  * parameters[3].val = "4"; //span number
 383                  *
 384                  * parameters[4].var = "nfas_secondary";
 385                  * parameters[4].val = "2"; //span number
 386                  *
 387                  * parameters[5].var = "nfas_group";
 388                  * parameters[5].val = "1";
 389                  * */
 390 
 391 
 392                 /* the last parameter .var member must be NULL! */
 393                 parameters[3].var = NULL;
 394 
 395                 /* send the configuration values down to the stack */
 396                 if (ftdm_configure_span_signaling(span, "sangoma_boost", on_signaling_event, parameters) != FTDM_SUCCESS) {
 397                         ftdm_log(FTDM_LOG_ERROR, "Error configuring sangoma_boost signaling abstraction in span %s\n", ftdm_span_get_name(span));
 398                         goto done;
 399                 }
 400 
 401         }
 402 
 403 
 404         /* configuration succeeded, we can proceed now to start each span
 405          * This step will launch at least 1 background (may be more, depending on the signaling stack used)
 406          * to handle *ALL* signaling events for this span, your on_signaling_event callback will be called always
 407          * in one of those infraestructure threads and you MUST NOT block in that handler to avoid delays and errors 
 408          * in the signaling processing for any call.
 409          * */
 410         for (i = 0; i < numspans; i++) {
 411                 spanname = span_names[i];
 412                 /* Retrieve a span by name (as specified in ftdm_span_create()) */
 413                 if (ftdm_span_find_by_name(spanname, &span) != FTDM_SUCCESS) {
 414                         ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", ftdm_span_get_name(span));
 415                         goto done;
 416                 }
 417 
 418                 if (ftdm_span_start(span) != FTDM_SUCCESS) {
 419                         ftdm_log(FTDM_LOG_ERROR, "Failing starting signaling on span %s\n", ftdm_span_get_name(span));
 420                         goto done;
 421                 }
 422 
 423         }
 424 
 425         app_running = 1;
 426         
 427         /* Retrieve the first created span to place the call (if dialing was specified) */
 428         if (ftdm_span_find(1, &span) != FTDM_SUCCESS) {
 429                 ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span 1\n");
 430                 goto done;
 431         }
 432 
 433         /* The application thread can go on and do anything else, like waiting for a shutdown signal */
 434         while(ftdm_running() && app_running) {
 435                 ftdm_sleep(1000);
 436                 run_timers();
 437                 ticks++;
 438                 if (!(ticks % 10) && todial && !g_outgoing_channel) {
 439                         ftdm_log(FTDM_LOG_NOTICE, "Originating call to number %s\n", todial);
 440                         place_call(span, todial);
 441                 }
 442         }
 443 
 444  done:
 445 
 446         ftdm_log(FTDM_LOG_NOTICE, "Shutting down FreeTDM ...\n");
 447 
 448         ftdm_mutex_destroy(&g_schedule_mutex);
 449 
 450         /* whenever you're done, this function will shutdown the signaling threads in any span that was started */
 451         ftdm_global_destroy();
 452 
 453         printf("Terminated!\n");
 454 
 455         sleep(2);
 456 
 457         exit(0);
 458 }
 459 
 460 /* For Emacs:
 461  * Local Variables:
 462  * mode:c
 463  * indent-tabs-mode:t
 464  * tab-width:4
 465  * c-basic-offset:4
 466  * End:
 467  * For VIM:
 468  * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
 469  */

/* [<][>][^][v][top][bottom][index][help] */