if HAVE_OPENR2
mod_LTLIBRARIES += ftmod_r2.la
-ftmod_r2_la_SOURCES = $(SRC)/ftmod/ftmod_r2/ftmod_r2.c
+ftmod_r2_la_SOURCES = $(SRC)/ftmod/ftmod_r2/ftmod_r2.c $(SRC)/ftmod/ftmod_r2/ftmod_r2_io_mf_lib.c
ftmod_r2_la_CFLAGS = $(AM_CFLAGS) $(FTDM_CFLAGS)
ftmod_r2_la_LDFLAGS = -shared -module -avoid-version -lopenr2
ftmod_r2_la_LIBADD = libfreetdm.la
*/
#ifdef WIN32
-#define _WIN32_WINNT 0x0501 // To make GetSystemTimes visible in windows.h
-#include <windows.h>
+# if (_WIN32_WINNT < 0x0501)
+# error "Need to target at least Windows XP/Server 2003 because GetSystemTimes is needed"
+# endif
+# include <windows.h>
#else /* LINUX */
#include <stdio.h>
* c-basic-offset:4
* End:
* For VIM:
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/
*/
#ifdef WIN32
-/* required for TryEnterCriticalSection definition. Must be defined before windows.h include */
-#define _WIN32_WINNT 0x0400
+# if (_WIN32_WINNT < 0x0400)
+# error "Need to target at least Windows 95/WINNT 4.0 because TryEnterCriticalSection is needed"
+# endif
+# include <windows.h>
#endif
#include "private/ftdm_core.h"
RelativePath=".\ftmod_r2.c"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\ftmod_r2_io_mf_lib.c"\r
+ >\r
+ </File>\r
</Filter>\r
</Files>\r
<Globals>\r
#endif
#include <stdio.h>
#include <openr2.h>
-#include "freetdm.h"
-#include "private/ftdm_core.h"
+#include <freetdm.h>
+#include <private/ftdm_core.h>
+
+#include "ftmod_r2_io_mf_lib.h" // ftdm_r2_get_native_channel_mf_generation_iface
/* when the user stops a span, we clear FTDM_R2_SPAN_STARTED, so that the signaling thread
* knows it must stop, and we wait for FTDM_R2_RUNNING to be clear, which tells us the
int charge_calls;
int forced_release;
int allow_collect_calls;
+ int use_channel_native_mf_generation;
} ft_r2_conf_t;
/* r2 configuration stored in span->signal_data */
/* .double_answer */ -1,
/* .charge_calls */ -1,
/* .forced_release */ -1,
- /* .allow_collect_calls */ -1
+ /* .allow_collect_calls */ -1,
+ /* .use_channel_native_mf_generation */ 0
};
ftdm_assert_return(sig_cb != NULL, FTDM_FAIL, "No signaling cb provided\n");
} else if (!strcasecmp(var, "max_dnis")) {
r2conf.max_dnis = atoi(val);
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with max dnis = %d\n", span->name, r2conf.max_dnis);
+ } else if (!strcasecmp(var, "use_channel_native_mf_generation")) {
+ r2conf.use_channel_native_mf_generation = ftdm_true(val);
+ ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with \"use native channel MF generation\" = %d\n", span->name, r2conf.use_channel_native_mf_generation);
} else {
snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var);
return FTDM_FAIL;
openr2_context_configure_from_advanced_file(r2data->r2context, r2conf.advanced_protocol_file);
}
+ if(r2conf.use_channel_native_mf_generation) {
+ openr2_context_set_mflib_interface(r2data->r2context, ftdm_r2_get_native_channel_mf_generation_iface());
+ }
+
spanpvt->r2calls = create_hashtable(FTDM_MAX_CHANNELS_SPAN, ftdm_hash_hashfromstring, ftdm_hash_equalkeys);
if (!spanpvt->r2calls) {
snprintf(span->last_error, sizeof(span->last_error), "Cannot create channel calls hash for span.");
openr2_chan_enable_call_files(r2chan);
}
- r2call = ftdm_malloc(sizeof(*r2call));
+ if (r2conf.use_channel_native_mf_generation) {
+ /* Allocate a new write handle per r2chan */
+ ftdm_r2_mf_write_handle_t *mf_write_handle = ftdm_calloc(1, sizeof(*mf_write_handle));
+ /* Associate to the FreeTDM channel */
+ mf_write_handle->ftdmchan = span->channels[i];
+ /* Make sure the FreeTDM channel supports MF the generation feature */
+ if (!ftdm_channel_test_feature(mf_write_handle->ftdmchan, FTDM_CHANNEL_FEATURE_MF_GENERATE)) {
+ ftdm_log_chan_msg(mf_write_handle->ftdmchan, FTDM_LOG_ERROR,
+ "FreeTDM channel does not support native MF generation: "
+ "\"use_channel_native_mf_generation\" configuration parameter cannot"
+ " be used\n");
+ goto fail;
+ }
+ /* Associate the mf_write_handle to the openR2 channel */
+ openr2_chan_set_mflib_handles(r2chan, mf_write_handle, NULL);
+ }
+
+ r2call = ftdm_calloc(1, sizeof(*r2call));
if (!r2call) {
snprintf(span->last_error, sizeof(span->last_error), "Cannot create all R2 call data structures for the span.");
ftdm_safe_free(r2chan);
goto fail;
}
- memset(r2call, 0, sizeof(*r2call));
openr2_chan_set_logging_func(r2chan, ftdm_r2_on_chan_log);
openr2_chan_set_client_data(r2chan, span->channels[i]);
r2call->r2chan = r2chan;
* c-basic-offset:4
* End:
* For VIM:
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/
--- /dev/null
+/*
+ * Copyright (c) 2011 Sebastien Trottier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <freetdm.h>
+#include <private/ftdm_core.h>
+
+#include <openr2.h>
+
+#include "ftmod_r2_io_mf_lib.h"
+
+/* Convert openr2 MF tone enum value to FreeTDM MF tone value
+ 1-15 bitwise OR FTDM_MF_DIRECTION_FORWARD/BACKWARD
+ 0 (stop playing)
+ openr2_mf_tone_t defined in r2proto.h
+*/
+static int ftdm_r2_openr2_mf_tone_to_ftdm_mf_tone(openr2_mf_tone_t
+ openr2_tone_value, int forward_signals)
+{
+ int tone;
+
+ switch (openr2_tone_value) {
+ case 0: return 0;
+#define TONE_FROM_NAME(name) case OR2_MF_TONE_##name: tone = name; break;
+ TONE_FROM_NAME(1)
+ TONE_FROM_NAME(2)
+ TONE_FROM_NAME(3)
+ TONE_FROM_NAME(4)
+ TONE_FROM_NAME(5)
+ TONE_FROM_NAME(6)
+ TONE_FROM_NAME(7)
+ TONE_FROM_NAME(8)
+ TONE_FROM_NAME(9)
+ TONE_FROM_NAME(10)
+ TONE_FROM_NAME(11)
+ TONE_FROM_NAME(12)
+ TONE_FROM_NAME(13)
+ TONE_FROM_NAME(14)
+ TONE_FROM_NAME(15)
+#undef TONE_FROM_NAME
+ default:
+ ftdm_assert(0, "Invalid openr2_tone_value\n");
+ return -1;
+ }
+
+ /* Add flag corresponding to direction */
+ if (forward_signals) {
+ tone |= FTDM_MF_DIRECTION_FORWARD;
+ } else {
+ tone |= FTDM_MF_DIRECTION_BACKWARD;
+ }
+
+ return tone;
+}
+
+/* MF generation routines (using IO command of a FreeTDM channel)
+ write_init stores the direction of the MF to generate */
+static void *ftdm_r2_io_mf_write_init(ftdm_r2_mf_write_handle_t *handle, int forward_signals)
+{
+ ftdm_log_chan(handle->ftdmchan, FTDM_LOG_DEBUG, "ftdm_r2_io_mf_write_init, "
+ "forward = %d\n", forward_signals);
+
+ handle->fwd = forward_signals;
+ return handle;
+}
+
+static int ftdm_r2_io_mf_generate_tone(ftdm_r2_mf_write_handle_t *handle, int16_t buffer[], int samples)
+{
+ /* Our mf_want_generate implementation always return 0, so mf_generate_tone should never be called */
+ ftdm_assert(0, "ftdm_r2_io_mf_generate_tone not implemented\n");
+ return 0;
+}
+
+/* \brief mf_select_tone starts tone generation or stops current tone
+ * \return 0 on success, -1 on error
+ */
+static int ftdm_r2_io_mf_select_tone(ftdm_r2_mf_write_handle_t *handle, char signal)
+{
+ int tone; /* (0, 1-15) (0 meaning to stop playing) */
+
+ ftdm_log_chan(handle->ftdmchan, FTDM_LOG_DEBUG, "ftdm_r2_io_mf_select_tone, "
+ "signal = %c\n", signal);
+
+ if (-1 == (tone = ftdm_r2_openr2_mf_tone_to_ftdm_mf_tone(signal, handle->fwd))) {
+ return -1;
+ }
+
+ /* Start/stop playback directly here, as select tone is called each time a tone
+ is started or stopped (called if tone changes, but silence is tone 0,
+ triggering a tone change) */
+ if (tone > 0) {
+ ftdm_channel_command(handle->ftdmchan, FTDM_COMMAND_START_MF_PLAYBACK, &tone);
+ } else {
+ /* tone 0 means to stop current tone */
+ ftdm_channel_command(handle->ftdmchan, FTDM_COMMAND_STOP_MF_PLAYBACK, NULL);
+ }
+ return 0;
+}
+
+static int ftdm_r2_io_mf_want_generate(ftdm_r2_mf_write_handle_t *handle, int signal)
+{
+ /* Return 0, meaning mf_generate_tone doesn't need to be called */
+ return 0;
+}
+
+/* MF lib interface that generate MF tones via FreeTDM channel IO commands
+ MF detection using the default openr2 provider (r2engine) */
+static openr2_mflib_interface_t g_mf_ftdm_io_iface = {
+ /* .mf_read_init */ (openr2_mf_read_init_func)openr2_mf_rx_init,
+ /* .mf_write_init */ (openr2_mf_write_init_func)ftdm_r2_io_mf_write_init,
+ /* .mf_detect_tone */ (openr2_mf_detect_tone_func)openr2_mf_rx,
+ /* .mf_generate_tone */ (openr2_mf_generate_tone_func)ftdm_r2_io_mf_generate_tone,
+ /* .mf_select_tone */ (openr2_mf_select_tone_func)ftdm_r2_io_mf_select_tone,
+ /* .mf_want_generate */ (openr2_mf_want_generate_func)ftdm_r2_io_mf_want_generate,
+ /* .mf_read_dispose */ NULL,
+ /* .mf_write_dispose */ NULL
+};
+
+openr2_mflib_interface_t *ftdm_r2_get_native_channel_mf_generation_iface()
+{
+ return &g_mf_ftdm_io_iface;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
--- /dev/null
+/*
+ * Copyright (c) 2011 Sebastien Trottier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _FTMOD_R2_IO_MFLIB_H_
+#define _FTMOD_R2_IO_MFLIB_H_
+
+#include <ftdm_declare.h>
+
+#include <openr2.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* MFC/R2 tone generator handle (mf_write_handle) */
+typedef struct {
+ /*! FTDM channel performing the MF generation */
+ ftdm_channel_t *ftdmchan;
+ /*! 1 if generating forward tones, otherwise generating reverse tones. */
+ int fwd;
+} ftdm_r2_mf_write_handle_t;
+
+/* MF lib interface that generate MF tones via FreeTDM channel IO commands
+ MF detection using the default openr2 provider (r2engine) */
+openr2_mflib_interface_t *ftdm_r2_get_native_channel_mf_generation_iface(void);
+
+#if defined(__cplusplus)
+} /* endif extern "C" */
+#endif
+
+#endif /* endif defined _FTMOD_R2_IO_MFLIB_H_ */
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
+ */
FTDM_COMMAND_SET_RX_QUEUE_SIZE = 54,
FTDM_COMMAND_SET_TX_QUEUE_SIZE = 55,
FTDM_COMMAND_SET_POLARITY = 56,
+ FTDM_COMMAND_START_MF_PLAYBACK = 57,
+ FTDM_COMMAND_STOP_MF_PLAYBACK = 58,
+
FTDM_COMMAND_COUNT,
} ftdm_command_t;
FTDM_ALARM_GENERAL = (1 << 30)
} ftdm_alarm_flag_t;
+/*! \brief MF generation direction flags
+ * \note Used in bitwise OR with channel ID as argument to MF_PLAYBACK I/O command, so value must be higher that 255
+ * \see FTDM_COMMAND_START_MF_PLAYBACK
+ * */
+
+typedef enum {
+ FTDM_MF_DIRECTION_FORWARD = (1 << 8),
+ FTDM_MF_DIRECTION_BACKWARD = (1 << 9)
+} ftdm_mf_direction_flag_t;
+
/*! \brief Override the default queue handler */
FT_DECLARE(ftdm_status_t) ftdm_global_set_queue_handler(ftdm_queue_handler_t *handler);
* c-basic-offset:4
* End:
* For VIM:
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/
FTDM_CHANNEL_FEATURE_HWEC = (1<<7), /*!< Channel has a hardware echo canceller */
FTDM_CHANNEL_FEATURE_HWEC_DISABLED_ON_IDLE = (1<<8), /*!< hardware echo canceller is disabled when there are no calls on this channel */
FTDM_CHANNEL_FEATURE_IO_STATS = (1<<9), /*!< Channel supports IO statistics (HDLC channels only) */
+ FTDM_CHANNEL_FEATURE_MF_GENERATE = (1<<10), /*!< Channel can generate R2 MF tones (read-only) */
} ftdm_channel_feature_t;
/*!< Channel flags. This used to be an enum but we reached the 32bit limit for enums, is safer this way */