root/src/ftmod/ftmod_r2/ftmod_r2.c

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

DEFINITIONS

This source file includes following definitions.
  1. ft_r2_clean_call
  2. ft_r2_accept_call
  3. ft_r2_answer_call
  4. FIO_CHANNEL_OUTGOING_CALL_FUNCTION
  5. ftdm_r2_start
  6. ftdm_r2_on_call_init
  7. ftdm_r2_on_call_offered
  8. ftdm_r2_on_call_accepted
  9. ftdm_r2_on_call_answered
  10. ftdm_r2_on_call_disconnect
  11. ftdm_r2_on_call_end
  12. ftdm_r2_on_call_read
  13. ftdm_r2_on_hardware_alarm
  14. ftdm_r2_on_os_error
  15. ftdm_r2_on_protocol_error
  16. ftdm_r2_on_line_blocked
  17. ftdm_r2_on_line_idle
  18. ftdm_r2_write_log
  19. ftdm_r2_on_context_log
  20. ftdm_r2_on_chan_log
  21. ftdm_r2_on_dnis_digit_received
  22. ftdm_r2_on_ani_digit_received
  23. ftdm_r2_io_set_cas
  24. ftdm_r2_io_get_cas
  25. ftdm_r2_io_flush_write_buffers
  26. ftdm_r2_io_write
  27. ftdm_r2_io_read
  28. ftdm_r2_io_wait
  29. ftdm_r2_io_open
  30. ftdm_r2_io_close
  31. ftdm_r2_io_setup
  32. ftdm_r2_io_get_oob_event
  33. FIO_SIG_CONFIGURE_FUNCTION
  34. ftdm_r2_channel_run
  35. ftdm_r2_run
  36. FIO_API_FUNCTION
  37. FIO_IO_LOAD_FUNCTION
  38. FIO_SIG_LOAD_FUNCTION
  39. FIO_SIG_UNLOAD_FUNCTION

   1 /*
   2  * Copyright (c) 2009, Moises Silva <moy@sangoma.com>
   3  * All rights reserved.
   4  * 
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 
   9  * * Redistributions of source code must retain the above copyright
  10  * notice, this list of conditions and the following disclaimer.
  11  * 
  12  * * Redistributions in binary form must reproduce the above copyright
  13  * notice, this list of conditions and the following disclaimer in the
  14  * documentation and/or other materials provided with the distribution.
  15  * 
  16  * * Neither the name of the original author; nor the names of any contributors
  17  * may be used to endorse or promote products derived from this software
  18  * without specific prior written permission.
  19  * 
  20  * 
  21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
  25  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  28  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 
  34 #include <stdio.h>
  35 #include <openr2.h>
  36 #include "private/ftdm_core.h"
  37 
  38 /* debug thread count for r2 legs */
  39 static ftdm_mutex_t* g_thread_count_mutex;
  40 static int32_t g_thread_count = 0;
  41 
  42 /* when the users kills a span we clear this flag to kill the signaling thread */
  43 /* FIXME: what about the calls that are already up-and-running? */
  44 typedef enum {
  45         FTDM_R2_RUNNING = (1 << 0),
  46 } ftdm_r2_flag_t;
  47 
  48 /* private call information stored in ftdmchan->call_data void* ptr */
  49 #define R2CALL(ftdmchan) ((ftdm_r2_call_t*)((ftdmchan)->call_data))
  50 typedef struct ftdm_r2_call_t {
  51     openr2_chan_t *r2chan;
  52         int accepted:1;
  53         int answer_pending:1;
  54         int state_ack_pending:1;
  55         int disconnect_rcvd:1;
  56         int ftdm_started:1;
  57         ftdm_channel_state_t chanstate;
  58         ftdm_size_t dnis_index;
  59         ftdm_size_t ani_index;
  60         char name[10];
  61 } ftdm_r2_call_t;
  62 
  63 /* this is just used as place holder in the stack when configuring the span to avoid using bunch of locals */
  64 typedef struct ft_r2_conf_s {
  65         /* openr2 types */
  66         openr2_variant_t variant;
  67         openr2_calling_party_category_t category;
  68         openr2_log_level_t loglevel;
  69 
  70         /* strings */
  71         char *logdir;
  72         char *advanced_protocol_file;
  73 
  74         /* ints */
  75         int32_t max_ani;
  76         int32_t max_dnis;
  77         int32_t mfback_timeout; 
  78         int32_t metering_pulse_timeout;
  79 
  80         /* booleans */
  81         int immediate_accept;
  82         int skip_category;
  83         int get_ani_first;
  84         int call_files;
  85         int double_answer;
  86         int charge_calls;
  87         int forced_release;
  88         int allow_collect_calls;
  89 } ft_r2_conf_t;
  90 
  91 /* r2 configuration stored in span->signal_data */
  92 typedef struct ftdm_r2_data_s {
  93         /* span flags */
  94         ftdm_r2_flag_t flags;
  95         /* openr2 handle for the R2 variant context */
  96         openr2_context_t *r2context;
  97         /* category to use when making calls */
  98         openr2_calling_party_category_t category;
  99         /* whether to use OR2_CALL_WITH_CHARGE or OR2_CALL_NO_CHARGE when accepting a call */
 100         int charge_calls:1;
 101         /* allow or reject collect calls */
 102         int allow_collect_calls:1;
 103         /* whether to use forced release when hanging up */
 104         int forced_release:1;
 105         /* whether accept the call when offered, or wait until the user decides to accept */
 106         int accept_on_offer:1;
 107 } ftdm_r2_data_t;
 108 
 109 /* one element per span will be stored in g_mod_data_hash global var to keep track of them
 110    and destroy them on module unload */
 111 typedef struct ftdm_r2_span_pvt_s {
 112         openr2_context_t *r2context; /* r2 context allocated for this span */
 113         ftdm_hash_t *r2calls; /* hash table of allocated call data per channel for this span */
 114 } ftdm_r2_span_pvt_t;
 115 
 116 /* span monitor thread */
 117 static void *ftdm_r2_run(ftdm_thread_t *me, void *obj);
 118 
 119 /* channel monitor thread */
 120 static void *ftdm_r2_channel_run(ftdm_thread_t *me, void *obj);
 121 
 122 /* hash of all the private span allocations
 123    we need to keep track of them to destroy them when unloading the module 
 124    since freetdm does not notify signaling modules when destroying a span
 125    span -> ftdm_r2_mod_allocs_t */
 126 static ftdm_hash_t *g_mod_data_hash;
 127 
 128 /* IO interface for the command API */
 129 static ftdm_io_interface_t g_ftdm_r2_interface;
 130 
 131 static void ft_r2_clean_call(ftdm_r2_call_t *call)
 132 {
 133     openr2_chan_t *r2chan = call->r2chan;
 134     memset(call, 0, sizeof(*call));
 135     call->r2chan = r2chan;
 136 }
 137 
 138 static void ft_r2_accept_call(ftdm_channel_t *ftdmchan)
 139 {
 140         openr2_chan_t *r2chan = R2CALL(ftdmchan)->r2chan;
 141         // FIXME: not always accept as no charge, let the user decide that
 142         // also we should check the return code from openr2_chan_accept_call and handle error condition
 143         // hanging up the call with protocol error as the reason, this openr2 API will fail only when there something
 144         // wrong at the I/O layer or the library itself
 145         openr2_chan_accept_call(r2chan, OR2_CALL_NO_CHARGE);
 146         R2CALL(ftdmchan)->accepted = 1;
 147 }
 148 
 149 static void ft_r2_answer_call(ftdm_channel_t *ftdmchan)
 150 {
 151         openr2_chan_t *r2chan = R2CALL(ftdmchan)->r2chan;
 152         // FIXME
 153         // 1. check openr2_chan_answer_call return code
 154         // 2. The openr2_chan_answer_call_with_mode should be used depending on user settings
 155         // openr2_chan_answer_call_with_mode(r2chan, OR2_ANSWER_SIMPLE);
 156         openr2_chan_answer_call(r2chan);
 157         R2CALL(ftdmchan)->answer_pending = 0;
 158 }
 159 
 160 static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call)
 161 {
 162         ftdm_status_t status;
 163         ftdm_mutex_lock(ftdmchan->mutex);
 164 
 165         /* the channel may be down but the thread not quite done */
 166         ftdm_wait_for_flag_cleared(ftdmchan, FTDM_CHANNEL_INTHREAD, 200);
 167 
 168         if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) {
 169                 ftdm_log(FTDM_LOG_ERROR, "%d:%d Yay! R2 outgoing call in channel that is already in thread.\n", 
 170                                 ftdmchan->span_id, ftdmchan->chan_id);
 171                 ftdm_mutex_unlock(ftdmchan->mutex);
 172                 return FTDM_FAIL;
 173         }
 174 
 175         ft_r2_clean_call(ftdmchan->call_data);
 176         R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
 177         ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DIALING);
 178         ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND);
 179         R2CALL(ftdmchan)->ftdm_started = 1;
 180         ftdm_mutex_unlock(ftdmchan->mutex);
 181 
 182         status = ftdm_thread_create_detached(ftdm_r2_channel_run, ftdmchan);
 183         if (status == FTDM_FAIL) {
 184                 ftdm_log(FTDM_LOG_ERROR, "%d:%d Cannot handle request to start call in channel, failed to create thread!\n", 
 185                                 ftdmchan->span_id, ftdmchan->chan_id);
 186                 ftdm_channel_done(ftdmchan);
 187                 return FTDM_FAIL;
 188         }
 189 
 190         return FTDM_SUCCESS;
 191 }
 192 
 193 static ftdm_status_t ftdm_r2_start(ftdm_span_t *span)
 194 {
 195         ftdm_r2_data_t *r2_data = span->signal_data;
 196         ftdm_set_flag(r2_data, FTDM_R2_RUNNING);
 197         return ftdm_thread_create_detached(ftdm_r2_run, span);
 198 }
 199 
 200 /* always called from the monitor thread */
 201 static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
 202 {
 203         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 204         ftdm_status_t status;
 205         ftdm_log(FTDM_LOG_NOTICE, "Received request to start call on chan %d\n", openr2_chan_get_number(r2chan));
 206 
 207         ftdm_mutex_lock(ftdmchan->mutex);
 208 
 209         if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) {
 210                 ftdm_log(FTDM_LOG_ERROR, "Cannot handle request to start call in channel %d, invalid state (%d)\n", 
 211                                 openr2_chan_get_number(r2chan), ftdmchan->state);
 212                 ftdm_mutex_unlock(ftdmchan->mutex);
 213                 return;
 214         }
 215 
 216         /* the channel may be down but the thread not quite done */
 217         ftdm_wait_for_flag_cleared(ftdmchan, FTDM_CHANNEL_INTHREAD, 200);
 218 
 219         if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) {
 220                 ftdm_log(FTDM_LOG_ERROR, "Cannot handle request to start call in channel %d, already in thread!\n", 
 221                                 openr2_chan_get_number(r2chan));
 222                 ftdm_mutex_unlock(ftdmchan->mutex);
 223                 return;
 224         }
 225         ft_r2_clean_call(ftdmchan->call_data);
 226         R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
 227         ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT);
 228         ftdm_mutex_unlock(ftdmchan->mutex);
 229 
 230         status = ftdm_thread_create_detached(ftdm_r2_channel_run, ftdmchan);
 231         if (status == FTDM_FAIL) {
 232                 ftdm_log(FTDM_LOG_ERROR, "Cannot handle request to start call in channel %d, failed to create thread!\n", 
 233                                 openr2_chan_get_number(r2chan));
 234         }
 235 }
 236 
 237 /* only called for incoming calls when the ANI, DNIS etc is complete and the user has to decide either to accept or reject the call */
 238 static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category)
 239 {
 240         ftdm_sigmsg_t sigev;
 241         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 242 
 243         ftdm_log(FTDM_LOG_NOTICE, "Call offered on chan %d, ANI = %s, DNIS = %s, Category = %s\n", openr2_chan_get_number(r2chan), 
 244                         ani, dnis, openr2_proto_get_category_string(category));
 245 
 246         /* notify the user about the new call */
 247         memset(&sigev, 0, sizeof(sigev));
 248         sigev.chan_id = ftdmchan->chan_id;
 249         sigev.span_id = ftdmchan->span_id;
 250         sigev.channel = ftdmchan;
 251         sigev.event_id = FTDM_SIGEVENT_START;
 252 
 253         if (ftdm_span_send_signal(ftdmchan->span, &sigev) != FTDM_SUCCESS) {
 254                 ftdm_log(FTDM_LOG_NOTICE, "Failed to handle call offered on chan %d\n", openr2_chan_get_number(r2chan));
 255                 openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER);
 256                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_CANCEL);
 257                 return; 
 258         }
 259         ftdm_channel_use(ftdmchan);
 260         R2CALL(ftdmchan)->ftdm_started = 1; 
 261 }
 262 
 263 static void ftdm_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode)
 264 {
 265         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 266         ftdm_log(FTDM_LOG_NOTICE, "Call accepted on chan %d\n", openr2_chan_get_number(r2chan));
 267         /* at this point the MF signaling has ended and there is no point on keep reading */
 268         openr2_chan_disable_read(r2chan);
 269         if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) {
 270                 R2CALL(ftdmchan)->state_ack_pending = 1;
 271                 if (R2CALL(ftdmchan)->answer_pending) {
 272                         ftdm_log(FTDM_LOG_DEBUG, "Answer was pending on chan %d, answering now.\n", openr2_chan_get_number(r2chan));
 273                         ft_r2_answer_call(ftdmchan);
 274                         return;
 275                 }
 276         } else {
 277                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS);
 278         }
 279 }
 280 
 281 static void ftdm_r2_on_call_answered(openr2_chan_t *r2chan)
 282 {
 283         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 284         ftdm_log(FTDM_LOG_NOTICE, "Call answered on chan %d\n", openr2_chan_get_number(r2chan));
 285         /* notify the upper layer of progress in the outbound call */
 286         if (OR2_DIR_FORWARD == openr2_chan_get_direction(r2chan)) {
 287                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP);
 288         }
 289 }
 290 
 291 /* may be called in the signaling or media thread depending on whether the hangup is product of MF or CAS signaling */
 292 static void ftdm_r2_on_call_disconnect(openr2_chan_t *r2chan, openr2_call_disconnect_cause_t cause)
 293 {
 294         ftdm_sigmsg_t sigev;
 295         ftdm_r2_data_t *r2data;
 296         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 297         ftdm_log(FTDM_LOG_NOTICE, "Call disconnected on chan %d\n", openr2_chan_get_number(r2chan));
 298 
 299         ftdm_log(FTDM_LOG_DEBUG, "Got openr2 disconnection, clearing call on channel %d\n", ftdmchan->physical_chan_id);
 300 
 301         R2CALL(ftdmchan)->disconnect_rcvd = 1;
 302 
 303         /* acknowledge the hangup, cause will be ignored. From here to -> HANGUP once the freetdm side hangs up as well */
 304         openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
 305 
 306         /* if the call has not been started yet we must go to HANGUP right here */ 
 307         if (!R2CALL(ftdmchan)->ftdm_started) {
 308                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
 309                 return;
 310         }
 311 
 312         /* FIXME: use the cause received from openr2 and map it to ftdm cause  */
 313         ftdmchan->caller_data.hangup_cause  = FTDM_CAUSE_NORMAL_CLEARING; 
 314 
 315         /* notify the user of the call terminating */
 316         memset(&sigev, 0, sizeof(sigev));
 317         sigev.chan_id = ftdmchan->chan_id;
 318         sigev.span_id = ftdmchan->span_id;
 319         sigev.channel = ftdmchan;
 320         sigev.event_id = FTDM_SIGEVENT_STOP;
 321         r2data = ftdmchan->span->signal_data;
 322 
 323         ftdm_span_send_signal(ftdmchan->span, &sigev);
 324 }
 325 
 326 static void ftdm_r2_on_call_end(openr2_chan_t *r2chan)
 327 {
 328         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 329         ftdm_log(FTDM_LOG_NOTICE, "Call finished on chan %d\n", openr2_chan_get_number(r2chan));
 330         /* this means the freetdm side disconnected the call, therefore we must move to DOWN here */
 331         if (!R2CALL(ftdmchan)->disconnect_rcvd) {
 332                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
 333                 return;
 334         }
 335 }
 336 
 337 static void ftdm_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen)
 338 {
 339         ftdm_log(FTDM_LOG_NOTICE, "Call read data on chan %d\n", openr2_chan_get_number(r2chan));
 340 }
 341 
 342 static void ftdm_r2_on_hardware_alarm(openr2_chan_t *r2chan, int alarm)
 343 {
 344         ftdm_log(FTDM_LOG_NOTICE, "Alarm on chan %d (%d)\n", openr2_chan_get_number(r2chan), alarm);
 345 }
 346 
 347 static void ftdm_r2_on_os_error(openr2_chan_t *r2chan, int errorcode)
 348 {
 349         ftdm_log(FTDM_LOG_ERROR, "OS error on chan %d: %s\n", openr2_chan_get_number(r2chan), strerror(errorcode));
 350 }
 351 
 352 static void ftdm_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_error_t reason)
 353 {
 354         ftdm_sigmsg_t sigev;
 355         ftdm_r2_data_t *r2data;
 356         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 357 
 358         ftdm_log(FTDM_LOG_ERROR, "Protocol error on chan %d\n", openr2_chan_get_number(r2chan));
 359 
 360         R2CALL(ftdmchan)->disconnect_rcvd = 1;
 361 
 362         if (!R2CALL(ftdmchan)->ftdm_started) {
 363                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
 364                 return;
 365         }
 366 
 367         ftdmchan->caller_data.hangup_cause  = FTDM_CAUSE_PROTOCOL_ERROR; 
 368 
 369         /* notify the user of the call terminating */
 370         memset(&sigev, 0, sizeof(sigev));
 371         sigev.chan_id = ftdmchan->chan_id;
 372         sigev.span_id = ftdmchan->span_id;
 373         sigev.channel = ftdmchan;
 374         sigev.event_id = FTDM_SIGEVENT_STOP;
 375         r2data = ftdmchan->span->signal_data;
 376 
 377         ftdm_span_send_signal(ftdmchan->span, &sigev);
 378 }
 379 
 380 static void ftdm_r2_on_line_blocked(openr2_chan_t *r2chan)
 381 {
 382         ftdm_log(FTDM_LOG_NOTICE, "Far end blocked on chan %d\n", openr2_chan_get_number(r2chan));
 383 }
 384 
 385 static void ftdm_r2_on_line_idle(openr2_chan_t *r2chan)
 386 {
 387         ftdm_log(FTDM_LOG_NOTICE, "Far end unblocked on chan %d\n", openr2_chan_get_number(r2chan));
 388 }
 389 
 390 static void ftdm_r2_write_log(openr2_log_level_t level, const char *message)
 391 {
 392         switch (level) {
 393                 case OR2_LOG_NOTICE:
 394                         ftdm_log(FTDM_LOG_NOTICE, "%s", message);
 395                         break;
 396                 case OR2_LOG_WARNING:
 397                         ftdm_log(FTDM_LOG_WARNING, "%s", message);
 398                         break;
 399                 case OR2_LOG_ERROR:
 400                         ftdm_log(FTDM_LOG_ERROR, "%s", message);
 401                         break;
 402                 case OR2_LOG_STACK_TRACE:
 403                 case OR2_LOG_MF_TRACE:
 404                 case OR2_LOG_CAS_TRACE:
 405                 case OR2_LOG_DEBUG:
 406                 case OR2_LOG_EX_DEBUG:
 407                         ftdm_log(FTDM_LOG_DEBUG, "%s", message);
 408                         break;
 409                 default:
 410                         ftdm_log(FTDM_LOG_WARNING, "We should handle logging level %d here.\n", level);
 411                         ftdm_log(FTDM_LOG_DEBUG, "%s", message);
 412                         break;
 413         }
 414 }
 415 
 416 static void ftdm_r2_on_context_log(openr2_context_t *r2context, openr2_log_level_t level, const char *fmt, va_list ap)
 417 {
 418 #define CONTEXT_TAG "Context -"
 419         char logmsg[256];
 420         char completemsg[sizeof(logmsg) + sizeof(CONTEXT_TAG) - 1];
 421         vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
 422         snprintf(completemsg, sizeof(completemsg), CONTEXT_TAG "%s", logmsg);
 423         ftdm_r2_write_log(level, completemsg);
 424 #undef CONTEXT_TAG
 425 }
 426 
 427 static void ftdm_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level, const char *fmt, va_list ap)
 428 {
 429 #define CHAN_TAG "Chan "
 430         char logmsg[256];
 431         char completemsg[sizeof(logmsg) + sizeof(CHAN_TAG) - 1];
 432         vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
 433         snprintf(completemsg, sizeof(completemsg), CHAN_TAG "%d: %s", openr2_chan_get_number(r2chan), logmsg);
 434         ftdm_r2_write_log(level, completemsg);
 435 #undef CHAN_TAG
 436 }
 437 
 438 static int ftdm_r2_on_dnis_digit_received(openr2_chan_t *r2chan, char digit)
 439 {
 440         ftdm_sigmsg_t sigev;
 441         ftdm_r2_data_t *r2data;
 442         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 443         ftdm_size_t collected_len = R2CALL(ftdmchan)->dnis_index;
 444 
 445         ftdm_log(FTDM_LOG_DEBUG, "DNIS digit %d received chan %d\n", digit, openr2_chan_get_number(r2chan));
 446 
 447         /* save the digit we just received */ 
 448         ftdmchan->caller_data.dnis.digits[collected_len] = digit;
 449         collected_len++;
 450         ftdmchan->caller_data.dnis.digits[collected_len] = '\0';
 451         R2CALL(ftdmchan)->dnis_index = collected_len;
 452 
 453         /* notify the user about the new digit and check if we should stop requesting more DNIS */
 454         memset(&sigev, 0, sizeof(sigev));
 455         sigev.chan_id = ftdmchan->chan_id;
 456         sigev.span_id = ftdmchan->span_id;
 457         sigev.channel = ftdmchan;
 458         sigev.event_id = FTDM_SIGEVENT_COLLECTED_DIGIT;
 459         r2data = ftdmchan->span->signal_data;
 460         if (ftdm_span_send_signal(ftdmchan->span, &sigev) == FTDM_BREAK) {
 461                 ftdm_log(FTDM_LOG_NOTICE, "Requested to stop getting DNIS. Current DNIS = %s on chan %d\n", ftdmchan->caller_data.dnis.digits, openr2_chan_get_number(r2chan));
 462                 return OR2_STOP_DNIS_REQUEST; 
 463         }
 464 
 465         /* the only other reason to stop requesting DNIS is that there is no more room to save it */
 466         if (collected_len == (sizeof(ftdmchan->caller_data.dnis.digits) - 1)) {
 467                 ftdm_log(FTDM_LOG_NOTICE, "No more room for DNIS. Current DNIS = %s on chan %d\n", ftdmchan->caller_data.dnis.digits, openr2_chan_get_number(r2chan));
 468                 return OR2_STOP_DNIS_REQUEST;
 469         }
 470 
 471         return OR2_CONTINUE_DNIS_REQUEST; 
 472 }
 473 
 474 static void ftdm_r2_on_ani_digit_received(openr2_chan_t *r2chan, char digit)
 475 {
 476         ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 477         ftdm_size_t collected_len = R2CALL(ftdmchan)->ani_index;
 478 
 479         /* check if we should drop ANI */
 480         if (collected_len == (sizeof(ftdmchan->caller_data.ani.digits) - 1)) {
 481                 ftdm_log(FTDM_LOG_NOTICE, "No more room for ANI %c on chan %d, digit dropped.\n", digit, openr2_chan_get_number(r2chan));
 482                 return;
 483         }
 484         ftdm_log(FTDM_LOG_DEBUG, "ANI digit %c received chan %d\n", digit, openr2_chan_get_number(r2chan));
 485 
 486         /* save the digit we just received */ 
 487         ftdmchan->caller_data.ani.digits[collected_len++] = digit;
 488         ftdmchan->caller_data.ani.digits[collected_len] = '\0';
 489 }
 490 
 491 static openr2_event_interface_t ftdm_r2_event_iface = {
 492         .on_call_init = ftdm_r2_on_call_init,
 493         .on_call_offered = ftdm_r2_on_call_offered,
 494         .on_call_accepted = ftdm_r2_on_call_accepted,
 495         .on_call_answered = ftdm_r2_on_call_answered,
 496         .on_call_disconnect = ftdm_r2_on_call_disconnect,
 497         .on_call_end = ftdm_r2_on_call_end,
 498         .on_call_read = ftdm_r2_on_call_read,
 499         .on_hardware_alarm = ftdm_r2_on_hardware_alarm,
 500         .on_os_error = ftdm_r2_on_os_error,
 501         .on_protocol_error = ftdm_r2_on_protocol_error,
 502         .on_line_blocked = ftdm_r2_on_line_blocked,
 503         .on_line_idle = ftdm_r2_on_line_idle,
 504         /* cast seems to be needed to get rid of the annoying warning regarding format attribute  */
 505         .on_context_log = (openr2_handle_context_logging_func)ftdm_r2_on_context_log,
 506         .on_dnis_digit_received = ftdm_r2_on_dnis_digit_received,
 507         .on_ani_digit_received = ftdm_r2_on_ani_digit_received,
 508         /* so far we do nothing with billing pulses */
 509         .on_billing_pulse_received = NULL
 510 };
 511 
 512 static int ftdm_r2_io_set_cas(openr2_chan_t *r2chan, int cas)
 513 {
 514         ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan);
 515         ftdm_status_t status = ftdm_channel_command(ftdm_chan, FTDM_COMMAND_SET_CAS_BITS, &cas);
 516         if (FTDM_FAIL == status) {
 517                 return -1;
 518         }
 519         return 0;
 520 }
 521 
 522 static int ftdm_r2_io_get_cas(openr2_chan_t *r2chan, int *cas)
 523 {
 524         ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan);
 525         ftdm_status_t status = ftdm_channel_command(ftdm_chan, FTDM_COMMAND_GET_CAS_BITS, cas);
 526         if (FTDM_FAIL == status) {
 527                 return -1;
 528         }
 529         return 0;
 530 }
 531 
 532 static int ftdm_r2_io_flush_write_buffers(openr2_chan_t *r2chan)
 533 {
 534         ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan);
 535         ftdm_status_t status = ftdm_channel_command(ftdm_chan, FTDM_COMMAND_FLUSH_TX_BUFFERS, NULL);
 536         if (FTDM_FAIL == status) {
 537                 return -1;
 538         }
 539         return 0;
 540 }
 541 
 542 static int ftdm_r2_io_write(openr2_chan_t *r2chan, const void *buf, int size)
 543 {
 544         ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan);
 545         ftdm_size_t outsize = size;
 546         ftdm_status_t status = ftdm_channel_write(ftdm_chan, (void *)buf, size, &outsize);
 547         if (FTDM_FAIL == status) {
 548                 return -1;
 549         }
 550         return outsize;
 551 }
 552 
 553 static int ftdm_r2_io_read(openr2_chan_t *r2chan, const void *buf, int size)
 554 {
 555         ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan);
 556         ftdm_size_t outsize = size;
 557         ftdm_status_t status = ftdm_channel_read(ftdm_chan, (void *)buf, &outsize);
 558         if (FTDM_FAIL == status) {
 559                 return -1;
 560         }
 561         return outsize;
 562 }
 563 
 564 static int ftdm_r2_io_wait(openr2_chan_t *r2chan, int *flags, int block)
 565 {
 566         ftdm_status_t status;
 567         ftdm_wait_flag_t ftdmflags = 0;
 568 
 569         ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan);
 570         int32_t timeout = block ? -1 : 0;
 571 
 572         if (*flags & OR2_IO_READ) {
 573                 ftdmflags |= FTDM_READ;
 574         }
 575         if (*flags & OR2_IO_WRITE) {
 576                 ftdmflags |= FTDM_WRITE;
 577         }
 578         if (*flags & OR2_IO_OOB_EVENT) {
 579                 ftdmflags |= FTDM_EVENTS;
 580         }
 581 
 582         status = ftdm_channel_wait(ftdm_chan, &ftdmflags, timeout);
 583 
 584         if (FTDM_SUCCESS != status) {
 585                 return -1;
 586         }
 587 
 588         *flags = 0;
 589         if (ftdmflags & FTDM_READ) {
 590                 *flags |= OR2_IO_READ;
 591         }
 592         if (ftdmflags & FTDM_WRITE) {
 593                 *flags |= OR2_IO_WRITE;
 594         }
 595         if (ftdmflags & FTDM_EVENTS) {
 596                 *flags |= OR2_IO_OOB_EVENT;
 597         }
 598 
 599         return 0;
 600 }
 601 
 602 /* The following openr2 hooks never get called, read on for reasoning ... */
 603 /* since freetdm takes care of opening the file descriptor and using openr2_chan_new_from_fd, openr2 should never call this hook */
 604 static openr2_io_fd_t ftdm_r2_io_open(openr2_context_t *r2context, int channo)
 605 {
 606         ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O open)!!\n");
 607         return NULL;
 608 }
 609 
 610 /* since freetdm takes care of closing the file descriptor and uses openr2_chan_new_from_fd, openr2 should never call this hook */
 611 static int ftdm_r2_io_close(openr2_chan_t *r2chan)
 612 {
 613         ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O close)!!\n");
 614         return 0;
 615 }
 616 
 617 /* since freetdm takes care of opening the file descriptor and using openr2_chan_new_from_fd, openr2 should never call this hook */
 618 static int ftdm_r2_io_setup(openr2_chan_t *r2chan)
 619 {
 620         ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O Setup)!!\n");
 621         return 0;
 622 }
 623 
 624 /* since the signaling thread calls openr2_chan_process_cas_signaling directly, openr2 should never call this hook */
 625 static int ftdm_r2_io_get_oob_event(openr2_chan_t *r2chan, openr2_oob_event_t *event)
 626 {
 627         *event = 0;
 628         ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O get oob event)!!\n");
 629         return 0;
 630 }
 631 
 632 static openr2_io_interface_t ftdm_r2_io_iface = {
 633         .open = ftdm_r2_io_open, /* never called */
 634         .close = ftdm_r2_io_close, /* never called */
 635         .set_cas = ftdm_r2_io_set_cas,
 636         .get_cas = ftdm_r2_io_get_cas,
 637         .flush_write_buffers = ftdm_r2_io_flush_write_buffers,
 638         .write = ftdm_r2_io_write,
 639         .read = ftdm_r2_io_read,
 640         .setup = ftdm_r2_io_setup, /* never called */
 641         .wait = ftdm_r2_io_wait,
 642         .get_oob_event = ftdm_r2_io_get_oob_event /* never called */
 643 };
 644 
 645 static FIO_SIG_CONFIGURE_FUNCTION(ftdm_r2_configure_span)
 646         //ftdm_status_t (ftdm_span_t *span, fio_signal_cb_t sig_cb, va_list ap)
 647 {
 648         int i = 0;
 649         int conf_failure = 0;
 650         char *var = NULL;
 651         char *val = NULL;
 652         ftdm_r2_data_t *r2data = NULL;
 653         ftdm_r2_span_pvt_t *spanpvt = NULL;
 654         ftdm_r2_call_t *r2call = NULL;
 655         openr2_chan_t *r2chan = NULL;
 656 
 657         assert(sig_cb != NULL);
 658 
 659         ft_r2_conf_t r2conf = 
 660         {
 661                 .variant = OR2_VAR_ITU,
 662                 .category = OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER,
 663                 .loglevel = OR2_LOG_ERROR | OR2_LOG_WARNING,
 664                 .max_ani = 10,
 665                 .max_dnis = 4,
 666                 .mfback_timeout = -1,
 667                 .metering_pulse_timeout = -1,
 668                 .allow_collect_calls = -1,
 669                 .immediate_accept = -1,
 670                 .skip_category = -1,
 671                 .forced_release = -1,
 672                 .charge_calls = -1,
 673                 .get_ani_first = -1,
 674                 .call_files = -1,
 675                 .logdir = NULL,
 676                 .advanced_protocol_file = NULL 
 677         };
 678 
 679 
 680         if (span->signal_type) {
 681                 snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling.");
 682                 return FTDM_FAIL;
 683         }
 684 
 685         while ((var = va_arg(ap, char *))) {
 686                 ftdm_log(FTDM_LOG_DEBUG, "Reading R2 parameter %s for span %d\n", var, span->span_id);
 687                 if (!strcasecmp(var, "variant")) {
 688                         if (!(val = va_arg(ap, char *))) {
 689                                 break;
 690                         }
 691                         if (ftdm_strlen_zero_buf(val)) {
 692                                 ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 variant parameter\n");
 693                                 continue;
 694                         }
 695                         r2conf.variant = openr2_proto_get_variant(val);
 696                         if (r2conf.variant == OR2_VAR_UNKNOWN) {
 697                                 ftdm_log(FTDM_LOG_ERROR, "Unknown R2 variant %s\n", val);
 698                                 conf_failure = 1;
 699                                 break;
 700                         }
 701                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d for variant %s\n", span->span_id, val);
 702                 } else if (!strcasecmp(var, "category")) {
 703                         if (!(val = va_arg(ap, char *))) {
 704                                 break;
 705                         }
 706                         if (ftdm_strlen_zero_buf(val)) {
 707                                 ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 category parameter\n");
 708                                 continue;
 709                         }
 710                         r2conf.category = openr2_proto_get_category(val);
 711                         if (r2conf.category == OR2_CALLING_PARTY_CATEGORY_UNKNOWN) {
 712                                 ftdm_log(FTDM_LOG_ERROR, "Unknown R2 caller category %s\n", val);
 713                                 conf_failure = 1;
 714                                 break;
 715                         }
 716                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with default category %s\n", span->span_id, val);
 717                 } else if (!strcasecmp(var, "logdir")) {
 718                         if (!(val = va_arg(ap, char *))) {
 719                                 break;
 720                         }
 721                         if (ftdm_strlen_zero_buf(val)) {
 722                                 ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 logdir parameter\n");
 723                                 continue;
 724                         }
 725                         r2conf.logdir = val;
 726                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with logdir %s\n", span->span_id, val);
 727                 } else if (!strcasecmp(var, "logging")) {
 728                         if (!(val = va_arg(ap, char *))) {
 729                                 break;
 730                         }
 731                         if (ftdm_strlen_zero_buf(val)) {
 732                                 ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 logging parameter\n");
 733                                 continue;
 734                         }
 735                         openr2_log_level_t tmplevel;
 736                         char *clevel;
 737                         char *logval = ftdm_malloc(strlen(val)+1); /* alloca man page scared me, so better to use good ol' malloc  */
 738                         if (!logval) {
 739                                 ftdm_log(FTDM_LOG_WARNING, "Ignoring R2 logging parameter: '%s', failed to alloc memory\n", val);
 740                                 continue;
 741                         }
 742                         strcpy(logval, val);
 743                         while (logval) {
 744                                 clevel = strsep(&logval, ",");
 745                                 if (-1 == (tmplevel = openr2_log_get_level(clevel))) {
 746                                         ftdm_log(FTDM_LOG_WARNING, "Ignoring invalid R2 logging level: '%s'\n", clevel);
 747                                         continue;
 748                                 }
 749                                 r2conf.loglevel |= tmplevel;
 750                                 ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with loglevel %s\n", span->span_id, clevel);
 751                         }
 752                         ftdm_safe_free(logval);
 753                 } else if (!strcasecmp(var, "advanced_protocol_file")) {
 754                         if (!(val = va_arg(ap, char *))) {
 755                                 break;
 756                         }
 757                         if (ftdm_strlen_zero_buf(val)) {
 758                                 ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 advanced_protocol_file parameter\n");
 759                                 continue;
 760                         }
 761                         r2conf.advanced_protocol_file = val;
 762                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with advanced protocol file %s\n", span->span_id, val);
 763                 } else if (!strcasecmp(var, "allow_collect_calls")) {
 764                         r2conf.allow_collect_calls = va_arg(ap, int);
 765                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with allow collect calls max ani = %d\n", span->span_id, r2conf.allow_collect_calls);
 766                 } else if (!strcasecmp(var, "double_answer")) {
 767                         r2conf.double_answer = va_arg(ap, int);
 768                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with double answer = %d\n", span->span_id, r2conf.double_answer);
 769                 } else if (!strcasecmp(var, "immediate_accept")) {
 770                         r2conf.immediate_accept = va_arg(ap, int);
 771                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with immediate accept = %d\n", span->span_id, r2conf.immediate_accept);
 772                 } else if (!strcasecmp(var, "skip_category")) {
 773                         r2conf.skip_category = va_arg(ap, int);
 774                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with skip category = %d\n", span->span_id, r2conf.skip_category);
 775                 } else if (!strcasecmp(var, "forced_release")) {
 776                         r2conf.forced_release = va_arg(ap, int);
 777                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with forced release = %d\n", span->span_id, r2conf.forced_release);
 778                 } else if (!strcasecmp(var, "charge_calls")) {
 779                         r2conf.charge_calls = va_arg(ap, int);
 780                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with charge calls = %d\n", span->span_id, r2conf.charge_calls);
 781                 } else if (!strcasecmp(var, "get_ani_first")) {
 782                         r2conf.get_ani_first = va_arg(ap, int);
 783                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with get ani first = %d\n", span->span_id, r2conf.get_ani_first);
 784                 } else if (!strcasecmp(var, "call_files")) {
 785                         r2conf.call_files = va_arg(ap, int);
 786                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with call files = %d\n", span->span_id, r2conf.call_files);
 787                 } else if (!strcasecmp(var, "mfback_timeout")) {
 788                         r2conf.mfback_timeout = va_arg(ap, int);
 789                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with MF backward timeout = %dms\n", span->span_id, r2conf.mfback_timeout);
 790                 } else if (!strcasecmp(var, "metering_pulse_timeout")) {
 791                         r2conf.metering_pulse_timeout = va_arg(ap, int);
 792                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with metering pulse timeout = %dms\n", span->span_id, r2conf.metering_pulse_timeout);
 793                 } else if (!strcasecmp(var, "max_ani")) {
 794                         r2conf.max_ani = va_arg(ap, int);
 795                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with max ani = %d\n", span->span_id, r2conf.max_ani);
 796                 } else if (!strcasecmp(var, "max_dnis")) {
 797                         r2conf.max_dnis = va_arg(ap, int);
 798                         ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with max dnis = %d\n", span->span_id, r2conf.max_dnis);
 799                 } else {
 800                         snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var);
 801                         return FTDM_FAIL;
 802                 }
 803         }
 804 
 805         if (conf_failure) {
 806                 snprintf(span->last_error, sizeof(span->last_error), "R2 configuration error");
 807                 return FTDM_FAIL;
 808         }
 809 
 810         r2data = ftdm_malloc(sizeof(*r2data));
 811         if (!r2data) {
 812                 snprintf(span->last_error, sizeof(span->last_error), "Failed to allocate R2 data.");
 813                 return FTDM_FAIL;
 814         }
 815         memset(r2data, 0, sizeof(*r2data));
 816 
 817         spanpvt = ftdm_malloc(sizeof(*spanpvt));
 818         if (!spanpvt) {
 819                 snprintf(span->last_error, sizeof(span->last_error), "Failed to allocate private span data container.");
 820                 goto fail;
 821         }
 822         memset(spanpvt, 0, sizeof(*spanpvt));
 823 
 824         r2data->r2context = openr2_context_new(r2conf.variant, &ftdm_r2_event_iface, r2conf.max_ani, r2conf.max_dnis);
 825         if (!r2data->r2context) {
 826                 snprintf(span->last_error, sizeof(span->last_error), "Cannot create openr2 context for span.");
 827                 goto fail;
 828         }
 829         openr2_context_set_io_type(r2data->r2context, OR2_IO_CUSTOM, &ftdm_r2_io_iface);
 830         openr2_context_set_log_level(r2data->r2context, r2conf.loglevel);
 831         openr2_context_set_ani_first(r2data->r2context, r2conf.get_ani_first);
 832         openr2_context_set_skip_category_request(r2data->r2context, r2conf.skip_category);
 833         openr2_context_set_mf_back_timeout(r2data->r2context, r2conf.mfback_timeout);
 834         openr2_context_set_metering_pulse_timeout(r2data->r2context, r2conf.metering_pulse_timeout);
 835         openr2_context_set_double_answer(r2data->r2context, r2conf.double_answer);
 836         openr2_context_set_immediate_accept(r2data->r2context, r2conf.immediate_accept);
 837         if (r2conf.logdir) {
 838                 openr2_context_set_log_directory(r2data->r2context, r2conf.logdir);
 839         }
 840         if (r2conf.advanced_protocol_file) {
 841                 openr2_context_configure_from_advanced_file(r2data->r2context, r2conf.advanced_protocol_file);
 842         }
 843 
 844         spanpvt->r2calls = create_hashtable(FTDM_MAX_CHANNELS_SPAN, ftdm_hash_hashfromstring, ftdm_hash_equalkeys);
 845         if (!spanpvt->r2calls) {
 846                 snprintf(span->last_error, sizeof(span->last_error), "Cannot create channel calls hash for span.");
 847                 goto fail;
 848         }
 849 
 850         for (i = 1; (i <= span->chan_count) && (i <= FTDM_MAX_CHANNELS_SPAN); i++) {
 851                 r2chan = openr2_chan_new_from_fd(r2data->r2context, span->channels[i], span->channels[i]->physical_chan_id);
 852                 if (!r2chan) {
 853                         snprintf(span->last_error, sizeof(span->last_error), "Cannot create all openr2 channels for span.");
 854                         goto fail;
 855                 }
 856                 if (r2conf.call_files) {
 857                         openr2_chan_enable_call_files(r2chan);
 858                         openr2_chan_set_log_level(r2chan, r2conf.loglevel);
 859                 }
 860 
 861                 r2call = ftdm_malloc(sizeof(*r2call));
 862                 if (!r2call) {
 863                         snprintf(span->last_error, sizeof(span->last_error), "Cannot create all R2 call data structures for the span.");
 864                         ftdm_safe_free(r2chan);
 865                         goto fail;
 866                 }
 867                 memset(r2call, 0, sizeof(*r2call));
 868                 openr2_chan_set_logging_func(r2chan, ftdm_r2_on_chan_log);
 869                 openr2_chan_set_client_data(r2chan, span->channels[i]);
 870         r2call->r2chan = r2chan;
 871                 span->channels[i]->call_data = r2call;
 872                 /* value and key are the same so just free one of them */
 873                 snprintf(r2call->name, sizeof(r2call->name), "chancall%d", i);
 874                 hashtable_insert(spanpvt->r2calls, (void *)r2call->name, r2call, HASHTABLE_FLAG_FREE_VALUE);
 875 
 876         }
 877         spanpvt->r2context = r2data->r2context;
 878 
 879         /* just the value must be freed by the hash */
 880         hashtable_insert(g_mod_data_hash, (void *)span->name, spanpvt, HASHTABLE_FLAG_FREE_VALUE);
 881 
 882         span->start = ftdm_r2_start;
 883         r2data->flags = 0;
 884         span->signal_cb = sig_cb;
 885         span->signal_type = FTDM_SIGTYPE_R2;
 886         span->signal_data = r2data;
 887         span->outgoing_call = r2_outgoing_call;
 888 
 889         return FTDM_SUCCESS;
 890 
 891 fail:
 892 
 893         if (r2data && r2data->r2context) {
 894                 openr2_context_delete(r2data->r2context);
 895         }
 896         if (spanpvt && spanpvt->r2calls) {
 897                 hashtable_destroy(spanpvt->r2calls);
 898         }
 899         ftdm_safe_free(r2data);
 900         ftdm_safe_free(spanpvt);
 901         return FTDM_FAIL;
 902 
 903 }
 904 
 905 static void *ftdm_r2_channel_run(ftdm_thread_t *me, void *obj)
 906 {
 907         ftdm_channel_t *closed_chan;
 908         uint32_t interval = 0;
 909         ftdm_sigmsg_t sigev;
 910         ftdm_channel_t *ftdmchan = (ftdm_channel_t *)obj;
 911         openr2_chan_t *r2chan = R2CALL(ftdmchan)->r2chan;
 912 
 913         ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INTHREAD);
 914 
 915         ftdm_mutex_lock(g_thread_count_mutex);
 916         g_thread_count++;
 917         ftdm_mutex_unlock(g_thread_count_mutex);
 918 
 919         ftdm_log(FTDM_LOG_DEBUG, "R2 CHANNEL thread starting on %d in state %s.\n", 
 920                         ftdmchan->physical_chan_id,
 921                         ftdm_channel_state2str(ftdmchan->state));
 922 
 923         if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) {
 924                 ftdm_log(FTDM_LOG_ERROR, "OPEN ERROR [%s]\n", ftdmchan->last_error);
 925                 goto endthread;
 926         }
 927 
 928         ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
 929 
 930         assert(interval != 0);
 931         ftdm_log(FTDM_LOG_DEBUG, "Got %d interval for chan %d\n", interval, ftdmchan->physical_chan_id);
 932 
 933         if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
 934                 /* FIXME: is this needed? */
 935                 memset(ftdmchan->caller_data.dnis.digits, 0, sizeof(ftdmchan->caller_data.collected));
 936                 memset(ftdmchan->caller_data.ani.digits, 0, sizeof(ftdmchan->caller_data.collected));
 937         }
 938 
 939         memset(&sigev, 0, sizeof(sigev));
 940         sigev.chan_id = ftdmchan->chan_id;
 941         sigev.span_id = ftdmchan->span_id;
 942         sigev.channel = ftdmchan;
 943 
 944         while (ftdm_running()) {
 945                 int32_t read_enabled = openr2_chan_get_read_enabled(r2chan);
 946                 ftdm_wait_flag_t flags = read_enabled ? ( FTDM_READ | FTDM_WRITE ) : 0;
 947 
 948                 if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE) && (R2CALL(ftdmchan)->chanstate != ftdmchan->state)) {
 949 
 950                         ftdm_log(FTDM_LOG_DEBUG, "Executing state handler on %d:%d for %s\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state));
 951                         R2CALL(ftdmchan)->chanstate = ftdmchan->state;
 952 
 953                         if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) && !R2CALL(ftdmchan)->accepted &&
 954                                         (ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS ||
 955                                          ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA ||
 956                                          ftdmchan->state == FTDM_CHANNEL_STATE_UP) ) {
 957                                 /* if an accept ack will be required we should not acknowledge the state change just yet, 
 958                                    it will be done below after processing the MF signals, otherwise we have a race condition between freetdm calling
 959                                    openr2_chan_answer_call and openr2 accepting the call first, if freetdm calls openr2_chan_answer_call before the accept cycle
 960                                    completes, openr2 will fail to answer the call */
 961                                 ftdm_log(FTDM_LOG_DEBUG, "State ack in chan %d:%d for state %s will have to wait a bit\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state));
 962                         } else if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN){
 963                                 /* the down state will be completed in ftdm_channel_done below */
 964                                 ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
 965                                 ftdm_channel_complete_state(ftdmchan);
 966                         }
 967 
 968                         switch (ftdmchan->state) {
 969 
 970                                 /* starting an incoming call */
 971                                 case FTDM_CHANNEL_STATE_COLLECT: 
 972                                         {
 973                                                 ftdm_log(FTDM_LOG_DEBUG, "COLLECT: Starting processing of incoming call in channel %d with interval %d\n", ftdmchan->physical_chan_id, interval);
 974                                         }
 975                                         break;
 976 
 977                                         /* starting an outgoing call */
 978                                 case FTDM_CHANNEL_STATE_DIALING:
 979                                         {
 980                                                 // FIXME: use user defined calling party
 981                                                 ftdm_channel_use(ftdmchan);
 982                                                 ftdm_log(FTDM_LOG_DEBUG, "DIALING: Starting processing of outgoing call in channel %d with interval %d\n", ftdmchan->physical_chan_id, interval);
 983                                                 if (openr2_chan_make_call(r2chan, ftdmchan->caller_data.cid_num.digits, ftdmchan->caller_data.dnis.digits, OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER)) {
 984                                                         ftdm_log(FTDM_LOG_ERROR, "%d:%d Failed to make call in R2 channel, openr2_chan_make_call failed\n", ftdmchan->span_id, ftdmchan->chan_id);
 985                                                         ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER;
 986                                                         ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
 987                                                 }
 988                                         }
 989                                         break;
 990 
 991                                         /* the call is ringing */
 992                                 case FTDM_CHANNEL_STATE_PROGRESS:
 993                                 case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
 994                                         {
 995                                                 if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
 996                                                         if (!R2CALL(ftdmchan)->accepted) {
 997                                                                 ftdm_log(FTDM_LOG_DEBUG, "PROGRESS: Accepting call on channel %d\n", ftdmchan->physical_chan_id);
 998                                                                 ft_r2_accept_call(ftdmchan);
 999                                                         } 
1000                                                 } else {
1001                                                         ftdm_log(FTDM_LOG_DEBUG, "PROGRESS: Notifying progress in channel %d\n", ftdmchan->physical_chan_id);
1002                                                         sigev.event_id = FTDM_SIGEVENT_PROGRESS;
1003                                                         if (ftdm_span_send_signal(ftdmchan->span, &sigev) != FTDM_SUCCESS) {
1004                                                                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
1005                                                         }
1006                                                 }
1007                                         }
1008                                         break;
1009 
1010                                         /* the call was answered */
1011                                 case FTDM_CHANNEL_STATE_UP:
1012                                         {
1013                                                 ftdm_log(FTDM_LOG_DEBUG, "UP: Call was answered on channel %d\n", ftdmchan->physical_chan_id);
1014                                                 if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
1015                                                         if (!R2CALL(ftdmchan)->accepted) {
1016                                                                 ftdm_log(FTDM_LOG_DEBUG, "UP: Call has not been accepted, need to accept first\n");
1017                                                                 // the answering will be done in the on_call_accepted handler
1018                                                                 ft_r2_accept_call(ftdmchan);
1019                                                                 R2CALL(ftdmchan)->answer_pending = 1;
1020                                                         } else {
1021                                                                 ft_r2_answer_call(ftdmchan);
1022                                                         }
1023                                                 } else {
1024                                                         ftdm_log(FTDM_LOG_DEBUG, "UP: Notifying of call answered in channel %d\n", ftdmchan->physical_chan_id);
1025                                                         sigev.event_id = FTDM_SIGEVENT_UP;
1026                                                         if (ftdm_span_send_signal(ftdmchan->span, &sigev) != FTDM_SUCCESS) {
1027                                                                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
1028                                                         }
1029                                                 }
1030                                         }
1031                                         break;
1032 
1033                                         /* just got hangup */
1034                                 case FTDM_CHANNEL_STATE_HANGUP: 
1035                                         {
1036                                                 /* FIXME: the cause should be retrieved from ftdmchan->caller_data.hangup_cause and translated from Q931 to R2 cause */
1037                                                 ftdm_log(FTDM_LOG_DEBUG, "HANGUP: Clearing call on channel %d\n", ftdmchan->physical_chan_id);
1038                                                 if (!R2CALL(ftdmchan)->disconnect_rcvd) {
1039                                                         /* this will disconnect the call, but need to wait for the call end before moving to DOWN */
1040                                                         openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
1041                                                 } else {
1042                                                         /* at this point on_call_end possibly was already called, 
1043                                                          * but we needed to wait for the freetdm confirmation before moving to DOWN */
1044                                                         ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
1045                                                 }
1046                                         }
1047                                         break;
1048 
1049                                         /* just got hangup from the freetdm side due to abnormal failure */
1050                                 case FTDM_CHANNEL_STATE_CANCEL:
1051                                         {
1052                                                 ftdm_log(FTDM_LOG_DEBUG, "CANCEL: Unable to receive call on channel %d\n", ftdmchan->physical_chan_id);
1053                                                 openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER);
1054                                         }
1055                                         break;
1056 
1057                                         /* finished call for good */
1058                                 case FTDM_CHANNEL_STATE_DOWN: 
1059                                         {
1060                                                 ftdm_log(FTDM_LOG_DEBUG, "DOWN: Placing channel %d back to the pool of available channels\n", ftdmchan->physical_chan_id);
1061                                                 ftdm_channel_done(ftdmchan);
1062                                                 goto endthread;
1063                                         }
1064                                         break;
1065 
1066                                 default:
1067                                         {
1068                                                 ftdm_log(FTDM_LOG_ERROR, "%s: Unhandled channel state change in channel %d\n", ftdm_channel_state2str(ftdmchan->state), ftdmchan->physical_chan_id);
1069                                         }
1070                                         break;
1071 
1072                         }
1073                 }
1074 
1075                 if (flags) {
1076                         if (ftdm_channel_wait(ftdmchan, &flags, interval * 2) != FTDM_SUCCESS) {
1077                                 ftdm_log(FTDM_LOG_DEBUG, "ftdm_channel_wait did not return FTDM_SUCCESS\n");
1078                                 continue;
1079                         }
1080 
1081                         /* handle timeout events first if any */
1082                         openr2_chan_run_schedule(r2chan);
1083 
1084                         /* openr2 will now try to detect MF tones, make sense out of them, reply if necessary with another tone and trigger 
1085                          * telephony events via the call event interface we provided when creating the R2 context.
1086                          * openr2 will also call our I/O callbacks to retrieve audio from the channel and call our wait poll I/O registered callback
1087                          * and will not return from this function until the I/O poll callback returns no pending events
1088                          * */
1089                         openr2_chan_process_mf_signaling(r2chan);
1090                         if (R2CALL(ftdmchan)->state_ack_pending) {
1091                                 ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
1092                                 ftdm_channel_complete_state(ftdmchan);
1093                                 R2CALL(ftdmchan)->state_ack_pending = 0;
1094                         }
1095                 } else {
1096                         /* once the MF signaling has end we just loop here waiting for state changes */
1097                         ftdm_sleep(interval);
1098                 }
1099 
1100         }
1101 
1102 endthread:
1103 
1104         closed_chan = ftdmchan;
1105         ftdm_channel_close(&closed_chan);
1106         ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_INTHREAD);
1107         ftdm_log(FTDM_LOG_DEBUG, "R2 channel %d thread ended.\n", ftdmchan->physical_chan_id);
1108 
1109         ftdm_mutex_lock(g_thread_count_mutex);
1110         g_thread_count--;
1111         ftdm_mutex_unlock(g_thread_count_mutex);
1112 
1113         return NULL;
1114 }
1115 
1116 static void *ftdm_r2_run(ftdm_thread_t *me, void *obj)
1117 {
1118         openr2_chan_t *r2chan;
1119         ftdm_status_t status;
1120         ftdm_span_t *span = (ftdm_span_t *) obj;
1121         ftdm_r2_data_t *r2data = span->signal_data;
1122         int waitms = 1000;
1123         int i;
1124 
1125         ftdm_log(FTDM_LOG_DEBUG, "OpenR2 monitor thread started.\n");
1126   r2chan = NULL;
1127         for (i = 1; i <= span->chan_count; i++) {
1128                 r2chan = R2CALL(span->channels[i])->r2chan;
1129                 openr2_chan_set_idle(r2chan);
1130                 openr2_chan_process_cas_signaling(r2chan);
1131         }
1132 
1133         while (ftdm_running() && ftdm_test_flag(r2data, FTDM_R2_RUNNING)) {
1134                 status = ftdm_span_poll_event(span, waitms);
1135                 if (FTDM_FAIL == status) {
1136                         ftdm_log(FTDM_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error);
1137                         continue;
1138                 }
1139                 if (FTDM_SUCCESS == status) {
1140                         ftdm_event_t *event;
1141                         while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) {
1142                                 if (event->enum_id == FTDM_OOB_CAS_BITS_CHANGE) {
1143                     r2chan = R2CALL(event->channel)->r2chan;
1144                                         ftdm_log(FTDM_LOG_DEBUG, "Handling CAS on channel %d.\n", openr2_chan_get_number(r2chan));
1145                                         // we only expect CAS and other OOB events on this thread/loop, once a call is started
1146                                         // the MF events (in-band signaling) are handled in the call thread
1147                                         openr2_chan_process_cas_signaling(r2chan);
1148                                 } else {
1149                                         ftdm_log(FTDM_LOG_DEBUG, "Ignoring event %d on channel %d.\n", event->enum_id, openr2_chan_get_number(r2chan));
1150                                         // XXX TODO: handle alarms here XXX
1151                                 }
1152                         }
1153                 } else if (status != FTDM_TIMEOUT) {
1154                         ftdm_log(FTDM_LOG_ERROR, "ftdm_span_poll_event returned %d.\n", status);
1155                 } else {
1156                         //ftdm_log(FTDM_LOG_DEBUG, "timed out waiting for event on span %d\n", span->span_id);
1157                 }
1158         }
1159 
1160     /*
1161     FIXME: we should set BLOCKED but at this point I/O routines of freetdm caused segfault
1162         for (i = 1; i <= span->chan_count; i++) {
1163                 r2chan = R2CALL(span->channels[i])->r2chan;
1164                 openr2_chan_set_blocked(r2chan);
1165         }
1166     */
1167 
1168         ftdm_clear_flag(r2data, FTDM_R2_RUNNING);
1169         ftdm_log(FTDM_LOG_DEBUG, "R2 thread ending.\n");
1170 
1171         return NULL;
1172 
1173 }
1174 
1175 static FIO_API_FUNCTION(ftdm_r2_api)
1176 {
1177         char *mycmd = NULL, *argv[10] = { 0 };
1178         int argc = 0;
1179 
1180         if (data) {
1181                 mycmd = ftdm_strdup(data);
1182                 argc = ftdm_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
1183         }
1184 
1185         if (argc == 2) {
1186                 if (!strcasecmp(argv[0], "kill")) {
1187                         int span_id = atoi(argv[1]);
1188                         ftdm_span_t *span = NULL;
1189 
1190                         if (ftdm_span_find_by_name(argv[1], &span) == FTDM_SUCCESS || ftdm_span_find(span_id, &span) == FTDM_SUCCESS) {
1191                                 ftdm_r2_data_t *r2data = span->signal_data;
1192 
1193                                 if (span->start != ftdm_r2_start) {
1194                                         stream->write_function(stream, "-ERR invalid span.\n");
1195                                         goto done;
1196                                 }
1197 
1198                                 ftdm_clear_flag(r2data, FTDM_R2_RUNNING);
1199                                 stream->write_function(stream, "+OK killed.\n");
1200                                 goto done;
1201                         } else {
1202                                 stream->write_function(stream, "-ERR invalid span.\n");
1203                                 goto done;
1204                         }
1205                 }
1206 
1207                 if (!strcasecmp(argv[0], "status")) {
1208                         int span_id = atoi(argv[1]);
1209                         ftdm_r2_data_t *r2data = NULL;
1210                         ftdm_span_t *span = NULL;
1211                         openr2_chan_t *r2chan = NULL;
1212                         openr2_context_t *r2context = NULL;
1213                         int i = 0;
1214 
1215                         if (ftdm_span_find_by_name(argv[1], &span) == FTDM_SUCCESS || ftdm_span_find(span_id, &span) == FTDM_SUCCESS) {
1216                                 if (span->start != ftdm_r2_start) {
1217                                         stream->write_function(stream, "-ERR not an R2 span.\n");
1218                                         goto done;
1219                                 }
1220                                 if (!(r2data =  span->signal_data)) {
1221                                         stream->write_function(stream, "-ERR invalid span. No R2 singal data in span.\n");
1222                                         goto done;
1223                                 }
1224                                 r2context = r2data->r2context;
1225                                 openr2_variant_t r2variant = openr2_context_get_variant(r2context);
1226                                 stream->write_function(stream, 
1227                                                 "Variant: %s\n"
1228                                                 "Max ANI: %d\n"
1229                                                 "Max DNIS: %d\n"
1230                                                 "ANI First: %s\n"
1231                                                 "Immediate Accept: %s\n",
1232                                                 openr2_proto_get_variant_string(r2variant),
1233                                                 openr2_context_get_max_ani(r2context),
1234                                                 openr2_context_get_max_dnis(r2context),
1235                                                 openr2_context_get_ani_first(r2context) ? "Yes" : "No",
1236                                                 openr2_context_get_immediate_accept(r2context) ? "Yes" : "No");
1237                                 stream->write_function(stream, "\n");
1238                                 stream->write_function(stream, "%4s %-12.12s %-12.12s\n", "Channel", "Tx CAS", "Rx CAS");
1239                                 for (i = 1; i <= span->chan_count; i++) {
1240                                         if (i == 16) continue;
1241                                         r2chan = R2CALL(span->channels[i])->r2chan;
1242                                         stream->write_function(stream, "%4d    %-12.12s %-12.12s\n", 
1243                                                         span->channels[i]->physical_chan_id,
1244                                                         openr2_chan_get_tx_cas_string(r2chan),
1245                                                         openr2_chan_get_rx_cas_string(r2chan));
1246                                 }
1247                                 stream->write_function(stream, "\n");
1248                                 stream->write_function(stream, "+OK.\n");
1249                                 goto done;
1250                         } else {
1251                                 stream->write_function(stream, "-ERR invalid span.\n");
1252                                 goto done;
1253                         }
1254                 }
1255 
1256         }
1257 
1258         if (argc == 1) {
1259                 if (!strcasecmp(argv[0], "threads")) {
1260                         ftdm_mutex_lock(g_thread_count_mutex);
1261                         stream->write_function(stream, "%d R2 channel threads up\n", g_thread_count);
1262                         ftdm_mutex_unlock(g_thread_count_mutex);
1263                         stream->write_function(stream, "+OK.\n");
1264                         goto done;
1265                 }
1266 
1267                 if (!strcasecmp(argv[0], "version")) {
1268                         stream->write_function(stream, "OpenR2 version: %s, revision: %s\n", openr2_get_version(), openr2_get_revision());
1269                         stream->write_function(stream, "+OK.\n");
1270                         goto done;
1271                 }
1272 
1273                 if (!strcasecmp(argv[0], "variants")) {
1274                         int32_t numvariants = 0;
1275                         const openr2_variant_entry_t *variants = openr2_proto_get_variant_list(&numvariants);
1276                         if (!variants) {
1277                                 stream->write_function(stream, "-ERR failed to retrieve openr2 variant list.\n");
1278                                 goto done;
1279                         }
1280 #define VARIANT_FORMAT "%4s %40s\n"
1281                         stream->write_function(stream, VARIANT_FORMAT, "Variant Code", "Country");
1282                         numvariants--;
1283                         for (; numvariants; numvariants--) {
1284                                 stream->write_function(stream, VARIANT_FORMAT, variants[numvariants].name, variants[numvariants].country);
1285                         }
1286                         stream->write_function(stream, "+OK.\n");
1287 #undef VARIANT_FORMAT
1288                         goto done;
1289                 }
1290         }
1291 
1292         stream->write_function(stream, "-ERR invalid command.\n");
1293 
1294 done:
1295 
1296         ftdm_safe_free(mycmd);
1297 
1298         return FTDM_SUCCESS;
1299 
1300 }
1301 
1302 static FIO_IO_LOAD_FUNCTION(ftdm_r2_io_init)
1303 {
1304         assert(fio != NULL);
1305         memset(&g_ftdm_r2_interface, 0, sizeof(g_ftdm_r2_interface));
1306 
1307         g_ftdm_r2_interface.name = "r2";
1308         g_ftdm_r2_interface.api = ftdm_r2_api;
1309 
1310         *fio = &g_ftdm_r2_interface;
1311 
1312         return FTDM_SUCCESS;
1313 }
1314 
1315 static FIO_SIG_LOAD_FUNCTION(ftdm_r2_init)
1316 {
1317         g_mod_data_hash = create_hashtable(10, ftdm_hash_hashfromstring, ftdm_hash_equalkeys);
1318         if (!g_mod_data_hash) {
1319                 return FTDM_FAIL;
1320         }
1321         ftdm_mutex_create(&g_thread_count_mutex);
1322         return FTDM_SUCCESS;
1323 }
1324 
1325 static FIO_SIG_UNLOAD_FUNCTION(ftdm_r2_destroy)
1326 {
1327         ftdm_hash_iterator_t *i = NULL;
1328         ftdm_r2_span_pvt_t *spanpvt = NULL;
1329         const void *key = NULL;
1330         void *val = NULL;
1331         for (i = hashtable_first(g_mod_data_hash); i; i = hashtable_next(i)) {
1332                 hashtable_this(i, &key, NULL, &val);
1333                 if (key && val) {
1334                         spanpvt = val;
1335                         openr2_context_delete(spanpvt->r2context);
1336                         hashtable_destroy(spanpvt->r2calls);
1337                 }
1338         }
1339         hashtable_destroy(g_mod_data_hash);
1340         ftdm_mutex_destroy(&g_thread_count_mutex);
1341         return FTDM_SUCCESS;
1342 }
1343 
1344 ftdm_module_t ftdm_module = { 
1345         "r2",
1346         ftdm_r2_io_init,
1347         NULL,
1348         ftdm_r2_init,
1349         ftdm_r2_configure_span,
1350         ftdm_r2_destroy
1351 };
1352         
1353 
1354 /* For Emacs:
1355  * Local Variables:
1356  * mode:c
1357  * indent-tabs-mode:t
1358  * tab-width:4
1359  * c-basic-offset:4
1360  * End:
1361  * For VIM:
1362  * vim:set softtabstop=4 shiftwidth=4 tabstop=4
1363  */

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