root/src/testsangomaboost.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. media_thread
  11. 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: boostsample <span name>
  38  * The span name must be a valid span defined in freetdm.conf
  39  * compile this program linking to the freetdm library (ie -lfreetdm)
  40  **/
  41 
  42 #ifndef __linux__
  43 #define _CRT_SECURE_NO_WARNINGS 1
  44 #endif
  45 
  46 #include <signal.h>
  47 
  48 #include "freetdm.h"
  49 #include <stdlib.h>
  50 #include <string.h>
  51 #include <errno.h>
  52 
  53 
  54 /* arbitrary limit for max calls in this sample program */
  55 #define MAX_CALLS 255
  56 
  57 /* some timers (in seconds) to fake responses in incoming calls */
  58 #define PROGRESS_TIMER 1
  59 #define ANSWER_TIMER 5
  60 #define HANGUP_TIMER 15
  61 
  62 /* simple variable used to stop the application */
  63 static int app_running = 0;
  64 
  65 typedef void (*expired_function_t)(ftdm_channel_t *channel);
  66 typedef struct dummy_timer_s {
  67         int time;
  68         ftdm_channel_t *channel;
  69         expired_function_t expired;
  70 } dummy_timer_t;
  71 
  72 /* dummy second resolution timers */
  73 static dummy_timer_t g_timers[MAX_CALLS];
  74 
  75 /* mutex to protect the timers (both, the test thread and the signaling thread may modify them) */
  76 static ftdm_mutex_t *g_schedule_mutex;
  77 
  78 /* mutex to protect the channel */
  79 static ftdm_mutex_t *g_channel_mutex;
  80 
  81 /* unique outgoing channel */
  82 static ftdm_channel_t *g_outgoing_channel = NULL;
  83 
  84 static void interrupt_requested(int signal)
  85 {
  86         app_running = 0;
  87 }
  88 
  89 static void schedule_timer(ftdm_channel_t *channel, int sec, expired_function_t expired)
  90 {
  91         int i;
  92         ftdm_mutex_lock(g_schedule_mutex);
  93         for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) {
  94                 /* check the timer slot is free to use */
  95                 if (!g_timers[i].time) {
  96                         g_timers[i].time = sec;
  97                         g_timers[i].channel = channel;
  98                         g_timers[i].expired = expired;
  99                         ftdm_mutex_unlock(g_schedule_mutex);
 100                         return;
 101                 }
 102         }
 103         ftdm_log(FTDM_LOG_ERROR, "Failed to schedule timer\n");
 104         ftdm_mutex_unlock(g_schedule_mutex);
 105 }
 106 
 107 static void run_timers(void)
 108 {
 109         int i;
 110         void *channel;
 111         expired_function_t expired_func = NULL;
 112         ftdm_mutex_lock(g_schedule_mutex);
 113         for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) {
 114                 /* if there's time left, decrement */
 115                 if (g_timers[i].time) {
 116                         g_timers[i].time--;
 117                 }
 118 
 119                 /* if time expired and we have an expired function, call it */
 120                 if (!g_timers[i].time && g_timers[i].expired) {
 121                         expired_func = g_timers[i].expired;
 122                         channel = g_timers[i].channel;
 123                         memset(&g_timers[i], 0, sizeof(g_timers[i]));
 124                         expired_func(channel);
 125                 }
 126         }
 127         ftdm_mutex_unlock(g_schedule_mutex);
 128 }
 129 
 130 static void release_timers(ftdm_channel_t *channel)
 131 {
 132         int i;
 133         ftdm_mutex_lock(g_schedule_mutex);
 134         for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) {
 135                 /* clear any timer belonging to the given channel */
 136                 if (g_timers[i].channel == channel) {
 137                         memset(&g_timers[i], 0, sizeof(g_timers[i]));
 138                 }
 139         }
 140         ftdm_mutex_unlock(g_schedule_mutex);
 141 }
 142 
 143 /*  hangup the call */ 
 144 static void send_hangup(ftdm_channel_t *channel)
 145 {
 146         char dtmfbuff[100];
 147         int rc;
 148         int spanid = ftdm_channel_get_span_id(channel);
 149         int chanid = ftdm_channel_get_id(channel);
 150         rc = ftdm_channel_dequeue_dtmf(channel, dtmfbuff, sizeof(dtmfbuff));
 151         if (rc) {
 152                 ftdm_log(FTDM_LOG_NOTICE, "Not hanging up channel %d:%d because has DTMF: %s\n", spanid, chanid, dtmfbuff);
 153                 schedule_timer(channel, HANGUP_TIMER, send_hangup);
 154                 return;
 155         }
 156         ftdm_log(FTDM_LOG_NOTICE, "-- Requesting hangup in channel %d:%d\n", spanid, chanid);
 157         ftdm_channel_call_hangup(channel);
 158 }
 159 
 160 /*  send answer for an incoming call */ 
 161 static void send_answer(ftdm_channel_t *channel)
 162 {
 163          /* we move the channel signaling state machine to UP (answered) */
 164         int spanid = ftdm_channel_get_span_id(channel);
 165         int chanid = ftdm_channel_get_id(channel);
 166         ftdm_log(FTDM_LOG_NOTICE, "-- Requesting answer in channel %d:%d\n", spanid, chanid);
 167         ftdm_channel_call_answer(channel);
 168         schedule_timer(channel, HANGUP_TIMER, send_hangup);
 169 }
 170 
 171 /* send progress for an incoming */
 172 static void send_progress(ftdm_channel_t *channel)
 173 {
 174          /* we move the channel signaling state machine to UP (answered) */
 175         int spanid = ftdm_channel_get_span_id(channel);
 176         int chanid = ftdm_channel_get_id(channel);
 177         ftdm_log(FTDM_LOG_NOTICE, "-- Requesting progress\n", spanid, chanid);
 178         ftdm_channel_call_indicate(channel, FTDM_CHANNEL_INDICATE_PROGRESS);
 179         schedule_timer(channel, ANSWER_TIMER, send_answer);
 180 }
 181 
 182 /* This function will be called in an undetermined signaling thread, you must not do 
 183  * any blocking operations here or the signaling stack may delay other call event processing 
 184  * The arguments for this function are defined in FIO_SIGNAL_CB_FUNCTION prototype, I just
 185  * name them here for your convenience:
 186  * ftdm_sigmsg_t *sigmsg
 187  * - The sigmsg structure contains the ftdm_channel structure that represents the channel where
 188  * the event occurred and the event_id of the signaling event that just occurred.
 189  * */
 190 static FIO_SIGNAL_CB_FUNCTION(on_signaling_event)
 191 {
 192         switch (sigmsg->event_id) {
 193         /* This event signals the start of an incoming call */
 194         case FTDM_SIGEVENT_START:
 195                 ftdm_log(FTDM_LOG_NOTICE, "Incoming call received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id);
 196                 schedule_timer(sigmsg->channel, PROGRESS_TIMER, send_progress);
 197                 break;
 198         /* This event signals progress on an outgoing call */
 199         case FTDM_SIGEVENT_PROGRESS_MEDIA:
 200                 ftdm_log(FTDM_LOG_NOTICE, "Progress message received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id);
 201                 break;
 202         /* This event signals answer in an outgoing call */
 203         case FTDM_SIGEVENT_UP:
 204                 ftdm_log(FTDM_LOG_NOTICE, "Answer received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id);
 205                 ftdm_channel_command(sigmsg->channel, FTDM_COMMAND_ENABLE_DTMF_DETECT, NULL);
 206                 /* now the channel is answered and we can use 
 207                  * ftdm_channel_wait() to wait for input/output in a channel (equivalent to poll() or select())
 208                  * ftdm_channel_read() to read available data in a channel
 209                  * ftdm_channel_write() to write to the channel */
 210                 break;
 211         /* This event signals hangup from the other end */
 212         case FTDM_SIGEVENT_STOP:
 213                 ftdm_log(FTDM_LOG_NOTICE, "Hangup received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id);
 214                 ftdm_mutex_lock(g_channel_mutex);
 215                 if (g_outgoing_channel == sigmsg->channel) {
 216                         g_outgoing_channel = NULL;
 217                 }
 218                 ftdm_mutex_unlock(g_channel_mutex);
 219                 /* release any timer for this channel */
 220                 release_timers(sigmsg->channel);
 221                 /* acknowledge the hangup */
 222                 ftdm_channel_call_hangup(sigmsg->channel);
 223                 break;
 224         default:
 225                 ftdm_log(FTDM_LOG_WARNING, "Unhandled event %s in channel %d:%d\n", ftdm_signal_event2str(sigmsg->event_id), 
 226                                 sigmsg->span_id, sigmsg->chan_id);
 227                 break;
 228         }
 229         return FTDM_SUCCESS;
 230 }
 231 
 232 static void place_call(const ftdm_span_t *span, const char *number)
 233 {
 234         ftdm_channel_t *ftdmchan = NULL;
 235         ftdm_caller_data_t caller_data = {{ 0 }};
 236         ftdm_status_t status = FTDM_FAIL;
 237 
 238         /* set destiny number */
 239         ftdm_set_string(caller_data.dnis.digits, number);
 240 
 241         /* set callerid */
 242         ftdm_set_string(caller_data.cid_name, "testsangomaboost");
 243         ftdm_set_string(caller_data.cid_num.digits, "1234");
 244 
 245         /* request to search for an outgoing channel top down with the given caller data.
 246          * it is also an option to use ftdm_channel_open_by_group to let freetdm hunt
 247          * an available channel in a given group instead of per span
 248          * */
 249         status = ftdm_channel_open_by_span(ftdm_span_get_id(span), FTDM_TOP_DOWN, &caller_data, &ftdmchan);
 250         if (status != FTDM_SUCCESS) {
 251                 ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n");
 252                 return;
 253         }
 254 
 255         ftdm_mutex_lock(g_channel_mutex);
 256         g_outgoing_channel = ftdmchan;
 257         ftdm_mutex_unlock(g_channel_mutex);
 258 
 259         /* set the caller data for the outgoing channel */
 260         ftdm_channel_set_caller_data(ftdmchan, &caller_data);
 261 
 262         status = ftdm_channel_call_place(ftdmchan);
 263         if (status != FTDM_SUCCESS) {
 264                 ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n");
 265                 return;
 266         }
 267 
 268         /* this is required to initialize the outgoing channel */
 269         ftdm_channel_init(ftdmchan);
 270 }
 271 
 272 static void *media_thread(ftdm_thread_t *th, void *data)
 273 {
 274         /* The application thread can go on and do anything else, like waiting for a shutdown signal */
 275         ftdm_wait_flag_t flags = FTDM_NO_FLAGS;
 276         ftdm_status_t status;
 277         ftdm_channel_t *chan;
 278         char iobuff[160];
 279         char dnis_str[] = "1234";
 280         int tx_dtmf = 0;
 281         ftdm_size_t datalen = 0;
 282         memset(iobuff, 0, sizeof(iobuff));
 283         while(ftdm_running() && app_running) {
 284                 ftdm_mutex_lock(g_channel_mutex);
 285                 chan = g_outgoing_channel;
 286                 ftdm_mutex_unlock(g_channel_mutex);
 287                 if (chan && tx_dtmf) {
 288                         flags = FTDM_WRITE | FTDM_READ;
 289                         ftdm_channel_wait(chan, &flags, 100);
 290                         if (flags & FTDM_WRITE) {
 291                                 datalen = sizeof(iobuff);
 292                                 status = ftdm_channel_write(chan, iobuff, datalen, &datalen);
 293                                 if (status != FTDM_SUCCESS) {
 294                                         ftdm_log(FTDM_LOG_ERROR, "writing to channel failed\n");
 295                                 }
 296                         }
 297                         if (flags & FTDM_READ) {
 298                                 datalen = sizeof(iobuff);
 299                                 status = ftdm_channel_read(chan, iobuff, &datalen);
 300                                 if (status != FTDM_SUCCESS) {
 301                                         ftdm_log(FTDM_LOG_ERROR, "reading from channel failed\n");
 302                                 }
 303                         }
 304                 } else if (chan && ftdm_channel_call_check_answered(chan)) {
 305                         ftdm_log(FTDM_LOG_NOTICE, "Transmitting DNIS %s\n", dnis_str);
 306                         ftdm_channel_command(g_outgoing_channel, FTDM_COMMAND_SEND_DTMF, dnis_str);
 307                         tx_dtmf = 1;
 308                 } else {
 309                         tx_dtmf = 0;
 310                         ftdm_sleep(100);
 311                 }
 312         }
 313         printf("Shutting down media thread ...\n");
 314         return NULL;
 315 }
 316 
 317 int main(int argc, char *argv[])
 318 {
 319         ftdm_conf_parameter_t parameters[20];
 320         ftdm_span_t *span;
 321         char *todial = NULL;
 322         const char *sigtype = NULL;
 323         int32_t ticks = 0;
 324 
 325         if (argc < 3) {
 326                 fprintf(stderr, "Usage: %s <span name> <cpe|net> [number to dial if any]\n", argv[0]);
 327                 exit(-1);
 328         }
 329 
 330         /* register a handler to shutdown things properly */
 331 #ifdef _WIN64
 332         // still trying to figure this one out otherwise triggers error
 333         if (signal(SIGINT, interrupt_requested) < 0) {
 334 #else
 335         if (signal(SIGINT, interrupt_requested) == SIG_ERR) {
 336 #endif
 337                 fprintf(stderr, "Could not set the SIGINT signal handler: %s\n", strerror(errno));
 338                 exit(-1);
 339         }
 340 
 341         if (!strcasecmp(argv[2], "cpe")) {
 342                 sigtype = "pri_cpe";
 343         } else if (!strcasecmp(argv[2], "net")) {
 344                 sigtype = "pri_net";
 345         } else {
 346                 fprintf(stderr, "Valid signaling types are cpe and net only\n");
 347                 exit(-1);
 348         }
 349         printf("Using signalling %s\n", sigtype);
 350 
 351         if (argc >= 4) {
 352                 todial = argv[3];
 353                 if (!strlen(todial)) {
 354                         todial = NULL;
 355                 }
 356         }
 357 
 358         /* clear any outstanding timers */
 359         memset(&g_timers, 0, sizeof(g_timers));
 360 
 361         /* set the logging level to use */
 362         ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
 363 
 364         /* Initialize the FTDM library */
 365         if (ftdm_global_init() != FTDM_SUCCESS) {
 366                 fprintf(stderr, "Error loading FreeTDM\n");
 367                 exit(-1);
 368         }
 369 
 370         /* create the schedule and channel mutex */
 371         ftdm_mutex_create(&g_schedule_mutex);
 372         ftdm_mutex_create(&g_channel_mutex);
 373 
 374         /* Load the FreeTDM configuration */
 375         if (ftdm_global_configuration() != FTDM_SUCCESS) {
 376                 fprintf(stderr, "Error configuring FreeTDM\n");
 377                 exit(-1);
 378         }
 379 
 380         /* At this point FreeTDM is ready to be used, the spans defined in freetdm.conf have the basic I/O board configuration
 381          * but no telephony signaling configuration at all. */
 382         printf("FreeTDM loaded ...\n");
 383 
 384         /* Retrieve a span by name (according to freetdm.conf) */
 385         if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) {
 386                 fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]);
 387                 goto done;
 388         }
 389 
 390         /* prepare the configuration parameters that will be sent down to the signaling stack, the array of paramters must be terminated by an 
 391          * array element with a null .var member */
 392 
 393         /* for sangoma_boost signaling (abstraction signaling used by Sangoma for PRI, BRI and SS7) the first parameter you must send
 394          * is sigmod, which must be either sangoma_prid, if you have the PRI stack available, or sangoma_brid for the BRI stack */
 395         parameters[0].var = "sigmod";   
 396         parameters[0].val = "sangoma_prid";     
 397 
 398         /* following parameters are signaling stack specific, this ones are for PRI */
 399         parameters[1].var = "switchtype";
 400         parameters[1].val = "national";
 401 
 402         parameters[2].var = "signalling";
 403         parameters[2].val = sigtype;
 404 
 405         /*
 406          * parameters[3].var = "nfas_primary";
 407          * parameters[3].val = "4"; //span number
 408          *
 409          * parameters[4].var = "nfas_secondary";
 410          * parameters[4].val = "2"; //span number
 411          *
 412          * parameters[5].var = "nfas_group";
 413          * parameters[5].val = "1";
 414          * */
 415 
 416         /* the last parameter .var member must be NULL! */
 417         parameters[3].var = NULL;
 418 
 419         /* send the configuration values down to the stack */
 420         if (ftdm_configure_span_signaling(span, "sangoma_boost", on_signaling_event, parameters) != FTDM_SUCCESS) {
 421                 fprintf(stderr, "Error configuring sangoma_boost signaling abstraction in span %s\n", ftdm_span_get_name(span));
 422                 goto done;
 423         }
 424 
 425         /* configuration succeeded, we can proceed now to start the span
 426          * This step will launch at least 1 background (may be more, depending on the signaling stack used)
 427          * to handle *ALL* signaling events for this span, your on_signaling_event callback will be called always
 428          * in one of those infraestructure threads and you MUST NOT block in that handler to avoid delays and errors 
 429          * in the signaling processing for any call.
 430          * */
 431         ftdm_span_start(span);
 432 
 433         if (ftdm_thread_create_detached(media_thread, NULL) != FTDM_SUCCESS){
 434                 fprintf(stderr, "Error launching media thread\n");
 435                 goto done;
 436         }
 437 
 438         app_running = 1;
 439 
 440         /* The application thread can go on and do anything else, like waiting for a shutdown signal */
 441         while(ftdm_running() && app_running) {
 442                 ftdm_sleep(1000);
 443                 run_timers();
 444                 ticks++;
 445                 if (!(ticks % 10) && todial && !g_outgoing_channel) {
 446                         ftdm_log(FTDM_LOG_NOTICE, "Originating call to number %s\n", todial);
 447                         place_call(span, todial);
 448                 }
 449         }
 450         printf("Shutting down FreeTDM ...\n");
 451  done:
 452 
 453         ftdm_mutex_destroy(&g_schedule_mutex);
 454         ftdm_mutex_destroy(&g_channel_mutex);
 455 
 456         /* whenever you're done, this function will shutdown the signaling threads in any span that was started */
 457         ftdm_global_destroy();
 458 
 459         return 0;
 460 }
 461 
 462 /* For Emacs:
 463  * Local Variables:
 464  * mode:c
 465  * indent-tabs-mode:t
 466  * tab-width:4
 467  * c-basic-offset:4
 468  * End:
 469  * For VIM:
 470  * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
 471  */

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