diff --git a/libs/freetdm/.gitignore b/libs/freetdm/.gitignore new file mode 100644 index 0000000000..d90d79d08d --- /dev/null +++ b/libs/freetdm/.gitignore @@ -0,0 +1,13 @@ +*.o +*.lo +*.so +*.a +*.orig +*.rej +*.log + +Makefile +config.* +configure +libtool +aclocal.m4 diff --git a/libs/freetdm/.update b/libs/freetdm/.update new file mode 100644 index 0000000000..e9475b68ed --- /dev/null +++ b/libs/freetdm/.update @@ -0,0 +1 @@ +Fri Oct 3 17:54:41 EDT 2008 diff --git a/libs/freetdm/AUTHORS b/libs/freetdm/AUTHORS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/freetdm/ChangeLog b/libs/freetdm/ChangeLog new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am new file mode 100644 index 0000000000..5f829b6614 --- /dev/null +++ b/libs/freetdm/Makefile.am @@ -0,0 +1,276 @@ +# Copyright (c) 2007, Anthony Minessale II +# 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. + +PREFIX = $(prefix) +SRC = src + +moddir = @modinstdir@ +libdir = @libdir@ +library_includedir = $(PREFIX)/include + +INCS = -I$(FT_SRCDIR)/$(SRC)/include -I$(FT_SRCDIR)/$(SRC)/isdn/include +if HAVE_SCTP +INCS += -I$(FT_SRCDIR)/$(SRC)/ftmod/ftmod_sangoma_boost +endif +MY_CFLAGS = $(INCS) $(FTDM_CFLAGS) -DFTDM_CONFIG_DIR=\"@confdir@\" -DFTDM_MOD_DIR=\"$(moddir)\" @COMP_VENDOR_CFLAGS@ @DEFS@ +COMPILE = $(CC) $(MY_CFLAGS) $(INCS) +LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(COMPILE) +LINK = $(LIBTOOL) --mode=link --tag=CC $(CC) $(MY_CFLAGS) $(LDFLAGS) -o $@ + + +# +# GNU pkgconfig file +# +EXTRA_DIST = freetdm.pc.in + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = freetdm.pc + + +# +# libfreetdm +# +libfreetdm_la_SOURCES = \ +$(SRC)/hashtable.c \ +$(SRC)/hashtable_itr.c \ +$(SRC)/ftdm_io.c \ +$(SRC)/ftdm_queue.c \ +$(SRC)/ftdm_config.c \ +$(SRC)/ftdm_callerid.c \ +$(SRC)/fsk.c \ +$(SRC)/uart.c \ +$(SRC)/g711.c \ +$(SRC)/libteletone_detect.c \ +$(SRC)/libteletone_generate.c \ +$(SRC)/ftdm_buffer.c \ +$(SRC)/ftdm_threadmutex.c \ +$(SRC)/ftdm_dso.c + +library_include_HEADERS = \ +$(SRC)/include/fsk.h \ +$(SRC)/include/g711.h \ +$(SRC)/include/hashtable.h \ +$(SRC)/include/hashtable_itr.h \ +$(SRC)/include/hashtable_private.h \ +$(SRC)/include/libteletone_detect.h \ +$(SRC)/include/libteletone_generate.h \ +$(SRC)/include/libteletone.h \ +$(SRC)/include/freetdm.h \ +$(SRC)/include/sangoma_tdm_api.h \ +$(SRC)/include/uart.h \ +$(SRC)/include/ftdm_buffer.h \ +$(SRC)/include/ftdm_config.h \ +$(SRC)/include/ftdm_threadmutex.h \ +$(SRC)/include/ftdm_dso.h \ +$(SRC)/include/ftdm_types.h + +lib_LTLIBRARIES = libfreetdm.la +libfreetdm_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +libfreetdm_la_LDFLAGS = -version-info 1:0:0 $(AM_LDFLAGS) +libfreetdm_la_LIBADD = $(LIBS) + +MYLIB = libfreetdm.la + +core: libfreetdm.la +core-install: install-libLTLIBRARIES + +# +# tools & test programs +# +noinst_PROGRAMS = testtones detect_tones detect_dtmf testisdn testpri testr2 testanalog testapp testcid +if HAVE_SCTP +noinst_PROGRAMS += testboost +endif +noinst_PROGRAMS += testsangomaboost + +testapp_SOURCES = $(SRC)/testapp.c +testapp_LDADD = libfreetdm.la +testapp_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testcid_SOURCES = $(SRC)/testcid.c +testcid_LDADD = libfreetdm.la +testcid_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testtones_SOURCES = $(SRC)/testtones.c +testtones_LDADD = libfreetdm.la +testtones_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +detect_tones_SOURCES = $(SRC)/detect_tones.c +detect_tones_LDADD = libfreetdm.la +detect_tones_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +detect_dtmf_SOURCES = $(SRC)/detect_dtmf.c +detect_dtmf_LDADD = libfreetdm.la +detect_dtmf_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testisdn_SOURCES = $(SRC)/testisdn.c +testisdn_LDADD = libfreetdm.la +testisdn_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testpri_SOURCES = $(SRC)/testpri.c +testpri_LDADD = libfreetdm.la +testpri_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testr2_SOURCES = $(SRC)/testr2.c +testr2_LDADD = libfreetdm.la +testr2_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +if HAVE_SCTP +testboost_SOURCES = $(SRC)/testboost.c +testboost_LDADD = libfreetdm.la +testboost_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +endif + +testsangomaboost_SOURCES = $(SRC)/testsangomaboost.c +testsangomaboost_LDADD = libfreetdm.la +testsangomaboost_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testanalog_SOURCES = $(SRC)/testanalog.c +testanalog_LDADD = libfreetdm.la +testanalog_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +# +# ftmod modules +# +mod_LTLIBRARIES = ftmod_zt.la ftmod_skel.la ftmod_isdn.la ftmod_analog.la ftmod_analog_em.la + + +if HAVE_SCTP +mod_LTLIBRARIES += ftmod_sangoma_boost.la +endif + +if LIBSANGOMA +mod_LTLIBRARIES += ftmod_wanpipe.la +endif + +if LIBPRI +mod_LTLIBRARIES += ftmod_libpri.la +endif + +if OPENR2 +mod_LTLIBRARIES += ftmod_r2.la +endif + +ftmod_zt_la_SOURCES = $(SRC)/ftmod/ftmod_zt/ftmod_zt.c +ftmod_zt_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ftmod_zt_la_LDFLAGS = -module -avoid-version +ftmod_zt_la_LIBADD = $(MYLIB) + +ftmod_skel_la_SOURCES = $(SRC)/ftmod/ftmod_skel/ftmod_skel.c +ftmod_skel_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ftmod_skel_la_LDFLAGS = -module -avoid-version +ftmod_skel_la_LIBADD = $(MYLIB) + +if LIBSANGOMA +ftmod_wanpipe_la_SOURCES = $(SRC)/ftmod/ftmod_wanpipe/ftmod_wanpipe.c +ftmod_wanpipe_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) -D__LINUX__ -I/usr/include/wanpipe +ftmod_wanpipe_la_LDFLAGS = -module -avoid-version -lsangoma +ftmod_wanpipe_la_LIBADD = $(MYLIB) +endif + +ftmod_isdn_la_SOURCES = \ +$(SRC)/isdn/EuroISDNStateNT.c \ +$(SRC)/isdn/EuroISDNStateTE.c \ +$(SRC)/isdn/mfifo.c \ +$(SRC)/isdn/Q921.c \ +$(SRC)/isdn/Q931api.c \ +$(SRC)/isdn/Q931.c \ +$(SRC)/isdn/Q931ie.c \ +$(SRC)/isdn/Q931mes.c \ +$(SRC)/isdn/Q931StateNT.c \ +$(SRC)/isdn/Q931StateTE.c \ +$(SRC)/isdn/nationalmes.c \ +$(SRC)/isdn/nationalStateNT.c \ +$(SRC)/isdn/nationalStateTE.c \ +$(SRC)/isdn/DMSmes.c \ +$(SRC)/isdn/DMSStateNT.c \ +$(SRC)/isdn/DMSStateTE.c \ +$(SRC)/isdn/5ESSmes.c \ +$(SRC)/isdn/5ESSStateNT.c \ +$(SRC)/isdn/5ESSStateTE.c \ +$(SRC)/isdn/Q932mes.c \ +$(SRC)/ftmod/ftmod_isdn/ftmod_isdn.c + +ftmod_isdn_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) -D_GNU_SOURCE +ftmod_isdn_la_LDFLAGS = $(PCAP_LIB_FLAGS) -module -avoid-version +ftmod_isdn_la_LIBADD = $(MYLIB) + +ftmod_analog_la_SOURCES = $(SRC)/ftmod/ftmod_analog/ftmod_analog.c +ftmod_analog_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ftmod_analog_la_LDFLAGS = -module -avoid-version +ftmod_analog_la_LIBADD = $(MYLIB) + +ftmod_analog_em_la_SOURCES = $(SRC)/ftmod/ftmod_analog_em/ftmod_analog_em.c +ftmod_analog_em_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ftmod_analog_em_la_LDFLAGS = -module -avoid-version +ftmod_analog_em_la_LIBADD = $(MYLIB) + +if HAVE_SCTP +ftmod_sangoma_boost_la_SOURCES = $(SRC)/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c $(SRC)/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c +ftmod_sangoma_boost_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ftmod_sangoma_boost_la_LDFLAGS = -module -avoid-version +ftmod_sangoma_boost_la_LIBADD = $(MYLIB) +endif + +if LIBPRI +ftmod_libpri_la_SOURCES = $(SRC)/ftmod/ftmod_libpri/ftmod_libpri.c $(SRC)/ftmod/ftmod_libpri/lpwrap_pri.c +ftmod_libpri_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ftmod_libpri_la_LDFLAGS = -module -avoid-version -lpri +ftmod_libpri_la_LIBADD = $(MYLIB) +endif + +if OPENR2 +ftmod_r2_la_SOURCES = $(SRC)/ftmod/ftmod_r2/ftmod_r2.c +ftmod_r2_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ftmod_r2_la_LDFLAGS = -module -avoid-version -lopenr2 +ftmod_r2_la_LIBADD = $(MYLIB) +endif + + +dox doxygen: + cd docs && doxygen $(FT_SRCDIR)/docs/Doxygen.conf + +mod_freetdm/mod_freetdm.$(DYNAMIC_LIB_EXTEN): $(MYLIB) mod_freetdm/mod_freetdm.c + cd mod_freetdm && make + +mod_freetdm: mod_freetdm/mod_freetdm.$(DYNAMIC_LIB_EXTEN) + +mod_freetdm-install: mod_freetdm + cd mod_freetdm && make install + +mod_freetdm-clean: + @if [ -f mod_freetdm/mod_freetdm.$(DYNAMIC_LIB_EXTEN) ] ; then cd mod_freetdm && make clean ; fi + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(PREFIX) + $(mkinstalldirs) $(DESTDIR)@confdir@ + @[ -f "$(DESTDIR)@confdir@/freetdm.conf" ] || ( cp conf/*.conf $(DESTDIR)@confdir@) + @echo OpenFTDM Installed diff --git a/libs/freetdm/NEWS b/libs/freetdm/NEWS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/freetdm/README b/libs/freetdm/README new file mode 100644 index 0000000000..2c2119f55b --- /dev/null +++ b/libs/freetdm/README @@ -0,0 +1,3 @@ +FREETDM (WORK IN PROGRESS) + +*shrug* diff --git a/libs/freetdm/TODO b/libs/freetdm/TODO new file mode 100644 index 0000000000..45b134bad3 --- /dev/null +++ b/libs/freetdm/TODO @@ -0,0 +1,9 @@ +== Interface inconsistency == +- enum_id member of ftdm_event_t is inconsistent. Most of the time is just for OOB events, the only other + type of event as of now is FTDM_EVENT_DTMF and is not using the enum_id member. I think we can get rid + of the FTDM_EVENT_DTMF and create ftdm_dtmf_event_t type instead of reusing ftdm_event_t + then ftdm_event_t would be renamed to ftdm_oob_event_t and the enum_id renamed to type, then ftdm_span_next_event() + will only return OOB events + +- query span hw status (connected/disconnected) on startup + diff --git a/libs/freetdm/acinclude.m4 b/libs/freetdm/acinclude.m4 new file mode 100644 index 0000000000..908ddba855 --- /dev/null +++ b/libs/freetdm/acinclude.m4 @@ -0,0 +1 @@ +m4_include([build/libpcap.m4]) diff --git a/libs/freetdm/bootstrap b/libs/freetdm/bootstrap new file mode 100755 index 0000000000..8ca4856832 --- /dev/null +++ b/libs/freetdm/bootstrap @@ -0,0 +1,6 @@ +#!/bin/bash +autoheader +libtoolize --force --copy +aclocal +automake -f --copy --add-missing +autoconf diff --git a/libs/freetdm/build/libpcap.m4 b/libs/freetdm/build/libpcap.m4 new file mode 100644 index 0000000000..c20d345ca6 --- /dev/null +++ b/libs/freetdm/build/libpcap.m4 @@ -0,0 +1,144 @@ +dnl libpcap.m4--PCAP libraries and includes +dnl Derrick Brashear +dnl from KTH krb and Arla +dnl $Id: libpcap.m4,v 1.4 2006/01/20 20:21:09 snsimon Exp $ + +AC_DEFUN([PCAP_INC_WHERE1], [ +ac_cv_found_pcap_inc=no +if test -f "$1/pcap.h" ; then + ac_cv_found_pcap_inc=yes +fi +]) + +AC_DEFUN([PCAP_INC_WHERE], [ + for i in $1; do + AC_MSG_CHECKING(for pcap header in $i) + PCAP_INC_WHERE1($i) + if test "$ac_cv_found_pcap_inc" = "yes"; then + ac_cv_pcap_where_inc=$i + AC_MSG_RESULT(found) + break + else + AC_MSG_RESULT(no found) + fi + done +]) + +AC_DEFUN([PCAP_LIB_WHERE1], [ +saved_LIBS=$LIBS +LIBS="$saved_LIBS -L$1 -lpcap" +AC_TRY_LINK(, +[pcap_lookupdev("");], +[ac_cv_found_pcap_lib=yes], +ac_cv_found_pcap_lib=no) +LIBS=$saved_LIBS +]) + +AC_DEFUN([TEST_LIBPATH], [ +changequote(<<, >>) +define(<>, translit(ac_cv_found_$2_lib, <<- *>>, <<__p>>)) +changequote([, ]) +if test "$AC_CV_FOUND" = "yes"; then + if test \! -r "$1/lib$2.a" -a \! -r "$1/lib$2.so" -a \! -r "$1/lib$2.sl" -a \! -r "$1/lib$2.dylib"; then + AC_CV_FOUND=no + fi +fi +]) + + +AC_DEFUN([PCAP_LIB_WHERE], [ + for i in $1; do + AC_MSG_CHECKING(for pcap library in $i) + PCAP_LIB_WHERE1($i) + TEST_LIBPATH($i, pcap) + if test "$ac_cv_found_pcap_lib" = "yes" ; then + ac_cv_pcap_where_lib=$i + AC_MSG_RESULT(found) + break + else + AC_MSG_RESULT(no found) + fi + done +]) + +AC_DEFUN([FIND_LIB_SUBDIR], +[dnl +AC_ARG_WITH([lib-subdir], AC_HELP_STRING([--with-lib-subdir=DIR],[Find libraries in DIR instead of lib])) +AC_CHECK_SIZEOF(long) +AC_CACHE_CHECK([what directory libraries are found in], [ac_cv_cmu_lib_subdir], +[test "X$with_lib_subdir" = "Xyes" && with_lib_subdir= +test "X$with_lib_subdir" = "Xno" && with_lib_subdir= + if test "X$with_lib_subdir" = "X" ; then + ac_cv_cmu_lib_subdir=lib + if test $ac_cv_sizeof_long -eq 4 ; then + test -d /usr/lib32 && ac_cv_cmu_lib_subdir=lib32 + test -r /usr/lib/libpcap.so && ac_cv_cmu_lib_subdir=lib + fi + if test $ac_cv_sizeof_long -eq 8 ; then + test -d /usr/lib64 && ac_cv_cmu_lib_subdir=lib64 + fi + else + ac_cv_cmu_lib_subdir=$with_lib_subdir + fi]) + AC_SUBST(LIB_SUBDIR, $ac_cv_cmu_lib_subdir) + ]) + + +AC_DEFUN([AX_LIB_PCAP], [ +AC_REQUIRE([FIND_LIB_SUBDIR]) +AC_ARG_WITH(pcap, + [ --with-pcap=PREFIX Compile with PCAP support], + [if test "X$with_pcap" = "X"; then + with_pcap=yes + fi]) +AC_ARG_WITH(pcap-lib, + [ --with-pcap-lib=dir use pcap libraries in dir], + [if test "$withval" = "yes" -o "$withval" = "no"; then + AC_MSG_ERROR([No argument for --with-pcap-lib]) + fi]) +AC_ARG_WITH(pcap-include, + [ --with-pcap-include=dir use pcap headers in dir], + [if test "$withval" = "yes" -o "$withval" = "no"; then + AC_MSG_ERROR([No argument for --with-pcap-include]) + fi]) + + if test "X$with_pcap" != "X"; then + if test "$with_pcap" != "yes"; then + ac_cv_pcap_where_lib=$with_pcap + ac_cv_pcap_where_inc=$with_pcap/include + fi + fi + + if test "X$with_pcap_lib" != "X"; then + ac_cv_pcap_where_lib=$with_pcap_lib + fi + if test "X$ac_cv_pcap_where_lib" = "X"; then + PCAP_LIB_WHERE(/usr/$LIB_SUBDIR /usr/local/$LIB_SUBDIR) + fi + + if test "X$with_pcap_include" != "X"; then + ac_cv_pcap_where_inc=$with_pcap_include + fi + if test "X$ac_cv_pcap_where_inc" = "X"; then + PCAP_INC_WHERE(/usr/ng/include /usr/include /usr/local/include) + fi + + AC_MSG_CHECKING(whether to include pcap) + if test "X$ac_cv_pcap_where_lib" != "X" -a "X$ac_cv_pcap_where_inc" != "X"; then + ac_cv_found_pcap=yes + AC_MSG_RESULT(yes) + PCAP_INC_DIR=$ac_cv_pcap_where_inc + PCAP_LIB_DIR=$ac_cv_pcap_where_lib + PCAP_INC_FLAGS="-I${PCAP_INC_DIR}" + PCAP_LIB_FLAGS="-L${PCAP_LIB_DIR} -lpcap" + AC_SUBST(PCAP_INC_DIR) + AC_SUBST(PCAP_LIB_DIR) + AC_SUBST(PCAP_INC_FLAGS) + AC_SUBST(PCAP_LIB_FLAGS) + AC_DEFINE([HAVE_LIBPCAP],[1],[libpcap]) + else + ac_cv_found_pcap=no + AC_MSG_RESULT(no) + fi + ]) + diff --git a/libs/freetdm/conf/freetdm.conf b/libs/freetdm/conf/freetdm.conf new file mode 100644 index 0000000000..1ea1a9f62a --- /dev/null +++ b/libs/freetdm/conf/freetdm.conf @@ -0,0 +1,19 @@ +[span wanpipe] +name => FreeTDM +number => 1 +fxs-channel => 1:3-4 + +[span wanpipe] +fxo-channel => 1:1-2 + +[span zt] +name => FreeTDM +number => 2 +fxs-channel => 1 + +[span zt] +name => FreeTDM +number => 2 +fxo-channel => 3 + + diff --git a/libs/freetdm/conf/freetdm.conf.xml b/libs/freetdm/conf/freetdm.conf.xml new file mode 100644 index 0000000000..69bf99a0a0 --- /dev/null +++ b/libs/freetdm/conf/freetdm.conf.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/conf/m3ua.conf b/libs/freetdm/conf/m3ua.conf new file mode 100644 index 0000000000..e3eeed3a4a --- /dev/null +++ b/libs/freetdm/conf/m3ua.conf @@ -0,0 +1,38 @@ +;M3UA SS7 Links Config +; +;ss7box-m3ua_mode => true +;local_sctp_ip => localhost +;local sctp_port => 30000 +;remote_sctp_ip => localhost +;remote_sctp_port => 30001 +;opc => 0-0-0 +;dpc => 0-0-0 + + +; AP Specific Stuff. This will likely move later. + +; CNAM Gateways +cnam1_dpc => 0-0-0 +cnam1_ssn => 253 +cnam2_dpc => 0-0-0 +cnam2_ssn => 253 +cnam3_dpc => 0-0-0 +cnam3_ssn => 253 + +;LNP Gateways +lnp1_dpc => 0-0-0 +lnp1_ssn => 253 +lnp2_dpc => 0-0-0 +lnp2_ssn => 253 +lnp3_dpc => 0-0-0 +lnp3_ssn => 253 + +;LNP Gateways +sms8001_dpc => 0-0-0 +sms8001_ssn => 253 +sms8002_dpc => 0-0-0 +sms8002_ssn => 253 +sms8003_dpc => 0-0-0 +sms8003_ssn => 253 + + diff --git a/libs/freetdm/conf/pika.conf b/libs/freetdm/conf/pika.conf new file mode 100644 index 0000000000..8cc8d9c11d --- /dev/null +++ b/libs/freetdm/conf/pika.conf @@ -0,0 +1,41 @@ +; each category is a config profile +; to apply the profile append it to a channel def in +; openzap.conf with @ +; e.g. +; [span pika] +; name => pika +; number => pika +; fxs-channel => 1:0:1-12@default + +[default] +; region is na or eu +;region => na +;rx-gain => 0.00 +;rx-agc-enabled => false +;rx-agc-targetPower => -15.00 +;rx-agc-minGain => -6.00 +;rx-agc-maxGain => 18.00 +;rx-agc-attackRate => 170 +;rx-agc-decayRate => 750 +;rx-agc-speechThreshold => -36.00 +;rx-vad-enabled => false +;rx-vad-activationThreshold => -40.00 +;rx-vad-activationDebounceTime => 72 +;rx-vad-deactivationThreshold => -40.00 +;rx-vad-deactivationDebounceTime => 984 +;rx-vad-preSpeechBufferSize => 240 +;tx-gain => 0.00 +;tx-agc-enabled => true +;tx-agc-targetPower => -15.00 +;tx-agc-minGain => -6.00 +;tx-agc-maxGain => 18.00 +;tx-agc-attackRate => 170 +;tx-agc-decayRate => 750 +;tx-agc-speechThreshold => -36.00 +;ec-enabled => false +;ec-doubleTalkerThreshold => -6.00 +;ec-speechPresentThreshold => -40.00 +;ec-echoSuppressionThreshold => -18.00 +;ec-echoSuppressionEnabled => true +;ec-comfortNoiseEnabled => true +;ec-adaptationModeEnabled => true diff --git a/libs/freetdm/conf/tones.conf b/libs/freetdm/conf/tones.conf new file mode 100644 index 0000000000..36db1d4d91 --- /dev/null +++ b/libs/freetdm/conf/tones.conf @@ -0,0 +1,63 @@ +[us] +generate-dial => v=-7;%(1000,0,350,440) +detect-dial => 350,440 + +generate-ring => v=-7;%(2000,4000,440,480) +detect-ring => 440,480 + +generate-busy => v=-7;%(500,500,480,620) +detect-busy => 480,620 + +generate-attn => v=0;%(100,100,1400,2060,2450,2600) +detect-attn => 1400,2060,2450,2600 + +generate-callwaiting-sas => v=0;%(300,0,440) +detect-callwaiting-sas => 440 + +generate-callwaiting-cas => v=0;%(80,0,2750,2130) +detect-callwaiting-cas => 2750,2130 + +detect-fail1 => 913.8 +detect-fail2 => 1370.6 +detect-fail3 => 1776.7 + + +[sg] +generate-dial => v=-7;%(1000,0,425) +detect-dial => 425 + +generate-ring => v=-7;%(2000,4000,425) +detect-ring => 425 + +generate-busy => v=-7;%(750,750,425) +detect-busy => 425 + +generate-attn => v=0;%(100,100,1400,2060,2450,2600) +detect-attn => 1400,2060,2450,2600 + +generate-callwaiting-sas => v=0;%(300,0,440) +detect-callwaiting-sas => 440 + +generate-callwaiting-cas => v=0;%(80,0,2750,2130) +detect-callwaiting-cas => 2750,2130 + +detect-fail1 => 913.8 +detect-fail2 => 1370.6 +detect-fail3 => 1776.7 + +[ru] +generate-dial => v=-7;%(1000,425) +detect-dial => 0 +generate-ring => v=-7;%(800,5000,425,0) +detect-ring => 425,0 +generate-busy => v=-7;%(350,350,425,0) +detect-busy => 425,0 +generate-attn => v=0;%(100,100,1400,2060,2450,2600) +detect-attn => 1400,2060,2450,2600 +generate-callwaiting-sas => v=0;%(300,0,440) +detect-callwaiting-sas => 440,480 +generate-callwaiting-cas => v=0;%(80,0,2750,2130) +detect-callwaiting-cas => 2750,2130 +detect-fail1 => 913.8 +detect-fail2 => 1370.6 +detect-fail3 => 1776.7 diff --git a/libs/freetdm/conf/wanpipe.conf b/libs/freetdm/conf/wanpipe.conf new file mode 100644 index 0000000000..ba609ac42e --- /dev/null +++ b/libs/freetdm/conf/wanpipe.conf @@ -0,0 +1,4 @@ +[defaults] +codec_ms => 20 +wink_ms => 150 +flash_ms => 750 diff --git a/libs/freetdm/conf/zt.conf b/libs/freetdm/conf/zt.conf new file mode 100644 index 0000000000..a060a40315 --- /dev/null +++ b/libs/freetdm/conf/zt.conf @@ -0,0 +1,8 @@ +[defaults] +codec_ms => 20 +wink_ms => 150 +flash_ms => 750 +echo_cancel_level => 64 +rxgain => 0.0 +txgain => 0.0 + diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac new file mode 100644 index 0000000000..fc141e888e --- /dev/null +++ b/libs/freetdm/configure.ac @@ -0,0 +1,181 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([freetdm],[pre-alpha],[bugs@freeswitch.org]) +AC_CONFIG_SRCDIR([src/ftdm_io.c]) + +AC_CONFIG_AUX_DIR(build) +AM_INIT_AUTOMAKE(libfreetdm,0.1) + +# Checks for programs. +AC_PROG_CC +AC_PROG_MAKE_SET +AM_PROG_CC_C_O + +AC_PREFIX_DEFAULT(/usr/local/freetdm) +# AC_PREFIX_DEFAULT does not get expanded until too late so we need to do this to use prefix in this script +if test "x$prefix" = "xNONE" ; then + prefix='/usr/local/freetdm' +fi + +# Absolute source/build directory +FT_SRCDIR=`(cd $srcdir && pwd)` +ft_builddir=`pwd` +AC_SUBST(FT_SRCDIR) +AC_SUBST(ft_builddir) + +if test "$sysconfdir" = "\${prefix}/etc" ; then + confdir="$prefix/conf" +else + confdir="$sysconfdir" +fi + +AC_SUBST(confdir) + +#override some default libtool behavior and invoke AC_PROG_LIBTOOL (see http://lists.gnu.org/archive/html/libtool/2007-03/msg00000.html) +m4_defun([_LT_AC_LANG_F77_CONFIG], [:]) +m4_defun([_LT_AC_LANG_GCJ_CONFIG], [:]) +m4_defun([_LT_AC_LANG_RC_CONFIG], [:]) +#AM_PROG_CC_C_O +AC_PROG_LIBTOOL +AC_PROG_INSTALL + +# Check for com;iler type +AC_DEFUN([AX_COMPILER_VENDOR], +[ +AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, + [ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown + # note: don't check for GCC first, since some other compilers define __GNUC__ + for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do + vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ +#if !($vencpp) + thisisanerror; +#endif +])], [ax_cv_]_AC_LANG_ABBREV[_compiler_vendor=`echo $ventest | cut -d: -f1`; break]) + done + ]) +]) +AC_ARG_ENABLE([enable_64], [AS_HELP_STRING([--enable-64], [Enable 64bit compilation])], [enable_64="$enableval"], [enable_64="no"]) + +AX_COMPILER_VENDOR + +case "${ax_cv_c_compiler_vendor}" in +gnu) + COMP_VENDOR_CFLAGS="-ffast-math -Wall -Werror -Wunused-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -O0" + ;; +sun) + COMP_VENDOR_CFLAGS="-xc99=all -mt -xCC -D__FUNCTION__=__func__ -xvpara" + if test "$enable_64" = "yes" ; then + COMP_VENDOR_CFLAGS="-m64 $COMP_VENDOR_CFLAGS" + fi + ;; +*) + COMP_VENDOR_CFLAGS="-std=c99 -Wall -Wunused-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes" + ;; +esac + + +#set SOLINK variable based on compiler and host +if test "x${ax_cv_c_compiler_vendor}" = "xsun" ; then + SOLINK="-Bdynamic -dy -G" +elif test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then + case "$host" in + *darwin*) + SOLINK="-dynamic -bundle -force-flat-namespace" + ;; + *-solaris2*) + SOLINK="-shared -Xlinker" + ;; + *) + SOLINK="-shared -Xlinker -x" + ;; + esac +else + AC_ERROR([Please update configure.in with SOLINK values for your compiler]) +fi + +# set DYNAMIC_LIB_EXTEN +# we should really be using libtool so we don't need to do this +case "$host" in + *cygwin* | *mingw*) + DYNAMIC_LIB_EXTEN="dll" + ;; + *) + DYNAMIC_LIB_EXTEN="so" + ;; +esac + +AC_SUBST(SOLINK) +AC_SUBST(DYNAMIC_LIB_EXTEN) + +AC_CHECK_LIB([dl], [dlopen]) +AC_CHECK_LIB([pthread], [pthread_create]) +AC_CHECK_LIB([m], [cos]) +AX_LIB_PCAP + +AC_CHECK_HEADERS([netinet/sctp.h netdb.h sys/select.h]) +AM_CONDITIONAL([HAVE_SCTP],[test "${ac_cv_header_netinet_sctp_h}" = "yes"]) + +AC_CHECK_FUNC([gethostbyname_r], + [], [AC_CHECK_LIB([nsl], [gethostbyname_r])] +) +if test "$ac_cv_func_gethostbyname_r" = "yes" -o "$ac_cv_lib_nsl_gethostbyname_r" = "yes" ; then + +AC_MSG_CHECKING([whether gethostbyname_r requires five arguments]) + +ac_cv_func_gethostbyname_r_five_args="no" + +AC_TRY_COMPILE([#include ], + [char *name; + struct hostent *he, *res; + char buffer[2048]; + int buflen = 2048; + (void)gethostbyname_r(name, he, buffer, buflen, &res)], + [ac_cv_func_gethostbyname_r_five_args="yes" + AC_DEFINE([HAVE_GETHOSTBYNAME_R_FIVE], [1], [gethostbyname_r has five arguments])] +) + + AC_MSG_RESULT([$ac_cv_func_gethostbyname_r_five_args]) + AC_DEFINE([HAVE_GETHOSTBYNAME_R],[1],[threadsafe gethostbyname]) +fi + +# Enable debugging +AC_ARG_ENABLE(debug, +[AC_HELP_STRING([--enable-debug],[build with debug information])],[enable_debug="$enableval"],[enable_debug="yes"]) + +if test "${enable_debug}" = "yes"; then + AC_DEFINE([DEBUG],[],[Enable extra debugging.]) + + if test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then + COMP_VENDOR_CFLAGS="$COMP_VENDOR_CFLAGS -g -ggdb" + fi +fi + +# Where to install the modules +AC_ARG_WITH([modinstdir], + [AS_HELP_STRING([--with-modinstdir=DIR], [Install modules into this location (default: $prefix/mod)])], [modinstdir="$withval"], [modinstdir="${prefix}/mod"]) + +AC_SUBST(modinstdir) + + +# libpri? +AC_ARG_WITH([libpri], + [AS_HELP_STRING([--with-libpri], [Install ftmod_libpri])], [enable_libpri="yes"], [enable_libpri="no"]) +AC_SUBST(enable_libpri) + +AC_CHECK_LIB([sangoma], [sangoma_span_chan_toif], [have_libsangoma="yes"]) +AM_CONDITIONAL([LIBSANGOMA],[test "${have_libsangoma}" = "yes"]) + +AM_CONDITIONAL([LIBPRI],[test "${enable_libpri}" = "yes"]) + +AC_CHECK_LIB([openr2], [openr2_context_set_io_type], [have_openr2="yes"]) +AM_CONDITIONAL([OPENR2],[test "${have_openr2}" = "yes"]) + +COMP_VENDOR_CFLAGS="$COMP_VENDOR_CFLAGS" +AC_SUBST(COMP_VENDOR_CFLAGS) +AC_CONFIG_FILES([Makefile + freetdm.pc + mod_freetdm/Makefile]) +AC_OUTPUT diff --git a/libs/freetdm/configure.gnu b/libs/freetdm/configure.gnu new file mode 100644 index 0000000000..5785ffc0e7 --- /dev/null +++ b/libs/freetdm/configure.gnu @@ -0,0 +1,3 @@ +#! /bin/sh +./configure "$@" --with-pic + diff --git a/libs/freetdm/cyginstall.sh b/libs/freetdm/cyginstall.sh new file mode 100644 index 0000000000..2536889a65 --- /dev/null +++ b/libs/freetdm/cyginstall.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# this script must be run from openzap root dir and it is assuming +# FreeSWITCH is trunk is located at ../../ +fsdir=../.. +set -x +cp Debug/*.dll $fsdir/Debug/ +cp Debug/mod/*.dll $fsdir/Debug/mod/ +cp Debug/*.pdb $fsdir/Debug/ +echo "FRIENDLY REMINDER: RECOMPILE ftmod_wanpipe WHENEVER YOU INSTALL NEW DRIVERS" +set +x + diff --git a/libs/freetdm/docs/Doxygen.conf b/libs/freetdm/docs/Doxygen.conf new file mode 100644 index 0000000000..c4e47076fa --- /dev/null +++ b/libs/freetdm/docs/Doxygen.conf @@ -0,0 +1,277 @@ +# Doxyfile 1.4.6 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = OpenZAP +PROJECT_NUMBER = +OUTPUT_DIRECTORY = . +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = YES +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +IGNORE_PREFIX = zap_ ZAP_ Q921 Q931 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ../src ../src/include \ + ../src/isdn ../src/isdn/include \ + ../mod_openzap ../ \ + ../src/ozmod \ + ../src/ozmod/ozmod_analog \ + ../src/ozmod/ozmod_analog_em \ + ../src/ozmod/ozmod_isdn \ + ../src/ozmod/ozmod_pika \ + ../src/ozmod/ozmod_skel \ + ../src/ozmod/ozmod_ss7_boost \ + ../src/ozmod/ozmod_wanpipe \ + ../src/ozmod/ozmod_zt + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = YES +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +USE_HTAGS = YES +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 1 +IGNORE_PREFIX = zap_ +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = freeswitch.chm +HHC_LOCATION = +GENERATE_CHI = YES +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = YES +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = NO +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = *.h +PREDEFINED = ZAP_DECLARE(x)=x \ + APR_DECLARE(x)=x \ + ZAP_MOD_DECLARE(x)=x \ + DoxyDefine(x)=x + +EXPAND_AS_DEFINED = NO +SKIP_FUNCTION_MACROS = NO +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = jpg +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/libs/freetdm/freetdm.2008.sln b/libs/freetdm/freetdm.2008.sln new file mode 100644 index 0000000000..363ea1e996 --- /dev/null +++ b/libs/freetdm/freetdm.2008.sln @@ -0,0 +1,112 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "freetdm", "msvc\freetdm.2008.vcproj", "{93B8812C-3EC4-4F78-8970-FFBFC99E167D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testanalog", "msvc\testanalog\testanalog.2008.vcproj", "{BB833648-BAFF-4BE2-94DB-F8BB043C588C}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testisdn", "msvc\testisdn\testisdn.2008.vcproj", "{6DA6FD42-641D-4147-92F5-3BC4AAA6589B}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_freetdm", "mod_freetdm\mod_freetdm.2008.vcproj", "{FE3540C5-3303-46E0-A69E-D92F775687F1}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_analog", "src\ftmod\ftmod_analog\ftmod_analog.2008.vcproj", "{37C94798-6E33-4B4F-8EE0-C72A7DC91157}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_analog_em", "src\ftmod\ftmod_analog_em\ftmod_analog_em.2008.vcproj", "{B3F49375-2834-4937-9D8C-4AC2EC911010}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_isdn", "src\ftmod\ftmod_isdn\ftmod_isdn.2008.vcproj", "{729344A5-D5E9-434D-8EE8-AF8C6C795D15}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_pika", "src\ftmod\ftmod_pika\ftmod_pika.2008.vcproj", "{E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_wanpipe", "src\ftmod\ftmod_wanpipe\ftmod_wanpipe.2008.vcproj", "{1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftmod_sangoma_boost", "src\ftmod\ftmod_sangoma_boost\ftmod_sangoma_boost.2008.vcproj", "{D021EF2A-460D-4827-A0F7-41FDECF46F1B}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testboost", "msvc\testboost\testboost.2008.vcproj", "{2B1BAF36-0241-43E7-B865-A8338AD48E2E}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsangomaboost", "msvc\testboost\testsangomaboost.2008.vcproj", "{0DA69C18-4FA1-4E8C-89CE-12498637C5BE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|Win32.ActiveCfg = Debug|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|Win32.Build.0 = Debug|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|Win32.ActiveCfg = Release|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|Win32.Build.0 = Release|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|Win32.ActiveCfg = Debug|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|Win32.Build.0 = Debug|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|Win32.ActiveCfg = Release|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|Win32.Build.0 = Release|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|Win32.Build.0 = Debug|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|Win32.ActiveCfg = Release|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|Win32.Build.0 = Release|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|Win32.ActiveCfg = Debug|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|Win32.Build.0 = Debug|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|Win32.ActiveCfg = Release|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|Win32.Build.0 = Release|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|Win32.ActiveCfg = Debug|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|Win32.Build.0 = Debug|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|Win32.ActiveCfg = Release|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|Win32.Build.0 = Release|Win32 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Debug|Win32.ActiveCfg = Debug|Win32 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Debug|Win32.Build.0 = Debug|Win32 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Release|Win32.ActiveCfg = Release|Win32 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Release|Win32.Build.0 = Release|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|Win32.ActiveCfg = Debug|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|Win32.Build.0 = Debug|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|Win32.ActiveCfg = Release|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|Win32.Build.0 = Release|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Debug|Win32.ActiveCfg = Debug|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Release|Win32.ActiveCfg = Release|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Debug|Win32.ActiveCfg = Debug|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Release|Win32.ActiveCfg = Release|Win32 + {D021EF2A-460D-4827-A0F7-41FDECF46F1B}.Debug|Win32.ActiveCfg = Debug|Win32 + {D021EF2A-460D-4827-A0F7-41FDECF46F1B}.Debug|Win32.Build.0 = Debug|Win32 + {D021EF2A-460D-4827-A0F7-41FDECF46F1B}.Release|Win32.ActiveCfg = Release|Win32 + {D021EF2A-460D-4827-A0F7-41FDECF46F1B}.Release|Win32.Build.0 = Release|Win32 + {2B1BAF36-0241-43E7-B865-A8338AD48E2E}.Debug|Win32.ActiveCfg = Debug|Win32 + {2B1BAF36-0241-43E7-B865-A8338AD48E2E}.Debug|Win32.Build.0 = Debug|Win32 + {2B1BAF36-0241-43E7-B865-A8338AD48E2E}.Release|Win32.ActiveCfg = Release|Win32 + {2B1BAF36-0241-43E7-B865-A8338AD48E2E}.Release|Win32.Build.0 = Release|Win32 + {0DA69C18-4FA1-4E8C-89CE-12498637C5BE}.Debug|Win32.ActiveCfg = Debug|Win32 + {0DA69C18-4FA1-4E8C-89CE-12498637C5BE}.Debug|Win32.Build.0 = Debug|Win32 + {0DA69C18-4FA1-4E8C-89CE-12498637C5BE}.Release|Win32.ActiveCfg = Release|Win32 + {0DA69C18-4FA1-4E8C-89CE-12498637C5BE}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/libs/freetdm/freetdm.pc.in b/libs/freetdm/freetdm.pc.in new file mode 100644 index 0000000000..082ed5c8f6 --- /dev/null +++ b/libs/freetdm/freetdm.pc.in @@ -0,0 +1,17 @@ +# +# OpenZAP pkg-config file +# +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: OpenZAP +Description: +Version: @PACKAGE_VERSION@ +URL: http://www.openzap.org/ +Requires: +Conflicts: +Libs: -L${libdir} -lopenzap +Libs.private: -lm +Cflags: -I${includedir} diff --git a/libs/freetdm/mod_freetdm/Makefile.in b/libs/freetdm/mod_freetdm/Makefile.in new file mode 100644 index 0000000000..ceea0dfbba --- /dev/null +++ b/libs/freetdm/mod_freetdm/Makefile.in @@ -0,0 +1,23 @@ +FT_CFLAGS=@CFLAGS@ @COMP_VENDOR_CFLAGS@ @DEFS@ + +BASE=../../.. +FT_DIR=.. +VERBOSE=1 +FTLA=$(FT_DIR)/libfreetdm.la +LOCAL_CFLAGS=-I$(FT_DIR)/src/include -I$(FT_DIR)/src/isdn/include $(FT_CFLAGS) +LOCAL_LDFLAGS=-L$(FT_DIR) -lfreetdm +include $(BASE)/build/modmake.rules + +local_depend: $(FTLA) + +$(FTLA): $(FT_DIR)/.update + cd $(FT_DIR) && $(MAKE) + +local_install: + cd $(FT_DIR) && $(MAKE) install + [ -f $(DESTDIR)@confdir@/autoload_configs/freetdm.conf.xml ] || cp -f $(FT_DIR)/conf/freetdm.conf.xml $(DESTDIR)@confdir@/autoload_configs + +local_clean: + cd $(FT_DIR) && $(MAKE) clean + + diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.2008.vcproj b/libs/freetdm/mod_freetdm/mod_freetdm.2008.vcproj new file mode 100644 index 0000000000..b59214cf89 --- /dev/null +++ b/libs/freetdm/mod_freetdm/mod_freetdm.2008.vcproj @@ -0,0 +1,369 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c new file mode 100644 index 0000000000..468bcf10de --- /dev/null +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -0,0 +1,3193 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mftilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mftilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Moises Silva + * + * + * mod_freetdm.c -- FreeTDM Endpoint Module + * + */ +#include +#include "freetdm.h" + +#ifndef __FUNCTION__ +#define __FUNCTION__ __SWITCH_FUNC__ +#endif + +#define FREETDM_VAR_PREFIX "freetdm_" +#define FREETDM_VAR_PREFIX_LEN 8 + +SWITCH_MODULE_LOAD_FUNCTION(mod_freetdm_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_freetdm_shutdown); +SWITCH_MODULE_DEFINITION(mod_freetdm, mod_freetdm_load, mod_freetdm_shutdown, NULL); + +switch_endpoint_interface_t *freetdm_endpoint_interface; + +static switch_memory_pool_t *module_pool = NULL; + +typedef enum { + ANALOG_OPTION_NONE = 0, + ANALOG_OPTION_3WAY = (1 << 0), + ANALOG_OPTION_CALL_SWAP = (1 << 1) +} analog_option_t; + +struct span_config { + ftdm_span_t *span; + char dialplan[80]; + char context[80]; + char dial_regex[256]; + char fail_dial_regex[256]; + char hold_music[256]; + char type[256]; + analog_option_t analog_options; +}; + +static struct span_config SPAN_CONFIG[FTDM_MAX_SPANS_INTERFACE] = {{0}}; + +typedef enum { + TFLAG_IO = (1 << 0), + TFLAG_DTMF = (1 << 1), + TFLAG_CODEC = (1 << 2), + TFLAG_BREAK = (1 << 3), + TFLAG_HOLD = (1 << 4), + TFLAG_DEAD = (1 << 5) +} TFLAGS; + +static struct { + int debug; + char *dialplan; + char *codec_string; + char *codec_order[SWITCH_MAX_CODECS]; + int codec_order_last; + char *codec_rates_string; + char *codec_rates[SWITCH_MAX_CODECS]; + int codec_rates_last; + unsigned int flags; + int fd; + int calls; + char hold_music[256]; + switch_mutex_t *mutex; + analog_option_t analog_options; +} globals; + +struct private_object { + unsigned int flags; + switch_codec_t read_codec; + switch_codec_t write_codec; + switch_frame_t read_frame; + unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_frame_t cng_frame; + unsigned char cng_databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_core_session_t *session; + switch_caller_profile_t *caller_profile; + unsigned int codec; + unsigned int codecs; + unsigned short samprate; + switch_mutex_t *mutex; + switch_mutex_t *flag_mutex; + ftdm_channel_t *ftdmchan; + uint32_t wr_error; +}; + +typedef struct private_object private_t; + + +static switch_status_t channel_on_init(switch_core_session_t *session); +static switch_status_t channel_on_hangup(switch_core_session_t *session); +static switch_status_t channel_on_destroy(switch_core_session_t *session); +static switch_status_t channel_on_routing(switch_core_session_t *session); +static switch_status_t channel_on_exchange_media(switch_core_session_t *session); +static switch_status_t channel_on_soft_execute(switch_core_session_t *session); +static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool, + switch_originate_flag_t flags, switch_call_cause_t *cancel_cause); +static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id); +static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id); +static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig); +ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session_t **sp); +void dump_chan(ftdm_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream); +void dump_chan_xml(ftdm_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream); + +static switch_core_session_t *ftdm_channel_get_session(ftdm_channel_t *channel, int32_t id) +{ + switch_core_session_t *session = NULL; + + if (id > FTDM_MAX_TOKENS) { + return NULL; + } + + if (!zstr(channel->tokens[id])) { + if (!(session = switch_core_session_locate(channel->tokens[id]))) { + ftdm_channel_clear_token(channel, channel->tokens[id]); + } + } + + return session; +} + +static const char *ftdm_channel_get_uuid(ftdm_channel_t *channel, int32_t id) +{ + if (id > FTDM_MAX_TOKENS) { + return NULL; + } + + if (!zstr(channel->tokens[id])) { + return channel->tokens[id]; + } + return NULL; +} + +static void stop_hold(switch_core_session_t *session_a, const char *uuid) +{ + switch_core_session_t *session; + switch_channel_t *channel, *channel_a; + ; + + if (!uuid) { + return; + } + + if ((session = switch_core_session_locate(uuid))) { + channel = switch_core_session_get_channel(session); + + if (switch_channel_test_flag(channel, CF_HOLD)) { + channel_a = switch_core_session_get_channel(session_a); + switch_ivr_unhold(session); + switch_channel_clear_flag(channel_a, CF_SUSPEND); + switch_channel_clear_flag(channel_a, CF_HOLD); + } else { + switch_channel_stop_broadcast(channel); + switch_channel_wait_for_flag(channel, CF_BROADCAST, SWITCH_FALSE, 2000, NULL); + } + + switch_core_session_rwunlock(session); + } +} + +static void start_hold(ftdm_channel_t *ftdmchan, switch_core_session_t *session_a, const char *uuid, const char *stream) +{ + switch_core_session_t *session; + switch_channel_t *channel, *channel_a; + + if (!uuid) { + return; + } + + + if ((session = switch_core_session_locate(uuid))) { + channel = switch_core_session_get_channel(session); + if (zstr(stream)) { + if (!strcasecmp(globals.hold_music, "indicate_hold")) { + stream = "indicate_hold"; + } + if (!strcasecmp(SPAN_CONFIG[ftdmchan->span->span_id].hold_music, "indicate_hold")) { + stream = "indicate_hold"; + } + } + + if (zstr(stream)) { + stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE); + } + + if (zstr(stream)) { + stream = SPAN_CONFIG[ftdmchan->span->span_id].hold_music; + } + + if (zstr(stream)) { + stream = globals.hold_music; + } + + + if (zstr(stream) && !(stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE))) { + stream = globals.hold_music; + } + + if (!zstr(stream)) { + if (!strcasecmp(stream, "indicate_hold")) { + channel_a = switch_core_session_get_channel(session_a); + switch_ivr_hold_uuid(uuid, NULL, 0); + switch_channel_set_flag(channel_a, CF_SUSPEND); + switch_channel_set_flag(channel_a, CF_HOLD); + } else { + switch_ivr_broadcast(switch_core_session_get_uuid(session), stream, SMF_ECHO_ALEG | SMF_LOOP); + } + } + + switch_core_session_rwunlock(session); + } +} + + +static void cycle_foreground(ftdm_channel_t *ftdmchan, int flash, const char *bcast) { + uint32_t i = 0; + switch_core_session_t *session; + switch_channel_t *channel; + private_t *tech_pvt; + + + for (i = 0; i < ftdmchan->token_count; i++) { + if ((session = ftdm_channel_get_session(ftdmchan, i))) { + const char *buuid; + tech_pvt = switch_core_session_get_private(session); + channel = switch_core_session_get_channel(session); + buuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + + + if (ftdmchan->token_count == 1 && flash) { + if (switch_test_flag(tech_pvt, TFLAG_HOLD)) { + stop_hold(session, buuid); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + } else { + start_hold(ftdmchan, session, buuid, bcast); + switch_set_flag_locked(tech_pvt, TFLAG_HOLD); + } + } else if (i) { + start_hold(ftdmchan, session, buuid, bcast); + switch_set_flag_locked(tech_pvt, TFLAG_HOLD); + } else { + stop_hold(session, buuid); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + if (!switch_channel_test_flag(channel, CF_ANSWERED)) { + switch_channel_mark_answered(channel); + } + } + switch_core_session_rwunlock(session); + } + } +} + + + + +static switch_status_t tech_init(private_t *tech_pvt, switch_core_session_t *session, ftdm_channel_t *ftdmchan) +{ + const char *dname = NULL; + uint32_t interval = 0, srate = 8000; + ftdm_codec_t codec; + + tech_pvt->ftdmchan = ftdmchan; + tech_pvt->read_frame.data = tech_pvt->databuf; + tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf); + tech_pvt->cng_frame.data = tech_pvt->cng_databuf; + tech_pvt->cng_frame.buflen = sizeof(tech_pvt->cng_databuf); + tech_pvt->cng_frame.flags = SFF_CNG; + tech_pvt->cng_frame.codec = &tech_pvt->read_codec; + memset(tech_pvt->cng_frame.data, 255, tech_pvt->cng_frame.buflen); + switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + switch_core_session_set_private(session, tech_pvt); + tech_pvt->session = session; + + if (FTDM_SUCCESS != ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve channel interval.\n"); + return SWITCH_STATUS_GENERR; + } + + if (FTDM_SUCCESS != ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_CODEC, &codec)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve channel codec.\n"); + return SWITCH_STATUS_GENERR; + } + + switch(codec) { + case FTDM_CODEC_ULAW: + { + dname = "PCMU"; + } + break; + case FTDM_CODEC_ALAW: + { + dname = "PCMA"; + } + break; + case FTDM_CODEC_SLIN: + { + dname = "L16"; + } + break; + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid codec value retrieved from channel, codec value: %d\n", codec); + return SWITCH_STATUS_GENERR; + } + } + + + if (switch_core_codec_init(&tech_pvt->read_codec, + dname, + NULL, + srate, + interval, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + return SWITCH_STATUS_GENERR; + } else { + if (switch_core_codec_init(&tech_pvt->write_codec, + dname, + NULL, + srate, + interval, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + switch_core_codec_destroy(&tech_pvt->read_codec); + return SWITCH_STATUS_GENERR; + } + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set codec %s %dms\n", dname, interval); + switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec); + switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec); + switch_set_flag_locked(tech_pvt, TFLAG_CODEC); + tech_pvt->read_frame.codec = &tech_pvt->read_codec; + switch_set_flag_locked(tech_pvt, TFLAG_IO); + + return SWITCH_STATUS_SUCCESS; + +} + +static switch_status_t channel_on_init(switch_core_session_t *session) +{ + switch_channel_t *channel; + private_t *tech_pvt = NULL; + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_SUCCESS; + } + + /* Move channel's state machine to ROUTING */ + switch_channel_set_state(channel, CS_ROUTING); + switch_mutex_lock(globals.mutex); + globals.calls++; + switch_mutex_unlock(globals.mutex); + + ftdm_channel_init(tech_pvt->ftdmchan); + + //switch_channel_set_flag(channel, CF_ACCEPT_CNG); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_routing(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_execute(switch_core_session_t *session) +{ + + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel)); + + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_destroy(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + + if ((tech_pvt = switch_core_session_get_private(session))) { + + if (tech_pvt->read_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->read_codec); + } + + if (tech_pvt->write_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->write_codec); + } + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_hangup(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (!tech_pvt->ftdmchan) { + goto end; + } + + ftdm_channel_clear_token(tech_pvt->ftdmchan, switch_core_session_get_uuid(session)); + + switch (tech_pvt->ftdmchan->type) { + case FTDM_CHAN_TYPE_FXO: + case FTDM_CHAN_TYPE_EM: + case FTDM_CHAN_TYPE_CAS: + { + ftdm_set_state_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + break; + case FTDM_CHAN_TYPE_FXS: + { + if (tech_pvt->ftdmchan->state != FTDM_CHANNEL_STATE_BUSY && tech_pvt->ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) { + if (tech_pvt->ftdmchan->token_count) { + cycle_foreground(tech_pvt->ftdmchan, 0, NULL); + } else { + ftdm_set_state_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } + } + break; + case FTDM_CHAN_TYPE_B: + { + if (tech_pvt->ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) { + if (tech_pvt->ftdmchan->state != FTDM_CHANNEL_STATE_TERMINATING) { + tech_pvt->ftdmchan->caller_data.hangup_cause = switch_channel_get_cause_q850(channel); + if (tech_pvt->ftdmchan->caller_data.hangup_cause < 1 || tech_pvt->ftdmchan->caller_data.hangup_cause > 127) { + tech_pvt->ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER; + } + } + ftdm_set_state_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } + break; + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unhandled channel type %d for channel %s\n", tech_pvt->ftdmchan->type, + switch_channel_get_name(channel)); + } + break; + } + + end: + + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel)); + switch_mutex_lock(globals.mutex); + globals.calls--; + if (globals.calls < 0) { + globals.calls = 0; + } + switch_mutex_unlock(globals.mutex); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch (sig) { + case SWITCH_SIG_KILL: + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + break; + case SWITCH_SIG_BREAK: + switch_set_flag_locked(tech_pvt, TFLAG_BREAK); + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_exchange_media(switch_core_session_t *session) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CHANNEL EXCHANGE_MEDIA\n"); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_soft_execute(switch_core_session_t *session) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CHANNEL SOFT_EXECUTE\n"); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) +{ + private_t *tech_pvt = NULL; + char tmp[2] = ""; + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + tmp[0] = dtmf->digit; + ftdm_channel_command(tech_pvt->ftdmchan, FTDM_COMMAND_SEND_DTMF, tmp); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + ftdm_size_t len; + ftdm_wait_flag_t wflags = FTDM_READ; + char dtmf[128] = ""; + ftdm_status_t status; + int total_to; + int chunk, do_break = 0; + + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + return SWITCH_STATUS_FALSE; + } + + /* Digium Cards sometimes timeout several times in a row here. + Yes, we support digium cards, ain't we nice....... + 6 double length intervals should compensate */ + chunk = tech_pvt->ftdmchan->effective_interval * 2; + total_to = chunk * 6; + + top: + + if (switch_channel_test_flag(channel, CF_SUSPEND)) { + do_break = 1; + } + + if (switch_test_flag(tech_pvt, TFLAG_BREAK)) { + switch_clear_flag_locked(tech_pvt, TFLAG_BREAK); + do_break = 1; + } + + if (switch_test_flag(tech_pvt, TFLAG_HOLD) || do_break) { + switch_yield(tech_pvt->ftdmchan->effective_interval * 1000); + tech_pvt->cng_frame.datalen = tech_pvt->ftdmchan->packet_len; + tech_pvt->cng_frame.samples = tech_pvt->cng_frame.datalen; + tech_pvt->cng_frame.flags = SFF_CNG; + *frame = &tech_pvt->cng_frame; + if (tech_pvt->ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + tech_pvt->cng_frame.samples /= 2; + } + return SWITCH_STATUS_SUCCESS; + } + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + goto fail; + } + + wflags = FTDM_READ; + status = ftdm_channel_wait(tech_pvt->ftdmchan, &wflags, chunk); + + if (status == FTDM_FAIL) { + goto fail; + } + + if (status == FTDM_TIMEOUT) { + if (!switch_test_flag(tech_pvt, TFLAG_HOLD)) { + total_to -= chunk; + if (total_to <= 0) { + goto fail; + } + } + + goto top; + } + + if (!(wflags & FTDM_READ)) { + goto fail; + } + + len = tech_pvt->read_frame.buflen; + if (ftdm_channel_read(tech_pvt->ftdmchan, tech_pvt->read_frame.data, &len) != FTDM_SUCCESS) { + goto fail; + } + + *frame = &tech_pvt->read_frame; + tech_pvt->read_frame.datalen = (uint32_t)len; + tech_pvt->read_frame.samples = tech_pvt->read_frame.datalen; + + if (tech_pvt->ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + tech_pvt->read_frame.samples /= 2; + } + + while (ftdm_channel_dequeue_dtmf(tech_pvt->ftdmchan, dtmf, sizeof(dtmf))) { + switch_dtmf_t _dtmf = { 0, SWITCH_DEFAULT_DTMF_DURATION }; + char *p; + for (p = dtmf; p && *p; p++) { + if (is_dtmf(*p)) { + _dtmf.digit = *p; + ftdm_log(FTDM_LOG_DEBUG, "queue DTMF [%c]\n", *p); + switch_channel_queue_dtmf(channel, &_dtmf); + } + } + } + return SWITCH_STATUS_SUCCESS; + + fail: + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + return SWITCH_STATUS_GENERR; + + +} + +static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + ftdm_size_t len; + unsigned char data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; + ftdm_wait_flag_t wflags = FTDM_WRITE; + ftdm_status_t status; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (!tech_pvt->ftdmchan) { + return SWITCH_STATUS_FALSE; + } + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + return SWITCH_STATUS_FALSE; + } + + if (switch_test_flag(tech_pvt, TFLAG_HOLD)) { + return SWITCH_STATUS_SUCCESS; + } + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + goto fail; + } + + if (switch_test_flag(frame, SFF_CNG)) { + frame->data = data; + frame->buflen = sizeof(data); + if ((frame->datalen = tech_pvt->write_codec.implementation->encoded_bytes_per_packet) > frame->buflen) { + goto fail; + } + memset(data, 255, frame->datalen); + } + + + wflags = FTDM_WRITE; + status = ftdm_channel_wait(tech_pvt->ftdmchan, &wflags, tech_pvt->ftdmchan->effective_interval * 10); + + if (!(wflags & FTDM_WRITE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Dropping frame! (write not ready)\n"); + return SWITCH_STATUS_SUCCESS; + } + + len = frame->datalen; + if (ftdm_channel_write(tech_pvt->ftdmchan, frame->data, frame->buflen, &len) != FTDM_SUCCESS) { + if (++tech_pvt->wr_error > 10) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "too many I/O write errors!\n"); + goto fail; + } + } else { + tech_pvt->wr_error = 0; + } + + return SWITCH_STATUS_SUCCESS; + + fail: + + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + return SWITCH_STATUS_GENERR; + +} + +static switch_status_t channel_receive_message_cas(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_channel_t *channel; + private_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + ftdm_log(FTDM_LOG_DEBUG, "Got Freeswitch message in R2 channel %d [%d]\n", tech_pvt->ftdmchan->physical_chan_id, + msg->message_id); + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_RINGING: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_PROGRESS); + } else { + ftdm_set_state_locked_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } + } + break; + case SWITCH_MESSAGE_INDICATE_PROGRESS: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_PROGRESS); + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_MEDIA); + } else { + ftdm_set_state_locked_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + ftdm_set_state_locked_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } + } + break; + case SWITCH_MESSAGE_INDICATE_ANSWER: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_ANSWERED); + } else { + /* lets make the ftmod_r2 module life easier by moving thru each + * state waiting for completion, clumsy, but does the job + */ + if (tech_pvt->ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { + ftdm_set_state_locked_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } + if (tech_pvt->ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { + ftdm_set_state_locked_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } + ftdm_set_state_locked_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_UP); + } + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_message_b(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_channel_t *channel; + private_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + if (tech_pvt->ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { + ftdm_mutex_unlock(tech_pvt->ftdmchan->mutex); + return SWITCH_STATUS_SUCCESS; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_RINGING: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + ftdm_set_flag(tech_pvt->ftdmchan, FTDM_CHANNEL_PROGRESS); + } else { + ftdm_set_state_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } + } + break; + case SWITCH_MESSAGE_INDICATE_PROGRESS: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + ftdm_set_flag(tech_pvt->ftdmchan, FTDM_CHANNEL_PROGRESS); + ftdm_set_flag(tech_pvt->ftdmchan, FTDM_CHANNEL_MEDIA); + } else { + /* Don't skip messages in the ISDN call setup + * TODO: make the isdn stack smart enough to handle that itself + * until then, this is here for safety... + */ + if (tech_pvt->ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { + ftdm_set_state_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } + ftdm_set_state_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } + } + break; + case SWITCH_MESSAGE_INDICATE_ANSWER: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + ftdm_set_flag(tech_pvt->ftdmchan, FTDM_CHANNEL_ANSWERED); + } else { + /* Don't skip messages in the ISDN call setup + * TODO: make the isdn stack smart enough to handle that itself + * until then, this is here for safety... + */ + if (tech_pvt->ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { + ftdm_set_state_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } + if (tech_pvt->ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { + ftdm_set_state_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } + ftdm_set_state_wait(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_UP); + } + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_message_fxo(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_channel_t *channel; + private_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_PROGRESS: + case SWITCH_MESSAGE_INDICATE_ANSWER: + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_ANSWERED); + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_PROGRESS); + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_MEDIA); + } else { + ftdm_set_state_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_UP); + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_message_fxs(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_channel_t *channel; + private_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_PROGRESS: + case SWITCH_MESSAGE_INDICATE_ANSWER: + if (!switch_channel_test_flag(channel, CF_OUTBOUND)) { + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_ANSWERED); + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_PROGRESS); + ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_MEDIA); + ftdm_set_state_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_UP); + switch_channel_mark_answered(channel); + } + break; + case SWITCH_MESSAGE_INDICATE_RINGING: + if (!switch_channel_test_flag(channel, CF_OUTBOUND)) { + + if (!switch_channel_test_flag(channel, CF_ANSWERED) && + !switch_channel_test_flag(channel, CF_EARLY_MEDIA) && + !switch_channel_test_flag(channel, CF_RING_READY) + ) { + ftdm_set_state_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_RING); + switch_channel_mark_ring_ready(channel); + } + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + private_t *tech_pvt; + switch_status_t status; + switch_channel_t *channel; + const char *var; + ftdm_channel_t *ftdmchan = NULL; + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + if (!(ftdmchan = tech_pvt->ftdmchan)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + ftdm_mutex_lock(ftdmchan->mutex); + + if (!tech_pvt->ftdmchan) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + status = SWITCH_STATUS_FALSE; + goto end; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_PROGRESS: + case SWITCH_MESSAGE_INDICATE_ANSWER: + if (!switch_channel_test_flag(channel, CF_OUTBOUND)) { + if ((var = switch_channel_get_variable(channel, "freetdm_pre_buffer_size"))) { + int tmp = atoi(var); + if (tmp > -1) { + ftdm_channel_command(tech_pvt->ftdmchan, FTDM_COMMAND_SET_PRE_BUFFER_SIZE, &tmp); + } + } + } + break; + case SWITCH_MESSAGE_INDICATE_UUID_CHANGE: + { + ftdm_channel_replace_token(tech_pvt->ftdmchan, msg->string_array_arg[0], msg->string_array_arg[1]); + } + break; + default: + break; + } + + switch (tech_pvt->ftdmchan->type) { + case FTDM_CHAN_TYPE_FXS: + case FTDM_CHAN_TYPE_EM: + status = channel_receive_message_fxs(session, msg); + break; + case FTDM_CHAN_TYPE_FXO: + status = channel_receive_message_fxo(session, msg); + break; + case FTDM_CHAN_TYPE_B: + status = channel_receive_message_b(session, msg); + break; + case FTDM_CHAN_TYPE_CAS: + status = channel_receive_message_cas(session, msg); + break; + default: + status = SWITCH_STATUS_FALSE; + break; + } + + end: + + ftdm_mutex_unlock(ftdmchan->mutex); + + return status; + +} + +switch_state_handler_table_t freetdm_state_handlers = { + /*.on_init */ channel_on_init, + /*.on_routing */ channel_on_routing, + /*.on_execute */ channel_on_execute, + /*.on_hangup */ channel_on_hangup, + /*.on_exchange_media */ channel_on_exchange_media, + /*.on_soft_execute */ channel_on_soft_execute, + /*.on_consume_media */ NULL, + /*.on_hibernate */ NULL, + /*.on_reset */ NULL, + /*.on_park*/ NULL, + /*.on_reporting*/ NULL, + /*.on_destroy*/ channel_on_destroy + +}; + +switch_io_routines_t freetdm_io_routines = { + /*.outgoing_channel */ channel_outgoing_channel, + /*.read_frame */ channel_read_frame, + /*.write_frame */ channel_write_frame, + /*.kill_channel */ channel_kill_channel, + /*.send_dtmf */ channel_send_dtmf, + /*.receive_message*/ channel_receive_message +}; + +/* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines +that allocate memory or you will have 1 channel with memory allocated from another channel's pool! +*/ +static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, switch_memory_pool_t **pool, + switch_originate_flag_t flags, switch_call_cause_t *cancel_cause) +{ + + const char *dest = NULL; + char *data = NULL; + int span_id = -1, group_id = -1,chan_id = 0; + ftdm_channel_t *ftdmchan = NULL; + switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + char name[128]; + ftdm_status_t status; + int direction = FTDM_TOP_DOWN; + ftdm_caller_data_t caller_data = {{ 0 }}; + char *span_name = NULL; + switch_event_header_t *h; + char *argv[3]; + int argc = 0; + const char *var; + const char *dest_num = NULL, *callerid_num = NULL; + + if (!outbound_profile) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (zstr(outbound_profile->destination_number)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid dial string\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + + data = switch_core_strdup(outbound_profile->pool, outbound_profile->destination_number); + + if (!zstr(outbound_profile->destination_number)) { + dest_num = switch_sanitize_number(switch_core_strdup(outbound_profile->pool, outbound_profile->destination_number)); + } + + if (!zstr(outbound_profile->caller_id_number)) { + callerid_num = switch_sanitize_number(switch_core_strdup(outbound_profile->pool, outbound_profile->caller_id_number)); + } + + if (!zstr(callerid_num) && !strcmp(callerid_num, "0000000000")) { + callerid_num = NULL; + } + + if ((argc = switch_separate_string(data, '/', argv, (sizeof(argv) / sizeof(argv[0])))) < 2) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid dial string\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (switch_is_number(argv[0])) { + span_id = atoi(argv[0]); + } else { + span_name = argv[0]; + } + + if (*argv[1] == 'A') { + direction = FTDM_BOTTOM_UP; + } else if (*argv[1] == 'a') { + direction = FTDM_TOP_DOWN; + } else { + chan_id = atoi(argv[1]); + } + + if (!(dest = argv[2])) { + dest = ""; + } + + if (span_id == 0 && chan_id != 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Span 0 is used to pick the first available span, selecting a channel is not supported (and doesn't make sense)\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (span_id == -1 && !zstr(span_name)) { + ftdm_span_t *span; + ftdm_status_t zstatus = ftdm_span_find_by_name(span_name, &span); + if (zstatus == FTDM_SUCCESS && span) { + span_id = span->span_id; + } + } + + if (span_id == -1) { + //Look for a group + ftdm_group_t *group; + ftdm_status_t zstatus = ftdm_group_find_by_name(span_name, &group); + if (zstatus == FTDM_SUCCESS && group) { + group_id = group->group_id; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing ftdm span or group: %s\n", span_name); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + } + + if (group_id < 0 && chan_id < 0) { + direction = FTDM_BOTTOM_UP; + chan_id = 0; + } + + if (switch_test_flag(outbound_profile, SWITCH_CPF_SCREEN)) { + caller_data.screen = 1; + } + + if (switch_test_flag(outbound_profile, SWITCH_CPF_HIDE_NUMBER)) { + caller_data.pres = 1; + } + + if (!zstr(dest)) { + ftdm_set_string(caller_data.dnis.digits, dest); + } + + if ((var = switch_event_get_header(var_event, "freetdm_outbound_ton")) || (var = switch_core_get_variable("freetdm_outbound_ton"))) { + if (!strcasecmp(var, "national")) { + caller_data.dnis.type = FTDM_TON_NATIONAL; + } else if (!strcasecmp(var, "international")) { + caller_data.dnis.type = FTDM_TON_INTERNATIONAL; + } else if (!strcasecmp(var, "local")) { + caller_data.dnis.type = FTDM_TON_SUBSCRIBER_NUMBER; + } else if (!strcasecmp(var, "unknown")) { + caller_data.dnis.type = FTDM_TON_UNKNOWN; + } + } else { + caller_data.dnis.type = outbound_profile->destination_number_ton; + } + + caller_data.dnis.plan = outbound_profile->destination_number_numplan; + + /* blindly copy data from outbound_profile. They will be overwritten + * by calling ftdm_caller_data if needed after */ + caller_data.cid_num.type = outbound_profile->caller_ton; + caller_data.cid_num.plan = outbound_profile->caller_numplan; + caller_data.rdnis.type = outbound_profile->rdnis_ton; + caller_data.rdnis.plan = outbound_profile->rdnis_numplan; + + ftdm_set_string(caller_data.cid_name, outbound_profile->caller_id_name); + ftdm_set_string(caller_data.cid_num.digits, switch_str_nil(outbound_profile->caller_id_number)); + + if (group_id >= 0) { + status = ftdm_channel_open_by_group(group_id, direction, &caller_data, &ftdmchan); + } else if (chan_id) { + status = ftdm_channel_open(span_id, chan_id, &ftdmchan); + } else { + status = ftdm_channel_open_by_span(span_id, direction, &caller_data, &ftdmchan); + } + + if (status != FTDM_SUCCESS) { + if (caller_data.hangup_cause == SWITCH_CAUSE_NONE) { + caller_data.hangup_cause = SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; + } + return caller_data.hangup_cause; + } + + if ((var = switch_event_get_header(var_event, "freetdm_pre_buffer_size"))) { + int tmp = atoi(var); + if (tmp > -1) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_PRE_BUFFER_SIZE, &tmp); + } + } + + ftdm_channel_clear_vars(ftdmchan); + for (h = var_event->headers; h; h = h->next) { + if (!strncasecmp(h->name, FREETDM_VAR_PREFIX, FREETDM_VAR_PREFIX_LEN)) { + char *v = h->name + FREETDM_VAR_PREFIX_LEN; + if (!zstr(v)) { + ftdm_channel_add_var(ftdmchan, v, h->value); + } + } + } + + if ((*new_session = switch_core_session_request(freetdm_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, pool)) != 0) { + private_t *tech_pvt; + switch_caller_profile_t *caller_profile; + switch_channel_t *channel = switch_core_session_get_channel(*new_session); + + switch_core_session_add_stream(*new_session, NULL); + if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) { + tech_init(tech_pvt, *new_session, ftdmchan); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); + switch_core_session_destroy(new_session); + cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + goto fail; + } + + snprintf(name, sizeof(name), "FreeTDM/%u:%u/%s", ftdmchan->span_id, ftdmchan->chan_id, dest); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect outbound channel %s\n", name); + switch_channel_set_name(channel, name); + switch_channel_set_variable(channel, "freetdm_span_name", ftdmchan->span->name); + switch_channel_set_variable_printf(channel, "freetdm_span_number", "%d", ftdmchan->span_id); + switch_channel_set_variable_printf(channel, "freetdm_chan_number", "%d", ftdmchan->chan_id); + ftdm_channel_set_caller_data(ftdmchan, &caller_data); + caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); + caller_profile->destination_number = switch_core_strdup(caller_profile->pool, switch_str_nil(dest_num)); + caller_profile->caller_id_number = switch_core_strdup(caller_profile->pool, switch_str_nil(callerid_num)); + switch_channel_set_caller_profile(channel, caller_profile); + tech_pvt->caller_profile = caller_profile; + + + switch_channel_set_flag(channel, CF_OUTBOUND); + switch_channel_set_state(channel, CS_INIT); + if (ftdm_channel_add_token(ftdmchan, switch_core_session_get_uuid(*new_session), ftdmchan->token_count) != FTDM_SUCCESS) { + switch_core_session_destroy(new_session); + cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + goto fail; + } + + + if (ftdm_channel_outgoing_call(ftdmchan) != FTDM_SUCCESS) { + if (tech_pvt->read_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->read_codec); + } + + if (tech_pvt->write_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->write_codec); + } + switch_core_session_destroy(new_session); + cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + goto fail; + } + + ftdm_channel_init(ftdmchan); + + return SWITCH_CAUSE_SUCCESS; + } + + fail: + + if (ftdmchan) { + ftdm_channel_done(ftdmchan); + } + + return cause; + +} + +ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session_t **sp) +{ + switch_core_session_t *session = NULL; + private_t *tech_pvt = NULL; + switch_channel_t *channel = NULL; + char name[128]; + + *sp = NULL; + + if (!(session = switch_core_session_request(freetdm_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Initilization Error!\n"); + return FTDM_FAIL; + } + + switch_core_session_add_stream(session, NULL); + + tech_pvt = (private_t *) switch_core_session_alloc(session, sizeof(private_t)); + assert(tech_pvt != NULL); + channel = switch_core_session_get_channel(session); + if (tech_init(tech_pvt, session, sigmsg->channel) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Initilization Error!\n"); + switch_core_session_destroy(&session); + return FTDM_FAIL; + } + + *sigmsg->channel->caller_data.collected = '\0'; + + if (zstr(sigmsg->channel->caller_data.cid_name)) { + switch_set_string(sigmsg->channel->caller_data.cid_name, sigmsg->channel->chan_name); + } + + if (zstr(sigmsg->channel->caller_data.cid_num.digits)) { + if (!zstr(sigmsg->channel->caller_data.ani.digits)) { + switch_set_string(sigmsg->channel->caller_data.cid_num.digits, sigmsg->channel->caller_data.ani.digits); + } else { + switch_set_string(sigmsg->channel->caller_data.cid_num.digits, sigmsg->channel->chan_number); + } + } + + tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), + "FreeTDM", + SPAN_CONFIG[sigmsg->channel->span_id].dialplan, + sigmsg->channel->caller_data.cid_name, + sigmsg->channel->caller_data.cid_num.digits, + NULL, + sigmsg->channel->caller_data.ani.digits, + sigmsg->channel->caller_data.aniII, + sigmsg->channel->caller_data.rdnis.digits, + (char *) modname, + SPAN_CONFIG[sigmsg->channel->span_id].context, + sigmsg->channel->caller_data.dnis.digits); + + assert(tech_pvt->caller_profile != NULL); + + if (sigmsg->channel->caller_data.screen == 1 || sigmsg->channel->caller_data.screen == 3) { + switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_SCREEN); + } + + if (sigmsg->channel->caller_data.pres) { + switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); + } + + snprintf(name, sizeof(name), "FreeTDM/%u:%u/%s", sigmsg->channel->span_id, sigmsg->channel->chan_id, tech_pvt->caller_profile->destination_number); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect inbound channel %s\n", name); + switch_channel_set_name(channel, name); + switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); + + switch_channel_set_variable(channel, "freetdm_span_name", sigmsg->channel->span->name); + switch_channel_set_variable_printf(channel, "freetdm_span_number", "%d", sigmsg->channel->span_id); + switch_channel_set_variable_printf(channel, "freetdm_chan_number", "%d", sigmsg->channel->chan_id); + + switch_channel_set_state(channel, CS_INIT); + if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error spawning thread\n"); + switch_core_session_destroy(&session); + return FTDM_FAIL; + } + + if (ftdm_channel_add_token(sigmsg->channel, switch_core_session_get_uuid(session), 0) != FTDM_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error adding token\n"); + switch_core_session_destroy(&session); + return FTDM_FAIL; + } + *sp = session; + + return FTDM_SUCCESS; +} + +static FIO_SIGNAL_CB_FUNCTION(on_common_signal) +{ + switch_event_t *event = NULL; + + switch (sigmsg->event_id) { + + case FTDM_SIGEVENT_ALARM_CLEAR: + case FTDM_SIGEVENT_ALARM_TRAP: + { + if (ftdm_channel_get_alarms(sigmsg->channel) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "failed to retrieve alarms\n"); + return FTDM_FAIL; + } + if (switch_event_create(&event, SWITCH_EVENT_TRAP) != SWITCH_STATUS_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "failed to create alarms events\n"); + return FTDM_FAIL; + } + if (sigmsg->event_id == FTDM_SIGEVENT_ALARM_CLEAR) { + ftdm_log(FTDM_LOG_NOTICE, "Alarm cleared on channel %d:%d [%s]\n", sigmsg->channel->span_id, sigmsg->channel->chan_id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "ftdm-alarm-clear"); + } else { + ftdm_log(FTDM_LOG_NOTICE, "Alarm raised on channel %d:%d [%s]\n", sigmsg->channel->span_id, sigmsg->channel->chan_id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "ftdm-alarm-trap"); + } + } + break; + default: + return FTDM_SUCCESS; + break; + } + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-name", "%s", sigmsg->channel->span->name); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-number", "%d", sigmsg->channel->span_id); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "chan-number", "%d", sigmsg->channel->chan_id); + + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_RECOVER)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "recover"); + } + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_LOOPBACK)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "loopback"); + } + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_YELLOW)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "yellow"); + } + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_RED)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "red"); + } + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_BLUE)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "blue"); + } + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_NOTOPEN)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "notopen"); + } + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_AIS)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "ais"); + } + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_RAI)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "rai"); + } + if (ftdm_test_alarm_flag(sigmsg->channel, FTDM_ALARM_GENERAL)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "general"); + } + switch_event_fire(&event); + + return FTDM_BREAK; +} + +static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + ftdm_status_t status; + + ftdm_log(FTDM_LOG_DEBUG, "got FXO sig %d:%d [%s]\n", sigmsg->channel->span_id, sigmsg->channel->chan_id, ftdm_signal_event2str(sigmsg->event_id)); + + switch(sigmsg->event_id) { + + case FTDM_SIGEVENT_PROGRESS_MEDIA: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_pre_answered(channel); + switch_core_session_rwunlock(session); + } + } + break; + case FTDM_SIGEVENT_STOP: + { + private_t *tech_pvt = NULL; + while((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + ftdm_channel_clear_token(sigmsg->channel, 0); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); + ftdm_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + } + break; + case FTDM_SIGEVENT_UP: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_answered(channel); + switch_core_session_rwunlock(session); + } + } + break; + case FTDM_SIGEVENT_START: + { + status = ftdm_channel_from_event(sigmsg, &session); + if (status != FTDM_SUCCESS) { + ftdm_set_state_locked(sigmsg->channel, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled msg type %d for channel %d:%d\n", + sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + + } + + return FTDM_SUCCESS; +} + +static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + ftdm_status_t status = FTDM_SUCCESS; + + ftdm_log(FTDM_LOG_DEBUG, "got FXS sig [%s]\n", ftdm_signal_event2str(sigmsg->event_id)); + + switch(sigmsg->event_id) { + case FTDM_SIGEVENT_UP: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_answered(channel); + switch_core_session_rwunlock(session); + } + } + break; + case FTDM_SIGEVENT_PROGRESS: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_ring_ready(channel); + switch_core_session_rwunlock(session); + } + } + break; + case FTDM_SIGEVENT_START: + { + ftdm_clear_flag_locked(sigmsg->channel, FTDM_CHANNEL_HOLD); + status = ftdm_channel_from_event(sigmsg, &session); + if (status != FTDM_SUCCESS) { + ftdm_set_state_locked(sigmsg->channel, FTDM_CHANNEL_STATE_BUSY); + } + } + break; + case FTDM_SIGEVENT_STOP: + { + private_t *tech_pvt = NULL; + switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; + if (sigmsg->channel->token_count) { + switch_core_session_t *session_a, *session_b, *session_t = NULL; + switch_channel_t *channel_a = NULL, *channel_b = NULL; + int digits = !zstr(sigmsg->channel->caller_data.collected); + const char *br_a_uuid = NULL, *br_b_uuid = NULL; + private_t *tech_pvt = NULL; + + + if ((session_a = switch_core_session_locate(sigmsg->channel->tokens[0]))) { + channel_a = switch_core_session_get_channel(session_a); + br_a_uuid = switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE); + + tech_pvt = switch_core_session_get_private(session_a); + stop_hold(session_a, switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE)); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + } + + if ((session_b = switch_core_session_locate(sigmsg->channel->tokens[1]))) { + channel_b = switch_core_session_get_channel(session_b); + br_b_uuid = switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE); + + tech_pvt = switch_core_session_get_private(session_b); + stop_hold(session_a, switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE)); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + } + + if (channel_a && channel_b && !switch_channel_test_flag(channel_a, CF_OUTBOUND) && !switch_channel_test_flag(channel_b, CF_OUTBOUND)) { + cause = SWITCH_CAUSE_ATTENDED_TRANSFER; + if (br_a_uuid && br_b_uuid) { + switch_ivr_uuid_bridge(br_a_uuid, br_b_uuid); + } else if (br_a_uuid && digits) { + session_t = switch_core_session_locate(br_a_uuid); + } else if (br_b_uuid && digits) { + session_t = switch_core_session_locate(br_b_uuid); + } + } + + if (session_t) { + switch_ivr_session_transfer(session_t, sigmsg->channel->caller_data.collected, NULL, NULL); + switch_core_session_rwunlock(session_t); + } + + if (session_a) { + switch_core_session_rwunlock(session_a); + } + + if (session_b) { + switch_core_session_rwunlock(session_b); + } + + + } + + while((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, cause); + ftdm_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + ftdm_channel_clear_token(sigmsg->channel, NULL); + + } + break; + + case FTDM_SIGEVENT_ADD_CALL: + { + cycle_foreground(sigmsg->channel, 1, NULL); + } + break; + case FTDM_SIGEVENT_FLASH: + { + + if (ftdm_test_flag(sigmsg->channel, FTDM_CHANNEL_HOLD) && sigmsg->channel->token_count == 1) { + switch_core_session_t *session; + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + const char *buuid; + switch_channel_t *channel; + private_t *tech_pvt; + + tech_pvt = switch_core_session_get_private(session); + channel = switch_core_session_get_channel(session); + buuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + ftdm_set_state_locked(sigmsg->channel, FTDM_CHANNEL_STATE_UP); + stop_hold(session, buuid); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + switch_core_session_rwunlock(session); + } + } else if (sigmsg->channel->token_count == 2 && (SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_3WAY)) { + if (ftdm_test_flag(sigmsg->channel, FTDM_CHANNEL_3WAY)) { + ftdm_clear_flag(sigmsg->channel, FTDM_CHANNEL_3WAY); + if ((session = ftdm_channel_get_session(sigmsg->channel, 1))) { + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + ftdm_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + cycle_foreground(sigmsg->channel, 1, NULL); + } else { + char *cmd; + cmd = switch_mprintf("three_way::%s", sigmsg->channel->tokens[0]); + ftdm_set_flag(sigmsg->channel, FTDM_CHANNEL_3WAY); + cycle_foreground(sigmsg->channel, 1, cmd); + free(cmd); + } + } else if ((SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_CALL_SWAP) + || (SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_3WAY) + ) { + cycle_foreground(sigmsg->channel, 1, NULL); + if (sigmsg->channel->token_count == 1) { + ftdm_set_flag_locked(sigmsg->channel, FTDM_CHANNEL_HOLD); + ftdm_set_state_locked(sigmsg->channel, FTDM_CHANNEL_STATE_DIALTONE); + } + } + + } + break; + + case FTDM_SIGEVENT_COLLECTED_DIGIT: + { + char *dtmf = sigmsg->raw_data; + char *regex = SPAN_CONFIG[sigmsg->channel->span->span_id].dial_regex; + char *fail_regex = SPAN_CONFIG[sigmsg->channel->span->span_id].fail_dial_regex; + + if (zstr(regex)) { + regex = NULL; + } + + if (zstr(fail_regex)) { + fail_regex = NULL; + } + + ftdm_log(FTDM_LOG_DEBUG, "got DTMF sig [%s]\n", dtmf); + switch_set_string(sigmsg->channel->caller_data.collected, dtmf); + + if ((regex || fail_regex) && !zstr(dtmf)) { + switch_regex_t *re = NULL; + int ovector[30]; + int match = 0; + + if (fail_regex) { + match = switch_regex_perform(dtmf, fail_regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? FTDM_SUCCESS : FTDM_BREAK; + switch_regex_safe_free(re); + } + + if (status == FTDM_SUCCESS && regex) { + match = switch_regex_perform(dtmf, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? FTDM_BREAK : FTDM_SUCCESS; + } + + switch_regex_safe_free(re); + } + + } + break; + + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled msg type %d for channel %d:%d\n", + sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + + } + + return status; +} + +static FIO_SIGNAL_CB_FUNCTION(on_r2_signal) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + ftdm_status_t status = FTDM_SUCCESS; + + ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel %d\n", ftdm_signal_event2str(sigmsg->event_id), sigmsg->channel->physical_chan_id); + + if (on_common_signal(sigmsg) == FTDM_BREAK) { + return FTDM_SUCCESS; + } + + switch(sigmsg->event_id) { + /* on_call_disconnect from the R2 side */ + case FTDM_SIGEVENT_STOP: + { + private_t *tech_pvt = NULL; + while((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); + ftdm_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + } + break; + + /* on_call_offered from the R2 side */ + case FTDM_SIGEVENT_START: + { + status = ftdm_channel_from_event(sigmsg, &session); + } + break; + + /* on DNIS received from the R2 forward side, return status == FTDM_BREAK to stop requesting DNIS */ + case FTDM_SIGEVENT_COLLECTED_DIGIT: + { + char *regex = SPAN_CONFIG[sigmsg->channel->span->span_id].dial_regex; + char *fail_regex = SPAN_CONFIG[sigmsg->channel->span->span_id].fail_dial_regex; + + if (zstr(regex)) { + regex = NULL; + } + + if (zstr(fail_regex)) { + fail_regex = NULL; + } + + ftdm_log(FTDM_LOG_DEBUG, "R2 DNIS so far [%s]\n", sigmsg->channel->caller_data.dnis.digits); + + if ((regex || fail_regex) && !zstr(sigmsg->channel->caller_data.dnis.digits)) { + switch_regex_t *re = NULL; + int ovector[30]; + int match = 0; + + if (fail_regex) { + match = switch_regex_perform(sigmsg->channel->caller_data.dnis.digits, fail_regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? FTDM_SUCCESS : FTDM_BREAK; + switch_regex_safe_free(re); + } + + if (status == FTDM_SUCCESS && regex) { + match = switch_regex_perform(sigmsg->channel->caller_data.dnis.digits, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? FTDM_BREAK : FTDM_SUCCESS; + } + + switch_regex_safe_free(re); + } + } + break; + + case FTDM_SIGEVENT_PROGRESS: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_ring_ready(channel); + switch_core_session_rwunlock(session); + } + } + break; + + case FTDM_SIGEVENT_UP: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + ftdm_tone_type_t tt = FTDM_TONE_DTMF; + channel = switch_core_session_get_channel(session); + switch_channel_mark_answered(channel); + if (ftdm_channel_command(sigmsg->channel, FTDM_COMMAND_ENABLE_DTMF_DETECT, &tt) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to enable DTMF detection in R2 channel %d:%d\n", sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + switch_core_session_rwunlock(session); + } + } + break; + + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled event %d from R2 for channel %d:%d\n", + sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + } + + return status; +} + +static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + ftdm_log(FTDM_LOG_DEBUG, "got clear channel sig [%s]\n", ftdm_signal_event2str(sigmsg->event_id)); + + if (on_common_signal(sigmsg) == FTDM_BREAK) { + return FTDM_SUCCESS; + } + + switch(sigmsg->event_id) { + case FTDM_SIGEVENT_START: + { + ftdm_tone_type_t tt = FTDM_TONE_DTMF; + + if (ftdm_channel_command(sigmsg->channel, FTDM_COMMAND_ENABLE_DTMF_DETECT, &tt) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "TONE ERROR\n"); + } + + return ftdm_channel_from_event(sigmsg, &session); + } + break; + case FTDM_SIGEVENT_STOP: + case FTDM_SIGEVENT_RESTART: + { + private_t *tech_pvt = NULL; + while((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); + ftdm_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + } + break; + case FTDM_SIGEVENT_UP: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + ftdm_tone_type_t tt = FTDM_TONE_DTMF; + channel = switch_core_session_get_channel(session); + switch_channel_mark_answered(channel); + if (ftdm_channel_command(sigmsg->channel, FTDM_COMMAND_ENABLE_DTMF_DETECT, &tt) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "TONE ERROR\n"); + } + switch_core_session_rwunlock(session); + } else { + const char *uuid = ftdm_channel_get_uuid(sigmsg->channel, 0); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Session for channel %d:%d not found [UUID: %s]\n", + sigmsg->channel->span_id, sigmsg->channel->chan_id, (uuid) ? uuid : "N/A"); + } + } + case FTDM_SIGEVENT_PROGRESS_MEDIA: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_pre_answered(channel); + switch_core_session_rwunlock(session); + } else { + const char *uuid = ftdm_channel_get_uuid(sigmsg->channel, 0); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Session for channel %d:%d not found [UUID: %s]\n", + sigmsg->channel->span_id, sigmsg->channel->chan_id, (uuid) ? uuid : "N/A"); + } + } + break; + case FTDM_SIGEVENT_PROGRESS: + { + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_ring_ready(channel); + switch_core_session_rwunlock(session); + } else { + const char *uuid = ftdm_channel_get_uuid(sigmsg->channel, 0); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Session for channel %d:%d not found [UUID: %s]\n", + sigmsg->channel->span_id, sigmsg->channel->chan_id, (uuid) ? uuid : "N/A"); + } + } + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled msg type %d for channel %d:%d\n", + sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + } + + return FTDM_SUCCESS; +} + + +static FIO_SIGNAL_CB_FUNCTION(on_analog_signal) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + if (on_common_signal(sigmsg) == FTDM_BREAK) { + return FTDM_SUCCESS; + } + + switch (sigmsg->channel->type) { + case FTDM_CHAN_TYPE_FXO: + case FTDM_CHAN_TYPE_EM: + { + status = on_fxo_signal(sigmsg); + } + break; + case FTDM_CHAN_TYPE_FXS: + { + status = on_fxs_signal(sigmsg); + } + break; + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled analog channel type %d for channel %d:%d\n", + sigmsg->channel->type, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + } + + return status; +} + +static void ftdm_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + char *data = NULL; + va_list ap; + + va_start(ap, fmt); + + if (switch_vasprintf(&data, fmt, ap) != -1) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, (char *)func, line, NULL, level, "%s", data); + free(data); + } + + va_end(ap); + +} + +static uint32_t enable_analog_option(const char *str, uint32_t current_options) +{ + if (!strcasecmp(str, "3-way")) { + current_options |= ANALOG_OPTION_3WAY; + current_options &= ~ANALOG_OPTION_CALL_SWAP; + } else if (!strcasecmp(str, "call-swap")) { + current_options |= ANALOG_OPTION_CALL_SWAP; + current_options &= ~ANALOG_OPTION_3WAY; + } + + return current_options; + +} + +static switch_status_t load_config(void) +{ + const char *cf = "freetdm.conf"; + switch_xml_t cfg, xml, settings, param, spans, myspan; + ftdm_span_t *boost_spans[FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN]; + ftdm_span_t *boost_span = NULL; + unsigned boosti = 0; + unsigned int i = 0; + + memset(boost_spans, 0, sizeof(boost_spans)); + memset(&globals, 0, sizeof(globals)); + switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool); + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); + return SWITCH_STATUS_TERM; + } + + if ((settings = switch_xml_child(cfg, "settings"))) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "debug")) { + globals.debug = atoi(val); + } else if (!strcasecmp(var, "hold-music")) { + switch_set_string(globals.hold_music, val); + } else if (!strcasecmp(var, "enable-analog-option")) { + globals.analog_options = enable_analog_option(val, globals.analog_options); + } + } + } + + if ((spans = switch_xml_child(cfg, "analog_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + ftdm_status_t zstatus = FTDM_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + const char *tonegroup = NULL; + char *digit_timeout = NULL; + char *max_digits = NULL; + char *hotline = NULL; + char *dial_regex = NULL; + char *hold_music = NULL; + char *fail_dial_regex = NULL; + const char *enable_callerid = "true"; + + uint32_t span_id = 0, to = 0, max = 0; + ftdm_span_t *span = NULL; + analog_option_t analog_options = ANALOG_OPTION_NONE; + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { + digit_timeout = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "dial-regex")) { + dial_regex = val; + } else if (!strcasecmp(var, "enable-callerid")) { + enable_callerid = val; + } else if (!strcasecmp(var, "fail-dial-regex")) { + fail_dial_regex = val; + } else if (!strcasecmp(var, "hold-music")) { + hold_music = val; + } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { + max_digits = val; + } else if (!strcasecmp(var, "hotline")) { + hotline = val; + } else if (!strcasecmp(var, "enable-analog-option")) { + analog_options = enable_analog_option(val, analog_options); + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + + + if (!tonegroup) { + tonegroup = "us"; + } + + if (digit_timeout) { + to = atoi(digit_timeout); + } + + if (max_digits) { + max = atoi(max_digits); + } + + if (name) { + zstatus = ftdm_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = ftdm_span_find(span_id, &span); + } + + if (zstatus != FTDM_SUCCESS) { + zstatus = ftdm_span_find_by_name(id, &span); + } + } + + if (zstatus != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + if (ftdm_configure_span("analog", span, on_analog_signal, + "tonemap", tonegroup, + "digit_timeout", &to, + "max_dialstr", &max, + "hotline", hotline, + "enable_callerid", enable_callerid, + TAG_END) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d\n", span_id); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_set_string(SPAN_CONFIG[span->span_id].context, context); + switch_set_string(SPAN_CONFIG[span->span_id].dialplan, dialplan); + SPAN_CONFIG[span->span_id].analog_options = analog_options | globals.analog_options; + + if (dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); + } + + if (fail_dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); + } + + if (hold_music) { + switch_set_string(SPAN_CONFIG[span->span_id].hold_music, hold_music); + } + switch_copy_string(SPAN_CONFIG[span->span_id].type, "analog", sizeof(SPAN_CONFIG[span->span_id].type)); + ftdm_span_start(span); + } + } + + if ((spans = switch_xml_child(cfg, "analog_em_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + ftdm_status_t zstatus = FTDM_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + const char *tonegroup = NULL; + char *digit_timeout = NULL; + char *max_digits = NULL; + char *dial_regex = NULL; + char *hold_music = NULL; + char *fail_dial_regex = NULL; + uint32_t span_id = 0, to = 0, max = 0; + ftdm_span_t *span = NULL; + analog_option_t analog_options = ANALOG_OPTION_NONE; + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { + digit_timeout = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "dial-regex")) { + dial_regex = val; + } else if (!strcasecmp(var, "fail-dial-regex")) { + fail_dial_regex = val; + } else if (!strcasecmp(var, "hold-music")) { + hold_music = val; + } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { + max_digits = val; + } else if (!strcasecmp(var, "enable-analog-option")) { + analog_options = enable_analog_option(val, analog_options); + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + + if (!tonegroup) { + tonegroup = "us"; + } + + if (digit_timeout) { + to = atoi(digit_timeout); + } + + if (max_digits) { + max = atoi(max_digits); + } + + + if (name) { + zstatus = ftdm_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = ftdm_span_find(span_id, &span); + } + + if (zstatus != FTDM_SUCCESS) { + zstatus = ftdm_span_find_by_name(id, &span); + } + } + + if (zstatus != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + + if (ftdm_configure_span("analog_em", span, on_analog_signal, + "tonemap", tonegroup, + "digit_timeout", &to, + "max_dialstr", &max, + TAG_END) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d\n", span_id); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_set_string(SPAN_CONFIG[span->span_id].context, context); + switch_set_string(SPAN_CONFIG[span->span_id].dialplan, dialplan); + SPAN_CONFIG[span->span_id].analog_options = analog_options | globals.analog_options; + + if (dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); + } + + if (fail_dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); + } + + if (hold_music) { + switch_set_string(SPAN_CONFIG[span->span_id].hold_music, hold_music); + } + switch_copy_string(SPAN_CONFIG[span->span_id].type, "analog_em", sizeof(SPAN_CONFIG[span->span_id].type)); + ftdm_span_start(span); + } + } + + if ((spans = switch_xml_child(cfg, "pri_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + ftdm_status_t zstatus = FTDM_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + //Q921NetUser_t mode = Q931_TE; + //Q931Dialect_t dialect = Q931_Dialect_National; + char *mode = NULL; + char *dialect = NULL; + uint32_t span_id = 0; + ftdm_span_t *span = NULL; + const char *tonegroup = NULL; + char *digit_timeout = NULL; + const char *opts = "none"; + uint32_t to = 0; + int q921loglevel = -1; + int q931loglevel = -1; + + // quick debug + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ID: '%s', Name:'%s'\n",id,name); + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "mode")) { + mode = val; + } else if (!strcasecmp(var, "dialect")) { + dialect = val; + } else if (!strcasecmp(var, "q921loglevel")) { + if ((q921loglevel = switch_log_str2level(val)) == SWITCH_LOG_INVALID) { + q921loglevel = -1; + } + } else if (!strcasecmp(var, "q931loglevel")) { + if ((q931loglevel = switch_log_str2level(val)) == SWITCH_LOG_INVALID) { + q931loglevel = -1; + } + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "opts")) { + opts = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { + digit_timeout = val; + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + if (name) { + zstatus = ftdm_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = ftdm_span_find(span_id, &span); + } + + if (zstatus != FTDM_SUCCESS) { + zstatus = ftdm_span_find_by_name(id, &span); + } + } + + if (digit_timeout) { + to = atoi(digit_timeout); + } + + if (zstatus != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + if (!tonegroup) { + tonegroup = "us"; + } + + if (ftdm_configure_span("isdn", span, on_clear_channel_signal, + "mode", mode, + "dialect", dialect, + "digit_timeout", &to, + "opts", opts, + "tonemap", tonegroup, + "q921loglevel", q921loglevel, + "q931loglevel", q931loglevel, + TAG_END) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d mode: %s dialect: %s error: %s\n", span_id, mode, dialect, span->last_error); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); + switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); + switch_copy_string(SPAN_CONFIG[span->span_id].type, "isdn", sizeof(SPAN_CONFIG[span->span_id].type)); + + ftdm_span_start(span); + } + } + + + + if ((spans = switch_xml_child(cfg, "libpri_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + ftdm_status_t zstatus = FTDM_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + + const char *o_node = "cpe"; + const char *o_switch = "dms100"; + const char *o_dp = "unknown"; + const char *o_l1 = "ulaw"; + const char *o_debug = "none"; + const char* opts = "none"; + + uint32_t span_id = 0; + ftdm_span_t *span = NULL; + + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "node")) { + o_node = val; + } else if (!strcasecmp(var, "switch")) { + o_switch = val; + } else if (!strcasecmp(var, "dp")) { + o_dp = val; + } else if (!strcasecmp(var, "l1")) { + o_l1 = val; + } else if (!strcasecmp(var, "debug")) { + o_debug = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "opts")) { + opts = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + if (name) { + zstatus = ftdm_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = ftdm_span_find(span_id, &span); + } + + if (zstatus != FTDM_SUCCESS) { + zstatus = ftdm_span_find_by_name(id, &span); + } + } + + if (zstatus != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + + if (ftdm_configure_span("libpri", span, on_clear_channel_signal, + "node", o_node, + "switch", o_switch, + "dp", o_dp, + "l1", o_l1, + "debug", o_debug, + "opts", opts, + TAG_END) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d node: %s switch: %s dp: %s l1: %s debug: %s error: %s\n", + span_id, switch_str_nil(o_node), switch_str_nil(o_switch), switch_str_nil(o_dp), switch_str_nil(o_l1), switch_str_nil(o_debug), + span->last_error); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); + switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); + switch_copy_string(SPAN_CONFIG[span->span_id].type, "isdn", sizeof(SPAN_CONFIG[span->span_id].type)); + + ftdm_span_start(span); + } + } + + if ((spans = switch_xml_child(cfg, "boost_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + char *sigmod = (char *) switch_xml_attr(myspan, "sigmod"); + ftdm_status_t zstatus = FTDM_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + uint32_t span_id = 0; + ftdm_span_t *span = NULL; + const char *tonegroup = NULL; + ftdm_conf_parameter_t spanparameters[30]; + unsigned paramindex = 0; + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "boost span requires an id or name as attribute: \n"); + continue; + } + memset(spanparameters, 0, sizeof(spanparameters)); + if (sigmod) { + spanparameters[paramindex].var = "sigmod"; + spanparameters[paramindex].val = sigmod; + paramindex++; + } + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + if (sizeof(spanparameters)/sizeof(spanparameters[0]) == paramindex) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many parameters for boost span, ignoring any parameter after %s\n", var); + break; + } + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else { + spanparameters[paramindex].var = var; + spanparameters[paramindex].val = val; + paramindex++; + } + } + + if (!tonegroup) { + tonegroup = "us"; + } + + if (name) { + zstatus = ftdm_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = ftdm_span_find(span_id, &span); + } + + if (zstatus != FTDM_SUCCESS) { + zstatus = ftdm_span_find_by_name(id, &span); + } + } + + if (zstatus != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + if (ftdm_configure_span_signaling("sangoma_boost", span, on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d error: %s\n", span_id, span->last_error); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); + switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); + + switch_copy_string(SPAN_CONFIG[span->span_id].type, "Sangoma (boost)", sizeof(SPAN_CONFIG[span->span_id].type)); + boost_spans[boosti++] = span; + } + } + + if ((spans = switch_xml_child(cfg, "r2_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + ftdm_status_t zstatus = FTDM_FAIL; + + /* strings */ + const char *variant = "itu"; + const char *category = "national_subscriber"; + const char *logdir = "/usr/local/freeswitch/log/"; /* FIXME: get PREFIX variable */ + const char *logging = "notice,warning,error"; + const char *advanced_protocol_file = ""; + + /* booleans */ + int call_files = 0; + int get_ani_first = -1; + int immediate_accept = -1; + int double_answer = -1; + int skip_category = -1; + int forced_release = -1; + int charge_calls = -1; + + /* integers */ + int mfback_timeout = -1; + int metering_pulse_timeout = -1; + int allow_collect_calls = -1; + int max_ani = 10; + int max_dnis = 4; + + /* common non r2 stuff */ + const char *context = "default"; + const char *dialplan = "XML"; + char *dial_regex = NULL; + char *fail_dial_regex = NULL; + uint32_t span_id = 0; + ftdm_span_t *span = NULL; + + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + /* string parameters */ + if (!strcasecmp(var, "variant")) { + variant = val; + } else if (!strcasecmp(var, "category")) { + category = val; + } else if (!strcasecmp(var, "logdir")) { + logdir = val; + } else if (!strcasecmp(var, "logging")) { + logging = val; + } else if (!strcasecmp(var, "advanced_protocol_file")) { + advanced_protocol_file = val; + + /* booleans */ + } else if (!strcasecmp(var, "allow_collect_calls")) { + allow_collect_calls = switch_true(val); + } else if (!strcasecmp(var, "immediate_accept")) { + immediate_accept = switch_true(val); + } else if (!strcasecmp(var, "double_answer")) { + double_answer = switch_true(val); + } else if (!strcasecmp(var, "skip_category")) { + skip_category = switch_true(var); + } else if (!strcasecmp(var, "forced_release")) { + forced_release = switch_true(val); + } else if (!strcasecmp(var, "charge_calls")) { + charge_calls = switch_true(val); + } else if (!strcasecmp(var, "get_ani_first")) { + get_ani_first = switch_true(val); + } else if (!strcasecmp(var, "call_files")) { + call_files = switch_true(val); + + /* integers */ + } else if (!strcasecmp(var, "mfback_timeout")) { + mfback_timeout = atoi(val); + } else if (!strcasecmp(var, "metering_pulse_timeout")) { + metering_pulse_timeout = atoi(val); + } else if (!strcasecmp(var, "max_ani")) { + max_ani = atoi(val); + } else if (!strcasecmp(var, "max_dnis")) { + max_dnis = atoi(val); + + /* common non r2 stuff */ + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "dial-regex")) { + dial_regex = val; + } else if (!strcasecmp(var, "fail-dial-regex")) { + fail_dial_regex = val; + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + if (name) { + zstatus = ftdm_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = ftdm_span_find(span_id, &span); + } + + if (zstatus != FTDM_SUCCESS) { + zstatus = ftdm_span_find_by_name(id, &span); + } + } + + if (zstatus != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + if (ftdm_configure_span("r2", span, on_r2_signal, + "variant", variant, + "max_ani", max_ani, + "max_dnis", max_dnis, + "category", category, + "logdir", logdir, + "logging", logging, + "advanced_protocol_file", advanced_protocol_file, + "allow_collect_calls", allow_collect_calls, + "immediate_accept", immediate_accept, + "double_answer", double_answer, + "skip_category", skip_category, + "forced_release", forced_release, + "charge_calls", charge_calls, + "get_ani_first", get_ani_first, + "call_files", call_files, + "mfback_timeout", mfback_timeout, + "metering_pulse_timeout", metering_pulse_timeout, + TAG_END) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error configuring R2 FreeTDM span %d, error: %s\n", + span_id, span->last_error); + continue; + } + + if (dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); + } + + if (fail_dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); + } + + SPAN_CONFIG[span->span_id].span = span; + switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); + switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); + switch_copy_string(SPAN_CONFIG[span->span_id].type, "r2", sizeof(SPAN_CONFIG[span->span_id].type)); + + if (ftdm_span_start(span) == FTDM_FAIL) { + ftdm_log(FTDM_LOG_ERROR, "Error starting R2 FreeTDM span %d, error: %s\n", span_id, span->last_error); + continue; + } + } + } + + /* start all boost spans now that we're done configuring. Unfortunately at this point boost modules have the limitation + * of needing all spans to be configured before starting them */ + for ( ; i < boosti; i++) { + boost_span = boost_spans[i]; + ftdm_log(FTDM_LOG_DEBUG, "Starting boost span %d\n", boost_span->span_id); + if (ftdm_span_start(boost_span) == FTDM_FAIL) { + ftdm_log(FTDM_LOG_ERROR, "Error starting boost FreeTDM span %d, error: %s\n", boost_span->span_id, boost_span->last_error); + continue; + } + } + + + switch_xml_free(xml); + + return SWITCH_STATUS_SUCCESS; +} + + +void dump_chan(ftdm_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream) +{ + if (chan_id > span->chan_count) { + return; + } + + stream->write_function(stream, + "span_id: %u\n" + "chan_id: %u\n" + "physical_span_id: %u\n" + "physical_chan_id: %u\n" + "type: %s\n" + "state: %s\n" + "last_state: %s\n" + "cid_date: %s\n" + "cid_name: %s\n" + "cid_num: %s\n" + "ani: %s\n" + "aniII: %s\n" + "dnis: %s\n" + "rdnis: %s\n" + "cause: %s\n\n", + span->channels[chan_id]->span_id, + span->channels[chan_id]->chan_id, + span->channels[chan_id]->physical_span_id, + span->channels[chan_id]->physical_chan_id, + ftdm_chan_type2str(span->channels[chan_id]->type), + ftdm_channel_state2str(span->channels[chan_id]->state), + ftdm_channel_state2str(span->channels[chan_id]->last_state), + span->channels[chan_id]->caller_data.cid_date, + span->channels[chan_id]->caller_data.cid_name, + span->channels[chan_id]->caller_data.cid_num.digits, + span->channels[chan_id]->caller_data.ani.digits, + span->channels[chan_id]->caller_data.aniII, + span->channels[chan_id]->caller_data.dnis.digits, + span->channels[chan_id]->caller_data.rdnis.digits, + switch_channel_cause2str(span->channels[chan_id]->caller_data.hangup_cause) + ); +} + +void dump_chan_xml(ftdm_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream) +{ + if (chan_id > span->chan_count) { + return; + } + + stream->write_function(stream, + " \n" + " %u\n" + " %u>\n" + " %u\n" + " %u\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " \n", + span->channels[chan_id]->span_id, + span->channels[chan_id]->chan_id, + span->channels[chan_id]->physical_span_id, + span->channels[chan_id]->physical_chan_id, + ftdm_chan_type2str(span->channels[chan_id]->type), + ftdm_channel_state2str(span->channels[chan_id]->state), + ftdm_channel_state2str(span->channels[chan_id]->last_state), + span->channels[chan_id]->caller_data.cid_date, + span->channels[chan_id]->caller_data.cid_name, + span->channels[chan_id]->caller_data.cid_num.digits, + span->channels[chan_id]->caller_data.ani.digits, + span->channels[chan_id]->caller_data.aniII, + span->channels[chan_id]->caller_data.dnis.digits, + span->channels[chan_id]->caller_data.rdnis.digits, + switch_channel_cause2str(span->channels[chan_id]->caller_data.hangup_cause) + ); +} + +#define FT_SYNTAX "list || dump [] || q931_pcap on|off [pcapfilename without suffix] || gains " +SWITCH_STANDARD_API(ft_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc) { + stream->write_function(stream, "%s", FT_SYNTAX); + goto end; + } + + if (!strcasecmp(argv[0], "dump")) { + if (argc < 2) { + stream->write_function(stream, "-ERR Usage: ft dump []\n"); + goto end; + } else { + uint32_t chan_id = 0; + ftdm_span_t *span; + char *as = NULL; + + ftdm_span_find_by_name(argv[1], &span); + + if (argc > 2) { + if (argv[3] && !strcasecmp(argv[2], "as")) { + as = argv[3]; + } else { + chan_id = atoi(argv[2]); + } + } + + if (argv[4] && !strcasecmp(argv[3], "as")) { + as = argv[4]; + } + + if (!zstr(as) && !strcasecmp(as, "xml")) { + stream->write_function(stream, "\n"); + if (!span) { + stream->write_function(stream, "invalid span\n"); + } else { + if (chan_id) { + if(chan_id > span->chan_count) { + stream->write_function(stream, "invalid channel\n"); + } else { + dump_chan_xml(span, chan_id, stream); + } + } else { + uint32_t j; + for (j = 1; j <= span->chan_count; j++) { + dump_chan_xml(span, j, stream); + } + + } + } + stream->write_function(stream, "\n"); + } else { + if (!span) { + stream->write_function(stream, "-ERR invalid span\n"); + } else { + if (chan_id) { + if(chan_id > span->chan_count) { + stream->write_function(stream, "-ERR invalid channel\n"); + } else { + dump_chan(span, chan_id, stream); + } + } else { + uint32_t j; + + stream->write_function(stream, "+OK\n"); + for (j = 1; j <= span->chan_count; j++) { + dump_chan(span, j, stream); + } + + } + } + } + } + } else if (!strcasecmp(argv[0], "list")) { + int j; + for (j = 0 ; j < FTDM_MAX_SPANS_INTERFACE; j++) { + if (SPAN_CONFIG[j].span) { + const char *flags = "none"; + ftdm_signaling_status_t sigstatus; + + if (SPAN_CONFIG[j].analog_options & ANALOG_OPTION_3WAY) { + flags = "3way"; + } else if (SPAN_CONFIG[j].analog_options & ANALOG_OPTION_CALL_SWAP) { + flags = "call swap"; + } + + if ((FTDM_SUCCESS == ftdm_span_get_sig_status(SPAN_CONFIG[j].span, &sigstatus))) { + stream->write_function(stream, + "+OK\n" + "span: %u (%s)\n" + "type: %s\n" + "signaling_status: %s\n" + "chan_count: %u\n" + "dialplan: %s\n" + "context: %s\n" + "dial_regex: %s\n" + "fail_dial_regex: %s\n" + "hold_music: %s\n" + "analog_options %s\n", + j, + SPAN_CONFIG[j].span->name, + SPAN_CONFIG[j].type, + ftdm_signaling_status2str(sigstatus), + SPAN_CONFIG[j].span->chan_count, + SPAN_CONFIG[j].dialplan, + SPAN_CONFIG[j].context, + SPAN_CONFIG[j].dial_regex, + SPAN_CONFIG[j].fail_dial_regex, + SPAN_CONFIG[j].hold_music, + flags + ); + } else { + stream->write_function(stream, + "+OK\n" + "span: %u (%s)\n" + "type: %s\n" + "chan_count: %u\n" + "dialplan: %s\n" + "context: %s\n" + "dial_regex: %s\n" + "fail_dial_regex: %s\n" + "hold_music: %s\n" + "analog_options %s\n", + j, + SPAN_CONFIG[j].span->name, + SPAN_CONFIG[j].type, + SPAN_CONFIG[j].span->chan_count, + SPAN_CONFIG[j].dialplan, + SPAN_CONFIG[j].context, + SPAN_CONFIG[j].dial_regex, + SPAN_CONFIG[j].fail_dial_regex, + SPAN_CONFIG[j].hold_music, + flags + ); + } + } + } + } else if (!strcasecmp(argv[0], "stop") || !strcasecmp(argv[0], "start")) { + char *span_name = argv[1]; + ftdm_span_t *span = NULL; + ftdm_status_t status; + + if (span_name) { + ftdm_span_find_by_name(span_name, &span); + } + + if (!span) { + stream->write_function(stream, "-ERR no span\n"); + goto end; + } + + if (!strcasecmp(argv[0], "stop")) { + status = ftdm_span_stop(span); + } else { + status = ftdm_span_start(span); + } + + stream->write_function(stream, status == FTDM_SUCCESS ? "+OK\n" : "-ERR failure\n"); + + goto end; + + /*Q931ToPcap enhancement*/ + } else if (!strcasecmp(argv[0], "q931_pcap")) { + int32_t span_id = 0; + ftdm_span_t *span; + const char *pcapfn = NULL; + char *tmp_path = NULL; + + if (argc < 3) { + stream->write_function(stream, "-ERR Usage: ft q931_pcap on|off [pcapfilename without suffix]\n"); + goto end; + } + span_id = atoi(argv[1]); + if (!(span_id && (span = SPAN_CONFIG[span_id].span))) { + stream->write_function(stream, "-ERR invalid span\n"); + goto end; + } + + /*Look for a given file name or use default file name*/ + if (argc > 3) { + if(argv[3]){ + pcapfn=argv[3]; + } + } + else { + pcapfn="q931"; + } + + /*Add log directory path to file name*/ + tmp_path=switch_mprintf("%s%s%s.pcap", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR, pcapfn); + + if(!strcasecmp(argv[2], "on")) { + if (ftdm_configure_span("isdn", span, on_clear_channel_signal, "q931topcap", 1, "pcapfilename", tmp_path, TAG_END) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_WARNING, "Error couldn't (re-)enable Q931-To-Pcap!\n"); + goto end; + } else { + stream->write_function(stream, "+OK\n"); + } + } else if(!strcasecmp(argv[2], "off")) { + if (ftdm_configure_span("isdn", span, on_clear_channel_signal, "q931topcap", 0, TAG_END) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error couldn't enable Q931-To-Pcap!\n"); + goto end; + } else { + stream->write_function(stream, "+OK\n"); + } + } else { + stream->write_function(stream, "-ERR Usage: ft q931_pcap on|off [pcapfilename without suffix]\n"); + goto end; + } + + } else if (!strcasecmp(argv[0], "gains")) { + unsigned int i = 0; + float txgain = 0.0; + float rxgain = 0.0; + uint32_t chan_id = 0; + ftdm_span_t *span = NULL; + if (argc < 4) { + stream->write_function(stream, "-ERR Usage: ft gains []\n"); + goto end; + } + ftdm_span_find_by_name(argv[3], &span); + if (!span) { + stream->write_function(stream, "-ERR invalid span\n"); + goto end; + } + if (argc > 4) { + chan_id = atoi(argv[4]); + if (chan_id > span->chan_count) { + stream->write_function(stream, "-ERR invalid chan\n"); + goto end; + } + } + i = sscanf(argv[1], "%f", &rxgain); + i += sscanf(argv[2], "%f", &txgain); + if (i != 2) { + stream->write_function(stream, "-ERR invalid gains\n"); + goto end; + } + if (chan_id) { + ftdm_channel_command(span->channels[chan_id], FTDM_COMMAND_SET_RX_GAIN, &rxgain); + ftdm_channel_command(span->channels[chan_id], FTDM_COMMAND_SET_TX_GAIN, &txgain); + } else { + for (i = 1; i < span->chan_count; i++) { + ftdm_channel_command(span->channels[i], FTDM_COMMAND_SET_RX_GAIN, &rxgain); + ftdm_channel_command(span->channels[i], FTDM_COMMAND_SET_TX_GAIN, &txgain); + } + } + stream->write_function(stream, "+OK gains set to Rx %f and Tx %f\n", rxgain, txgain); + } else { + char *rply = ftdm_api_execute(cmd, NULL); + + if (rply) { + stream->write_function(stream, "%s", rply); + free(rply); + } else { + stream->write_function(stream, "-ERR Usage: %s\n", FT_SYNTAX); + } + } + /*Q931ToPcap enhancement done*/ + + end: + + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_STANDARD_APP(disable_ec_function) +{ + private_t *tech_pvt; + int x = 0; + + if (!switch_core_session_check_interface(session, freetdm_endpoint_interface)) { + ftdm_log(FTDM_LOG_ERROR, "This application is only for FreeTDM channels.\n"); + return; + } + + tech_pvt = switch_core_session_get_private(session); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_LOSE_RACE); + return; + } + + ftdm_channel_command(tech_pvt->ftdmchan, FTDM_COMMAND_DISABLE_ECHOCANCEL, &x); + ftdm_channel_command(tech_pvt->ftdmchan, FTDM_COMMAND_DISABLE_ECHOTRAIN, &x); + ftdm_log(FTDM_LOG_INFO, "Echo Canceller Disabled\n"); +} + + +SWITCH_MODULE_LOAD_FUNCTION(mod_freetdm_load) +{ + + switch_api_interface_t *commands_api_interface; + switch_application_interface_t *app_interface; + + module_pool = pool; + + ftdm_global_set_logger(ftdm_logger); + + if (ftdm_global_init() != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error loading FreeTDM\n"); + return SWITCH_STATUS_TERM; + } + + if (ftdm_global_configuration() != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM\n"); + return SWITCH_STATUS_TERM; + } + + if (load_config() != SWITCH_STATUS_SUCCESS) { + ftdm_global_destroy(); + return SWITCH_STATUS_TERM; + } + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + freetdm_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); + freetdm_endpoint_interface->interface_name = "freetdm"; + freetdm_endpoint_interface->io_routines = &freetdm_io_routines; + freetdm_endpoint_interface->state_handler = &freetdm_state_handlers; + + SWITCH_ADD_API(commands_api_interface, "ftdm", "FreeTDM commands", ft_function, FT_SYNTAX); + + SWITCH_ADD_APP(app_interface, "disable_ec", "Disable Echo Canceller", "Disable Echo Canceller", disable_ec_function, "", SAF_NONE); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_freetdm_shutdown) +{ + ftdm_global_destroy(); + + // this breaks pika but they are MIA so *shrug* + //return SWITCH_STATUS_NOUNLOAD; + return SWITCH_STATUS_SUCCESS; +} + +/* 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: + */ diff --git a/libs/freetdm/mod_freetdm/mod_openzap.2005.vcproj b/libs/freetdm/mod_freetdm/mod_openzap.2005.vcproj new file mode 100644 index 0000000000..491d0d574a --- /dev/null +++ b/libs/freetdm/mod_freetdm/mod_openzap.2005.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/msvc/freetdm.2008.vcproj b/libs/freetdm/msvc/freetdm.2008.vcproj new file mode 100644 index 0000000000..8540fce75e --- /dev/null +++ b/libs/freetdm/msvc/freetdm.2008.vcproj @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/msvc/openzap.2005.vcproj b/libs/freetdm/msvc/openzap.2005.vcproj new file mode 100644 index 0000000000..85d4a89206 --- /dev/null +++ b/libs/freetdm/msvc/openzap.2005.vcproj @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/msvc/testanalog/testanalog.2005.vcproj b/libs/freetdm/msvc/testanalog/testanalog.2005.vcproj new file mode 100644 index 0000000000..93553d2a5f --- /dev/null +++ b/libs/freetdm/msvc/testanalog/testanalog.2005.vcproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/msvc/testanalog/testanalog.2008.vcproj b/libs/freetdm/msvc/testanalog/testanalog.2008.vcproj new file mode 100644 index 0000000000..24f418ab19 --- /dev/null +++ b/libs/freetdm/msvc/testanalog/testanalog.2008.vcproj @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/msvc/testboost/testboost.2008.vcproj b/libs/freetdm/msvc/testboost/testboost.2008.vcproj new file mode 100644 index 0000000000..d525f1c784 --- /dev/null +++ b/libs/freetdm/msvc/testboost/testboost.2008.vcproj @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/msvc/testboost/testsangomaboost.2008.vcproj b/libs/freetdm/msvc/testboost/testsangomaboost.2008.vcproj new file mode 100644 index 0000000000..f6aec0b1de --- /dev/null +++ b/libs/freetdm/msvc/testboost/testsangomaboost.2008.vcproj @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/msvc/testisdn/testisdn.2005.vcproj b/libs/freetdm/msvc/testisdn/testisdn.2005.vcproj new file mode 100644 index 0000000000..133557166b --- /dev/null +++ b/libs/freetdm/msvc/testisdn/testisdn.2005.vcproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/msvc/testisdn/testisdn.2008.vcproj b/libs/freetdm/msvc/testisdn/testisdn.2008.vcproj new file mode 100644 index 0000000000..86698ec3e4 --- /dev/null +++ b/libs/freetdm/msvc/testisdn/testisdn.2008.vcproj @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/openzap.2005.sln b/libs/freetdm/openzap.2005.sln new file mode 100644 index 0000000000..c9f666b381 --- /dev/null +++ b/libs/freetdm/openzap.2005.sln @@ -0,0 +1,82 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openzap", "msvc\openzap.2005.vcproj", "{93B8812C-3EC4-4F78-8970-FFBFC99E167D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testanalog", "msvc\testanalog\testanalog.2005.vcproj", "{BB833648-BAFF-4BE2-94DB-F8BB043C588C}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testisdn", "msvc\testisdn\testisdn.2005.vcproj", "{6DA6FD42-641D-4147-92F5-3BC4AAA6589B}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_openzap", "mod_openzap\mod_openzap.2005.vcproj", "{FE3540C5-3303-46E0-A69E-D92F775687F1}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_analog", "src\ozmod\ozmod_analog\ozmod_analog.2005.vcproj", "{37C94798-6E33-4B4F-8EE0-C72A7DC91157}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_analog_em", "src\ozmod\ozmod_analog_em\ozmod_analog_em.2005.vcproj", "{C539D7C8-26A8-4A94-B938-77672165C130}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_isdn", "src\ozmod\ozmod_isdn\ozmod_isdn.2005.vcproj", "{729344A5-D5E9-434D-8EE8-AF8C6C795D15}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_wanpipe", "src\ozmod\ozmod_wanpipe\ozmod_wanpipe.2005.vcproj", "{1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_pika", "src\ozmod\ozmod_pika\ozmod_pika.2005.vcproj", "{E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|Win32.ActiveCfg = Debug|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|Win32.Build.0 = Debug|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|Win32.ActiveCfg = Release|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|Win32.Build.0 = Release|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|Win32.ActiveCfg = Debug|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|Win32.Build.0 = Debug|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|Win32.ActiveCfg = Release|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|Win32.Build.0 = Release|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|Win32.Build.0 = Debug|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|Win32.ActiveCfg = Release|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|Win32.Build.0 = Release|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|Win32.ActiveCfg = Debug|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|Win32.Build.0 = Debug|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|Win32.ActiveCfg = Release|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|Win32.Build.0 = Release|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|Win32.ActiveCfg = Debug|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|Win32.Build.0 = Debug|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|Win32.ActiveCfg = Release|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|Win32.Build.0 = Release|Win32 + {C539D7C8-26A8-4A94-B938-77672165C130}.Debug|Win32.ActiveCfg = Debug|Win32 + {C539D7C8-26A8-4A94-B938-77672165C130}.Debug|Win32.Build.0 = Debug|Win32 + {C539D7C8-26A8-4A94-B938-77672165C130}.Release|Win32.ActiveCfg = Release|Win32 + {C539D7C8-26A8-4A94-B938-77672165C130}.Release|Win32.Build.0 = Release|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|Win32.ActiveCfg = Debug|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|Win32.Build.0 = Debug|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|Win32.ActiveCfg = Release|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|Win32.Build.0 = Release|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Debug|Win32.ActiveCfg = Debug|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Release|Win32.ActiveCfg = Release|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Debug|Win32.ActiveCfg = Debug|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Release|Win32.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/libs/freetdm/ozrename.sh b/libs/freetdm/ozrename.sh new file mode 100755 index 0000000000..369be1c3a1 --- /dev/null +++ b/libs/freetdm/ozrename.sh @@ -0,0 +1,99 @@ +# renaming main header and build file +copy="cp -r" + +$copy src/include/openzap.h src/include/freetdm.h +svn delete src/include/openzap.h +$copy openzap.pc.in freetdm.pc.in +svn delete openzap.pc.in + +# create mod_freetdm +mkdir mod_freetdm +cp mod_openzap/* mod_freetdm/ +mv mod_freetdm/mod_openzap.c mod_freetdm/mod_freetdm.c +svn delete --force mod_openzap + + +##### ozmod stuff #### +# rename anything ozmod to ftmod, including directories first +mkdir ./src/ftmod +for file in `find ./src/ozmod -name *ozmod_* -type d` +do + $copy ${file} ${file//ozmod/ftmod} +done + +#remove .svn directories in the copied ozmod dirs +find ./src/ftmod -name *.svn -exec rm -rf {} \; + +# copy ozmod c files +for file in `find ./src/ftmod -name *ozmod_*.c` +do + mv $file ${file//ozmod/ftmod} +done + +# copy ozmod h files +for file in `find ./src/ftmod -name *ozmod_*.h` +do + mv $file ${file//ozmod/ftmod} +done + +#### end ozmod stuff #### + +# renaming other zap files +for file in `find ./ -name *zap_*.c` +do + mv $file ${file//zap_/ftdm_} +done + +for file in `find ./ -name *zap_*.h` +do + mv $file ${file//zap_/ftdm_} +done + +svn revert -R src/ozmod +svn delete --force src/ozmod + +# replace full openzap occurences first (handles openzap.h, libopenzap etc) +find ./ -name *.c -exec sed -i 's,openzap,freetdm,g' {} \; + +find ./ -name *.h -exec sed -i 's,openzap,freetdm,g' {} \; + +sed -i 's,openzap,freetdm,g' Makefile.am + +sed -i 's,openzap,freetdm,g' configure.ac + +sed -i 's,openzap,freetdm,g' mod_freetdm/Makefile.in + +# replace inside files +find ./ -name *.c -exec sed -i 's,oz,ft,g' {} \; +find ./ -name *.c -exec sed -i 's,OZ,FT,g' {} \; +find ./ -name *.c -exec sed -i 's,zap,ftdm,g' {} \; +find ./ -name *.c -exec sed -i 's,ZAP,FTDM,g' {} \; +find ./ -name *.c -exec sed -i 's,zchan,ftdmchan,g' {} \; + +find ./ -name *.h -exec sed -i 's,oz,ft,g' {} \; +find ./ -name *.h -exec sed -i 's,OZ,FT,g' {} \; +find ./ -name *.h -exec sed -i 's,zap,ftdm,g' {} \; +find ./ -name *.h -exec sed -i 's,ZAP,FTDM,g' {} \; +find ./ -name *.h -exec sed -i 's,zchan,ftdmchan,g' {} \; + +sed -i 's,oz,ft,g' Makefile.am +sed -i 's,OZ,FT,g' Makefile.am +sed -i 's,zap,ftdm,g' Makefile.am +sed -i 's,ZAP,FTDM,g' Makefile.am +sed -i 's,zchan,ftdmchan,g' Makefile.am + +sed -i 's,oz,ft,g' configure.ac +sed -i 's,OZ,FT,g' configure.ac +sed -i 's,zap,ftdm,g' configure.ac +sed -i 's,ZAP,FTDM,g' configure.ac +sed -i 's,zchan,ftdmchan,g' configure.ac + +sed -i 's,oz,ft,g' mod_freetdm/Makefile.in +sed -i 's,OZ,FT,g' mod_freetdm/Makefile.in +sed -i 's,zap,ftdm,g' mod_freetdm/Makefile.in +sed -i 's,ZAP,FTDM,g' mod_freetdm/Makefile.in +sed -i 's,zchan,ftdmchan,g' mod_freetdm/Makefile.in + +svn add src/ftmod/ +svn add mod_freetdm/ + diff --git a/libs/freetdm/ozreplace.sh b/libs/freetdm/ozreplace.sh new file mode 100755 index 0000000000..71f4b13766 --- /dev/null +++ b/libs/freetdm/ozreplace.sh @@ -0,0 +1,13 @@ +##### ozmod stuff #### +# replace full openzap occurences first (handles openzap.h, libopenzap etc) +sed -i 's,openzap,freetdm,g' $1 +sed -i 's,ozmod,ftmod,g' $1 + +sed -i 's,zchan,ftdmchan,g' $1 +sed -i 's,oz,ft,g' $1 +sed -i 's,OZ,FT,g' $1 +sed -i 's,zap,ftdm,g' $1 +sed -i 's,ZAP,FTDM,g' $1 +sed -i 's,zio,fio,g' $1 +sed -i 's,ZIO,FIO,g' $1 + diff --git a/libs/freetdm/patches/oz.diff b/libs/freetdm/patches/oz.diff new file mode 100644 index 0000000000..2e9c558b7c --- /dev/null +++ b/libs/freetdm/patches/oz.diff @@ -0,0 +1,134 @@ +Index: src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c +=================================================================== +--- src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c (revision 745) ++++ src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c (working copy) +@@ -98,19 +98,21 @@ + * so we can have one analong handler thread that will deal with all the idle analog channels for events + * the alternative would be for the driver to provide one socket for all of the oob events for all analog channels + */ +-static __inline__ int tdmv_api_wait_socket(sng_fd_t fd, int timeout, int *flags) ++static __inline__ int tdmv_api_wait_socket(zap_channel_t *zchan, int timeout, int *flags) + { + + #ifdef LIBSANGOMA_VERSION + int err; +- sangoma_wait_obj_t sangoma_wait_obj; ++ sangoma_wait_obj_t *sangoma_wait_obj = zchan->mod_data; + +- sangoma_init_wait_obj(&sangoma_wait_obj, fd, 1, 1, *flags, SANGOMA_WAIT_OBJ); ++ sangoma_init_wait_obj(sangoma_wait_obj, zchan->sockfd, 1, 1, 0, SANGOMA_WAIT_OBJ); + +- err=sangoma_socket_waitfor_many(&sangoma_wait_obj,1 , timeout); ++ err = sangoma_socket_waitfor_many(&sangoma_wait_obj, 1, timeout); ++ + if (err > 0) { +- *flags=sangoma_wait_obj.flags_out; ++ *flags = sangoma_wait_obj.flags_out; + } ++ + return err; + + #else +@@ -118,7 +120,7 @@ + int res; + + memset(&pfds[0], 0, sizeof(pfds[0])); +- pfds[0].fd = fd; ++ pfds[0].fd = zchan->sockfd; + pfds[0].events = *flags; + res = poll(pfds, 1, timeout); + *flags = 0; +@@ -200,6 +202,15 @@ + + if (sockfd != WP_INVALID_SOCKET && zap_span_add_channel(span, sockfd, type, &chan) == ZAP_SUCCESS) { + wanpipe_tdm_api_t tdm_api; ++#ifdef LIBSANGOMA_VERSION ++ sangoma_wait_obj_t *sangoma_wait_obj; ++ ++ sangoma_wait_obj = malloc(sizeof(*sangoma_wait_obj)); ++ memset(sangoma_wait_obj, 0, sizeof(*sangoma_wait_obj)); ++ sangoma_init_wait_obj(sangoma_wait_obj, sockfd, 1, 1, 0, SANGOMA_WAIT_OBJ); ++ chan->mod_data = sangoma_wait_obj; ++#endif ++ + memset(&tdm_api,0,sizeof(tdm_api)); + + chan->physical_span_id = spanno; +@@ -211,7 +222,7 @@ + + dtmf = "software"; + +- /* FIXME: Handle Error Conditino Check for return code */ ++ /* FIXME: Handle Error Condition Check for return code */ + err= sangoma_tdm_get_hw_coding(chan->sockfd, &tdm_api); + + if (tdm_api.wp_tdm_cmd.hw_tdm_coding) { +@@ -606,7 +617,7 @@ + inflags |= POLLPRI; + } + +- result = tdmv_api_wait_socket(zchan->sockfd, to, &inflags); ++ result = tdmv_api_wait_socket(zchan, to, &inflags); + + *flags = ZAP_NO_FLAGS; + +@@ -643,26 +654,30 @@ + ZIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event) + { + #ifdef LIBSANGOMA_VERSION +- sangoma_wait_obj_t pfds[ZAP_MAX_CHANNELS_SPAN]; ++ sangoma_wait_obj_t *pfds[ZAP_MAX_CHANNELS_SPAN] = { 0 }; + #else + struct pollfd pfds[ZAP_MAX_CHANNELS_SPAN]; + #endif + + uint32_t i, j = 0, k = 0, l = 0; +- int objects=0; + int r; + + for(i = 1; i <= span->chan_count; i++) { + zap_channel_t *zchan = span->channels[i]; + ++ + #ifdef LIBSANGOMA_VERSION +- sangoma_init_wait_obj(&pfds[j], zchan->sockfd , 1, 1, POLLPRI, SANGOMA_WAIT_OBJ); ++ if (!zchan->mod_data) { ++ continue; ++ } ++ pfds[j] = zchan->mod_data; ++ + #else + memset(&pfds[j], 0, sizeof(pfds[j])); + pfds[j].fd = span->channels[i]->sockfd; + pfds[j].events = POLLPRI; + #endif +- objects++; ++ + /* The driver probably should be able to do this wink/flash/ringing by itself this is sort of a hack to make it work! */ + + if (zap_test_flag(zchan, ZAP_CHANNEL_WINK) || zap_test_flag(zchan, ZAP_CHANNEL_FLASH)) { +@@ -703,7 +718,7 @@ + ms = l; + } + #ifdef LIBSANGOMA_VERSION +- r = sangoma_socket_waitfor_many(pfds,objects,ms); ++ r = sangoma_socket_waitfor_many(pfds, j, ms); + #else + r = poll(pfds, j, ms); + #endif +@@ -935,6 +950,15 @@ + */ + static ZIO_CHANNEL_DESTROY_FUNCTION(wanpipe_channel_destroy) + { ++ sangoma_wait_obj_t *sangoma_wait_obj; ++ ++ if (zchan->mod_data) { ++ sangoma_wait_obj = zchan->mod_data; ++ zchan->mod_data = NULL; ++ sangoma_release_wait_obj(sangoma_wait_obj); ++ free(sangoma_wait_obj); ++ } ++ + if (zchan->sockfd > -1) { + close(zchan->sockfd); + zchan->sockfd = WP_INVALID_SOCKET; diff --git a/libs/freetdm/src/detect_dtmf.c b/libs/freetdm/src/detect_dtmf.c new file mode 100644 index 0000000000..e7d9648192 --- /dev/null +++ b/libs/freetdm/src/detect_dtmf.c @@ -0,0 +1,33 @@ +//#include "freetdm.h" +#include "libteletone_detect.h" + +int main(int argc, char *argv[]) +{ + int fd, b; + short sln[512] = {0}; + teletone_dtmf_detect_state_t dtmf_detect = {0}; + char digit_str[128] = ""; + + if (argc < 2) { + fprintf(stderr, "Arg Error!\n"); + exit(-1); + } + + teletone_dtmf_detect_init (&dtmf_detect, 8000); + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "File Error! [%s]\n", strerror(errno)); + exit(-1); + } + + while((b = read(fd, sln, 320)) > 0) { + teletone_dtmf_detect(&dtmf_detect, sln, b / 2); + teletone_dtmf_get(&dtmf_detect, digit_str, sizeof(digit_str)); + if (*digit_str) { + printf("digit: %s\n", digit_str); + } + } + close(fd); + return 0; +} + diff --git a/libs/freetdm/src/detect_tones.c b/libs/freetdm/src/detect_tones.c new file mode 100644 index 0000000000..5cd52d4d99 --- /dev/null +++ b/libs/freetdm/src/detect_tones.c @@ -0,0 +1,32 @@ +//#include "freetdm.h" +#include "libteletone_detect.h" + +int main(int argc, char *argv[]) +{ + teletone_multi_tone_t mt = {0}; + teletone_tone_map_t map = {{0}}; + + int fd, b; + short sln[512] = {0}; + + if (argc < 2) { + fprintf(stderr, "Arg Error!\n"); + exit(-1); + } + + map.freqs[0] = atof("350"); + map.freqs[1] = atof("440"); + teletone_multi_tone_init(&mt, &map); + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "File Error! [%s]\n", strerror(errno)); + exit(-1); + } + + while((b = read(fd, sln, 320)) > 0) { + printf("TEST %d %d\n", b, teletone_multi_tone_detect(&mt, sln, b / 2)); + } + close(fd); + return 0; +} + diff --git a/libs/freetdm/src/fsk.c b/libs/freetdm/src/fsk.c new file mode 100644 index 0000000000..60a4722a4c --- /dev/null +++ b/libs/freetdm/src/fsk.c @@ -0,0 +1,351 @@ + +/* + * bell202.c + * + * Copyright (c) 2005 Robert Krten. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This module contains a Bell-202 1200-baud FSK decoder, suitable for + * use in a library. The general style of the library calls is modeled + * after the POSIX pthread_*() functions. + * + * 2005 03 20 R. Krten created +*/ +#include +#include +#include +#include +#include +#include + +#include "fsk.h" +#include "uart.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +fsk_modem_definition_t fsk_modem_definitions[] = +{ + { /* FSK_V23_FORWARD_MODE1 */ 1700, 1300, 600 }, + { /* FSK_V23_FORWARD_MODE2 */ 2100, 1300, 1200 }, + { /* FSK_V23_BACKWARD */ 450, 390, 75 }, + { /* FSK_BELL202 */ 2200, 1200, 1200 }, +}; + +/* + * dsp_fsk_attr_init + * + * Initializes the attributes structure; this must be done before the + * attributes structure is used. +*/ + +void dsp_fsk_attr_init (dsp_fsk_attr_t *attr) +{ + memset(attr, 0, sizeof(*attr)); +} + +/* + * dsp_fsk_attr_get_bithandler + * dsp_fsk_attr_set_bithandler + * dsp_fsk_attr_get_bytehandler + * dsp_fsk_attr_set_bytehandler + * dsp_fsk_attr_getsamplerate + * dsp_fsk_attr_setsamplerate + * + * These functions get and set their respective elements from the + * attributes structure. If an error code is returned, it is just + * zero == ok, -1 == fail. +*/ + +bithandler_func_t dsp_fsk_attr_get_bithandler(dsp_fsk_attr_t *attr, void **bithandler_arg) +{ + *bithandler_arg = attr->bithandler_arg; + return attr->bithandler; +} + +void dsp_fsk_attr_set_bithandler(dsp_fsk_attr_t *attr, bithandler_func_t bithandler, void *bithandler_arg) +{ + attr->bithandler = bithandler; + attr->bithandler_arg = bithandler_arg; +} + +bytehandler_func_t dsp_fsk_attr_get_bytehandler(dsp_fsk_attr_t *attr, void **bytehandler_arg) +{ + *bytehandler_arg = attr->bytehandler_arg; + return attr->bytehandler; +} + +void dsp_fsk_attr_set_bytehandler(dsp_fsk_attr_t *attr, bytehandler_func_t bytehandler, void *bytehandler_arg) +{ + attr->bytehandler = bytehandler; + attr->bytehandler_arg = bytehandler_arg; +} + +int dsp_fsk_attr_get_samplerate (dsp_fsk_attr_t *attr) +{ + return attr->sample_rate; +} + +int dsp_fsk_attr_set_samplerate (dsp_fsk_attr_t *attr, int samplerate) +{ + if (samplerate <= 0) { + return -1; + } + attr->sample_rate = samplerate; + return 0; +} + +/* + * dsp_fsk_create + * + * Creates a handle for subsequent use. The handle is created to contain + * a context data structure for use by the sample handler function. The + * function expects an initialized attributes structure, and returns the + * handle or a NULL if there were errors. + * + * Once created, the handle can be used until it is destroyed. +*/ + +dsp_fsk_handle_t *dsp_fsk_create(dsp_fsk_attr_t *attr) +{ + int i; + double phi_mark, phi_space; + dsp_fsk_handle_t *handle; + + handle = ftdm_malloc(sizeof(*handle)); + if (!handle) { + return NULL; + } + + memset(handle, 0, sizeof(*handle)); + + /* fill the attributes member */ + memcpy(&handle->attr, attr, sizeof(*attr)); + + /* see if we can do downsampling. We only really need 6 samples to "match" */ + if (attr->sample_rate / fsk_modem_definitions[FSK_BELL202].freq_mark > 6) { + handle->downsampling_count = attr->sample_rate / fsk_modem_definitions[FSK_BELL202].freq_mark / 6; + } else { + handle->downsampling_count = 1; + } + handle->current_downsample = 1; + + /* calculate the correlate size (number of samples required for slowest wave) */ + handle->corrsize = attr->sample_rate / handle->downsampling_count / fsk_modem_definitions[FSK_BELL202].freq_mark; + + /* allocate the correlation sin/cos arrays and initialize */ + for (i = 0; i < 4; i++) { + handle->correlates[i] = ftdm_malloc(sizeof(double) * handle->corrsize); + if (handle->correlates[i] == NULL) { + /* some failed, back out memory allocations */ + dsp_fsk_destroy(&handle); + return NULL; + } + } + + /* now initialize them */ + phi_mark = 2. * M_PI / ((double) attr->sample_rate / (double) handle->downsampling_count / (double) fsk_modem_definitions[FSK_BELL202].freq_mark); + phi_space = 2. * M_PI / ((double) attr->sample_rate / (double) handle->downsampling_count / (double) fsk_modem_definitions[FSK_BELL202].freq_space); + + for (i = 0; i < handle->corrsize; i++) { + handle->correlates[0][i] = sin(phi_mark * (double) i); + handle->correlates[1][i] = cos(phi_mark * (double) i); + handle->correlates[2][i] = sin(phi_space * (double) i); + handle->correlates[3][i] = cos(phi_space * (double) i); + } + + /* initialize the ring buffer */ + handle->buffer = ftdm_malloc(sizeof(double) * handle->corrsize); + if (!handle->buffer) { /* failed; back out memory allocations */ + dsp_fsk_destroy(&handle); + return NULL; + } + memset(handle->buffer, 0, sizeof(double) * handle->corrsize); + handle->ringstart = 0; + + /* initalize intra-cell position */ + handle->cellpos = 0; + handle->celladj = fsk_modem_definitions[FSK_BELL202].baud_rate / (double) attr->sample_rate * (double) handle->downsampling_count; + + /* if they have provided a byte handler, add a UART to the processing chain */ + if (handle->attr.bytehandler) { + dsp_uart_attr_t uart_attr; + dsp_uart_handle_t *uart_handle; + + dsp_uart_attr_init(&uart_attr); + dsp_uart_attr_set_bytehandler(&uart_attr, handle->attr.bytehandler, handle->attr.bytehandler_arg); + uart_handle = dsp_uart_create(&uart_attr); + if (uart_handle == NULL) { + dsp_fsk_destroy(&handle); + return NULL; + } + handle->attr.bithandler = dsp_uart_bit_handler; + handle->attr.bithandler_arg = uart_handle; + } + + return handle; +} + +/* + * dsp_fsk_destroy + * + * Destroys a handle, releasing any associated memory. Sets handle pointer to NULL + * so A destroyed handle can not be used for anything after the destroy. +*/ + +void dsp_fsk_destroy(dsp_fsk_handle_t **handle) +{ + int i; + + /* if empty handle, just return */ + if (*handle == NULL) { + return; + } + + for (i = 0; i < 4; i++) { + if ((*handle)->correlates[i] != NULL) { + ftdm_safe_free((*handle)->correlates[i]); + (*handle)->correlates[i] = NULL; + } + } + + if ((*handle)->buffer != NULL) { + ftdm_safe_free((*handle)->buffer); + (*handle)->buffer = NULL; + } + + if ((*handle)->attr.bytehandler) { + dsp_uart_handle_t** dhandle = (void *)(&(*handle)->attr.bithandler_arg); + dsp_uart_destroy(dhandle); + } + + ftdm_safe_free(*handle); + *handle = NULL; +} + +/* + * dsp_fsk_sample + * + * This is the main processing entry point. The function accepts a normalized + * sample (i.e., one whose range is between -1 and +1). The function performs + * the Bell-202 FSK modem decode processing, and, if it detects a valid bit, + * will call the bithandler associated with the attributes structure. + * + * For the Bell-202 standard, a logical zero (space) is 2200 Hz, and a logical + * one (mark) is 1200 Hz. +*/ + +void +dsp_fsk_sample (dsp_fsk_handle_t *handle, double normalized_sample) +{ + double val; + double factors[4]; + int i, j; + + /* if we can avoid processing samples, do so */ + if (handle->downsampling_count != 1) { + if (handle->current_downsample < handle->downsampling_count) { + handle->current_downsample++; + return; /* throw this sample out */ + } + handle->current_downsample = 1; + } + + /* store sample in buffer */ + handle->buffer[handle->ringstart++] = normalized_sample; + if (handle->ringstart >= handle->corrsize) { + handle->ringstart = 0; + } + + /* do the correlation calculation */ + factors[0] = factors[1] = factors[2] = factors[3] = 0; /* clear out intermediate sums */ + j = handle->ringstart; + for (i = 0; i < handle->corrsize; i++) { + if (j >= handle->corrsize) { + j = 0; + } + val = handle->buffer[j]; + factors[0] += handle->correlates[0][i] * val; + factors[1] += handle->correlates[1][i] * val; + factors[2] += handle->correlates[2][i] * val; + factors[3] += handle->correlates[3][i] * val; + j++; + } + + /* store the bit (bit value is comparison of the two sets of correlate factors) */ + handle->previous_bit = handle->current_bit; + handle->current_bit = (factors[0] * factors[0] + factors[1] * factors[1] > factors[2] * factors[2] + factors[3] * factors[3]); + + /* if there's a transition, we can synchronize the cell position */ + if (handle->previous_bit != handle->current_bit) { + handle->cellpos = 0.5; /* adjust cell position to be in the middle of the cell */ + } + handle->cellpos += handle->celladj; /* walk the cell along */ + + if (handle->cellpos > 1.0) { + handle->cellpos -= 1.0; + + switch (handle->state) { + case FSK_STATE_DATA: + { + + (*handle->attr.bithandler) (handle->attr.bithandler_arg, handle->current_bit); + } + break; + case FSK_STATE_CHANSEIZE: + { + + if (handle->last_bit != handle->current_bit) { + handle->conscutive_state_bits++; + } else { + handle->conscutive_state_bits = 0; + } + + if (handle->conscutive_state_bits > 15) { + handle->state = FSK_STATE_CARRIERSIG; + handle->conscutive_state_bits = 0; + } + } + break; + case FSK_STATE_CARRIERSIG: + { + if (handle->current_bit) { + handle->conscutive_state_bits++; + } else { + handle->conscutive_state_bits = 0; + } + + if (handle->conscutive_state_bits > 15) { + handle->state = FSK_STATE_DATA; + handle->conscutive_state_bits = 0; + } + } + break; + } + + handle->last_bit = handle->current_bit; + } +} + diff --git a/libs/freetdm/src/ftdm_buffer.c b/libs/freetdm/src/ftdm_buffer.c new file mode 100644 index 0000000000..2f08e7bbee --- /dev/null +++ b/libs/freetdm/src/ftdm_buffer.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "ftdm_buffer.h" + +static unsigned buffer_id = 0; + +struct ftdm_buffer { + unsigned char *data; + unsigned char *head; + ftdm_size_t used; + ftdm_size_t actually_used; + ftdm_size_t datalen; + ftdm_size_t max_len; + ftdm_size_t blocksize; + unsigned id; + int loops; +}; + + +FT_DECLARE(ftdm_status_t) ftdm_buffer_create(ftdm_buffer_t **buffer, ftdm_size_t blocksize, ftdm_size_t start_len, ftdm_size_t max_len) +{ + ftdm_buffer_t *new_buffer; + + new_buffer = ftdm_malloc(sizeof(*new_buffer)); + if (new_buffer) { + memset(new_buffer, 0, sizeof(*new_buffer)); + + if (start_len) { + new_buffer->data = ftdm_malloc(start_len); + if (!new_buffer->data) { + ftdm_safe_free(new_buffer); + return FTDM_MEMERR; + } + memset(new_buffer->data, 0, start_len); + } + + new_buffer->max_len = max_len; + new_buffer->datalen = start_len; + new_buffer->id = buffer_id++; + new_buffer->blocksize = blocksize; + new_buffer->head = new_buffer->data; + + *buffer = new_buffer; + return FTDM_SUCCESS; + } + + return FTDM_MEMERR; +} + +FT_DECLARE(ftdm_size_t) ftdm_buffer_len(ftdm_buffer_t *buffer) +{ + + assert(buffer != NULL); + + return buffer->datalen; + +} + + +FT_DECLARE(ftdm_size_t) ftdm_buffer_freespace(ftdm_buffer_t *buffer) +{ + assert(buffer != NULL); + + + if (buffer->max_len) { + return (ftdm_size_t) (buffer->max_len - buffer->used); + } + return 1000000; + +} + +FT_DECLARE(ftdm_size_t) ftdm_buffer_inuse(ftdm_buffer_t *buffer) +{ + assert(buffer != NULL); + + return buffer->used; +} + +FT_DECLARE(ftdm_size_t) ftdm_buffer_seek(ftdm_buffer_t *buffer, ftdm_size_t datalen) +{ + ftdm_size_t reading = 0; + + assert(buffer != NULL); + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + buffer->used = buffer->actually_used - reading; + buffer->head = buffer->data + reading; + + return reading; +} + +FT_DECLARE(ftdm_size_t) ftdm_buffer_toss(ftdm_buffer_t *buffer, ftdm_size_t datalen) +{ + ftdm_size_t reading = 0; + + assert(buffer != NULL); + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + buffer->used -= reading; + buffer->head += reading; + + return buffer->used; +} + +FT_DECLARE(void) ftdm_buffer_set_loops(ftdm_buffer_t *buffer, int loops) +{ + buffer->loops = loops; +} + +FT_DECLARE(ftdm_size_t) ftdm_buffer_read_loop(ftdm_buffer_t *buffer, void *data, ftdm_size_t datalen) +{ + ftdm_size_t len; + if ((len = ftdm_buffer_read(buffer, data, datalen)) < datalen) { + if (buffer->loops == 0) { + return len; + } + buffer->head = buffer->data; + buffer->used = buffer->actually_used; + len = ftdm_buffer_read(buffer, (char*)data + len, datalen - len); + buffer->loops--; + } + return len; +} + +FT_DECLARE(ftdm_size_t) ftdm_buffer_read(ftdm_buffer_t *buffer, void *data, ftdm_size_t datalen) +{ + ftdm_size_t reading = 0; + + assert(buffer != NULL); + assert(data != NULL); + + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + memcpy(data, buffer->head, reading); + buffer->used -= reading; + buffer->head += reading; + + /* if (buffer->id == 4) printf("%u o %d = %d\n", buffer->id, (unsigned)reading, (unsigned)buffer->used); */ + return reading; +} + +FT_DECLARE(ftdm_size_t) ftdm_buffer_write(ftdm_buffer_t *buffer, const void *data, ftdm_size_t datalen) +{ + ftdm_size_t freespace, actual_freespace; + + assert(buffer != NULL); + assert(data != NULL); + assert(buffer->data != NULL); + + if (!datalen) { + return buffer->used; + } + + actual_freespace = buffer->datalen - buffer->actually_used; + if (actual_freespace < datalen && (!buffer->max_len || (buffer->used + datalen <= buffer->max_len))) { + memmove(buffer->data, buffer->head, buffer->used); + buffer->head = buffer->data; + buffer->actually_used = buffer->used; + } + + freespace = buffer->datalen - buffer->used; + + /* + if (buffer->data != buffer->head) { + memmove(buffer->data, buffer->head, buffer->used); + buffer->head = buffer->data; + } + */ + + if (freespace < datalen) { + ftdm_size_t new_size, new_block_size; + void *data; + + new_size = buffer->datalen + datalen; + new_block_size = buffer->datalen + buffer->blocksize; + + if (new_block_size > new_size) { + new_size = new_block_size; + } + buffer->head = buffer->data; + data = realloc(buffer->data, new_size); + if (!data) { + return 0; + } + buffer->data = data; + buffer->head = buffer->data; + buffer->datalen = new_size; + } + + + freespace = buffer->datalen - buffer->used; + + if (freespace < datalen) { + return 0; + } else { + memcpy(buffer->head + buffer->used, data, datalen); + buffer->used += datalen; + buffer->actually_used += datalen; + } + /* if (buffer->id == 4) printf("%u i %d = %d\n", buffer->id, (unsigned)datalen, (unsigned)buffer->used); */ + + return buffer->used; +} + +FT_DECLARE(void) ftdm_buffer_zero(ftdm_buffer_t *buffer) +{ + assert(buffer != NULL); + assert(buffer->data != NULL); + + buffer->used = 0; + buffer->actually_used = 0; + buffer->head = buffer->data; +} + +FT_DECLARE(ftdm_size_t) ftdm_buffer_zwrite(ftdm_buffer_t *buffer, const void *data, ftdm_size_t datalen) +{ + ftdm_size_t w; + + if (!(w = ftdm_buffer_write(buffer, data, datalen))) { + ftdm_buffer_zero(buffer); + return ftdm_buffer_write(buffer, data, datalen); + } + + return w; +} + +FT_DECLARE(void) ftdm_buffer_destroy(ftdm_buffer_t **buffer) +{ + if (*buffer) { + ftdm_safe_free((*buffer)->data); + ftdm_safe_free(*buffer); + } + + *buffer = NULL; +} + +/* 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: + */ diff --git a/libs/freetdm/src/ftdm_callerid.c b/libs/freetdm/src/ftdm_callerid.c new file mode 100644 index 0000000000..16dd5c8ed7 --- /dev/null +++ b/libs/freetdm/src/ftdm_callerid.c @@ -0,0 +1,307 @@ +#include "freetdm.h" +#include "fsk.h" +#include "uart.h" + + + +static void fsk_byte_handler (void *x, int data) +{ + ftdm_fsk_data_state_t *state = (ftdm_fsk_data_state_t *) x; + uint8_t byte = (uint8_t)data; + + top: + + if (state->init == 3) { + return; + } + + if (state->dlen) { + goto add_byte; + } + + if (state->bpos == 1) { + state->blen = byte; + + if ((uint32_t)(state->dlen = state->bpos + byte + 2) > state->bufsize) { + state->dlen = state->bufsize; + } + goto top; + } + + add_byte: + + if (state->bpos <= state->dlen) { + state->buf[state->bpos++] = byte; + } else { + state->init = 3; + } +} + +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_init(ftdm_fsk_data_state_t *state, uint8_t *data, uint32_t datalen) +{ + memset(state, 0, sizeof(*state)); + state->buf = data; + state->bufsize = datalen; + state->bpos = 2; + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_sdmf(ftdm_fsk_data_state_t *state, const char *date, char *number) +{ + size_t dlen = strlen(date); + size_t nlen = strlen(number); + + state->buf[0] = FTDM_CID_TYPE_SDMF; + memcpy(&state->buf[state->bpos], date, dlen); + state->bpos += dlen; + memcpy(&state->buf[state->bpos], number, nlen); + state->bpos += nlen; + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_mdmf(ftdm_fsk_data_state_t *state, ftdm_mdmf_type_t type, const uint8_t *data, uint32_t datalen) +{ + state->buf[0] = FTDM_CID_TYPE_MDMF; + state->buf[state->bpos++] = type; + state->buf[state->bpos++] = (uint8_t)datalen; + memcpy(&state->buf[state->bpos], data, datalen); + state->bpos += datalen; + return FTDM_SUCCESS; +} + + +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_checksum(ftdm_fsk_data_state_t *state) +{ + uint32_t i; + uint8_t check = 0; + + state->buf[1] = (uint8_t)(state->bpos - 2); + + for (i = 0; i < state->bpos; i++) { + check = check + state->buf[i]; + } + + state->checksum = state->buf[state->bpos] = (uint8_t)(256 - check); + state->bpos++; + + state->dlen = state->bpos; + state->blen = state->buf[1]; + + return FTDM_SUCCESS; +} + + +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_parse(ftdm_fsk_data_state_t *state, ftdm_size_t *type, char **data, ftdm_size_t *len) +{ + + ftdm_size_t i; + int sum = 0; + + top: + + if (state->checksum != 0 || state->ppos >= state->dlen - 1) { + return FTDM_FAIL; + } + + if (!state->ppos) { + for(i = 0; i < state->bpos; i++) { + sum += state->buf[i]; + } + state->checksum = sum % 256; + state->ppos = 2; + + if (state->buf[0] != FTDM_CID_TYPE_MDMF && state->buf[0] != FTDM_CID_TYPE_SDMF) { + state->checksum = -1; + } + goto top; + } + + if (state->buf[0] == FTDM_CID_TYPE_SDMF) { + /* convert sdmf to mdmf so we don't need 2 parsers */ + if (state->ppos == 2) { + *type = MDMF_DATETIME; + *len = 8; + } else { + if (state->buf[state->ppos] == 'P' || state->buf[state->ppos] == 'O') { + *type = MDMF_NO_NUM; + *len = 1; + } else { + *type = MDMF_PHONE_NUM; + *len = state->blen - 8; + } + } + *data = (char *)&state->buf[state->ppos]; + state->ppos += *len; + return FTDM_SUCCESS; + } else if (state->buf[0] == FTDM_CID_TYPE_MDMF) { + *type = state->buf[state->ppos++]; + *len = state->buf[state->ppos++]; + *data = (char *)&state->buf[state->ppos]; + state->ppos += *len; + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +FT_DECLARE(ftdm_status_t) ftdm_fsk_demod_feed(ftdm_fsk_data_state_t *state, int16_t *data, ftdm_size_t samples) +{ + uint32_t x; + int16_t *sp = data; + + if (state->init == 3) { + return FTDM_FAIL; + } + + for (x = 0; x < samples; x++) { + dsp_fsk_sample (state->fsk1200_handle, (double) *sp++ / 32767.0); + if (state->dlen && state->bpos >= state->dlen) { + state->init = 3; + return FTDM_FAIL; + } + } + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_fsk_demod_destroy(ftdm_fsk_data_state_t *state) +{ + dsp_fsk_destroy(&state->fsk1200_handle); + memset(state, 0, sizeof(*state)); + return FTDM_SUCCESS; +} + +FT_DECLARE(int) ftdm_fsk_demod_init(ftdm_fsk_data_state_t *state, int rate, uint8_t *buf, ftdm_size_t bufsize) +{ + + dsp_fsk_attr_t fsk1200_attr; + + if (state->fsk1200_handle) { + dsp_fsk_destroy(&state->fsk1200_handle); + } + + memset(state, 0, sizeof(*state)); + memset(buf, 0, bufsize); + state->buf = buf; + state->bufsize = bufsize; + + dsp_fsk_attr_init (&fsk1200_attr); + dsp_fsk_attr_set_samplerate (&fsk1200_attr, rate); + dsp_fsk_attr_set_bytehandler (&fsk1200_attr, fsk_byte_handler, state); + state->fsk1200_handle = dsp_fsk_create (&fsk1200_attr); + + if (state->fsk1200_handle == NULL) { + return FTDM_FAIL; + } + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_size_t) ftdm_fsk_modulator_generate_bit(ftdm_fsk_modulator_t *fsk_trans, int8_t bit, int16_t *buf, ftdm_size_t buflen) +{ + ftdm_size_t i; + + for(i = 0 ; i < buflen; i++) { + fsk_trans->bit_accum += fsk_trans->bit_factor; + if (fsk_trans->bit_accum >= FTDM_FSK_MOD_FACTOR) { + fsk_trans->bit_accum -= (FTDM_FSK_MOD_FACTOR + fsk_trans->bit_factor); + break; + } + + buf[i] = teletone_dds_state_modulate_sample(&fsk_trans->dds, bit); + } + + return i; +} + + +FT_DECLARE(int32_t) ftdm_fsk_modulator_generate_carrier_bits(ftdm_fsk_modulator_t *fsk_trans, uint32_t bits) +{ + uint32_t i = 0; + ftdm_size_t r = 0; + int8_t bit = 1; + + for (i = 0; i < bits; i++) { + if ((r = ftdm_fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) { + if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != FTDM_SUCCESS) { + break; + } + } else { + break; + } + } + + return i; +} + + +FT_DECLARE(void) ftdm_fsk_modulator_generate_chan_sieze(ftdm_fsk_modulator_t *fsk_trans) +{ + uint32_t i = 0; + ftdm_size_t r = 0; + int8_t bit = 0; + + for (i = 0; i < fsk_trans->chan_sieze_bits; i++) { + if ((r = ftdm_fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) { + if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != FTDM_SUCCESS) { + break; + } + } else { + break; + } + bit = !bit; + } + + +} + + +FT_DECLARE(void) ftdm_fsk_modulator_send_data(ftdm_fsk_modulator_t *fsk_trans) +{ + ftdm_size_t r = 0; + int8_t bit = 0; + + while((bit = ftdm_bitstream_get_bit(&fsk_trans->bs)) > -1) { + if ((r = ftdm_fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) { + if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != FTDM_SUCCESS) { + break; + } + } else { + break; + } + } +} + + +FT_DECLARE(ftdm_status_t) ftdm_fsk_modulator_init(ftdm_fsk_modulator_t *fsk_trans, + fsk_modem_types_t modem_type, + uint32_t sample_rate, + ftdm_fsk_data_state_t *fsk_data, + float db_level, + uint32_t carrier_bits_start, + uint32_t carrier_bits_stop, + uint32_t chan_sieze_bits, + ftdm_fsk_write_sample_t write_sample_callback, + void *user_data) +{ + memset(fsk_trans, 0, sizeof(*fsk_trans)); + fsk_trans->modem_type = modem_type; + teletone_dds_state_set_tone(&fsk_trans->dds, fsk_modem_definitions[fsk_trans->modem_type].freq_space, sample_rate, 0); + teletone_dds_state_set_tone(&fsk_trans->dds, fsk_modem_definitions[fsk_trans->modem_type].freq_mark, sample_rate, 1); + fsk_trans->bit_factor = (uint32_t)((fsk_modem_definitions[fsk_trans->modem_type].baud_rate * FTDM_FSK_MOD_FACTOR) / (float)sample_rate); + fsk_trans->samples_per_bit = (uint32_t) (sample_rate / fsk_modem_definitions[fsk_trans->modem_type].baud_rate); + fsk_trans->est_bytes = (int32_t)(((fsk_data->dlen * 10) + carrier_bits_start + carrier_bits_stop + chan_sieze_bits) * ((fsk_trans->samples_per_bit + 1) * 2)); + fsk_trans->bit_accum = 0; + fsk_trans->fsk_data = fsk_data; + teletone_dds_state_set_tx_level(&fsk_trans->dds, db_level); + ftdm_bitstream_init(&fsk_trans->bs, fsk_trans->fsk_data->buf, (uint32_t)fsk_trans->fsk_data->dlen, FTDM_ENDIAN_BIG, 1); + fsk_trans->carrier_bits_start = carrier_bits_start; + fsk_trans->carrier_bits_stop = carrier_bits_stop; + fsk_trans->chan_sieze_bits = chan_sieze_bits; + fsk_trans->write_sample_callback = write_sample_callback; + fsk_trans->user_data = user_data; + return FTDM_SUCCESS; +} + diff --git a/libs/freetdm/src/ftdm_config.c b/libs/freetdm/src/ftdm_config.c new file mode 100644 index 0000000000..a7a8f041b1 --- /dev/null +++ b/libs/freetdm/src/ftdm_config.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "ftdm_config.h" + +int ftdm_config_open_file(ftdm_config_t *cfg, const char *file_path) +{ + FILE *f; + const char *path = NULL; + char path_buf[1024]; + + if (file_path[0] == '/') { + path = file_path; + } else { + snprintf(path_buf, sizeof(path_buf), "%s%s%s", FTDM_CONFIG_DIR, FTDM_PATH_SEPARATOR, file_path); + path = path_buf; + } + + if (!path) { + return 0; + } + + memset(cfg, 0, sizeof(*cfg)); + cfg->lockto = -1; + ftdm_log(FTDM_LOG_DEBUG, "Configuration file is %s.\n", path); + f = fopen(path, "r"); + + if (!f) { + if (file_path[0] != '/') { + int last = -1; + char *var, *val; + + snprintf(path_buf, sizeof(path_buf), "%s%sfreetdm.conf", FTDM_CONFIG_DIR, FTDM_PATH_SEPARATOR); + path = path_buf; + + if ((f = fopen(path, "r")) == 0) { + return 0; + } + + cfg->file = f; + ftdm_set_string(cfg->path, path); + + while (ftdm_config_next_pair(cfg, &var, &val)) { + if ((cfg->sectno != last) && !strcmp(cfg->section, file_path)) { + cfg->lockto = cfg->sectno; + return 1; + } + } + + ftdm_config_close_file(cfg); + memset(cfg, 0, sizeof(*cfg)); + return 0; + } + + return 0; + } else { + cfg->file = f; + ftdm_set_string(cfg->path, path); + return 1; + } +} + +void ftdm_config_close_file(ftdm_config_t *cfg) +{ + + if (cfg->file) { + fclose(cfg->file); + } + + memset(cfg, 0, sizeof(*cfg)); +} + + + +int ftdm_config_next_pair(ftdm_config_t *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + if (!cfg->path) { + return 0; + } + + for (;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']')) != 0) { + *end = '\0'; + (*var)++; + if (**var == '+') { + (*var)++; + ftdm_copy_string(cfg->section, *var, sizeof(cfg->section)); + cfg->sectno++; + + if (cfg->lockto > -1 && cfg->sectno != cfg->lockto) { + break; + } + cfg->catno = 0; + cfg->lineno = 0; + *var = (char *) ""; + *val = (char *) ""; + return 1; + } else { + ftdm_copy_string(cfg->category, *var, sizeof(cfg->category)); + cfg->catno++; + } + continue; + } + + + + if (**var == '#' || **var == ';' || **var == '\n' || **var == '\r') { + continue; + } + + if (!strncmp(*var, "__END__", 7)) { + break; + } + + + if ((end = strchr(*var, ';')) && *(end+1) == *end) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n')) != 0) { + if (*(end - 1) == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + + if ((*val = strchr(*var, '=')) == 0) { + ret = -1; + /* log_printf(0, server.log, "Invalid syntax on %s: line %d\n", cfg->path, cfg->lineno); */ + continue; + } else { + p = *val - 1; + *(*val) = '\0'; + (*val)++; + if (*(*val) == '>') { + *(*val) = '\0'; + (*val)++; + } + + while ((*p == ' ' || *p == '\t') && p != *var) { + *p = '\0'; + p--; + } + + p = *val; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *val = p; + ret = 1; + break; + } + } + + + return ret; + +} + +FT_DECLARE (int) ftdm_config_get_cas_bits(char *strvalue, unsigned char *outbits) +{ + char cas_bits[5]; + unsigned char bit = 0x8; + int x = 0; + char *double_colon = strchr(strvalue, ':'); + if (!double_colon) { + ftdm_log(FTDM_LOG_ERROR, "No CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", strvalue); + return -1; + } + double_colon++; + *outbits = 0; + cas_bits[4] = 0; + if (sscanf(double_colon, "%c%c%c%c", &cas_bits[0], &cas_bits[1], &cas_bits[2], &cas_bits[3]) != 4) { + ftdm_log(FTDM_LOG_ERROR, "Invalid CAS bits specified: '%s', :xxxx definition expected, where x is 1 or 0\n", double_colon); + return -1; + } + ftdm_log(FTDM_LOG_DEBUG, "CAS bits specification found: %s\n", cas_bits); + for (; cas_bits[x]; x++) { + if ('1' == cas_bits[x]) { + *outbits |= bit; + } else if ('0' != cas_bits[x]) { + ftdm_log(FTDM_LOG_ERROR, "Invalid CAS pattern specified: %s, just 0 or 1 allowed for each bit\n"); + return -1; + } + bit >>= 1; + } + return 0; +} + +/* 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: + */ diff --git a/libs/freetdm/src/ftdm_dso.c b/libs/freetdm/src/ftdm_dso.c new file mode 100644 index 0000000000..a0981ebdd9 --- /dev/null +++ b/libs/freetdm/src/ftdm_dso.c @@ -0,0 +1,116 @@ +/* + * Cross Platform dso/dll load abstraction + * Copyright(C) 2008 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + +#include "freetdm.h" +#include "ftdm_dso.h" +#include +#include + +#ifdef WIN32 +#include +#include + + +FT_DECLARE(void) ftdm_dso_destroy(ftdm_dso_lib_t *lib) { + if (lib && *lib) { + FreeLibrary(*lib); + *lib = NULL; + } +} + +FT_DECLARE(ftdm_dso_lib_t) ftdm_dso_open(const char *path, char **err) { + HINSTANCE lib; + + lib = LoadLibraryEx(path, NULL, 0); + + if (!lib) { + LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + } + + if (!lib) { + DWORD error = GetLastError(); + char tmp[80]; + sprintf(tmp, "dll open error [%ul]\n", error); + *err = ftdm_strdup(tmp); + } + + return lib; +} + +FT_DECLARE(void*) ftdm_dso_func_sym(ftdm_dso_lib_t lib, const char *sym, char **err) { + FARPROC func = GetProcAddress(lib, sym); + if (!func) { + DWORD error = GetLastError(); + char tmp[80]; + sprintf(tmp, "dll sym error [%ul]\n", error); + *err = ftdm_strdup(tmp); + } + return (void *)(intptr_t)func; // this should really be addr - ftdm_dso_func_data +} + +#else + +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + + +#include + +FT_DECLARE(void) ftdm_dso_destroy(ftdm_dso_lib_t *lib) { + if (lib && *lib) { + dlclose(*lib); + *lib = NULL; + } +} + +FT_DECLARE(ftdm_dso_lib_t) ftdm_dso_open(const char *path, char **err) { + void *lib = dlopen(path, RTLD_NOW | RTLD_LOCAL); + if (lib == NULL) { + *err = ftdm_strdup(dlerror()); + } + return lib; +} + +FT_DECLARE(void*) ftdm_dso_func_sym(ftdm_dso_lib_t lib, const char *sym, char **err) { + void *func = dlsym(lib, sym); + if (!func) { + *err = ftdm_strdup(dlerror()); + } + return func; +} +#endif + +/* }====================================================== */ + +/* 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 + */ diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c new file mode 100644 index 0000000000..6a1b21c6ad --- /dev/null +++ b/libs/freetdm/src/ftdm_io.c @@ -0,0 +1,3927 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + * Contributors: + * + * Moises Silva + * David Yat Sin + * + */ + +#define _GNU_SOURCE +#ifndef WIN32 +#endif +#include "freetdm.h" +#include +#ifdef WIN32 +#include +#endif +#ifdef FTDM_PIKA_SUPPORT +#include "ftdm_pika.h" +#endif + +#define SPAN_PENDING_CHANS_QUEUE_SIZE 1000 + +static int time_is_init = 0; + +static void time_init(void) +{ +#ifdef WIN32 + timeBeginPeriod(1); +#endif + time_is_init = 1; +} + +static void time_end(void) +{ +#ifdef WIN32 + timeEndPeriod(1); +#endif + time_is_init = 0; +} + +FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void) +{ +#ifdef WIN32 + return timeGetTime(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); +#endif +} + +static struct { + ftdm_hash_t *interface_hash; + ftdm_hash_t *module_hash; + ftdm_hash_t *span_hash; + ftdm_hash_t *group_hash; + ftdm_mutex_t *mutex; + ftdm_mutex_t *span_mutex; + ftdm_mutex_t *group_mutex; + uint32_t span_index; + uint32_t group_index; + uint32_t running; + ftdm_span_t *spans; + ftdm_group_t *groups; +} globals; + + +/* enum lookup funcs */ +FTDM_ENUM_NAMES(TONEMAP_NAMES, TONEMAP_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_tonemap, ftdm_tonemap2str, ftdm_tonemap_t, TONEMAP_NAMES, FTDM_TONEMAP_INVALID) + +FTDM_ENUM_NAMES(OOB_NAMES, OOB_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_oob_event, ftdm_oob_event2str, ftdm_oob_event_t, OOB_NAMES, FTDM_OOB_INVALID) + +FTDM_ENUM_NAMES(TRUNK_TYPE_NAMES, TRUNK_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_trunk_type, ftdm_trunk_type2str, ftdm_trunk_type_t, TRUNK_TYPE_NAMES, FTDM_TRUNK_NONE) + +FTDM_ENUM_NAMES(START_TYPE_NAMES, START_TYPE_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_analog_start_type, ftdm_analog_start_type2str, ftdm_analog_start_type_t, START_TYPE_NAMES, FTDM_ANALOG_START_NA) + +FTDM_ENUM_NAMES(SIGNAL_NAMES, SIGNAL_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_signal_event, ftdm_signal_event2str, ftdm_signal_event_t, SIGNAL_NAMES, FTDM_SIGEVENT_INVALID) + +FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID) + +FTDM_ENUM_NAMES(MDMF_TYPE_NAMES, MDMF_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_mdmf_type, ftdm_mdmf_type2str, ftdm_mdmf_type_t, MDMF_TYPE_NAMES, MDMF_INVALID) + +FTDM_ENUM_NAMES(CHAN_TYPE_NAMES, CHAN_TYPE_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_chan_type, ftdm_chan_type2str, ftdm_chan_type_t, CHAN_TYPE_NAMES, FTDM_CHAN_TYPE_COUNT) + +FTDM_ENUM_NAMES(SIGNALING_STATUS_NAMES, SIGSTATUS_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_signaling_status, ftdm_signaling_status2str, ftdm_signaling_status_t, SIGNALING_STATUS_NAMES, FTDM_SIG_STATE_INVALID) + +static const char *cut_path(const char *in) +{ + const char *p, *ret = in; + char delims[] = "/\\"; + char *i; + + for (i = delims; *i; i++) { + p = in; + while ((p = strchr(p, *i)) != 0) { + ret = ++p; + } + } + return ret; +} + +static void null_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + if (file && func && line && level && fmt) { + return; + } + return; +} + + +static const char *LEVEL_NAMES[] = { + "EMERG", + "ALERT", + "CRIT", + "ERROR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG", + NULL +}; + +static int ftdm_log_level = 7; + +static void default_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + const char *fp; + char data[1024]; + va_list ap; + + if (level < 0 || level > 7) { + level = 7; + } + if (level > ftdm_log_level) { + return; + } + + fp = cut_path(file); + + va_start(ap, fmt); + + vsnprintf(data, sizeof(data), fmt, ap); + + + fprintf(stderr, "[%s] %s:%d %s() %s", LEVEL_NAMES[level], file, line, func, data); + + va_end(ap); + +} + +static __inline__ void *ftdm_std_malloc(void *pool, ftdm_size_t size) +{ + void *ptr = malloc(size); + pool = NULL; /* fix warning */ + ftdm_assert_return(ptr != NULL, NULL, "Out of memory"); + return ptr; +} + +static __inline__ void *ftdm_std_calloc(void *pool, ftdm_size_t elements, ftdm_size_t size) +{ + void *ptr = calloc(elements, size); + pool = NULL; + ftdm_assert_return(ptr != NULL, NULL, "Out of memory"); + return ptr; +} + +static __inline__ void ftdm_std_free(void *pool, void *ptr) +{ + pool = NULL; + ftdm_assert_return(ptr != NULL, , "Attempted to free null pointer"); + free(ptr); +} + +FT_DECLARE_DATA ftdm_memory_handler_t g_ftdm_mem_handler = +{ + /*.pool =*/ NULL, + /*.malloc =*/ ftdm_std_malloc, + /*.calloc =*/ ftdm_std_calloc, + /*.free =*/ ftdm_std_free +}; + +FT_DECLARE_DATA ftdm_crash_policy_t g_ftdm_crash_policy = FTDM_CRASH_NEVER; + +static ftdm_status_t ftdm_set_caller_data(ftdm_span_t *span, ftdm_caller_data_t *caller_data) +{ + if (!caller_data) { + ftdm_log(FTDM_LOG_CRIT, "Error: trying to set caller data, but no caller_data!\n"); + return FTDM_FAIL; + } + + if (caller_data->cid_num.plan == FTDM_NPI_INVALID) { + caller_data->cid_num.plan = span->default_caller_data.cid_num.plan; + } + + if (caller_data->cid_num.type == FTDM_TON_INVALID) { + caller_data->cid_num.type = span->default_caller_data.cid_num.type; + } + + if (caller_data->ani.plan == FTDM_NPI_INVALID) { + caller_data->ani.plan = span->default_caller_data.ani.plan; + } + + if (caller_data->ani.type == FTDM_TON_INVALID) { + caller_data->ani.type = span->default_caller_data.ani.type; + } + + if (caller_data->rdnis.plan == FTDM_NPI_INVALID) { + caller_data->rdnis.plan = span->default_caller_data.rdnis.plan; + } + + if (caller_data->rdnis.type == FTDM_NPI_INVALID) { + caller_data->rdnis.type = span->default_caller_data.rdnis.type; + } + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_set_caller_data(ftdm_channel_t *ftdmchan, ftdm_caller_data_t *caller_data) +{ + ftdm_status_t err = FTDM_SUCCESS; + if (!ftdmchan) { + ftdm_log(FTDM_LOG_CRIT, "Error: trying to set caller data, but no ftdmchan!\n"); + return FTDM_FAIL; + } + if ((err = ftdm_set_caller_data(ftdmchan->span, caller_data)) != FTDM_SUCCESS) { + return err; + } + ftdmchan->caller_data = *caller_data; + return FTDM_SUCCESS; +} + +FT_DECLARE_DATA ftdm_logger_t ftdm_log = null_logger; + +FT_DECLARE(void) ftdm_global_set_crash_policy(ftdm_crash_policy_t policy) +{ + g_ftdm_crash_policy |= policy; +} + +FT_DECLARE(ftdm_status_t) ftdm_global_set_memory_handler(ftdm_memory_handler_t *handler) +{ + if (!handler) { + return FTDM_FAIL; + } + if (!handler->malloc) { + return FTDM_FAIL; + } + if (!handler->calloc) { + return FTDM_FAIL; + } + if (!handler->free) { + return FTDM_FAIL; + } + memcpy(&g_ftdm_mem_handler, handler, sizeof(*handler)); + return FTDM_SUCCESS; +} + +FT_DECLARE(void) ftdm_global_set_logger(ftdm_logger_t logger) +{ + if (logger) { + ftdm_log = logger; + } else { + ftdm_log = null_logger; + } +} + +FT_DECLARE(void) ftdm_global_set_default_logger(int level) +{ + if (level < 0 || level > 7) { + level = 7; + } + + ftdm_log = default_logger; + ftdm_log_level = level; +} + +FT_DECLARE_NONSTD(int) ftdm_hash_equalkeys(void *k1, void *k2) +{ + return strcmp((char *) k1, (char *) k2) ? 0 : 1; +} + +FT_DECLARE_NONSTD(uint32_t) ftdm_hash_hashfromstring(void *ky) +{ + unsigned char *str = (unsigned char *) ky; + uint32_t hash = 0; + int c; + + while ((c = *str++)) { + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + +static ftdm_status_t ftdm_channel_destroy(ftdm_channel_t *ftdmchan) +{ + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CONFIGURED)) { + + while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { + ftdm_log(FTDM_LOG_INFO, "Waiting for thread to exit on channel %u:%u\n", ftdmchan->span_id, ftdmchan->chan_id); + ftdm_sleep(500); + } + + ftdm_mutex_lock(ftdmchan->pre_buffer_mutex); + ftdm_buffer_destroy(&ftdmchan->pre_buffer); + ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex); + + ftdm_buffer_destroy(&ftdmchan->digit_buffer); + ftdm_buffer_destroy(&ftdmchan->gen_dtmf_buffer); + ftdm_buffer_destroy(&ftdmchan->dtmf_buffer); + ftdm_buffer_destroy(&ftdmchan->fsk_buffer); + ftdmchan->pre_buffer_size = 0; + + hashtable_destroy(ftdmchan->variable_hash); + + ftdm_safe_free(ftdmchan->dtmf_hangup_buf); + + if (ftdmchan->tone_session.buffer) { + teletone_destroy_session(&ftdmchan->tone_session); + memset(&ftdmchan->tone_session, 0, sizeof(ftdmchan->tone_session)); + } + + + if (ftdmchan->span->fio->channel_destroy) { + ftdm_log(FTDM_LOG_INFO, "Closing channel %s:%u:%u fd:%d\n", ftdmchan->span->type, ftdmchan->span_id, ftdmchan->chan_id, ftdmchan->sockfd); + if (ftdmchan->span->fio->channel_destroy(ftdmchan) == FTDM_SUCCESS) { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_CONFIGURED); + } else { + ftdm_log(FTDM_LOG_ERROR, "Error Closing channel %u:%u fd:%d\n", ftdmchan->span_id, ftdmchan->chan_id, ftdmchan->sockfd); + } + } + + ftdm_mutex_destroy(&ftdmchan->mutex); + ftdm_mutex_destroy(&ftdmchan->pre_buffer_mutex); + } + + return FTDM_SUCCESS; +} + +static ftdm_status_t ftdm_span_destroy(ftdm_span_t *span) +{ + ftdm_status_t status = FTDM_SUCCESS; + unsigned j; + + ftdm_mutex_lock(span->mutex); + + /* stop the signaling */ + if (span->stop) { + status = span->stop(span); + } + + /* destroy the channels */ + ftdm_clear_flag(span, FTDM_SPAN_CONFIGURED); + for(j = 1; j <= span->chan_count && span->channels[j]; j++) { + ftdm_channel_t *cur_chan = span->channels[j]; + if (cur_chan) { + if (ftdm_test_flag(cur_chan, FTDM_CHANNEL_CONFIGURED)) { + ftdm_channel_destroy(cur_chan); + } + ftdm_safe_free(cur_chan); + cur_chan = NULL; + } + } + + /* destroy the I/O for the span */ + if (span->fio && span->fio->span_destroy) { + ftdm_log(FTDM_LOG_INFO, "Destroying span %u type (%s)\n", span->span_id, span->type); + if (span->fio->span_destroy(span) != FTDM_SUCCESS) { + status = FTDM_FAIL; + } + ftdm_safe_free(span->type); + ftdm_safe_free(span->dtmf_hangup); + } + + /* destroy final basic resources of the span data structure */ + ftdm_queue_destroy(&span->pendingchans); + ftdm_mutex_unlock(span->mutex); + ftdm_mutex_destroy(&span->mutex); + ftdm_safe_free(span->signal_data); + + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_get_alarms(ftdm_channel_t *ftdmchan) +{ + ftdm_status_t status = FTDM_FAIL; + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CONFIGURED)) { + if (ftdmchan->span->fio->get_alarms) { + if ((status = ftdmchan->span->fio->get_alarms(ftdmchan)) == FTDM_SUCCESS) { + *ftdmchan->last_error = '\0'; + if (ftdm_test_alarm_flag(ftdmchan, FTDM_ALARM_RED)) { + snprintf(ftdmchan->last_error + strlen(ftdmchan->last_error), sizeof(ftdmchan->last_error) - strlen(ftdmchan->last_error), "RED/"); + } + if (ftdm_test_alarm_flag(ftdmchan, FTDM_ALARM_YELLOW)) { + snprintf(ftdmchan->last_error + strlen(ftdmchan->last_error), sizeof(ftdmchan->last_error) - strlen(ftdmchan->last_error), "YELLOW/"); + } + if (ftdm_test_alarm_flag(ftdmchan, FTDM_ALARM_BLUE)) { + snprintf(ftdmchan->last_error + strlen(ftdmchan->last_error), sizeof(ftdmchan->last_error) - strlen(ftdmchan->last_error), "BLUE/"); + } + if (ftdm_test_alarm_flag(ftdmchan, FTDM_ALARM_LOOPBACK)) { + snprintf(ftdmchan->last_error + strlen(ftdmchan->last_error), sizeof(ftdmchan->last_error) - strlen(ftdmchan->last_error), "LOOP/"); + } + if (ftdm_test_alarm_flag(ftdmchan, FTDM_ALARM_RECOVER)) { + snprintf(ftdmchan->last_error + strlen(ftdmchan->last_error), sizeof(ftdmchan->last_error) - strlen(ftdmchan->last_error), "RECOVER/"); + } + *(ftdmchan->last_error + strlen(ftdmchan->last_error) - 1) = '\0'; + + } + } else { + status = FTDM_NOTIMPL; + } + } + + return status; +} + +static void ftdm_span_add(ftdm_span_t *span) +{ + ftdm_span_t *sp; + ftdm_mutex_lock(globals.span_mutex); + for (sp = globals.spans; sp && sp->next; sp = sp->next); + if (sp) { + sp->next = span; + } else { + globals.spans = span; + } + hashtable_insert(globals.span_hash, (void *)span->name, span, HASHTABLE_FLAG_NONE); + ftdm_mutex_unlock(globals.span_mutex); +} + +#if 0 +static void ftdm_span_del(ftdm_span_t *span) +{ + ftdm_span_t *last = NULL, *sp; + + ftdm_mutex_lock(globals.span_mutex); + for (sp = globals.spans; sp; sp = sp->next) { + + if (sp == span) { + if (last) { + last->next = sp->next; + } else { + globals.spans = sp->next; + } + hashtable_remove(globals.span_hash, (void *)sp->name); + break; + } + + last = sp; + } + ftdm_mutex_unlock(globals.span_mutex); +} +#endif + +FT_DECLARE(ftdm_status_t) ftdm_span_stop(ftdm_span_t *span) +{ + if (span->stop) { + span->stop(span); + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +FT_DECLARE(ftdm_status_t) ftdm_span_create(ftdm_io_interface_t *fio, ftdm_span_t **span, const char *name) +{ + ftdm_span_t *new_span = NULL; + ftdm_status_t status = FTDM_FAIL; + + ftdm_assert(fio != NULL, "No IO provided\n"); + + ftdm_mutex_lock(globals.mutex); + + if (globals.span_index < FTDM_MAX_SPANS_INTERFACE) { + new_span = ftdm_calloc(sizeof(*new_span), 1); + ftdm_assert(new_span, "allocating span failed\n"); + + status = ftdm_mutex_create(&new_span->mutex); + ftdm_assert(status == FTDM_SUCCESS, "mutex creation failed\n"); + + status = ftdm_queue_create(&new_span->pendingchans, SPAN_PENDING_CHANS_QUEUE_SIZE); + ftdm_assert(status == FTDM_SUCCESS, "span chans queue creation failed\n"); + + ftdm_set_flag(new_span, FTDM_SPAN_CONFIGURED); + new_span->span_id = ++globals.span_index; + new_span->fio = fio; + ftdm_copy_string(new_span->tone_map[FTDM_TONEMAP_DIAL], "%(1000,0,350,440)", FTDM_TONEMAP_LEN); + ftdm_copy_string(new_span->tone_map[FTDM_TONEMAP_RING], "%(2000,4000,440,480)", FTDM_TONEMAP_LEN); + ftdm_copy_string(new_span->tone_map[FTDM_TONEMAP_BUSY], "%(500,500,480,620)", FTDM_TONEMAP_LEN); + ftdm_copy_string(new_span->tone_map[FTDM_TONEMAP_ATTN], "%(100,100,1400,2060,2450,2600)", FTDM_TONEMAP_LEN); + new_span->trunk_type = FTDM_TRUNK_NONE; + new_span->data_type = FTDM_TYPE_SPAN; + + ftdm_mutex_lock(globals.span_mutex); + if (!ftdm_strlen_zero(name) && hashtable_search(globals.span_hash, (void *)name)) { + ftdm_log(FTDM_LOG_WARNING, "name %s is already used, substituting 'span%d' as the name\n", name, new_span->span_id); + name = NULL; + } + ftdm_mutex_unlock(globals.span_mutex); + + if (!name) { + char buf[128] = ""; + snprintf(buf, sizeof(buf), "span%d", new_span->span_id); + name = buf; + } + new_span->name = ftdm_strdup(name); + ftdm_span_add(new_span); + *span = new_span; + status = FTDM_SUCCESS; + } + ftdm_mutex_unlock(globals.mutex); + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_span_close_all(void) +{ + ftdm_span_t *span; + uint32_t i = 0, j; + + ftdm_mutex_lock(globals.span_mutex); + for (span = globals.spans; span; span = span->next) { + if (ftdm_test_flag(span, FTDM_SPAN_CONFIGURED)) { + for(j = 1; j <= span->chan_count && span->channels[j]; j++) { + ftdm_channel_t *toclose = span->channels[j]; + if (ftdm_test_flag(toclose, FTDM_CHANNEL_INUSE)) { + ftdm_channel_close(&toclose); + } + i++; + } + } + } + ftdm_mutex_unlock(globals.span_mutex); + + return i ? FTDM_SUCCESS : FTDM_FAIL; +} + +FT_DECLARE(ftdm_status_t) ftdm_span_load_tones(ftdm_span_t *span, const char *mapname) +{ + ftdm_config_t cfg; + char *var, *val; + int x = 0; + + if (!ftdm_config_open_file(&cfg, "tones.conf")) { + snprintf(span->last_error, sizeof(span->last_error), "error loading tones."); + return FTDM_FAIL; + } + + while (ftdm_config_next_pair(&cfg, &var, &val)) { + int detect = 0; + + if (!strcasecmp(cfg.category, mapname) && var && val) { + uint32_t index; + char *name = NULL; + + if (!strncasecmp(var, "detect-", 7)) { + name = var + 7; + detect = 1; + } else if (!strncasecmp(var, "generate-", 9)) { + name = var + 9; + } else { + ftdm_log(FTDM_LOG_WARNING, "Unknown tone name %s\n", var); + continue; + } + + index = ftdm_str2ftdm_tonemap(name); + + if (index >= FTDM_TONEMAP_INVALID || index == FTDM_TONEMAP_NONE) { + ftdm_log(FTDM_LOG_WARNING, "Unknown tone name %s\n", name); + } else { + if (detect) { + char *p = val, *next; + int i = 0; + do { + teletone_process_t this; + next = strchr(p, ','); + this = (teletone_process_t)atof(p); + span->tone_detect_map[index].freqs[i++] = this; + if (next) { + p = next + 1; + } + } while (next); + ftdm_log(FTDM_LOG_DEBUG, "added tone detect [%s] = [%s]\n", name, val); + } else { + ftdm_log(FTDM_LOG_DEBUG, "added tone generation [%s] = [%s]\n", name, val); + ftdm_copy_string(span->tone_map[index], val, sizeof(span->tone_map[index])); + } + x++; + } + } + } + + ftdm_config_close_file(&cfg); + + if (!x) { + snprintf(span->last_error, sizeof(span->last_error), "error loading tones."); + return FTDM_FAIL; + } + + return FTDM_SUCCESS; + +} + +#define FTDM_SLINEAR_MAX_VALUE 32767 +#define FTDM_SLINEAR_MIN_VALUE -32767 +static void reset_gain_table(unsigned char *gain_table, float new_gain, ftdm_codec_t codec_gain) +{ + /* sample value */ + unsigned char sv = 0; + /* linear gain factor */ + float lingain = 0; + /* linear value for each table sample */ + float linvalue = 0; + /* amplified (or attenuated in case of negative amplification) sample value */ + int ampvalue = 0; + + /* gain tables are only for alaw and ulaw */ + if (codec_gain != FTDM_CODEC_ALAW && codec_gain != FTDM_CODEC_ULAW) { + ftdm_log(FTDM_LOG_WARNING, "Not resetting gain table because codec is not ALAW or ULAW but %d\n", codec_gain); + return; + } + + if (!new_gain) { + /* for a 0.0db gain table, each alaw/ulaw sample value is left untouched (0 ==0, 1 == 1, 2 == 2 etc)*/ + sv = 0; + while (1) { + gain_table[sv] = sv; + if (sv == (FTDM_GAINS_TABLE_SIZE-1)) { + break; + } + sv++; + } + return; + } + + /* use the 20log rule to increase the gain: http://en.wikipedia.org/wiki/Gain, http:/en.wipedia.org/wiki/20_log_rule#Definitions */ + lingain = (float)pow(10.0, new_gain/ 20.0); + sv = 0; + while (1) { + /* get the linear value for this alaw/ulaw sample value */ + linvalue = codec_gain == FTDM_CODEC_ALAW ? (float)alaw_to_linear(sv) : (float)ulaw_to_linear(sv); + + /* multiply the linear value and the previously calculated linear gain */ + ampvalue = (int)(linvalue * lingain); + + /* chop it if goes beyond the limits */ + if (ampvalue > FTDM_SLINEAR_MAX_VALUE) { + ampvalue = FTDM_SLINEAR_MAX_VALUE; + } + + if (ampvalue < FTDM_SLINEAR_MIN_VALUE) { + ampvalue = FTDM_SLINEAR_MIN_VALUE; + } + gain_table[sv] = codec_gain == FTDM_CODEC_ALAW ? linear_to_alaw(ampvalue) : linear_to_ulaw(ampvalue); + if (sv == (FTDM_GAINS_TABLE_SIZE-1)) { + break; + } + sv++; + } +} + +FT_DECLARE(ftdm_status_t) ftdm_span_add_channel(ftdm_span_t *span, ftdm_socket_t sockfd, ftdm_chan_type_t type, ftdm_channel_t **chan) +{ + unsigned char i = 0; + if (span->chan_count < FTDM_MAX_CHANNELS_SPAN) { + ftdm_channel_t *new_chan = span->channels[++span->chan_count]; + + if (!new_chan) { + if (!(new_chan = ftdm_calloc(1, sizeof(*new_chan)))) { + return FTDM_FAIL; + } + span->channels[span->chan_count] = new_chan; + } + + new_chan->type = type; + new_chan->sockfd = sockfd; + new_chan->fio = span->fio; + new_chan->span_id = span->span_id; + new_chan->chan_id = span->chan_count; + new_chan->span = span; + new_chan->fds[0] = -1; + new_chan->fds[1] = -1; + new_chan->data_type = FTDM_TYPE_CHANNEL; + if (!new_chan->dtmf_on) { + new_chan->dtmf_on = FTDM_DEFAULT_DTMF_ON; + } + + if (!new_chan->dtmf_off) { + new_chan->dtmf_off = FTDM_DEFAULT_DTMF_OFF; + } + + ftdm_mutex_create(&new_chan->mutex); + ftdm_mutex_create(&new_chan->pre_buffer_mutex); + + ftdm_buffer_create(&new_chan->digit_buffer, 128, 128, 0); + ftdm_buffer_create(&new_chan->gen_dtmf_buffer, 128, 128, 0); + new_chan->variable_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + + new_chan->dtmf_hangup_buf = ftdm_calloc (span->dtmf_hangup_len + 1, sizeof (char)); + + /* set 0.0db gain table */ + i = 0; + while (1) { + new_chan->txgain_table[i] = i; + new_chan->rxgain_table[i] = i; + if (i == (sizeof(new_chan->txgain_table)-1)) { + break; + } + i++; + } + + ftdm_set_flag(new_chan, FTDM_CHANNEL_CONFIGURED | FTDM_CHANNEL_READY); + *chan = new_chan; + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +FT_DECLARE(ftdm_status_t) ftdm_span_find_by_name(const char *name, ftdm_span_t **span) +{ + ftdm_status_t status = FTDM_FAIL; + + ftdm_mutex_lock(globals.span_mutex); + if (!ftdm_strlen_zero(name)) { + if ((*span = hashtable_search(globals.span_hash, (void *)name))) { + status = FTDM_SUCCESS; + } else { + int span_id = atoi(name); + + ftdm_span_find(span_id, span); + if (*span) { + status = FTDM_SUCCESS; + } + } + } + ftdm_mutex_unlock(globals.span_mutex); + + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_span_find(uint32_t id, ftdm_span_t **span) +{ + ftdm_span_t *fspan = NULL, *sp; + + if (id > FTDM_MAX_SPANS_INTERFACE) { + return FTDM_FAIL; + } + + ftdm_mutex_lock(globals.span_mutex); + for (sp = globals.spans; sp; sp = sp->next) { + if (sp->span_id == id) { + fspan = sp; + break; + } + } + ftdm_mutex_unlock(globals.span_mutex); + + if (!fspan || !ftdm_test_flag(fspan, FTDM_SPAN_CONFIGURED)) { + return FTDM_FAIL; + } + + *span = fspan; + + return FTDM_SUCCESS; + +} + +FT_DECLARE(ftdm_status_t) ftdm_span_set_event_callback(ftdm_span_t *span, fio_event_cb_t event_callback) +{ + ftdm_mutex_lock(span->mutex); + span->event_callback = event_callback; + ftdm_mutex_unlock(span->mutex); + return FTDM_SUCCESS; +} + + +FT_DECLARE(ftdm_status_t) ftdm_span_poll_event(ftdm_span_t *span, uint32_t ms) +{ + assert(span->fio != NULL); + + if (span->fio->poll_event) { + return span->fio->poll_event(span, ms); + } else { + ftdm_log(FTDM_LOG_ERROR, "poll_event method not implemented in module %s!", span->fio->name); + } + + return FTDM_NOTIMPL; +} + +FT_DECLARE(ftdm_status_t) ftdm_span_next_event(ftdm_span_t *span, ftdm_event_t **event) +{ + ftdm_status_t status = FTDM_FAIL; + ftdm_sigmsg_t sigmsg; + ftdm_assert_return(span->fio != NULL, FTDM_FAIL, "No I/O module attached to this span!\n"); + + if (!span->fio->next_event) { + ftdm_log(FTDM_LOG_ERROR, "next_event method not implemented in module %s!", span->fio->name); + return FTDM_NOTIMPL; + } + + status = span->fio->next_event(span, event); + if (status != FTDM_SUCCESS) { + return status; + } + + /* before returning the event to the user we do some core operations with certain OOB events */ + memset(&sigmsg, 0, sizeof(sigmsg)); + sigmsg.span_id = span->span_id; + sigmsg.chan_id = (*event)->channel->chan_id; + sigmsg.channel = (*event)->channel; + switch ((*event)->enum_id) { + case FTDM_OOB_ALARM_CLEAR: + { + sigmsg.event_id = FTDM_SIGEVENT_ALARM_CLEAR; + ftdm_clear_flag_locked((*event)->channel, FTDM_CHANNEL_IN_ALARM); + ftdm_span_send_signal(span, &sigmsg); + } + break; + case FTDM_OOB_ALARM_TRAP: + { + sigmsg.event_id = FTDM_SIGEVENT_ALARM_TRAP; + ftdm_set_flag_locked((*event)->channel, FTDM_CHANNEL_IN_ALARM); + ftdm_span_send_signal(span, &sigmsg); + } + break; + default: + /* NOOP */ + break; + } + + return status; +} + +static ftdm_status_t ftdmchan_fsk_write_sample(int16_t *buf, ftdm_size_t buflen, void *user_data) +{ + ftdm_channel_t *ftdmchan = (ftdm_channel_t *) user_data; + ftdm_buffer_write(ftdmchan->fsk_buffer, buf, buflen * 2); + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_send_fsk_data(ftdm_channel_t *ftdmchan, ftdm_fsk_data_state_t *fsk_data, float db_level) +{ + struct ftdm_fsk_modulator fsk_trans; + + if (!ftdmchan->fsk_buffer) { + ftdm_buffer_create(&ftdmchan->fsk_buffer, 128, 128, 0); + } else { + ftdm_buffer_zero(ftdmchan->fsk_buffer); + } + + if (ftdmchan->token_count > 1) { + ftdm_fsk_modulator_init(&fsk_trans, FSK_BELL202, ftdmchan->rate, fsk_data, db_level, 80, 5, 0, ftdmchan_fsk_write_sample, ftdmchan); + ftdm_fsk_modulator_send_all((&fsk_trans)); + } else { + ftdm_fsk_modulator_init(&fsk_trans, FSK_BELL202, ftdmchan->rate, fsk_data, db_level, 180, 5, 300, ftdmchan_fsk_write_sample, ftdmchan); + ftdm_fsk_modulator_send_all((&fsk_trans)); + ftdmchan->buffer_delay = 3500 / ftdmchan->effective_interval; + } + + return FTDM_SUCCESS; +} + + +FT_DECLARE(ftdm_status_t) ftdm_channel_set_event_callback(ftdm_channel_t *ftdmchan, fio_event_cb_t event_callback) +{ + ftdm_mutex_lock(ftdmchan->mutex); + ftdmchan->event_callback = event_callback; + ftdm_mutex_unlock(ftdmchan->mutex); + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_clear_token(ftdm_channel_t *ftdmchan, const char *token) +{ + ftdm_status_t status = FTDM_FAIL; + + ftdm_mutex_lock(ftdmchan->mutex); + if (token == NULL) { + memset(ftdmchan->tokens, 0, sizeof(ftdmchan->tokens)); + ftdmchan->token_count = 0; + } else if (*token != '\0') { + char tokens[FTDM_MAX_TOKENS][FTDM_TOKEN_STRLEN]; + int32_t i, count = ftdmchan->token_count; + memcpy(tokens, ftdmchan->tokens, sizeof(tokens)); + memset(ftdmchan->tokens, 0, sizeof(ftdmchan->tokens)); + ftdmchan->token_count = 0; + + for (i = 0; i < count; i++) { + if (strcmp(tokens[i], token)) { + ftdm_copy_string(ftdmchan->tokens[ftdmchan->token_count], tokens[i], sizeof(ftdmchan->tokens[ftdmchan->token_count])); + ftdmchan->token_count++; + } + } + + status = FTDM_SUCCESS; + } + ftdm_mutex_unlock(ftdmchan->mutex); + + return status; +} + +FT_DECLARE(void) ftdm_channel_rotate_tokens(ftdm_channel_t *ftdmchan) +{ + if (ftdmchan->token_count) { + memmove(ftdmchan->tokens[1], ftdmchan->tokens[0], ftdmchan->token_count * FTDM_TOKEN_STRLEN); + ftdm_copy_string(ftdmchan->tokens[0], ftdmchan->tokens[ftdmchan->token_count], FTDM_TOKEN_STRLEN); + *ftdmchan->tokens[ftdmchan->token_count] = '\0'; + } +} + +FT_DECLARE(void) ftdm_channel_replace_token(ftdm_channel_t *ftdmchan, const char *old_token, const char *new_token) +{ + unsigned int i; + + if (ftdmchan->token_count) { + for(i = 0; i < ftdmchan->token_count; i++) { + if (!strcmp(ftdmchan->tokens[i], old_token)) { + ftdm_copy_string(ftdmchan->tokens[i], new_token, FTDM_TOKEN_STRLEN); + break; + } + } + } +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_add_token(ftdm_channel_t *ftdmchan, char *token, int end) +{ + ftdm_status_t status = FTDM_FAIL; + + ftdm_mutex_lock(ftdmchan->mutex); + if (ftdmchan->token_count < FTDM_MAX_TOKENS) { + if (end) { + ftdm_copy_string(ftdmchan->tokens[ftdmchan->token_count++], token, FTDM_TOKEN_STRLEN); + } else { + memmove(ftdmchan->tokens[1], ftdmchan->tokens[0], ftdmchan->token_count * FTDM_TOKEN_STRLEN); + ftdm_copy_string(ftdmchan->tokens[0], token, FTDM_TOKEN_STRLEN); + ftdmchan->token_count++; + } + status = FTDM_SUCCESS; + } + ftdm_mutex_unlock(ftdmchan->mutex); + + return status; +} + + +FT_DECLARE(ftdm_status_t) ftdm_channel_complete_state(ftdm_channel_t *ftdmchan) +{ + ftdm_channel_state_t state = ftdmchan->state; + + if (state == FTDM_CHANNEL_STATE_PROGRESS) { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); + } else if (state == FTDM_CHANNEL_STATE_UP) { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED); + } else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA); + } + + return FTDM_SUCCESS; +} + +static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map) +{ + int x = 0, ok = 0; + ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND; + + for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) { + int i = 0, proceed = 0; + if (!state_map->nodes[x].type) { + break; + } + + if (state_map->nodes[x].direction != direction) { + continue; + } + + if (state_map->nodes[x].check_states[0] == FTDM_ANY_STATE) { + proceed = 1; + } else { + for(i = 0; i < FTDM_MAP_MAX; i++) { + if (state_map->nodes[x].check_states[i] == ftdmchan->state) { + proceed = 1; + break; + } + } + } + + if (!proceed) { + continue; + } + + for(i = 0; i < FTDM_MAP_MAX; i++) { + ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE); + if (state_map->nodes[x].states[i] == FTDM_END) { + break; + } + if (state_map->nodes[x].states[i] == state) { + ok = !ok; + goto end; + } + } + } + end: + + return ok; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int lock) +{ + int ok = 1; + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) { + return FTDM_FAIL; + } + + if (ftdm_test_flag(ftdmchan->span, FTDM_SPAN_SUSPENDED)) { + if (state != FTDM_CHANNEL_STATE_RESTART && state != FTDM_CHANNEL_STATE_DOWN) { + return FTDM_FAIL; + } + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { + ftdm_log(FTDM_LOG_CRIT, "Ignored state change request from %s to %s, the previous state change has not been processed yet\n", + ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + return FTDM_FAIL; + } + + if (lock) { + ftdm_mutex_lock(ftdmchan->mutex); + } + + if (ftdmchan->span->state_map) { + ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map); + goto end; + } + + switch(ftdmchan->state) { + case FTDM_CHANNEL_STATE_HANGUP: + case FTDM_CHANNEL_STATE_TERMINATING: + { + ok = 0; + switch(state) { + case FTDM_CHANNEL_STATE_DOWN: + case FTDM_CHANNEL_STATE_BUSY: + case FTDM_CHANNEL_STATE_RESTART: + ok = 1; + break; + default: + break; + } + } + break; + case FTDM_CHANNEL_STATE_UP: + { + ok = 1; + switch(state) { + case FTDM_CHANNEL_STATE_PROGRESS: + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + case FTDM_CHANNEL_STATE_RING: + ok = 0; + break; + default: + break; + } + } + break; + case FTDM_CHANNEL_STATE_DOWN: + { + ok = 0; + + switch(state) { + case FTDM_CHANNEL_STATE_DIALTONE: + case FTDM_CHANNEL_STATE_COLLECT: + case FTDM_CHANNEL_STATE_DIALING: + case FTDM_CHANNEL_STATE_RING: + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + case FTDM_CHANNEL_STATE_PROGRESS: + case FTDM_CHANNEL_STATE_GET_CALLERID: + case FTDM_CHANNEL_STATE_GENRING: + ok = 1; + break; + default: + break; + } + } + break; + case FTDM_CHANNEL_STATE_BUSY: + { + switch(state) { + case FTDM_CHANNEL_STATE_UP: + ok = 0; + break; + default: + break; + } + } + break; + case FTDM_CHANNEL_STATE_RING: + { + switch(state) { + case FTDM_CHANNEL_STATE_UP: + ok = 1; + break; + default: + break; + } + } + break; + default: + break; + } + + end: + + if (state == ftdmchan->state) { + ok = 0; + } + + + if (ok) { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + + ftdm_mutex_lock(ftdmchan->span->mutex); + ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); + ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan); + ftdm_mutex_unlock(ftdmchan->span->mutex); + + ftdmchan->last_state = ftdmchan->state; + ftdmchan->state = state; + } + + if (lock) { + ftdm_mutex_unlock(ftdmchan->mutex); + } + + return ok ? FTDM_SUCCESS : FTDM_FAIL; +} + + +FT_DECLARE(ftdm_status_t) ftdm_group_channel_use_count(ftdm_group_t *group, uint32_t *count) +{ + uint32_t j; + + *count = 0; + + if (!group) { + return FTDM_FAIL; + } + + for(j = 0; j < group->chan_count && group->channels[j]; j++) { + if (group->channels[j]) { + if (ftdm_test_flag(group->channels[j], FTDM_CHANNEL_INUSE)) { + (*count)++; + } + } + } + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_group(uint32_t group_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) +{ + ftdm_status_t status = FTDM_FAIL; + ftdm_channel_t *check; + uint32_t i, count; + ftdm_group_t *group = NULL; + + if (group_id) { + ftdm_group_find(group_id, &group); + } + + if (!group) { + ftdm_log(FTDM_LOG_ERROR, "Group %d not defined!\n", group_id); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + ftdm_group_channel_use_count(group, &count); + + if (count >= group->chan_count) { + ftdm_log(FTDM_LOG_ERROR, "All circuits are busy (%d channels used out of %d available).\n", count, group->chan_count); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + + if (direction == FTDM_TOP_DOWN) { + i = 0; + } else { + i = group->chan_count-1; + } + + ftdm_mutex_lock(group->mutex); + for (;;) { + if (direction == FTDM_TOP_DOWN) { + if (i >= group->chan_count) { + break; + } + } else { + if (i < 0) { + break; + } + } + + if (!(check = group->channels[i])) { + status = FTDM_FAIL; + break; + } + + if (ftdm_test_flag(check, FTDM_CHANNEL_READY) && + !ftdm_test_flag(check, FTDM_CHANNEL_INUSE) && + !ftdm_test_flag(check, FTDM_CHANNEL_SUSPENDED) && + !ftdm_test_flag(check, FTDM_CHANNEL_IN_ALARM) && + check->state == FTDM_CHANNEL_STATE_DOWN && + FTDM_IS_VOICE_CHANNEL(check) + ) { + ftdm_span_t* span = NULL; + ftdm_span_find(check->span_id, &span); + if (span && span->channel_request) { + status = span->channel_request(span, check->chan_id, direction, caller_data, ftdmchan); + break; + } + + status = check->fio->open(check); + + if (status == FTDM_SUCCESS) { + ftdm_set_flag(check, FTDM_CHANNEL_INUSE); + ftdm_channel_open_chan(check); + *ftdmchan = check; + break; + } + } + + if (direction == FTDM_TOP_DOWN) { + i++; + } else { + i--; + } + } + ftdm_mutex_unlock(group->mutex); + return status; +} + + +FT_DECLARE(ftdm_status_t) ftdm_span_channel_use_count(ftdm_span_t *span, uint32_t *count) +{ + uint32_t j; + + *count = 0; + + if (!span || !ftdm_test_flag(span, FTDM_SPAN_CONFIGURED)) { + return FTDM_FAIL; + } + + for(j = 1; j <= span->chan_count && span->channels[j]; j++) { + if (span->channels[j]) { + if (ftdm_test_flag(span->channels[j], FTDM_CHANNEL_INUSE)) { + (*count)++; + } + } + } + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) +{ + ftdm_status_t status = FTDM_FAIL; + ftdm_channel_t *check; + uint32_t i, j, count; + ftdm_span_t *span = NULL; + uint32_t span_max; + + if (span_id) { + ftdm_span_find(span_id, &span); + + if (!span || !ftdm_test_flag(span, FTDM_SPAN_CONFIGURED)) { + ftdm_log(FTDM_LOG_CRIT, "SPAN NOT DEFINED!\n"); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + ftdm_span_channel_use_count(span, &count); + + if (count >= span->chan_count) { + ftdm_log(FTDM_LOG_CRIT, "All circuits are busy.\n"); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + if (span->channel_request && !span->suggest_chan_id) { + ftdm_set_caller_data(span, caller_data); + return span->channel_request(span, 0, direction, caller_data, ftdmchan); + } + + span_max = span_id; + j = span_id; + } else { + ftdm_log(FTDM_LOG_CRIT, "No span supplied\n"); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + ftdm_mutex_lock(span->mutex); + + if (direction == FTDM_TOP_DOWN) { + i = 1; + } else { + i = span->chan_count; + } + + for(;;) { + + if (direction == FTDM_TOP_DOWN) { + if (i > span->chan_count) { + break; + } + } else { + if (i == 0) { + break; + } + } + + if (!(check = span->channels[i])) { + status = FTDM_FAIL; + break; + } + + if (ftdm_test_flag(check, FTDM_CHANNEL_READY) && + !ftdm_test_flag(check, FTDM_CHANNEL_INUSE) && + !ftdm_test_flag(check, FTDM_CHANNEL_SUSPENDED) && + !ftdm_test_flag(check, FTDM_CHANNEL_IN_ALARM) && + check->state == FTDM_CHANNEL_STATE_DOWN && + FTDM_IS_VOICE_CHANNEL(check) + ) { + + if (span && span->channel_request) { + ftdm_set_caller_data(span, caller_data); + status = span->channel_request(span, i, direction, caller_data, ftdmchan); + break; + } + + status = check->fio->open(check); + + if (status == FTDM_SUCCESS) { + ftdm_set_flag(check, FTDM_CHANNEL_INUSE); + ftdm_channel_open_chan(check); + *ftdmchan = check; + break; + } + } + + if (direction == FTDM_TOP_DOWN) { + i++; + } else { + i--; + } + } + + ftdm_mutex_unlock(span->mutex); + + return status; +} + +static ftdm_status_t ftdm_channel_reset(ftdm_channel_t *ftdmchan) +{ + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_OPEN); + ftdmchan->event_callback = NULL; + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF); + ftdm_channel_done(ftdmchan); + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_HOLD); + + memset(ftdmchan->tokens, 0, sizeof(ftdmchan->tokens)); + ftdmchan->token_count = 0; + + ftdm_channel_flush_dtmf(ftdmchan); + + if (ftdmchan->gen_dtmf_buffer) { + ftdm_buffer_zero(ftdmchan->gen_dtmf_buffer); + } + + if (ftdmchan->digit_buffer) { + ftdm_buffer_zero(ftdmchan->digit_buffer); + } + + if (!ftdmchan->dtmf_on) { + ftdmchan->dtmf_on = FTDM_DEFAULT_DTMF_ON; + } + + if (!ftdmchan->dtmf_off) { + ftdmchan->dtmf_off = FTDM_DEFAULT_DTMF_OFF; + } + + memset(ftdmchan->dtmf_hangup_buf, '\0', ftdmchan->span->dtmf_hangup_len); + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_TRANSCODE)) { + ftdmchan->effective_codec = ftdmchan->native_codec; + ftdmchan->packet_len = ftdmchan->native_interval * (ftdmchan->effective_codec == FTDM_CODEC_SLIN ? 16 : 8); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_TRANSCODE); + } + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *ftdmchan) +{ + if (ftdmchan->init_state != FTDM_CHANNEL_STATE_DOWN) { + ftdm_set_state_locked(ftdmchan, ftdmchan->init_state); + ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN; + } + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan) +{ + ftdm_status_t status = FTDM_FAIL; + + assert(ftdmchan != NULL); + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SUSPENDED)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", "Channel is suspended\n"); + return FTDM_FAIL; + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_IN_ALARM)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", "Channel is alarmed\n"); + return FTDM_FAIL; + } + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY) || (status = ftdm_mutex_trylock(ftdmchan->mutex)) != FTDM_SUCCESS) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Channel is not ready or is in use %d %d", ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY), status); + return status; + } + + status = FTDM_FAIL; + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) { + status = ftdmchan->span->fio->open(ftdmchan); + if (status == FTDM_SUCCESS) { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OPEN | FTDM_CHANNEL_INUSE); + } + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", "Channel is not ready"); + } + + ftdm_mutex_unlock(ftdmchan->mutex); + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_open(uint32_t span_id, uint32_t chan_id, ftdm_channel_t **ftdmchan) +{ + ftdm_channel_t *check; + ftdm_status_t status = FTDM_FAIL; + ftdm_span_t *span = NULL; + + ftdm_mutex_unlock(globals.mutex); + ftdm_span_find(span_id, &span); + + if (!span || !ftdm_test_flag(span, FTDM_SPAN_CONFIGURED) || chan_id >= FTDM_MAX_CHANNELS_SPAN) { + ftdm_log(FTDM_LOG_CRIT, "SPAN NOT DEFINED!\n"); + *ftdmchan = NULL; + goto done; + } + + if (span->channel_request) { + ftdm_log(FTDM_LOG_ERROR, "Individual channel selection not implemented on this span.\n"); + *ftdmchan = NULL; + goto done; + } + + if (!(check = span->channels[chan_id])) { + ftdm_log(FTDM_LOG_ERROR, "Invalid Channel %d\n", chan_id); + *ftdmchan = NULL; + goto done; + } + + if (ftdm_test_flag(check, FTDM_CHANNEL_SUSPENDED) || ftdm_test_flag(check, FTDM_CHANNEL_IN_ALARM) || + !ftdm_test_flag(check, FTDM_CHANNEL_READY) || (status = ftdm_mutex_trylock(check->mutex)) != FTDM_SUCCESS) { + *ftdmchan = NULL; + goto done; + } + + status = FTDM_FAIL; + + if (ftdm_test_flag(check, FTDM_CHANNEL_READY) && (!ftdm_test_flag(check, FTDM_CHANNEL_INUSE) || + (check->type == FTDM_CHAN_TYPE_FXS && check->token_count == 1))) { + if (!ftdm_test_flag(check, FTDM_CHANNEL_OPEN)) { + status = check->fio->open(check); + if (status == FTDM_SUCCESS) { + ftdm_set_flag(check, FTDM_CHANNEL_OPEN); + } + } else { + status = FTDM_SUCCESS; + } + ftdm_set_flag(check, FTDM_CHANNEL_INUSE); + *ftdmchan = check; + } + ftdm_mutex_unlock(check->mutex); + + done: + ftdm_mutex_unlock(globals.mutex); + + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_outgoing_call(ftdm_channel_t *ftdmchan) +{ + ftdm_status_t status; + + assert(ftdmchan != NULL); + + if (ftdmchan->span->outgoing_call) { + if ((status = ftdmchan->span->outgoing_call(ftdmchan)) == FTDM_SUCCESS) { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); + } + return status; + } else { + ftdm_log(FTDM_LOG_ERROR, "outgoing_call method not implemented!\n"); + } + + return FTDM_FAIL; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_set_sig_status(ftdm_channel_t *ftdmchan, ftdm_signaling_status_t sigstatus) +{ + ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "Null channel\n"); + ftdm_assert_return(ftdmchan->span != NULL, FTDM_FAIL, "Null span\n"); + + if (ftdmchan->span->set_channel_sig_status) { + return ftdmchan->span->set_channel_sig_status(ftdmchan, sigstatus); + } else { + ftdm_log(FTDM_LOG_ERROR, "set_channel_sig_status method not implemented!\n"); + return FTDM_FAIL; + } +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_get_sig_status(ftdm_channel_t *ftdmchan, ftdm_signaling_status_t *sigstatus) +{ + ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "Null channel\n"); + ftdm_assert_return(ftdmchan->span != NULL, FTDM_FAIL, "Null span\n"); + ftdm_assert_return(sigstatus != NULL, FTDM_FAIL, "Null sig status parameter\n"); + + if (ftdmchan->span->get_channel_sig_status) { + return ftdmchan->span->get_channel_sig_status(ftdmchan, sigstatus); + } else { + ftdm_log(FTDM_LOG_ERROR, "get_channel_sig_status method not implemented!\n"); + return FTDM_FAIL; + } +} + +FT_DECLARE(ftdm_status_t) ftdm_span_set_sig_status(ftdm_span_t *span, ftdm_signaling_status_t sigstatus) +{ + ftdm_assert_return(span != NULL, FTDM_FAIL, "Null span\n"); + + if (span->set_span_sig_status) { + return span->set_span_sig_status(span, sigstatus); + } else { + ftdm_log(FTDM_LOG_ERROR, "set_span_sig_status method not implemented!\n"); + return FTDM_FAIL; + } +} + +FT_DECLARE(ftdm_status_t) ftdm_span_get_sig_status(ftdm_span_t *span, ftdm_signaling_status_t *sigstatus) +{ + ftdm_assert_return(span != NULL, FTDM_FAIL, "Null span\n"); + ftdm_assert_return(sigstatus != NULL, FTDM_FAIL, "Null sig status parameter\n"); + + if (span->get_span_sig_status) { + return span->get_span_sig_status(span, sigstatus); + } else { + ftdm_log(FTDM_LOG_ERROR, "get_span_sig_status method not implemented!\n"); + return FTDM_FAIL; + } +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan) +{ + assert(ftdmchan != NULL); + + ftdm_mutex_lock(ftdmchan->mutex); + + memset(&ftdmchan->caller_data, 0, sizeof(ftdmchan->caller_data)); + + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_INUSE); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_WINK); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_FLASH); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_HOLD); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RINGING); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_3WAY); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_MEDIA); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_ANSWERED); + ftdm_mutex_lock(ftdmchan->pre_buffer_mutex); + ftdm_buffer_destroy(&ftdmchan->pre_buffer); + ftdmchan->pre_buffer_size = 0; + ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex); + + ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN; + ftdmchan->state = FTDM_CHANNEL_STATE_DOWN; + + ftdm_log(FTDM_LOG_DEBUG, "channel done %u:%u\n", ftdmchan->span_id, ftdmchan->chan_id); + + ftdm_mutex_unlock(ftdmchan->mutex); + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_use(ftdm_channel_t *ftdmchan) +{ + + assert(ftdmchan != NULL); + + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INUSE); + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_close(ftdm_channel_t **ftdmchan) +{ + ftdm_channel_t *check; + ftdm_status_t status = FTDM_FAIL; + + assert(ftdmchan != NULL); + check = *ftdmchan; + *ftdmchan = NULL; + + if (!check) { + return FTDM_FAIL; + } + + if (!ftdm_test_flag(check, FTDM_CHANNEL_INUSE)) { + ftdm_log(FTDM_LOG_WARNING, "Called ftdm_channel_close but never ftdm_channel_open in chan %d:%d??\n", check->span_id, check->chan_id); + return FTDM_FAIL; + } + + if (ftdm_test_flag(check, FTDM_CHANNEL_CONFIGURED)) { + ftdm_mutex_lock(check->mutex); + if (ftdm_test_flag(check, FTDM_CHANNEL_OPEN)) { + status = check->fio->close(check); + if (status == FTDM_SUCCESS) { + ftdm_clear_flag(check, FTDM_CHANNEL_INUSE); + ftdm_channel_reset(check); + *ftdmchan = NULL; + } + } + check->ring_count = 0; + ftdm_mutex_unlock(check->mutex); + } + + return status; +} + + +static ftdm_status_t ftdmchan_activate_dtmf_buffer(ftdm_channel_t *ftdmchan) +{ + + if (!ftdmchan->dtmf_buffer) { + if (ftdm_buffer_create(&ftdmchan->dtmf_buffer, 1024, 3192, 0) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to allocate DTMF Buffer!\n"); + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "buffer error"); + return FTDM_FAIL; + } else { + ftdm_log(FTDM_LOG_DEBUG, "Created DTMF Buffer!\n"); + } + } + + + if (!ftdmchan->tone_session.buffer) { + memset(&ftdmchan->tone_session, 0, sizeof(ftdmchan->tone_session)); + teletone_init_session(&ftdmchan->tone_session, 0, NULL, NULL); + } + + ftdmchan->tone_session.rate = ftdmchan->rate; + ftdmchan->tone_session.duration = ftdmchan->dtmf_on * (ftdmchan->tone_session.rate / 1000); + ftdmchan->tone_session.wait = ftdmchan->dtmf_off * (ftdmchan->tone_session.rate / 1000); + ftdmchan->tone_session.volume = -7; + + /* + ftdmchan->tone_session.debug = 1; + ftdmchan->tone_session.debug_stream = stdout; + */ + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_command_t command, void *obj) +{ + ftdm_status_t status = FTDM_FAIL; + + assert(ftdmchan != NULL); + assert(ftdmchan->fio != NULL); + + ftdm_mutex_lock(ftdmchan->mutex); + + switch(command) { + + case FTDM_COMMAND_ENABLE_CALLERID_DETECT: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_CALLERID)) { + if (ftdm_fsk_demod_init(&ftdmchan->fsk, ftdmchan->rate, ftdmchan->fsk_buf, sizeof(ftdmchan->fsk_buf)) != FTDM_SUCCESS) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + GOTO_STATUS(done, FTDM_FAIL); + } + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT); + } + } + break; + case FTDM_COMMAND_DISABLE_CALLERID_DETECT: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_CALLERID)) { + ftdm_fsk_demod_destroy(&ftdmchan->fsk); + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT); + } + } + break; + case FTDM_COMMAND_TRACE_INPUT: + { + char *path = (char *) obj; + if (ftdmchan->fds[0] > 0) { + close(ftdmchan->fds[0]); + ftdmchan->fds[0] = -1; + } + if ((ftdmchan->fds[0] = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) > -1) { + ftdm_log(FTDM_LOG_DEBUG, "Tracing channel %u:%u to [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, path); + GOTO_STATUS(done, FTDM_SUCCESS); + } + + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + GOTO_STATUS(done, FTDM_FAIL); + } + break; + case FTDM_COMMAND_TRACE_OUTPUT: + { + char *path = (char *) obj; + if (ftdmchan->fds[1] > 0) { + close(ftdmchan->fds[1]); + ftdmchan->fds[1] = -1; + } + if ((ftdmchan->fds[1] = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) > -1) { + ftdm_log(FTDM_LOG_DEBUG, "Tracing channel %u:%u to [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, path); + GOTO_STATUS(done, FTDM_SUCCESS); + } + + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + GOTO_STATUS(done, FTDM_FAIL); + } + break; + case FTDM_COMMAND_SET_INTERVAL: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL)) { + ftdmchan->effective_interval = FTDM_COMMAND_OBJ_INT; + if (ftdmchan->effective_interval == ftdmchan->native_interval) { + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_BUFFER); + } else { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BUFFER); + } + ftdmchan->packet_len = ftdmchan->native_interval * (ftdmchan->effective_codec == FTDM_CODEC_SLIN ? 16 : 8); + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + case FTDM_COMMAND_GET_INTERVAL: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL)) { + FTDM_COMMAND_OBJ_INT = ftdmchan->effective_interval; + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + case FTDM_COMMAND_SET_CODEC: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_CODECS)) { + ftdmchan->effective_codec = FTDM_COMMAND_OBJ_INT; + + if (ftdmchan->effective_codec == ftdmchan->native_codec) { + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_TRANSCODE); + } else { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_TRANSCODE); + } + ftdmchan->packet_len = ftdmchan->native_interval * (ftdmchan->effective_codec == FTDM_CODEC_SLIN ? 16 : 8); + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + + case FTDM_COMMAND_SET_NATIVE_CODEC: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_CODECS)) { + ftdmchan->effective_codec = ftdmchan->native_codec; + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_TRANSCODE); + ftdmchan->packet_len = ftdmchan->native_interval * (ftdmchan->effective_codec == FTDM_CODEC_SLIN ? 16 : 8); + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + + case FTDM_COMMAND_GET_CODEC: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_CODECS)) { + FTDM_COMMAND_OBJ_INT = ftdmchan->effective_codec; + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + case FTDM_COMMAND_GET_NATIVE_CODEC: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_CODECS)) { + FTDM_COMMAND_OBJ_INT = ftdmchan->native_codec; + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + case FTDM_COMMAND_ENABLE_PROGRESS_DETECT: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_PROGRESS)) { + /* if they don't have thier own, use ours */ + ftdm_channel_clear_detected_tones(ftdmchan); + ftdm_channel_clear_needed_tones(ftdmchan); + teletone_multi_tone_init(&ftdmchan->span->tone_finder[FTDM_TONEMAP_DIAL], &ftdmchan->span->tone_detect_map[FTDM_TONEMAP_DIAL]); + teletone_multi_tone_init(&ftdmchan->span->tone_finder[FTDM_TONEMAP_RING], &ftdmchan->span->tone_detect_map[FTDM_TONEMAP_RING]); + teletone_multi_tone_init(&ftdmchan->span->tone_finder[FTDM_TONEMAP_BUSY], &ftdmchan->span->tone_detect_map[FTDM_TONEMAP_BUSY]); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT); + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + case FTDM_COMMAND_DISABLE_PROGRESS_DETECT: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_PROGRESS)) { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT); + ftdm_channel_clear_detected_tones(ftdmchan); + ftdm_channel_clear_needed_tones(ftdmchan); + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + case FTDM_COMMAND_ENABLE_DTMF_DETECT: + { + /* if they don't have thier own, use ours */ + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) { + ftdm_tone_type_t tt = FTDM_COMMAND_OBJ_INT; + if (tt == FTDM_TONE_DTMF) { + teletone_dtmf_detect_init (&ftdmchan->dtmf_detect, ftdmchan->rate); + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_DTMF_DETECT); + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF); + GOTO_STATUS(done, FTDM_SUCCESS); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "invalid command"); + GOTO_STATUS(done, FTDM_FAIL); + } + } + } + break; + case FTDM_COMMAND_DISABLE_DTMF_DETECT: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) { + ftdm_tone_type_t tt = FTDM_COMMAND_OBJ_INT; + if (tt == FTDM_TONE_DTMF) { + teletone_dtmf_detect_init (&ftdmchan->dtmf_detect, ftdmchan->rate); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF); + GOTO_STATUS(done, FTDM_SUCCESS); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "invalid command"); + GOTO_STATUS(done, FTDM_FAIL); + } + } + } + + case FTDM_COMMAND_SET_PRE_BUFFER_SIZE: + { + int val = FTDM_COMMAND_OBJ_INT; + + if (val < 0) { + val = 0; + } + + ftdmchan->pre_buffer_size = val * 8; + + ftdm_mutex_lock(ftdmchan->pre_buffer_mutex); + if (!ftdmchan->pre_buffer_size) { + ftdm_buffer_destroy(&ftdmchan->pre_buffer); + } else if (!ftdmchan->pre_buffer) { + ftdm_buffer_create(&ftdmchan->pre_buffer, 1024, ftdmchan->pre_buffer_size, 0); + } + ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex); + + GOTO_STATUS(done, FTDM_SUCCESS); + + } + break; + case FTDM_COMMAND_GET_DTMF_ON_PERIOD: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_GENERATE)) { + FTDM_COMMAND_OBJ_INT = ftdmchan->dtmf_on; + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + case FTDM_COMMAND_GET_DTMF_OFF_PERIOD: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_GENERATE)) { + FTDM_COMMAND_OBJ_INT = ftdmchan->dtmf_on; + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + case FTDM_COMMAND_SET_DTMF_ON_PERIOD: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_GENERATE)) { + int val = FTDM_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + ftdmchan->dtmf_on = val; + GOTO_STATUS(done, FTDM_SUCCESS); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, FTDM_FAIL); + } + } + } + break; + case FTDM_COMMAND_SET_DTMF_OFF_PERIOD: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_GENERATE)) { + int val = FTDM_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + ftdmchan->dtmf_off = val; + GOTO_STATUS(done, FTDM_SUCCESS); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, FTDM_FAIL); + } + } + } + break; + case FTDM_COMMAND_SEND_DTMF: + { + if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_GENERATE)) { + char *digits = FTDM_COMMAND_OBJ_CHAR_P; + + if ((status = ftdmchan_activate_dtmf_buffer(ftdmchan)) != FTDM_SUCCESS) { + GOTO_STATUS(done, status); + } + + ftdm_buffer_write(ftdmchan->gen_dtmf_buffer, digits, strlen(digits)); + + GOTO_STATUS(done, FTDM_SUCCESS); + } + } + break; + + case FTDM_COMMAND_DISABLE_ECHOCANCEL: + { + ftdm_mutex_lock(ftdmchan->pre_buffer_mutex); + ftdm_buffer_destroy(&ftdmchan->pre_buffer); + ftdmchan->pre_buffer_size = 0; + ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex); + } + break; + + /* FIXME: validate user gain values */ + case FTDM_COMMAND_SET_RX_GAIN: + { + ftdmchan->rxgain = FTDM_COMMAND_OBJ_FLOAT; + reset_gain_table(ftdmchan->rxgain_table, ftdmchan->rxgain, ftdmchan->native_codec); + if (ftdmchan->rxgain == 0.0) { + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN); + } else { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN); + } + } + break; + case FTDM_COMMAND_GET_RX_GAIN: + { + FTDM_COMMAND_OBJ_FLOAT = ftdmchan->rxgain; + } + break; + case FTDM_COMMAND_SET_TX_GAIN: + { + ftdmchan->txgain = FTDM_COMMAND_OBJ_FLOAT; + reset_gain_table(ftdmchan->txgain_table, ftdmchan->txgain, ftdmchan->native_codec); + if (ftdmchan->txgain == 0.0) { + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_USE_TX_GAIN); + } else { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_USE_TX_GAIN); + } + } + break; + case FTDM_COMMAND_GET_TX_GAIN: + { + FTDM_COMMAND_OBJ_FLOAT = ftdmchan->txgain; + } + break; + default: + break; + } + + if (!ftdmchan->fio->command) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented"); + ftdm_log(FTDM_LOG_ERROR, "no command function defined by the I/O freetdm module!\n"); + GOTO_STATUS(done, FTDM_FAIL); + } + + status = ftdmchan->fio->command(ftdmchan, command, obj); + + if (status == FTDM_NOTIMPL) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "I/O command %d not implemented in backend", command); + ftdm_log(FTDM_LOG_ERROR, "I/O backend does not support command %d!\n", command); + } +done: + ftdm_mutex_unlock(ftdmchan->mutex); + return status; + +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_wait(ftdm_channel_t *ftdmchan, ftdm_wait_flag_t *flags, int32_t to) +{ + assert(ftdmchan != NULL); + assert(ftdmchan->fio != NULL); + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open"); + return FTDM_FAIL; + } + + if (!ftdmchan->fio->wait) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented"); + return FTDM_FAIL; + } + + return ftdmchan->fio->wait(ftdmchan, flags, to); + +} + +/*******************************/ +FIO_CODEC_FUNCTION(fio_slin2ulaw) +{ + int16_t sln_buf[512] = {0}, *sln = sln_buf; + uint8_t *lp = data; + uint32_t i; + ftdm_size_t len = *datalen; + + if (max > len) { + max = len; + } + + memcpy(sln, data, max); + + for(i = 0; i < max; i++) { + *lp++ = linear_to_ulaw(*sln++); + } + + *datalen = max / 2; + + return FTDM_SUCCESS; + +} + + +FIO_CODEC_FUNCTION(fio_ulaw2slin) +{ + int16_t *sln = data; + uint8_t law[1024] = {0}, *lp = law; + uint32_t i; + ftdm_size_t len = *datalen; + + if (max > len) { + max = len; + } + + memcpy(law, data, max); + + for(i = 0; i < max; i++) { + *sln++ = ulaw_to_linear(*lp++); + } + + *datalen = max * 2; + + return FTDM_SUCCESS; +} + +FIO_CODEC_FUNCTION(fio_slin2alaw) +{ + int16_t sln_buf[512] = {0}, *sln = sln_buf; + uint8_t *lp = data; + uint32_t i; + ftdm_size_t len = *datalen; + + if (max > len) { + max = len; + } + + memcpy(sln, data, max); + + for(i = 0; i < max; i++) { + *lp++ = linear_to_alaw(*sln++); + } + + *datalen = max / 2; + + return FTDM_SUCCESS; + +} + + +FIO_CODEC_FUNCTION(fio_alaw2slin) +{ + int16_t *sln = data; + uint8_t law[1024] = {0}, *lp = law; + uint32_t i; + ftdm_size_t len = *datalen; + + if (max > len) { + max = len; + } + + memcpy(law, data, max); + + for(i = 0; i < max; i++) { + *sln++ = alaw_to_linear(*lp++); + } + + *datalen = max * 2; + + return FTDM_SUCCESS; +} + +FIO_CODEC_FUNCTION(fio_ulaw2alaw) +{ + ftdm_size_t len = *datalen; + uint32_t i; + uint8_t *lp = data; + + if (max > len) { + max = len; + } + + for(i = 0; i < max; i++) { + *lp = ulaw_to_alaw(*lp); + lp++; + } + + return FTDM_SUCCESS; +} + +FIO_CODEC_FUNCTION(fio_alaw2ulaw) +{ + ftdm_size_t len = *datalen; + uint32_t i; + uint8_t *lp = data; + + if (max > len) { + max = len; + } + + for(i = 0; i < max; i++) { + *lp = alaw_to_ulaw(*lp); + lp++; + } + + return FTDM_SUCCESS; +} + +/******************************/ + +FT_DECLARE(void) ftdm_channel_clear_detected_tones(ftdm_channel_t *ftdmchan) +{ + uint32_t i; + + memset(ftdmchan->detected_tones, 0, sizeof(ftdmchan->detected_tones[0]) * FTDM_TONEMAP_INVALID); + + for (i = 1; i < FTDM_TONEMAP_INVALID; i++) { + ftdmchan->span->tone_finder[i].tone_count = 0; + } +} + +FT_DECLARE(void) ftdm_channel_clear_needed_tones(ftdm_channel_t *ftdmchan) +{ + memset(ftdmchan->needed_tones, 0, sizeof(ftdmchan->needed_tones[0]) * FTDM_TONEMAP_INVALID); +} + +FT_DECLARE(ftdm_size_t) ftdm_channel_dequeue_dtmf(ftdm_channel_t *ftdmchan, char *dtmf, ftdm_size_t len) +{ + ftdm_size_t bytes = 0; + + assert(ftdmchan != NULL); + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) { + return FTDM_FAIL; + } + + if (ftdmchan->digit_buffer && ftdm_buffer_inuse(ftdmchan->digit_buffer)) { + ftdm_mutex_lock(ftdmchan->mutex); + if ((bytes = ftdm_buffer_read(ftdmchan->digit_buffer, dtmf, len)) > 0) { + *(dtmf + bytes) = '\0'; + } + ftdm_mutex_unlock(ftdmchan->mutex); + } + + return bytes; +} + +FT_DECLARE(void) ftdm_channel_flush_dtmf(ftdm_channel_t *ftdmchan) +{ + if (ftdmchan->digit_buffer && ftdm_buffer_inuse(ftdmchan->digit_buffer)) { + ftdm_mutex_lock(ftdmchan->mutex); + ftdm_buffer_zero(ftdmchan->digit_buffer); + ftdm_mutex_unlock(ftdmchan->mutex); + } +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, const char *dtmf) +{ + ftdm_status_t status; + register ftdm_size_t len, inuse; + ftdm_size_t wr = 0; + const char *p; + + assert(ftdmchan != NULL); + + if (ftdmchan->pre_buffer) { + ftdm_buffer_zero(ftdmchan->pre_buffer); + } + + ftdm_mutex_lock(ftdmchan->mutex); + + inuse = ftdm_buffer_inuse(ftdmchan->digit_buffer); + len = strlen(dtmf); + + if (len + inuse > ftdm_buffer_len(ftdmchan->digit_buffer)) { + ftdm_buffer_toss(ftdmchan->digit_buffer, strlen(dtmf)); + } + + if (ftdmchan->span->dtmf_hangup_len) { + for (p = dtmf; ftdm_is_dtmf(*p); p++) { + memmove (ftdmchan->dtmf_hangup_buf, ftdmchan->dtmf_hangup_buf + 1, ftdmchan->span->dtmf_hangup_len - 1); + ftdmchan->dtmf_hangup_buf[ftdmchan->span->dtmf_hangup_len - 1] = *p; + if (!strcmp(ftdmchan->dtmf_hangup_buf, ftdmchan->span->dtmf_hangup)) { + ftdm_log(FTDM_LOG_DEBUG, "DTMF hangup detected.\n"); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + break; + } + } + } + + p = dtmf; + while (wr < len && p) { + if (ftdm_is_dtmf(*p)) { + wr++; + } else { + break; + } + p++; + } + + status = ftdm_buffer_write(ftdmchan->digit_buffer, dtmf, wr) ? FTDM_SUCCESS : FTDM_FAIL; + ftdm_mutex_unlock(ftdmchan->mutex); + + return status; +} + + +static ftdm_status_t handle_dtmf(ftdm_channel_t *ftdmchan, ftdm_size_t datalen) +{ + ftdm_buffer_t *buffer = NULL; + ftdm_size_t dblen = 0; + int wrote = 0; + + if (ftdmchan->gen_dtmf_buffer && (dblen = ftdm_buffer_inuse(ftdmchan->gen_dtmf_buffer))) { + char digits[128] = ""; + char *cur; + int x = 0; + + if (dblen > sizeof(digits) - 1) { + dblen = sizeof(digits) - 1; + } + + if (ftdm_buffer_read(ftdmchan->gen_dtmf_buffer, digits, dblen) && !ftdm_strlen_zero_buf(digits)) { + ftdm_log(FTDM_LOG_DEBUG, "%d:%d GENERATE DTMF [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, digits); + + cur = digits; + + if (*cur == 'F') { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLASH, NULL); + cur++; + } + + for (; *cur; cur++) { + if ((wrote = teletone_mux_tones(&ftdmchan->tone_session, &ftdmchan->tone_session.TONES[(int)*cur]))) { + ftdm_buffer_write(ftdmchan->dtmf_buffer, ftdmchan->tone_session.buffer, wrote * 2); + x++; + } else { + ftdm_log(FTDM_LOG_ERROR, "%d:%d Problem Adding DTMF SEQ [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, digits); + return FTDM_FAIL; + } + } + + if (x) { + ftdmchan->skip_read_frames = (wrote / (ftdmchan->effective_interval * 8)) + 4; + } + } + } + + + if (!ftdmchan->buffer_delay || --ftdmchan->buffer_delay == 0) { + if (ftdmchan->dtmf_buffer && (dblen = ftdm_buffer_inuse(ftdmchan->dtmf_buffer))) { + buffer = ftdmchan->dtmf_buffer; + } else if (ftdmchan->fsk_buffer && (dblen = ftdm_buffer_inuse(ftdmchan->fsk_buffer))) { + buffer = ftdmchan->fsk_buffer; + } + } + + if (buffer) { + ftdm_size_t dlen = datalen; + uint8_t auxbuf[1024]; + ftdm_size_t len, br, max = sizeof(auxbuf); + + if (ftdmchan->native_codec != FTDM_CODEC_SLIN) { + dlen *= 2; + } + + len = dblen > dlen ? dlen : dblen; + + br = ftdm_buffer_read(buffer, auxbuf, len); + if (br < dlen) { + memset(auxbuf + br, 0, dlen - br); + } + + if (ftdmchan->native_codec != FTDM_CODEC_SLIN) { + if (ftdmchan->native_codec == FTDM_CODEC_ULAW) { + fio_slin2ulaw(auxbuf, max, &dlen); + } else if (ftdmchan->native_codec == FTDM_CODEC_ALAW) { + fio_slin2alaw(auxbuf, max, &dlen); + } + } + + return ftdmchan->fio->write(ftdmchan, auxbuf, &dlen); + } + + return FTDM_SUCCESS; + +} + + +FT_DECLARE(void) ftdm_generate_sln_silence(int16_t *data, uint32_t samples, uint32_t divisor) +{ + int16_t x; + uint32_t i; + int sum_rnd = 0; + int16_t rnd2 = (int16_t) ftdm_current_time_in_ms() * (int16_t) (intptr_t) data; + + assert(divisor); + + for (i = 0; i < samples; i++, sum_rnd = 0) { + for (x = 0; x < 6; x++) { + rnd2 = rnd2 * 31821U + 13849U; + sum_rnd += rnd2 ; + } + //switch_normalize_to_16bit(sum_rnd); + *data = (int16_t) ((int16_t) sum_rnd / (int) divisor); + + data++; + } +} + + + +FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen) +{ + ftdm_status_t status = FTDM_FAIL; + fio_codec_t codec_func = NULL; + ftdm_size_t max = *datalen; + unsigned i = 0; + + ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "ftdmchan is null\n"); + ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No I/O module attached to ftdmchan\n"); + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open"); + return FTDM_FAIL; + } + + if (!ftdmchan->fio->read) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented"); + return FTDM_FAIL; + } + + status = ftdmchan->fio->read(ftdmchan, data, datalen); + if (ftdmchan->fds[0] > -1) { + int dlen = (int) *datalen; + if (write(ftdmchan->fds[0], data, dlen) != dlen) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "file write error!"); + return FTDM_FAIL; + } + } + + if (status == FTDM_SUCCESS) { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN) + && (ftdmchan->native_codec == FTDM_CODEC_ALAW || ftdmchan->native_codec == FTDM_CODEC_ULAW)) { + unsigned char *rdata = data; + for (i = 0; i < *datalen; i++) { + rdata[i] = ftdmchan->rxgain_table[rdata[i]]; + } + } + handle_dtmf(ftdmchan, *datalen); + } + + if (status == FTDM_SUCCESS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_TRANSCODE) && ftdmchan->effective_codec != ftdmchan->native_codec) { + if (ftdmchan->native_codec == FTDM_CODEC_ULAW && ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + codec_func = fio_ulaw2slin; + } else if (ftdmchan->native_codec == FTDM_CODEC_ULAW && ftdmchan->effective_codec == FTDM_CODEC_ALAW) { + codec_func = fio_ulaw2alaw; + } else if (ftdmchan->native_codec == FTDM_CODEC_ALAW && ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + codec_func = fio_alaw2slin; + } else if (ftdmchan->native_codec == FTDM_CODEC_ALAW && ftdmchan->effective_codec == FTDM_CODEC_ULAW) { + codec_func = fio_alaw2ulaw; + } + + if (codec_func) { + status = codec_func(data, max, datalen); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!"); + status = FTDM_FAIL; + } + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT) || + ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT)) { + uint8_t sln_buf[1024] = {0}; + int16_t *sln; + ftdm_size_t slen = 0; + char digit_str[80] = ""; + + if (ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + sln = data; + slen = *datalen / 2; + } else { + ftdm_size_t len = *datalen; + uint32_t i; + uint8_t *lp = data; + + slen = sizeof(sln_buf) / 2; + if (len > slen) { + len = slen; + } + + sln = (int16_t *) sln_buf; + for(i = 0; i < len; i++) { + if (ftdmchan->effective_codec == FTDM_CODEC_ULAW) { + *sln++ = ulaw_to_linear(*lp++); + } else if (ftdmchan->effective_codec == FTDM_CODEC_ALAW) { + *sln++ = alaw_to_linear(*lp++); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!"); + return FTDM_FAIL; + } + } + sln = (int16_t *) sln_buf; + slen = len; + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT)) { + if (ftdm_fsk_demod_feed(&ftdmchan->fsk, sln, slen) != FTDM_SUCCESS) { + ftdm_size_t type, mlen; + char str[128], *sp; + + while(ftdm_fsk_data_parse(&ftdmchan->fsk, &type, &sp, &mlen) == FTDM_SUCCESS) { + *(str+mlen) = '\0'; + ftdm_copy_string(str, sp, ++mlen); + ftdm_clean_string(str); + ftdm_log(FTDM_LOG_DEBUG, "FSK: TYPE %s LEN %d VAL [%s]\n", ftdm_mdmf_type2str(type), mlen-1, str); + + switch(type) { + case MDMF_DDN: + case MDMF_PHONE_NUM: + { + if (mlen > sizeof(ftdmchan->caller_data.ani)) { + mlen = sizeof(ftdmchan->caller_data.ani); + } + ftdm_set_string(ftdmchan->caller_data.ani.digits, str); + ftdm_set_string(ftdmchan->caller_data.cid_num.digits, ftdmchan->caller_data.ani.digits); + } + break; + case MDMF_NO_NUM: + { + ftdm_set_string(ftdmchan->caller_data.ani.digits, *str == 'P' ? "private" : "unknown"); + ftdm_set_string(ftdmchan->caller_data.cid_name, ftdmchan->caller_data.ani.digits); + } + break; + case MDMF_PHONE_NAME: + { + if (mlen > sizeof(ftdmchan->caller_data.cid_name)) { + mlen = sizeof(ftdmchan->caller_data.cid_name); + } + ftdm_set_string(ftdmchan->caller_data.cid_name, str); + } + break; + case MDMF_NO_NAME: + { + ftdm_set_string(ftdmchan->caller_data.cid_name, *str == 'P' ? "private" : "unknown"); + } + case MDMF_DATETIME: + { + if (mlen > sizeof(ftdmchan->caller_data.cid_date)) { + mlen = sizeof(ftdmchan->caller_data.cid_date); + } + ftdm_set_string(ftdmchan->caller_data.cid_date, str); + } + break; + } + } + ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_CALLERID_DETECT, NULL); + } + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT) && !ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_PROGRESS)) { + uint32_t i; + + for (i = 1; i < FTDM_TONEMAP_INVALID; i++) { + if (ftdmchan->span->tone_finder[i].tone_count) { + if (ftdmchan->needed_tones[i] && teletone_multi_tone_detect(&ftdmchan->span->tone_finder[i], sln, (int)slen)) { + if (++ftdmchan->detected_tones[i]) { + ftdmchan->needed_tones[i] = 0; + ftdmchan->detected_tones[0]++; + } + } + } + } + } + + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) && !ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) { + teletone_dtmf_detect(&ftdmchan->dtmf_detect, sln, (int)slen); + teletone_dtmf_get(&ftdmchan->dtmf_detect, digit_str, sizeof(digit_str)); + + if(*digit_str) { + fio_event_cb_t event_callback = NULL; + + if (ftdmchan->state == FTDM_CHANNEL_STATE_CALLWAITING && (*digit_str == 'D' || *digit_str == 'A')) { + ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]++; + } else { + ftdm_channel_queue_dtmf(ftdmchan, digit_str); + + if (ftdmchan->span->event_callback) { + event_callback = ftdmchan->span->event_callback; + } else if (ftdmchan->event_callback) { + event_callback = ftdmchan->event_callback; + } + + if (event_callback) { + ftdmchan->event_header.channel = ftdmchan; + ftdmchan->event_header.e_type = FTDM_EVENT_DTMF; + ftdmchan->event_header.data = digit_str; + event_callback(ftdmchan, &ftdmchan->event_header); + ftdmchan->event_header.e_type = FTDM_EVENT_NONE; + ftdmchan->event_header.data = NULL; + } + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF)) { + ftdmchan->skip_read_frames = 20; + } + } + } + } + } + + if (ftdmchan->skip_read_frames > 0 || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MUTE)) { + + ftdm_mutex_lock(ftdmchan->pre_buffer_mutex); + if (ftdmchan->pre_buffer && ftdm_buffer_inuse(ftdmchan->pre_buffer)) { + ftdm_buffer_zero(ftdmchan->pre_buffer); + } + ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex); + + + memset(data, 255, *datalen); + + if (ftdmchan->skip_read_frames > 0) { + ftdmchan->skip_read_frames--; + } + } else { + ftdm_mutex_lock(ftdmchan->pre_buffer_mutex); + if (ftdmchan->pre_buffer_size && ftdmchan->pre_buffer) { + ftdm_buffer_write(ftdmchan->pre_buffer, data, *datalen); + if (ftdm_buffer_inuse(ftdmchan->pre_buffer) >= ftdmchan->pre_buffer_size) { + ftdm_buffer_read(ftdmchan->pre_buffer, data, *datalen); + } else { + memset(data, 255, *datalen); + } + } + ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex); + } + + + return status; +} + + +FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t datasize, ftdm_size_t *datalen) +{ + ftdm_status_t status = FTDM_FAIL; + fio_codec_t codec_func = NULL; + ftdm_size_t max = datasize; + unsigned int i = 0; + + assert(ftdmchan != NULL); + assert(ftdmchan->fio != NULL); + + if (!ftdmchan->buffer_delay && + ((ftdmchan->dtmf_buffer && ftdm_buffer_inuse(ftdmchan->dtmf_buffer)) || + (ftdmchan->fsk_buffer && ftdm_buffer_inuse(ftdmchan->fsk_buffer)))) { + /* read size writing DTMF ATM */ + return FTDM_SUCCESS; + } + + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open"); + return FTDM_FAIL; + } + + if (!ftdmchan->fio->write) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented"); + return FTDM_FAIL; + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_TRANSCODE) && ftdmchan->effective_codec != ftdmchan->native_codec) { + if (ftdmchan->native_codec == FTDM_CODEC_ULAW && ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + codec_func = fio_slin2ulaw; + } else if (ftdmchan->native_codec == FTDM_CODEC_ULAW && ftdmchan->effective_codec == FTDM_CODEC_ALAW) { + codec_func = fio_alaw2ulaw; + } else if (ftdmchan->native_codec == FTDM_CODEC_ALAW && ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + codec_func = fio_slin2alaw; + } else if (ftdmchan->native_codec == FTDM_CODEC_ALAW && ftdmchan->effective_codec == FTDM_CODEC_ULAW) { + codec_func = fio_ulaw2alaw; + } + + if (codec_func) { + status = codec_func(data, max, datalen); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!"); + status = FTDM_FAIL; + } + } + + if (ftdmchan->fds[1] > -1) { + int dlen = (int) *datalen; + if ((write(ftdmchan->fds[1], data, dlen)) != dlen) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "file write error!"); + return FTDM_FAIL; + } + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USE_TX_GAIN) + && (ftdmchan->native_codec == FTDM_CODEC_ALAW || ftdmchan->native_codec == FTDM_CODEC_ULAW)) { + unsigned char *wdata = data; + for (i = 0; i < *datalen; i++) { + wdata[i] = ftdmchan->txgain_table[wdata[i]]; + } + } + status = ftdmchan->fio->write(ftdmchan, data, datalen); + + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan) +{ + if(ftdmchan->variable_hash) { + hashtable_destroy(ftdmchan->variable_hash); + } + ftdmchan->variable_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + + if(!ftdmchan->variable_hash) + return FTDM_FAIL; + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_add_var(ftdm_channel_t *ftdmchan, const char *var_name, const char *value) +{ + char *t_name = 0, *t_val = 0; + + if(!ftdmchan->variable_hash || !var_name || !value) + { + return FTDM_FAIL; + } + + t_name = ftdm_strdup(var_name); + t_val = ftdm_strdup(value); + + if(hashtable_insert(ftdmchan->variable_hash, t_name, t_val, HASHTABLE_FLAG_FREE_KEY | HASHTABLE_FLAG_FREE_VALUE)) { + return FTDM_SUCCESS; + } + return FTDM_FAIL; +} + +FT_DECLARE(const char *) ftdm_channel_get_var(ftdm_channel_t *ftdmchan, const char *var_name) +{ + if(!ftdmchan->variable_hash || !var_name) + { + return NULL; + } + return (const char *) hashtable_search(ftdmchan->variable_hash, (void *)var_name); +} + +static struct { + ftdm_io_interface_t *pika_interface; +} interfaces; + + +FT_DECLARE(char *) ftdm_api_execute(const char *type, const char *cmd) +{ + ftdm_io_interface_t *fio = NULL; + char *dup = NULL, *p; + char *rval = NULL; + + if (type && !cmd) { + dup = ftdm_strdup(type); + if ((p = strchr(dup, ' '))) { + *p++ = '\0'; + cmd = p; + } + + type = dup; + } + + ftdm_mutex_lock(globals.mutex); + if (!(fio = (ftdm_io_interface_t *) hashtable_search(globals.interface_hash, (void *)type))) { + ftdm_load_module_assume(type); + if ((fio = (ftdm_io_interface_t *) hashtable_search(globals.interface_hash, (void *)type))) { + ftdm_log(FTDM_LOG_INFO, "auto-loaded '%s'\n", type); + } + } + ftdm_mutex_unlock(globals.mutex); + + if (fio && fio->api) { + ftdm_stream_handle_t stream = { 0 }; + ftdm_status_t status; + FTDM_STANDARD_STREAM(stream); + status = fio->api(&stream, cmd); + + if (status != FTDM_SUCCESS) { + ftdm_safe_free(stream.data); + } else { + rval = (char *) stream.data; + } + } + + ftdm_safe_free(dup); + + return rval; +} + + +static ftdm_status_t ftdm_group_add_channels(const char* name, ftdm_span_t* span, int currindex); +static ftdm_status_t load_config(void) +{ + char cfg_name[] = "freetdm.conf"; + ftdm_config_t cfg; + char *var, *val; + int catno = -1; + int currindex = 0; + ftdm_span_t *span = NULL; + unsigned configured = 0, d = 0; + char name[80] = ""; + char number[25] = ""; + char group_name[80] = "default"; + ftdm_io_interface_t *fio = NULL; + ftdm_analog_start_type_t tmp; + ftdm_size_t len = 0; + + if (!ftdm_config_open_file(&cfg, cfg_name)) { + return FTDM_FAIL; + } + + while (ftdm_config_next_pair(&cfg, &var, &val)) { + if (*cfg.category == '#') { + if (cfg.catno != catno) { + ftdm_log(FTDM_LOG_DEBUG, "Skipping %s\n", cfg.category); + catno = cfg.catno; + } + } else if (!strncasecmp(cfg.category, "span", 4)) { + if (cfg.catno != catno) { + char *type = cfg.category + 4; + char *name; + + if (*type == ' ') { + type++; + } + + ftdm_log(FTDM_LOG_DEBUG, "found config for span\n"); + catno = cfg.catno; + + if (ftdm_strlen_zero(type)) { + ftdm_log(FTDM_LOG_CRIT, "failure creating span, no type specified.\n"); + span = NULL; + continue; + } + + if ((name = strchr(type, ' '))) { + *name++ = '\0'; + } + + ftdm_mutex_lock(globals.mutex); + if (!(fio = (ftdm_io_interface_t *) hashtable_search(globals.interface_hash, type))) { + ftdm_load_module_assume(type); + if ((fio = (ftdm_io_interface_t *) hashtable_search(globals.interface_hash, type))) { + ftdm_log(FTDM_LOG_INFO, "auto-loaded '%s'\n", type); + } + } + ftdm_mutex_unlock(globals.mutex); + + if (!fio) { + ftdm_log(FTDM_LOG_CRIT, "failure creating span, no such type '%s'\n", type); + span = NULL; + continue; + } + + if (!fio->configure_span) { + ftdm_log(FTDM_LOG_CRIT, "failure creating span, no configure_span method for '%s'\n", type); + span = NULL; + continue; + } + + if (ftdm_span_create(fio, &span, name) == FTDM_SUCCESS) { + span->type = ftdm_strdup(type); + d = 0; + + ftdm_log(FTDM_LOG_DEBUG, "created span %d (%s) of type %s\n", span->span_id, span->name, type); + + } else { + ftdm_log(FTDM_LOG_CRIT, "failure creating span of type %s\n", type); + span = NULL; + continue; + } + } + + if (!span) { + continue; + } + + ftdm_log(FTDM_LOG_DEBUG, "span %d [%s]=[%s]\n", span->span_id, var, val); + + if (!strcasecmp(var, "trunk_type")) { + span->trunk_type = ftdm_str2ftdm_trunk_type(val); + ftdm_log(FTDM_LOG_DEBUG, "setting trunk type to '%s'\n", ftdm_trunk_type2str(span->trunk_type)); + } else if (!strcasecmp(var, "name")) { + if (!strcasecmp(val, "undef")) { + *name = '\0'; + } else { + ftdm_copy_string(name, val, sizeof(name)); + } + } else if (!strcasecmp(var, "number")) { + if (!strcasecmp(val, "undef")) { + *number = '\0'; + } else { + ftdm_copy_string(number, val, sizeof(number)); + } + } else if (!strcasecmp(var, "analog-start-type")) { + if (span->trunk_type == FTDM_TRUNK_FXS || span->trunk_type == FTDM_TRUNK_FXO || span->trunk_type == FTDM_TRUNK_EM) { + if ((tmp = ftdm_str2ftdm_analog_start_type(val)) != FTDM_ANALOG_START_NA) { + span->start_type = tmp; + ftdm_log(FTDM_LOG_DEBUG, "changing start type to '%s'\n", ftdm_analog_start_type2str(span->start_type)); + } + } else { + ftdm_log(FTDM_LOG_ERROR, "This option is only valid on analog trunks!\n"); + } + } else if (!strcasecmp(var, "fxo-channel")) { + if (span->trunk_type == FTDM_TRUNK_NONE) { + span->trunk_type = FTDM_TRUNK_FXO; + ftdm_log(FTDM_LOG_DEBUG, "setting trunk type to '%s' start(%s)\n", ftdm_trunk_type2str(span->trunk_type), + ftdm_analog_start_type2str(span->start_type)); + } + if (span->trunk_type == FTDM_TRUNK_FXO) { + currindex = span->chan_count; + configured += fio->configure_span(span, val, FTDM_CHAN_TYPE_FXO, name, number); + ftdm_group_add_channels(group_name, span, currindex); + } else { + ftdm_log(FTDM_LOG_WARNING, "Cannot add FXO channels to an FXS trunk!\n"); + } + } else if (!strcasecmp(var, "fxs-channel")) { + if (span->trunk_type == FTDM_TRUNK_NONE) { + span->trunk_type = FTDM_TRUNK_FXS; + ftdm_log(FTDM_LOG_DEBUG, "setting trunk type to '%s' start(%s)\n", ftdm_trunk_type2str(span->trunk_type), + ftdm_analog_start_type2str(span->start_type)); + } + if (span->trunk_type == FTDM_TRUNK_FXS) { + currindex = span->chan_count; + configured += fio->configure_span(span, val, FTDM_CHAN_TYPE_FXS, name, number); + ftdm_group_add_channels(group_name, span, currindex); + } else { + ftdm_log(FTDM_LOG_WARNING, "Cannot add FXS channels to an FXO trunk!\n"); + } + } else if (!strcasecmp(var, "em-channel")) { + if (span->trunk_type == FTDM_TRUNK_NONE) { + span->trunk_type = FTDM_TRUNK_EM; + ftdm_log(FTDM_LOG_DEBUG, "setting trunk type to '%s' start(%s)\n", ftdm_trunk_type2str(span->trunk_type), + ftdm_analog_start_type2str(span->start_type)); + } + if (span->trunk_type == FTDM_TRUNK_EM) { + currindex = span->chan_count; + configured += fio->configure_span(span, val, FTDM_CHAN_TYPE_EM, name, number); + ftdm_group_add_channels(group_name, span, currindex); + } else { + ftdm_log(FTDM_LOG_WARNING, "Cannot add EM channels to a non-EM trunk!\n"); + } + } else if (!strcasecmp(var, "b-channel")) { + currindex = span->chan_count; + configured += fio->configure_span(span, val, FTDM_CHAN_TYPE_B, name, number); + ftdm_group_add_channels(group_name, span, currindex); + } else if (!strcasecmp(var, "d-channel")) { + if (d) { + ftdm_log(FTDM_LOG_WARNING, "ignoring extra d-channel\n"); + } else { + ftdm_chan_type_t qtype; + if (!strncasecmp(val, "lapd:", 5)) { + qtype = FTDM_CHAN_TYPE_DQ931; + val += 5; + } else { + qtype = FTDM_CHAN_TYPE_DQ921; + } + configured += fio->configure_span(span, val, qtype, name, number); + d++; + } + } else if (!strcasecmp(var, "cas-channel")) { + currindex = span->chan_count; + configured += fio->configure_span(span, val, FTDM_CHAN_TYPE_CAS, name, number); + ftdm_group_add_channels(group_name, span, currindex); + } else if (!strcasecmp(var, "dtmf_hangup")) { + span->dtmf_hangup = ftdm_strdup(val); + span->dtmf_hangup_len = strlen(val); + } else if (!strcasecmp(var, "group")) { + len = strlen(val); + if (len >= sizeof(group_name)) { + len = sizeof(group_name) - 1; + ftdm_log(FTDM_LOG_WARNING, "Truncating group name %s to %zd length\n", val, len); + } + memcpy(group_name, val, len); + group_name[len] = '\0'; + } else { + ftdm_log(FTDM_LOG_ERROR, "unknown span variable '%s'\n", var); + } + } else { + ftdm_log(FTDM_LOG_ERROR, "unknown param [%s] '%s' / '%s'\n", cfg.category, var, val); + } + } + ftdm_config_close_file(&cfg); + + ftdm_log(FTDM_LOG_INFO, "Configured %u channel(s)\n", configured); + + return configured ? FTDM_SUCCESS : FTDM_FAIL; +} + +static ftdm_status_t process_module_config(ftdm_io_interface_t *fio) +{ + ftdm_config_t cfg; + char *var, *val; + char filename[256] = ""; + + ftdm_assert_return(fio != NULL, FTDM_FAIL, "fio argument is null\n"); + + snprintf(filename, sizeof(filename), "%s.conf", fio->name); + + if (!fio->configure) { + ftdm_log(FTDM_LOG_DEBUG, "Module %s does not support configuration.\n", fio->name); + return FTDM_FAIL; + } + + if (!ftdm_config_open_file(&cfg, filename)) { + ftdm_log(FTDM_LOG_ERROR, "Cannot open %s\n", filename); + return FTDM_FAIL; + } + + while (ftdm_config_next_pair(&cfg, &var, &val)) { + fio->configure(cfg.category, var, val, cfg.lineno); + } + + ftdm_config_close_file(&cfg); + + return FTDM_SUCCESS; +} + +FT_DECLARE(char *) ftdm_build_dso_path(const char *name, char *path, ftdm_size_t len) +{ +#ifdef WIN32 + const char *ext = ".dll"; + //const char *EXT = ".DLL"; +#define FTDM_MOD_DIR "." //todo +#elif defined (MACOSX) || defined (DARWIN) + const char *ext = ".dylib"; + //const char *EXT = ".DYLIB"; +#else + const char *ext = ".so"; + //const char *EXT = ".SO"; +#endif + if (*name == *FTDM_PATH_SEPARATOR) { + snprintf(path, len, "%s%s", name, ext); + } else { + snprintf(path, len, "%s%s%s%s", FTDM_MOD_DIR, FTDM_PATH_SEPARATOR, name, ext); + } + return path; +} + +FT_DECLARE(ftdm_status_t) ftdm_global_add_io_interface(ftdm_io_interface_t *interface1) +{ + ftdm_status_t ret = FTDM_SUCCESS; + ftdm_mutex_lock(globals.mutex); + if (hashtable_search(globals.interface_hash, (void *)interface1->name)) { + ftdm_log(FTDM_LOG_ERROR, "Interface %s already loaded!\n", interface1->name); + } else { + hashtable_insert(globals.interface_hash, (void *)interface1->name, interface1, HASHTABLE_FLAG_NONE); + } + ftdm_mutex_unlock(globals.mutex); + return ret; +} + +FT_DECLARE(int) ftdm_load_module(const char *name) +{ + ftdm_dso_lib_t lib; + int count = 0, x = 0; + char path[128] = ""; + char *err; + ftdm_module_t *mod; + + ftdm_build_dso_path(name, path, sizeof(path)); + + if (!(lib = ftdm_dso_open(path, &err))) { + ftdm_log(FTDM_LOG_ERROR, "Error loading %s [%s]\n", path, err); + ftdm_safe_free(err); + return 0; + } + + if (!(mod = (ftdm_module_t *) ftdm_dso_func_sym(lib, "ftdm_module", &err))) { + ftdm_log(FTDM_LOG_ERROR, "Error loading %s [%s]\n", path, err); + ftdm_safe_free(err); + return 0; + } + + if (mod->io_load) { + ftdm_io_interface_t *interface1 = NULL; /* name conflict w/windows here */ + + if (mod->io_load(&interface1) != FTDM_SUCCESS || !interface1 || !interface1->name) { + ftdm_log(FTDM_LOG_ERROR, "Error loading %s\n", path); + } else { + ftdm_log(FTDM_LOG_INFO, "Loading IO from %s [%s]\n", path, interface1->name); + if (ftdm_global_add_io_interface(interface1) == FTDM_SUCCESS) { + process_module_config(interface1); + x++; + } + } + } + + if (mod->sig_load) { + if (mod->sig_load() != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error loading %s\n", path); + } else { + ftdm_log(FTDM_LOG_INFO, "Loading SIG from %s\n", path); + x++; + } + } + + if (x) { + char *p; + mod->lib = lib; + ftdm_set_string(mod->path, path); + if (mod->name[0] == '\0') { + if (!(p = strrchr(path, *FTDM_PATH_SEPARATOR))) { + p = path; + } + ftdm_set_string(mod->name, p); + } + + ftdm_mutex_lock(globals.mutex); + if (hashtable_search(globals.module_hash, (void *)mod->name)) { + ftdm_log(FTDM_LOG_ERROR, "Module %s already loaded!\n", mod->name); + ftdm_dso_destroy(&lib); + } else { + hashtable_insert(globals.module_hash, (void *)mod->name, mod, HASHTABLE_FLAG_NONE); + count++; + } + ftdm_mutex_unlock(globals.mutex); + } else { + ftdm_log(FTDM_LOG_ERROR, "Unloading %s\n", path); + ftdm_dso_destroy(&lib); + } + + return count; +} + +FT_DECLARE(int) ftdm_load_module_assume(const char *name) +{ + char buf[256] = ""; + + snprintf(buf, sizeof(buf), "ftmod_%s", name); + return ftdm_load_module(buf); +} + +FT_DECLARE(int) ftdm_load_modules(void) +{ + char cfg_name[] = "modules.conf"; + ftdm_config_t cfg; + char *var, *val; + int count = 0; + + if (!ftdm_config_open_file(&cfg, cfg_name)) { + return FTDM_FAIL; + } + + while (ftdm_config_next_pair(&cfg, &var, &val)) { + if (!strcasecmp(cfg.category, "modules")) { + if (!strcasecmp(var, "load")) { + count += ftdm_load_module(val); + } + } + } + + return count; +} + +FT_DECLARE(ftdm_status_t) ftdm_unload_modules(void) +{ + ftdm_hash_iterator_t *i = NULL; + ftdm_dso_lib_t lib = NULL; + char modpath[255] = { 0 }; + + /* stop signaling interfaces first as signaling depends on I/O and not the other way around */ + for (i = hashtable_first(globals.module_hash); i; i = hashtable_next(i)) { + const void *key = NULL; + void *val = NULL; + ftdm_module_t *mod = NULL; + + hashtable_this(i, &key, NULL, &val); + + if (!key || !val) { + continue; + } + + mod = (ftdm_module_t *) val; + + if (!mod->sig_unload) { + continue; + } + + ftdm_log(FTDM_LOG_INFO, "Unloading signaling interface %s\n", mod->name); + + if (mod->sig_unload() != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error unloading signaling interface %s\n", mod->name); + continue; + } + + ftdm_log(FTDM_LOG_INFO, "Unloaded signaling interface %s\n", mod->name); + } + + /* Now go ahead with I/O interfaces */ + for (i = hashtable_first(globals.module_hash); i; i = hashtable_next(i)) { + const void *key = NULL; + void *val = NULL; + ftdm_module_t *mod = NULL; + + hashtable_this(i, &key, NULL, &val); + + if (!key || !val) { + continue; + } + + mod = (ftdm_module_t *) val; + + if (!mod->io_unload) { + continue; + } + + ftdm_log(FTDM_LOG_INFO, "Unloading I/O interface %s\n", mod->name); + + if (mod->io_unload() != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error unloading I/O interface %s\n", mod->name); + continue; + } + + ftdm_log(FTDM_LOG_INFO, "Unloaded I/O interface %s\n", mod->name); + } + + /* Now unload the actual shared object/dll */ + for (i = hashtable_first(globals.module_hash); i; i = hashtable_next(i)) { + ftdm_module_t *mod = NULL; + const void *key = NULL; + void *val = NULL; + + hashtable_this(i, &key, NULL, &val); + + if (!key || !val) { + continue; + } + + mod = (ftdm_module_t *) val; + + lib = mod->lib; + snprintf(modpath, sizeof(modpath), "%s", mod->path); + ftdm_log(FTDM_LOG_INFO, "Unloading module %s\n", modpath); + ftdm_dso_destroy(&lib); + ftdm_log(FTDM_LOG_INFO, "Unloaded module %s\n", modpath); + } + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_configure_span(const char *type, ftdm_span_t *span, fio_signal_cb_t sig_cb, ...) +{ + ftdm_module_t *mod = (ftdm_module_t *) hashtable_search(globals.module_hash, (void *)type); + ftdm_status_t status = FTDM_FAIL; + + if (!mod) { + ftdm_load_module_assume(type); + if ((mod = (ftdm_module_t *) hashtable_search(globals.module_hash, (void *)type))) { + ftdm_log(FTDM_LOG_INFO, "auto-loaded '%s'\n", type); + } + } + + if (mod && mod->sig_configure) { + va_list ap; + va_start(ap, sig_cb); + status = mod->sig_configure(span, sig_cb, ap); + va_end(ap); + } else { + ftdm_log(FTDM_LOG_ERROR, "can't find '%s'\n", type); + status = FTDM_FAIL; + } + + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_configure_span_signaling(const char *type, ftdm_span_t *span, fio_signal_cb_t sig_cb, ftdm_conf_parameter_t *parameters) +{ + ftdm_module_t *mod = (ftdm_module_t *) hashtable_search(globals.module_hash, (void *)type); + ftdm_status_t status = FTDM_FAIL; + + ftdm_assert_return(type != NULL, FTDM_FAIL, "No signaling type"); + ftdm_assert_return(span != NULL, FTDM_FAIL, "No span"); + ftdm_assert_return(sig_cb != NULL, FTDM_FAIL, "No signaling callback"); + ftdm_assert_return(parameters != NULL, FTDM_FAIL, "No parameters"); + + if (!mod) { + ftdm_load_module_assume(type); + if ((mod = (ftdm_module_t *) hashtable_search(globals.module_hash, (void *)type))) { + ftdm_log(FTDM_LOG_INFO, "auto-loaded '%s'\n", type); + } + } + + if (!mod) { + ftdm_log(FTDM_LOG_ERROR, "Failed to load module type: %s\n", type); + return FTDM_FAIL; + } + + if (mod->configure_span_signaling) { + status = mod->configure_span_signaling(span, sig_cb, parameters); + } else { + ftdm_log(FTDM_LOG_ERROR, "Module %s did not implement the signaling configuration method\n", type); + } + + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_span_start(ftdm_span_t *span) +{ + if (span->start) { + return span->start(span); + } + + return FTDM_FAIL; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_add_to_group(const char* name, ftdm_channel_t* ftdmchan) +{ + unsigned int i; + ftdm_group_t* group = NULL; + + ftdm_mutex_lock(globals.group_mutex); + + ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "Cannot add a null channel to a group\n"); + + if (ftdm_group_find_by_name(name, &group) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_DEBUG, "Creating new group:%s\n", name); + ftdm_group_create(&group, name); + } + + /*verify that group does not already include this channel first */ + for(i = 0; i < group->chan_count; i++) { + if (group->channels[i]->physical_span_id == ftdmchan->physical_span_id && + group->channels[i]->physical_chan_id == ftdmchan->physical_chan_id) { + + ftdm_mutex_unlock(globals.group_mutex); + ftdm_log(FTDM_LOG_DEBUG, "Channel %d:%d is already added to group %s\n", + group->channels[i]->physical_span_id, + group->channels[i]->physical_chan_id, + name); + return FTDM_SUCCESS; + } + } + + if (group->chan_count >= FTDM_MAX_CHANNELS_GROUP) { + ftdm_log(FTDM_LOG_ERROR, "Max number of channels exceeded (max:%d)\n", FTDM_MAX_CHANNELS_GROUP); + ftdm_mutex_unlock(globals.group_mutex); + return FTDM_FAIL; + } + + group->channels[group->chan_count++] = ftdmchan; + ftdm_mutex_unlock(globals.group_mutex); + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_remove_from_group(ftdm_group_t* group, ftdm_channel_t* ftdmchan) +{ + unsigned int i, j; + //Need to test this function + ftdm_mutex_lock(globals.group_mutex); + + for (i=0; i < group->chan_count; i++) { + if (group->channels[i]->physical_span_id == ftdmchan->physical_span_id && + group->channels[i]->physical_chan_id == ftdmchan->physical_chan_id) { + + j=i; + while(j < group->chan_count-1) { + group->channels[j] = group->channels[j+1]; + j++; + } + group->channels[group->chan_count--] = NULL; + if (group->chan_count <=0) { + /* Delete group if it is empty */ + hashtable_remove(globals.group_hash, (void *)group->name); + } + ftdm_mutex_unlock(globals.group_mutex); + return FTDM_SUCCESS; + } + } + + ftdm_mutex_unlock(globals.group_mutex); + //Group does not contain this channel + return FTDM_FAIL; +} + +static ftdm_status_t ftdm_group_add_channels(const char* name, ftdm_span_t* span, int currindex) +{ + unsigned chan_index = 0; + + ftdm_assert_return(strlen(name) > 0, FTDM_FAIL, "Invalid group name provided\n"); + ftdm_assert_return(currindex >= 0, FTDM_FAIL, "Invalid current channel index provided\n"); + + if (!span->chan_count) { + return FTDM_SUCCESS; + } + + for (chan_index = currindex+1; chan_index <= span->chan_count; chan_index++) { + if (!FTDM_IS_VOICE_CHANNEL(span->channels[chan_index])) { + continue; + } + if (ftdm_channel_add_to_group(name, span->channels[chan_index])) { + ftdm_log(FTDM_LOG_ERROR, "Failed to add chan:%d to group:%s\n", chan_index, name); + } + } + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_group_find(uint32_t id, ftdm_group_t **group) +{ + ftdm_group_t *fgroup = NULL, *grp; + + if (id > FTDM_MAX_GROUPS_INTERFACE) { + return FTDM_FAIL; + } + + + ftdm_mutex_lock(globals.group_mutex); + for (grp = globals.groups; grp; grp = grp->next) { + if (grp->group_id == id) { + fgroup = grp; + break; + } + } + ftdm_mutex_unlock(globals.group_mutex); + + if (!fgroup) { + return FTDM_FAIL; + } + + *group = fgroup; + + return FTDM_SUCCESS; + +} + +FT_DECLARE(ftdm_status_t) ftdm_group_find_by_name(const char *name, ftdm_group_t **group) +{ + ftdm_status_t status = FTDM_FAIL; + *group = NULL; + ftdm_mutex_lock(globals.group_mutex); + if (!ftdm_strlen_zero(name)) { + if ((*group = hashtable_search(globals.group_hash, (void *) name))) { + status = FTDM_SUCCESS; + } + } + ftdm_mutex_unlock(globals.group_mutex); + return status; +} + +static void ftdm_group_add(ftdm_group_t *group) +{ + ftdm_group_t *grp; + ftdm_mutex_lock(globals.group_mutex); + + for (grp = globals.groups; grp && grp->next; grp = grp->next); + + if (grp) { + grp->next = group; + } else { + globals.groups = group; + } + hashtable_insert(globals.group_hash, (void *)group->name, group, HASHTABLE_FLAG_NONE); + + ftdm_mutex_unlock(globals.group_mutex); +} + + +FT_DECLARE(ftdm_status_t) ftdm_group_create(ftdm_group_t **group, const char *name) +{ + ftdm_group_t *new_group = NULL; + ftdm_status_t status = FTDM_FAIL; + + ftdm_mutex_lock(globals.mutex); + if (globals.group_index < FTDM_MAX_GROUPS_INTERFACE) { + new_group = ftdm_calloc(1, sizeof(*new_group)); + + ftdm_assert(new_group != NULL, "Failed to create new ftdm group, expect a crash\n"); + + status = ftdm_mutex_create(&new_group->mutex); + + ftdm_assert(status == FTDM_SUCCESS, "Failed to create group mutex, expect a crash\n"); + + new_group->group_id = ++globals.group_index; + new_group->name = ftdm_strdup(name); + ftdm_group_add(new_group); + *group = new_group; + status = FTDM_SUCCESS; + } else { + ftdm_log(FTDM_LOG_ERROR, "Group %s was not added, we exceeded the max number of groups\n", name); + } + ftdm_mutex_unlock(globals.mutex); + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t *sigmsg) +{ + ftdm_status_t status = FTDM_FAIL; + + if (span->signal_cb) { + if (sigmsg->channel) { + ftdm_mutex_lock(sigmsg->channel->mutex); + } + + status = span->signal_cb(sigmsg); + + if (sigmsg->channel) { + ftdm_mutex_unlock(sigmsg->channel->mutex); + } + } + + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_global_init(void) +{ + memset(&globals, 0, sizeof(globals)); + + time_init(); + + ftdm_thread_override_default_stacksize(FTDM_THREAD_STACKSIZE); + + memset(&interfaces, 0, sizeof(interfaces)); + globals.interface_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + globals.module_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + globals.span_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + globals.group_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + ftdm_mutex_create(&globals.mutex); + ftdm_mutex_create(&globals.span_mutex); + ftdm_mutex_create(&globals.group_mutex); + globals.running = 1; + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_global_configuration(void) +{ + int modcount = ftdm_load_modules(); + ftdm_log(FTDM_LOG_NOTICE, "Modules configured: %d \n", modcount); + + if (load_config() != FTDM_SUCCESS) { + globals.running = 0; + ftdm_log(FTDM_LOG_ERROR, "FreeTDM global configuration failed!\n"); + return FTDM_FAIL; + } + return FTDM_SUCCESS; +} + +FT_DECLARE(uint32_t) ftdm_running(void) +{ + return globals.running; +} + + +FT_DECLARE(ftdm_status_t) ftdm_global_destroy(void) +{ + ftdm_span_t *sp; + + time_end(); + + globals.running = 0; + + globals.span_index = 0; + + ftdm_span_close_all(); + + ftdm_mutex_lock(globals.span_mutex); + for (sp = globals.spans; sp;) { + ftdm_span_t *cur_span = sp; + sp = sp->next; + + if (cur_span) { + if (ftdm_test_flag(cur_span, FTDM_SPAN_CONFIGURED)) { + ftdm_span_destroy(cur_span); + } + + hashtable_remove(globals.span_hash, (void *)cur_span->name); + ftdm_safe_free(cur_span->type); + ftdm_safe_free(cur_span->name); + ftdm_safe_free(cur_span); + cur_span = NULL; + } + } + globals.spans = NULL; + ftdm_mutex_unlock(globals.span_mutex); + + ftdm_unload_modules(); + + ftdm_mutex_lock(globals.mutex); + hashtable_destroy(globals.interface_hash); + hashtable_destroy(globals.module_hash); + hashtable_destroy(globals.span_hash); + ftdm_mutex_unlock(globals.mutex); + ftdm_mutex_destroy(&globals.mutex); + ftdm_mutex_destroy(&globals.span_mutex); + + memset(&globals, 0, sizeof(globals)); + return FTDM_SUCCESS; +} + + +FT_DECLARE(uint32_t) ftdm_separate_string(char *buf, char delim, char **array, int arraylen) +{ + int argc; + char *ptr; + int quot = 0; + char qc = '\''; + int x; + + if (!buf || !array || !arraylen) { + return 0; + } + + memset(array, 0, arraylen * sizeof(*array)); + + ptr = buf; + + for (argc = 0; *ptr && (argc < arraylen - 1); argc++) { + array[argc] = ptr; + for (; *ptr; ptr++) { + if (*ptr == qc) { + if (quot) { + quot--; + } else { + quot++; + } + } else if ((*ptr == delim) && !quot) { + *ptr++ = '\0'; + break; + } + } + } + + if (*ptr) { + array[argc++] = ptr; + } + + /* strip quotes and leading / trailing spaces */ + for (x = 0; x < argc; x++) { + char *p; + + while(*(array[x]) == ' ') { + (array[x])++; + } + p = array[x]; + while((p = strchr(array[x], qc))) { + memmove(p, p+1, strlen(p)); + p++; + } + p = array[x] + (strlen(array[x]) - 1); + while(*p == ' ') { + *p-- = '\0'; + } + } + + return argc; +} + +FT_DECLARE(void) ftdm_bitstream_init(ftdm_bitstream_t *bsp, uint8_t *data, uint32_t datalen, ftdm_endian_t endian, uint8_t ss) +{ + memset(bsp, 0, sizeof(*bsp)); + bsp->data = data; + bsp->datalen = datalen; + bsp->endian = endian; + bsp->ss = ss; + + if (endian < 0) { + bsp->top = bsp->bit_index = 7; + bsp->bot = 0; + } else { + bsp->top = bsp->bit_index = 0; + bsp->bot = 7; + } + +} + +FT_DECLARE(int8_t) ftdm_bitstream_get_bit(ftdm_bitstream_t *bsp) +{ + int8_t bit = -1; + + + if (bsp->byte_index >= bsp->datalen) { + goto done; + } + + if (bsp->ss) { + if (!bsp->ssv) { + bsp->ssv = 1; + return 0; + } else if (bsp->ssv == 2) { + bsp->byte_index++; + bsp->ssv = 0; + return 1; + } + } + + + + + bit = (bsp->data[bsp->byte_index] >> (bsp->bit_index)) & 1; + + if (bsp->bit_index == bsp->bot) { + bsp->bit_index = bsp->top; + if (bsp->ss) { + bsp->ssv = 2; + goto done; + } + + if (++bsp->byte_index > bsp->datalen) { + bit = -1; + goto done; + } + + } else { + bsp->bit_index = bsp->bit_index + bsp->endian; + } + + + done: + return bit; +} + +FT_DECLARE(void) print_hex_bytes(uint8_t *data, ftdm_size_t dlen, char *buf, ftdm_size_t blen) +{ + char *bp = buf; + uint8_t *byte = data; + uint32_t i, j = 0; + + if (blen < (dlen * 3) + 2) { + return; + } + + *bp++ = '['; + j++; + + for(i = 0; i < dlen; i++) { + snprintf(bp, blen-j, "%02x ", *byte++); + bp += 3; + j += 3; + } + + *--bp = ']'; + +} + +FT_DECLARE(void) print_bits(uint8_t *b, int bl, char *buf, int blen, ftdm_endian_t e, uint8_t ss) +{ + ftdm_bitstream_t bs; + int j = 0, c = 0; + int8_t bit; + uint32_t last; + + if (blen < (bl * 10) + 2) { + return; + } + + ftdm_bitstream_init(&bs, b, bl, e, ss); + last = bs.byte_index; + while((bit = ftdm_bitstream_get_bit(&bs)) > -1) { + buf[j++] = bit ? '1' : '0'; + if (bs.byte_index != last) { + buf[j++] = ' '; + last = bs.byte_index; + if (++c == 8) { + buf[j++] = '\n'; + c = 0; + } + } + } + +} + + + +FT_DECLARE_NONSTD(ftdm_status_t) ftdm_console_stream_raw_write(ftdm_stream_handle_t *handle, uint8_t *data, ftdm_size_t datalen) +{ + ftdm_size_t need = handle->data_len + datalen; + + if (need >= handle->data_size) { + void *new_data; + need += handle->alloc_chunk; + + if (!(new_data = realloc(handle->data, need))) { + return FTDM_MEMERR; + } + + handle->data = new_data; + handle->data_size = need; + } + + memcpy((uint8_t *) (handle->data) + handle->data_len, data, datalen); + handle->data_len += datalen; + handle->end = (uint8_t *) (handle->data) + handle->data_len; + *(uint8_t *)handle->end = '\0'; + + return FTDM_SUCCESS; +} + +FT_DECLARE(int) ftdm_vasprintf(char **ret, const char *fmt, va_list ap) /* code from switch_apr.c */ +{ +#ifdef HAVE_VASPRINTF + return vasprintf(ret, fmt, ap); +#else + char *buf; + int len; + size_t buflen; + va_list ap2; + char *tmp = NULL; + +#ifdef _MSC_VER +#if _MSC_VER >= 1500 + /* hack for incorrect assumption in msvc header files for code analysis */ + __analysis_assume(tmp); +#endif + ap2 = ap; +#else + va_copy(ap2, ap); +#endif + + len = vsnprintf(tmp, 0, fmt, ap2); + + if (len > 0 && (buf = ftdm_malloc((buflen = (size_t) (len + 1)))) != NULL) { + len = vsnprintf(buf, buflen, fmt, ap); + *ret = buf; + } else { + *ret = NULL; + len = -1; + } + + va_end(ap2); + return len; +#endif +} + +FT_DECLARE_NONSTD(ftdm_status_t) ftdm_console_stream_write(ftdm_stream_handle_t *handle, const char *fmt, ...) +{ + va_list ap; + char *buf = handle->data; + char *end = handle->end; + int ret = 0; + char *data = NULL; + + if (handle->data_len >= handle->data_size) { + return FTDM_FAIL; + } + + va_start(ap, fmt); + ret = ftdm_vasprintf(&data, fmt, ap); + va_end(ap); + + if (data) { + ftdm_size_t remaining = handle->data_size - handle->data_len; + ftdm_size_t need = strlen(data) + 1; + + if ((remaining < need) && handle->alloc_len) { + ftdm_size_t new_len; + void *new_data; + + new_len = handle->data_size + need + handle->alloc_chunk; + if ((new_data = realloc(handle->data, new_len))) { + handle->data_size = handle->alloc_len = new_len; + handle->data = new_data; + buf = handle->data; + remaining = handle->data_size - handle->data_len; + handle->end = (uint8_t *) (handle->data) + handle->data_len; + end = handle->end; + } else { + ftdm_log(FTDM_LOG_CRIT, "Memory Error!\n"); + ftdm_safe_free(data); + return FTDM_FAIL; + } + } + + if (remaining < need) { + ret = -1; + } else { + ret = 0; + snprintf(end, remaining, "%s", data); + handle->data_len = strlen(buf); + handle->end = (uint8_t *) (handle->data) + handle->data_len; + } + ftdm_safe_free(data); + } + + return ret ? FTDM_FAIL : FTDM_SUCCESS; +} + +FT_DECLARE(char *) ftdm_strdup(const char *str) +{ + ftdm_size_t len = strlen(str) + 1; + void *new = ftdm_malloc(len); + + if (!new) { + return NULL; + } + + return (char *)memcpy(new, str, len); +} + +FT_DECLARE(char *) ftdm_strndup(const char *str, ftdm_size_t inlen) +{ + char *new = NULL; + ftdm_size_t len = strlen(str) + 1; + if (len > (inlen+1)) { + len = inlen+1; + } + new = (char *)ftdm_malloc(len); + + if (!new) { + return NULL; + } + + memcpy(new, str, len-1); + new[len-1] = 0; + return new; +} + + +/* 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: + */ diff --git a/libs/freetdm/src/ftdm_m3ua.c b/libs/freetdm/src/ftdm_m3ua.c new file mode 100644 index 0000000000..31f4cd5c5e --- /dev/null +++ b/libs/freetdm/src/ftdm_m3ua.c @@ -0,0 +1,692 @@ +/* + * ftdm_m3ua.c + * freetdm + * + * Created by Shane Burrell on 4/3/08. + * Copyright 2008 Shane Burrell. All rights reserved. + * + * + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic * + * + * 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 "m3ua_client.h" +#include "ftdm_m3ua.h" + +#define MAX_REQ_ID MAX_PENDING_CALLS +typedef uint16_t m3ua_request_id_t; + +typedef enum { + BST_FREE, + BST_WAITING, + BST_READY, + BST_FAIL +} m3ua_request_status_t; + +typedef struct { + m3ua_request_status_t status; + m3uac_event_t event; + ftdm_span_t *span; + ftdm_channel_t *ftdmchan; +} m3ua_request_t; + + +struct general_config { + uint32_t region; +}; +typedef struct general_config general_config_t; + + +struct m3ua_channel_profile { + char name[80]; + int cust_span; + unsigned char opc[3]; + unsigned char dpc[3]; + int local_ip[4]; + int local_port; + int remote_ip[4]; + int remote_port; + int m3ua_mode; +}; +typedef struct m3ua_channel_profile m3ua_channel_profile_t; + +static struct { + ftdm_hash_t *profile_hash; + general_config_t general_config; +} globals; + +struct m3ua_span_data { + uint32_t boardno; + uint32_t flags; +}; +typedef struct m3ua_span_data m3ua_span_data_t; + +struct m3ua_chan_data { + ftdm_buffer_t *digit_buffer; + ftdm_mutex_t *digit_mutex; + ftdm_size_t dtmf_len; + uint32_t flags; + uint32_t hdlc_bytes; +}; +typedef struct m3ua_chan_data m3ua_chan_data_t; + +static ftdm_mutex_t *request_mutex = NULL; +static ftdm_mutex_t *signal_mutex = NULL; + +static uint8_t req_map[MAX_REQ_ID+1] = { 0 }; + +static void release_request_id(m3ua_request_id_t r) +{ + ftdm_mutex_lock(request_mutex); + req_map[r] = 0; + ftdm_mutex_unlock(request_mutex); +} + +/*static m3ua_request_id_t next_request_id(void) +{ + m3ua_request_id_t r = 0; + int ok = 0; + + while(!ok) { + ftdm_mutex_lock(request_mutex); + for (r = 1; r <= MAX_REQ_ID; r++) { + if (!req_map[r]) { + ok = 1; + req_map[r] = 1; + break; + } + } + ftdm_mutex_unlock(request_mutex); + if (!ok) { + ftdm_sleep(5); + } + } + return r; +} +*/ + +static __inline__ void state_advance(ftdm_channel_t *ftdmchan) +{ + + m3ua_data_t *m3ua_data = ftdmchan->span->signal_data; + m3uac_connection_t *mcon = &m3ua_data->mcon; + ftdm_sigmsg_t sig; + ftdm_status_t status; + + ftdm_log(FTDM_LOG_DEBUG, "%d:%d STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state)); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + + switch (ftdmchan->state) { + case FTDM_CHANNEL_STATE_DOWN: + { + if (ftdmchan->extra_id) { + release_request_id((m3ua_request_id_t)ftdmchan->extra_id); + ftdmchan->extra_id = 0; + } + ftdm_channel_done(ftdmchan); + } + break; + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + case FTDM_CHANNEL_STATE_PROGRESS: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; + if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + m3uac_exec_command(mcon, + ftdmchan->physical_span_id-1, + ftdmchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + } + break; + case FTDM_CHANNEL_STATE_RING: + { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_START; + if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } + + } + break; + case FTDM_CHANNEL_STATE_RESTART: + { + if (ftdmchan->last_state != FTDM_CHANNEL_STATE_HANGUP && ftdmchan->last_state != FTDM_CHANNEL_STATE_DOWN) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + case FTDM_CHANNEL_STATE_UP: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_UP; + if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + if (!(ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA))) { + m3uac_exec_command(mcon, + ftdmchan->physical_span_id-1, + ftdmchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + m3uac_exec_command(mcon, + ftdmchan->physical_span_id-1, + ftdmchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_ANSWERED, + 0); + } + } + break; + case FTDM_CHANNEL_STATE_DIALING: + { + } + break; + case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: + { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + break; + case FTDM_CHANNEL_STATE_HANGUP: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_ANSWERED) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA)) { + m3uac_exec_command(mcon, + ftdmchan->physical_span_id-1, + ftdmchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_STOPPED, + ftdmchan->caller_data.hangup_cause); + } else { + m3uac_exec_command(mcon, + ftdmchan->physical_span_id-1, + ftdmchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_NACK, + ftdmchan->caller_data.hangup_cause); + } + } + break; + case FTDM_CHANNEL_STATE_CANCEL: + { + sig.event_id = FTDM_SIGEVENT_STOP; + status = m3ua_data->signal_cb(&sig); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + m3uac_exec_command(mcon, + ftdmchan->physical_span_id-1, + ftdmchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0); + } + break; + case FTDM_CHANNEL_STATE_TERMINATING: + { + sig.event_id = FTDM_SIGEVENT_STOP; + status = m3ua_data->signal_cb(&sig); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + m3uac_exec_command(mcon, + ftdmchan->physical_span_id-1, + ftdmchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0); + } + break; + default: + break; + } +} + + +static __inline__ void check_state(ftdm_span_t *span) +{ + if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) { + uint32_t j; + ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE); + for(j = 1; j <= span->chan_count; j++) { + if (ftdm_test_flag((&span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) { + ftdm_clear_flag_locked((&span->channels[j]), FTDM_CHANNEL_STATE_CHANGE); + state_advance(&span->channels[j]); + ftdm_channel_complete_state(&span->channels[j]); + } + } + } +} + + +static int parse_ss7_event(ftdm_span_t *span, m3uac_connection_t *mcon, m3uac_event_t *event) +{ + ftdm_mutex_lock(signal_mutex); + + if (!ftdm_running()) { + ftdm_log(FTDM_LOG_WARNING, "System is shutting down.\n"); + goto end; + } + + + if (ftdm_test_flag(span, FTDM_SPAN_SUSPENDED) && + event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && event->event_id != SIGBOOST_EVENT_HEARTBEAT) { + + ftdm_log(FTDM_LOG_WARNING, + "INVALID EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", + m3uac_event_id_name(event->event_id), + event->event_id, + event->span+1, + event->chan+1, + event->release_cause, + event->call_setup_id, + event->fseqno, + (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), + (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") + ); + + goto end; + } + + + ftdm_log(FTDM_LOG_DEBUG, + "RX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", + m3uac_event_id_name(event->event_id), + event->event_id, + event->span+1, + event->chan+1, + event->release_cause, + event->call_setup_id, + event->fseqno, + (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), + (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") + ); + + + + switch(event->event_id) { + + case SIGBOOST_EVENT_CALL_START: + //handle_call_start(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + //handle_call_stop(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + //handle_call_start_ack(mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + //handle_call_start_nack(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + //handle_call_answer(span, mcon, event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + //handle_heartbeat(mcon, event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + //handle_call_done(span, mcon, event); + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + //handle_call_loop_start(event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + //handle_call_stop(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + //handle_restart_ack(mcon, span, event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + //handle_gap_abate(event); + break; + default: + ftdm_log(FTDM_LOG_WARNING, "No handler implemented for [%s]\n", m3uac_event_id_name(event->event_id)); + break; + } + + end: + + ftdm_mutex_unlock(signal_mutex); + + return 0; +} + +static FIO_CONFIGURE_FUNCTION(m3ua_configure) +{ + m3ua_channel_profile_t *profile = NULL; + + int ok = 1; + + if (!(profile = (m3ua_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) { + profile = ftdm_malloc(sizeof(*profile)); + memset(profile, 0, sizeof(*profile)); + ftdm_set_string(profile->name, category); + hashtable_insert(globals.profile_hash, (void *)profile->name, profile); + ftdm_log(FTDM_LOG_INFO, "creating profile [%s]\n", category); + } + +// ftdm_set_string(m3ua_data->mcon. cfg.local_ip, local_ip); + if (!strcasecmp(var, "local_sctp_port")) { + profile->local_port = 30000 ; + profile->remote_port = 30000; + profile->cust_span++; + } + ok = 1; + + + if (ok) { + ftdm_log(FTDM_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category); + } else { + ftdm_log(FTDM_LOG_ERROR, "unknown param [%s]\n", var); + } + + return FTDM_SUCCESS; +} + +static FIO_CONFIGURE_SPAN_FUNCTION(m3ua_configure_span) +{ + + return FTDM_FAIL; +} + +static FIO_OPEN_FUNCTION(m3ua_open) +{ + + return FTDM_FAIL; +} + +static FIO_CLOSE_FUNCTION(m3ua_close) +{ + + return FTDM_FAIL; +} + +/*static FIO_SET_INTERVAL_FUNCTION(m3ua_set_interval) +{ + + return 0; +}*/ + +static FIO_WAIT_FUNCTION(m3ua_wait) +{ + + return FTDM_FAIL; +} + +static FIO_READ_FUNCTION(m3ua_read) +{ + + return FTDM_FAIL; +} + +static FIO_WRITE_FUNCTION(m3ua_write) +{ + + return FTDM_FAIL; +} + +static FIO_COMMAND_FUNCTION(m3ua_command) +{ + return FTDM_FAIL; +} + +static FIO_SPAN_POLL_EVENT_FUNCTION(m3ua_poll_event) +{ + return FTDM_FAIL; +} + +static FIO_SPAN_NEXT_EVENT_FUNCTION(m3ua_next_event) +{ + return FTDM_FAIL; +} + + +static FIO_SPAN_DESTROY_FUNCTION(m3ua_span_destroy) +{ + m3ua_span_data_t *span_data = (m3ua_span_data_t *) span->mod_data; + + if (span_data) { + ftdm_safe_free(span_data); + } + + return FTDM_SUCCESS; +} +static FIO_CHANNEL_DESTROY_FUNCTION(m3ua_channel_destroy) +{ + m3ua_chan_data_t *chan_data = (m3ua_chan_data_t *) ftdmchan->mod_data; + m3ua_span_data_t *span_data = (m3ua_span_data_t *) ftdmchan->span->mod_data; + + if (!chan_data) { + return FTDM_FAIL; + } + + + + + + + ftdm_mutex_destroy(&chan_data->digit_mutex); + ftdm_buffer_destroy(&chan_data->digit_buffer); + + + ftdm_safe_free(chan_data); + + if (span_data) { + ftdm_safe_free(span_data); + } + + + return FTDM_SUCCESS; +} + + + +static FIO_GET_ALARMS_FUNCTION(m3ua_get_alarms) +{ + return FTDM_FAIL; +} + +static ftdm_io_interface_t m3ua_interface; + +ftdm_status_t m3ua_init(ftdm_io_interface_t **zint) +{ + assert(zint != NULL); + memset(&m3ua_interface, 0, sizeof(m3ua_interface)); + + m3ua_interface.name = "m3ua"; + m3ua_interface.configure = m3ua_configure; + m3ua_interface.configure_span = m3ua_configure_span; + m3ua_interface.open = m3ua_open; + m3ua_interface.close = m3ua_close; + m3ua_interface.wait = m3ua_wait; + m3ua_interface.read = m3ua_read; + m3ua_interface.write = m3ua_write; + m3ua_interface.command = m3ua_command; + m3ua_interface.poll_event = m3ua_poll_event; + m3ua_interface.next_event = m3ua_next_event; + m3ua_interface.channel_destroy = m3ua_channel_destroy; + m3ua_interface.span_destroy = m3ua_span_destroy; + m3ua_interface.get_alarms = m3ua_get_alarms; + *zint = &m3ua_interface; + + return FTDM_FAIL; +} + +ftdm_status_t m3ua_destroy(void) +{ + return FTDM_FAIL; +} + + +static void *m3ua_run(ftdm_thread_t *me, void *obj) +{ + ftdm_span_t *span = (ftdm_span_t *) obj; + m3ua_data_t *m3ua_data = span->signal_data; + m3uac_connection_t *mcon, *pcon; + uint32_t ms = 10, too_long = 60000; + + + m3ua_data->pcon = m3ua_data->mcon; + + if (m3uac_connection_open(&m3ua_data->mcon, + m3ua_data->mcon.cfg.local_ip, + m3ua_data->mcon.cfg.local_port, + m3ua_data->mcon.cfg.remote_ip, + m3ua_data->mcon.cfg.remote_port) < 0) { + ftdm_log(FTDM_LOG_DEBUG, "Error: Opening MCON Socket [%d] %s\n", m3ua_data->mcon.socket, strerror(errno)); + goto end; + } + + if (m3uac_connection_open(&m3ua_data->pcon, + m3ua_data->pcon.cfg.local_ip, + ++m3ua_data->pcon.cfg.local_port, + m3ua_data->pcon.cfg.remote_ip, + m3ua_data->pcon.cfg.remote_port) < 0) { + ftdm_log(FTDM_LOG_DEBUG, "Error: Opening PCON Socket [%d] %s\n", m3ua_data->pcon.socket, strerror(errno)); + goto end; + } + + mcon = &m3ua_data->mcon; + pcon = &m3ua_data->pcon; + + top: + + //init_outgoing_array(); + + m3uac_exec_command(mcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + + while (ftdm_test_flag(m3ua_data, FTDM_M3UA_RUNNING)) { + fd_set rfds, efds; + struct timeval tv = { 0, ms * 1000 }; + int max, activity, i = 0; + m3uac_event_t *event = NULL; + + if (!ftdm_running()) { + m3uac_exec_command(mcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + break; + } + + FD_ZERO(&rfds); + FD_ZERO(&efds); + FD_SET(mcon->socket, &rfds); + FD_SET(mcon->socket, &efds); + FD_SET(pcon->socket, &rfds); + FD_SET(pcon->socket, &efds); + + max = ((pcon->socket > mcon->socket) ? pcon->socket : mcon->socket) + 1; + + if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) { + goto error; + } + + if (activity) { + if (FD_ISSET(pcon->socket, &efds) || FD_ISSET(mcon->socket, &efds)) { + goto error; + } + + if (FD_ISSET(pcon->socket, &rfds)) { + if ((event = m3uac_connection_readp(pcon, i))) { + parse_ss7_event(span, mcon, event); + } else goto top; + } + + if (FD_ISSET(mcon->socket, &rfds)) { + if ((event = m3uac_connection_read(mcon, i))) { + parse_ss7_event(span, mcon, event); + } else goto top; + } + } + + check_state(span); + mcon->hb_elapsed += ms; + + if (mcon->hb_elapsed >= too_long && (mcon->up || !ftdm_test_flag(span, FTDM_SPAN_SUSPENDED))) { + ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART); + ftdm_set_flag_locked(span, FTDM_SPAN_SUSPENDED); + mcon->up = 0; + ftdm_log(FTDM_LOG_CRIT, "Lost Heartbeat!\n"); + } + + } + + goto end; + + error: + ftdm_log(FTDM_LOG_CRIT, "Socket Error!\n"); + + end: + + m3uac_connection_close(&m3ua_data->mcon); + m3uac_connection_close(&m3ua_data->pcon); + + ftdm_clear_flag(m3ua_data, FTDM_M3UA_RUNNING); + + ftdm_log(FTDM_LOG_DEBUG, "M3UA thread ended.\n"); + return NULL; +} +ftdm_status_t m3ua_start(ftdm_span_t *span) +{ + m3ua_data_t *m3ua_data = span->signal_data; + ftdm_set_flag(m3ua_data, FTDM_M3UA_RUNNING); + return ftdm_thread_create_detached(m3ua_run, span); +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: +*/ diff --git a/libs/freetdm/src/ftdm_queue.c b/libs/freetdm/src/ftdm_queue.c new file mode 100644 index 0000000000..60ee74737d --- /dev/null +++ b/libs/freetdm/src/ftdm_queue.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2009, Sangoma Technologies + * Moises Silva + * 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" + +static ftdm_status_t ftdm_std_queue_create(ftdm_queue_t **outqueue, ftdm_size_t capacity); +static ftdm_status_t ftdm_std_queue_enqueue(ftdm_queue_t *queue, void *obj); +static void *ftdm_std_queue_dequeue(ftdm_queue_t *queue); +static ftdm_status_t ftdm_std_queue_wait(ftdm_queue_t *queue, int ms); +static ftdm_status_t ftdm_std_queue_get_interrupt(ftdm_queue_t *queue, ftdm_interrupt_t **interrupt); +static ftdm_status_t ftdm_std_queue_destroy(ftdm_queue_t **inqueue); + +struct ftdm_queue { + ftdm_mutex_t *mutex; + ftdm_interrupt_t *interrupt; + ftdm_size_t capacity; + ftdm_size_t size; + unsigned rindex; + unsigned windex; + void **elements; +}; + +FT_DECLARE_DATA ftdm_queue_handler_t g_ftdm_queue_handler = +{ + /*.create = */ ftdm_std_queue_create, + /*.enqueue = */ ftdm_std_queue_enqueue, + /*.dequeue = */ ftdm_std_queue_dequeue, + /*.wait = */ ftdm_std_queue_wait, + /*.get_interrupt = */ ftdm_std_queue_get_interrupt, + /*.destroy = */ ftdm_std_queue_destroy +}; + +FT_DECLARE(ftdm_status_t) ftdm_global_set_queue_handler(ftdm_queue_handler_t *handler) +{ + if (!handler || + !handler->create || + !handler->enqueue || + !handler->dequeue || + !handler->wait || + !handler->get_interrupt || + !handler->destroy) { + return FTDM_FAIL; + } + memcpy(&g_ftdm_queue_handler, handler, sizeof(*handler)); + return FTDM_SUCCESS; +} + +static ftdm_status_t ftdm_std_queue_create(ftdm_queue_t **outqueue, ftdm_size_t capacity) +{ + ftdm_queue_t *queue = NULL; + ftdm_assert_return(outqueue, FTDM_FAIL, "Queue double pointer is null\n"); + ftdm_assert_return(capacity > 0, FTDM_FAIL, "Queue capacity is not bigger than 0\n"); + + *outqueue = NULL; + queue = ftdm_calloc(1, sizeof(*queue)); + if (!queue) { + return FTDM_FAIL; + } + + queue->elements = ftdm_calloc(1, (sizeof(void*)*capacity)); + if (!queue->elements) { + goto failed; + } + queue->capacity = capacity; + + if (ftdm_mutex_create(&queue->mutex) != FTDM_SUCCESS) { + goto failed; + } + + if (ftdm_interrupt_create(&queue->interrupt, FTDM_INVALID_SOCKET) != FTDM_SUCCESS) { + goto failed; + } + + *outqueue = queue; + return FTDM_SUCCESS; + +failed: + if (queue) { + if (queue->interrupt) { + ftdm_interrupt_destroy(&queue->interrupt); + } + if (queue->mutex) { + ftdm_mutex_destroy(&queue->mutex); + } + ftdm_safe_free(queue->elements); + ftdm_safe_free(queue); + } + return FTDM_FAIL; +} + +static ftdm_status_t ftdm_std_queue_enqueue(ftdm_queue_t *queue, void *obj) +{ + ftdm_status_t status = FTDM_FAIL; + + ftdm_assert_return(queue != NULL, FTDM_FAIL, "Queue is null!"); + + ftdm_mutex_lock(queue->mutex); + + if (queue->windex == queue->capacity) { + /* try to see if we can wrap around */ + queue->windex = 0; + } + + if (queue->size != 0 && queue->windex == queue->rindex) { + ftdm_log(FTDM_LOG_ERROR, "Failed to enqueue obj %p in queue %p, no more room! windex == rindex == %d!\n", obj, queue, queue->windex); + goto done; + } + + queue->elements[queue->windex++] = obj; + queue->size++; + status = FTDM_SUCCESS; + + /* wake up queue reader */ + ftdm_interrupt_signal(queue->interrupt); + +done: + + ftdm_mutex_unlock(queue->mutex); + + return status; +} + +static void *ftdm_std_queue_dequeue(ftdm_queue_t *queue) +{ + void *obj = NULL; + + ftdm_assert_return(queue != NULL, NULL, "Queue is null!"); + + ftdm_mutex_lock(queue->mutex); + + if (queue->size == 0) { + goto done; + } + + obj = queue->elements[queue->rindex]; + queue->elements[queue->rindex++] = NULL; + queue->size--; + if (queue->rindex == queue->capacity) { + queue->rindex = 0; + } + +done: + + ftdm_mutex_unlock(queue->mutex); + + return obj; +} + +static ftdm_status_t ftdm_std_queue_wait(ftdm_queue_t *queue, int ms) +{ + ftdm_status_t ret; + ftdm_assert_return(queue != NULL, FTDM_FAIL, "Queue is null!"); + + ftdm_mutex_lock(queue->mutex); + + /* if there is elements in the queue, no need to wait */ + if (queue->size != 0) { + ftdm_mutex_unlock(queue->mutex); + return FTDM_SUCCESS; + } + + /* no elements on the queue, wait for someone to write an element */ + ret = ftdm_interrupt_wait(queue->interrupt, ms); + + /* got an element or timeout, bail out */ + ftdm_mutex_unlock(queue->mutex); + + return ret; +} + +static ftdm_status_t ftdm_std_queue_get_interrupt(ftdm_queue_t *queue, ftdm_interrupt_t **interrupt) +{ + ftdm_assert_return(queue != NULL, FTDM_FAIL, "Queue is null!\n"); + ftdm_assert_return(interrupt != NULL, FTDM_FAIL, "Queue is null!\n"); + *interrupt = queue->interrupt; + return FTDM_SUCCESS; +} + +static ftdm_status_t ftdm_std_queue_destroy(ftdm_queue_t **inqueue) +{ + ftdm_queue_t *queue = NULL; + ftdm_assert_return(inqueue != NULL, FTDM_FAIL, "Queue is null!\n"); + ftdm_assert_return(*inqueue != NULL, FTDM_FAIL, "Queue is null!\n"); + + queue = *inqueue; + ftdm_interrupt_destroy(&queue->interrupt); + ftdm_mutex_destroy(&queue->mutex); + ftdm_safe_free(queue->elements); + ftdm_safe_free(queue); + *inqueue = NULL; + return FTDM_SUCCESS; +} + +/* 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: + */ diff --git a/libs/freetdm/src/ftdm_threadmutex.c b/libs/freetdm/src/ftdm_threadmutex.c new file mode 100644 index 0000000000..34c802b1de --- /dev/null +++ b/libs/freetdm/src/ftdm_threadmutex.c @@ -0,0 +1,457 @@ +/* + * Cross Platform Thread/Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + * Contributors: + * + * Moises Silva + * + */ + +#ifdef WIN32 +/* required for TryEnterCriticalSection definition. Must be defined before windows.h include */ +#define _WIN32_WINNT 0x0400 +#endif + +#include "freetdm.h" +#include "ftdm_threadmutex.h" + +#ifdef WIN32 +#include + +#define FTDM_THREAD_CALLING_CONVENTION __stdcall + +struct ftdm_mutex { + CRITICAL_SECTION mutex; +}; + +#else +#include +#include + +#define FTDM_THREAD_CALLING_CONVENTION + +struct ftdm_mutex { + pthread_mutex_t mutex; +}; + +#endif + +struct ftdm_interrupt { + ftdm_socket_t device; +#ifdef WIN32 + /* for generic interruption */ + HANDLE event; +#else + /* for generic interruption */ + int readfd; + int writefd; +#endif +}; + +struct ftdm_thread { +#ifdef WIN32 + void *handle; +#else + pthread_t handle; +#endif + void *private_data; + ftdm_thread_function_t function; + ftdm_size_t stack_size; +#ifndef WIN32 + pthread_attr_t attribute; +#endif +}; + +ftdm_size_t thread_default_stacksize = 0; + +FT_DECLARE(void) ftdm_thread_override_default_stacksize(ftdm_size_t size) +{ + thread_default_stacksize = size; +} + +static void * FTDM_THREAD_CALLING_CONVENTION thread_launch(void *args) +{ + void *exit_val; + ftdm_thread_t *thread = (ftdm_thread_t *)args; + exit_val = thread->function(thread, thread->private_data); +#ifndef WIN32 + pthread_attr_destroy(&thread->attribute); +#endif + ftdm_safe_free(thread); + + return exit_val; +} + +FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached(ftdm_thread_function_t func, void *data) +{ + return ftdm_thread_create_detached_ex(func, data, thread_default_stacksize); +} + +FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached_ex(ftdm_thread_function_t func, void *data, ftdm_size_t stack_size) +{ + ftdm_thread_t *thread = NULL; + ftdm_status_t status = FTDM_FAIL; + + if (!func || !(thread = (ftdm_thread_t *)ftdm_malloc(sizeof(ftdm_thread_t)))) { + goto done; + } + + thread->private_data = data; + thread->function = func; + thread->stack_size = stack_size; + +#if defined(WIN32) + thread->handle = (void *)_beginthreadex(NULL, (unsigned)thread->stack_size, (unsigned int (__stdcall *)(void *))thread_launch, thread, 0, NULL); + if (!thread->handle) { + goto fail; + } + CloseHandle(thread->handle); + + status = FTDM_SUCCESS; + goto done; +#else + + if (pthread_attr_init(&thread->attribute) != 0) goto fail; + + if (pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0) goto failpthread; + + if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0) goto failpthread; + + if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) goto failpthread; + + status = FTDM_SUCCESS; + goto done; + failpthread: + pthread_attr_destroy(&thread->attribute); +#endif + + fail: + if (thread) { + ftdm_safe_free(thread); + } + done: + return status; +} + + +FT_DECLARE(ftdm_status_t) ftdm_mutex_create(ftdm_mutex_t **mutex) +{ + ftdm_status_t status = FTDM_FAIL; +#ifndef WIN32 + pthread_mutexattr_t attr; +#endif + ftdm_mutex_t *check = NULL; + + check = (ftdm_mutex_t *)ftdm_malloc(sizeof(**mutex)); + if (!check) + goto done; +#ifdef WIN32 + InitializeCriticalSection(&check->mutex); +#else + if (pthread_mutexattr_init(&attr)) + goto done; + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) + goto fail; + + if (pthread_mutex_init(&check->mutex, &attr)) + goto fail; + + goto success; + + fail: + pthread_mutexattr_destroy(&attr); + goto done; + + success: +#endif + *mutex = check; + status = FTDM_SUCCESS; + + done: + return status; +} + +FT_DECLARE(ftdm_status_t) ftdm_mutex_destroy(ftdm_mutex_t **mutex) +{ + ftdm_mutex_t *mp = *mutex; + *mutex = NULL; + if (!mp) { + return FTDM_FAIL; + } +#ifdef WIN32 + DeleteCriticalSection(&mp->mutex); +#else + if (pthread_mutex_destroy(&mp->mutex)) + return FTDM_FAIL; +#endif + ftdm_safe_free(mp); + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) _ftdm_mutex_lock(ftdm_mutex_t *mutex) +{ +#ifdef WIN32 + EnterCriticalSection(&mutex->mutex); +#else + int err; + if ((err = pthread_mutex_lock(&mutex->mutex))) { + ftdm_log(FTDM_LOG_ERROR, "Failed to lock mutex %d:%s\n", err, strerror(err)); + return FTDM_FAIL; + } +#endif + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) _ftdm_mutex_trylock(ftdm_mutex_t *mutex) +{ +#ifdef WIN32 + if (!TryEnterCriticalSection(&mutex->mutex)) + return FTDM_FAIL; +#else + if (pthread_mutex_trylock(&mutex->mutex)) + return FTDM_FAIL; +#endif + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) _ftdm_mutex_unlock(ftdm_mutex_t *mutex) +{ +#ifdef WIN32 + LeaveCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_unlock(&mutex->mutex)) + return FTDM_FAIL; +#endif + return FTDM_SUCCESS; +} + + +FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, ftdm_socket_t device) +{ + ftdm_interrupt_t *interrupt = NULL; +#ifndef WIN32 + int fds[2]; +#endif + + ftdm_assert_return(ininterrupt != NULL, FTDM_FAIL, "interrupt double pointer is null!\n"); + + interrupt = ftdm_calloc(1, sizeof(*interrupt)); + if (!interrupt) { + ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt memory\n"); + return FTDM_FAIL; + } + + interrupt->device = device; +#ifdef WIN32 + interrupt->interrupt = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!interrupt->interrupt) { + ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt event\n"); + goto failed; + } +#else + if (pipe(fds)) { + ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno)); + goto failed; + } + interrupt->readfd = fds[0]; + interrupt->writefd = fds[1]; +#endif + + *ininterrupt = interrupt; + return FTDM_SUCCESS; + +failed: + if (interrupt) { +#ifndef WIN32 + if (interrupt->readfd) { + close(interrupt->readfd); + close(interrupt->writefd); + interrupt->readfd = -1; + interrupt->writefd = -1; + } +#endif + ftdm_safe_free(interrupt); + } + return FTDM_FAIL; +} + +#define ONE_BILLION 1000000000 + +FT_DECLARE(ftdm_status_t) ftdm_interrupt_wait(ftdm_interrupt_t *interrupt, int ms) +{ + int num = 1; +#ifdef WIN32 + DWORD res = 0; + HANDLE ints[2]; +#else + int res = 0; + struct pollfd ints[2]; + char pipebuf[255]; +#endif + + ftdm_assert_return(interrupt != NULL, FTDM_FAIL, "Condition is null!\n"); + + + /* start implementation */ +#ifdef WIN32 + ints[0] = interrupt->event; + if (interrupt->device != FTDM_INVALID_SOCKET) { + num++; + ints[1] = interrupt->device; + } + res = WaitForMultipleObjects(num, &ints, FALSE, ms >= 0 ? ms : INFINITE); + switch (res) { + case WAIT_TIMEOUT: + return FTDM_TIMEOUT; + case WAIT_FAILED: + case WAIT_ABANDONED: /* is it right to fail with abandoned? */ + return FTDM_FAIL; + default: + if (res >= (sizeof(ints)/sizeof(ints[0]))) { + ftdm_log(FTDM_LOG_ERROR, "Error waiting for freetdm interrupt event (WaitForSingleObject returned %d)\n", res); + return FTDM_FAIL; + } + return FTDM_SUCCESS; + } +#else + ints[0].fd = interrupt->readfd; + ints[0].events = POLLIN; + ints[0].revents = 0; + + if (interrupt->device != FTDM_INVALID_SOCKET) { + num++; + ints[1].fd = interrupt->device; + ints[1].events = POLLIN; + ints[1].revents = 0; + } + + res = poll(ints, num, ms); + + if (res == -1) { + ftdm_log(FTDM_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno)); + return FTDM_FAIL; + } + + if (res == 0) { + return FTDM_TIMEOUT; + } + + if (ints[0].revents & POLLIN) { + res = read(ints[0].fd, pipebuf, sizeof(pipebuf)); + if (res == -1) { + ftdm_log(FTDM_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno)); + } + } + + return FTDM_SUCCESS; +#endif +} + +FT_DECLARE(ftdm_status_t) ftdm_interrupt_signal(ftdm_interrupt_t *interrupt) +{ + ftdm_assert_return(interrupt != NULL, FTDM_FAIL, "Interrupt is null!\n"); +#ifdef WIN32 + if (!SetEvent(interrupt->interrupt)) { + ftdm_log(FTDM_LOG_ERROR, "Failed to signal interrupt\n"); + return FTDM_FAIL; + } +#else + int err; + if ((err = write(interrupt->writefd, "w", 1)) != 1) { + ftdm_log(FTDM_LOG_ERROR, "Failed to signal interrupt: %s\n", errno, strerror(errno)); + return FTDM_FAIL; + } +#endif + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_interrupt_destroy(ftdm_interrupt_t **ininterrupt) +{ + ftdm_interrupt_t *interrupt = NULL; + ftdm_assert_return(ininterrupt != NULL, FTDM_FAIL, "Interrupt null when destroying!\n"); + interrupt = *ininterrupt; +#ifdef WIN32 + CloseHandle(interrupt->interrupt); +#else + close(interrupt->readfd); + close(interrupt->writefd); + interrupt->readfd = -1; + interrupt->writefd = -1; +#endif + ftdm_safe_free(interrupt); + *ininterrupt = NULL; + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_interrupt_multiple_wait(ftdm_interrupt_t *interrupts[], ftdm_size_t size, int ms) +{ +#ifndef WIN32 + int i; + int res = 0; + int numdevices = 0; + char pipebuf[255]; + struct pollfd ints[size*2]; + + memset(&ints, 0, sizeof(ints)); + + for (i = 0; i < size; i++) { + ints[i].events = POLLIN; + ints[i].revents = 0; + ints[i].fd = interrupts[i]->readfd; + if (interrupts[i]->device != FTDM_INVALID_SOCKET) { + ints[i+numdevices].events = POLLIN; + ints[i+numdevices].revents = 0; + ints[i+numdevices].fd = interrupts[i]->device; + numdevices++; + } + } + + res = poll(ints, size + numdevices, ms); + + if (res == -1) { + ftdm_log(FTDM_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno)); + return FTDM_FAIL; + } + + if (res == 0) { + return FTDM_TIMEOUT; + } + + for (i = 0; i < size; i++) { + if (ints[i].revents & POLLIN) { + res = read(ints[i].fd, pipebuf, sizeof(pipebuf)); + if (res == -1) { + ftdm_log(FTDM_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno)); + } + } + } + +#endif + return FTDM_SUCCESS; +} + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftdm_analog.h b/libs/freetdm/src/ftmod/ftmod_analog/ftdm_analog.h new file mode 100644 index 0000000000..487829fd7b --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_analog/ftdm_analog.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 FTDM_ANALOG_H +#define FTDM_ANALOG_H +#include "freetdm.h" + +typedef enum { + FTDM_ANALOG_RUNNING = (1 << 0), + FTDM_ANALOG_CALLERID = (1 << 1) +} ftdm_analog_flag_t; + +#define FTDM_MAX_HOTLINE_STR 20 +#define MAX_DTMF 256 + +struct ftdm_analog_data { + uint32_t flags; + uint32_t max_dialstr; + uint32_t digit_timeout; + char hotline[FTDM_MAX_HOTLINE_STR]; +}; + + + +static void *ftdm_analog_run(ftdm_thread_t *me, void *obj); +typedef struct ftdm_analog_data ftdm_analog_data_t; + +#endif + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.2008.vcproj new file mode 100644 index 0000000000..448f03a545 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.2008.vcproj @@ -0,0 +1,353 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c new file mode 100644 index 0000000000..c71b19d202 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "ftdm_analog.h" + +#ifndef localtime_r +struct tm * localtime_r(const time_t *clock, struct tm *result); +#endif + +static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj); + +/** + * \brief Starts an FXO channel thread (outgoing call) + * \param ftdmchan Channel to initiate call on + * \return Success or failure + * + * Initialises state, starts tone progress detection and runs the channel in a new a thread. + */ +static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_fxo_outgoing_call) +{ + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK) && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { + ftdm_channel_clear_needed_tones(ftdmchan); + ftdm_channel_clear_detected_tones(ftdmchan); + + ftdm_channel_command(ftdmchan, FTDM_COMMAND_OFFHOOK, NULL); + ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_PROGRESS_DETECT, NULL); + ftdmchan->needed_tones[FTDM_TONEMAP_DIAL] = 1; + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALING); + ftdm_thread_create_detached(ftdm_analog_channel_run, ftdmchan); + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +/** + * \brief Starts an FXS channel thread (outgoing call) + * \param ftdmchan Channel to initiate call on + * \return Success or failure + * + * Indicates call waiting if channel is already in use, otherwise runs the channel in a new thread. + */ +static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_fxs_outgoing_call) +{ + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_CALLWAITING); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_GENRING); + ftdm_thread_create_detached(ftdm_analog_channel_run, ftdmchan); + } + + return FTDM_SUCCESS; +} + +/** + * \brief Starts an analog span thread (monitor) + * \param span Span to monitor + * \return Success or failure + */ +static ftdm_status_t ftdm_analog_start(ftdm_span_t *span) +{ + ftdm_analog_data_t *analog_data = span->signal_data; + ftdm_set_flag(analog_data, FTDM_ANALOG_RUNNING); + return ftdm_thread_create_detached(ftdm_analog_run, span); +} + +/** + * \brief Initialises an analog span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_configure_span) +//ftdm_status_t ftdm_analog_configure_span(ftdm_span_t *span, char *tonemap, uint32_t digit_timeout, uint32_t max_dialstr, fio_signal_cb_t sig_cb) +{ + ftdm_analog_data_t *analog_data; + const char *tonemap = "us"; + const char *hotline = ""; + uint32_t digit_timeout = 10; + uint32_t max_dialstr = MAX_DTMF; + const char *var, *val; + int *intval; + uint32_t flags = FTDM_ANALOG_CALLERID; + + assert(sig_cb != NULL); + + if (span->signal_type) { + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling."); + return FTDM_FAIL; + } + + analog_data = ftdm_malloc(sizeof(*analog_data)); + assert(analog_data != NULL); + memset(analog_data, 0, sizeof(*analog_data)); + + while ((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "tonemap")) { + if (!(val = va_arg(ap, char *))) { + break; + } + tonemap = val; + } else if (!strcasecmp(var, "digit_timeout")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + digit_timeout = *intval; + } else if (!strcasecmp(var, "enable_callerid")) { + if (!(val = va_arg(ap, char *))) { + break; + } + + if (ftdm_true(val)) { + flags |= FTDM_ANALOG_CALLERID; + } else { + flags &= ~FTDM_ANALOG_CALLERID; + } + } else if (!strcasecmp(var, "max_dialstr")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + max_dialstr = *intval; + } else if (!strcasecmp(var, "hotline")) { + if (!(val = va_arg(ap, char *))) { + break; + } + hotline = val; + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return FTDM_FAIL; + } + } + + + if (digit_timeout < 2000 || digit_timeout > 10000) { + digit_timeout = 2000; + } + + if ((max_dialstr < 1 && !strlen(hotline)) || max_dialstr > MAX_DTMF) { + max_dialstr = MAX_DTMF; + } + + span->start = ftdm_analog_start; + analog_data->flags = flags; + analog_data->digit_timeout = digit_timeout; + analog_data->max_dialstr = max_dialstr; + span->signal_cb = sig_cb; + strncpy(analog_data->hotline, hotline, sizeof(analog_data->hotline)); + span->signal_type = FTDM_SIGTYPE_ANALOG; + span->signal_data = analog_data; + span->outgoing_call = span->trunk_type == FTDM_TRUNK_FXS ? analog_fxs_outgoing_call : analog_fxo_outgoing_call; + ftdm_span_load_tones(span, tonemap); + + return FTDM_SUCCESS; + +} + +/** + * \brief Retrieves tone generation output to be sent + * \param ts Teletone generator + * \param map Tone map + * \return -1 on error, 0 on success + */ +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + ftdm_buffer_t *dt_buffer = ts->user_data; + int wrote; + + if (!dt_buffer) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + ftdm_buffer_write(dt_buffer, ts->buffer, wrote * 2); + return 0; +} + +/** + * \brief Sends caller id on an analog channel (FSK coded) + * \param ftdmchan Channel to send caller id on + */ +static void send_caller_id(ftdm_channel_t *ftdmchan) +{ + ftdm_fsk_data_state_t fsk_data; + uint8_t databuf[1024] = ""; + char time_str[9]; + struct tm tm; + time_t now; + ftdm_mdmf_type_t mt = MDMF_INVALID; + + time(&now); +#ifdef WIN32 + _tzset(); + _localtime64_s(&tm, &now); +#else + localtime_r(&now, &tm); +#endif + strftime(time_str, sizeof(time_str), "%m%d%H%M", &tm); + + ftdm_fsk_data_init(&fsk_data, databuf, sizeof(databuf)); + ftdm_fsk_data_add_mdmf(&fsk_data, MDMF_DATETIME, (uint8_t *) time_str, 8); + + if (ftdm_strlen_zero(ftdmchan->caller_data.cid_num.digits)) { + mt = MDMF_NO_NUM; + ftdm_set_string(ftdmchan->caller_data.cid_num.digits, "O"); + } else if (!strcasecmp(ftdmchan->caller_data.cid_num.digits, "P") || !strcasecmp(ftdmchan->caller_data.cid_num.digits, "O")) { + mt = MDMF_NO_NUM; + } else { + mt = MDMF_PHONE_NUM; + } + ftdm_fsk_data_add_mdmf(&fsk_data, mt, (uint8_t *) ftdmchan->caller_data.cid_num.digits, (uint8_t)strlen(ftdmchan->caller_data.cid_num.digits)); + + if (ftdm_strlen_zero(ftdmchan->caller_data.cid_name)) { + mt = MDMF_NO_NAME; + ftdm_set_string(ftdmchan->caller_data.cid_name, "O"); + } else if (!strcasecmp(ftdmchan->caller_data.cid_name, "P") || !strcasecmp(ftdmchan->caller_data.cid_name, "O")) { + mt = MDMF_NO_NAME; + } else { + mt = MDMF_PHONE_NAME; + } + ftdm_fsk_data_add_mdmf(&fsk_data, mt, (uint8_t *) ftdmchan->caller_data.cid_name, (uint8_t)strlen(ftdmchan->caller_data.cid_name)); + + ftdm_fsk_data_add_checksum(&fsk_data); + ftdm_channel_send_fsk_data(ftdmchan, &fsk_data, -14); +} + +/** + * \brief Main thread function for analog channel (outgoing call) + * \param me Current thread + * \param obj Channel to run in this thread + */ +static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) +{ + ftdm_channel_t *ftdmchan = (ftdm_channel_t *) obj; + ftdm_buffer_t *dt_buffer = NULL; + teletone_generation_session_t ts; + uint8_t frame[1024]; + ftdm_size_t len, rlen; + ftdm_tone_type_t tt = FTDM_TONE_DTMF; + char dtmf[MAX_DTMF+1] = ""; + ftdm_size_t dtmf_offset = 0; + ftdm_analog_data_t *analog_data = ftdmchan->span->signal_data; + ftdm_channel_t *closed_chan; + uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = 30000; + ftdm_sigmsg_t sig; + ftdm_status_t status; + + ftdm_log(FTDM_LOG_DEBUG, "ANALOG CHANNEL thread starting.\n"); + + ts.buffer = NULL; + + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "OPEN ERROR [%s]\n", ftdmchan->last_error); + goto done; + } + + if (ftdm_buffer_create(&dt_buffer, 1024, 3192, 0) != FTDM_SUCCESS) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "memory error!"); + ftdm_log(FTDM_LOG_ERROR, "MEM ERROR\n"); + goto done; + } + + if (ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_DTMF_DETECT, &tt) != FTDM_SUCCESS) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "error initilizing tone detector!"); + ftdm_log(FTDM_LOG_ERROR, "TONE ERROR\n"); + goto done; + } + + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INTHREAD); + teletone_init_session(&ts, 0, teletone_handler, dt_buffer); + ts.rate = 8000; +#if 0 + ts.debug = 1; + ts.debug_stream = stdout; +#endif + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval); + ftdm_buffer_set_loops(dt_buffer, -1); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + + assert(interval != 0); + + while (ftdm_running() && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { + ftdm_wait_flag_t flags = FTDM_READ; + ftdm_size_t dlen = 0; + + len = sizeof(frame); + + elapsed += interval; + state_counter += interval; + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { + switch(ftdmchan->state) { + case FTDM_CHANNEL_STATE_GET_CALLERID: + { + if (state_counter > 5000 || !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT)) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_CALLERID_DETECT, NULL); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_IDLE); + } + } + break; + case FTDM_CHANNEL_STATE_DIALING: + { + if (state_counter > dial_timeout) { + if (ftdmchan->needed_tones[FTDM_TONEMAP_DIAL]) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + } + } + } + break; + case FTDM_CHANNEL_STATE_GENRING: + { + if (state_counter > 60000) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } else if (!ftdmchan->fsk_buffer || !ftdm_buffer_inuse(ftdmchan->fsk_buffer)) { + ftdm_sleep(interval); + continue; + } + } + break; + case FTDM_CHANNEL_STATE_DIALTONE: + { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_HOLD) && state_counter > 10000) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } + } + break; + case FTDM_CHANNEL_STATE_BUSY: + { + if (state_counter > 20000) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_ATTN); + } + } + break; + case FTDM_CHANNEL_STATE_ATTN: + { + if (state_counter > 20000) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + case FTDM_CHANNEL_STATE_HANGUP: + { + if (state_counter > 500) { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RINGING)) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GENERATE_RING_OFF, NULL); + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK) && + (ftdmchan->last_state == FTDM_CHANNEL_STATE_RING || ftdmchan->last_state == FTDM_CHANNEL_STATE_DIALTONE + || ftdmchan->last_state >= FTDM_CHANNEL_STATE_IDLE)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else { + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING; + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + } + break; + case FTDM_CHANNEL_STATE_CALLWAITING: + { + int done = 0; + + if (ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] == 1) { + send_caller_id(ftdmchan); + ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]++; + } else if (state_counter > 600 && !ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]) { + send_caller_id(ftdmchan); + ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]++; + } else if (state_counter > 1000 && !ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]) { + done = 1; + } else if (state_counter > 10000) { + if (ftdmchan->fsk_buffer) { + ftdm_buffer_zero(ftdmchan->fsk_buffer); + } else { + ftdm_buffer_create(&ftdmchan->fsk_buffer, 128, 128, 0); + } + + ts.user_data = ftdmchan->fsk_buffer; + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_CALLWAITING_SAS]); + ts.user_data = dt_buffer; + done = 1; + } + + if (done) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); + ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0; + } + } + case FTDM_CHANNEL_STATE_UP: + case FTDM_CHANNEL_STATE_IDLE: + { + ftdm_sleep(interval); + continue; + } + break; + case FTDM_CHANNEL_STATE_DOWN: + { + goto done; + } + break; + default: + break; + } + } else { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); + ftdm_channel_complete_state(ftdmchan); + indicate = 0; + state_counter = 0; + + 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)); + switch(ftdmchan->state) { + case FTDM_CHANNEL_STATE_UP: + { + ftdm_channel_use(ftdmchan); + ftdm_channel_clear_needed_tones(ftdmchan); + ftdm_channel_flush_dtmf(ftdmchan); + + if (ftdmchan->type == FTDM_CHAN_TYPE_FXO && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK)) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_OFFHOOK, NULL); + } + + if (ftdmchan->fsk_buffer && ftdm_buffer_inuse(ftdmchan->fsk_buffer)) { + ftdm_log(FTDM_LOG_DEBUG, "Cancel FSK transmit due to early answer.\n"); + ftdm_buffer_zero(ftdmchan->fsk_buffer); + } + + if (ftdmchan->type == FTDM_CHAN_TYPE_FXS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RINGING)) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GENERATE_RING_OFF, NULL); + } + + if (ftdmchan->token_count == 1) { + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_HOLD); + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_HOLD)) { + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_HOLD); + sig.event_id = FTDM_SIGEVENT_ADD_CALL; + } else { + sig.event_id = FTDM_SIGEVENT_UP; + } + + ftdm_span_send_signal(ftdmchan->span, &sig); + continue; + } + break; + case FTDM_CHANNEL_STATE_DIALING: + { + ftdm_channel_use(ftdmchan); + } + break; + case FTDM_CHANNEL_STATE_IDLE: + { + ftdm_channel_use(ftdmchan); + sig.event_id = FTDM_SIGEVENT_START; + + if (ftdmchan->type == FTDM_CHAN_TYPE_FXO) { + ftdm_set_string(ftdmchan->caller_data.dnis.digits, ftdmchan->chan_number); + } else { + ftdm_set_string(ftdmchan->caller_data.dnis.digits, dtmf); + } + + ftdm_span_send_signal(ftdmchan->span, &sig); + continue; + } + break; + case FTDM_CHANNEL_STATE_DOWN: + { + sig.event_id = FTDM_SIGEVENT_STOP; + ftdm_span_send_signal(ftdmchan->span, &sig); + goto done; + } + break; + case FTDM_CHANNEL_STATE_DIALTONE: + { + memset(&ftdmchan->caller_data, 0, sizeof(ftdmchan->caller_data)); + *dtmf = '\0'; + dtmf_offset = 0; + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_DIAL]); + indicate = 1; + } + break; + case FTDM_CHANNEL_STATE_CALLWAITING: + { + ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0; + if (ftdmchan->fsk_buffer) { + ftdm_buffer_zero(ftdmchan->fsk_buffer); + } else { + ftdm_buffer_create(&ftdmchan->fsk_buffer, 128, 128, 0); + } + + ts.user_data = ftdmchan->fsk_buffer; + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_CALLWAITING_SAS]); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_CALLWAITING_CAS]); + ts.user_data = dt_buffer; + } + break; + case FTDM_CHANNEL_STATE_GENRING: + { + ftdm_sigmsg_t sig; + + send_caller_id(ftdmchan); + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GENERATE_RING_ON, NULL); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + sig.event_id = FTDM_SIGEVENT_PROGRESS; + ftdm_span_send_signal(ftdmchan->span, &sig); + + } + break; + case FTDM_CHANNEL_STATE_GET_CALLERID: + { + memset(&ftdmchan->caller_data, 0, sizeof(ftdmchan->caller_data)); + ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_CALLERID_DETECT, NULL); + continue; + } + break; + case FTDM_CHANNEL_STATE_RING: + { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_RING]); + indicate = 1; + + } + break; + case FTDM_CHANNEL_STATE_BUSY: + { + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CIRCUIT_CONGESTION; + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK) && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_BUSY]); + indicate = 1; + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + case FTDM_CHANNEL_STATE_ATTN: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK) && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_ATTN]); + indicate = 1; + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + default: + break; + } + } + + + if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALTONE || ftdmchan->state == FTDM_CHANNEL_STATE_COLLECT) { + if ((dlen = ftdm_channel_dequeue_dtmf(ftdmchan, dtmf + dtmf_offset, sizeof(dtmf) - strlen(dtmf)))) { + + if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALTONE) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_COLLECT); + collecting = 1; + } + dtmf_offset = strlen(dtmf); + last_digit = elapsed; + sig.event_id = FTDM_SIGEVENT_COLLECTED_DIGIT; + sig.raw_data = dtmf; + if (ftdm_span_send_signal(ftdmchan->span, &sig) == FTDM_BREAK) { + collecting = 0; + } + } + else if(!analog_data->max_dialstr) + { + last_digit = elapsed; + collecting = 0; + strcpy(dtmf, analog_data->hotline); + } + } + + + if (last_digit && (!collecting || ((elapsed - last_digit > analog_data->digit_timeout) || strlen(dtmf) >= analog_data->max_dialstr))) { + ftdm_log(FTDM_LOG_DEBUG, "Number obtained [%s]\n", dtmf); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_IDLE); + last_digit = 0; + collecting = 0; + } + + if (ftdm_channel_wait(ftdmchan, &flags, interval * 2) != FTDM_SUCCESS) { + continue; + } + + if (!(flags & FTDM_READ)) { + continue; + } + + if (ftdm_channel_read(ftdmchan, frame, &len) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "READ ERROR [%s]\n", ftdmchan->last_error); + goto done; + } + + if (ftdmchan->type == FTDM_CHAN_TYPE_FXO && ftdmchan->detected_tones[0]) { + ftdm_sigmsg_t sig; + int i; + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + sig.event_id = FTDM_SIGEVENT_TONE_DETECTED; + + for (i = 1; i < FTDM_TONEMAP_INVALID; i++) { + if (ftdmchan->detected_tones[i]) { + ftdm_log(FTDM_LOG_DEBUG, "Detected tone %s on %d:%d\n", ftdm_tonemap2str(i), ftdmchan->span_id, ftdmchan->chan_id); + sig.raw_data = &i; + ftdm_span_send_signal(ftdmchan->span, &sig); + } + } + + if (ftdmchan->detected_tones[FTDM_TONEMAP_BUSY] || + ftdmchan->detected_tones[FTDM_TONEMAP_FAIL1] || + ftdmchan->detected_tones[FTDM_TONEMAP_FAIL2] || + ftdmchan->detected_tones[FTDM_TONEMAP_FAIL3] || + ftdmchan->detected_tones[FTDM_TONEMAP_ATTN] + ) { + ftdm_log(FTDM_LOG_ERROR, "Failure indication detected!\n"); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else if (ftdmchan->detected_tones[FTDM_TONEMAP_DIAL]) { + if (ftdm_strlen_zero(ftdmchan->caller_data.dnis.digits)) { + ftdm_log(FTDM_LOG_ERROR, "No Digits to send!\n"); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else { + if (ftdm_channel_command(ftdmchan, FTDM_COMMAND_SEND_DTMF, ftdmchan->caller_data.dnis.digits) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Send Digits Failed [%s]\n", ftdmchan->last_error); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else { + state_counter = 0; + ftdmchan->needed_tones[FTDM_TONEMAP_RING] = 1; + ftdmchan->needed_tones[FTDM_TONEMAP_BUSY] = 1; + ftdmchan->needed_tones[FTDM_TONEMAP_FAIL1] = 1; + ftdmchan->needed_tones[FTDM_TONEMAP_FAIL2] = 1; + ftdmchan->needed_tones[FTDM_TONEMAP_FAIL3] = 1; + dial_timeout = ((ftdmchan->dtmf_on + ftdmchan->dtmf_off) * strlen(ftdmchan->caller_data.dnis.digits)) + 2000; + } + } + } else if (ftdmchan->detected_tones[FTDM_TONEMAP_RING]) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + } + + ftdm_channel_clear_detected_tones(ftdmchan); + } + + if ((ftdmchan->dtmf_buffer && ftdm_buffer_inuse(ftdmchan->dtmf_buffer)) || (ftdmchan->fsk_buffer && ftdm_buffer_inuse(ftdmchan->fsk_buffer))) { + //rlen = len; + //memset(frame, 0, len); + //ftdm_channel_write(ftdmchan, frame, sizeof(frame), &rlen); + continue; + } + + if (!indicate) { + continue; + } + + if (ftdmchan->type == FTDM_CHAN_TYPE_FXO && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK)) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_OFFHOOK, NULL); + } + + if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) { + len *= 2; + } + + rlen = ftdm_buffer_read_loop(dt_buffer, frame, len); + + if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) { + fio_codec_t codec_func = NULL; + + if (ftdmchan->native_codec == FTDM_CODEC_ULAW) { + codec_func = fio_slin2ulaw; + } else if (ftdmchan->native_codec == FTDM_CODEC_ALAW) { + codec_func = fio_slin2alaw; + } + + if (codec_func) { + status = codec_func(frame, sizeof(frame), &rlen); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!"); + goto done; + } + } + + ftdm_channel_write(ftdmchan, frame, sizeof(frame), &rlen); + } + + done: + + + if (ftdmchan->type == FTDM_CHAN_TYPE_FXO && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK)) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_ONHOOK, NULL); + } + + if (ftdmchan->type == FTDM_CHAN_TYPE_FXS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RINGING)) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GENERATE_RING_OFF, NULL); + } + + + closed_chan = ftdmchan; + ftdm_channel_close(&ftdmchan); + + ftdm_channel_command(closed_chan, FTDM_COMMAND_SET_NATIVE_CODEC, NULL); + + if (ts.buffer) { + teletone_destroy_session(&ts); + } + + if (dt_buffer) { + ftdm_buffer_destroy(&dt_buffer); + } + + if (closed_chan->state != FTDM_CHANNEL_STATE_DOWN) { + ftdm_set_state_locked(closed_chan, FTDM_CHANNEL_STATE_DOWN); + } + + ftdm_log(FTDM_LOG_DEBUG, "ANALOG CHANNEL %d:%d thread ended.\n", closed_chan->span_id, closed_chan->chan_id); + ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD); + + return NULL; +} + +/** + * \brief Processes freetdm event + * \param span Span on which the event was fired + * \param event Event to be treated + * \return Success or failure + */ +static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *event) +{ + ftdm_sigmsg_t sig; + ftdm_analog_data_t *analog_data = event->channel->span->signal_data; + int locked = 0; + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = event->channel->chan_id; + sig.span_id = event->channel->span_id; + sig.channel = event->channel; + + + ftdm_log(FTDM_LOG_DEBUG, "EVENT [%s][%d:%d] STATE [%s]\n", + ftdm_oob_event2str(event->enum_id), event->channel->span_id, event->channel->chan_id, ftdm_channel_state2str(event->channel->state)); + + ftdm_mutex_lock(event->channel->mutex); + locked++; + + switch(event->enum_id) { + case FTDM_OOB_RING_START: + { + if (event->channel->type != FTDM_CHAN_TYPE_FXO) { + ftdm_log(FTDM_LOG_ERROR, "Cannot get a RING_START event on a non-fxo channel, please check your config.\n"); + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DOWN); + goto end; + } + if (!event->channel->ring_count && (event->channel->state == FTDM_CHANNEL_STATE_DOWN && !ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD))) { + if (ftdm_test_flag(analog_data, FTDM_ANALOG_CALLERID)) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_GET_CALLERID); + } else { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_IDLE); + } + event->channel->ring_count = 1; + ftdm_mutex_unlock(event->channel->mutex); + locked = 0; + ftdm_thread_create_detached(ftdm_analog_channel_run, event->channel); + } else { + event->channel->ring_count++; + } + } + break; + case FTDM_OOB_ONHOOK: + { + if (ftdm_test_flag(event->channel, FTDM_CHANNEL_RINGING)) { + ftdm_channel_command(event->channel, FTDM_COMMAND_GENERATE_RING_OFF, NULL); + } + + if (event->channel->state != FTDM_CHANNEL_STATE_DOWN) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DOWN); + } + + } + break; + case FTDM_OOB_FLASH: + { + if (event->channel->state == FTDM_CHANNEL_STATE_CALLWAITING) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_UP); + ftdm_clear_flag_locked(event->channel, FTDM_CHANNEL_STATE_CHANGE); + ftdm_clear_flag_locked(event->channel->span, FTDM_SPAN_STATE_CHANGE); + event->channel->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0; + } + + ftdm_channel_rotate_tokens(event->channel); + + if (ftdm_test_flag(event->channel, FTDM_CHANNEL_HOLD) && event->channel->token_count != 1) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_UP); + } else { + sig.event_id = FTDM_SIGEVENT_FLASH; + ftdm_span_send_signal(span, &sig); + } + } + break; + case FTDM_OOB_OFFHOOK: + { + if (event->channel->type == FTDM_CHAN_TYPE_FXS) { + if (ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD)) { + if (ftdm_test_flag(event->channel, FTDM_CHANNEL_RINGING)) { + ftdm_channel_command(event->channel, FTDM_COMMAND_GENERATE_RING_OFF, NULL); + } + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_UP); + } else { + if(!analog_data->max_dialstr) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_COLLECT); + } else { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DIALTONE); + } + ftdm_mutex_unlock(event->channel->mutex); + locked = 0; + ftdm_thread_create_detached(ftdm_analog_channel_run, event->channel); + } + } else { + if (!ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD)) { + if (ftdm_test_flag(event->channel, FTDM_CHANNEL_OFFHOOK)) { + ftdm_channel_command(event->channel, FTDM_COMMAND_ONHOOK, NULL); + } + } + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DOWN); + } + } + } + + end: + + if (locked) { + ftdm_mutex_unlock(event->channel->mutex); + } + return FTDM_SUCCESS; +} + +/** + * \brief Main thread function for analog span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *ftdm_analog_run(ftdm_thread_t *me, void *obj) +{ + ftdm_span_t *span = (ftdm_span_t *) obj; + ftdm_analog_data_t *analog_data = span->signal_data; + int errs = 0; + + ftdm_log(FTDM_LOG_DEBUG, "ANALOG thread starting.\n"); + + while(ftdm_running() && ftdm_test_flag(analog_data, FTDM_ANALOG_RUNNING)) { + int waitms = 1000; + ftdm_status_t status; + + if ((status = ftdm_span_poll_event(span, waitms)) != FTDM_FAIL) { + errs = 0; + } + + switch(status) { + case FTDM_SUCCESS: + { + ftdm_event_t *event; + while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) { + if (event->enum_id == FTDM_OOB_NOOP) { + continue; + } + if (process_event(span, event) != FTDM_SUCCESS) { + goto end; + } + } + } + break; + case FTDM_FAIL: + { + ftdm_log(FTDM_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error); + if (++errs > 300) { + ftdm_log(FTDM_LOG_CRIT, "Too Many Errors!\n"); + goto end; + } + } + break; + default: + break; + } + + } + + end: + + ftdm_clear_flag(analog_data, FTDM_ANALOG_RUNNING); + + ftdm_log(FTDM_LOG_DEBUG, "ANALOG thread ending.\n"); + + return NULL; +} + +/** + * \brief FreeTDM analog signaling module initialisation + * \return Success + */ +static FIO_SIG_LOAD_FUNCTION(ftdm_analog_init) +{ + return FTDM_SUCCESS; +} + +/** + * \brief FreeTDM analog signaling module definition + */ +EX_DECLARE_DATA ftdm_module_t ftdm_module = { + "analog", + NULL, + NULL, + ftdm_analog_init, + ftdm_analog_configure_span, + NULL +}; + + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ozmod_analog.2005.vcproj b/libs/freetdm/src/ftmod/ftmod_analog/ozmod_analog.2005.vcproj new file mode 100644 index 0000000000..7a1cf53b20 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_analog/ozmod_analog.2005.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h b/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h new file mode 100644 index 0000000000..83d5bb31d9 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2008, Anthony Minessale II + * 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. + * + * Contributor(s): + * + * John Wehle (john@feith.com) + * + */ + +#ifndef FTDM_ANALOG_EM_H +#define FTDM_ANALOG_EM_H +#include "freetdm.h" + +#define MAX_DIALSTRING 256 + +typedef enum { + FTDM_ANALOG_EM_RUNNING = (1 << 0) +} ftdm_analog_em_flag_t; + + +struct ftdm_analog_data { + uint32_t flags; + uint32_t max_dialstr; + uint32_t digit_timeout; +}; + +static void *ftdm_analog_em_run(ftdm_thread_t *me, void *obj); +typedef struct ftdm_analog_data ftdm_analog_em_data_t; + +#endif + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.2008.vcproj new file mode 100644 index 0000000000..8ad183797a --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.2008.vcproj @@ -0,0 +1,353 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c new file mode 100644 index 0000000000..bb11798c68 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c @@ -0,0 +1,707 @@ +/* + * Copyright (c) 2008, Anthony Minessale II + * 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. + * + * Contributor(s): + * + * John Wehle (john@feith.com) + * + */ + +#include "freetdm.h" +#include "ftdm_analog_em.h" + +#ifndef localtime_r +struct tm * localtime_r(const time_t *clock, struct tm *result); +#endif + +static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj); + +/** + * \brief Starts an EM channel thread (outgoing call) + * \param ftdmchan Channel to initiate call on + * \return Success or failure + * + * Initialises state, starts tone progress detection and runs the channel in a new a thread. + */ +static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_em_outgoing_call) +{ + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK) && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { + ftdm_channel_clear_needed_tones(ftdmchan); + ftdm_channel_clear_detected_tones(ftdmchan); + + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); + + ftdm_channel_command(ftdmchan, FTDM_COMMAND_OFFHOOK, NULL); + ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_PROGRESS_DETECT, NULL); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALING); + ftdm_thread_create_detached(ftdm_analog_em_channel_run, ftdmchan); + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +/** + * \brief Starts an EM span thread (monitor) + * \param span Span to monitor + * \return Success or failure + */ +static ftdm_status_t ftdm_analog_em_start(ftdm_span_t *span) +{ + ftdm_analog_em_data_t *analog_data = span->signal_data; + ftdm_set_flag(analog_data, FTDM_ANALOG_EM_RUNNING); + return ftdm_thread_create_detached(ftdm_analog_em_run, span); +} + +/** + * \brief Initialises an EM span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span) +//ftdm_status_t ftdm_analog_em_configure_span(ftdm_span_t *span, char *tonemap, uint32_t digit_timeout, uint32_t max_dialstr, fio_signal_cb_t sig_cb) +{ + ftdm_analog_em_data_t *analog_data; + const char *tonemap = "us"; + uint32_t digit_timeout = 10; + uint32_t max_dialstr = 11; + const char *var, *val; + int *intval; + + assert(sig_cb != NULL); + + if (span->signal_type) { + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling."); + return FTDM_FAIL; + } + + analog_data = ftdm_malloc(sizeof(*analog_data)); + assert(analog_data != NULL); + memset(analog_data, 0, sizeof(*analog_data)); + + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "tonemap")) { + if (!(val = va_arg(ap, char *))) { + break; + } + tonemap = val; + } else if (!strcasecmp(var, "digit_timeout")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + digit_timeout = *intval; + } else if (!strcasecmp(var, "max_dialstr")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + max_dialstr = *intval; + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return FTDM_FAIL; + } + } + + + if (digit_timeout < 2000 || digit_timeout > 10000) { + digit_timeout = 2000; + } + + if (max_dialstr < 2 || max_dialstr > MAX_DIALSTRING) { + ftdm_log(FTDM_LOG_ERROR, "Invalid max_dialstr, setting to %d\n", MAX_DIALSTRING); + max_dialstr = MAX_DIALSTRING; + } + + span->start = ftdm_analog_em_start; + analog_data->digit_timeout = digit_timeout; + analog_data->max_dialstr = max_dialstr; + span->signal_cb = sig_cb; + span->signal_type = FTDM_SIGTYPE_ANALOG; + span->signal_data = analog_data; + span->outgoing_call = analog_em_outgoing_call; + ftdm_span_load_tones(span, tonemap); + + return FTDM_SUCCESS; + +} + +/** + * \brief Retrieves tone generation output to be sent + * \param ts Teletone generator + * \param map Tone map + * \return -1 on error, 0 on success + */ +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + ftdm_buffer_t *dt_buffer = ts->user_data; + int wrote; + + if (!dt_buffer) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + ftdm_buffer_write(dt_buffer, ts->buffer, wrote * 2); + return 0; +} + +/** + * \brief Main thread function for EM channel (outgoing call) + * \param me Current thread + * \param obj Channel to run in this thread + */ +static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) +{ + ftdm_channel_t *ftdmchan = (ftdm_channel_t *) obj; + ftdm_buffer_t *dt_buffer = NULL; + teletone_generation_session_t ts; + uint8_t frame[1024]; + ftdm_size_t len, rlen; + ftdm_tone_type_t tt = FTDM_TONE_DTMF; + char dtmf[128] = ""; + ftdm_size_t dtmf_offset = 0; + ftdm_analog_em_data_t *analog_data = ftdmchan->span->signal_data; + ftdm_channel_t *closed_chan; + uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = 30000; + ftdm_sigmsg_t sig; + ftdm_status_t status; + + ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread starting.\n"); + + ts.buffer = NULL; + + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "OPEN ERROR [%s]\n", ftdmchan->last_error); + goto done; + } + + if (ftdm_buffer_create(&dt_buffer, 1024, 3192, 0) != FTDM_SUCCESS) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "memory error!"); + ftdm_log(FTDM_LOG_ERROR, "MEM ERROR\n"); + goto done; + } + + if (ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_DTMF_DETECT, &tt) != FTDM_SUCCESS) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "error initilizing tone detector!"); + ftdm_log(FTDM_LOG_ERROR, "TONE ERROR\n"); + goto done; + } + + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INTHREAD); + teletone_init_session(&ts, 0, teletone_handler, dt_buffer); + ts.rate = 8000; +#if 0 + ts.debug = 1; + ts.debug_stream = stdout; +#endif + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval); + ftdm_buffer_set_loops(dt_buffer, -1); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + + assert(interval != 0); + + while (ftdm_running() && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { + ftdm_wait_flag_t flags = FTDM_READ; + ftdm_size_t dlen = 0; + + len = sizeof(frame); + + elapsed += interval; + state_counter += interval; + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { + switch(ftdmchan->state) { + case FTDM_CHANNEL_STATE_DIALING: + { + if (! ftdmchan->needed_tones[FTDM_TONEMAP_RING] + && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_WINK)) { + if (ftdm_strlen_zero(ftdmchan->caller_data.dnis.digits)) { + ftdm_log(FTDM_LOG_ERROR, "No Digits to send!\n"); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else { + if (ftdm_channel_command(ftdmchan, FTDM_COMMAND_SEND_DTMF, ftdmchan->caller_data.dnis.digits) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Send Digits Failed [%s]\n", ftdmchan->last_error); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else { + state_counter = 0; + ftdmchan->needed_tones[FTDM_TONEMAP_RING] = 1; + ftdmchan->needed_tones[FTDM_TONEMAP_BUSY] = 1; + ftdmchan->needed_tones[FTDM_TONEMAP_FAIL1] = 1; + ftdmchan->needed_tones[FTDM_TONEMAP_FAIL2] = 1; + ftdmchan->needed_tones[FTDM_TONEMAP_FAIL3] = 1; + dial_timeout = ((ftdmchan->dtmf_on + ftdmchan->dtmf_off) * strlen(ftdmchan->caller_data.dnis.digits)) + 2000; + } + } + break; + } + if (state_counter > dial_timeout) { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_WINK)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + } + } + } + break; + case FTDM_CHANNEL_STATE_DIALTONE: + { + if (state_counter > 10000) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } + } + break; + case FTDM_CHANNEL_STATE_BUSY: + { + if (state_counter > 20000) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_ATTN); + } + } + break; + case FTDM_CHANNEL_STATE_ATTN: + { + if (state_counter > 20000) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + case FTDM_CHANNEL_STATE_HANGUP: + { + if (state_counter > 500) { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK) && + (ftdmchan->last_state == FTDM_CHANNEL_STATE_RING || ftdmchan->last_state == FTDM_CHANNEL_STATE_DIALTONE + || ftdmchan->last_state >= FTDM_CHANNEL_STATE_IDLE)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else { + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING; + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + } + break; + case FTDM_CHANNEL_STATE_UP: + case FTDM_CHANNEL_STATE_IDLE: + { + ftdm_sleep(interval); + continue; + } + break; + case FTDM_CHANNEL_STATE_DOWN: + { + goto done; + } + break; + default: + break; + } + } else { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); + ftdm_channel_complete_state(ftdmchan); + indicate = 0; + state_counter = 0; + + 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)); + switch(ftdmchan->state) { + case FTDM_CHANNEL_STATE_UP: + { + ftdm_channel_use(ftdmchan); + ftdm_channel_clear_needed_tones(ftdmchan); + ftdm_channel_flush_dtmf(ftdmchan); + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK)) { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_OFFHOOK, NULL); + } + + sig.event_id = FTDM_SIGEVENT_UP; + + ftdm_span_send_signal(ftdmchan->span, &sig); + continue; + } + break; + case FTDM_CHANNEL_STATE_DIALING: + { + ftdm_channel_use(ftdmchan); + } + break; + case FTDM_CHANNEL_STATE_IDLE: + { + ftdm_channel_use(ftdmchan); + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + ftdm_set_string(ftdmchan->caller_data.dnis.digits, ftdmchan->chan_number); + } else { + ftdm_set_string(ftdmchan->caller_data.dnis.digits, dtmf); + } + + sig.event_id = FTDM_SIGEVENT_START; + + ftdm_span_send_signal(ftdmchan->span, &sig); + continue; + } + break; + case FTDM_CHANNEL_STATE_DOWN: + { + sig.event_id = FTDM_SIGEVENT_STOP; + ftdm_span_send_signal(ftdmchan->span, &sig); + goto done; + } + break; + case FTDM_CHANNEL_STATE_DIALTONE: + { + memset(&ftdmchan->caller_data, 0, sizeof(ftdmchan->caller_data)); + *dtmf = '\0'; + dtmf_offset = 0; + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_DIAL]); + indicate = 1; + + ftdm_channel_command(ftdmchan, FTDM_COMMAND_WINK, NULL); + } + break; + case FTDM_CHANNEL_STATE_RING: + { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_RING]); + indicate = 1; + } + break; + case FTDM_CHANNEL_STATE_BUSY: + { + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CIRCUIT_CONGESTION; + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK) && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_BUSY]); + indicate = 1; + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + case FTDM_CHANNEL_STATE_ATTN: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OFFHOOK) && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_ATTN]); + indicate = 1; + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + default: + break; + } + } + + + if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALTONE || ftdmchan->state == FTDM_CHANNEL_STATE_COLLECT) { + if ((dlen = ftdm_channel_dequeue_dtmf(ftdmchan, dtmf + dtmf_offset, sizeof(dtmf) - strlen(dtmf)))) { + + if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALTONE) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_COLLECT); + collecting = 1; + } + dtmf_offset = strlen(dtmf); + last_digit = elapsed; + sig.event_id = FTDM_SIGEVENT_COLLECTED_DIGIT; + sig.raw_data = dtmf; + if (ftdm_span_send_signal(ftdmchan->span, &sig) == FTDM_BREAK) { + collecting = 0; + } + } + } + + + if (last_digit && (!collecting || ((elapsed - last_digit > analog_data->digit_timeout) || strlen(dtmf) > analog_data->max_dialstr))) { + ftdm_log(FTDM_LOG_DEBUG, "Number obtained [%s]\n", dtmf); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_IDLE); + last_digit = 0; + collecting = 0; + } + + if (ftdm_channel_wait(ftdmchan, &flags, interval * 2) != FTDM_SUCCESS) { + continue; + } + + if (!(flags & FTDM_READ)) { + continue; + } + + if (ftdm_channel_read(ftdmchan, frame, &len) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "READ ERROR [%s]\n", ftdmchan->last_error); + goto done; + } + + if (ftdmchan->detected_tones[0]) { + ftdm_sigmsg_t sig; + int i; + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + sig.event_id = FTDM_SIGEVENT_TONE_DETECTED; + + for (i = 1; i < FTDM_TONEMAP_INVALID; i++) { + if (ftdmchan->detected_tones[i]) { + ftdm_log(FTDM_LOG_DEBUG, "Detected tone %s on %d:%d\n", ftdm_tonemap2str(i), ftdmchan->span_id, ftdmchan->chan_id); + sig.raw_data = &i; + ftdm_span_send_signal(ftdmchan->span, &sig); + } + } + + if (ftdmchan->detected_tones[FTDM_TONEMAP_BUSY] || + ftdmchan->detected_tones[FTDM_TONEMAP_FAIL1] || + ftdmchan->detected_tones[FTDM_TONEMAP_FAIL2] || + ftdmchan->detected_tones[FTDM_TONEMAP_FAIL3] || + ftdmchan->detected_tones[FTDM_TONEMAP_ATTN] + ) { + ftdm_log(FTDM_LOG_ERROR, "Failure indication detected!\n"); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_BUSY); + } else if (ftdmchan->detected_tones[FTDM_TONEMAP_RING]) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + } + + ftdm_channel_clear_detected_tones(ftdmchan); + } + + if ((ftdmchan->dtmf_buffer && ftdm_buffer_inuse(ftdmchan->dtmf_buffer))) { + rlen = len; + memset(frame, 0, len); + ftdm_channel_write(ftdmchan, frame, sizeof(frame), &rlen); + continue; + } + + if (!indicate) { + continue; + } + + if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) { + len *= 2; + } + + rlen = ftdm_buffer_read_loop(dt_buffer, frame, len); + + if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) { + fio_codec_t codec_func = NULL; + + if (ftdmchan->native_codec == FTDM_CODEC_ULAW) { + codec_func = fio_slin2ulaw; + } else if (ftdmchan->native_codec == FTDM_CODEC_ALAW) { + codec_func = fio_slin2alaw; + } + + if (codec_func) { + status = codec_func(frame, sizeof(frame), &rlen); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!"); + goto done; + } + } + + ftdm_channel_write(ftdmchan, frame, sizeof(frame), &rlen); + } + + done: + + ftdm_channel_command(ftdmchan, FTDM_COMMAND_ONHOOK, NULL); + + closed_chan = ftdmchan; + ftdm_channel_close(&ftdmchan); + + ftdm_channel_command(closed_chan, FTDM_COMMAND_SET_NATIVE_CODEC, NULL); + + if (ts.buffer) { + teletone_destroy_session(&ts); + } + + if (dt_buffer) { + ftdm_buffer_destroy(&dt_buffer); + } + + ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD); + + ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread ended.\n"); + + return NULL; +} + +/** + * \brief Processes EM events coming from ftdmtel/dahdi + * \param span Span on which the event was fired + * \param event Event to be treated + * \return Success or failure + */ +static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *event) +{ + ftdm_sigmsg_t sig; + int locked = 0; + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = event->channel->chan_id; + sig.span_id = event->channel->span_id; + sig.channel = event->channel; + + + ftdm_log(FTDM_LOG_DEBUG, "EVENT [%s][%d:%d] STATE [%s]\n", + ftdm_oob_event2str(event->enum_id), event->channel->span_id, event->channel->chan_id, ftdm_channel_state2str(event->channel->state)); + + ftdm_mutex_lock(event->channel->mutex); + locked++; + + switch(event->enum_id) { + case FTDM_OOB_ONHOOK: + { + if (event->channel->state != FTDM_CHANNEL_STATE_DOWN) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DOWN); + } + + } + break; + case FTDM_OOB_OFFHOOK: + { + if (ftdm_test_flag(event->channel, FTDM_CHANNEL_INTHREAD)) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_UP); + } else { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DIALTONE); + ftdm_mutex_unlock(event->channel->mutex); + locked = 0; + ftdm_thread_create_detached(ftdm_analog_em_channel_run, event->channel); + } + break; + } + case FTDM_OOB_WINK: + { + if (event->channel->state != FTDM_CHANNEL_STATE_DIALING) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_DOWN); + } else { + ftdm_set_flag_locked(event->channel, FTDM_CHANNEL_WINK); + } + + } + break; + } + if (locked) { + ftdm_mutex_unlock(event->channel->mutex); + } + return FTDM_SUCCESS; +} + +/** + * \brief Main thread function for EM span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *ftdm_analog_em_run(ftdm_thread_t *me, void *obj) +{ + ftdm_span_t *span = (ftdm_span_t *) obj; + ftdm_analog_em_data_t *analog_data = span->signal_data; + + ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM thread starting.\n"); + + while(ftdm_running() && ftdm_test_flag(analog_data, FTDM_ANALOG_EM_RUNNING)) { + int waitms = 10; + ftdm_status_t status; + + status = ftdm_span_poll_event(span, waitms); + + switch(status) { + case FTDM_SUCCESS: + { + ftdm_event_t *event; + while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) { + if (event->enum_id == FTDM_OOB_NOOP) { + continue; + } + if (process_event(span, event) != FTDM_SUCCESS) { + goto end; + } + } + } + break; + case FTDM_FAIL: + { + ftdm_log(FTDM_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error); + } + break; + default: + break; + } + + } + + end: + + ftdm_clear_flag(analog_data, FTDM_ANALOG_EM_RUNNING); + + ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM thread ending.\n"); + + return NULL; +} + +/** + * \brief FreeTDM analog EM module initialisation + * \return Success + */ +static FIO_SIG_LOAD_FUNCTION(ftdm_analog_em_init) +{ + return FTDM_SUCCESS; +} + +/** + * \brief FreeTDM analog EM module definition + */ +EX_DECLARE_DATA ftdm_module_t ftdm_module = { + "analog_em", + NULL, + NULL, + ftdm_analog_em_init, + ftdm_analog_em_configure_span, + NULL +}; + + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ozmod_analog_em.2005.vcproj b/libs/freetdm/src/ftmod/ftmod_analog_em/ozmod_analog_em.2005.vcproj new file mode 100644 index 0000000000..9ecbadc232 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ozmod_analog_em.2005.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_isdn/ftdm_isdn.h b/libs/freetdm/src/ftmod/ftmod_isdn/ftdm_isdn.h new file mode 100644 index 0000000000..7168abeb3b --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_isdn/ftdm_isdn.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 FTDM_ISDN_H +#define FTDM_ISDN_H +#include "freetdm.h" + +#define DEFAULT_DIGIT_TIMEOUT 10000 /* default overlap timeout: 10 seconds */ + + +typedef enum { + FTDM_ISDN_OPT_NONE = 0, + FTDM_ISDN_OPT_SUGGEST_CHANNEL = (1 << 0), + FTDM_ISDN_OPT_OMIT_DISPLAY_IE = (1 << 1), /*!< Do not send Caller name in outgoing SETUP message (= Display IE) */ + FTDM_ISDN_OPT_DISABLE_TONES = (1 << 2), /*!< Disable tone generating thread (NT mode) */ + + FTDM_ISDN_OPT_MAX = (2 << 0) +} ftdm_isdn_opts_t; + +typedef enum { + FTDM_ISDN_RUNNING = (1 << 0), + FTDM_ISDN_TONES_RUNNING = (1 << 1), + FTDM_ISDN_STOP = (1 << 2) +} ftdm_isdn_flag_t; + + +struct ftdm_isdn_data { + Q921Data_t q921; + Q931_TrunkInfo_t q931; + ftdm_channel_t *dchan; + ftdm_channel_t *dchans[2]; + struct ftdm_sigmsg sigmsg; + uint32_t flags; + int32_t mode; + int32_t digit_timeout; + ftdm_isdn_opts_t opts; + ftdm_caller_data_t *outbound_crv[32768]; + ftdm_channel_t *channels_local_crv[32768]; + ftdm_channel_t *channels_remote_crv[32768]; +}; + +typedef struct ftdm_isdn_data ftdm_isdn_data_t; + + +/* b-channel private data */ +struct ftdm_isdn_bchan_data +{ + L2ULONG digit_timeout; +}; + +typedef struct ftdm_isdn_bchan_data ftdm_isdn_bchan_data_t; + + +#endif + +/* 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: + */ + diff --git a/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.2008.vcproj new file mode 100644 index 0000000000..0177d4246a --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.2008.vcproj @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c b/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c new file mode 100644 index 0000000000..3445599d13 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_isdn/ftmod_isdn.c @@ -0,0 +1,2420 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + */ + +/** + * Workaround for missing u_int / u_short types on solaris + */ +#if defined(HAVE_LIBPCAP) && defined(__SunOS) +#define __EXTENSIONS__ +#endif + +#include "freetdm.h" +#include "Q931.h" +#include "Q921.h" +#ifdef WIN32 +#include +#else +#include +#endif + +#include "ftdm_isdn.h" + +#define LINE "--------------------------------------------------------------------------------" +//#define IODEBUG + +/* helper macros */ +#define FTDM_SPAN_IS_BRI(x) ((x)->trunk_type == FTDM_TRUNK_BRI || (x)->trunk_type == FTDM_TRUNK_BRI_PTMP) +#define FTDM_SPAN_IS_NT(x) (((ftdm_isdn_data_t *)(x)->signal_data)->mode == Q921_NT) + + +#ifdef HAVE_LIBPCAP +/*-------------------------------------------------------------------------*/ +/*Q931ToPcap functions*/ +#include +#endif + +#define SNAPLEN 1522 +#define MAX_ETHER_PAYLOAD_SIZE 1500 +#define MIN_ETHER_PAYLOAD_SIZE 42 +#define SIZE_ETHERNET 18 +#define VLANID_OFFSET 15 +#define SIZE_IP 20 +#define SIZE_TCP 20 +#define SIZE_TPKT 4 +#define SIZE_ETHERNET_CRC 4 +#define OVERHEAD SIZE_ETHERNET+SIZE_IP+SIZE_TCP+SIZE_TPKT +#define MAX_Q931_SIZE MAX_ETHER_PAYLOAD_SIZE-SIZE_IP-SIZE_TCP-SIZE_TPKT +#define TPKT_SIZE_OFFSET SIZE_ETHERNET+SIZE_IP+SIZE_TCP+2 +#define IP_SIZE_OFFSET SIZE_ETHERNET+2 +#define TCP_SEQ_OFFSET SIZE_ETHERNET+SIZE_IP+4 + +#ifdef HAVE_LIBPCAP +/*Some globals*/ +unsigned long pcapfilesize = 0; +unsigned long tcp_next_seq_no_send = 0; +unsigned long tcp_next_seq_no_rec = 0; +pcap_dumper_t *pcapfile = NULL; +struct pcap_pkthdr pcaphdr; +pcap_t *pcaphandle = NULL; +char *pcapfn = NULL; +int do_q931ToPcap= 0; + +/*Predefined Ethernet Frame with Q931-over-IP encapsulated - From remote TDM host to FreeSWITCH*/ +L3UCHAR recFrame[SNAPLEN]= { + /*IEEE 802.3 VLAN 802.1q Ethernet Frame Header*/ + 2,0,1,0xAA,0xAA,0xAA,2,0,1,0xBB,0xBB,0xBB,0x81,0,0xE0,0,0x08,0, + /*IPv4 Header (minimal size; no options)*/ + 0x45,0,0,44,0,0,0,0,64,6,0,0,2,2,2,2,1,1,1,1, + /*TCP-Header*/ + 0,0x66,0,0x66,0,0,0,0,0,0,0,0,0x50,0,0,1,0,0,0,0, + /*TPKT-Header RFC 1006*/ + 3,0,0,0 + }; + +/*Predefined Ethernet Frame with Q931-over-IP encapsulated - Frome FreeSWITCH to remote TDM host*/ +L3UCHAR sendFrame[SNAPLEN]= { + /*IEEE 802.3 VLAN 802.1q Ethernet Frame Header*/ + 2,0,1,0xBB,0xBB,0xBB,2,0,1,0xAA,0xAA,0xAA,0x81,0,0xE0,0,0x08,0, + /*IPv4 Header (minimal size; no options)*/ + 0x45,0,0,44,0,0,0,0,64,6,0,0,1,1,1,1,2,2,2,2, + /*TCP-Header*/ + 0,0x66,0,0x66,0,0,0,0,0,0,0,0,0x50,0,0,1,0,0,0,0, + /*TPKT-Header RFC 1006*/ + 3,0,0,0 + }; + +/** + * \brief Opens a pcap file for capture + * \return Success or failure + */ +static ftdm_status_t openPcapFile(void) +{ + if(!pcaphandle) + { + pcaphandle = pcap_open_dead(DLT_EN10MB, SNAPLEN); + if (!pcaphandle) + { + ftdm_log(FTDM_LOG_ERROR, "Can't open pcap session: (%s)\n", pcap_geterr(pcaphandle)); + return FTDM_FAIL; + } + } + + if(!pcapfile){ + /* Open the dump file */ + if(!(pcapfile=pcap_dump_open(pcaphandle, pcapfn))){ + ftdm_log(FTDM_LOG_ERROR, "Error opening output file (%s)\n", pcap_geterr(pcaphandle)); + return FTDM_FAIL; + } + } + else{ + ftdm_log(FTDM_LOG_WARNING, "Pcap file is already open!\n"); + return FTDM_FAIL; + } + + ftdm_log(FTDM_LOG_DEBUG, "Pcap file '%s' successfully opened!\n", pcapfn); + + pcaphdr.ts.tv_sec = 0; + pcaphdr.ts.tv_usec = 0; + pcapfilesize = 24; /*current pcap file header seems to be 24 bytes*/ + tcp_next_seq_no_send = 0; + tcp_next_seq_no_rec = 0; + + return FTDM_SUCCESS; +} + +/** + * \brief Closes a pcap file + * \return Success + */ +static ftdm_status_t closePcapFile(void) +{ + if (pcapfile) { + pcap_dump_close(pcapfile); + if (pcaphandle) pcap_close(pcaphandle); + + ftdm_log(FTDM_LOG_DEBUG, "Pcap file closed! File size is %lu bytes.\n", pcapfilesize); + + pcaphdr.ts.tv_sec = 0; + pcaphdr.ts.tv_usec = 0; + pcapfile = NULL; + pcaphandle = NULL; + pcapfilesize = 0; + tcp_next_seq_no_send = 0; + tcp_next_seq_no_rec = 0; + } + + /*We have allways success with this? I think so*/ + return FTDM_SUCCESS; +} + +/** + * \brief Writes a Q931 packet to a pcap file + * \return Success or failure + */ +static ftdm_status_t writeQ931PacketToPcap(L3UCHAR* q931buf, L3USHORT q931size, L3ULONG span_id, L3USHORT direction) +{ + L3UCHAR *frame = NULL; + struct timeval ts; + u_char spanid = (u_char)span_id; + unsigned long *tcp_next_seq_no = NULL; + + spanid=span_id; + + /*The total length of the ethernet frame generated by this function has a min length of 66 + so we don't have to care about padding :)*/ + + + /*FS is sending the packet*/ + if(direction==0){ + frame=sendFrame; + tcp_next_seq_no = &tcp_next_seq_no_send; + } + /*FS is receiving the packet*/ + else{ + frame=recFrame; + tcp_next_seq_no = &tcp_next_seq_no_rec; + } + + /*Set spanid in VLAN-ID tag*/ + frame[VLANID_OFFSET] = spanid; + + /*** Write sent packet ***/ + if(q931size > MAX_Q931_SIZE) + { + /*WARNING*/ + ftdm_log(FTDM_LOG_WARNING, "Q931 packet size is too big (%u)! Ignoring it!\n", q931size); + return FTDM_FAIL; + } + + /*Copy q931 buffer into frame*/ + memcpy(frame+OVERHEAD,q931buf,q931size); + + /*Store TCP sequence number in TCP header*/ + frame[TCP_SEQ_OFFSET]=(*tcp_next_seq_no>>24)&0xFF; + frame[TCP_SEQ_OFFSET+1]=(*tcp_next_seq_no>>16)&0xFF; + frame[TCP_SEQ_OFFSET+2]=(*tcp_next_seq_no>>8)&0xFF; + frame[TCP_SEQ_OFFSET+3]=*tcp_next_seq_no & 0xFF; + + /*Store size of TPKT packet*/ + q931size+=4; + frame[TPKT_SIZE_OFFSET]=(q931size>>8)&0xFF; + frame[TPKT_SIZE_OFFSET+1]=q931size&0xFF; + + /*Calc next TCP sequence number*/ + *tcp_next_seq_no+=q931size; + + /*Store size of IP packet*/ + q931size+=SIZE_IP+SIZE_TCP; + frame[IP_SIZE_OFFSET]=(q931size>>8)&0xFF; + frame[IP_SIZE_OFFSET+1]=q931size&0xFF; + + pcaphdr.caplen = SIZE_ETHERNET+SIZE_ETHERNET_CRC+q931size; + pcaphdr.len = pcaphdr.caplen; + + /* Set Timestamp */ + /* Get Time in ms. usecs would be better ... */ + gettimeofday(&ts, NULL); + /*Write it into packet header*/ + pcaphdr.ts.tv_sec = ts.tv_sec; + pcaphdr.ts.tv_usec = ts.tv_usec; + + pcap_dump((u_char*)pcapfile, &pcaphdr, frame); + pcap_dump_flush(pcapfile); + + /*Maintain pcap file size*/ + pcapfilesize+=pcaphdr.caplen; + pcapfilesize+=sizeof(struct pcap_pkthdr); + + ftdm_log(FTDM_LOG_DEBUG, "Added %u bytes to pcap file. File size is now %lu, \n", q931size, pcapfilesize); + + return FTDM_SUCCESS; +} + +#endif + +/** + * \brief Unloads pcap IO + * \return Success or failure + */ +static FIO_IO_UNLOAD_FUNCTION(close_pcap) +{ +#ifdef HAVE_LIBPCAP + return closePcapFile(); +#else + return FTDM_SUCCESS; +#endif +} + +/*Q931ToPcap functions DONE*/ +/*-------------------------------------------------------------------------*/ + +/** + * \brief Gets current time + * \return Current time (in ms) + */ +static L2ULONG ftdm_time_now(void) +{ + return (L2ULONG)ftdm_current_time_in_ms(); +} + +/** + * \brief Initialises an ISDN channel (outgoing call) + * \param ftdmchan Channel to initiate call on + * \return Success or failure + */ +static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) +{ + ftdm_status_t status = FTDM_SUCCESS; + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALING); + return status; +} + +/** + * \brief Requests an ISDN channel on a span (outgoing call) + * \param span Span where to get a channel + * \param chan_id Specific channel to get (0 for any) + * \param direction Call direction (inbound, outbound) + * \param caller_data Caller information + * \param ftdmchan Channel to initialise + * \return Success or failure + */ +static FIO_CHANNEL_REQUEST_FUNCTION(isdn_channel_request) +{ + Q931mes_Generic *gen = (Q931mes_Generic *) caller_data->raw_data; + Q931ie_BearerCap BearerCap; + Q931ie_ChanID ChanID = { 0 }; + Q931ie_CallingNum CallingNum; + Q931ie_CallingNum *ptrCallingNum; + Q931ie_CalledNum CalledNum; + Q931ie_CalledNum *ptrCalledNum; + Q931ie_Display Display, *ptrDisplay; + Q931ie_HLComp HLComp; /* High-Layer Compatibility IE */ + Q931ie_ProgInd Progress; /* Progress Indicator IE */ + ftdm_status_t status = FTDM_FAIL; + ftdm_isdn_data_t *isdn_data = span->signal_data; + int sanity = 60000; + int codec = 0; + + /* + * get codec type + */ + ftdm_channel_command(span->channels[chan_id], FTDM_COMMAND_GET_NATIVE_CODEC, &codec); + + /* + * Q.931 Setup Message + */ + Q931InitMesGeneric(gen); + gen->MesType = Q931mes_SETUP; + gen->CRVFlag = 0; /* outgoing call */ + + /* + * Bearer Capability IE + */ + Q931InitIEBearerCap(&BearerCap); + BearerCap.CodStand = Q931_CODING_ITU; /* ITU-T = 0, ISO/IEC = 1, National = 2, Network = 3 */ + BearerCap.ITC = Q931_ITC_SPEECH; /* Speech */ + BearerCap.TransMode = 0; /* Circuit = 0, Packet = 1 */ + BearerCap.ITR = Q931_ITR_64K; /* 64k */ + BearerCap.Layer1Ident = 1; + BearerCap.UIL1Prot = (codec == FTDM_CODEC_ALAW) ? Q931_UIL1P_G711A : Q931_UIL1P_G711U; /* U-law = 2, A-law = 3 */ + gen->BearerCap = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &BearerCap); + + /* + * Channel ID IE + */ + Q931InitIEChanID(&ChanID); + ChanID.IntType = FTDM_SPAN_IS_BRI(span) ? 0 : 1; /* PRI = 1, BRI = 0 */ + + if(!FTDM_SPAN_IS_NT(span)) { + ChanID.PrefExcl = (isdn_data->opts & FTDM_ISDN_OPT_SUGGEST_CHANNEL) ? 0 : 1; /* 0 = preferred, 1 exclusive */ + } else { + ChanID.PrefExcl = 1; /* always exclusive in NT-mode */ + } + + if(ChanID.IntType) { + ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */ + ChanID.ChanMapType = 3; /* B-Chan */ + ChanID.ChanSlot = (unsigned char)chan_id; + } else { + ChanID.InfoChanSel = (unsigned char)chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */ + } + gen->ChanID = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &ChanID); + + /* + * Progress IE + */ + Q931InitIEProgInd(&Progress); + Progress.CodStand = Q931_CODING_ITU; /* 0 = ITU */ + Progress.Location = 0; /* 0 = User, 1 = Private Network */ + Progress.ProgDesc = 3; /* 1 = Not end-to-end ISDN */ + gen->ProgInd = Q931AppendIE((L3UCHAR *)gen, (L3UCHAR *)&Progress); + + /* + * Display IE + */ + if (!(isdn_data->opts & FTDM_ISDN_OPT_OMIT_DISPLAY_IE)) { + Q931InitIEDisplay(&Display); + Display.Size = Display.Size + (unsigned char)strlen(caller_data->cid_name); + gen->Display = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &Display); + ptrDisplay = Q931GetIEPtr(gen->Display, gen->buf); + ftdm_copy_string((char *)ptrDisplay->Display, caller_data->cid_name, strlen(caller_data->cid_name)+1); + } + + /* + * Calling Number IE + */ + Q931InitIECallingNum(&CallingNum); + CallingNum.TypNum = Q931_TON_UNKNOWN; + CallingNum.NumPlanID = Q931_NUMPLAN_E164; + CallingNum.PresInd = Q931_PRES_ALLOWED; + CallingNum.ScreenInd = Q931_SCREEN_USER_NOT_SCREENED; + CallingNum.Size = CallingNum.Size + (unsigned char)strlen(caller_data->cid_num.digits); + gen->CallingNum = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &CallingNum); + ptrCallingNum = Q931GetIEPtr(gen->CallingNum, gen->buf); + ftdm_copy_string((char *)ptrCallingNum->Digit, caller_data->cid_num.digits, strlen(caller_data->cid_num.digits)+1); + + + /* + * Called number IE + */ + Q931InitIECalledNum(&CalledNum); + CalledNum.TypNum = Q931_TON_UNKNOWN; + CalledNum.NumPlanID = Q931_NUMPLAN_E164; + CalledNum.Size = CalledNum.Size + (unsigned char)strlen(caller_data->dnis.digits); + gen->CalledNum = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &CalledNum); + ptrCalledNum = Q931GetIEPtr(gen->CalledNum, gen->buf); + ftdm_copy_string((char *)ptrCalledNum->Digit, caller_data->dnis.digits, strlen(caller_data->dnis.digits)+1); + + /* + * High-Layer Compatibility IE (Note: Required for AVM FritzBox) + */ + Q931InitIEHLComp(&HLComp); + HLComp.CodStand = Q931_CODING_ITU; /* ITU */ + HLComp.Interpret = 4; /* only possible value */ + HLComp.PresMeth = 1; /* High-layer protocol profile */ + HLComp.HLCharID = 1; /* Telephony = 1, Fax G2+3 = 4, Fax G4 = 65 (Class I)/ 68 (Class II or III) */ + gen->HLComp = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &HLComp); + + caller_data->call_state = FTDM_CALLER_STATE_DIALING; + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + isdn_data->outbound_crv[gen->CRV] = caller_data; + //isdn_data->channels_local_crv[gen->CRV] = ftdmchan; + + while(ftdm_running() && caller_data->call_state == FTDM_CALLER_STATE_DIALING) { + ftdm_sleep(1); + + if (!--sanity) { + caller_data->call_state = FTDM_CALLER_STATE_FAIL; + break; + } + } + isdn_data->outbound_crv[gen->CRV] = NULL; + + if (caller_data->call_state == FTDM_CALLER_STATE_SUCCESS) { + ftdm_channel_t *new_chan = NULL; + int fail = 1; + + new_chan = NULL; + if (caller_data->chan_id < FTDM_MAX_CHANNELS_SPAN && caller_data->chan_id <= span->chan_count) { + new_chan = span->channels[caller_data->chan_id]; + } + + if (new_chan && (status = ftdm_channel_open_chan(new_chan) == FTDM_SUCCESS)) { + if (ftdm_test_flag(new_chan, FTDM_CHANNEL_INUSE) || new_chan->state != FTDM_CHANNEL_STATE_DOWN) { + if (new_chan->state == FTDM_CHANNEL_STATE_DOWN || new_chan->state >= FTDM_CHANNEL_STATE_TERMINATING) { + int x = 0; + ftdm_log(FTDM_LOG_WARNING, "Channel %d:%d ~ %d:%d is already in use waiting for it to become available.\n"); + + for (x = 0; x < 200; x++) { + if (!ftdm_test_flag(new_chan, FTDM_CHANNEL_INUSE)) { + break; + } + ftdm_sleep(5); + } + } + if (ftdm_test_flag(new_chan, FTDM_CHANNEL_INUSE)) { + ftdm_log(FTDM_LOG_ERROR, "Channel %d:%d ~ %d:%d is already in use.\n", + new_chan->span_id, + new_chan->chan_id, + new_chan->physical_span_id, + new_chan->physical_chan_id + ); + new_chan = NULL; + } + } + + if (new_chan && new_chan->state == FTDM_CHANNEL_STATE_DOWN) { + isdn_data->channels_local_crv[gen->CRV] = new_chan; + memset(&new_chan->caller_data, 0, sizeof(new_chan->caller_data)); + ftdm_set_flag(new_chan, FTDM_CHANNEL_OUTBOUND); + ftdm_set_state_locked(new_chan, FTDM_CHANNEL_STATE_DIALING); + switch(gen->MesType) { + case Q931mes_ALERTING: + new_chan->init_state = FTDM_CHANNEL_STATE_PROGRESS_MEDIA; + break; + case Q931mes_CONNECT: + new_chan->init_state = FTDM_CHANNEL_STATE_UP; + break; + default: + new_chan->init_state = FTDM_CHANNEL_STATE_PROGRESS; + break; + } + + fail = 0; + } + } + + if (!fail) { + *ftdmchan = new_chan; + return FTDM_SUCCESS; + } else { + Q931ie_Cause cause; + gen->MesType = Q931mes_DISCONNECT; + cause.IEId = Q931ie_CAUSE; + cause.Size = sizeof(Q931ie_Cause); + cause.CodStand = 0; + cause.Location = 1; + cause.Recom = 1; + //should we be casting here.. or do we need to translate value? + cause.Value = (unsigned char) FTDM_CAUSE_WRONG_CALL_STATE; + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + if (gen->CRV) { + Q931ReleaseCRV(&isdn_data->q931, gen->CRV); + } + + if (new_chan) { + ftdm_log(FTDM_LOG_CRIT, "Channel is busy\n"); + } else { + ftdm_log(FTDM_LOG_CRIT, "Failed to open channel for new setup message\n"); + } + } + } + + *ftdmchan = NULL; + return FTDM_FAIL; + +} + +/** + * \brief Handler for Q931 error + * \param pvt Private structure (span?) + * \param id Error number + * \param p1 ?? + * \param p2 ?? + * \return 0 + */ +static L3INT ftdm_isdn_931_err(void *pvt, L3INT id, L3INT p1, L3INT p2) +{ + ftdm_log(FTDM_LOG_ERROR, "ERROR: [%s] [%d] [%d]\n", q931_error_to_name(id), p1, p2); + return 0; +} + +/** + * \brief Handler for Q931 event message + * \param pvt Span to handle + * \param msg Message string + * \param mlen Message string length + * \return 0 + */ +static L3INT ftdm_isdn_931_34(void *pvt, L2UCHAR *msg, L2INT mlen) +{ + ftdm_span_t *span = (ftdm_span_t *) pvt; + ftdm_isdn_data_t *isdn_data = span->signal_data; + Q931mes_Generic *gen = (Q931mes_Generic *) msg; + uint32_t chan_id = 0; + int chan_hunt = 0; + ftdm_channel_t *ftdmchan = NULL; + ftdm_caller_data_t *caller_data = NULL; + + if (Q931IsIEPresent(gen->ChanID)) { + Q931ie_ChanID *chanid = Q931GetIEPtr(gen->ChanID, gen->buf); + + if(chanid->IntType) + chan_id = chanid->ChanSlot; + else + chan_id = chanid->InfoChanSel; + + /* "any" channel specified */ + if(chanid->InfoChanSel == 3) { + chan_hunt++; + } + } else if (FTDM_SPAN_IS_NT(span)) { + /* no channel ie */ + chan_hunt++; + } + + assert(span != NULL); + assert(isdn_data != NULL); + + ftdm_log(FTDM_LOG_DEBUG, "Yay I got an event! Type:[%02x] Size:[%d] CRV: %d (%#hx, CTX: %s)\n", gen->MesType, gen->Size, gen->CRV, gen->CRV, gen->CRVFlag ? "Terminator" : "Originator"); + + if (gen->CRVFlag && (caller_data = isdn_data->outbound_crv[gen->CRV])) { + if (chan_id) { + caller_data->chan_id = chan_id; + } + + switch(gen->MesType) { + case Q931mes_STATUS: + case Q931mes_CALL_PROCEEDING: + break; + case Q931mes_ALERTING: + case Q931mes_PROGRESS: + case Q931mes_CONNECT: + { + caller_data->call_state = FTDM_CALLER_STATE_SUCCESS; + } + break; + default: + caller_data->call_state = FTDM_CALLER_STATE_FAIL; + break; + } + + return 0; + } + + if (gen->CRVFlag) { + ftdmchan = isdn_data->channels_local_crv[gen->CRV]; + } else { + ftdmchan = isdn_data->channels_remote_crv[gen->CRV]; + } + + ftdm_log(FTDM_LOG_DEBUG, "ftdmchan %x (%d:%d) source isdn_data->channels_%s_crv[%#hx]\n", ftdmchan, ftdmchan ? ftdmchan->span_id : -1, ftdmchan ? ftdmchan->chan_id : -1, gen->CRVFlag ? "local" : "remote", gen->CRV); + + + if (gen->ProtDisc == 3) { + switch(gen->MesType) { + case Q931mes_SERVICE: + { + Q931ie_ChangeStatus *changestatus = Q931GetIEPtr(gen->ChangeStatus, gen->buf); + if (ftdmchan) { + switch (changestatus->NewStatus) { + case 0: /* change status to "in service" */ + { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_SUSPENDED); + ftdm_log(FTDM_LOG_DEBUG, "Channel %d:%d in service\n", ftdmchan->span_id, ftdmchan->chan_id); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } + break; + case 1: + { /* change status to "maintenance" */ + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_SUSPENDED); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + } + break; + case 2: + { /* change status to "out of service" */ + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_SUSPENDED); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + } + break; + default: /* unknown */ + { + break; + } + } + } + } + break; + default: + break; + } + } else { + switch(gen->MesType) { + case Q931mes_RESTART: + { + if (chan_id) { + ftdmchan = span->channels[chan_id]; + } + if (ftdmchan) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } else { + uint32_t i; + for (i = 1; i < span->chan_count; i++) { + ftdm_set_state_locked((span->channels[i]), FTDM_CHANNEL_STATE_RESTART); + } + } + } + break; + case Q931mes_RELEASE: + case Q931mes_RELEASE_COMPLETE: + { + const char *what = gen->MesType == Q931mes_RELEASE ? "Release" : "Release Complete"; + if (ftdmchan) { + if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING || ftdmchan->state == FTDM_CHANNEL_STATE_HANGUP) { + if (gen->MesType == Q931mes_RELEASE) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + else if((gen->MesType == Q931mes_RELEASE && ftdmchan->state <= FTDM_CHANNEL_STATE_UP) || + (gen->MesType == Q931mes_RELEASE_COMPLETE && ftdmchan->state == FTDM_CHANNEL_STATE_DIALING)) { + + /* + * Don't keep inbound channels open if the remote side hangs up before we answered + */ + Q931ie_Cause *cause = Q931GetIEPtr(gen->Cause, gen->buf); + ftdm_sigmsg_t sig; + ftdm_status_t status; + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + sig.channel->caller_data.hangup_cause = (cause) ? cause->Value : FTDM_CAUSE_NORMAL_UNSPECIFIED; + + sig.event_id = FTDM_SIGEVENT_STOP; + status = ftdm_span_send_signal(ftdmchan->span, &sig); + + ftdm_log(FTDM_LOG_DEBUG, "Received %s in state %s, requested hangup for channel %d:%d\n", what, ftdm_channel_state2str(ftdmchan->state), ftdmchan->span_id, chan_id); + } + else { + ftdm_log(FTDM_LOG_DEBUG, "Ignoring %s on channel %d\n", what, chan_id); + } + } else { + ftdm_log(FTDM_LOG_CRIT, "Received %s with no matching channel %d\n", what, chan_id); + } + } + break; + case Q931mes_DISCONNECT: + { + if (ftdmchan) { + Q931ie_Cause *cause = Q931GetIEPtr(gen->Cause, gen->buf); + ftdmchan->caller_data.hangup_cause = cause->Value; + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); + } else { + ftdm_log(FTDM_LOG_CRIT, "Received Disconnect with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_ALERTING: + { + if (ftdmchan) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } else { + ftdm_log(FTDM_LOG_CRIT, "Received Alerting with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_PROGRESS: + { + if (ftdmchan) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } else { + ftdm_log(FTDM_LOG_CRIT, "Received Progress with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_CONNECT: + { + if (ftdmchan) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + + gen->MesType = Q931mes_CONNECT_ACKNOWLEDGE; + gen->CRVFlag = 0; /* outbound */ + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + } else { + ftdm_log(FTDM_LOG_CRIT, "Received Connect with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_SETUP: + { + Q931ie_CallingNum *callingnum = Q931GetIEPtr(gen->CallingNum, gen->buf); + Q931ie_CalledNum *callednum = Q931GetIEPtr(gen->CalledNum, gen->buf); + int fail = 1; + int fail_cause = 0; + int overlap_dial = 0; + uint32_t cplen = mlen; + + if(ftdmchan && ftdmchan == isdn_data->channels_remote_crv[gen->CRV]) { + ftdm_log(FTDM_LOG_INFO, "Duplicate SETUP message(?) for Channel %d:%d ~ %d:%d in state %s [ignoring]\n", + ftdmchan->span_id, + ftdmchan->chan_id, + ftdmchan->physical_span_id, + ftdmchan->physical_chan_id, + ftdm_channel_state2str(ftdmchan->state)); + break; + } + + ftdmchan = NULL; + /* + * Channel selection for incoming calls: + */ + if (FTDM_SPAN_IS_NT(span) && chan_hunt) { + uint32_t x; + + /* + * In NT-mode with channel selection "any", + * try to find a free channel + */ + for (x = 1; x <= span->chan_count; x++) { + ftdm_channel_t *zc = span->channels[x]; + + if (!ftdm_test_flag(zc, FTDM_CHANNEL_INUSE) && zc->state == FTDM_CHANNEL_STATE_DOWN) { + ftdmchan = zc; + break; + } + } + } + else if (!FTDM_SPAN_IS_NT(span) && chan_hunt) { + /* + * In TE-mode this ("any") is invalid + */ + fail_cause = FTDM_CAUSE_CHANNEL_UNACCEPTABLE; + + ftdm_log(FTDM_LOG_ERROR, "Invalid channel selection in incoming call (network side didn't specify a channel)\n"); + } + else { + /* + * Otherwise simply try to select the channel we've been told + * + * TODO: NT mode is abled to select a different channel if the one chosen + * by the TE side is already in use + */ + if (chan_id > 0 && chan_id < FTDM_MAX_CHANNELS_SPAN && chan_id <= span->chan_count) { + ftdmchan = span->channels[chan_id]; + } + else { + /* invalid channel id */ + fail_cause = FTDM_CAUSE_CHANNEL_UNACCEPTABLE; + + ftdm_log(FTDM_LOG_ERROR, "Invalid channel selection in incoming call (none selected or out of bounds)\n"); + } + } + + if (!callednum || !strlen((char *)callednum->Digit)) { + if (FTDM_SPAN_IS_NT(span)) { + ftdm_log(FTDM_LOG_NOTICE, "No destination number found, assuming overlap dial\n"); + overlap_dial++; + } + else { + ftdm_log(FTDM_LOG_ERROR, "No destination number found\n"); + ftdmchan = NULL; + } + } + + if (ftdmchan) { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE) || ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) { + if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN || ftdmchan->state >= FTDM_CHANNEL_STATE_TERMINATING) { + int x = 0; + ftdm_log(FTDM_LOG_WARNING, "Channel %d:%d ~ %d:%d is already in use waiting for it to become available.\n", + ftdmchan->span_id, + ftdmchan->chan_id, + ftdmchan->physical_span_id, + ftdmchan->physical_chan_id); + + for (x = 0; x < 200; x++) { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE)) { + break; + } + ftdm_sleep(5); + } + } + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE)) { + ftdm_log(FTDM_LOG_ERROR, "Channel %d:%d ~ %d:%d is already in use.\n", + ftdmchan->span_id, + ftdmchan->chan_id, + ftdmchan->physical_span_id, + ftdmchan->physical_chan_id + ); + ftdmchan = NULL; + } + } + + if (ftdmchan && ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) { + isdn_data->channels_remote_crv[gen->CRV] = ftdmchan; + memset(&ftdmchan->caller_data, 0, sizeof(ftdmchan->caller_data)); + + if (ftdmchan->mod_data) { + memset(ftdmchan->mod_data, 0, sizeof(ftdm_isdn_bchan_data_t)); + } + + ftdm_set_string(ftdmchan->caller_data.cid_num.digits, (char *)callingnum->Digit); + ftdm_set_string(ftdmchan->caller_data.cid_name, (char *)callingnum->Digit); + ftdm_set_string(ftdmchan->caller_data.ani.digits, (char *)callingnum->Digit); + if (!overlap_dial) { + ftdm_set_string(ftdmchan->caller_data.dnis.digits, (char *)callednum->Digit); + } + + ftdmchan->caller_data.CRV = gen->CRV; + if (cplen > sizeof(ftdmchan->caller_data.raw_data)) { + cplen = sizeof(ftdmchan->caller_data.raw_data); + } + gen->CRVFlag = !(gen->CRVFlag); + memcpy(ftdmchan->caller_data.raw_data, msg, cplen); + ftdmchan->caller_data.raw_data_len = cplen; + fail = 0; + } + } + + if (fail) { + Q931ie_Cause cause; + gen->MesType = Q931mes_DISCONNECT; + gen->CRVFlag = 1; /* inbound call */ + cause.IEId = Q931ie_CAUSE; + cause.Size = sizeof(Q931ie_Cause); + cause.CodStand = Q931_CODING_ITU; + cause.Location = 1; + cause.Recom = 1; + //should we be casting here.. or do we need to translate value? + cause.Value = (unsigned char)((fail_cause) ? fail_cause : FTDM_CAUSE_WRONG_CALL_STATE); + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + if (gen->CRV) { + Q931ReleaseCRV(&isdn_data->q931, gen->CRV); + } + + if (ftdmchan) { + ftdm_log(FTDM_LOG_CRIT, "Channel is busy\n"); + } else { + ftdm_log(FTDM_LOG_CRIT, "Failed to open channel for new setup message\n"); + } + + } else { + Q931ie_ChanID ChanID; + + /* + * Update Channel ID IE + */ + Q931InitIEChanID(&ChanID); + ChanID.IntType = FTDM_SPAN_IS_BRI(ftdmchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */ + ChanID.PrefExcl = FTDM_SPAN_IS_NT(ftdmchan->span) ? 1 : 0; /* Exclusive in NT-mode = 1, Preferred otherwise = 0 */ + if(ChanID.IntType) { + ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */ + ChanID.ChanMapType = 3; /* B-Chan */ + ChanID.ChanSlot = (unsigned char)ftdmchan->chan_id; + } else { + ChanID.InfoChanSel = (unsigned char)ftdmchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */ + } + gen->ChanID = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &ChanID); + + if (overlap_dial) { + Q931ie_ProgInd progress; + + /* + * Setup Progress indicator + */ + progress.IEId = Q931ie_PROGRESS_INDICATOR; + progress.Size = sizeof(Q931ie_ProgInd); + progress.CodStand = Q931_CODING_ITU; /* ITU */ + progress.Location = 1; /* private network serving the local user */ + progress.ProgDesc = 8; /* call is not end-to-end isdn = 1, in-band information available = 8 */ + gen->ProgInd = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &progress); + + /* + * Send SETUP ACK + */ + gen->MesType = Q931mes_SETUP_ACKNOWLEDGE; + gen->CRVFlag = 1; /* inbound call */ + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALTONE); + } else { + /* + * Advance to RING state + */ + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING); + } + } + } + break; + + case Q931mes_CALL_PROCEEDING: + { + if (ftdmchan) { + ftdm_log(FTDM_LOG_CRIT, "Received CALL PROCEEDING message for channel %d\n", chan_id); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } else { + ftdm_log(FTDM_LOG_CRIT, "Received CALL PROCEEDING with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_CONNECT_ACKNOWLEDGE: + { + if (ftdmchan) { + ftdm_log(FTDM_LOG_DEBUG, "Received CONNECT_ACK message for channel %d\n", chan_id); + } else { + ftdm_log(FTDM_LOG_DEBUG, "Received CONNECT_ACK with no matching channel %d\n", chan_id); + } + } + break; + + case Q931mes_INFORMATION: + { + if (ftdmchan) { + ftdm_log(FTDM_LOG_CRIT, "Received INFORMATION message for channel %d\n", ftdmchan->chan_id); + + if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALTONE) { + char digit = '\0'; + + /* + * overlap dial digit indication + */ + if (Q931IsIEPresent(gen->CalledNum)) { + ftdm_isdn_bchan_data_t *data = (ftdm_isdn_bchan_data_t *)ftdmchan->mod_data; + Q931ie_CalledNum *callednum = Q931GetIEPtr(gen->CalledNum, gen->buf); + int pos; + + digit = callednum->Digit[strlen((char *)callednum->Digit) - 1]; + if (digit == '#') { + callednum->Digit[strlen((char *)callednum->Digit) - 1] = '\0'; + } + + /* TODO: make this more safe with strncat() */ + pos = (int)strlen(ftdmchan->caller_data.dnis.digits); + strcat(&ftdmchan->caller_data.dnis.digits[pos], (char *)callednum->Digit); + + /* update timer */ + data->digit_timeout = ftdm_time_now() + isdn_data->digit_timeout; + + ftdm_log(FTDM_LOG_DEBUG, "Received new overlap digit (%s), destination number: %s\n", callednum->Digit, ftdmchan->caller_data.dnis.digits); + } + + if (Q931IsIEPresent(gen->SendComplete) || digit == '#') { + ftdm_log(FTDM_LOG_DEBUG, "Leaving overlap dial mode\n"); + + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING); + } + } + } else { + ftdm_log(FTDM_LOG_CRIT, "Received INFORMATION message with no matching channel\n"); + } + } + break; + + case Q931mes_STATUS_ENQUIRY: + { + /* + * !! HACK ALERT !! + * + * Map FreeTDM channel states to Q.931 states + */ + Q931ie_CallState state; + Q931ie_Cause cause; + + gen->MesType = Q931mes_STATUS; + gen->CRVFlag = gen->CRVFlag ? 0 : 1; + + state.CodStand = Q931_CODING_ITU; /* ITU-T */ + state.CallState = Q931_U0; /* Default: Null */ + + cause.IEId = Q931ie_CAUSE; + cause.Size = sizeof(Q931ie_Cause); + cause.CodStand = Q931_CODING_ITU; /* ITU */ + cause.Location = 1; /* private network */ + cause.Recom = 1; /* */ + *cause.Diag = '\0'; + + if(ftdmchan) { + switch(ftdmchan->state) { + case FTDM_CHANNEL_STATE_UP: + state.CallState = Q931_U10; /* Active */ + break; + case FTDM_CHANNEL_STATE_RING: + state.CallState = Q931_U6; /* Call present */ + break; + case FTDM_CHANNEL_STATE_DIALING: + state.CallState = Q931_U1; /* Call initiated */ + break; + case FTDM_CHANNEL_STATE_DIALTONE: + state.CallState = Q931_U25; /* Overlap receiving */ + break; + + /* TODO: map missing states */ + + default: + state.CallState = Q931_U0; + } + + cause.Value = 30; /* response to STATUS ENQUIRY */ + } else { + cause.Value = 98; /* */ + } + + gen->CallState = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &state); + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + } + break; + + default: + ftdm_log(FTDM_LOG_CRIT, "Received unhandled message %d (%#x)\n", (int)gen->MesType, (int)gen->MesType); + break; + } + } + + return 0; +} + +/** + * \brief Handler for Q921 read event + * \param pvt Span were message is coming from + * \param ind Q921 indication + * \param tei Terminal Endpoint Identifier + * \param msg Message string + * \param mlen Message string length + * \return 0 on success, 1 on failure + */ +static int ftdm_isdn_921_23(void *pvt, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR *msg, L2INT mlen) +{ + int ret, offset = (ind == Q921_DL_DATA) ? 4 : 3; + char bb[4096] = ""; + + switch(ind) { + case Q921_DL_DATA: + case Q921_DL_UNIT_DATA: + print_hex_bytes(msg + offset, mlen - offset, bb, sizeof(bb)); +#ifdef HAVE_LIBPCAP + /*Q931ToPcap*/ + if(do_q931ToPcap==1){ + ftdm_span_t *span = (ftdm_span_t *) pvt; + if(writeQ931PacketToPcap(msg + offset, mlen - offset, span->span_id, 1) != FTDM_SUCCESS){ + ftdm_log(FTDM_LOG_WARNING, "Couldn't write Q931 buffer to pcap file!\n"); + } + } + /*Q931ToPcap done*/ +#endif + ftdm_log(FTDM_LOG_DEBUG, "READ %d\n%s\n%s\n\n\n", (int)mlen - offset, LINE, bb); + + default: + ret = Q931Rx23(pvt, ind, tei, msg, mlen); + if (ret != 0) + ftdm_log(FTDM_LOG_DEBUG, "931 parse error [%d] [%s]\n", ret, q931_error_to_name(ret)); + break; + } + + return ((ret >= 0) ? 1 : 0); +} + +/** + * \brief Handler for Q921 write event + * \param pvt Span were message is coming from + * \param msg Message string + * \param mlen Message string length + * \return 0 on success, -1 on failure + */ +static int ftdm_isdn_921_21(void *pvt, L2UCHAR *msg, L2INT mlen) +{ + ftdm_span_t *span = (ftdm_span_t *) pvt; + ftdm_size_t len = (ftdm_size_t) mlen; + ftdm_isdn_data_t *isdn_data = span->signal_data; + +#ifdef IODEBUG + char bb[4096] = ""; + print_hex_bytes(msg, len, bb, sizeof(bb)); + print_bits(msg, (int)len, bb, sizeof(bb), FTDM_ENDIAN_LITTLE, 0); + ftdm_log(FTDM_LOG_DEBUG, "WRITE %d\n%s\n%s\n\n", (int)len, LINE, bb); + +#endif + + assert(span != NULL); + return ftdm_channel_write(isdn_data->dchan, msg, len, &len) == FTDM_SUCCESS ? 0 : -1; +} + +/** + * \brief Handler for channel state change + * \param ftdmchan Channel to handle + */ +static __inline__ void state_advance(ftdm_channel_t *ftdmchan) +{ + Q931mes_Generic *gen = (Q931mes_Generic *) ftdmchan->caller_data.raw_data; + ftdm_isdn_data_t *isdn_data = ftdmchan->span->signal_data; + ftdm_sigmsg_t sig; + ftdm_status_t status; + + ftdm_log(FTDM_LOG_DEBUG, "%d:%d STATE [%s]\n", + ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state)); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + + switch (ftdmchan->state) { + case FTDM_CHANNEL_STATE_DOWN: + { + if (gen->CRV) { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + isdn_data->channels_local_crv[gen->CRV] = NULL; + } else { + isdn_data->channels_remote_crv[gen->CRV] = NULL; + } + Q931ReleaseCRV(&isdn_data->q931, gen->CRV); + } + ftdm_channel_done(ftdmchan); + } + break; + case FTDM_CHANNEL_STATE_PROGRESS: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_PROGRESS; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + gen->MesType = Q931mes_CALL_PROCEEDING; + gen->CRVFlag = 1; /* inbound */ + + if (FTDM_SPAN_IS_NT(ftdmchan->span)) { + Q931ie_ChanID ChanID; + + /* + * Set new Channel ID + */ + Q931InitIEChanID(&ChanID); + ChanID.IntType = FTDM_SPAN_IS_BRI(ftdmchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */ + ChanID.PrefExcl = 1; /* always exclusive in NT-mode */ + + if(ChanID.IntType) { + ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */ + ChanID.ChanMapType = 3; /* B-Chan */ + ChanID.ChanSlot = (unsigned char)ftdmchan->chan_id; + } else { + ChanID.InfoChanSel = (unsigned char)ftdmchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */ + } + gen->ChanID = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &ChanID); + } + + Q931Rx43(&isdn_data->q931, (void *)gen, gen->Size); + } + } + break; + case FTDM_CHANNEL_STATE_DIALTONE: + { + ftdm_isdn_bchan_data_t *data = (ftdm_isdn_bchan_data_t *)ftdmchan->mod_data; + + if (data) { + data->digit_timeout = ftdm_time_now() + isdn_data->digit_timeout; + } + } + break; + case FTDM_CHANNEL_STATE_RING: + { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_START; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } + } + break; + case FTDM_CHANNEL_STATE_RESTART: + { + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_UNSPECIFIED; + sig.event_id = FTDM_SIGEVENT_RESTART; + status = ftdm_span_send_signal(ftdmchan->span, &sig); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + break; + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + return; + } + } + gen->MesType = Q931mes_ALERTING; + gen->CRVFlag = 1; /* inbound call */ + Q931Rx43(&isdn_data->q931, (void *)gen, gen->Size); + } + } + break; + case FTDM_CHANNEL_STATE_UP: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_UP; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + return; + } + } + gen->MesType = Q931mes_CONNECT; + gen->BearerCap = 0; + gen->CRVFlag = 1; /* inbound call */ + Q931Rx43(&isdn_data->q931, (void *)gen, ftdmchan->caller_data.raw_data_len); + } + } + break; + case FTDM_CHANNEL_STATE_DIALING: + if (!(isdn_data->opts & FTDM_ISDN_OPT_SUGGEST_CHANNEL)) { + Q931ie_BearerCap BearerCap; + Q931ie_ChanID ChanID; + Q931ie_CallingNum CallingNum; + Q931ie_CallingNum *ptrCallingNum; + Q931ie_CalledNum CalledNum; + Q931ie_CalledNum *ptrCalledNum; + Q931ie_Display Display, *ptrDisplay; + Q931ie_HLComp HLComp; /* High-Layer Compatibility IE */ + Q931ie_ProgInd Progress; /* Progress Indicator IE */ + int codec = 0; + + /* + * get codec type + */ + ftdm_channel_command(ftdmchan->span->channels[ftdmchan->chan_id], FTDM_COMMAND_GET_NATIVE_CODEC, &codec); + + /* + * Q.931 Setup Message + */ + Q931InitMesGeneric(gen); + gen->MesType = Q931mes_SETUP; + gen->CRVFlag = 0; /* outbound(?) */ + + /* + * Bearer Capability IE + */ + Q931InitIEBearerCap(&BearerCap); + BearerCap.CodStand = Q931_CODING_ITU; /* ITU-T = 0, ISO/IEC = 1, National = 2, Network = 3 */ + BearerCap.ITC = Q931_ITC_SPEECH; /* Speech */ + BearerCap.TransMode = 0; /* Circuit = 0, Packet = 1 */ + BearerCap.ITR = Q931_ITR_64K; /* 64k = 16, Packet mode = 0 */ + BearerCap.Layer1Ident = 1; + BearerCap.UIL1Prot = (codec == FTDM_CODEC_ALAW) ? 3 : 2; /* U-law = 2, A-law = 3 */ + gen->BearerCap = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &BearerCap); + + /* + * ChannelID IE + */ + Q931InitIEChanID(&ChanID); + ChanID.IntType = FTDM_SPAN_IS_BRI(ftdmchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */ + ChanID.PrefExcl = FTDM_SPAN_IS_NT(ftdmchan->span) ? 1 : 0; /* Exclusive in NT-mode = 1, Preferred otherwise = 0 */ + if(ChanID.IntType) { + ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */ + ChanID.ChanMapType = 3; /* B-Chan */ + ChanID.ChanSlot = (unsigned char)ftdmchan->chan_id; + } else { + ChanID.InfoChanSel = (unsigned char)ftdmchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */ + } + gen->ChanID = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &ChanID); + + /* + * Progress IE + */ + Q931InitIEProgInd(&Progress); + Progress.CodStand = Q931_CODING_ITU; /* 0 = ITU */ + Progress.Location = 0; /* 0 = User, 1 = Private Network */ + Progress.ProgDesc = 3; /* 1 = Not end-to-end ISDN */ + gen->ProgInd = Q931AppendIE((L3UCHAR *)gen, (L3UCHAR *)&Progress); + + /* + * Display IE + */ + if (!(isdn_data->opts & FTDM_ISDN_OPT_OMIT_DISPLAY_IE)) { + Q931InitIEDisplay(&Display); + Display.Size = Display.Size + (unsigned char)strlen(ftdmchan->caller_data.cid_name); + gen->Display = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &Display); + ptrDisplay = Q931GetIEPtr(gen->Display, gen->buf); + ftdm_copy_string((char *)ptrDisplay->Display, ftdmchan->caller_data.cid_name, strlen(ftdmchan->caller_data.cid_name)+1); + } + + /* + * CallingNum IE + */ + Q931InitIECallingNum(&CallingNum); + CallingNum.TypNum = ftdmchan->caller_data.ani.type; + CallingNum.NumPlanID = Q931_NUMPLAN_E164; + CallingNum.PresInd = Q931_PRES_ALLOWED; + CallingNum.ScreenInd = Q931_SCREEN_USER_NOT_SCREENED; + CallingNum.Size = CallingNum.Size + (unsigned char)strlen(ftdmchan->caller_data.cid_num.digits); + gen->CallingNum = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &CallingNum); + ptrCallingNum = Q931GetIEPtr(gen->CallingNum, gen->buf); + ftdm_copy_string((char *)ptrCallingNum->Digit, ftdmchan->caller_data.cid_num.digits, strlen(ftdmchan->caller_data.cid_num.digits)+1); + + /* + * CalledNum IE + */ + Q931InitIECalledNum(&CalledNum); + CalledNum.TypNum = ftdmchan->caller_data.dnis.type; + CalledNum.NumPlanID = Q931_NUMPLAN_E164; + CalledNum.Size = CalledNum.Size + (unsigned char)strlen(ftdmchan->caller_data.dnis.digits); + gen->CalledNum = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &CalledNum); + ptrCalledNum = Q931GetIEPtr(gen->CalledNum, gen->buf); + ftdm_copy_string((char *)ptrCalledNum->Digit, ftdmchan->caller_data.dnis.digits, strlen(ftdmchan->caller_data.dnis.digits)+1); + + /* + * High-Layer Compatibility IE (Note: Required for AVM FritzBox) + */ + Q931InitIEHLComp(&HLComp); + HLComp.CodStand = Q931_CODING_ITU; /* ITU */ + HLComp.Interpret = 4; /* only possible value */ + HLComp.PresMeth = 1; /* High-layer protocol profile */ + HLComp.HLCharID = Q931_HLCHAR_TELEPHONY; /* Telephony = 1, Fax G2+3 = 4, Fax G4 = 65 (Class I)/ 68 (Class II or III) */ /* TODO: make accessible from user layer */ + gen->HLComp = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &HLComp); + + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + isdn_data->channels_local_crv[gen->CRV] = ftdmchan; + } + break; + case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: + { + /* reply RELEASE with RELEASE_COMPLETE message */ + if(ftdmchan->last_state == FTDM_CHANNEL_STATE_HANGUP) { + gen->MesType = Q931mes_RELEASE_COMPLETE; + + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + } + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + break; + case FTDM_CHANNEL_STATE_HANGUP: + { + Q931ie_Cause cause; + + ftdm_log(FTDM_LOG_DEBUG, "Hangup: Direction %s\n", ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? "Outbound" : "Inbound"); + + gen->CRVFlag = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? 0 : 1; + + cause.IEId = Q931ie_CAUSE; + cause.Size = sizeof(Q931ie_Cause); + cause.CodStand = Q931_CODING_ITU; /* ITU */ + cause.Location = 1; /* private network */ + cause.Recom = 1; /* */ + + /* + * BRI PTMP needs special handling here... + * TODO: cleanup / refine (see above) + */ + if (ftdmchan->last_state == FTDM_CHANNEL_STATE_RING) { + /* + * inbound call [was: number unknown (= not found in routing state)] + * (in Q.931 spec terms: Reject request) + */ + gen->MesType = Q931mes_RELEASE_COMPLETE; + + //cause.Value = (unsigned char) FTDM_CAUSE_UNALLOCATED; + cause.Value = (unsigned char) ftdmchan->caller_data.hangup_cause; + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + /* we're done, release channel */ + //ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + else if (ftdmchan->last_state <= FTDM_CHANNEL_STATE_PROGRESS) { + /* + * just release all unanswered calls [was: inbound call, remote side hung up before we answered] + */ + gen->MesType = Q931mes_RELEASE; + + cause.Value = (unsigned char) ftdmchan->caller_data.hangup_cause; + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (void *)gen, gen->Size); + + /* this will be triggered by the RELEASE_COMPLETE reply */ + /* ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); */ + } + else { + /* + * call connected, hangup + */ + gen->MesType = Q931mes_DISCONNECT; + + cause.Value = (unsigned char) ftdmchan->caller_data.hangup_cause; + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + } + } + break; + case FTDM_CHANNEL_STATE_TERMINATING: + { + ftdm_log(FTDM_LOG_DEBUG, "Terminating: Direction %s\n", ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? "Outbound" : "Inbound"); + + sig.event_id = FTDM_SIGEVENT_STOP; + status = ftdm_span_send_signal(ftdmchan->span, &sig); + gen->MesType = Q931mes_RELEASE; + gen->CRVFlag = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? 0 : 1; + Q931Rx43(&isdn_data->q931, (void *)gen, gen->Size); + } + default: + break; + } +} + +/** + * \brief Checks current state on a span + * \param span Span to check status on + */ +static __inline__ void check_state(ftdm_span_t *span) +{ + if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) { + uint32_t j; + ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE); + for(j = 1; j <= span->chan_count; j++) { + if (ftdm_test_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) { + ftdm_mutex_lock(span->channels[j]->mutex); + ftdm_clear_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE); + state_advance(span->channels[j]); + ftdm_channel_complete_state(span->channels[j]); + ftdm_mutex_unlock(span->channels[j]->mutex); + } + } + } +} + +/** + * \brief Processes FreeTDM event on a span + * \param span Span to process event on + * \param event Event to process + * \return Success or failure + */ +static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *event) +{ + ftdm_log(FTDM_LOG_DEBUG, "EVENT [%s][%d:%d] STATE [%s]\n", + ftdm_oob_event2str(event->enum_id), event->channel->span_id, event->channel->chan_id, ftdm_channel_state2str(event->channel->state)); + + switch(event->enum_id) { + case FTDM_OOB_ALARM_TRAP: + { + if (event->channel->state != FTDM_CHANNEL_STATE_DOWN) { + if (event->channel->type == FTDM_CHAN_TYPE_B) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_RESTART); + } + } + + + ftdm_set_flag(event->channel, FTDM_CHANNEL_SUSPENDED); + + + ftdm_channel_get_alarms(event->channel); + ftdm_log(FTDM_LOG_WARNING, "channel %d:%d (%d:%d) has alarms! [%s]\n", + event->channel->span_id, event->channel->chan_id, + event->channel->physical_span_id, event->channel->physical_chan_id, + event->channel->last_error); + } + break; + case FTDM_OOB_ALARM_CLEAR: + { + + ftdm_log(FTDM_LOG_WARNING, "channel %d:%d (%d:%d) alarms Cleared!\n", event->channel->span_id, event->channel->chan_id, + event->channel->physical_span_id, event->channel->physical_chan_id); + + ftdm_clear_flag(event->channel, FTDM_CHANNEL_SUSPENDED); + ftdm_channel_get_alarms(event->channel); + } + break; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Checks for events on a span + * \param span Span to check for events + */ +static __inline__ void check_events(ftdm_span_t *span) +{ + ftdm_status_t status; + + status = ftdm_span_poll_event(span, 5); + + switch(status) { + case FTDM_SUCCESS: + { + ftdm_event_t *event; + while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) { + if (event->enum_id == FTDM_OOB_NOOP) { + continue; + } + if (process_event(span, event) != FTDM_SUCCESS) { + break; + } + } + } + break; + case FTDM_FAIL: + { + ftdm_log(FTDM_LOG_DEBUG, "Event Failure! %d\n", ftdm_running()); + } + break; + default: + break; + } +} + +/** + * \brief Retrieves tone generation output to be sent + * \param ts Teletone generator + * \param map Tone map + * \return -1 on error, 0 on success + */ +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + ftdm_buffer_t *dt_buffer = ts->user_data; + int wrote; + + if (!dt_buffer) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + ftdm_buffer_write(dt_buffer, ts->buffer, wrote * 2); + return 0; +} + +/** + * \brief Main thread function for tone generation on a span + * \param me Current thread + * \param obj Span to generate tones on + */ +static void *ftdm_isdn_tones_run(ftdm_thread_t *me, void *obj) +{ + ftdm_span_t *span = (ftdm_span_t *) obj; + ftdm_isdn_data_t *isdn_data = span->signal_data; + ftdm_buffer_t *dt_buffer = NULL; + teletone_generation_session_t ts = {{{{0}}}}; + unsigned char frame[1024]; + uint32_t x; + int interval = 0; + int offset = 0; + + ftdm_log(FTDM_LOG_DEBUG, "ISDN tones thread starting.\n"); + ftdm_set_flag(isdn_data, FTDM_ISDN_TONES_RUNNING); + + if (ftdm_buffer_create(&dt_buffer, 1024, 1024, 0) != FTDM_SUCCESS) { + snprintf(isdn_data->dchan->last_error, sizeof(isdn_data->dchan->last_error), "memory error!"); + ftdm_log(FTDM_LOG_ERROR, "MEM ERROR\n"); + goto done; + } + ftdm_buffer_set_loops(dt_buffer, -1); + + /* get a tone generation friendly interval to avoid distortions */ + for (x = 1; x <= span->chan_count; x++) { + if (span->channels[x]->type != FTDM_CHAN_TYPE_DQ921) { + ftdm_channel_command(span->channels[x], FTDM_COMMAND_GET_INTERVAL, &interval); + break; + } + } + if (!interval) { + interval = 20; + } + ftdm_log(FTDM_LOG_NOTICE, "Tone generating interval %d\n", interval); + + /* init teletone */ + teletone_init_session(&ts, 0, teletone_handler, dt_buffer); + ts.rate = 8000; + ts.duration = ts.rate; + + /* main loop */ + while(ftdm_running() && ftdm_test_flag(isdn_data, FTDM_ISDN_TONES_RUNNING) && !ftdm_test_flag(isdn_data, FTDM_ISDN_STOP)) { + ftdm_wait_flag_t flags; + ftdm_status_t status; + int last_chan_state = 0; + int gated = 0; + L2ULONG now = ftdm_time_now(); + + /* + * check b-channel states and generate & send tones if neccessary + */ + for (x = 1; x <= span->chan_count; x++) { + ftdm_channel_t *ftdmchan = span->channels[x]; + ftdm_size_t len = sizeof(frame), rlen; + + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) { + continue; + } + + /* + * Generate tones based on current bchan state + * (Recycle buffer content if succeeding channels share the + * same state, this saves some cpu cycles) + */ + switch (ftdmchan->state) { + case FTDM_CHANNEL_STATE_DIALTONE: + { + ftdm_isdn_bchan_data_t *data = (ftdm_isdn_bchan_data_t *)ftdmchan->mod_data; + + /* check overlap dial timeout first before generating tone */ + if (data && data->digit_timeout && data->digit_timeout <= now) { + if (strlen(ftdmchan->caller_data.dnis.digits) > 0) { + ftdm_log(FTDM_LOG_DEBUG, "Overlap dial timeout, advancing to RING state\n"); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING); + } else { + /* no digits received, hangup */ + ftdm_log(FTDM_LOG_DEBUG, "Overlap dial timeout, no digits received, going to HANGUP state\n"); + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_RECOVERY_ON_TIMER_EXPIRE; /* TODO: probably wrong cause value */ + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + data->digit_timeout = 0; + continue; + } + + if (last_chan_state != ftdmchan->state) { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_DIAL]); + last_chan_state = ftdmchan->state; + } + } + break; + + case FTDM_CHANNEL_STATE_RING: + { + if (last_chan_state != ftdmchan->state) { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_RING]); + last_chan_state = ftdmchan->state; + } + } + break; + + default: /* Not in a tone generating state, go to next round */ + continue; + } + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + continue; + } + ftdm_log(FTDM_LOG_NOTICE, "Successfully opened channel %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id); + } + + flags = FTDM_READ; + + status = ftdm_channel_wait(ftdmchan, &flags, (gated) ? 0 : interval); + switch(status) { + case FTDM_FAIL: + continue; + + case FTDM_TIMEOUT: + gated = 1; + continue; + + default: + if (!(flags & FTDM_READ)) { + continue; + } + } + gated = 1; + + status = ftdm_channel_read(ftdmchan, frame, &len); + if (status != FTDM_SUCCESS || len <= 0) { + continue; + } + + if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) { + len *= 2; + } + + /* seek to current offset */ + ftdm_buffer_seek(dt_buffer, offset); + + rlen = ftdm_buffer_read_loop(dt_buffer, frame, len); + + if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) { + fio_codec_t codec_func = NULL; + + if (ftdmchan->native_codec == FTDM_CODEC_ULAW) { + codec_func = fio_slin2ulaw; + } else if (ftdmchan->native_codec == FTDM_CODEC_ALAW) { + codec_func = fio_slin2alaw; + } + + if (codec_func) { + status = codec_func(frame, sizeof(frame), &rlen); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!"); + goto done; + } + } + ftdm_channel_write(ftdmchan, frame, sizeof(frame), &rlen); + } + + /* + * sleep a bit if there was nothing to do + */ + if (!gated) { + ftdm_sleep(interval); + } + + offset += (ts.rate / (1000 / interval)) << 1; + if (offset >= ts.rate) { + offset = 0; + } + } + +done: + if (ts.buffer) { + teletone_destroy_session(&ts); + } + + if (dt_buffer) { + ftdm_buffer_destroy(&dt_buffer); + } + + ftdm_log(FTDM_LOG_DEBUG, "ISDN tone thread ended.\n"); + ftdm_clear_flag(isdn_data, FTDM_ISDN_TONES_RUNNING); + + return NULL; +} + +/** + * \brief Main thread function for an ISDN span + * \param me Current thread + * \param obj Span to monitor + */ +static void *ftdm_isdn_run(ftdm_thread_t *me, void *obj) +{ + ftdm_span_t *span = (ftdm_span_t *) obj; + ftdm_isdn_data_t *isdn_data = span->signal_data; + unsigned char frame[1024]; + ftdm_size_t len = sizeof(frame); + int errs = 0; + +#ifdef WIN32 + timeBeginPeriod(1); +#endif + + ftdm_log(FTDM_LOG_DEBUG, "ISDN thread starting.\n"); + ftdm_set_flag(isdn_data, FTDM_ISDN_RUNNING); + + Q921Start(&isdn_data->q921); + + while(ftdm_running() && ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING) && !ftdm_test_flag(isdn_data, FTDM_ISDN_STOP)) { + ftdm_wait_flag_t flags = FTDM_READ; + ftdm_status_t status = ftdm_channel_wait(isdn_data->dchan, &flags, 100); + + Q921TimerTick(&isdn_data->q921); + Q931TimerTick(&isdn_data->q931); + check_state(span); + check_events(span); + + /* + * + */ + switch(status) { + case FTDM_FAIL: + { + ftdm_log(FTDM_LOG_ERROR, "D-Chan Read Error!\n"); + snprintf(span->last_error, sizeof(span->last_error), "D-Chan Read Error!"); + if (++errs == 10) { + isdn_data->dchan->state = FTDM_CHANNEL_STATE_UP; + goto done; + } + } + break; + case FTDM_TIMEOUT: + { + errs = 0; + } + break; + default: + { + errs = 0; + if (flags & FTDM_READ) { + + if (ftdm_test_flag(isdn_data->dchan, FTDM_CHANNEL_SUSPENDED)) { + ftdm_clear_flag_all(span, FTDM_CHANNEL_SUSPENDED); + } + len = sizeof(frame); + if (ftdm_channel_read(isdn_data->dchan, frame, &len) == FTDM_SUCCESS) { +#ifdef IODEBUG + char bb[4096] = ""; + print_hex_bytes(frame, len, bb, sizeof(bb)); + + print_bits(frame, (int)len, bb, sizeof(bb), FTDM_ENDIAN_LITTLE, 0); + ftdm_log(FTDM_LOG_DEBUG, "READ %d\n%s\n%s\n\n", (int)len, LINE, bb); +#endif + + Q921QueueHDLCFrame(&isdn_data->q921, frame, (int)len); + Q921Rx12(&isdn_data->q921); + } + } else { + ftdm_log(FTDM_LOG_DEBUG, "No Read FLAG!\n"); + } + } + break; + } + } + + done: + ftdm_channel_close(&isdn_data->dchans[0]); + ftdm_channel_close(&isdn_data->dchans[1]); + ftdm_clear_flag(isdn_data, FTDM_ISDN_RUNNING); + +#ifdef WIN32 + timeEndPeriod(1); +#endif + + ftdm_log(FTDM_LOG_DEBUG, "ISDN thread ended.\n"); + return NULL; +} + +/** + * \brief FreeTDM ISDN signaling module initialisation + * \return Success + */ +static FIO_SIG_LOAD_FUNCTION(ftdm_isdn_init) +{ + Q931Initialize(); + + Q921SetGetTimeCB(ftdm_time_now); + Q931SetGetTimeCB(ftdm_time_now); + + return FTDM_SUCCESS; +} + +/** + * \brief Receives a Q931 indication message + * \param pvt Span were message is coming from + * \param ind Q931 indication + * \param tei Terminal Endpoint Identifier + * \param msg Message string + * \param mlen Message string length + * \return 0 on success + */ +static int q931_rx_32(void *pvt, Q921DLMsg_t ind, L3UCHAR tei, L3UCHAR *msg, L3INT mlen) +{ + int offset = 4; + char bb[4096] = ""; + + switch(ind) { + case Q921_DL_UNIT_DATA: + offset = 3; + + case Q921_DL_DATA: + print_hex_bytes(msg + offset, mlen - offset, bb, sizeof(bb)); +#ifdef HAVE_LIBPCAP + /*Q931ToPcap*/ + if(do_q931ToPcap==1){ + ftdm_span_t *span = (ftdm_span_t *) pvt; + if(writeQ931PacketToPcap(msg + offset, mlen - offset, span->span_id, 0) != FTDM_SUCCESS){ + ftdm_log(FTDM_LOG_WARNING, "Couldn't write Q931 buffer to pcap file!\n"); + } + } + /*Q931ToPcap done*/ +#endif + ftdm_log(FTDM_LOG_DEBUG, "WRITE %d\n%s\n%s\n\n", (int)mlen - offset, LINE, bb); + break; + + default: + break; + } + + return Q921Rx32(pvt, ind, tei, msg, mlen); +} + +/** + * \brief Logs Q921 message + * \param pvt Span were message is coming from + * \param level Q921 log level + * \param msg Message string + * \param size Message string length + * \return 0 + */ +static int ftdm_isdn_q921_log(void *pvt, Q921LogLevel_t level, char *msg, L2INT size) +{ + ftdm_span_t *span = (ftdm_span_t *) pvt; + + ftdm_log("Span", "Q.921", span->span_id, (int)level, "%s", msg); + return 0; +} + +/** + * \brief Logs Q931 message + * \param pvt Span were message is coming from + * \param level Q931 log level + * \param msg Message string + * \param size Message string length + * \return 0 + */ +static L3INT ftdm_isdn_q931_log(void *pvt, Q931LogLevel_t level, char *msg, L3INT size) +{ + ftdm_span_t *span = (ftdm_span_t *) pvt; + + ftdm_log("Span", "Q.931", span->span_id, (int)level, "%s", msg); + return 0; +} +/** + * \brief ISDN state map + */ +static ftdm_state_map_t isdn_state_map = { + { + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_ANY_STATE}, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + {FTDM_CHANNEL_STATE_DIALING, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DIALING, FTDM_END}, + {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_UP, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_UP, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END} + }, + + /****************************************/ + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_ANY_STATE}, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + {FTDM_CHANNEL_STATE_DIALTONE, FTDM_CHANNEL_STATE_RING, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DIALTONE, FTDM_END}, + {FTDM_CHANNEL_STATE_RING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, + FTDM_CHANNEL_STATE_CANCEL, FTDM_CHANNEL_STATE_UP, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_UP, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + }, + + + } +}; + +/** + * \brief Stops an ISDN span + * \param span Span to halt + * \return Success + * + * Sets a stop flag and waits for the threads to end + */ +static ftdm_status_t ftdm_isdn_stop(ftdm_span_t *span) +{ + ftdm_isdn_data_t *isdn_data = span->signal_data; + + if (!ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) { + return FTDM_FAIL; + } + + ftdm_set_flag(isdn_data, FTDM_ISDN_STOP); + + while(ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) { + ftdm_sleep(100); + } + + while(ftdm_test_flag(isdn_data, FTDM_ISDN_TONES_RUNNING)) { + ftdm_sleep(100); + } + + return FTDM_SUCCESS; + +} + +/** + * \brief Starts an ISDN span + * \param span Span to halt + * \return Success or failure + * + * Launches a thread to monitor the span and a thread to generate tones on the span + */ +static ftdm_status_t ftdm_isdn_start(ftdm_span_t *span) +{ + ftdm_status_t ret; + ftdm_isdn_data_t *isdn_data = span->signal_data; + + if (ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) { + return FTDM_FAIL; + } + + ftdm_clear_flag(isdn_data, FTDM_ISDN_STOP); + ret = ftdm_thread_create_detached(ftdm_isdn_run, span); + + if (ret != FTDM_SUCCESS) { + return ret; + } + + if (FTDM_SPAN_IS_NT(span) && !(isdn_data->opts & FTDM_ISDN_OPT_DISABLE_TONES)) { + ret = ftdm_thread_create_detached(ftdm_isdn_tones_run, span); + } + return ret; +} + +/** + * \brief Parses an option string to flags + * \param in String to parse for configuration options + * \return Flags + */ +static uint32_t parse_opts(const char *in) +{ + uint32_t flags = 0; + + if (!in) { + return 0; + } + + if (strstr(in, "suggest_channel")) { + flags |= FTDM_ISDN_OPT_SUGGEST_CHANNEL; + } + + if (strstr(in, "omit_display")) { + flags |= FTDM_ISDN_OPT_OMIT_DISPLAY_IE; + } + + if (strstr(in, "disable_tones")) { + flags |= FTDM_ISDN_OPT_DISABLE_TONES; + } + + return flags; +} + +/** + * \brief Initialises an ISDN span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static FIO_SIG_CONFIGURE_FUNCTION(ftdm_isdn_configure_span) +{ + uint32_t i, x = 0; + ftdm_channel_t *dchans[2] = {0}; + ftdm_isdn_data_t *isdn_data; + const char *tonemap = "us"; + char *var, *val; + Q931Dialect_t dialect = Q931_Dialect_National; + int32_t digit_timeout = 0; + int q921loglevel = -1; + int q931loglevel = -1; +#ifdef HAVE_LIBPCAP + int q931topcap = -1; /*Q931ToPcap*/ + int openPcap = 0; /*Flag: open Pcap file please*/ +#endif + + if (span->signal_type) { +#ifdef HAVE_LIBPCAP + /*Q931ToPcap: Get the content of the q931topcap and pcapfilename args given by mod_freetdm */ + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "q931topcap")) { + q931topcap = va_arg(ap, int); + if(q931topcap==1) { + /*PCAP on*/; + openPcap=1; + } else if (q931topcap==0) { + /*PCAP off*/ + if (closePcapFile() != FTDM_SUCCESS) return FTDM_FAIL; + do_q931ToPcap=0; + return FTDM_SUCCESS; + } + } + if (!strcasecmp(var, "pcapfilename")) { + /*Put filename into global var*/ + pcapfn = va_arg(ap, char*); + } + } + /*We know now, that user wants to enable Q931ToPcap and what file name he wants, so open it please*/ + if(openPcap==1){ + if(openPcapFile() != FTDM_SUCCESS) return FTDM_FAIL; + do_q931ToPcap=1; + return FTDM_SUCCESS; + } + /*Q931ToPcap done*/ +#endif + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling [%d].", span->signal_type); + return FTDM_FAIL; + } + + if (span->trunk_type >= FTDM_TRUNK_NONE) { + ftdm_log(FTDM_LOG_WARNING, "Invalid trunk type '%s' defaulting to T1.\n", ftdm_trunk_type2str(span->trunk_type)); + span->trunk_type = FTDM_TRUNK_T1; + } + + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->type == FTDM_CHAN_TYPE_DQ921) { + if (x > 1) { + snprintf(span->last_error, sizeof(span->last_error), "Span has more than 2 D-Channels!"); + return FTDM_FAIL; + } else { + if (ftdm_channel_open(span->span_id, i, &dchans[x]) == FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_DEBUG, "opening d-channel #%d %d:%d\n", x, dchans[x]->span_id, dchans[x]->chan_id); + dchans[x]->state = FTDM_CHANNEL_STATE_UP; + x++; + } + } + } + } + + if (!x) { + snprintf(span->last_error, sizeof(span->last_error), "Span has no D-Channels!"); + return FTDM_FAIL; + } + + isdn_data = ftdm_malloc(sizeof(*isdn_data)); + assert(isdn_data != NULL); + memset(isdn_data, 0, sizeof(*isdn_data)); + + isdn_data->mode = Q931_TE; + dialect = Q931_Dialect_National; + + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "mode")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->mode = strcasecmp(val, "net") ? Q931_TE : Q931_NT; + } else if (!strcasecmp(var, "dialect")) { + if (!(val = va_arg(ap, char *))) { + break; + } + dialect = q931_str2Q931Dialect_type(val); + if (dialect == Q931_Dialect_Count) { + return FTDM_FAIL; + } + } else if (!strcasecmp(var, "opts")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->opts = parse_opts(val); + } else if (!strcasecmp(var, "tonemap")) { + if (!(val = va_arg(ap, char *))) { + break; + } + tonemap = (const char *)val; + } else if (!strcasecmp(var, "digit_timeout")) { + int *optp; + if (!(optp = va_arg(ap, int *))) { + break; + } + digit_timeout = *optp; + } else if (!strcasecmp(var, "q921loglevel")) { + q921loglevel = va_arg(ap, int); + if (q921loglevel < Q921_LOG_NONE) { + q921loglevel = Q921_LOG_NONE; + } else if (q921loglevel > Q921_LOG_DEBUG) { + q921loglevel = Q921_LOG_DEBUG; + } + } else if (!strcasecmp(var, "q931loglevel")) { + q931loglevel = va_arg(ap, int); + if (q931loglevel < Q931_LOG_NONE) { + q931loglevel = Q931_LOG_NONE; + } else if (q931loglevel > Q931_LOG_DEBUG) { + q931loglevel = Q931_LOG_DEBUG; + } + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return FTDM_FAIL; + } + } + + + if (!digit_timeout) { + digit_timeout = DEFAULT_DIGIT_TIMEOUT; + } + else if (digit_timeout < 3000 || digit_timeout > 30000) { + ftdm_log(FTDM_LOG_WARNING, "Digit timeout %d ms outside of range (3000 - 30000 ms), using default (10000 ms)\n", digit_timeout); + digit_timeout = DEFAULT_DIGIT_TIMEOUT; + } + + /* allocate per b-chan data */ + if (isdn_data->mode == Q931_NT) { + ftdm_isdn_bchan_data_t *data; + + data = ftdm_malloc((span->chan_count - 1) * sizeof(ftdm_isdn_bchan_data_t)); + if (!data) { + return FTDM_FAIL; + } + + for (i = 1; i <= span->chan_count; i++, data++) { + if (span->channels[i]->type == FTDM_CHAN_TYPE_B) { + span->channels[i]->mod_data = data; + memset(data, 0, sizeof(ftdm_isdn_bchan_data_t)); + } + } + } + + span->start = ftdm_isdn_start; + span->stop = ftdm_isdn_stop; + span->signal_cb = sig_cb; + isdn_data->dchans[0] = dchans[0]; + isdn_data->dchans[1] = dchans[1]; + isdn_data->dchan = isdn_data->dchans[0]; + isdn_data->digit_timeout = digit_timeout; + + Q921_InitTrunk(&isdn_data->q921, + 0, + 0, + isdn_data->mode, + span->trunk_type == FTDM_TRUNK_BRI_PTMP ? Q921_PTMP : Q921_PTP, + 0, + ftdm_isdn_921_21, + (Q921Tx23CB_t)ftdm_isdn_921_23, + span, + &isdn_data->q931); + + Q921SetLogCB(&isdn_data->q921, &ftdm_isdn_q921_log, isdn_data); + Q921SetLogLevel(&isdn_data->q921, (Q921LogLevel_t)q921loglevel); + + Q931Api_InitTrunk(&isdn_data->q931, + dialect, + isdn_data->mode, + span->trunk_type, + ftdm_isdn_931_34, + (Q931Tx32CB_t)q931_rx_32, + ftdm_isdn_931_err, + &isdn_data->q921, + span); + + Q931SetLogCB(&isdn_data->q931, &ftdm_isdn_q931_log, isdn_data); + Q931SetLogLevel(&isdn_data->q931, (Q931LogLevel_t)q931loglevel); + + isdn_data->q931.autoRestartAck = 1; + isdn_data->q931.autoConnectAck = 0; + isdn_data->q931.autoServiceAck = 1; + span->signal_data = isdn_data; + span->signal_type = FTDM_SIGTYPE_ISDN; + span->outgoing_call = isdn_outgoing_call; + + if ((isdn_data->opts & FTDM_ISDN_OPT_SUGGEST_CHANNEL)) { + span->channel_request = isdn_channel_request; + span->suggest_chan_id = 1; + } + span->state_map = &isdn_state_map; + + ftdm_span_load_tones(span, tonemap); + + return FTDM_SUCCESS; +} + +/** + * \brief FreeTDM ISDN signaling module definition + */ +EX_DECLARE_DATA ftdm_module_t ftdm_module = { + "isdn", + NULL, + close_pcap, + ftdm_isdn_init, + ftdm_isdn_configure_span, + NULL +}; + + + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_isdn/ozmod_isdn.2005.vcproj b/libs/freetdm/src/ftmod/ftmod_isdn/ozmod_isdn.2005.vcproj new file mode 100644 index 0000000000..5c8d2efc0f --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_isdn/ozmod_isdn.2005.vcproj @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c new file mode 100644 index 0000000000..929a874657 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c @@ -0,0 +1,1364 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "ftmod_libpri.h" + +/** + * \brief Unloads libpri IO module + * \return Success + */ +static FIO_IO_UNLOAD_FUNCTION(ftdm_libpri_unload) +{ + return FTDM_SUCCESS; +} + +/** + * \brief Starts a libpri channel (outgoing call) + * \param ftdmchan Channel to initiate call on + * \return Success or failure + */ +static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) +{ + ftdm_status_t status = FTDM_SUCCESS; + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALING); + return status; +} + +/** + * \brief Requests an libpri channel on a span (outgoing call) + * \param span Span where to get a channel (unused) + * \param chan_id Specific channel to get (0 for any) (unused) + * \param direction Call direction (unused) + * \param caller_data Caller information (unused) + * \param ftdmchan Channel to initialise (unused) + * \return Failure + */ +static FIO_CHANNEL_REQUEST_FUNCTION(isdn_channel_request) +{ + return FTDM_FAIL; +} + +#ifdef WIN32 +/** + * \brief Logs a libpri error + * \param s Error string + */ +static void s_pri_error(char *s) +#else +/** + * \brief Logs a libpri error + * \param pri libpri structure (unused) + * \param s Error string + */ +static void s_pri_error(struct pri *pri, char *s) +#endif +{ + ftdm_log(FTDM_LOG_ERROR, "%s", s); +} + +#ifdef WIN32 +/** + * \brief Logs a libpri message + * \param s Message string + */ +static void s_pri_message(char *s) +#else +/** + * \brief Logs a libpri message + * \param pri libpri structure (unused) + * \param s Message string + */ +static void s_pri_message(struct pri *pri, char *s) +#endif +{ + ftdm_log(FTDM_LOG_DEBUG, "%s", s); +} + +/** + * \brief Parses an option string to flags + * \param in String to parse for configuration options + * \return Flags + */ +static uint32_t parse_opts(const char *in) +{ + uint32_t flags = 0; + + if (!in) { + return 0; + } + + if (strstr(in, "suggest_channel")) { + flags |= FTMOD_LIBPRI_OPT_SUGGEST_CHANNEL; + } + + if (strstr(in, "omit_display")) { + flags |= FTMOD_LIBPRI_OPT_OMIT_DISPLAY_IE; + } + + if (strstr(in, "omit_redirecting_number")) { + flags |= FTMOD_LIBPRI_OPT_OMIT_REDIRECTING_NUMBER_IE; + } + + return flags; +} + +/** + * \brief Parses a debug string to flags + * \param in Debug string to parse for + * \return Flags + */ +static int parse_debug(const char *in) +{ + int flags = 0; + + if (!in) { + return 0; + } + + if (strstr(in, "q921_raw")) { + flags |= PRI_DEBUG_Q921_RAW; + } + + if (strstr(in, "q921_dump")) { + flags |= PRI_DEBUG_Q921_DUMP; + } + + if (strstr(in, "q921_state")) { + flags |= PRI_DEBUG_Q921_STATE; + } + + if (strstr(in, "config")) { + flags |= PRI_DEBUG_CONFIG; + } + + if (strstr(in, "q931_dump")) { + flags |= PRI_DEBUG_Q931_DUMP; + } + + if (strstr(in, "q931_state")) { + flags |= PRI_DEBUG_Q931_STATE; + } + + if (strstr(in, "q931_anomaly")) { + flags |= PRI_DEBUG_Q931_ANOMALY; + } + + if (strstr(in, "apdu")) { + flags |= PRI_DEBUG_APDU; + } + + if (strstr(in, "aoc")) { + flags |= PRI_DEBUG_AOC; + } + + if (strstr(in, "all")) { + flags |= PRI_DEBUG_ALL; + } + + if (strstr(in, "none")) { + flags = 0; + } + + return flags; +} + +static ftdm_io_interface_t ftdm_libpri_interface; + +static ftdm_status_t ftdm_libpri_start(ftdm_span_t *span); + +/** + * \brief API function to kill or debug a libpri span + * \param stream API stream handler + * \param data String containing argurments + * \return Flags + */ +static FIO_API_FUNCTION(ftdm_libpri_api) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + + if (data) { + mycmd = ftdm_strdup(data); + argc = ftdm_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (argc == 2) { + if (!strcasecmp(argv[0], "kill")) { + int span_id = atoi(argv[1]); + ftdm_span_t *span = NULL; + + if (ftdm_span_find_by_name(argv[1], &span) == FTDM_SUCCESS || ftdm_span_find(span_id, &span) == FTDM_SUCCESS) { + ftdm_libpri_data_t *isdn_data = span->signal_data; + + if (span->start != ftdm_libpri_start) { + stream->write_function(stream, "%s: -ERR invalid span.\n", __FILE__); + goto done; + } + + ftdm_clear_flag((&isdn_data->spri), LPWRAP_PRI_READY); + stream->write_function(stream, "%s: +OK killed.\n", __FILE__); + goto done; + } else { + stream->write_function(stream, "%s: -ERR invalid span.\n", __FILE__); + goto done; + } + } + } + + if (argc > 2) { + if (!strcasecmp(argv[0], "debug")) { + ftdm_span_t *span = NULL; + + if (ftdm_span_find_by_name(argv[1], &span) == FTDM_SUCCESS) { + ftdm_libpri_data_t *isdn_data = span->signal_data; + if (span->start != ftdm_libpri_start) { + stream->write_function(stream, "%s: -ERR invalid span.\n", __FILE__); + goto done; + } + + pri_set_debug(isdn_data->spri.pri, parse_debug(argv[2])); + stream->write_function(stream, "%s: +OK debug set.\n", __FILE__); + goto done; + } else { + stream->write_function(stream, "%s: -ERR invalid span.\n", __FILE__); + goto done; + } + } + + } + + stream->write_function(stream, "%s: -ERR invalid command.\n", __FILE__); + + done: + + ftdm_safe_free(mycmd); + + return FTDM_SUCCESS; +} + +/** + * \brief Loads libpri IO module + * \param fio FreeTDM IO interface + * \return Success + */ +static FIO_IO_LOAD_FUNCTION(ftdm_libpri_io_init) +{ + assert(fio != NULL); + memset(&ftdm_libpri_interface, 0, sizeof(ftdm_libpri_interface)); + + ftdm_libpri_interface.name = "libpri"; + ftdm_libpri_interface.api = ftdm_libpri_api; + + *fio = &ftdm_libpri_interface; + + return FTDM_SUCCESS; +} + +/** + * \brief Loads libpri signaling module + * \param fio FreeTDM IO interface + * \return Success + */ +static FIO_SIG_LOAD_FUNCTION(ftdm_libpri_init) +{ + pri_set_error(s_pri_error); + pri_set_message(s_pri_message); + return FTDM_SUCCESS; +} + +/** + * \brief libpri state map + */ +static ftdm_state_map_t isdn_state_map = { + { + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_ANY_STATE}, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + {FTDM_CHANNEL_STATE_DIALING, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DIALING, FTDM_END}, + {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_UP, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_UP, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END} + }, + + /****************************************/ + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_ANY_STATE}, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + {FTDM_CHANNEL_STATE_DIALTONE, FTDM_CHANNEL_STATE_RING, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DIALTONE, FTDM_END}, + {FTDM_CHANNEL_STATE_RING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, + FTDM_CHANNEL_STATE_CANCEL, FTDM_CHANNEL_STATE_UP, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_UP, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + }, + + + } +}; + +/** + * \brief Handler for channel state change + * \param ftdmchan Channel to handle + */ +static __inline__ void state_advance(ftdm_channel_t *ftdmchan) +{ + //Q931mes_Generic *gen = (Q931mes_Generic *) ftdmchan->caller_data.raw_data; + ftdm_libpri_data_t *isdn_data = ftdmchan->span->signal_data; + ftdm_status_t status; + ftdm_sigmsg_t sig; + q931_call *call = (q931_call *) ftdmchan->call_data; + + + ftdm_log(FTDM_LOG_DEBUG, "%d:%d STATE [%s]\n", + ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state)); + + +#if 0 + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) && !call) { + ftdm_log(FTDM_LOG_WARNING, "NO CALL!!!!\n"); + } +#endif + + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + + + switch (ftdmchan->state) { + case FTDM_CHANNEL_STATE_DOWN: + { + ftdmchan->call_data = NULL; + ftdm_channel_done(ftdmchan); + } + break; + case FTDM_CHANNEL_STATE_PROGRESS: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_PROGRESS; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else if (call) { + pri_progress(isdn_data->spri.pri, call, ftdmchan->chan_id, 1); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } + } + break; + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else if (call) { + pri_proceeding(isdn_data->spri.pri, call, ftdmchan->chan_id, 1); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } + } + break; + case FTDM_CHANNEL_STATE_RING: + { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + if (call) { + pri_acknowledge(isdn_data->spri.pri, call, ftdmchan->chan_id, 0); + sig.event_id = FTDM_SIGEVENT_START; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } + } + } + break; + case FTDM_CHANNEL_STATE_RESTART: + { + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_UNSPECIFIED; + sig.event_id = FTDM_SIGEVENT_RESTART; + status = ftdm_span_send_signal(ftdmchan->span, &sig); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + break; + case FTDM_CHANNEL_STATE_UP: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_UP; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else if (call) { + pri_answer(isdn_data->spri.pri, call, 0, 1); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } + } + break; + case FTDM_CHANNEL_STATE_DIALING: + if (isdn_data) { + struct pri_sr *sr; + int dp; + + if (!(call = pri_new_call(isdn_data->spri.pri))) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + return; + } + + + dp = ftdmchan->caller_data.dnis.type; + switch(dp) { + case FTDM_TON_NATIONAL: + dp = PRI_NATIONAL_ISDN; + break; + case FTDM_TON_INTERNATIONAL: + dp = PRI_INTERNATIONAL_ISDN; + break; + case FTDM_TON_SUBSCRIBER_NUMBER: + dp = PRI_LOCAL_ISDN; + break; + default: + dp = isdn_data->dp; + } + + ftdmchan->call_data = call; + sr = pri_sr_new(); + assert(sr); + pri_sr_set_channel(sr, ftdmchan->chan_id, 0, 0); + pri_sr_set_bearer(sr, 0, isdn_data->l1); + pri_sr_set_called(sr, ftdmchan->caller_data.dnis.digits, dp, 1); + pri_sr_set_caller(sr, ftdmchan->caller_data.cid_num.digits, (isdn_data->opts & FTMOD_LIBPRI_OPT_OMIT_DISPLAY_IE ? NULL : ftdmchan->caller_data.cid_name), + dp, (ftdmchan->caller_data.pres != 1 ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_PROHIB_USER_NUMBER_NOT_SCREENED)); + + if (!(isdn_data->opts & FTMOD_LIBPRI_OPT_OMIT_REDIRECTING_NUMBER_IE)) { + pri_sr_set_redirecting(sr, ftdmchan->caller_data.cid_num.digits, dp, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, PRI_REDIR_UNCONDITIONAL); + } + + if (pri_setup(isdn_data->spri.pri, call, sr)) { + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER; + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + + pri_sr_free(sr); + } + + break; + case FTDM_CHANNEL_STATE_HANGUP: + { + if (call) { + pri_hangup(isdn_data->spri.pri, call, ftdmchan->caller_data.hangup_cause); + pri_destroycall(isdn_data->spri.pri, call); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } + } + break; + case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: + break; + case FTDM_CHANNEL_STATE_TERMINATING: + { + sig.event_id = FTDM_SIGEVENT_STOP; + status = ftdm_span_send_signal(ftdmchan->span, &sig); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + + } + default: + break; + } + + + + return; +} + +/** + * \brief Checks current state on a span + * \param span Span to check status on + */ +static __inline__ void check_state(ftdm_span_t *span) +{ + if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) { + uint32_t j; + ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE); + for(j = 1; j <= span->chan_count; j++) { + if (ftdm_test_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) { + ftdm_mutex_lock(span->channels[j]->mutex); + ftdm_clear_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE); + state_advance(span->channels[j]); + ftdm_channel_complete_state(span->channels[j]); + ftdm_mutex_unlock(span->channels[j]->mutex); + } + } + } +} + +/** + * \brief Handler for libpri information event (incoming call?) + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_info(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + ftdm_log(FTDM_LOG_DEBUG, "number is: %s\n", pevent->ring.callednum); + if (strlen(pevent->ring.callednum) > 3) { + ftdm_log(FTDM_LOG_DEBUG, "final number is: %s\n", pevent->ring.callednum); + pri_answer(spri->pri, pevent->ring.call, 0, 1); + } + return 0; +} + +/** + * \brief Handler for libpri hangup event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_hangup(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + ftdm_span_t *span = spri->private_info; + ftdm_channel_t *ftdmchan = NULL; + q931_call *call = NULL; + ftdmchan = span->channels[pevent->hangup.channel]; + + if (ftdmchan) { + call = (q931_call *) ftdmchan->call_data; + ftdm_log(FTDM_LOG_DEBUG, "-- Hangup on channel %d:%d\n", spri->span->span_id, pevent->hangup.channel); + ftdmchan->caller_data.hangup_cause = pevent->hangup.cause; + pri_release(spri->pri, call, 0); + pri_destroycall(spri->pri, call); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); + } else { + ftdm_log(FTDM_LOG_DEBUG, "-- Hangup on channel %d:%d %s but it's not in use?\n", spri->span->span_id, + pevent->hangup.channel, ftdmchan->chan_id); + } + + return 0; +} + +/** + * \brief Handler for libpri answer event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_answer(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + ftdm_span_t *span = spri->private_info; + ftdm_channel_t *ftdmchan = NULL; + + ftdmchan = span->channels[pevent->answer.channel]; + + if (ftdmchan) { + ftdm_log(FTDM_LOG_DEBUG, "-- Answer on channel %d:%d\n", spri->span->span_id, pevent->answer.channel); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + } else { + ftdm_log(FTDM_LOG_DEBUG, "-- Answer on channel %d:%d %s but it's not in use?\n", spri->span->span_id, pevent->answer.channel, ftdmchan->chan_id); + + } + + return 0; +} + +/** + * \brief Handler for libpri proceed event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_proceed(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + ftdm_span_t *span = spri->private_info; + ftdm_channel_t *ftdmchan = NULL; + + ftdmchan = span->channels[pevent->proceeding.channel]; + + if (ftdmchan) { + ftdm_log(FTDM_LOG_DEBUG, "-- Proceeding on channel %d:%d\n", spri->span->span_id, pevent->proceeding.channel); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } else { + ftdm_log(FTDM_LOG_DEBUG, "-- Proceeding on channel %d:%d %s but it's not in use?\n", spri->span->span_id, + pevent->proceeding.channel, ftdmchan->chan_id); + } + + return 0; +} + +/** + * \brief Handler for libpri ringing event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_ringing(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + ftdm_span_t *span = spri->private_info; + ftdm_channel_t *ftdmchan = NULL; + + ftdmchan = span->channels[pevent->ringing.channel]; + + if (ftdmchan) { + ftdm_log(FTDM_LOG_DEBUG, "-- Ringing on channel %d:%d\n", spri->span->span_id, pevent->ringing.channel); + /* we may get on_ringing even when we're already in FTDM_CHANNEL_STATE_PROGRESS_MEDIA */ + if (ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { + /* dont try to move to STATE_PROGRESS to avoid annoying veto warning */ + return 0; + } + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } else { + ftdm_log(FTDM_LOG_DEBUG, "-- Ringing on channel %d:%d %s but it's not in use?\n", spri->span->span_id, + pevent->ringing.channel, ftdmchan->chan_id); + } + + return 0; +} + +/** + * \brief Handler for libpri ring event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 on success + */ +static int on_ring(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + ftdm_span_t *span = spri->private_info; + ftdm_channel_t *ftdmchan = NULL; + int ret = 0; + + //switch_mutex_lock(globals.channel_mutex); + + ftdmchan = span->channels[pevent->ring.channel]; + if (!ftdmchan || ftdmchan->state != FTDM_CHANNEL_STATE_DOWN || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE)) { + ftdm_log(FTDM_LOG_WARNING, "--Duplicate Ring on channel %d:%d (ignored)\n", spri->span->span_id, pevent->ring.channel); + ret = 0; + goto done; + } + + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_WARNING, "--Failure opening channel %d:%d (ignored)\n", spri->span->span_id, pevent->ring.channel); + ret = 0; + goto done; + } + + + ftdm_log(FTDM_LOG_NOTICE, "-- Ring on channel %d:%d (from %s to %s)\n", spri->span->span_id, pevent->ring.channel, + pevent->ring.callingnum, pevent->ring.callednum); + + memset(&ftdmchan->caller_data, 0, sizeof(ftdmchan->caller_data)); + + ftdm_set_string(ftdmchan->caller_data.cid_num.digits, (char *)pevent->ring.callingnum); + if (!ftdm_strlen_zero((char *)pevent->ring.callingname)) { + ftdm_set_string(ftdmchan->caller_data.cid_name, (char *)pevent->ring.callingname); + } else { + ftdm_set_string(ftdmchan->caller_data.cid_name, (char *)pevent->ring.callingnum); + } + ftdm_set_string(ftdmchan->caller_data.ani.digits, (char *)pevent->ring.callingani); + ftdm_set_string(ftdmchan->caller_data.dnis.digits, (char *)pevent->ring.callednum); + + if (pevent->ring.ani2 >= 0) { + snprintf(ftdmchan->caller_data.aniII, 5, "%.2d", pevent->ring.ani2); + } + + // scary to trust this pointer, you'd think they would give you a copy of the call data so you own it...... + ftdmchan->call_data = pevent->ring.call; + + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING); + + done: + //switch_mutex_unlock(globals.channel_mutex); + + return ret; +} + +/** + * \brief Processes freetdm event + * \param span Span on which the event was fired + * \param event Event to be treated + * \return Success or failure + */ +static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *event) +{ + ftdm_log(FTDM_LOG_DEBUG, "EVENT [%s][%d][%d:%d] STATE [%s]\n", + ftdm_oob_event2str(event->enum_id), event->enum_id, event->channel->span_id, event->channel->chan_id, ftdm_channel_state2str(event->channel->state)); + + switch(event->enum_id) { + case FTDM_OOB_ALARM_TRAP: + { + if (event->channel->state != FTDM_CHANNEL_STATE_DOWN) { + if (event->channel->type == FTDM_CHAN_TYPE_B) { + ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_RESTART); + } + } + + + ftdm_set_flag(event->channel, FTDM_CHANNEL_SUSPENDED); + + + ftdm_channel_get_alarms(event->channel); + ftdm_log(FTDM_LOG_WARNING, "channel %d:%d (%d:%d) has alarms! [%s]\n", + event->channel->span_id, event->channel->chan_id, + event->channel->physical_span_id, event->channel->physical_chan_id, + event->channel->last_error); + } + break; + case FTDM_OOB_ALARM_CLEAR: + { + + ftdm_log(FTDM_LOG_WARNING, "channel %d:%d (%d:%d) alarms Cleared!\n", event->channel->span_id, event->channel->chan_id, + event->channel->physical_span_id, event->channel->physical_chan_id); + + ftdm_clear_flag(event->channel, FTDM_CHANNEL_SUSPENDED); + ftdm_channel_get_alarms(event->channel); + } + break; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Checks for events on a span + * \param span Span to check for events + */ +static __inline__ void check_events(ftdm_span_t *span) +{ + ftdm_status_t status; + + status = ftdm_span_poll_event(span, 5); + + switch(status) { + case FTDM_SUCCESS: + { + ftdm_event_t *event; + while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) { + if (event->enum_id == FTDM_OOB_NOOP) { + continue; + } + if (process_event(span, event) != FTDM_SUCCESS) { + break; + } + } + } + break; + case FTDM_FAIL: + { + ftdm_log(FTDM_LOG_DEBUG, "Event Failure! %d\n", ftdm_running()); + ftdm_sleep(2000); + } + break; + default: + break; + } +} + +/** + * \brief Checks flags on a pri span + * \param spri Pri wrapper structure (libpri, span, dchan) + * \return 0 on success, -1 on error + */ +static int check_flags(lpwrap_pri_t *spri) +{ + ftdm_span_t *span = spri->private_info; + + if (!ftdm_running() || ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) { + return -1; + } + + check_state(span); + check_events(span); + + + return 0; +} + +/** + * \brief Handler for libpri restart event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_restart(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + ftdm_span_t *span = spri->private_info; + ftdm_channel_t *ftdmchan; + + ftdm_log(FTDM_LOG_NOTICE, "-- Restarting %d:%d\n", spri->span->span_id, pevent->restart.channel); + + ftdmchan = span->channels[pevent->restart.channel]; + + if (!ftdmchan) { + return 0; + } + + if (pevent->restart.channel < 1) { + ftdm_set_state_all(ftdmchan->span, FTDM_CHANNEL_STATE_RESTART); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART); + } + + return 0; +} + +/** + * \brief Handler for libpri dchan up event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_dchan_up(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + if (!ftdm_test_flag(spri, LPWRAP_PRI_READY)) { + ftdm_log(FTDM_LOG_INFO, "Span %d D-Chan UP!\n", spri->span->span_id); + ftdm_set_flag(spri, LPWRAP_PRI_READY); + ftdm_set_state_all(spri->span, FTDM_CHANNEL_STATE_RESTART); + } + + return 0; +} + +/** + * \brief Handler for libpri dchan down event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_dchan_down(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + if (ftdm_test_flag(spri, LPWRAP_PRI_READY)) { + ftdm_log(FTDM_LOG_INFO, "Span %d D-Chan DOWN!\n", spri->span->span_id); + ftdm_clear_flag(spri, LPWRAP_PRI_READY); + ftdm_set_state_all(spri->span, FTDM_CHANNEL_STATE_RESTART); + + } + + return 0; +} + +/** + * \brief Handler for any libpri event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_anything(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + ftdm_log(FTDM_LOG_DEBUG, "Caught Event span %d %u (%s)\n", spri->span->span_id, event_type, lpwrap_pri_event_str(event_type)); + return 0; +} + +/** + * \brief Handler for libpri io fail event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_io_fail(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + ftdm_log(FTDM_LOG_DEBUG, "Caught Event span %d %u (%s)\n", spri->span->span_id, event_type, lpwrap_pri_event_str(event_type)); + return 0; +} + +/** + * \brief Main thread function for libpri span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *ftdm_libpri_run(ftdm_thread_t *me, void *obj) +{ + ftdm_span_t *span = (ftdm_span_t *) obj; + ftdm_libpri_data_t *isdn_data = span->signal_data; + int i, x = 0; + int down = 0; + int got_d = 0; + + ftdm_set_flag(span, FTDM_SPAN_IN_THREAD); + + while(ftdm_running() && !ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) { + if (!got_d) { + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->type == FTDM_CHAN_TYPE_DQ921) { + if (ftdm_channel_open(span->span_id, i, &isdn_data->dchan) == FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_DEBUG, "opening d-channel #%d %d:%d\n", x, isdn_data->dchan->span_id, isdn_data->dchan->chan_id); + isdn_data->dchan->state = FTDM_CHANNEL_STATE_UP; + got_d = 1; + x++; + break; + } + } + } + } + + + if (lpwrap_init_pri(&isdn_data->spri, + span, // span + isdn_data->dchan, // dchan + isdn_data->pswitch, + isdn_data->node, + isdn_data->debug) < 0) { + snprintf(span->last_error, sizeof(span->last_error), "PRI init FAIL!"); + } else { + + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANY, on_anything); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RING, on_ring); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RINGING, on_ringing); + //LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_SETUP_ACK, on_proceed); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_PROCEEDING, on_proceed); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANSWER, on_answer); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_DCHAN_UP, on_dchan_up); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_DCHAN_DOWN, on_dchan_down); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP_REQ, on_hangup); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP, on_hangup); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_INFO_RECEIVED, on_info); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RESTART, on_restart); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_IO_FAIL, on_io_fail); + + if (down) { + ftdm_log(FTDM_LOG_INFO, "PRI back up on span %d\n", isdn_data->spri.span->span_id); + ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART); + down = 0; + } + + isdn_data->spri.on_loop = check_flags; + isdn_data->spri.private_info = span; + lpwrap_run_pri(&isdn_data->spri); + + } + + if (!ftdm_running() || ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) { + break; + } + + ftdm_log(FTDM_LOG_CRIT, "PRI down on span %d\n", isdn_data->spri.span->span_id); + + if (!down) { + ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART); + check_state(span); + } + + check_state(span); + check_events(span); + + down++; + ftdm_sleep(5000); + } + + ftdm_log(FTDM_LOG_DEBUG, "PRI thread ended on span %d\n", isdn_data->spri.span->span_id); + + ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD); + ftdm_clear_flag(isdn_data, FTMOD_LIBPRI_RUNNING); + + return NULL; +} + +/** + * \brief Stops a libpri span + * \param span Span to halt + * \return Success + * + * Sets a stop flag and waits for the thread to end + */ +static ftdm_status_t ftdm_libpri_stop(ftdm_span_t *span) +{ + ftdm_libpri_data_t *isdn_data = span->signal_data; + + if (!ftdm_test_flag(isdn_data, FTMOD_LIBPRI_RUNNING)) { + return FTDM_FAIL; + } + + ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART); + check_state(span); + ftdm_set_flag(span, FTDM_SPAN_STOP_THREAD); + while(ftdm_test_flag(span, FTDM_SPAN_IN_THREAD)) { + ftdm_sleep(100); + } + check_state(span); + + return FTDM_SUCCESS; +} + +/** + * \brief Starts a libpri span + * \param span Span to halt + * \return Success or failure + * + * Launches a thread to monitor the span + */ +static ftdm_status_t ftdm_libpri_start(ftdm_span_t *span) +{ + ftdm_status_t ret; + ftdm_libpri_data_t *isdn_data = span->signal_data; + + if (ftdm_test_flag(isdn_data, FTMOD_LIBPRI_RUNNING)) { + return FTDM_FAIL; + } + + ftdm_clear_flag(span, FTDM_SPAN_STOP_THREAD); + ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD); + + ftdm_set_flag(isdn_data, FTMOD_LIBPRI_RUNNING); + ret = ftdm_thread_create_detached(ftdm_libpri_run, span); + + if (ret != FTDM_SUCCESS) { + return ret; + } + + return ret; +} + +/** + * \brief Converts a node string to node value + * \param node Node string to convert + * \return -1 on failure, node value on success + */ +static int str2node(char *node) +{ + if (!strcasecmp(node, "cpe") || !strcasecmp(node, "user")) + return PRI_CPE; + if (!strcasecmp(node, "network") || !strcasecmp(node, "net")) + return PRI_NETWORK; + return -1; +} + +/** + * \brief Converts a switch string to switch value + * \param swtype Swtype string to convert + * \return Switch value + */ +static int str2switch(char *swtype) +{ + if (!strcasecmp(swtype, "ni1")) + return PRI_SWITCH_NI1; + if (!strcasecmp(swtype, "ni2")) + return PRI_SWITCH_NI2; + if (!strcasecmp(swtype, "dms100")) + return PRI_SWITCH_DMS100; + if (!strcasecmp(swtype, "lucent5e") || !strcasecmp(swtype, "5ess")) + return PRI_SWITCH_LUCENT5E; + if (!strcasecmp(swtype, "att4ess") || !strcasecmp(swtype, "4ess")) + return PRI_SWITCH_ATT4ESS; + if (!strcasecmp(swtype, "euroisdn")) + return PRI_SWITCH_EUROISDN_E1; + if (!strcasecmp(swtype, "gr303eoc")) + return PRI_SWITCH_GR303_EOC; + if (!strcasecmp(swtype, "gr303tmc")) + return PRI_SWITCH_GR303_TMC; + return PRI_SWITCH_DMS100; +} + +/** + * \brief Converts a L1 string to L1 value + * \param l1 L1 string to convert + * \return L1 value + */ +static int str2l1(char *l1) +{ + if (!strcasecmp(l1, "alaw")) + return PRI_LAYER_1_ALAW; + + return PRI_LAYER_1_ULAW; +} + +/** + * \brief Converts a DP string to DP value + * \param dp DP string to convert + * \return DP value + */ +static int str2dp(char *dp) +{ + if (!strcasecmp(dp, "international")) + return PRI_INTERNATIONAL_ISDN; + if (!strcasecmp(dp, "national")) + return PRI_NATIONAL_ISDN; + if (!strcasecmp(dp, "local")) + return PRI_LOCAL_ISDN; + if (!strcasecmp(dp, "private")) + return PRI_PRIVATE; + if (!strcasecmp(dp, "unknown")) + return PRI_UNKNOWN; + + return PRI_UNKNOWN; +} + +/** + * \brief Initialises a libpri span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static FIO_SIG_CONFIGURE_FUNCTION(ftdm_libpri_configure_span) +{ + uint32_t i, x = 0; + //ftdm_channel_t *dchans[2] = {0}; + ftdm_libpri_data_t *isdn_data; + char *var, *val; + char *debug = NULL; + + if (span->trunk_type >= FTDM_TRUNK_NONE) { + ftdm_log(FTDM_LOG_WARNING, "Invalid trunk type '%s' defaulting to T1.\n", ftdm_trunk_type2str(span->trunk_type)); + span->trunk_type = FTDM_TRUNK_T1; + } + + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->type == FTDM_CHAN_TYPE_DQ921) { + if (x > 1) { + snprintf(span->last_error, sizeof(span->last_error), "Span has more than 2 D-Channels!"); + return FTDM_FAIL; + } else { +#if 0 + if (ftdm_channel_open(span->span_id, i, &dchans[x]) == FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_DEBUG, "opening d-channel #%d %d:%d\n", x, dchans[x]->span_id, dchans[x]->chan_id); + dchans[x]->state = FTDM_CHANNEL_STATE_UP; + x++; + } +#endif + } + } + } + +#if 0 + if (!x) { + snprintf(span->last_error, sizeof(span->last_error), "Span has no D-Channels!"); + return FTDM_FAIL; + } +#endif + + isdn_data = ftdm_malloc(sizeof(*isdn_data)); + assert(isdn_data != NULL); + memset(isdn_data, 0, sizeof(*isdn_data)); + + if (span->trunk_type == FTDM_TRUNK_E1) { + ftdm_log(FTDM_LOG_NOTICE, "Setting default Layer 1 to ALAW since this is an E1 trunk\n"); + isdn_data->l1 = PRI_LAYER_1_ALAW; + } else if (span->trunk_type == FTDM_TRUNK_T1) { + ftdm_log(FTDM_LOG_NOTICE, "Setting default Layer 1 to ULAW since this is a T1 trunk\n"); + isdn_data->l1 = PRI_LAYER_1_ULAW; + } + + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "node")) { + int node; + if (!(val = va_arg(ap, char *))) { + break; + } + node = str2node(val); + if (-1 == node) { + ftdm_log(FTDM_LOG_ERROR, "Unknown node type %s, defaulting to CPE mode\n", val); + node = PRI_CPE; + } + isdn_data->node = node; + } else if (!strcasecmp(var, "switch")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->pswitch = str2switch(val); + } else if (!strcasecmp(var, "opts")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->opts = parse_opts(val); + } else if (!strcasecmp(var, "dp")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->dp = str2dp(val); + } else if (!strcasecmp(var, "l1")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->l1 = str2l1(val); + } else if (!strcasecmp(var, "debug")) { + if (!(val = va_arg(ap, char *))) { + break; + } + debug = val; + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return FTDM_FAIL; + } + } + + span->start = ftdm_libpri_start; + span->stop = ftdm_libpri_stop; + span->signal_cb = sig_cb; + //isdn_data->dchans[0] = dchans[0]; + //isdn_data->dchans[1] = dchans[1]; + //isdn_data->dchan = isdn_data->dchans[0]; + + isdn_data->debug = parse_debug(debug); + + + span->signal_data = isdn_data; + span->signal_type = FTDM_SIGTYPE_ISDN; + span->outgoing_call = isdn_outgoing_call; + + if ((isdn_data->opts & FTMOD_LIBPRI_OPT_SUGGEST_CHANNEL)) { + span->channel_request = isdn_channel_request; + span->suggest_chan_id = 1; + } + + span->state_map = &isdn_state_map; + + return FTDM_SUCCESS; +} + +/** + * \brief FreeTDM libpri signaling and IO module definition + */ +ftdm_module_t ftdm_module = { + "libpri", + ftdm_libpri_io_init, + ftdm_libpri_unload, + ftdm_libpri_init, + ftdm_libpri_configure_span, + NULL +}; + + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h new file mode 100644 index 0000000000..0bf8c46817 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2009, Anthony Minessale II + * 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_LIBPRI_H +#define FTMOD_LIBPRI_H +#include "freetdm.h" +#include "lpwrap_pri.h" + +typedef enum { + FTMOD_LIBPRI_OPT_NONE = 0, + FTMOD_LIBPRI_OPT_SUGGEST_CHANNEL = (1 << 0), + FTMOD_LIBPRI_OPT_OMIT_DISPLAY_IE = (1 << 1), + FTMOD_LIBPRI_OPT_OMIT_REDIRECTING_NUMBER_IE = (1 << 2), + + FTMOD_LIBPRI_OPT_MAX = (1 << 3) +} ftdm_isdn_opts_t; + +typedef enum { + FTMOD_LIBPRI_RUNNING = (1 << 0) +} ftdm_isdn_flag_t; + + +struct ftdm_libpri_data { + ftdm_channel_t *dchan; + ftdm_channel_t *dchans[2]; + struct ftdm_sigmsg sigmsg; + uint32_t flags; + int32_t mode; + ftdm_isdn_opts_t opts; + + int node; + int pswitch; + char *dialplan; + unsigned int l1; + unsigned int dp; + + int debug; + + lpwrap_pri_t spri; +}; + +typedef struct ftdm_libpri_data ftdm_libpri_data_t; + + +/* b-channel private data */ +struct ftdm_isdn_bchan_data +{ + int32_t digit_timeout; +}; + +typedef struct ftdm_isdn_bchan_data ftdm_isdn_bchan_data_t; + + +#endif + +/* 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: + */ + diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c new file mode 100644 index 0000000000..de6a311ef9 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2009, Anthony Minessale II + * 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. + */ + +//#define IODEBUG + +#include "freetdm.h" +#include "lpwrap_pri.h" +#ifndef HAVE_GETTIMEOFDAY + +#ifdef WIN32 +#include + +static __inline int gettimeofday(struct timeval *tp, void *nothing) +{ +#ifdef WITHOUT_MM_LIB + SYSTEMTIME st; + time_t tt; + struct tm tmtm; + /* mktime converts local to UTC */ + GetLocalTime (&st); + tmtm.tm_sec = st.wSecond; + tmtm.tm_min = st.wMinute; + tmtm.tm_hour = st.wHour; + tmtm.tm_mday = st.wDay; + tmtm.tm_mon = st.wMonth - 1; + tmtm.tm_year = st.wYear - 1900; tmtm.tm_isdst = -1; + tt = mktime (&tmtm); + tp->tv_sec = tt; + tp->tv_usec = st.wMilliseconds * 1000; +#else + /** + ** The earlier time calculations using GetLocalTime + ** had a time resolution of 10ms.The timeGetTime, part + ** of multimedia apis offer a better time resolution + ** of 1ms.Need to link against winmm.lib for this + **/ + unsigned long Ticks = 0; + unsigned long Sec =0; + unsigned long Usec = 0; + Ticks = timeGetTime(); + + Sec = Ticks/1000; + Usec = (Ticks - (Sec*1000))*1000; + tp->tv_sec = Sec; + tp->tv_usec = Usec; +#endif /* WITHOUT_MM_LIB */ + (void)nothing; + return 0; +} +#endif /* WIN32 */ +#endif /* HAVE_GETTIMEOFDAY */ + +static struct lpwrap_pri_event_list LPWRAP_PRI_EVENT_LIST[] = { + {0, LPWRAP_PRI_EVENT_ANY, "ANY"}, + {1, LPWRAP_PRI_EVENT_DCHAN_UP, "DCHAN_UP"}, + {2, LPWRAP_PRI_EVENT_DCHAN_DOWN, "DCHAN_DOWN"}, + {3, LPWRAP_PRI_EVENT_RESTART, "RESTART"}, + {4, LPWRAP_PRI_EVENT_CONFIG_ERR, "CONFIG_ERR"}, + {5, LPWRAP_PRI_EVENT_RING, "RING"}, + {6, LPWRAP_PRI_EVENT_HANGUP, "HANGUP"}, + {7, LPWRAP_PRI_EVENT_RINGING, "RINGING"}, + {8, LPWRAP_PRI_EVENT_ANSWER, "ANSWER"}, + {9, LPWRAP_PRI_EVENT_HANGUP_ACK, "HANGUP_ACK"}, + {10, LPWRAP_PRI_EVENT_RESTART_ACK, "RESTART_ACK"}, + {11, LPWRAP_PRI_EVENT_FACNAME, "FACNAME"}, + {12, LPWRAP_PRI_EVENT_INFO_RECEIVED, "INFO_RECEIVED"}, + {13, LPWRAP_PRI_EVENT_PROCEEDING, "PROCEEDING"}, + {14, LPWRAP_PRI_EVENT_SETUP_ACK, "SETUP_ACK"}, + {15, LPWRAP_PRI_EVENT_HANGUP_REQ, "HANGUP_REQ"}, + {16, LPWRAP_PRI_EVENT_NOTIFY, "NOTIFY"}, + {17, LPWRAP_PRI_EVENT_PROGRESS, "PROGRESS"}, + {18, LPWRAP_PRI_EVENT_KEYPAD_DIGIT, "KEYPAD_DIGIT"}, + {19, LPWRAP_PRI_EVENT_IO_FAIL, "IO_FAIL"}, +}; + +#define LINE "--------------------------------------------------------------------------------" + +const char *lpwrap_pri_event_str(lpwrap_pri_event_t event_id) +{ + return LPWRAP_PRI_EVENT_LIST[event_id].name; +} + +static int __pri_lpwrap_read(struct pri *pri, void *buf, int buflen) +{ + struct lpwrap_pri *spri = (struct lpwrap_pri *) pri_get_userdata(pri); + ftdm_size_t len = buflen; + int res; + ftdm_status_t zst; + + if ((zst = ftdm_channel_read(spri->dchan, buf, &len)) != FTDM_SUCCESS) { + if (zst == FTDM_FAIL) { + ftdm_log(FTDM_LOG_CRIT, "span %d D-READ FAIL! [%s]\n", spri->span->span_id, spri->dchan->last_error); + spri->errs++; + } else { + ftdm_log(FTDM_LOG_CRIT, "span %d D-READ TIMEOUT\n", spri->span->span_id); + } + + ftdm_clear_flag(spri, LPWRAP_PRI_READY); + return -1; + } + spri->errs = 0; + res = (int)len; + memset(&((unsigned char*)buf)[res],0,2); + res+=2; + +#ifdef IODEBUG + { + char bb[2048] = { 0 }; + + print_hex_bytes(buf, res - 2, bb, sizeof(bb)); + ftdm_log(FTDM_LOG_DEBUG, "READ %d\n", res-2); + } +#endif + + return res; +} + +static int __pri_lpwrap_write(struct pri *pri, void *buf, int buflen) +{ + struct lpwrap_pri *spri = (struct lpwrap_pri *) pri_get_userdata(pri); + ftdm_size_t len = buflen -2; + + if (ftdm_channel_write(spri->dchan, buf, buflen, &len) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_CRIT, "span %d D-WRITE FAIL! [%s]\n", spri->span->span_id, spri->dchan->last_error); + ftdm_clear_flag(spri, LPWRAP_PRI_READY); + return -1; + } + +#ifdef IODEBUG + { + char bb[2048] = { 0 }; + + print_hex_bytes(buf, buflen - 2, bb, sizeof(bb)); + ftdm_log(FTDM_LOG_DEBUG, "WRITE %d\n", (int)buflen-2); + } +#endif + + return (int) buflen; +} + +int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *dchan, int swtype, int node, int debug) +{ + int ret = -1; + + memset(spri, 0, sizeof(struct lpwrap_pri)); + + spri->dchan = dchan; + spri->span = span; + + if ((spri->pri = pri_new_cb(spri->dchan->sockfd, node, swtype, __pri_lpwrap_read, __pri_lpwrap_write, spri))){ + unsigned char buf[4] = { 0 }; + size_t buflen = sizeof(buf), len = 0; + pri_set_debug(spri->pri, debug); + ret = 0; + ftdm_set_flag(spri, LPWRAP_PRI_READY); + ftdm_channel_write(spri->dchan, buf, buflen, &len); + } else { + fprintf(stderr, "Unable to create PRI\n"); + } + + return ret; +} + + +int lpwrap_one_loop(struct lpwrap_pri *spri) +{ + fd_set rfds, efds; + struct timeval now = {0,0}, *next; + pri_event *event; + event_handler handler; + int sel; + + if (spri->on_loop) { + if ((sel = spri->on_loop(spri)) < 0) { + return sel; + } + } + + if (spri->errs >= 2) { + spri->errs = 0; + return -1; + } + + FD_ZERO(&rfds); + FD_ZERO(&efds); + +#ifdef _MSC_VER + //Windows macro for FD_SET includes a warning C4127: conditional expression is constant +#pragma warning(push) +#pragma warning(disable:4127) +#endif + + FD_SET(pri_fd(spri->pri), &rfds); + FD_SET(pri_fd(spri->pri), &efds); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + now.tv_sec = 0; + now.tv_usec = 100000; + + sel = select(pri_fd(spri->pri) + 1, &rfds, NULL, &efds, &now); + + event = NULL; + + if (!sel) { + if ((next = pri_schedule_next(spri->pri))) { + gettimeofday(&now, NULL); + if (now.tv_sec >= next->tv_sec && (now.tv_usec >= next->tv_usec || next->tv_usec <= 100000)) { + //ftdm_log(FTDM_LOG_DEBUG, "Check event\n"); + event = pri_schedule_run(spri->pri); + } + } + } else if (sel > 0) { + event = pri_check_event(spri->pri); + } + + if (event) { + /* 0 is catchall event handler */ + if ((handler = spri->eventmap[event->e] ? spri->eventmap[event->e] : spri->eventmap[0] ? spri->eventmap[0] : NULL)) { + handler(spri, event->e, event); + } else { + ftdm_log(FTDM_LOG_CRIT, "No event handler found for event %d.\n", event->e); + } + } + + + return sel; + + + if ((handler = spri->eventmap[LPWRAP_PRI_EVENT_IO_FAIL] ? spri->eventmap[LPWRAP_PRI_EVENT_IO_FAIL] : spri->eventmap[0] ? spri->eventmap[0] : NULL)) { + handler(spri, LPWRAP_PRI_EVENT_IO_FAIL, NULL); + } + + return -1; +} + +int lpwrap_run_pri(struct lpwrap_pri *spri) +{ + int ret = 0; + + for (;;){ + ret = lpwrap_one_loop(spri); + + if (ret < 0) { + +#ifndef WIN32 //This needs to be adressed fror WIN32 still + if (errno == EINTR){ + /* Igonore an interrupted system call */ + continue; + } +#endif + ftdm_log(FTDM_LOG_CRIT, "Error = %i [%s]\n", ret, strerror(errno)); + break; + } + } + + return ret; + +} + +/* 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: + */ + diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h new file mode 100644 index 0000000000..f92c5bbafa --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2009, Anthony Minessale II + * 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 _LPWRAP_PRI_H +#define _LPWRAP_PRI_H +#include +#include + + +#define LPWRAP_MAX_CHAN_PER_SPAN 32 + +typedef enum { + LPWRAP_PRI_EVENT_ANY = 0, + LPWRAP_PRI_EVENT_DCHAN_UP = PRI_EVENT_DCHAN_UP, + LPWRAP_PRI_EVENT_DCHAN_DOWN = PRI_EVENT_DCHAN_DOWN, + LPWRAP_PRI_EVENT_RESTART = PRI_EVENT_RESTART, + LPWRAP_PRI_EVENT_CONFIG_ERR = PRI_EVENT_CONFIG_ERR, + LPWRAP_PRI_EVENT_RING = PRI_EVENT_RING, + LPWRAP_PRI_EVENT_HANGUP = PRI_EVENT_HANGUP, + LPWRAP_PRI_EVENT_RINGING = PRI_EVENT_RINGING, + LPWRAP_PRI_EVENT_ANSWER = PRI_EVENT_ANSWER, + LPWRAP_PRI_EVENT_HANGUP_ACK = PRI_EVENT_HANGUP_ACK, + LPWRAP_PRI_EVENT_RESTART_ACK = PRI_EVENT_RESTART_ACK, + LPWRAP_PRI_EVENT_FACNAME = PRI_EVENT_FACNAME, + LPWRAP_PRI_EVENT_INFO_RECEIVED = PRI_EVENT_INFO_RECEIVED, + LPWRAP_PRI_EVENT_PROCEEDING = PRI_EVENT_PROCEEDING, + LPWRAP_PRI_EVENT_SETUP_ACK = PRI_EVENT_SETUP_ACK, + LPWRAP_PRI_EVENT_HANGUP_REQ = PRI_EVENT_HANGUP_REQ, + LPWRAP_PRI_EVENT_NOTIFY = PRI_EVENT_NOTIFY, + LPWRAP_PRI_EVENT_PROGRESS = PRI_EVENT_PROGRESS, + LPWRAP_PRI_EVENT_KEYPAD_DIGIT = PRI_EVENT_KEYPAD_DIGIT, + LPWRAP_PRI_EVENT_IO_FAIL = 19, + + /* don't touch */ + LPWRAP_PRI_EVENT_MAX +} lpwrap_pri_event_t; + +typedef enum { + LPWRAP_PRI_NETWORK = PRI_NETWORK, + LPWRAP_PRI_CPE = PRI_CPE +} lpwrap_pri_node_t; + +typedef enum { + LPWRAP_PRI_SWITCH_UNKNOWN = PRI_SWITCH_UNKNOWN, + LPWRAP_PRI_SWITCH_NI2 = PRI_SWITCH_NI2, + LPWRAP_PRI_SWITCH_DMS100 = PRI_SWITCH_DMS100, + LPWRAP_PRI_SWITCH_LUCENT5E = PRI_SWITCH_LUCENT5E, + LPWRAP_PRI_SWITCH_ATT4ESS = PRI_SWITCH_ATT4ESS, + LPWRAP_PRI_SWITCH_EUROISDN_E1 = PRI_SWITCH_EUROISDN_E1, + LPWRAP_PRI_SWITCH_EUROISDN_T1 = PRI_SWITCH_EUROISDN_T1, + LPWRAP_PRI_SWITCH_NI1 = PRI_SWITCH_NI1, + LPWRAP_PRI_SWITCH_GR303_EOC = PRI_SWITCH_GR303_EOC, + LPWRAP_PRI_SWITCH_GR303_TMC = PRI_SWITCH_GR303_TMC, + LPWRAP_PRI_SWITCH_QSIG = PRI_SWITCH_QSIG, + + /* don't touch */ + LPWRAP_PRI_SWITCH_MAX +} lpwrap_pri_switch_t; + +typedef enum { + LPWRAP_PRI_READY = (1 << 0) +} lpwrap_pri_flag_t; + +struct lpwrap_pri; +typedef int (*event_handler)(struct lpwrap_pri *, lpwrap_pri_event_t, pri_event *); +typedef int (*loop_handler)(struct lpwrap_pri *); + +struct lpwrap_pri { + struct pri *pri; + ftdm_span_t *span; + ftdm_channel_t *dchan; + unsigned int flags; + void *private_info; + event_handler eventmap[LPWRAP_PRI_EVENT_MAX]; + loop_handler on_loop; + int errs; +}; + +typedef struct lpwrap_pri lpwrap_pri_t; + +struct lpwrap_pri_event_list { + int event_id; + int pri_event; + const char *name; +}; + + + +#define LPWRAP_MAP_PRI_EVENT(spri, event, func) spri.eventmap[event] = func; + +const char *lpwrap_pri_event_str(lpwrap_pri_event_t event_id); +int lpwrap_one_loop(struct lpwrap_pri *spri); +int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *dchan, int swtype, int node, int debug); +int lpwrap_run_pri(struct lpwrap_pri *spri); + +#endif diff --git a/libs/freetdm/src/ftmod/ftmod_pika/ftdm_pika.h b/libs/freetdm/src/ftmod/ftmod_pika/ftdm_pika.h new file mode 100644 index 0000000000..882eaba411 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_pika/ftdm_pika.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 FTDM_PIKA_H +#define FTDM_PIKA_H +#include "freetdm.h" +#include "pikahmpapi.h" + + + +#define PIKA_STR2ENUM_P(_FUNC1, _FUNC2, _TYPE) _TYPE _FUNC1 (const char *name); const char * _FUNC2 (_TYPE type); +#define PIKA_STR2ENUM(_FUNC1, _FUNC2, _TYPE, _STRINGS, _MAX) \ + _TYPE _FUNC1 (const char *name) \ + { \ + int i; \ + _TYPE t = _MAX ; \ + \ + for (i = 0; i < _MAX ; i++) { \ + if (!strcasecmp(name, _STRINGS[i])) { \ + t = (_TYPE) i; \ + break; \ + } \ + } \ + \ + return t; \ + } \ + const char * _FUNC2 (_TYPE type) \ + { \ + if (type > _MAX) { \ + type = _MAX; \ + } \ + return _STRINGS[(int)type]; \ + } + + +typedef enum { + PIKA_SPAN_FRAMING_T1_D4, + PIKA_SPAN_FRAMING_T1_ESF, + PIKA_SPAN_FRAMING_E1_BASIC, + PIKA_SPAN_FRAMING_E1_CRC4, + PIKA_SPAN_INVALID +} PIKA_TSpanFraming; +#define PIKA_SPAN_STRINGS "T1_D4", "T1_ESF", "E1_BASIC", "E1_CRC4" +PIKA_STR2ENUM_P(pika_str2span, pika_span2str, PIKA_TSpanFraming) + +typedef enum { + PIKA_SPAN_ENCODING_T1_AMI_ZS_NONE, + PIKA_SPAN_ENCODING_T1_AMI_ZS_GTE, + PIKA_SPAN_ENCODING_T1_AMI_ZS_BELL, + PIKA_SPAN_ENCODING_T1_AMI_ZS_JAM8, + PIKA_SPAN_ENCODING_T1_B8ZS, + PIKA_SPAN_ENCODING_E1_AMI, + PIKA_SPAN_ENCODING_E1_HDB3, + PIKA_SPAN_ENCODING_INVALID +} PIKA_TSpanEncoding; +#define PIKA_SPAN_ENCODING_STRINGS "T1_AMI_ZS_NONE", "T1_AMI_ZS_GTE", "T1_AMI_ZS_BELL", "T1_AMI_ZS_JAM8", "T1_B8ZS", "E1_AMI", "E1_HDB3" +PIKA_STR2ENUM_P(pika_str2span_encoding, pika_span_encoding2str, PIKA_TSpanEncoding) + +typedef enum { + PIKA_SPAN_LOOP_LENGTH_SHORT_HAUL, + PIKA_SPAN_LOOP_LENGTH_LONG_HAUL, + PIKA_SPAN_LOOP_INVALID +} PIKA_TSpanLoopLength; +#define PIKA_LL_STRINGS "SHORT_HAUL", "LONG_HAUL" +PIKA_STR2ENUM_P(pika_str2loop_length, pika_loop_length2str, PIKA_TSpanLoopLength) + +typedef enum { + PIKA_SPAN_LBO_T1_LONG_0_DB, + PIKA_SPAN_LBO_T1_LONG_7_DB, + PIKA_SPAN_LBO_T1_LONG_15_DB, + PIKA_SPAN_LBO_T1_LONG_22_DB, + PIKA_SPAN_LBO_T1_SHORT_133_FT, + PIKA_SPAN_LBO_T1_SHORT_266_FT, + PIKA_SPAN_LBO_T1_SHORT_399_FT, + PIKA_SPAN_LBO_T1_SHORT_533_FT, + PIKA_SPAN_LBO_T1_SHORT_655_FT, + PIKA_SPAN_LBO_E1_WAVEFORM_120_OHM, + PIKA_SPAN_LBO_INVALID +} PIKA_TSpanBuildOut; +#define PIKA_LBO_STRINGS "T1_LONG_0_DB", "T1_LONG_7_DB", "T1_LONG_15_DB", "T1_LONG_22_DB", "T1_SHORT_133_FT", "T1_SHORT_266_FT", "T1_SHORT_399_FT", "T1_SHORT_533_FT", "T1_SHORT_655_FT", "E1_WAVEFORM_120_OHM" +PIKA_STR2ENUM_P(pika_str2lbo, pika_lbo2str, PIKA_TSpanBuildOut) + +typedef enum { + PIKA_SPAN_COMPAND_MODE_MU_LAW = 1, + PIKA_SPAN_COMPAND_MODE_A_LAW, + PIKA_SPAN_COMPAND_MODE_INVALID +} PIKA_TSpanCompandMode; +#define PIKA_SPAN_COMPAND_MODE_STRINGS "MU_LAW", "A_LAW" +PIKA_STR2ENUM_P(pika_str2compand_mode, pika_compand_mode2str, PIKA_TSpanCompandMode) + +#endif + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_pika/ftmod_pika.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_pika/ftmod_pika.2008.vcproj new file mode 100644 index 0000000000..905fdd0b5c --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_pika/ftmod_pika.2008.vcproj @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_pika/ftmod_pika.c b/libs/freetdm/src/ftmod/ftmod_pika/ftmod_pika.c new file mode 100644 index 0000000000..f20cbc53a7 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_pika/ftmod_pika.c @@ -0,0 +1,1469 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "ftdm_pika.h" + + +#define MAX_NUMBER_OF_TRUNKS 64 +#define PIKA_BLOCK_SIZE 160 +#define PIKA_BLOCK_LEN 20 +#define PIKA_NUM_BUFFERS 8 +#define TRY_OR_DIE(__code, __status, __label) if ((status = __code ) != __status) goto __label +#define pk_atof(__a) (PK_FLOAT) atof(__a) + +PK_VOID PK_CALLBACK media_out_callback(PKH_TPikaEvent *event); + +FTDM_ENUM_NAMES(PIKA_SPAN_NAMES, PIKA_SPAN_STRINGS) +PIKA_STR2ENUM(pika_str2span, pika_span2str, PIKA_TSpanFraming, PIKA_SPAN_NAMES, PIKA_SPAN_INVALID) + +FTDM_ENUM_NAMES(PIKA_SPAN_ENCODING_NAMES, PIKA_SPAN_ENCODING_STRINGS) +PIKA_STR2ENUM(pika_str2span_encoding, pika_span_encoding2str, PIKA_TSpanEncoding, PIKA_SPAN_ENCODING_NAMES, PIKA_SPAN_ENCODING_INVALID) + +FTDM_ENUM_NAMES(PIKA_LL_NAMES, PIKA_LL_STRINGS) +PIKA_STR2ENUM(pika_str2loop_length, pika_loop_length2str, PIKA_TSpanLoopLength, PIKA_LL_NAMES, PIKA_SPAN_LOOP_INVALID) + +FTDM_ENUM_NAMES(PIKA_LBO_NAMES, PIKA_LBO_STRINGS) +PIKA_STR2ENUM(pika_str2lbo, pika_lbo2str, PIKA_TSpanBuildOut, PIKA_LBO_NAMES, PIKA_SPAN_LBO_INVALID) + +FTDM_ENUM_NAMES(PIKA_SPAN_COMPAND_MODE_NAMES, PIKA_SPAN_COMPAND_MODE_STRINGS) +PIKA_STR2ENUM(pika_str2compand_mode, pika_compand_mode2str, PIKA_TSpanCompandMode, PIKA_SPAN_COMPAND_MODE_NAMES, PIKA_SPAN_COMPAND_MODE_INVALID) + + +typedef enum { + PK_FLAG_READY = (1 << 0), + PK_FLAG_LOCKED = (1 << 1) +} pk_flag_t; + +struct general_config { + uint32_t region; +}; +typedef struct general_config general_config_t; + +struct pika_channel_profile { + char name[80]; + PKH_TRecordConfig record_config; + PKH_TPlayConfig play_config; + int ec_enabled; + PKH_TECConfig ec_config; + PKH_TSpanConfig span_config; + general_config_t general_config; + int cust_span; +}; +typedef struct pika_channel_profile pika_channel_profile_t; + +static struct { + PKH_TSystemDeviceList board_list; + TPikaHandle open_boards[MAX_NUMBER_OF_TRUNKS]; + TPikaHandle system_handle; + PKH_TSystemConfig system_config; + PKH_TRecordConfig record_config; + PKH_TPlayConfig play_config; + PKH_TECConfig ec_config; + PKH_TSpanConfig t1_span_config; + PKH_TSpanConfig e1_span_config; + ftdm_hash_t *profile_hash; + general_config_t general_config; +} globals; + + +struct pika_span_data { + TPikaHandle event_queue; + PKH_TPikaEvent last_oob_event; + uint32_t boardno; + PKH_TSpanConfig span_config; + TPikaHandle handle; + uint32_t flags; +}; +typedef struct pika_span_data pika_span_data_t; + +struct pika_chan_data { + TPikaHandle handle; + TPikaHandle media_in; + TPikaHandle media_out; + TPikaHandle media_in_queue; + TPikaHandle media_out_queue; + PKH_TPikaEvent last_media_event; + PKH_TPikaEvent last_oob_event; + PKH_TRecordConfig record_config; + PKH_TPlayConfig play_config; + int ec_enabled; + PKH_TECConfig ec_config; + PKH_THDLCConfig hdlc_config; + ftdm_buffer_t *digit_buffer; + ftdm_mutex_t *digit_mutex; + ftdm_size_t dtmf_len; + uint32_t flags; + uint32_t hdlc_bytes; +}; +typedef struct pika_chan_data pika_chan_data_t; + +static const char *pika_board_type_string(PK_UINT type) +{ + if (type == PKH_BOARD_TYPE_DIGITAL_GATEWAY) { + return "digital_gateway"; + } + + if (type == PKH_BOARD_TYPE_ANALOG_GATEWAY) { + return "analog_gateway"; + } + + return "unknown"; +} + +/** + * \brief Process configuration variable for a pika profile + * \param category Pika profile name + * \param var Variable name + * \param val Variable value + * \param lineno Line number from configuration file (unused) + * \return Success + */ +static FIO_CONFIGURE_FUNCTION(pika_configure) +{ + pika_channel_profile_t *profile = NULL; + int ok = 1; + + if (!(profile = (pika_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) { + profile = ftdm_malloc(sizeof(*profile)); + memset(profile, 0, sizeof(*profile)); + ftdm_set_string(profile->name, category); + profile->ec_config = globals.ec_config; + profile->record_config = globals.record_config; + profile->play_config = globals.play_config; + hashtable_insert(globals.profile_hash, (void *)profile->name, profile, HASHTABLE_FLAG_NONE); + ftdm_log(FTDM_LOG_INFO, "creating profile [%s]\n", category); + } + + if (!strcasecmp(var, "rx-gain")) { + profile->record_config.gain = pk_atof(val); + } else if (!strcasecmp(var, "rx-agc-enabled")) { + profile->record_config.AGC.enabled = ftdm_true(val); + } else if (!strcasecmp(var, "rx-agc-targetPower")) { + profile->record_config.AGC.targetPower = pk_atof(val); + } else if (!strcasecmp(var, "rx-agc-minGain")) { + profile->record_config.AGC.minGain = pk_atof(val); + } else if (!strcasecmp(var, "rx-agc-maxGain")) { + profile->record_config.AGC.maxGain = pk_atof(val); + } else if (!strcasecmp(var, "rx-agc-attackRate")) { + profile->record_config.AGC.attackRate = atoi(val); + } else if (!strcasecmp(var, "rx-agc-decayRate")) { + profile->record_config.AGC.decayRate = atoi(val); + } else if (!strcasecmp(var, "rx-agc-speechThreshold")) { + profile->record_config.AGC.speechThreshold = pk_atof(val); + } else if (!strcasecmp(var, "rx-vad-enabled")) { + profile->record_config.VAD.enabled = ftdm_true(val); + } else if (!strcasecmp(var, "rx-vad-activationThreshold")) { + profile->record_config.VAD.activationThreshold = pk_atof(val); + } else if (!strcasecmp(var, "rx-vad-activationDebounceTime")) { + profile->record_config.VAD.activationDebounceTime = atoi(val); + } else if (!strcasecmp(var, "rx-vad-deactivationThreshold")) { + profile->record_config.VAD.deactivationThreshold = pk_atof(val); + } else if (!strcasecmp(var, "rx-vad-deactivationDebounceTime")) { + profile->record_config.VAD.deactivationDebounceTime = atoi(val); + } else if (!strcasecmp(var, "rx-vad-preSpeechBufferSize")) { + profile->record_config.VAD.preSpeechBufferSize = atoi(val); + } else if (!strcasecmp(var, "tx-gain")) { + profile->play_config.gain = pk_atof(val); + } else if (!strcasecmp(var, "tx-agc-enabled")) { + profile->play_config.AGC.enabled = ftdm_true(val); + } else if (!strcasecmp(var, "tx-agc-targetPower")) { + profile->play_config.AGC.targetPower = pk_atof(val); + } else if (!strcasecmp(var, "tx-agc-minGain")) { + profile->play_config.AGC.minGain = pk_atof(val); + } else if (!strcasecmp(var, "tx-agc-maxGain")) { + profile->play_config.AGC.maxGain = pk_atof(val); + } else if (!strcasecmp(var, "tx-agc-attackRate")) { + profile->play_config.AGC.attackRate = atoi(val); + } else if (!strcasecmp(var, "tx-agc-decayRate")) { + profile->play_config.AGC.decayRate = atoi(val); + } else if (!strcasecmp(var, "tx-agc-speechThreshold")) { + profile->play_config.AGC.speechThreshold = pk_atof(val); + } else if (!strcasecmp(var, "ec-enabled")) { + profile->ec_enabled = ftdm_true(val); + } else if (!strcasecmp(var, "ec-doubleTalkerThreshold")) { + profile->ec_config.doubleTalkerThreshold = pk_atof(val); + } else if (!strcasecmp(var, "ec-speechPresentThreshold")) { + profile->ec_config.speechPresentThreshold = pk_atof(val); + } else if (!strcasecmp(var, "ec-echoSuppressionThreshold")) { + profile->ec_config.echoSuppressionThreshold = pk_atof(val); + } else if (!strcasecmp(var, "ec-echoSuppressionEnabled")) { + profile->ec_config.echoSuppressionEnabled = ftdm_true(val); + } else if (!strcasecmp(var, "ec-comfortNoiseEnabled")) { + profile->ec_config.comfortNoiseEnabled = ftdm_true(val); + } else if (!strcasecmp(var, "ec-adaptationModeEnabled")) { + profile->ec_config.adaptationModeEnabled = ftdm_true(val); + } else if (!strcasecmp(var, "framing")) { + profile->span_config.framing = pika_str2span(val); + profile->cust_span++; + } else if (!strcasecmp(var, "encoding")) { + profile->span_config.encoding = pika_str2span_encoding(val); + profile->cust_span++; + } else if (!strcasecmp(var, "loopLength")) { + profile->span_config.loopLength = pika_str2loop_length(val); + profile->cust_span++; + } else if (!strcasecmp(var, "buildOut")) { + profile->span_config.buildOut = pika_str2lbo(val); + profile->cust_span++; + } else if (!strcasecmp(var, "compandMode")) { + profile->span_config.compandMode = pika_str2compand_mode(val); + profile->cust_span++; + } else if (!strcasecmp(var, "region")) { + if (!strcasecmp(val, "eu")) { + profile->general_config.region = PKH_TRUNK_EU; + } else { + profile->general_config.region = PKH_TRUNK_NA; + } + } else { + ok = 0; + } + + if (ok) { + ftdm_log(FTDM_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category); + } else { + ftdm_log(FTDM_LOG_ERROR, "unknown param [%s]\n", var); + } + + return FTDM_SUCCESS; +} + +/** + * \brief Pika event handler + * \param event Pika event + */ +PK_VOID PK_CALLBACK media_out_callback(PKH_TPikaEvent *event) +{ + PK_STATUS pk_status; + ftdm_channel_t *ftdmchan = event->userData; + pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + + //PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + //PKH_EVENT_GetText(event->id, event_text, sizeof(event_text)); + //ftdm_log(FTDM_LOG_DEBUG, "Event: %s\n", event_text); + + switch (event->id) { + case PKH_EVENT_PLAY_IDLE: + { + while (ftdm_buffer_inuse(chan_data->digit_buffer)) { + char dtmf[128] = ""; + ftdm_mutex_lock(chan_data->digit_mutex); + chan_data->dtmf_len = ftdm_buffer_read(chan_data->digit_buffer, dtmf, sizeof(dtmf)); + pk_status = PKH_TG_PlayDTMF(chan_data->media_out, dtmf); + ftdm_mutex_unlock(chan_data->digit_mutex); + } + } + break; + case PKH_EVENT_TG_TONE_PLAYED: + { + + if (!event->p1) { + ftdm_mutex_lock(chan_data->digit_mutex); + PKH_PLAY_Start(chan_data->media_out); + chan_data->dtmf_len = 0; + ftdm_mutex_unlock(chan_data->digit_mutex); + } + + + } + break; + default: + break; + } + +} + +/** + * \brief Initialises a range of pika channels + * \param span FreeTDM span + * \param boardno Pika board number + * \param spanno Pika span number + * \param start Initial pika channel number + * \param end Final pika channel number + * \param type FreeTDM channel type + * \param name FreeTDM span name + * \param number FreeTDM span number + * \param profile Pika channel profile + * \return number of spans configured + */ +static unsigned pika_open_range(ftdm_span_t *span, unsigned boardno, unsigned spanno, unsigned start, unsigned end, + ftdm_chan_type_t type, char *name, char *number, pika_channel_profile_t *profile) +{ + unsigned configured = 0, x; + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + pika_span_data_t *span_data; + + if (boardno >= globals.board_list.numberOfBoards) { + ftdm_log(FTDM_LOG_ERROR, "Board %u is not present!\n", boardno); + return 0; + } + + if (!globals.open_boards[boardno]) { + status = PKH_BOARD_Open(globals.board_list.board[boardno].id, + NULL, + &globals.open_boards[boardno]); + if(status != PK_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error: PKH_BOARD_Open %d failed(%s)!\n", boardno, + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return 0; + } + + ftdm_log(FTDM_LOG_DEBUG, "Open board %u\n", boardno); + + //PKH_BOARD_SetDebugTrace(globals.open_boards[boardno], 1, 0); + + } + + if (span->mod_data) { + span_data = span->mod_data; + } else { + span_data = ftdm_malloc(sizeof(*span_data)); + assert(span_data != NULL); + memset(span_data, 0, sizeof(*span_data)); + span_data->boardno = boardno; + + status = PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &span_data->event_queue); + + if (status != PK_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error: PKH_QUEUE_Create failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + ftdm_safe_free(span_data); + return 0; + } + + //PKH_QUEUE_Attach(span_data->event_queue, globals.open_boards[boardno], NULL); + + span->mod_data = span_data; + } + + if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO) { + start--; + end--; + } + + for(x = start; x < end; x++) { + ftdm_channel_t *chan; + pika_chan_data_t *chan_data = NULL; + + chan_data = ftdm_malloc(sizeof *chan_data); + assert(chan_data); + memset(chan_data, 0, sizeof(*chan_data)); + ftdm_span_add_channel(span, 0, type, &chan); + chan->mod_data = chan_data; + + if ((type == FTDM_CHAN_TYPE_B || type == FTDM_CHAN_TYPE_DQ921) && !span_data->handle) { + PKH_TBoardConfig boardConfig; + + TRY_OR_DIE(PKH_BOARD_GetConfig(globals.open_boards[boardno], &boardConfig), PK_SUCCESS, error); + if ((profile && profile->general_config.region == PKH_TRUNK_EU) || ftdm_test_flag(span_data, PK_FLAG_LOCKED)) { + if (span->trunk_type == FTDM_TRUNK_T1) { + ftdm_log(FTDM_LOG_WARNING, "Changing trunk type to E1 based on previous config.\n"); + } + span->trunk_type = FTDM_TRUNK_E1; + } + + if (span->trunk_type == FTDM_TRUNK_T1) { + if (ftdm_test_flag(span_data, PK_FLAG_LOCKED)) { + ftdm_log(FTDM_LOG_WARNING, "Already locked into E1 mode!\n"); + } + } else if (span->trunk_type == FTDM_TRUNK_E1) { + boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_E1; + if ((status = PKH_BOARD_SetConfig(globals.open_boards[boardno], &boardConfig)) != PK_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error: [%s]\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + } + ftdm_set_flag(span_data, PK_FLAG_LOCKED); + } + + TRY_OR_DIE(PKH_SPAN_Open(globals.open_boards[boardno], spanno, NULL, &span_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_SPAN_GetConfig(span_data->handle, &span_data->span_config), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, span_data->handle, (PK_VOID*) span), PK_SUCCESS, error); + } + + if (type == FTDM_CHAN_TYPE_FXO) { + PKH_TTrunkConfig trunkConfig; + + TRY_OR_DIE(PKH_TRUNK_Open(globals.open_boards[boardno], x, &chan_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_TRUNK_Seize(chan_data->handle), PK_SUCCESS, error); + + if (profile && profile->general_config.region == PKH_TRUNK_EU) { + TRY_OR_DIE(PKH_TRUNK_GetConfig(chan_data->handle, &trunkConfig), PK_SUCCESS, error); + trunkConfig.internationalControl = PKH_PHONE_INTERNATIONAL_CONTROL_EU; + trunkConfig.audioFormat = PKH_AUDIO_ALAW; + trunkConfig.compandMode = PKH_PHONE_AUDIO_ALAW; + chan->native_codec = chan->effective_codec = FTDM_CODEC_ALAW; + TRY_OR_DIE(PKH_TRUNK_SetConfig(chan_data->handle, &trunkConfig), PK_SUCCESS, error); + } else { + chan->native_codec = chan->effective_codec = FTDM_CODEC_ULAW; + } + + + TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_TRUNK_GetMediaStreams(chan_data->handle, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_TRUNK_Start(chan_data->handle), PK_SUCCESS, error); + } else if (type == FTDM_CHAN_TYPE_FXS) { + PKH_TPhoneConfig phoneConfig; + + if (profile && profile->general_config.region == PKH_TRUNK_EU) { + TRY_OR_DIE(PKH_PHONE_GetConfig(chan_data->handle, &phoneConfig), PK_SUCCESS, error); + phoneConfig.internationalControl = PKH_PHONE_INTERNATIONAL_CONTROL_EU; + phoneConfig.compandMode = PKH_PHONE_AUDIO_ALAW; + chan->native_codec = chan->effective_codec = FTDM_CODEC_ALAW; + TRY_OR_DIE(PKH_PHONE_SetConfig(chan_data->handle, &phoneConfig), PK_SUCCESS, error); + } else { + chan->native_codec = chan->effective_codec = FTDM_CODEC_ULAW; + } + + TRY_OR_DIE(PKH_PHONE_Open(globals.open_boards[boardno], x, &chan_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_PHONE_Seize(chan_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_PHONE_GetMediaStreams(chan_data->handle, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_PHONE_Start(chan_data->handle), PK_SUCCESS, error); + } else if (type == FTDM_CHAN_TYPE_B) { + TRY_OR_DIE(PKH_SPAN_SeizeChannel(span_data->handle, x), PK_SUCCESS, error); + TRY_OR_DIE(PKH_SPAN_GetMediaStreams(span_data->handle, x, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error); + } else if (type == FTDM_CHAN_TYPE_DQ921) { + TRY_OR_DIE(PKH_SPAN_HDLC_Open(span_data->handle, PKH_SPAN_HDLC_MODE_NORMAL, &chan_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_SPAN_HDLC_GetConfig(chan_data->handle, &chan_data->hdlc_config), PK_SUCCESS, error); + chan_data->hdlc_config.channelId = x; + TRY_OR_DIE(PKH_SPAN_HDLC_SetConfig(chan_data->handle, &chan_data->hdlc_config), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error); + + if (profile) { + if (profile->cust_span) { + span_data->span_config.framing = profile->span_config.framing; + span_data->span_config.encoding = profile->span_config.encoding; + span_data->span_config.loopLength = profile->span_config.loopLength; + span_data->span_config.buildOut = profile->span_config.buildOut; + span_data->span_config.compandMode = profile->span_config.compandMode; + } else { + if (profile->general_config.region == PKH_TRUNK_EU) { + span_data->span_config = globals.e1_span_config; + } else { + span_data->span_config = globals.t1_span_config; + } + } + } else { + if (span->trunk_type == FTDM_TRUNK_E1) { + span_data->span_config = globals.e1_span_config; + } else { + span_data->span_config = globals.t1_span_config; + } + } + + PKH_SPAN_SetConfig(span_data->handle, &span_data->span_config); + TRY_OR_DIE(PKH_SPAN_Start(span_data->handle), PK_SUCCESS, error); + } + + goto ok; + + error: + PKH_ERROR_GetText(status, error_text, sizeof(error_text)); + ftdm_log(FTDM_LOG_ERROR, "failure configuring device b%ds%dc%d [%s]\n", boardno, spanno, x, error_text); + continue; + ok: + ftdm_set_flag(chan_data, PK_FLAG_READY); + status = PKH_RECORD_GetConfig(chan_data->media_in, &chan_data->record_config); + chan_data->record_config.encoding = PKH_RECORD_ENCODING_MU_LAW; + chan_data->record_config.samplingRate = PKH_RECORD_SAMPLING_RATE_8KHZ; + chan_data->record_config.bufferSize = PIKA_BLOCK_SIZE; + chan_data->record_config.numberOfBuffers = PIKA_NUM_BUFFERS; + chan_data->record_config.VAD.enabled = PK_FALSE; + //chan_data->record_config.speechSegmentEventsEnabled = PK_FALSE; + //chan_data->record_config.gain = rxgain; + + status = PKH_PLAY_GetConfig(chan_data->media_out, &chan_data->play_config); + chan_data->play_config.encoding = PKH_RECORD_ENCODING_MU_LAW; + chan_data->play_config.samplingRate = PKH_RECORD_SAMPLING_RATE_8KHZ; + chan_data->play_config.AGC.enabled = PK_FALSE; + ftdm_log(FTDM_LOG_INFO, "configuring device b%ds%dc%d as FreeTDM device %d:%d\n", boardno, spanno, x, chan->span_id, chan->chan_id); + + if (profile) { + ftdm_log(FTDM_LOG_INFO, "applying config profile %s to device %d:%d\n", profile->name, chan->span_id, chan->chan_id); + chan_data->record_config.gain = profile->record_config.gain; + chan_data->record_config.AGC = profile->record_config.AGC; + chan_data->record_config.VAD = profile->record_config.VAD; + chan_data->play_config.gain = profile->play_config.gain; + chan_data->play_config.AGC = profile->play_config.AGC; + chan_data->ec_enabled = profile->ec_enabled; + chan_data->ec_config = profile->ec_config; + } + + if (type == FTDM_CHAN_TYPE_B) { + if (span_data->span_config.compandMode == PKH_SPAN_COMPAND_MODE_A_LAW) { + chan->native_codec = chan->effective_codec = FTDM_CODEC_ALAW; + } else { + chan->native_codec = chan->effective_codec = FTDM_CODEC_ULAW; + } + } + + status = PKH_RECORD_SetConfig(chan_data->media_in, &chan_data->record_config); + status = PKH_PLAY_SetConfig(chan_data->media_out, &chan_data->play_config); + + chan->physical_span_id = spanno; + chan->physical_chan_id = x; + + chan->rate = 8000; + chan->packet_len = (uint32_t)chan_data->record_config.bufferSize; + chan->effective_interval = chan->native_interval = chan->packet_len / 8; + + PKH_RECORD_Start(chan_data->media_in); + PKH_PLAY_Start(chan_data->media_out); + if (chan_data->ec_enabled) { + PKH_EC_SetConfig(chan_data->media_in, &chan_data->ec_config); + PKH_EC_Start(chan_data->media_in, chan_data->media_in, chan_data->media_out); + } + + if (!ftdm_strlen_zero(name)) { + ftdm_copy_string(chan->chan_name, name, sizeof(chan->chan_name)); + } + + if (!ftdm_strlen_zero(number)) { + ftdm_copy_string(chan->chan_number, number, sizeof(chan->chan_number)); + } + + ftdm_channel_set_feature(chan, FTDM_CHANNEL_FEATURE_DTMF_GENERATE); + ftdm_buffer_create(&chan_data->digit_buffer, 128, 128, 0); + ftdm_mutex_create(&chan_data->digit_mutex); + + configured++; + } + + + return configured; +} + +/** + * \brief Initialises an freetdm pika span from a configuration string + * \param span FreeTDM span + * \param str Configuration string + * \param type FreeTDM span type + * \param name FreeTDM span name + * \param number FreeTDM span number + * \return Success or failure + */ +static FIO_CONFIGURE_SPAN_FUNCTION(pika_configure_span) +{ + int items, i; + char *mydata, *item_list[10]; + char *bd, *sp, *ch = NULL, *mx; + int boardno; + int channo; + int spanno; + int top = 0; + unsigned configured = 0; + char *profile_name = NULL; + pika_channel_profile_t *profile = NULL; + + assert(str != NULL); + + mydata = ftdm_strdup(str); + assert(mydata != NULL); + + if ((profile_name = strchr(mydata, '@'))) { + *profile_name++ = '\0'; + if (!ftdm_strlen_zero(profile_name)) { + profile = (pika_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)profile_name); + } + } + + items = ftdm_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0]))); + + for(i = 0; i < items; i++) { + bd = item_list[i]; + if ((sp = strchr(bd, ':'))) { + *sp++ = '\0'; + if ((ch = strchr(sp, ':'))) { + *ch++ = '\0'; + } + } + + if (!(bd && sp && ch)) { + ftdm_log(FTDM_LOG_ERROR, "Invalid input\n"); + continue; + } + + boardno = atoi(bd); + channo = atoi(ch); + spanno = atoi(sp); + + + if (boardno < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid board number %d\n", boardno); + continue; + } + + if (channo < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid channel number %d\n", channo); + continue; + } + + if (spanno < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid span number %d\n", channo); + continue; + } + + if ((mx = strchr(ch, '-'))) { + mx++; + top = atoi(mx) + 1; + } else { + top = channo + 1; + } + + + if (top < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid range number %d\n", top); + continue; + } + + configured += pika_open_range(span, boardno, spanno, channo, top, type, name, number, profile); + + } + + ftdm_safe_free(mydata); + + return configured; +} + +/** + * \brief Opens Pika channel + * \param ftdmchan Channel to open + * \return Success or failure + */ +static FIO_OPEN_FUNCTION(pika_open) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + + if (!chan_data && !ftdm_test_flag(chan_data, PK_FLAG_READY)) { + return FTDM_FAIL; + } + + if (chan_data->media_in_queue) { + PKH_QUEUE_Flush(chan_data->media_in_queue); + } + + if (ftdmchan->type == FTDM_CHAN_TYPE_FXS || ftdmchan->type == FTDM_CHAN_TYPE_FXO || ftdmchan->type == FTDM_CHAN_TYPE_B) { + PKH_PLAY_Start(chan_data->media_out); + } + return FTDM_SUCCESS; +} + +/** + * \brief Closes Pika channel + * \param ftdmchan Channel to close + * \return Success + */ +static FIO_CLOSE_FUNCTION(pika_close) +{ + return FTDM_SUCCESS; +} + +/** + * \brief Waits for an event on a Pika channel + * \param ftdmchan Channel to open + * \param flags Type of event to wait for + * \param to Time to wait (in ms) + * \return Success, failure or timeout + */ +static FIO_WAIT_FUNCTION(pika_wait) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + PK_STATUS status; + ftdm_wait_flag_t myflags = *flags; + PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + + *flags = FTDM_NO_FLAGS; + + if (myflags & FTDM_READ) { + if (chan_data->hdlc_bytes) { + *flags |= FTDM_READ; + return FTDM_SUCCESS; + } + status = PKH_QUEUE_WaitOnEvent(chan_data->media_in_queue, to, &chan_data->last_media_event); + + if (status == PK_SUCCESS) { + if (chan_data->last_media_event.id == PKH_EVENT_QUEUE_TIMEOUT || chan_data->last_media_event.id == PKH_EVENT_RECORD_BUFFER_OVERFLOW) { + return FTDM_TIMEOUT; + } + + *flags |= FTDM_READ; + return FTDM_SUCCESS; + } + + PKH_EVENT_GetText(chan_data->last_media_event.id, event_text, sizeof(event_text)); + ftdm_log(FTDM_LOG_DEBUG, "Event: %s\n", event_text); + } + + return FTDM_SUCCESS; +} + +/** + * \brief Reads data from a Pika channel + * \param ftdmchan Channel to read from + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success or failure + */ +static FIO_READ_FUNCTION(pika_read) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + PK_STATUS status; + PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + uint32_t len; + + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) { + if ((status = PKH_SPAN_HDLC_GetMessage(chan_data->handle, data, *datalen)) == PK_SUCCESS) { + *datalen = chan_data->hdlc_bytes; + chan_data->hdlc_bytes = 0; + return FTDM_SUCCESS; + } + return FTDM_FAIL; + } + + if (!(len = chan_data->last_media_event.p0)) { + len = ftdmchan->packet_len; + } + + if (len < *datalen) { + *datalen = len; + } + + if ((status = PKH_RECORD_GetData(chan_data->media_in, data, *datalen)) == PK_SUCCESS) { + return FTDM_SUCCESS; + } + + + PKH_ERROR_GetText(status, event_text, sizeof(event_text)); + ftdm_log(FTDM_LOG_DEBUG, "ERR: %s\n", event_text); + return FTDM_FAIL; +} + +/** + * \brief Writes data to a Pika channel + * \param ftdmchan Channel to write to + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success or failure + */ +static FIO_WRITE_FUNCTION(pika_write) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + PK_STATUS status; + + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) { + if ((status = PKH_SPAN_HDLC_SendMessage(chan_data->handle, data, *datalen)) == PK_SUCCESS) { + return FTDM_SUCCESS; + } + return FTDM_FAIL; + } + + if (PKH_PLAY_AddData(chan_data->media_out, 0, data, *datalen) == PK_SUCCESS) { + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +/** + * \brief Executes an FreeTDM command on a Pika channel + * \param ftdmchan Channel to execute command on + * \param command FreeTDM command to execute + * \param obj Object (unused) + * \return Success or failure + */ +static FIO_COMMAND_FUNCTION(pika_command) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + //pika_span_data_t *span_data = (pika_span_data_t *) ftdmchan->span->mod_data; + PK_STATUS pk_status; + ftdm_status_t status = FTDM_SUCCESS; + + switch(command) { + case FTDM_COMMAND_OFFHOOK: + { + if ((pk_status = PKH_TRUNK_SetHookSwitch(chan_data->handle, PKH_TRUNK_OFFHOOK)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error)); + GOTO_STATUS(done, FTDM_FAIL); + } else { + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK); + } + } + break; + case FTDM_COMMAND_ONHOOK: + { + if ((pk_status = PKH_TRUNK_SetHookSwitch(chan_data->handle, PKH_TRUNK_ONHOOK)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error)); + GOTO_STATUS(done, FTDM_FAIL); + } else { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK); + } + } + break; + case FTDM_COMMAND_GENERATE_RING_ON: + { + if ((pk_status = PKH_PHONE_RingStart(chan_data->handle, 0, 0)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error)); + GOTO_STATUS(done, FTDM_FAIL); + } else { + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING); + } + } + break; + case FTDM_COMMAND_GENERATE_RING_OFF: + { + if ((pk_status = PKH_PHONE_RingStop(chan_data->handle)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error)); + GOTO_STATUS(done, FTDM_FAIL); + } else { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING); + } + } + break; + case FTDM_COMMAND_GET_INTERVAL: + { + + FTDM_COMMAND_OBJ_INT = ftdmchan->native_interval; + + } + break; + case FTDM_COMMAND_SET_INTERVAL: + { + int interval = FTDM_COMMAND_OBJ_INT; + int len = interval * 8; + chan_data->record_config.bufferSize = len; + chan_data->record_config.numberOfBuffers = (PK_UINT)chan_data->record_config.bufferSize; + ftdmchan->packet_len = (uint32_t)chan_data->record_config.bufferSize; + ftdmchan->effective_interval = ftdmchan->native_interval = ftdmchan->packet_len / 8; + PKH_RECORD_SetConfig(chan_data->media_in, &chan_data->record_config); + GOTO_STATUS(done, FTDM_SUCCESS); + } + break; + case FTDM_COMMAND_GET_DTMF_ON_PERIOD: + { + + FTDM_COMMAND_OBJ_INT = ftdmchan->dtmf_on; + GOTO_STATUS(done, FTDM_SUCCESS); + + } + break; + case FTDM_COMMAND_GET_DTMF_OFF_PERIOD: + { + FTDM_COMMAND_OBJ_INT = ftdmchan->dtmf_on; + GOTO_STATUS(done, FTDM_SUCCESS); + } + break; + case FTDM_COMMAND_SET_DTMF_ON_PERIOD: + { + int val = FTDM_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + ftdmchan->dtmf_on = val; + GOTO_STATUS(done, FTDM_SUCCESS); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, FTDM_FAIL); + } + } + break; + case FTDM_COMMAND_SET_DTMF_OFF_PERIOD: + { + int val = FTDM_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + ftdmchan->dtmf_off = val; + GOTO_STATUS(done, FTDM_SUCCESS); + } else { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, FTDM_FAIL); + } + } + break; + case FTDM_COMMAND_SEND_DTMF: + { + char *digits = FTDM_COMMAND_OBJ_CHAR_P; + ftdm_log(FTDM_LOG_DEBUG, "Adding DTMF SEQ [%s]\n", digits); + ftdm_mutex_lock(chan_data->digit_mutex); + ftdm_buffer_write(chan_data->digit_buffer, digits, strlen(digits)); + ftdm_mutex_unlock(chan_data->digit_mutex); + pk_status = PKH_PLAY_Stop(chan_data->media_out); + + if (pk_status != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, ftdmchan->last_error, sizeof(ftdmchan->last_error)); + GOTO_STATUS(done, FTDM_FAIL); + } + GOTO_STATUS(done, FTDM_SUCCESS); + } + break; + default: + break; + }; + + done: + return status; +} + +/** + * \brief Checks for events on a Pika span + * \param span Span to check for events + * \param ms Time to wait for event + * \return Success if event is waiting or failure if not + */ +static FIO_SPAN_POLL_EVENT_FUNCTION(pika_poll_event) +{ + pika_span_data_t *span_data = (pika_span_data_t *) span->mod_data; + PK_STATUS status; + PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + + status = PKH_QUEUE_WaitOnEvent(span_data->event_queue, ms, &span_data->last_oob_event); + + if (status == PK_SUCCESS) { + ftdm_channel_t *ftdmchan = NULL; + uint32_t *data = (uint32_t *) span_data->last_oob_event.userData; + ftdm_data_type_t data_type = FTDM_TYPE_NONE; + + if (span_data->last_oob_event.id == PKH_EVENT_QUEUE_TIMEOUT) { + return FTDM_TIMEOUT; + } + + if (data) { + data_type = *data; + } + + if (data_type == FTDM_TYPE_CHANNEL) { + ftdmchan = span_data->last_oob_event.userData; + } else if (data_type == FTDM_TYPE_SPAN) { + ftdm_time_t last_event_time = ftdm_current_time_in_ms(); + uint32_t event_id = 0; + + switch (span_data->last_oob_event.id) { + case PKH_EVENT_SPAN_ALARM_T1_RED: + case PKH_EVENT_SPAN_ALARM_T1_YELLOW: + case PKH_EVENT_SPAN_ALARM_T1_AIS: + case PKH_EVENT_SPAN_ALARM_E1_RED: + case PKH_EVENT_SPAN_ALARM_E1_RAI: + case PKH_EVENT_SPAN_ALARM_E1_AIS: + case PKH_EVENT_SPAN_ALARM_E1_RMAI: + case PKH_EVENT_SPAN_ALARM_E1_TS16AIS: + case PKH_EVENT_SPAN_ALARM_E1_TS16LOS: + case PKH_EVENT_SPAN_OUT_OF_SYNC: + case PKH_EVENT_SPAN_FRAMING_ERROR: + case PKH_EVENT_SPAN_LOSS_OF_SIGNAL: + case PKH_EVENT_SPAN_OUT_OF_CRC_MF_SYNC: + case PKH_EVENT_SPAN_OUT_OF_CAS_MF_SYNC: + event_id = FTDM_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_T1_RED_CLEAR: + case PKH_EVENT_SPAN_ALARM_T1_YELLOW_CLEAR: + case PKH_EVENT_SPAN_ALARM_T1_AIS_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_RED_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_RAI_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_AIS_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_RMAI_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_TS16AIS_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_TS16LOS_CLEAR: + case PKH_EVENT_SPAN_IN_SYNC: + case PKH_EVENT_SPAN_LOSS_OF_SIGNAL_CLEAR: + case PKH_EVENT_SPAN_IN_CRC_MF_SYNC: + case PKH_EVENT_SPAN_IN_CAS_MF_SYNC: + event_id = FTDM_OOB_ALARM_CLEAR; + break; + case PKH_EVENT_SPAN_MESSAGE: + case PKH_EVENT_SPAN_ABCD_SIGNAL_CHANGE: + break; + } + + if (event_id) { + uint32_t x = 0; + ftdm_channel_t *ftdmchan; + pika_chan_data_t *chan_data; + for(x = 1; x <= span->chan_count; x++) { + ftdmchan = span->channels[x]; + assert(ftdmchan != NULL); + chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + assert(chan_data != NULL); + + + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_EVENT); + ftdmchan->last_event_time = last_event_time; + chan_data->last_oob_event = span_data->last_oob_event; + } + + } + + } + + PKH_EVENT_GetText(span_data->last_oob_event.id, event_text, sizeof(event_text)); + //ftdm_log(FTDM_LOG_DEBUG, "Event: %s\n", event_text); + + if (ftdmchan) { + pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + + assert(chan_data != NULL); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_EVENT); + ftdmchan->last_event_time = ftdm_current_time_in_ms(); + chan_data->last_oob_event = span_data->last_oob_event; + } + + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +/** + * \brief Retrieves an event from a Pika span + * \param span Span to retrieve event from + * \param event FreeTDM event to return + * \return Success or failure + */ +static FIO_SPAN_NEXT_EVENT_FUNCTION(pika_next_event) +{ + uint32_t i, event_id = 0; + + for(i = 1; i <= span->chan_count; i++) { + if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) { + pika_chan_data_t *chan_data = (pika_chan_data_t *) span->channels[i]->mod_data; + PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + + ftdm_clear_flag(span->channels[i], FTDM_CHANNEL_EVENT); + + PKH_EVENT_GetText(chan_data->last_oob_event.id, event_text, sizeof(event_text)); + + switch(chan_data->last_oob_event.id) { + case PKH_EVENT_HDLC_MESSAGE: + chan_data->hdlc_bytes = chan_data->last_oob_event.p2; + continue; + case PKH_EVENT_TRUNK_HOOKFLASH: + event_id = FTDM_OOB_FLASH; + break; + case PKH_EVENT_TRUNK_RING_OFF: + event_id = FTDM_OOB_RING_STOP; + break; + case PKH_EVENT_TRUNK_RING_ON: + event_id = FTDM_OOB_RING_START; + break; + + case PKH_EVENT_PHONE_OFFHOOK: + ftdm_set_flag_locked(span->channels[i], FTDM_CHANNEL_OFFHOOK); + event_id = FTDM_OOB_OFFHOOK; + break; + + case PKH_EVENT_TRUNK_BELOW_THRESHOLD: + case PKH_EVENT_TRUNK_ABOVE_THRESHOLD: + case PKH_EVENT_PHONE_ONHOOK: + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_OFFHOOK); + event_id = FTDM_OOB_ONHOOK; + break; + + + + case PKH_EVENT_SPAN_ALARM_T1_RED: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RED); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RED ALARM"); + event_id = FTDM_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_T1_YELLOW: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_YELLOW); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "YELLOW ALARM"); + event_id = FTDM_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_T1_AIS: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_AIS); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "AIS ALARM"); + event_id = FTDM_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_E1_RED: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RED); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RED ALARM"); + event_id = FTDM_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_E1_RAI: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RAI); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RAI ALARM"); + event_id = FTDM_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_E1_AIS: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_AIS); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "AIS ALARM"); + event_id = FTDM_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_E1_RMAI: + case PKH_EVENT_SPAN_ALARM_E1_TS16AIS: + case PKH_EVENT_SPAN_ALARM_E1_TS16LOS: + case PKH_EVENT_SPAN_OUT_OF_SYNC: + case PKH_EVENT_SPAN_FRAMING_ERROR: + case PKH_EVENT_SPAN_LOSS_OF_SIGNAL: + case PKH_EVENT_SPAN_OUT_OF_CRC_MF_SYNC: + case PKH_EVENT_SPAN_OUT_OF_CAS_MF_SYNC: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_GENERAL); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "GENERAL ALARM"); + event_id = FTDM_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_T1_RED_CLEAR: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RED); + case PKH_EVENT_SPAN_ALARM_T1_YELLOW_CLEAR: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_YELLOW); + case PKH_EVENT_SPAN_ALARM_T1_AIS_CLEAR: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_AIS); + case PKH_EVENT_SPAN_ALARM_E1_RED_CLEAR: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RED); + case PKH_EVENT_SPAN_ALARM_E1_RAI_CLEAR: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_RAI); + case PKH_EVENT_SPAN_ALARM_E1_AIS_CLEAR: + ftdm_set_alarm_flag(span->channels[i], FTDM_ALARM_AIS); + case PKH_EVENT_SPAN_ALARM_E1_RMAI_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_TS16AIS_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_TS16LOS_CLEAR: + case PKH_EVENT_SPAN_IN_SYNC: + case PKH_EVENT_SPAN_LOSS_OF_SIGNAL_CLEAR: + case PKH_EVENT_SPAN_IN_CRC_MF_SYNC: + case PKH_EVENT_SPAN_IN_CAS_MF_SYNC: + ftdm_clear_alarm_flag(span->channels[i], FTDM_ALARM_GENERAL); + event_id = FTDM_OOB_ALARM_CLEAR; + break; + case PKH_EVENT_SPAN_MESSAGE: + case PKH_EVENT_SPAN_ABCD_SIGNAL_CHANGE: + break; + + + + + case PKH_EVENT_TRUNK_ONHOOK: + case PKH_EVENT_TRUNK_OFFHOOK: + case PKH_EVENT_TRUNK_DIALED : + case PKH_EVENT_TRUNK_REVERSAL: + case PKH_EVENT_TRUNK_LCSO: + case PKH_EVENT_TRUNK_DROPOUT: + case PKH_EVENT_TRUNK_LOF: + case PKH_EVENT_TRUNK_RX_OVERLOAD: + default: + ftdm_log(FTDM_LOG_DEBUG, "Unhandled event %d on channel %d [%s]\n", chan_data->last_oob_event.id, i, event_text); + event_id = FTDM_OOB_INVALID; + break; + } + + span->channels[i]->last_event_time = 0; + span->event_header.e_type = FTDM_EVENT_OOB; + span->event_header.enum_id = event_id; + span->event_header.channel = span->channels[i]; + *event = &span->event_header; + return FTDM_SUCCESS; + } + } + + return FTDM_FAIL; +} + +/** + * \brief Destroys a Pika Span + * \param span Span to destroy + * \return Success + */ +static FIO_SPAN_DESTROY_FUNCTION(pika_span_destroy) +{ + pika_span_data_t *span_data = (pika_span_data_t *) span->mod_data; + + if (span_data) { + PKH_QUEUE_Destroy(span_data->event_queue); + ftdm_safe_free(span_data); + } + + return FTDM_SUCCESS; +} + +/** + * \brief Destroys a Pika Channel + * \param ftdmchan Channel to destroy + * \return Success or failure + */ +static FIO_CHANNEL_DESTROY_FUNCTION(pika_channel_destroy) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) ftdmchan->mod_data; + pika_span_data_t *span_data = (pika_span_data_t *) ftdmchan->span->mod_data; + + if (!chan_data) { + return FTDM_FAIL; + } + + if (!ftdm_test_flag(chan_data, PK_FLAG_READY)) { + goto end; + } + + PKH_RECORD_Stop(chan_data->media_in); + PKH_PLAY_Stop(chan_data->media_out); + PKH_QUEUE_Destroy(chan_data->media_in_queue); + PKH_QUEUE_Destroy(chan_data->media_out_queue); + + switch(ftdmchan->type) { + case FTDM_CHAN_TYPE_FXS: + PKH_QUEUE_Detach(span_data->event_queue, chan_data->handle); + PKH_PHONE_Close(chan_data->handle); + break; + case FTDM_CHAN_TYPE_FXO: + PKH_QUEUE_Detach(span_data->event_queue, chan_data->handle); + PKH_TRUNK_Close(chan_data->handle); + break; + case FTDM_CHAN_TYPE_DQ921: + PKH_SPAN_Stop(span_data->handle); + break; + default: + break; + } + + + ftdm_mutex_destroy(&chan_data->digit_mutex); + ftdm_buffer_destroy(&chan_data->digit_buffer); + + end: + ftdm_safe_free(chan_data); + + return FTDM_SUCCESS; +} + +/** + * \brief Gets alarms from a Pika Channel (does nothing) + * \param ftdmchan Channel to get alarms from + * \return Failure + */ +static FIO_GET_ALARMS_FUNCTION(pika_get_alarms) +{ + return FTDM_FAIL; +} + +static ftdm_io_interface_t pika_interface; + +/** + * \brief Loads Pika IO module + * \param fio FreeTDM IO interface + * \return Success or failure + */ +static FIO_IO_LOAD_FUNCTION(pika_init) +{ + + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + uint32_t i; + int ok = 0; + PKH_TLogMasks m; + TPikaHandle tmpHandle; + + assert(fio != NULL); + memset(&pika_interface, 0, sizeof(pika_interface)); + memset(&globals, 0, sizeof(globals)); + globals.general_config.region = PKH_TRUNK_NA; + + globals.profile_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + + // Open the system object, to enumerate boards configured for this system + if ((status = PKH_SYSTEM_Open(&globals.system_handle)) != PK_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error: PKH_SYSTEM_Open failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return FTDM_FAIL; + } + + // Retrieves a list of all boards in this system, existing, + // or listed in pika.cfg + if ((status = PKH_SYSTEM_Detect(globals.system_handle, &globals.board_list)) != PK_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error: PKH_SYSTEM_Detect failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return FTDM_FAIL; + } + + PKH_SYSTEM_GetConfig(globals.system_handle, &globals.system_config); + globals.system_config.maxAudioProcessBlockSize = PIKA_BLOCK_LEN; + globals.system_config.playBufferSize = PIKA_BLOCK_SIZE; + globals.system_config.recordBufferSize = PIKA_BLOCK_SIZE; + globals.system_config.recordNumberOfBuffers = PIKA_NUM_BUFFERS; + PKH_SYSTEM_SetConfig(globals.system_handle, &globals.system_config); + + status = PKH_MEDIA_STREAM_Create(&tmpHandle); + status = PKH_RECORD_GetConfig(tmpHandle, &globals.record_config); + status = PKH_PLAY_GetConfig(tmpHandle, &globals.play_config); + status = PKH_EC_GetConfig(tmpHandle, &globals.ec_config); + status = PKH_MEDIA_STREAM_Destroy(tmpHandle); + + + + ftdm_log(FTDM_LOG_DEBUG, "Found %u board%s\n", globals.board_list.numberOfBoards, globals.board_list.numberOfBoards == 1 ? "" : "s"); + for(i = 0; i < globals.board_list.numberOfBoards; ++i) { + ftdm_log(FTDM_LOG_INFO, "Found PIKA board type:[%s] id:[%u] serno:[%u]\n", + pika_board_type_string(globals.board_list.board[i].type), globals.board_list.board[i].id, (uint32_t) + globals.board_list.board[i].serialNumber); + + if (globals.board_list.board[i].type == PKH_BOARD_TYPE_DIGITAL_GATEWAY) { + TPikaHandle board_handle, span_handle; + PKH_TBoardConfig boardConfig; + PKH_BOARD_GetConfig(board_handle, &boardConfig); + PKH_BOARD_Open(globals.board_list.board[i].id, NULL, &board_handle); + PKH_SPAN_Open(board_handle, 0, NULL, &span_handle); + PKH_SPAN_GetConfig(span_handle, &globals.t1_span_config); + PKH_SPAN_Close(span_handle); + boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_E1; + PKH_BOARD_SetConfig(board_handle, &boardConfig); + PKH_SPAN_Open(board_handle, 0, NULL, &span_handle); + PKH_SPAN_GetConfig(span_handle, &globals.e1_span_config); + PKH_SPAN_Close(span_handle); + boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_T1; + PKH_BOARD_SetConfig(board_handle, &boardConfig); + PKH_BOARD_Close(board_handle); + } + ok++; + + } + + if (!ok) { + return FTDM_FAIL; + } + + pika_interface.name = "pika"; + pika_interface.configure = pika_configure; + pika_interface.configure_span = pika_configure_span; + pika_interface.open = pika_open; + pika_interface.close = pika_close; + pika_interface.wait = pika_wait; + pika_interface.read = pika_read; + pika_interface.write = pika_write; + pika_interface.command = pika_command; + pika_interface.poll_event = pika_poll_event; + pika_interface.next_event = pika_next_event; + pika_interface.channel_destroy = pika_channel_destroy; + pika_interface.span_destroy = pika_span_destroy; + pika_interface.get_alarms = pika_get_alarms; + *fio = &pika_interface; + + + ftdm_log(FTDM_LOG_INFO, "Dumping Default configs:\n"); + ftdm_log(FTDM_LOG_INFO, "rx-gain => %0.2f\n", (float)globals.record_config.gain); + ftdm_log(FTDM_LOG_INFO, "rx-agc-enabled => %s\n", globals.record_config.AGC.enabled ? "true" : "false"); + ftdm_log(FTDM_LOG_INFO, "rx-agc-targetPower => %0.2f\n", (float)globals.record_config.AGC.targetPower); + ftdm_log(FTDM_LOG_INFO, "rx-agc-minGain => %0.2f\n", (float)globals.record_config.AGC.minGain); + ftdm_log(FTDM_LOG_INFO, "rx-agc-maxGain => %0.2f\n", (float)globals.record_config.AGC.maxGain); + ftdm_log(FTDM_LOG_INFO, "rx-agc-attackRate => %d\n", (int)globals.record_config.AGC.attackRate); + ftdm_log(FTDM_LOG_INFO, "rx-agc-decayRate => %d\n", (int)globals.record_config.AGC.decayRate); + ftdm_log(FTDM_LOG_INFO, "rx-agc-speechThreshold => %0.2f\n", (float)globals.record_config.AGC.speechThreshold); + ftdm_log(FTDM_LOG_INFO, "rx-vad-enabled => %s\n", globals.record_config.VAD.enabled ? "true" : "false"); + ftdm_log(FTDM_LOG_INFO, "rx-vad-activationThreshold => %0.2f\n", (float)globals.record_config.VAD.activationThreshold); + ftdm_log(FTDM_LOG_INFO, "rx-vad-activationDebounceTime => %d\n", (int)globals.record_config.VAD.activationDebounceTime); + ftdm_log(FTDM_LOG_INFO, "rx-vad-deactivationThreshold => %0.2f\n", (float)globals.record_config.VAD.deactivationThreshold); + ftdm_log(FTDM_LOG_INFO, "rx-vad-deactivationDebounceTime => %d\n", (int)globals.record_config.VAD.deactivationDebounceTime); + ftdm_log(FTDM_LOG_INFO, "rx-vad-preSpeechBufferSize => %d\n", (int)globals.record_config.VAD.preSpeechBufferSize); + ftdm_log(FTDM_LOG_INFO, "tx-gain => %0.2f\n", (float)globals.play_config.gain); + ftdm_log(FTDM_LOG_INFO, "tx-agc-enabled => %s\n", globals.play_config.AGC.enabled ? "true" : "false"); + ftdm_log(FTDM_LOG_INFO, "tx-agc-targetPower => %0.2f\n", (float)globals.play_config.AGC.targetPower); + ftdm_log(FTDM_LOG_INFO, "tx-agc-minGain => %0.2f\n", (float)globals.play_config.AGC.minGain); + ftdm_log(FTDM_LOG_INFO, "tx-agc-maxGain => %0.2f\n", (float)globals.play_config.AGC.maxGain); + ftdm_log(FTDM_LOG_INFO, "tx-agc-attackRate => %d\n", (int)globals.play_config.AGC.attackRate); + ftdm_log(FTDM_LOG_INFO, "tx-agc-decayRate => %d\n", (int)globals.play_config.AGC.decayRate); + ftdm_log(FTDM_LOG_INFO, "tx-agc-speechThreshold => %0.2f\n", (float)globals.play_config.AGC.speechThreshold); + ftdm_log(FTDM_LOG_INFO, "ec-doubleTalkerThreshold => %0.2f\n", (float)globals.ec_config.doubleTalkerThreshold); + ftdm_log(FTDM_LOG_INFO, "ec-speechPresentThreshold => %0.2f\n", (float)globals.ec_config.speechPresentThreshold); + ftdm_log(FTDM_LOG_INFO, "ec-echoSuppressionThreshold => %0.2f\n", (float)globals.ec_config.echoSuppressionThreshold); + ftdm_log(FTDM_LOG_INFO, "ec-echoSuppressionEnabled => %s\n", globals.ec_config.echoSuppressionEnabled ? "true" : "false"); + ftdm_log(FTDM_LOG_INFO, "ec-comfortNoiseEnabled => %s\n", globals.ec_config.comfortNoiseEnabled ? "true" : "false"); + ftdm_log(FTDM_LOG_INFO, "ec-adaptationModeEnabled => %s\n", globals.ec_config.adaptationModeEnabled ? "true" : "false"); + + + + memset(&m, 0, sizeof(m)); + //m.apiMask = 0xffffffff; + //PKH_LOG_SetMasks(&m); + + return FTDM_SUCCESS; +} + +/** + * \brief Unloads Pika IO module + * \return Success + */ +static FIO_IO_UNLOAD_FUNCTION(pika_destroy) +{ + uint32_t x; + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + + for (x = 0; x < MAX_NUMBER_OF_TRUNKS; x++) { + if (globals.open_boards[x]) { + ftdm_log(FTDM_LOG_INFO, "Closing board %u\n", x); + PKH_BOARD_Close(globals.open_boards[x]); + } + } + + // The system can now be closed. + if ((status = PKH_SYSTEM_Close(globals.system_handle)) != PK_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error: PKH_SYSTEM_Close failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + } else { + ftdm_log(FTDM_LOG_INFO, "Closing system handle\n"); + } + + hashtable_destroy(globals.profile_hash); + + return FTDM_SUCCESS; +} + +/** + * \brief Pika IO module definition + */ +EX_DECLARE_DATA ftdm_module_t ftdm_module = { + "pika", + pika_init, + pika_destroy, +}; + +/* 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: + */ + diff --git a/libs/freetdm/src/ftmod/ftmod_pika/ozmod_pika.2005.vcproj b/libs/freetdm/src/ftmod/ftmod_pika/ozmod_pika.2005.vcproj new file mode 100644 index 0000000000..646cc11428 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_pika/ozmod_pika.2005.vcproj @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c new file mode 100644 index 0000000000..85fe09f7bc --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -0,0 +1,1363 @@ +/* + * Copyright (c) 2009, Moises Silva + * 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 +#include +#include "freetdm.h" + +/* debug thread count for r2 legs */ +static ftdm_mutex_t* g_thread_count_mutex; +static int32_t g_thread_count = 0; + +/* when the users kills a span we clear this flag to kill the signaling thread */ +/* FIXME: what about the calls that are already up-and-running? */ +typedef enum { + FTDM_R2_RUNNING = (1 << 0), +} ftdm_r2_flag_t; + +/* private call information stored in ftdmchan->call_data void* ptr */ +#define R2CALL(ftdmchan) ((ftdm_r2_call_t*)((ftdmchan)->call_data)) +typedef struct ftdm_r2_call_t { + openr2_chan_t *r2chan; + int accepted:1; + int answer_pending:1; + int state_ack_pending:1; + int disconnect_rcvd:1; + int ftdm_started:1; + ftdm_channel_state_t chanstate; + ftdm_size_t dnis_index; + ftdm_size_t ani_index; + char name[10]; +} ftdm_r2_call_t; + +/* this is just used as place holder in the stack when configuring the span to avoid using bunch of locals */ +typedef struct ft_r2_conf_s { + /* openr2 types */ + openr2_variant_t variant; + openr2_calling_party_category_t category; + openr2_log_level_t loglevel; + + /* strings */ + char *logdir; + char *advanced_protocol_file; + + /* ints */ + int32_t max_ani; + int32_t max_dnis; + int32_t mfback_timeout; + int32_t metering_pulse_timeout; + + /* booleans */ + int immediate_accept; + int skip_category; + int get_ani_first; + int call_files; + int double_answer; + int charge_calls; + int forced_release; + int allow_collect_calls; +} ft_r2_conf_t; + +/* r2 configuration stored in span->signal_data */ +typedef struct ftdm_r2_data_s { + /* span flags */ + ftdm_r2_flag_t flags; + /* openr2 handle for the R2 variant context */ + openr2_context_t *r2context; + /* category to use when making calls */ + openr2_calling_party_category_t category; + /* whether to use OR2_CALL_WITH_CHARGE or OR2_CALL_NO_CHARGE when accepting a call */ + int charge_calls:1; + /* allow or reject collect calls */ + int allow_collect_calls:1; + /* whether to use forced release when hanging up */ + int forced_release:1; + /* whether accept the call when offered, or wait until the user decides to accept */ + int accept_on_offer:1; +} ftdm_r2_data_t; + +/* one element per span will be stored in g_mod_data_hash global var to keep track of them + and destroy them on module unload */ +typedef struct ftdm_r2_span_pvt_s { + openr2_context_t *r2context; /* r2 context allocated for this span */ + ftdm_hash_t *r2calls; /* hash table of allocated call data per channel for this span */ +} ftdm_r2_span_pvt_t; + +/* span monitor thread */ +static void *ftdm_r2_run(ftdm_thread_t *me, void *obj); + +/* channel monitor thread */ +static void *ftdm_r2_channel_run(ftdm_thread_t *me, void *obj); + +/* hash of all the private span allocations + we need to keep track of them to destroy them when unloading the module + since freetdm does not notify signaling modules when destroying a span + span -> ftdm_r2_mod_allocs_t */ +static ftdm_hash_t *g_mod_data_hash; + +/* IO interface for the command API */ +static ftdm_io_interface_t g_ftdm_r2_interface; + +static void ft_r2_clean_call(ftdm_r2_call_t *call) +{ + openr2_chan_t *r2chan = call->r2chan; + memset(call, 0, sizeof(*call)); + call->r2chan = r2chan; +} + +static void ft_r2_accept_call(ftdm_channel_t *ftdmchan) +{ + openr2_chan_t *r2chan = R2CALL(ftdmchan)->r2chan; + // FIXME: not always accept as no charge, let the user decide that + // also we should check the return code from openr2_chan_accept_call and handle error condition + // hanging up the call with protocol error as the reason, this openr2 API will fail only when there something + // wrong at the I/O layer or the library itself + openr2_chan_accept_call(r2chan, OR2_CALL_NO_CHARGE); + R2CALL(ftdmchan)->accepted = 1; +} + +static void ft_r2_answer_call(ftdm_channel_t *ftdmchan) +{ + openr2_chan_t *r2chan = R2CALL(ftdmchan)->r2chan; + // FIXME + // 1. check openr2_chan_answer_call return code + // 2. The openr2_chan_answer_call_with_mode should be used depending on user settings + // openr2_chan_answer_call_with_mode(r2chan, OR2_ANSWER_SIMPLE); + openr2_chan_answer_call(r2chan); + R2CALL(ftdmchan)->answer_pending = 0; +} + +static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call) +{ + ftdm_status_t status; + ftdm_mutex_lock(ftdmchan->mutex); + + /* the channel may be down but the thread not quite done */ + ftdm_wait_for_flag_cleared(ftdmchan, FTDM_CHANNEL_INTHREAD, 200); + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { + ftdm_log(FTDM_LOG_ERROR, "%d:%d Yay! R2 outgoing call in channel that is already in thread.\n", + ftdmchan->span_id, ftdmchan->chan_id); + ftdm_mutex_unlock(ftdmchan->mutex); + return FTDM_FAIL; + } + + ft_r2_clean_call(ftdmchan->call_data); + R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN; + ftdm_channel_set_state(ftdmchan, FTDM_CHANNEL_STATE_DIALING, 0); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); + R2CALL(ftdmchan)->ftdm_started = 1; + ftdm_mutex_unlock(ftdmchan->mutex); + + status = ftdm_thread_create_detached(ftdm_r2_channel_run, ftdmchan); + if (status == FTDM_FAIL) { + ftdm_log(FTDM_LOG_ERROR, "%d:%d Cannot handle request to start call in channel, failed to create thread!\n", + ftdmchan->span_id, ftdmchan->chan_id); + ftdm_channel_done(ftdmchan); + return FTDM_FAIL; + } + + return FTDM_SUCCESS; +} + +static ftdm_status_t ftdm_r2_start(ftdm_span_t *span) +{ + ftdm_r2_data_t *r2_data = span->signal_data; + ftdm_set_flag(r2_data, FTDM_R2_RUNNING); + return ftdm_thread_create_detached(ftdm_r2_run, span); +} + +/* always called from the monitor thread */ +static void ftdm_r2_on_call_init(openr2_chan_t *r2chan) +{ + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + ftdm_status_t status; + ftdm_log(FTDM_LOG_NOTICE, "Received request to start call on chan %d\n", openr2_chan_get_number(r2chan)); + + ftdm_mutex_lock(ftdmchan->mutex); + + if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) { + ftdm_log(FTDM_LOG_ERROR, "Cannot handle request to start call in channel %d, invalid state (%d)\n", + openr2_chan_get_number(r2chan), ftdmchan->state); + ftdm_mutex_unlock(ftdmchan->mutex); + return; + } + + /* the channel may be down but the thread not quite done */ + ftdm_wait_for_flag_cleared(ftdmchan, FTDM_CHANNEL_INTHREAD, 200); + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { + ftdm_log(FTDM_LOG_ERROR, "Cannot handle request to start call in channel %d, already in thread!\n", + openr2_chan_get_number(r2chan)); + ftdm_mutex_unlock(ftdmchan->mutex); + return; + } + ft_r2_clean_call(ftdmchan->call_data); + R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN; + ftdm_channel_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT, 0); + ftdm_mutex_unlock(ftdmchan->mutex); + + status = ftdm_thread_create_detached(ftdm_r2_channel_run, ftdmchan); + if (status == FTDM_FAIL) { + ftdm_log(FTDM_LOG_ERROR, "Cannot handle request to start call in channel %d, failed to create thread!\n", + openr2_chan_get_number(r2chan)); + } +} + +/* 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 */ +static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category) +{ + ftdm_sigmsg_t sigev; + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + + ftdm_log(FTDM_LOG_NOTICE, "Call offered on chan %d, ANI = %s, DNIS = %s, Category = %s\n", openr2_chan_get_number(r2chan), + ani, dnis, openr2_proto_get_category_string(category)); + + /* notify the user about the new call */ + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = ftdmchan->chan_id; + sigev.span_id = ftdmchan->span_id; + sigev.channel = ftdmchan; + sigev.event_id = FTDM_SIGEVENT_START; + + if (ftdm_span_send_signal(ftdmchan->span, &sigev) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_NOTICE, "Failed to handle call offered on chan %d\n", openr2_chan_get_number(r2chan)); + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); + return; + } + ftdm_channel_use(ftdmchan); + R2CALL(ftdmchan)->ftdm_started = 1; +} + +static void ftdm_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode) +{ + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + ftdm_log(FTDM_LOG_NOTICE, "Call accepted on chan %d\n", openr2_chan_get_number(r2chan)); + /* at this point the MF signaling has ended and there is no point on keep reading */ + openr2_chan_disable_read(r2chan); + if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) { + R2CALL(ftdmchan)->state_ack_pending = 1; + if (R2CALL(ftdmchan)->answer_pending) { + ftdm_log(FTDM_LOG_DEBUG, "Answer was pending on chan %d, answering now.\n", openr2_chan_get_number(r2chan)); + ft_r2_answer_call(ftdmchan); + return; + } + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } +} + +static void ftdm_r2_on_call_answered(openr2_chan_t *r2chan) +{ + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + ftdm_log(FTDM_LOG_NOTICE, "Call answered on chan %d\n", openr2_chan_get_number(r2chan)); + /* notify the upper layer of progress in the outbound call */ + if (OR2_DIR_FORWARD == openr2_chan_get_direction(r2chan)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); + } +} + +/* may be called in the signaling or media thread depending on whether the hangup is product of MF or CAS signaling */ +static void ftdm_r2_on_call_disconnect(openr2_chan_t *r2chan, openr2_call_disconnect_cause_t cause) +{ + ftdm_sigmsg_t sigev; + ftdm_r2_data_t *r2data; + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + ftdm_log(FTDM_LOG_NOTICE, "Call disconnected on chan %d\n", openr2_chan_get_number(r2chan)); + + ftdm_log(FTDM_LOG_DEBUG, "Got openr2 disconnection, clearing call on channel %d\n", ftdmchan->physical_chan_id); + + R2CALL(ftdmchan)->disconnect_rcvd = 1; + + /* acknowledge the hangup, cause will be ignored. From here to -> HANGUP once the freetdm side hangs up as well */ + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING); + + /* if the call has not been started yet we must go to HANGUP right here */ + if (!R2CALL(ftdmchan)->ftdm_started) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + return; + } + + /* FIXME: use the cause received from openr2 and map it to ftdm cause */ + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING; + + /* notify the user of the call terminating */ + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = ftdmchan->chan_id; + sigev.span_id = ftdmchan->span_id; + sigev.channel = ftdmchan; + sigev.event_id = FTDM_SIGEVENT_STOP; + r2data = ftdmchan->span->signal_data; + + ftdm_span_send_signal(ftdmchan->span, &sigev); +} + +static void ftdm_r2_on_call_end(openr2_chan_t *r2chan) +{ + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + ftdm_log(FTDM_LOG_NOTICE, "Call finished on chan %d\n", openr2_chan_get_number(r2chan)); + /* this means the freetdm side disconnected the call, therefore we must move to DOWN here */ + if (!R2CALL(ftdmchan)->disconnect_rcvd) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + return; + } +} + +static void ftdm_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen) +{ + ftdm_log(FTDM_LOG_NOTICE, "Call read data on chan %d\n", openr2_chan_get_number(r2chan)); +} + +static void ftdm_r2_on_hardware_alarm(openr2_chan_t *r2chan, int alarm) +{ + ftdm_log(FTDM_LOG_NOTICE, "Alarm on chan %d (%d)\n", openr2_chan_get_number(r2chan), alarm); +} + +static void ftdm_r2_on_os_error(openr2_chan_t *r2chan, int errorcode) +{ + ftdm_log(FTDM_LOG_ERROR, "OS error on chan %d: %s\n", openr2_chan_get_number(r2chan), strerror(errorcode)); +} + +static void ftdm_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_error_t reason) +{ + ftdm_sigmsg_t sigev; + ftdm_r2_data_t *r2data; + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + + ftdm_log(FTDM_LOG_ERROR, "Protocol error on chan %d\n", openr2_chan_get_number(r2chan)); + + R2CALL(ftdmchan)->disconnect_rcvd = 1; + + if (!R2CALL(ftdmchan)->ftdm_started) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + return; + } + + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_PROTOCOL_ERROR; + + /* notify the user of the call terminating */ + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = ftdmchan->chan_id; + sigev.span_id = ftdmchan->span_id; + sigev.channel = ftdmchan; + sigev.event_id = FTDM_SIGEVENT_STOP; + r2data = ftdmchan->span->signal_data; + + ftdm_span_send_signal(ftdmchan->span, &sigev); +} + +static void ftdm_r2_on_line_blocked(openr2_chan_t *r2chan) +{ + ftdm_log(FTDM_LOG_NOTICE, "Far end blocked on chan %d\n", openr2_chan_get_number(r2chan)); +} + +static void ftdm_r2_on_line_idle(openr2_chan_t *r2chan) +{ + ftdm_log(FTDM_LOG_NOTICE, "Far end unblocked on chan %d\n", openr2_chan_get_number(r2chan)); +} + +static void ftdm_r2_write_log(openr2_log_level_t level, const char *message) +{ + switch (level) { + case OR2_LOG_NOTICE: + ftdm_log(FTDM_LOG_NOTICE, "%s", message); + break; + case OR2_LOG_WARNING: + ftdm_log(FTDM_LOG_WARNING, "%s", message); + break; + case OR2_LOG_ERROR: + ftdm_log(FTDM_LOG_ERROR, "%s", message); + break; + case OR2_LOG_STACK_TRACE: + case OR2_LOG_MF_TRACE: + case OR2_LOG_CAS_TRACE: + case OR2_LOG_DEBUG: + case OR2_LOG_EX_DEBUG: + ftdm_log(FTDM_LOG_DEBUG, "%s", message); + break; + default: + ftdm_log(FTDM_LOG_WARNING, "We should handle logging level %d here.\n", level); + ftdm_log(FTDM_LOG_DEBUG, "%s", message); + break; + } +} + +static void ftdm_r2_on_context_log(openr2_context_t *r2context, openr2_log_level_t level, const char *fmt, va_list ap) +{ +#define CONTEXT_TAG "Context -" + char logmsg[256]; + char completemsg[sizeof(logmsg) + sizeof(CONTEXT_TAG) - 1]; + vsnprintf(logmsg, sizeof(logmsg), fmt, ap); + snprintf(completemsg, sizeof(completemsg), CONTEXT_TAG "%s", logmsg); + ftdm_r2_write_log(level, completemsg); +#undef CONTEXT_TAG +} + +static void ftdm_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level, const char *fmt, va_list ap) +{ +#define CHAN_TAG "Chan " + char logmsg[256]; + char completemsg[sizeof(logmsg) + sizeof(CHAN_TAG) - 1]; + vsnprintf(logmsg, sizeof(logmsg), fmt, ap); + snprintf(completemsg, sizeof(completemsg), CHAN_TAG "%d: %s", openr2_chan_get_number(r2chan), logmsg); + ftdm_r2_write_log(level, completemsg); +#undef CHAN_TAG +} + +static int ftdm_r2_on_dnis_digit_received(openr2_chan_t *r2chan, char digit) +{ + ftdm_sigmsg_t sigev; + ftdm_r2_data_t *r2data; + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + ftdm_size_t collected_len = R2CALL(ftdmchan)->dnis_index; + + ftdm_log(FTDM_LOG_DEBUG, "DNIS digit %d received chan %d\n", digit, openr2_chan_get_number(r2chan)); + + /* save the digit we just received */ + ftdmchan->caller_data.dnis.digits[collected_len] = digit; + collected_len++; + ftdmchan->caller_data.dnis.digits[collected_len] = '\0'; + R2CALL(ftdmchan)->dnis_index = collected_len; + + /* notify the user about the new digit and check if we should stop requesting more DNIS */ + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = ftdmchan->chan_id; + sigev.span_id = ftdmchan->span_id; + sigev.channel = ftdmchan; + sigev.event_id = FTDM_SIGEVENT_COLLECTED_DIGIT; + r2data = ftdmchan->span->signal_data; + if (ftdm_span_send_signal(ftdmchan->span, &sigev) == FTDM_BREAK) { + 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)); + return OR2_STOP_DNIS_REQUEST; + } + + /* the only other reason to stop requesting DNIS is that there is no more room to save it */ + if (collected_len == (sizeof(ftdmchan->caller_data.dnis.digits) - 1)) { + 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)); + return OR2_STOP_DNIS_REQUEST; + } + + return OR2_CONTINUE_DNIS_REQUEST; +} + +static void ftdm_r2_on_ani_digit_received(openr2_chan_t *r2chan, char digit) +{ + ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan); + ftdm_size_t collected_len = R2CALL(ftdmchan)->ani_index; + + /* check if we should drop ANI */ + if (collected_len == (sizeof(ftdmchan->caller_data.ani.digits) - 1)) { + ftdm_log(FTDM_LOG_NOTICE, "No more room for ANI %c on chan %d, digit dropped.\n", digit, openr2_chan_get_number(r2chan)); + return; + } + ftdm_log(FTDM_LOG_DEBUG, "ANI digit %c received chan %d\n", digit, openr2_chan_get_number(r2chan)); + + /* save the digit we just received */ + ftdmchan->caller_data.ani.digits[collected_len++] = digit; + ftdmchan->caller_data.ani.digits[collected_len] = '\0'; +} + +static openr2_event_interface_t ftdm_r2_event_iface = { + .on_call_init = ftdm_r2_on_call_init, + .on_call_offered = ftdm_r2_on_call_offered, + .on_call_accepted = ftdm_r2_on_call_accepted, + .on_call_answered = ftdm_r2_on_call_answered, + .on_call_disconnect = ftdm_r2_on_call_disconnect, + .on_call_end = ftdm_r2_on_call_end, + .on_call_read = ftdm_r2_on_call_read, + .on_hardware_alarm = ftdm_r2_on_hardware_alarm, + .on_os_error = ftdm_r2_on_os_error, + .on_protocol_error = ftdm_r2_on_protocol_error, + .on_line_blocked = ftdm_r2_on_line_blocked, + .on_line_idle = ftdm_r2_on_line_idle, + /* cast seems to be needed to get rid of the annoying warning regarding format attribute */ + .on_context_log = (openr2_handle_context_logging_func)ftdm_r2_on_context_log, + .on_dnis_digit_received = ftdm_r2_on_dnis_digit_received, + .on_ani_digit_received = ftdm_r2_on_ani_digit_received, + /* so far we do nothing with billing pulses */ + .on_billing_pulse_received = NULL +}; + +static int ftdm_r2_io_set_cas(openr2_chan_t *r2chan, int cas) +{ + ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan); + ftdm_status_t status = ftdm_channel_command(ftdm_chan, FTDM_COMMAND_SET_CAS_BITS, &cas); + if (FTDM_FAIL == status) { + return -1; + } + return 0; +} + +static int ftdm_r2_io_get_cas(openr2_chan_t *r2chan, int *cas) +{ + ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan); + ftdm_status_t status = ftdm_channel_command(ftdm_chan, FTDM_COMMAND_GET_CAS_BITS, cas); + if (FTDM_FAIL == status) { + return -1; + } + return 0; +} + +static int ftdm_r2_io_flush_write_buffers(openr2_chan_t *r2chan) +{ + ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan); + ftdm_status_t status = ftdm_channel_command(ftdm_chan, FTDM_COMMAND_FLUSH_TX_BUFFERS, NULL); + if (FTDM_FAIL == status) { + return -1; + } + return 0; +} + +static int ftdm_r2_io_write(openr2_chan_t *r2chan, const void *buf, int size) +{ + ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan); + ftdm_size_t outsize = size; + ftdm_status_t status = ftdm_channel_write(ftdm_chan, (void *)buf, size, &outsize); + if (FTDM_FAIL == status) { + return -1; + } + return outsize; +} + +static int ftdm_r2_io_read(openr2_chan_t *r2chan, const void *buf, int size) +{ + ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan); + ftdm_size_t outsize = size; + ftdm_status_t status = ftdm_channel_read(ftdm_chan, (void *)buf, &outsize); + if (FTDM_FAIL == status) { + return -1; + } + return outsize; +} + +static int ftdm_r2_io_wait(openr2_chan_t *r2chan, int *flags, int block) +{ + ftdm_status_t status; + ftdm_wait_flag_t ftdmflags = 0; + + ftdm_channel_t *ftdm_chan = openr2_chan_get_fd(r2chan); + int32_t timeout = block ? -1 : 0; + + if (*flags & OR2_IO_READ) { + ftdmflags |= FTDM_READ; + } + if (*flags & OR2_IO_WRITE) { + ftdmflags |= FTDM_WRITE; + } + if (*flags & OR2_IO_OOB_EVENT) { + ftdmflags |= FTDM_EVENTS; + } + + status = ftdm_channel_wait(ftdm_chan, &ftdmflags, timeout); + + if (FTDM_SUCCESS != status) { + return -1; + } + + *flags = 0; + if (ftdmflags & FTDM_READ) { + *flags |= OR2_IO_READ; + } + if (ftdmflags & FTDM_WRITE) { + *flags |= OR2_IO_WRITE; + } + if (ftdmflags & FTDM_EVENTS) { + *flags |= OR2_IO_OOB_EVENT; + } + + return 0; +} + +/* The following openr2 hooks never get called, read on for reasoning ... */ +/* since freetdm takes care of opening the file descriptor and using openr2_chan_new_from_fd, openr2 should never call this hook */ +static openr2_io_fd_t ftdm_r2_io_open(openr2_context_t *r2context, int channo) +{ + ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O open)!!\n"); + return NULL; +} + +/* since freetdm takes care of closing the file descriptor and uses openr2_chan_new_from_fd, openr2 should never call this hook */ +static int ftdm_r2_io_close(openr2_chan_t *r2chan) +{ + ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O close)!!\n"); + return 0; +} + +/* since freetdm takes care of opening the file descriptor and using openr2_chan_new_from_fd, openr2 should never call this hook */ +static int ftdm_r2_io_setup(openr2_chan_t *r2chan) +{ + ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O Setup)!!\n"); + return 0; +} + +/* since the signaling thread calls openr2_chan_process_cas_signaling directly, openr2 should never call this hook */ +static int ftdm_r2_io_get_oob_event(openr2_chan_t *r2chan, openr2_oob_event_t *event) +{ + *event = 0; + ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O get oob event)!!\n"); + return 0; +} + +static openr2_io_interface_t ftdm_r2_io_iface = { + .open = ftdm_r2_io_open, /* never called */ + .close = ftdm_r2_io_close, /* never called */ + .set_cas = ftdm_r2_io_set_cas, + .get_cas = ftdm_r2_io_get_cas, + .flush_write_buffers = ftdm_r2_io_flush_write_buffers, + .write = ftdm_r2_io_write, + .read = ftdm_r2_io_read, + .setup = ftdm_r2_io_setup, /* never called */ + .wait = ftdm_r2_io_wait, + .get_oob_event = ftdm_r2_io_get_oob_event /* never called */ +}; + +static FIO_SIG_CONFIGURE_FUNCTION(ftdm_r2_configure_span) + //ftdm_status_t (ftdm_span_t *span, fio_signal_cb_t sig_cb, va_list ap) +{ + int i = 0; + int conf_failure = 0; + char *var = NULL; + char *val = NULL; + ftdm_r2_data_t *r2data = NULL; + ftdm_r2_span_pvt_t *spanpvt = NULL; + ftdm_r2_call_t *r2call = NULL; + openr2_chan_t *r2chan = NULL; + + assert(sig_cb != NULL); + + ft_r2_conf_t r2conf = + { + .variant = OR2_VAR_ITU, + .category = OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER, + .loglevel = OR2_LOG_ERROR | OR2_LOG_WARNING, + .max_ani = 10, + .max_dnis = 4, + .mfback_timeout = -1, + .metering_pulse_timeout = -1, + .allow_collect_calls = -1, + .immediate_accept = -1, + .skip_category = -1, + .forced_release = -1, + .charge_calls = -1, + .get_ani_first = -1, + .call_files = -1, + .logdir = NULL, + .advanced_protocol_file = NULL + }; + + + if (span->signal_type) { + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling."); + return FTDM_FAIL; + } + + while ((var = va_arg(ap, char *))) { + ftdm_log(FTDM_LOG_DEBUG, "Reading R2 parameter %s for span %d\n", var, span->span_id); + if (!strcasecmp(var, "variant")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (ftdm_strlen_zero_buf(val)) { + ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 variant parameter\n"); + continue; + } + r2conf.variant = openr2_proto_get_variant(val); + if (r2conf.variant == OR2_VAR_UNKNOWN) { + ftdm_log(FTDM_LOG_ERROR, "Unknown R2 variant %s\n", val); + conf_failure = 1; + break; + } + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d for variant %s\n", span->span_id, val); + } else if (!strcasecmp(var, "category")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (ftdm_strlen_zero_buf(val)) { + ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 category parameter\n"); + continue; + } + r2conf.category = openr2_proto_get_category(val); + if (r2conf.category == OR2_CALLING_PARTY_CATEGORY_UNKNOWN) { + ftdm_log(FTDM_LOG_ERROR, "Unknown R2 caller category %s\n", val); + conf_failure = 1; + break; + } + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with default category %s\n", span->span_id, val); + } else if (!strcasecmp(var, "logdir")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (ftdm_strlen_zero_buf(val)) { + ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 logdir parameter\n"); + continue; + } + r2conf.logdir = val; + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with logdir %s\n", span->span_id, val); + } else if (!strcasecmp(var, "logging")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (ftdm_strlen_zero_buf(val)) { + ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 logging parameter\n"); + continue; + } + openr2_log_level_t tmplevel; + char *clevel; + char *logval = ftdm_malloc(strlen(val)+1); /* alloca man page scared me, so better to use good ol' malloc */ + if (!logval) { + ftdm_log(FTDM_LOG_WARNING, "Ignoring R2 logging parameter: '%s', failed to alloc memory\n", val); + continue; + } + strcpy(logval, val); + while (logval) { + clevel = strsep(&logval, ","); + if (-1 == (tmplevel = openr2_log_get_level(clevel))) { + ftdm_log(FTDM_LOG_WARNING, "Ignoring invalid R2 logging level: '%s'\n", clevel); + continue; + } + r2conf.loglevel |= tmplevel; + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with loglevel %s\n", span->span_id, clevel); + } + ftdm_safe_free(logval); + } else if (!strcasecmp(var, "advanced_protocol_file")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (ftdm_strlen_zero_buf(val)) { + ftdm_log(FTDM_LOG_NOTICE, "Ignoring empty R2 advanced_protocol_file parameter\n"); + continue; + } + r2conf.advanced_protocol_file = val; + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with advanced protocol file %s\n", span->span_id, val); + } else if (!strcasecmp(var, "allow_collect_calls")) { + r2conf.allow_collect_calls = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with allow collect calls max ani = %d\n", span->span_id, r2conf.allow_collect_calls); + } else if (!strcasecmp(var, "double_answer")) { + r2conf.double_answer = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with double answer = %d\n", span->span_id, r2conf.double_answer); + } else if (!strcasecmp(var, "immediate_accept")) { + r2conf.immediate_accept = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with immediate accept = %d\n", span->span_id, r2conf.immediate_accept); + } else if (!strcasecmp(var, "skip_category")) { + r2conf.skip_category = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with skip category = %d\n", span->span_id, r2conf.skip_category); + } else if (!strcasecmp(var, "forced_release")) { + r2conf.forced_release = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with forced release = %d\n", span->span_id, r2conf.forced_release); + } else if (!strcasecmp(var, "charge_calls")) { + r2conf.charge_calls = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with charge calls = %d\n", span->span_id, r2conf.charge_calls); + } else if (!strcasecmp(var, "get_ani_first")) { + r2conf.get_ani_first = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with get ani first = %d\n", span->span_id, r2conf.get_ani_first); + } else if (!strcasecmp(var, "call_files")) { + r2conf.call_files = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with call files = %d\n", span->span_id, r2conf.call_files); + } else if (!strcasecmp(var, "mfback_timeout")) { + r2conf.mfback_timeout = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with MF backward timeout = %dms\n", span->span_id, r2conf.mfback_timeout); + } else if (!strcasecmp(var, "metering_pulse_timeout")) { + r2conf.metering_pulse_timeout = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with metering pulse timeout = %dms\n", span->span_id, r2conf.metering_pulse_timeout); + } else if (!strcasecmp(var, "max_ani")) { + r2conf.max_ani = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with max ani = %d\n", span->span_id, r2conf.max_ani); + } else if (!strcasecmp(var, "max_dnis")) { + r2conf.max_dnis = va_arg(ap, int); + ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with max dnis = %d\n", span->span_id, r2conf.max_dnis); + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var); + return FTDM_FAIL; + } + } + + if (conf_failure) { + snprintf(span->last_error, sizeof(span->last_error), "R2 configuration error"); + return FTDM_FAIL; + } + + r2data = ftdm_malloc(sizeof(*r2data)); + if (!r2data) { + snprintf(span->last_error, sizeof(span->last_error), "Failed to allocate R2 data."); + return FTDM_FAIL; + } + memset(r2data, 0, sizeof(*r2data)); + + spanpvt = ftdm_malloc(sizeof(*spanpvt)); + if (!spanpvt) { + snprintf(span->last_error, sizeof(span->last_error), "Failed to allocate private span data container."); + goto fail; + } + memset(spanpvt, 0, sizeof(*spanpvt)); + + r2data->r2context = openr2_context_new(r2conf.variant, &ftdm_r2_event_iface, r2conf.max_ani, r2conf.max_dnis); + if (!r2data->r2context) { + snprintf(span->last_error, sizeof(span->last_error), "Cannot create openr2 context for span."); + goto fail; + } + openr2_context_set_io_type(r2data->r2context, OR2_IO_CUSTOM, &ftdm_r2_io_iface); + openr2_context_set_log_level(r2data->r2context, r2conf.loglevel); + openr2_context_set_ani_first(r2data->r2context, r2conf.get_ani_first); + openr2_context_set_skip_category_request(r2data->r2context, r2conf.skip_category); + openr2_context_set_mf_back_timeout(r2data->r2context, r2conf.mfback_timeout); + openr2_context_set_metering_pulse_timeout(r2data->r2context, r2conf.metering_pulse_timeout); + openr2_context_set_double_answer(r2data->r2context, r2conf.double_answer); + openr2_context_set_immediate_accept(r2data->r2context, r2conf.immediate_accept); + if (r2conf.logdir) { + openr2_context_set_log_directory(r2data->r2context, r2conf.logdir); + } + if (r2conf.advanced_protocol_file) { + openr2_context_configure_from_advanced_file(r2data->r2context, r2conf.advanced_protocol_file); + } + + 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."); + goto fail; + } + + for (i = 1; (i <= span->chan_count) && (i <= FTDM_MAX_CHANNELS_SPAN); i++) { + r2chan = openr2_chan_new_from_fd(r2data->r2context, span->channels[i], span->channels[i]->physical_chan_id); + if (!r2chan) { + snprintf(span->last_error, sizeof(span->last_error), "Cannot create all openr2 channels for span."); + goto fail; + } + if (r2conf.call_files) { + openr2_chan_enable_call_files(r2chan); + openr2_chan_set_log_level(r2chan, r2conf.loglevel); + } + + r2call = ftdm_malloc(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; + span->channels[i]->call_data = r2call; + /* value and key are the same so just free one of them */ + snprintf(r2call->name, sizeof(r2call->name), "chancall%d", i); + hashtable_insert(spanpvt->r2calls, (void *)r2call->name, r2call, HASHTABLE_FLAG_FREE_VALUE); + + } + spanpvt->r2context = r2data->r2context; + + /* just the value must be freed by the hash */ + hashtable_insert(g_mod_data_hash, (void *)span->name, spanpvt, HASHTABLE_FLAG_FREE_VALUE); + + span->start = ftdm_r2_start; + r2data->flags = 0; + span->signal_cb = sig_cb; + span->signal_type = FTDM_SIGTYPE_R2; + span->signal_data = r2data; + span->outgoing_call = r2_outgoing_call; + + return FTDM_SUCCESS; + +fail: + + if (r2data && r2data->r2context) { + openr2_context_delete(r2data->r2context); + } + if (spanpvt && spanpvt->r2calls) { + hashtable_destroy(spanpvt->r2calls); + } + ftdm_safe_free(r2data); + ftdm_safe_free(spanpvt); + return FTDM_FAIL; + +} + +static void *ftdm_r2_channel_run(ftdm_thread_t *me, void *obj) +{ + ftdm_channel_t *closed_chan; + uint32_t interval = 0; + ftdm_sigmsg_t sigev; + ftdm_channel_t *ftdmchan = (ftdm_channel_t *)obj; + openr2_chan_t *r2chan = R2CALL(ftdmchan)->r2chan; + + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INTHREAD); + + ftdm_mutex_lock(g_thread_count_mutex); + g_thread_count++; + ftdm_mutex_unlock(g_thread_count_mutex); + + ftdm_log(FTDM_LOG_DEBUG, "R2 CHANNEL thread starting on %d in state %s.\n", + ftdmchan->physical_chan_id, + ftdm_channel_state2str(ftdmchan->state)); + + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "OPEN ERROR [%s]\n", ftdmchan->last_error); + goto endthread; + } + + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval); + + assert(interval != 0); + ftdm_log(FTDM_LOG_DEBUG, "Got %d interval for chan %d\n", interval, ftdmchan->physical_chan_id); + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + /* FIXME: is this needed? */ + memset(ftdmchan->caller_data.dnis.digits, 0, sizeof(ftdmchan->caller_data.collected)); + memset(ftdmchan->caller_data.ani.digits, 0, sizeof(ftdmchan->caller_data.collected)); + } + + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = ftdmchan->chan_id; + sigev.span_id = ftdmchan->span_id; + sigev.channel = ftdmchan; + + while (ftdm_running()) { + int32_t read_enabled = openr2_chan_get_read_enabled(r2chan); + ftdm_wait_flag_t flags = read_enabled ? ( FTDM_READ | FTDM_WRITE ) : 0; + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE) && (R2CALL(ftdmchan)->chanstate != ftdmchan->state)) { + + 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)); + R2CALL(ftdmchan)->chanstate = ftdmchan->state; + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) && !R2CALL(ftdmchan)->accepted && + (ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS || + ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA || + ftdmchan->state == FTDM_CHANNEL_STATE_UP) ) { + /* if an accept ack will be required we should not acknowledge the state change just yet, + it will be done below after processing the MF signals, otherwise we have a race condition between freetdm calling + openr2_chan_answer_call and openr2 accepting the call first, if freetdm calls openr2_chan_answer_call before the accept cycle + completes, openr2 will fail to answer the call */ + 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)); + } else if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN){ + /* the down state will be completed in ftdm_channel_done below */ + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + ftdm_channel_complete_state(ftdmchan); + } + + switch (ftdmchan->state) { + + /* starting an incoming call */ + case FTDM_CHANNEL_STATE_COLLECT: + { + ftdm_log(FTDM_LOG_DEBUG, "COLLECT: Starting processing of incoming call in channel %d with interval %d\n", ftdmchan->physical_chan_id, interval); + } + break; + + /* starting an outgoing call */ + case FTDM_CHANNEL_STATE_DIALING: + { + // FIXME: use user defined calling party + ftdm_channel_use(ftdmchan); + ftdm_log(FTDM_LOG_DEBUG, "DIALING: Starting processing of outgoing call in channel %d with interval %d\n", ftdmchan->physical_chan_id, interval); + if (openr2_chan_make_call(r2chan, ftdmchan->caller_data.cid_num.digits, ftdmchan->caller_data.dnis.digits, OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER)) { + 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); + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER; + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } + break; + + /* the call is ringing */ + case FTDM_CHANNEL_STATE_PROGRESS: + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + if (!R2CALL(ftdmchan)->accepted) { + ftdm_log(FTDM_LOG_DEBUG, "PROGRESS: Accepting call on channel %d\n", ftdmchan->physical_chan_id); + ft_r2_accept_call(ftdmchan); + } + } else { + ftdm_log(FTDM_LOG_DEBUG, "PROGRESS: Notifying progress in channel %d\n", ftdmchan->physical_chan_id); + sigev.event_id = FTDM_SIGEVENT_PROGRESS; + if (ftdm_span_send_signal(ftdmchan->span, &sigev) != FTDM_SUCCESS) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } + } + break; + + /* the call was answered */ + case FTDM_CHANNEL_STATE_UP: + { + ftdm_log(FTDM_LOG_DEBUG, "UP: Call was answered on channel %d\n", ftdmchan->physical_chan_id); + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + if (!R2CALL(ftdmchan)->accepted) { + ftdm_log(FTDM_LOG_DEBUG, "UP: Call has not been accepted, need to accept first\n"); + // the answering will be done in the on_call_accepted handler + ft_r2_accept_call(ftdmchan); + R2CALL(ftdmchan)->answer_pending = 1; + } else { + ft_r2_answer_call(ftdmchan); + } + } else { + ftdm_log(FTDM_LOG_DEBUG, "UP: Notifying of call answered in channel %d\n", ftdmchan->physical_chan_id); + sigev.event_id = FTDM_SIGEVENT_UP; + if (ftdm_span_send_signal(ftdmchan->span, &sigev) != FTDM_SUCCESS) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } + } + break; + + /* just got hangup */ + case FTDM_CHANNEL_STATE_HANGUP: + { + /* FIXME: the cause should be retrieved from ftdmchan->caller_data.hangup_cause and translated from Q931 to R2 cause */ + ftdm_log(FTDM_LOG_DEBUG, "HANGUP: Clearing call on channel %d\n", ftdmchan->physical_chan_id); + if (!R2CALL(ftdmchan)->disconnect_rcvd) { + /* this will disconnect the call, but need to wait for the call end before moving to DOWN */ + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING); + } else { + /* at this point on_call_end possibly was already called, + * but we needed to wait for the freetdm confirmation before moving to DOWN */ + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + } + break; + + /* just got hangup from the freetdm side due to abnormal failure */ + case FTDM_CHANNEL_STATE_CANCEL: + { + ftdm_log(FTDM_LOG_DEBUG, "CANCEL: Unable to receive call on channel %d\n", ftdmchan->physical_chan_id); + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER); + } + break; + + /* finished call for good */ + case FTDM_CHANNEL_STATE_DOWN: + { + ftdm_log(FTDM_LOG_DEBUG, "DOWN: Placing channel %d back to the pool of available channels\n", ftdmchan->physical_chan_id); + ftdm_channel_done(ftdmchan); + goto endthread; + } + break; + + default: + { + ftdm_log(FTDM_LOG_ERROR, "%s: Unhandled channel state change in channel %d\n", ftdm_channel_state2str(ftdmchan->state), ftdmchan->physical_chan_id); + } + break; + + } + } + + if (flags) { + if (ftdm_channel_wait(ftdmchan, &flags, interval * 2) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_DEBUG, "ftdm_channel_wait did not return FTDM_SUCCESS\n"); + continue; + } + + /* handle timeout events first if any */ + openr2_chan_run_schedule(r2chan); + + /* openr2 will now try to detect MF tones, make sense out of them, reply if necessary with another tone and trigger + * telephony events via the call event interface we provided when creating the R2 context. + * openr2 will also call our I/O callbacks to retrieve audio from the channel and call our wait poll I/O registered callback + * and will not return from this function until the I/O poll callback returns no pending events + * */ + openr2_chan_process_mf_signaling(r2chan); + if (R2CALL(ftdmchan)->state_ack_pending) { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + ftdm_channel_complete_state(ftdmchan); + R2CALL(ftdmchan)->state_ack_pending = 0; + } + } else { + /* once the MF signaling has end we just loop here waiting for state changes */ + ftdm_sleep(interval); + } + + } + +endthread: + + closed_chan = ftdmchan; + ftdm_channel_close(&closed_chan); + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_INTHREAD); + ftdm_log(FTDM_LOG_DEBUG, "R2 channel %d thread ended.\n", ftdmchan->physical_chan_id); + + ftdm_mutex_lock(g_thread_count_mutex); + g_thread_count--; + ftdm_mutex_unlock(g_thread_count_mutex); + + return NULL; +} + +static void *ftdm_r2_run(ftdm_thread_t *me, void *obj) +{ + openr2_chan_t *r2chan; + ftdm_status_t status; + ftdm_span_t *span = (ftdm_span_t *) obj; + ftdm_r2_data_t *r2data = span->signal_data; + int waitms = 1000; + int i; + + ftdm_log(FTDM_LOG_DEBUG, "OpenR2 monitor thread started.\n"); + r2chan = NULL; + for (i = 1; i <= span->chan_count; i++) { + r2chan = R2CALL(span->channels[i])->r2chan; + openr2_chan_set_idle(r2chan); + openr2_chan_process_cas_signaling(r2chan); + } + + while (ftdm_running() && ftdm_test_flag(r2data, FTDM_R2_RUNNING)) { + status = ftdm_span_poll_event(span, waitms); + if (FTDM_FAIL == status) { + ftdm_log(FTDM_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error); + continue; + } + if (FTDM_SUCCESS == status) { + ftdm_event_t *event; + while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) { + if (event->enum_id == FTDM_OOB_CAS_BITS_CHANGE) { + r2chan = R2CALL(event->channel)->r2chan; + ftdm_log(FTDM_LOG_DEBUG, "Handling CAS on channel %d.\n", openr2_chan_get_number(r2chan)); + // we only expect CAS and other OOB events on this thread/loop, once a call is started + // the MF events (in-band signaling) are handled in the call thread + openr2_chan_process_cas_signaling(r2chan); + } else { + ftdm_log(FTDM_LOG_DEBUG, "Ignoring event %d on channel %d.\n", event->enum_id, openr2_chan_get_number(r2chan)); + // XXX TODO: handle alarms here XXX + } + } + } else if (status != FTDM_TIMEOUT) { + ftdm_log(FTDM_LOG_ERROR, "ftdm_span_poll_event returned %d.\n", status); + } else { + //ftdm_log(FTDM_LOG_DEBUG, "timed out waiting for event on span %d\n", span->span_id); + } + } + + /* + FIXME: we should set BLOCKED but at this point I/O routines of freetdm caused segfault + for (i = 1; i <= span->chan_count; i++) { + r2chan = R2CALL(span->channels[i])->r2chan; + openr2_chan_set_blocked(r2chan); + } + */ + + ftdm_clear_flag(r2data, FTDM_R2_RUNNING); + ftdm_log(FTDM_LOG_DEBUG, "R2 thread ending.\n"); + + return NULL; + +} + +static FIO_API_FUNCTION(ftdm_r2_api) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + + if (data) { + mycmd = ftdm_strdup(data); + argc = ftdm_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (argc == 2) { + if (!strcasecmp(argv[0], "kill")) { + int span_id = atoi(argv[1]); + ftdm_span_t *span = NULL; + + if (ftdm_span_find_by_name(argv[1], &span) == FTDM_SUCCESS || ftdm_span_find(span_id, &span) == FTDM_SUCCESS) { + ftdm_r2_data_t *r2data = span->signal_data; + + if (span->start != ftdm_r2_start) { + stream->write_function(stream, "-ERR invalid span.\n"); + goto done; + } + + ftdm_clear_flag(r2data, FTDM_R2_RUNNING); + stream->write_function(stream, "+OK killed.\n"); + goto done; + } else { + stream->write_function(stream, "-ERR invalid span.\n"); + goto done; + } + } + + if (!strcasecmp(argv[0], "status")) { + int span_id = atoi(argv[1]); + ftdm_r2_data_t *r2data = NULL; + ftdm_span_t *span = NULL; + openr2_chan_t *r2chan = NULL; + openr2_context_t *r2context = NULL; + int i = 0; + + if (ftdm_span_find_by_name(argv[1], &span) == FTDM_SUCCESS || ftdm_span_find(span_id, &span) == FTDM_SUCCESS) { + if (span->start != ftdm_r2_start) { + stream->write_function(stream, "-ERR not an R2 span.\n"); + goto done; + } + if (!(r2data = span->signal_data)) { + stream->write_function(stream, "-ERR invalid span. No R2 singal data in span.\n"); + goto done; + } + r2context = r2data->r2context; + openr2_variant_t r2variant = openr2_context_get_variant(r2context); + stream->write_function(stream, + "Variant: %s\n" + "Max ANI: %d\n" + "Max DNIS: %d\n" + "ANI First: %s\n" + "Immediate Accept: %s\n", + openr2_proto_get_variant_string(r2variant), + openr2_context_get_max_ani(r2context), + openr2_context_get_max_dnis(r2context), + openr2_context_get_ani_first(r2context) ? "Yes" : "No", + openr2_context_get_immediate_accept(r2context) ? "Yes" : "No"); + stream->write_function(stream, "\n"); + stream->write_function(stream, "%4s %-12.12s %-12.12s\n", "Channel", "Tx CAS", "Rx CAS"); + for (i = 1; i <= span->chan_count; i++) { + if (i == 16) continue; + r2chan = R2CALL(span->channels[i])->r2chan; + stream->write_function(stream, "%4d %-12.12s %-12.12s\n", + span->channels[i]->physical_chan_id, + openr2_chan_get_tx_cas_string(r2chan), + openr2_chan_get_rx_cas_string(r2chan)); + } + stream->write_function(stream, "\n"); + stream->write_function(stream, "+OK.\n"); + goto done; + } else { + stream->write_function(stream, "-ERR invalid span.\n"); + goto done; + } + } + + } + + if (argc == 1) { + if (!strcasecmp(argv[0], "threads")) { + ftdm_mutex_lock(g_thread_count_mutex); + stream->write_function(stream, "%d R2 channel threads up\n", g_thread_count); + ftdm_mutex_unlock(g_thread_count_mutex); + stream->write_function(stream, "+OK.\n"); + goto done; + } + + if (!strcasecmp(argv[0], "version")) { + stream->write_function(stream, "OpenR2 version: %s, revision: %s\n", openr2_get_version(), openr2_get_revision()); + stream->write_function(stream, "+OK.\n"); + goto done; + } + + if (!strcasecmp(argv[0], "variants")) { + int32_t numvariants = 0; + const openr2_variant_entry_t *variants = openr2_proto_get_variant_list(&numvariants); + if (!variants) { + stream->write_function(stream, "-ERR failed to retrieve openr2 variant list.\n"); + goto done; + } +#define VARIANT_FORMAT "%4s %40s\n" + stream->write_function(stream, VARIANT_FORMAT, "Variant Code", "Country"); + numvariants--; + for (; numvariants; numvariants--) { + stream->write_function(stream, VARIANT_FORMAT, variants[numvariants].name, variants[numvariants].country); + } + stream->write_function(stream, "+OK.\n"); +#undef VARIANT_FORMAT + goto done; + } + } + + stream->write_function(stream, "-ERR invalid command.\n"); + +done: + + ftdm_safe_free(mycmd); + + return FTDM_SUCCESS; + +} + +static FIO_IO_LOAD_FUNCTION(ftdm_r2_io_init) +{ + assert(fio != NULL); + memset(&g_ftdm_r2_interface, 0, sizeof(g_ftdm_r2_interface)); + + g_ftdm_r2_interface.name = "r2"; + g_ftdm_r2_interface.api = ftdm_r2_api; + + *fio = &g_ftdm_r2_interface; + + return FTDM_SUCCESS; +} + +static FIO_SIG_LOAD_FUNCTION(ftdm_r2_init) +{ + g_mod_data_hash = create_hashtable(10, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + if (!g_mod_data_hash) { + return FTDM_FAIL; + } + ftdm_mutex_create(&g_thread_count_mutex); + return FTDM_SUCCESS; +} + +static FIO_SIG_UNLOAD_FUNCTION(ftdm_r2_destroy) +{ + ftdm_hash_iterator_t *i = NULL; + ftdm_r2_span_pvt_t *spanpvt = NULL; + const void *key = NULL; + void *val = NULL; + for (i = hashtable_first(g_mod_data_hash); i; i = hashtable_next(i)) { + hashtable_this(i, &key, NULL, &val); + if (key && val) { + spanpvt = val; + openr2_context_delete(spanpvt->r2context); + hashtable_destroy(spanpvt->r2calls); + } + } + hashtable_destroy(g_mod_data_hash); + ftdm_mutex_destroy(&g_thread_count_mutex); + return FTDM_SUCCESS; +} + +ftdm_module_t ftdm_module = { + "r2", + ftdm_r2_io_init, + NULL, + ftdm_r2_init, + ftdm_r2_configure_span, + ftdm_r2_destroy +}; + + +/* 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 + */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/BOOST.limitations b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/BOOST.limitations new file mode 100644 index 0000000000..663b7d376a --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/BOOST.limitations @@ -0,0 +1,20 @@ +== Boost sigmod current limitations == +- we don't support having openzap spans with physical channels + belonging to other physical spans. this is due to netborder sangoma abstraction, therefore + any openzap span using sigboost must have only channels belonging to the corresponding + physical span. + + This is the reason we added group functionality in openzap core, furthermore, previous groups in openzap + were only possible through adding of b-channels to a single span, but this forces the user to create groups + of channels only whithin the same type of trunk among other things. + +- all spans must be configured and then started, cannot configure, start, configure start etc + this is due to netborder telesoft abstraction. that requires configuring everything and + then starting everything at once. + +- sangoma_prid and sangoma_brid on Windows had to be compiled hacking make/Makefile.platform to comment all VC runtime checks, + otherwise when running in debug mode exceptions are thrown due to loss of data ie short to char conversions. + +== TODO == +- proper upper layer management of HW alarms (this must be done in mod_openzap.c) + diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/boost-tasks.txt b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/boost-tasks.txt new file mode 100644 index 0000000000..f0eab3a277 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/boost-tasks.txt @@ -0,0 +1,146 @@ +== General Design == + +NBE will do its current loading of spans and configuration process through Sangoma Board Manager (SBM). +After doing SangomaBoardManager::getInstance().configure -> start. It will proceed to initalize +the openzap stack (just as the TelesoftStack is loaded after starting SMB. The procedure will be: + +- create a static or malloced zap_io_interface_t +- call zap_global_set_logger with the logging hooks. +- call zap_global_set_memhandler() with the memory hooks. +- call zap_global_init() to initialize the stack +- call zap_add_io_iface() to add the I/O iface. +- iterate over all SBM spans configured for BRI or any boost-managed signaling and: + * call zap_span_create(NBE I/O mod, in_ptrSpan, SMB span name) + * Fill in some members like: + span->trunk_type = E1/T1/J1/FXO/FXS etc ... + * iterate over all channels in SMB span and: + * zap_span_add_channel(zap_span, sock, type:CAS|BCHAN|DCHAN|ETC) + * call zap_configure_span("sangoma_boost", span, sigmsg_callback, "param1", value1, "param2", value1 ...) + * zap_span_start(span); + + +At this point, NBE would receive signaling msgs via sigmsg_callback registered when configuring +and NBE would request hangup or making calls throug openzap API, like zap_set_state_* and zap_channel_outgoing_call() to place calls. + +When NBE wants to check for link status. + + zap_get_siglink_state() which would return + ZAP_SIG_STATE_UP (D-chan UP, R2 bits in IDLE, ss7?) + ZAP_SIG_STATE_SUSPENDED (D-chan in power saving mode?) + ZAP_SIG_STATE_DOWN (D-chan down, R2 bits in blocked, ss7?) + + Whenever a state in sig link changes, the sigmsg_callback will be used to notify NBE or any other user. + +NOTE: right now hardware alarms notification in openzap is seriously broken, +see ozmod_libpri.c process_event ... reads an event from hardware (zap_event_t *), +then checks the event type, if its ZAP_OOB_ALARM_TRAP prepares a zap_sigmsg_t +(signaling event) setting its event_id to ZAP_OOB_ALARM_TRAP, which is *WRONG* +because event_id is of type zap_signal_event_t and not zap_oob_event_t! +this means on alarm the user will get ZAP_SIGEVENT_PROGRESS_MEDIA!! which is +value 7 that is in conflict with ZAP_OOB_ALARM_TRAP, I think a separate +callback should be used if the outside user wants to be notified about +hardware events like HW DTMF or so. Currently there is alreadya generic DTMF +listener. + +== Tasks Stage 1 / OpenZAP and Boost changes (To be tested with FreeSWITCH) == + +- Change malloc and other mem functions in openzap + to use internal hooks provided via zap_global_set_memhandler() + which would be called before zap_global_init(), this is + already done for the logger via zap_global_set_logger() + + question: should the mem routines allow for memory pool ptr? + this could be useful to provide a memory pool to + the whole module. + + question: should we allow hooks for threads and locking? + I think we can skip this one unless needed. They already + use their own threading abstraction which is working for + Linux and Windows. If we ever need to profile threading + we can add profiling hooks. + + question: I had to add openzap calls to the hash table and libteletone implementations, is that acceptable? + +- Modify zap_global_init() API + + This API must just initialize vars, mutexes etc. + and NOT DO ANY CONFIGURATION LOADING, PARSING, SPAN CREATION and I/O + configuration, which is what is currently doing. + We don't want zap_global_init() to create the spans based on that configuration + since NBE will have its own configuration and will take care of creating + the needed data structures on its own. + +- Add new zap_std_io_config() API + + This API will parse the standard openzap.conf module and create the spans. + This will be used by FS but not by NBE, which will create the openzap spans by itself. + The NBE flow to initialize openzap will be: + +- Add new API zap_global_add_io_iface(), + + This API will add a new I/O interface structure to the internal openzap hash of I/O structs. + This is needed because NBE I/O structure will NOT be loaded from an openzap module (.so/.dll) + but rather just registered on runtime (probably from a static structure in NBE code). + This openzap hash is used by zap_api_execute() for example, to find the module that can + handle a given API, ie (oz libpri status). This is an example of how an openzap I/O interface + can decide to implement just the ->api() member to handle commands and NOTHING else, + so I/O interfaces not necessary are hardware-related. + +- Add new zap_channel_get_siglink_state(zap_channel, zap_siglink_status_t &status) + +- Modify mod_openzap.c to read proto= setting in boost spans, this will determine wich boost sig + module will handle the configuration and those channels. + + + + Then as first config arg to zap_config_span() the boost proto module name would be included as "sigmod" which will be used + by ozmod_sangoma_boost to decide which sig module must handle that span configuration + +- Create minimal boost mod interface. + + ozmod_boost_ss7 should load sig boost mods and get interface via dlsym(boost_get_interface) boost_get_interface(boost_iface); + The boost interface will have + * const char *name // boost sigmod name (brid,ss7d) + * set_write_boost_msg_cb(callback) // tell the stack how to send us boost messages + * set_sig_status_cb(callback); // tell the stack how to notify us about link status changes + * write_boost_msg(struct boost_msg) // send a boost msg to the stack + * configure_span(zap_span_t span, "configuration", value, "configuration", value) // configure a given span + * get_sig_status(openzap_sigstatus_t status) + * start(span) // to start a given openzap span + * stop(span) // to stop the stack on a given openzap span + +- Migrate current sangoma_brid sig module to openzap + * Make sangoma_brid a library + * Move from using malloc, threading, locking, logging and I/O to openzap functions. Export the boost sigmod interface and its supporting code. + +== State 2 Tasks == + +- Create the I/O NBE interface and supporting functions. It must be possible to poll over the span + given that ozmod_sangoma_boost BRI module and others may need to *wait* for data. The poll() + function in I/O NBE interface would wait on a pthread condition or Windows event, which would + be triggered by some external NBE component registered with Sangoma Board Manager (SMB) for d-chan + data, whenever d-chan data arrives, saves the data in a buffer and triggers the condition to wakeup + any waiter, then the waiter (sangoma_brid or any other boost client) calls zap_channel_read which calls + our own I/O NBE interface read method and retrieves the data from the buffer. + + Dropped alternative design: + Another option is to add a new API zap_span_push_incoming_data(span/chan, data); However this changes + the model openzap has followed and I don't think fits that well, since now we have 2 different models + to support in openzap. + +== TODO == + +- how about logging specific modules, like, just ozmod_boost, or just the BRI stack? + more work to be done so the BRI module uses zap_log instead of current syslog + then work to be done to be able to filter logs from specific openzap code? is it worth it? + +- remove FORCE_SEGFAULT from sprid + + +=== Shortcomings == + +- we had to drop smg support in the branch where we work on sangoma prid. + After all, most people using sangoma_prid is using freeswitch/openzap and not Sangoma Media Gateway + The problem is in freeswitch/openzap mode, sangoma_boost ozmod takes care of span events (POLLPRI) + where in SMG and Netborder POLLPRI is done typically by sangoma board manager. + diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftdm_sangoma_boost.h b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftdm_sangoma_boost.h new file mode 100644 index 0000000000..31a231f309 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftdm_sangoma_boost.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 FTDM_SANGOMA_BOOST_H +#define FTDM_SANGOMA_BOOST_H +#include "sangoma_boost_client.h" +#include "freetdm.h" + +#define MAX_CHANS_PER_TRUNKGROUP 1024 + +typedef enum { + FTDM_SANGOMA_BOOST_RUNNING = (1 << 0), + FTDM_SANGOMA_BOOST_RESTARTING = (1 << 1) +} ftdm_sangoma_boost_flag_t; + +typedef struct ftdm_sangoma_boost_data { + sangomabc_connection_t mcon; + sangomabc_connection_t pcon; + int iteration; + uint32_t flags; + boost_sigmod_interface_t *sigmod; + ftdm_queue_t *boost_queue; +} ftdm_sangoma_boost_data_t; + +typedef struct ftdm_sangoma_boost_trunkgroup { + ftdm_mutex_t *mutex; + ftdm_size_t size; /* Number of b-channels in group */ + unsigned int last_used_index; /* index of last b-channel used */ + ftdm_channel_t* ftdmchans[MAX_CHANS_PER_TRUNKGROUP]; + //DAVIDY need to merge congestion timeouts to this struct +} ftdm_sangoma_boost_trunkgroup_t; +#endif + +/* 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: + */ + diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2008.vcproj new file mode 100644 index 0000000000..12efa0384c --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.2008.vcproj @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c new file mode 100644 index 0000000000..e7317e3679 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c @@ -0,0 +1,2273 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + * Contributors: + * + * Moises Silva + * David Yat Sin + * Nenad Corbic + * + */ + +/* NOTE: +On __WINDOWS__ platform this code works with sigmod ONLY, don't try to make sense of any socket code for win +I basically ifdef out everything that the compiler complained about +*/ + +#include "freetdm.h" +#include "sangoma_boost_client.h" +#include "ftdm_sangoma_boost.h" +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +/* Boost signaling modules global hash and its mutex */ +ftdm_mutex_t *g_boost_modules_mutex = NULL; +ftdm_hash_t *g_boost_modules_hash = NULL; + +#define MAX_TRUNK_GROUPS 64 +//DAVIDY need to merge congestion_timeouts with ftdm_sangoma_boost_trunkgroups +static time_t congestion_timeouts[MAX_TRUNK_GROUPS]; + +static ftdm_sangoma_boost_trunkgroup_t *g_trunkgroups[MAX_TRUNK_GROUPS]; + +#define BOOST_QUEUE_SIZE 500 + +/* get freetdm span and chan depending on the span mode */ +#define BOOST_SPAN(ftdmchan) ((ftdm_sangoma_boost_data_t*)(ftdmchan)->span->signal_data)->sigmod ? ftdmchan->physical_span_id : ftdmchan->physical_span_id-1 +#define BOOST_CHAN(ftdmchan) ((ftdm_sangoma_boost_data_t*)(ftdmchan)->span->signal_data)->sigmod ? ftdmchan->physical_chan_id : ftdmchan->physical_chan_id-1 + +/** + * \brief Strange flag + */ +typedef enum { + SFLAG_FREE_REQ_ID = (1 << 0), + SFLAG_SENT_FINAL_MSG = (1 << 1), + SFLAG_SENT_ACK = (1 << 2), + SFLAG_RECVD_ACK = (1 << 3), + SFLAG_HANGUP = (1 << 4), + SFLAG_TERMINATING = (1 << 5) +} sflag_t; + +typedef uint16_t sangoma_boost_request_id_t; + +/** + * \brief SANGOMA boost request status + */ +typedef enum { + BST_FREE, + BST_WAITING, + BST_ACK, + BST_READY, + BST_FAIL +} sangoma_boost_request_status_t; + +/** + * \brief SANGOMA boost request structure + */ +typedef struct { + sangoma_boost_request_status_t status; + sangomabc_short_event_t event; + ftdm_span_t *span; + ftdm_channel_t *ftdmchan; + int hangup_cause; + int flags; +} sangoma_boost_request_t; + +//#define MAX_REQ_ID FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN * FTDM_MAX_CHANNELS_PHYSICAL_SPAN +#define MAX_REQ_ID 6000 + +static uint16_t SETUP_GRID[FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN+1][FTDM_MAX_CHANNELS_PHYSICAL_SPAN+1] = {{ 0 }}; + +static sangoma_boost_request_t OUTBOUND_REQUESTS[MAX_REQ_ID+1] = {{ 0 }}; + +static ftdm_mutex_t *request_mutex = NULL; + +static uint8_t req_map[MAX_REQ_ID+1] = { 0 }; +static uint8_t nack_map[MAX_REQ_ID+1] = { 0 }; + +/** + * \brief Releases span and channel from setup grid + * \param span Span number + * \param chan Channel number + * \param func Calling function + * \param line Line number on request + * \return NULL if not found, channel otherwise + */ +static void __release_request_id_span_chan(int span, int chan, const char *func, int line) +{ + int id; + + ftdm_mutex_lock(request_mutex); + if ((id = SETUP_GRID[span][chan])) { + assert(id <= MAX_REQ_ID); + req_map[id] = 0; + SETUP_GRID[span][chan] = 0; + } + ftdm_mutex_unlock(request_mutex); +} +#define release_request_id_span_chan(s, c) __release_request_id_span_chan(s, c, __FUNCTION__, __LINE__) + +/** + * \brief Releases request ID + * \param func Calling function + * \param line Line number on request + * \return NULL if not found, channel otherwise + */ +static void __release_request_id(sangoma_boost_request_id_t r, const char *func, int line) +{ + assert(r <= MAX_REQ_ID); + ftdm_mutex_lock(request_mutex); + req_map[r] = 0; + ftdm_mutex_unlock(request_mutex); +} +#define release_request_id(r) __release_request_id(r, __FUNCTION__, __LINE__) + +static sangoma_boost_request_id_t last_req = 0; + +/** + * \brief Gets the first available tank request ID + * \param func Calling function + * \param line Line number on request + * \return 0 on failure, request ID on success + */ +static sangoma_boost_request_id_t __next_request_id(const char *func, int line) +{ + sangoma_boost_request_id_t r = 0, i = 0; + int found=0; + + ftdm_mutex_lock(request_mutex); + //r = ++last_req; + //while(!r || req_map[r]) { + + for (i=1; i<= MAX_REQ_ID; i++){ + r = ++last_req; + + if (r >= MAX_REQ_ID) { + r = i = last_req = 1; + } + + if (req_map[r]) { + /* Busy find another */ + continue; + + } + + req_map[r] = 1; + found=1; + break; + + } + + ftdm_mutex_unlock(request_mutex); + + if (!found) { + return 0; + } + + return r; +} +#define next_request_id() __next_request_id(__FUNCTION__, __LINE__) + +/** + * \brief Finds the channel that triggered an event + * \param span Span where to search the channel + * \param event SANGOMA event + * \param force Do not wait for the channel to be available if in use + * \return NULL if not found, channel otherwise + */ +static ftdm_channel_t *find_ftdmchan(ftdm_span_t *span, sangomabc_short_event_t *event, int force) +{ + uint32_t i; + ftdm_channel_t *ftdmchan = NULL; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + uint32_t targetspan = event->span+1; + uint32_t targetchan = event->chan+1; + if (sangoma_boost_data->sigmod) { + /* span is not strictly needed here since we're supposed to get only events for our span */ + targetspan = event->span; + targetchan = event->chan; + } + + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->physical_span_id == targetspan && span->channels[i]->physical_chan_id == targetchan) { + ftdmchan = span->channels[i]; + if (force || (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE))) { + break; + } else { + ftdmchan = NULL; + ftdm_log(FTDM_LOG_DEBUG, "Channel %d:%d ~ %d:%d is already in use.\n", + span->channels[i]->span_id, + span->channels[i]->chan_id, + span->channels[i]->physical_span_id, + span->channels[i]->physical_chan_id + ); + break; + } + } + } + + return ftdmchan; +} + +static int check_congestion(int trunk_group) +{ + if (congestion_timeouts[trunk_group]) { + time_t now = time(NULL); + + if (now >= congestion_timeouts[trunk_group]) { + congestion_timeouts[trunk_group] = 0; + } else { + return 1; + } + } + + return 0; +} + + +/** + * \brief Requests an sangoma boost channel on a span (outgoing call) + * \param span Span where to get a channel + * \param chan_id Specific channel to get (0 for any) + * \param direction Call direction + * \param caller_data Caller information + * \param ftdmchan Channel to initialise + * \return Success or failure + */ +static FIO_CHANNEL_REQUEST_FUNCTION(sangoma_boost_channel_request) +{ + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + ftdm_status_t status = FTDM_FAIL; + sangoma_boost_request_id_t r; + sangomabc_event_t event = {0}; + /* sanity has to be more than 8 seconds. + * In PRI specs, timeout is 4 seconds for remote switch to respond to a SETUP, + * and PRI stack will retransmit a second SETUP after the first timeout, so + * we should allow for at least 8 seconds. + */ + int boost_request_timeout = 10000; + sangoma_boost_request_status_t st; + char dnis[128] = ""; + char *gr = NULL; + uint32_t count = 0; + int tg=0; + + if (sangoma_boost_data->sigmod) { + ftdm_log(FTDM_LOG_CRIT, "This function should not be called when sigmod was configured in boost\n"); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + if (ftdm_test_flag(span, FTDM_SPAN_SUSPENDED)) { + ftdm_log(FTDM_LOG_CRIT, "SPAN is not online.\n"); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + ftdm_set_string(dnis, caller_data->dnis.digits); + + r = next_request_id(); + if (r == 0) { + ftdm_log(FTDM_LOG_CRIT, "All tanks ids are busy.\n"); + *ftdmchan = NULL; + return FTDM_FAIL; + } + sangomabc_call_init(&event, caller_data->cid_num.digits, dnis, r); + + if ((gr = strchr(dnis, '@'))) { + *gr++ = '\0'; + } + + if (gr && *(gr+1)) { + tg = atoi(gr+1); + if (tg > 0) { + tg--; + } + } + event.trunk_group = tg; + + if (check_congestion(tg)) { + ftdm_log(FTDM_LOG_CRIT, "All circuits are busy. Trunk Group=%i (BOOST REQUESTED BACK OFF)\n",tg+1); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + ftdm_span_channel_use_count(span, &count); + + if (count >= span->chan_count) { + ftdm_log(FTDM_LOG_CRIT, "All circuits are busy.\n"); + *ftdmchan = NULL; + return FTDM_FAIL; + } + + if (gr && *(gr+1)) { + switch(*gr) { + case 'g': + event.hunt_group = SIGBOOST_HUNTGRP_SEQ_ASC; + break; + case 'G': + event.hunt_group = SIGBOOST_HUNTGRP_SEQ_DESC; + break; + case 'r': + event.hunt_group = SIGBOOST_HUNTGRP_RR_ASC; + break; + case 'R': + event.hunt_group = SIGBOOST_HUNTGRP_RR_DESC; + break; + default: + ftdm_log(FTDM_LOG_WARNING, "Failed to determine huntgroup (%s)\n", gr); + event.hunt_group = SIGBOOST_HUNTGRP_SEQ_ASC; + } + } + + ftdm_set_string(event.calling_name, caller_data->cid_name); + ftdm_set_string(event.rdnis.digits, caller_data->rdnis.digits); + if (strlen(caller_data->rdnis.digits)) { + event.rdnis.digits_count = strlen(caller_data->rdnis.digits)+1; + event.rdnis.ton = caller_data->rdnis.type; + event.rdnis.npi = caller_data->rdnis.plan; + } + + event.calling.screening_ind = caller_data->screen; + event.calling.presentation_ind = caller_data->pres; + + event.calling.ton = caller_data->cid_num.type; + event.calling.npi = caller_data->cid_num.plan; + + event.called.ton = caller_data->dnis.type; + event.called.npi = caller_data->dnis.plan; + + OUTBOUND_REQUESTS[r].status = BST_WAITING; + OUTBOUND_REQUESTS[r].span = span; + + if (sangomabc_connection_write(&sangoma_boost_data->mcon, &event) <= 0) { + ftdm_log(FTDM_LOG_CRIT, "Failed to tx boost event [%s]\n", strerror(errno)); + status = FTDM_FAIL; + if (!sangoma_boost_data->sigmod) { + *ftdmchan = NULL; + } + goto done; + } + + while(ftdm_running() && OUTBOUND_REQUESTS[r].status == BST_WAITING) { + ftdm_sleep(1); + if (--boost_request_timeout <= 0) { + status = FTDM_FAIL; + if (!sangoma_boost_data->sigmod) { + *ftdmchan = NULL; + } + ftdm_log(FTDM_LOG_CRIT, "s%dc%d: Csid:%d Timed out waiting for boost channel request response, current status: BST_WAITING\n", (*ftdmchan)->physical_span_id, (*ftdmchan)->physical_chan_id, r); + goto done; + } + } + + if (OUTBOUND_REQUESTS[r].status == BST_READY && OUTBOUND_REQUESTS[r].ftdmchan) { + *ftdmchan = OUTBOUND_REQUESTS[r].ftdmchan; + status = FTDM_SUCCESS; + } else { + status = FTDM_FAIL; + if (!sangoma_boost_data->sigmod) { + *ftdmchan = NULL; + } + } + + done: + + st = OUTBOUND_REQUESTS[r].status; + OUTBOUND_REQUESTS[r].status = BST_FREE; + + if (status == FTDM_FAIL) { + if (st == BST_FAIL) { + caller_data->hangup_cause = OUTBOUND_REQUESTS[r].hangup_cause; + } else { + caller_data->hangup_cause = FTDM_CAUSE_RECOVERY_ON_TIMER_EXPIRE; + } + } + + if (st == BST_FAIL) { + release_request_id(r); + } else if (st != BST_READY) { + ftdm_assert_return(r <= MAX_REQ_ID, FTDM_FAIL, "Invalid index\n"); + nack_map[r] = 1; + if (sangoma_boost_data->sigmod) { + sangomabc_exec_command(&sangoma_boost_data->mcon, + BOOST_SPAN((*ftdmchan)), + BOOST_CHAN((*ftdmchan)), + r, + SIGBOOST_EVENT_CALL_START_NACK, + 0, 0); + } else { + sangomabc_exec_command(&sangoma_boost_data->mcon, + 0, + 0, + r, + SIGBOOST_EVENT_CALL_START_NACK, + 0, 0); + } + } + + return status; +} + +/** + * \brief Starts an sangoma boost channel (outgoing call) + * \param ftdmchan Channel to initiate call on + * \return Success + */ +static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(sangoma_boost_outgoing_call) +{ + char dnis[128] = ""; + sangoma_boost_request_id_t r; + sangomabc_event_t event = {0}; + ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; + if (!sangoma_boost_data->sigmod) { + return FTDM_SUCCESS; + } + ftdm_set_string(dnis, ftdmchan->caller_data.dnis.digits); + + r = next_request_id(); + if (r == 0) { + ftdm_log(FTDM_LOG_CRIT, "All boost request ids are busy.\n"); + return FTDM_FAIL; + } + + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); + + sangomabc_call_init(&event, ftdmchan->caller_data.cid_num.digits, dnis, r); + + event.span = (uint8_t)ftdmchan->physical_span_id; + event.chan = (uint8_t)ftdmchan->physical_chan_id; + + ftdm_set_string(event.calling_name, ftdmchan->caller_data.cid_name); + ftdm_set_string(event.rdnis.digits, ftdmchan->caller_data.rdnis.digits); + if (strlen(ftdmchan->caller_data.rdnis.digits)) { + event.rdnis.digits_count = strlen(ftdmchan->caller_data.rdnis.digits)+1; + event.rdnis.ton = ftdmchan->caller_data.rdnis.type; + event.rdnis.npi = ftdmchan->caller_data.rdnis.plan; + } + + event.calling.screening_ind = ftdmchan->caller_data.screen; + event.calling.presentation_ind = ftdmchan->caller_data.pres; + + event.calling.ton = ftdmchan->caller_data.cid_num.type; + event.calling.npi = ftdmchan->caller_data.cid_num.plan; + + event.called.ton = ftdmchan->caller_data.dnis.type; + event.called.npi = ftdmchan->caller_data.dnis.plan; + + OUTBOUND_REQUESTS[r].status = BST_WAITING; + OUTBOUND_REQUESTS[r].span = ftdmchan->span; + OUTBOUND_REQUESTS[r].ftdmchan = ftdmchan; + + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DIALING); + + ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s over boost channel with request id %d\n", event.called_number_digits, r); + if (sangomabc_connection_write(&sangoma_boost_data->mcon, &event) <= 0) { + ftdm_log(FTDM_LOG_CRIT, "Failed to tx boost event [%s]\n", strerror(errno)); + return FTDM_FAIL; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Handler for call start ack no media event + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_progress(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_channel_t *ftdmchan; + + + if ((ftdmchan = find_ftdmchan(span, event, 1))) { + ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; + ftdm_mutex_lock(ftdmchan->mutex); + if (!sangoma_boost_data->sigmod && ftdmchan->state == FTDM_CHANNEL_STATE_HOLD) { + if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { + ftdmchan->init_state = FTDM_CHANNEL_STATE_PROGRESS_MEDIA; + ftdm_log(FTDM_LOG_DEBUG, "Channel init state updated to PROGRESS_MEDIA [Csid:%d]\n", event->call_setup_id); + } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { + ftdmchan->init_state = FTDM_CHANNEL_STATE_PROGRESS; + ftdm_log(FTDM_LOG_DEBUG, "Channel init state updated to PROGRESS [Csid:%d]\n", event->call_setup_id); + } else { + ftdmchan->init_state = FTDM_CHANNEL_STATE_IDLE; + ftdm_log(FTDM_LOG_DEBUG, "Channel init state updated to IDLE [Csid:%d]\n", event->call_setup_id); + } + } else { + if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } else { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_IDLE); + } + } + ftdm_mutex_unlock(ftdmchan->mutex); + } +} + +/** + * \brief Handler for call start ack event + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_start_ack(sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + + ftdm_channel_t *ftdmchan = NULL; + uint32_t event_span = event->span+1; + uint32_t event_chan = event->chan+1; + + if (nack_map[event->call_setup_id]) { + return; + } + + if (mcon->sigmod) { + event_span = event->span; + event_chan = event->chan; + } + + OUTBOUND_REQUESTS[event->call_setup_id].event = *event; + SETUP_GRID[event->span][event->chan] = event->call_setup_id; + + if (mcon->sigmod) { + ftdmchan = OUTBOUND_REQUESTS[event->call_setup_id].ftdmchan; + } else { + ftdmchan = find_ftdmchan(OUTBOUND_REQUESTS[event->call_setup_id].span, event, 0); + } + + + if (ftdmchan) { + ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to open FTDM channel [%s]\n", ftdmchan->last_error); + } else { + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INUSE); + ftdmchan->sflags = SFLAG_RECVD_ACK; + + if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { + if (sangoma_boost_data->sigmod) { + ftdm_log(FTDM_LOG_DEBUG, "Channel state changing to PROGRESS_MEDIA [Csid:%d]\n", event->call_setup_id); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); + } else { + ftdmchan->init_state = FTDM_CHANNEL_STATE_PROGRESS_MEDIA; + ftdm_log(FTDM_LOG_DEBUG, "Channel init state changed to PROGRESS_MEDIA [Csid:%d]\n", event->call_setup_id); + } + } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { + if (sangoma_boost_data->sigmod) { + ftdm_log(FTDM_LOG_DEBUG, "Channel state changing to PROGRESS [Csid:%d]\n", event->call_setup_id); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); + } else { + ftdmchan->init_state = FTDM_CHANNEL_STATE_PROGRESS; + ftdm_log(FTDM_LOG_DEBUG, "Channel init state changed to PROGRESS [Csid:%d]\n", event->call_setup_id); + } + } else { + if (sangoma_boost_data->sigmod) { + /* should we set a state here? */ + } else { + ftdmchan->init_state = FTDM_CHANNEL_STATE_IDLE; + ftdm_log(FTDM_LOG_DEBUG, "Channel init state changed to IDLE [Csid:%d]\n", event->call_setup_id); + } + } + if (!sangoma_boost_data->sigmod) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HOLD); + ftdm_log(FTDM_LOG_DEBUG, "Assigned chan %d:%d (%d:%d) to CSid=%d\n", + ftdmchan->span_id, ftdmchan->chan_id, event_span, event_chan, event->call_setup_id); + OUTBOUND_REQUESTS[event->call_setup_id].ftdmchan = ftdmchan; + } + OUTBOUND_REQUESTS[event->call_setup_id].flags = event->flags; + OUTBOUND_REQUESTS[event->call_setup_id].status = BST_READY; + return; + } + } + + if (!ftdmchan) { + ftdm_log(FTDM_LOG_CRIT, "START ACK CANT FIND A CHAN %d:%d\n", event->span+1,event->chan+1); + } else { + /* only reason to be here is failed to open channel when we we're in sigmod */ + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); + } + ftdm_set_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG); + sangomabc_exec_command(mcon, + event->span, + event->chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_STOPPED, + FTDM_CAUSE_DESTINATION_OUT_OF_ORDER, 0); + OUTBOUND_REQUESTS[event->call_setup_id].status = BST_FAIL; + OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER; +} + +/** + * \brief Handler for call done event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_done(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_channel_t *ftdmchan; + int r = 0; + + if ((ftdmchan = find_ftdmchan(span, event, 1))) { + ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; + ftdm_mutex_lock(ftdmchan->mutex); + + if (sangoma_boost_data->sigmod) { + /* not really completely done, but if we ever get an incoming call before moving to HANGUP_COMPLETE + * handle_incoming_call() will take care of moving the state machine to release the channel */ + sangomabc_exec_command(&sangoma_boost_data->mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_RELEASED, + 0, 0); + } + + if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN || ftdmchan->state == FTDM_CHANNEL_STATE_HANGUP_COMPLETE || ftdm_test_sflag(ftdmchan, SFLAG_TERMINATING)) { + goto done; + } + + ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE, 0, r); + if (r) { + ftdm_set_sflag(ftdmchan, SFLAG_FREE_REQ_ID); + ftdm_mutex_unlock(ftdmchan->mutex); + return; + } + } + + done: + + if (ftdmchan) { + ftdm_mutex_unlock(ftdmchan->mutex); + } + + if (event->call_setup_id) { + release_request_id(event->call_setup_id); + } else { + release_request_id_span_chan(event->span, event->chan); + } +} + +/** + * \brief Handler for call start nack event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_start_nack(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_channel_t *ftdmchan; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + uint32_t count = 0; + int delay = 0; + int tg=event->trunk_group; + + ftdm_span_channel_use_count(span, &count); + + delay = (int) (count / 100) * 2; + + if (delay > 10) { + delay = 10; + } else if (delay < 1) { + delay = 1; + } + + if (tg < 0 || tg >= MAX_TRUNK_GROUPS) { + ftdm_log(FTDM_LOG_CRIT, "Invalid All Ckt Busy trunk group number %i\n", tg); + tg=0; + } + + congestion_timeouts[tg] = time(NULL) + delay; + event->release_cause = 17; + + } else if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { + event->release_cause = 17; + } + + if (event->call_setup_id) { + if (sangoma_boost_data->sigmod) { + ftdmchan = OUTBOUND_REQUESTS[event->call_setup_id].ftdmchan; + ftdmchan->call_data = (void*)(intptr_t)event->event_id; + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); + } else { + sangomabc_exec_command(mcon, + 0, + 0, + event->call_setup_id, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0, 0); + OUTBOUND_REQUESTS[event->call_setup_id].event = *event; + OUTBOUND_REQUESTS[event->call_setup_id].status = BST_FAIL; + OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = event->release_cause; + ftdm_log(FTDM_LOG_DEBUG, "setting outbound request status %d to BST_FAIL\n", event->call_setup_id); + } + return; + } else { + if ((ftdmchan = find_ftdmchan(span, event, 1))) { + int r = 0; + + /* if there is no call setup id this should not be an outbound channel for sure */ + ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND), "Yay, outbound flag should not be set here!\n"); + + ftdmchan->call_data = (void*)(intptr_t)event->event_id; + ftdm_mutex_lock(ftdmchan->mutex); + ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING, 0, r); + if (r == FTDM_STATE_CHANGE_SUCCESS) { + ftdmchan->caller_data.hangup_cause = event->release_cause; + } + ftdm_mutex_unlock(ftdmchan->mutex); + if (r) { + return; + } + } + } + + if (ftdmchan) { + ftdm_set_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); + } + + /* nobody else will do it so we have to do it ourselves */ + sangomabc_exec_command(mcon, + event->span, + event->chan, + 0, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0, 0); +} + +static void handle_call_released(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_channel_t *ftdmchan; + + if ((ftdmchan = find_ftdmchan(span, event, 1))) { + ftdm_log(FTDM_LOG_DEBUG, "Releasing completely chan s%dc%d\n", event->span, event->chan); + ftdm_channel_done(ftdmchan); + } else { + ftdm_log(FTDM_LOG_ERROR, "Odd, We could not find chan: s%dc%d to release the call completely!!\n", event->span, event->chan); + } +} + +/** + * \brief Handler for call stop event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_stop(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_channel_t *ftdmchan; + + if ((ftdmchan = find_ftdmchan(span, event, 1))) { + int r = 0; + + ftdm_mutex_lock(ftdmchan->mutex); + + if (ftdm_test_sflag(ftdmchan, SFLAG_HANGUP)) { + /* racing condition where both sides initiated a hangup + * Do not change current state as channel is already clearing + * itself through local initiated hangup */ + + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0, 0); + ftdm_mutex_unlock(ftdmchan->mutex); + return; + } else if (ftdmchan->state == FTDM_CHANNEL_STATE_HOLD) { + ftdmchan->init_state = FTDM_CHANNEL_STATE_TERMINATING; + ftdm_log(FTDM_LOG_DEBUG, "Channel init state updated to TERMINATING [Csid:%d]\n", event->call_setup_id); + OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = event->release_cause; + ftdmchan->caller_data.hangup_cause = event->release_cause; + ftdm_mutex_unlock(ftdmchan->mutex); + return; + } else { + ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING, 0, r); + } + + if (r == FTDM_STATE_CHANGE_SUCCESS) { + ftdmchan->caller_data.hangup_cause = event->release_cause; + } + + if (r) { + ftdm_set_sflag(ftdmchan, SFLAG_FREE_REQ_ID); + } + + ftdm_mutex_unlock(ftdmchan->mutex); + + if (r) { + return; + } + } else { /* we have to do it ourselves.... */ + ftdm_log(FTDM_LOG_ERROR, "Odd, We could not find chan: s%dc%d\n", event->span, event->chan); + release_request_id_span_chan(event->span, event->chan); + } +} + +/** + * \brief Handler for call answer event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_answer(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_channel_t *ftdmchan; + + if ((ftdmchan = find_ftdmchan(span, event, 1))) { + ftdm_mutex_lock(ftdmchan->mutex); + if (ftdmchan->state == FTDM_CHANNEL_STATE_HOLD) { + ftdmchan->init_state = FTDM_CHANNEL_STATE_UP; + } else { + int r = 0; + ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_UP, 0, r); + } + ftdm_mutex_unlock(ftdmchan->mutex); + } else { + ftdm_log(FTDM_LOG_CRIT, "ANSWER CANT FIND A CHAN %d:%d\n", event->span+1,event->chan+1); + sangomabc_exec_command(mcon, + event->span, + event->chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_STOPPED, + FTDM_CAUSE_DESTINATION_OUT_OF_ORDER, 0); + } +} + +static __inline__ void advance_chan_states(ftdm_channel_t *ftdmchan); + +/** + * \brief Handler for call start event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_start(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_event_t *event) +{ + ftdm_channel_t *ftdmchan; + + if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 0))) { + if ((ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 1))) { + int r; + if (ftdmchan->state == FTDM_CHANNEL_STATE_UP) { + ftdm_log(FTDM_LOG_CRIT, "ZCHAN STATE UP -> Changed to TERMINATING %d:%d\n", event->span+1,event->chan+1); + ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING, 0, r); + } else if (ftdm_test_sflag(ftdmchan, SFLAG_HANGUP)) { + ftdm_log(FTDM_LOG_CRIT, "ZCHAN STATE HANGUP -> Changed to HANGUP COMPLETE %d:%d\n", event->span+1,event->chan+1); + ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE, 0, r); + } else { + ftdm_log(FTDM_LOG_CRIT, "ZCHAN STATE INVALID %s on IN CALL %d:%d\n", ftdm_channel_state2str(ftdmchan->state),event->span+1,event->chan+1); + + } + ftdm_set_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG); + ftdmchan=NULL; + } + ftdm_log(FTDM_LOG_CRIT, "START CANT FIND CHAN %d:%d\n", event->span+1,event->chan+1); + goto error; + } + + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_CRIT, "START CANT OPEN CHAN %d:%d\n", event->span+1,event->chan+1); + goto error; + } + + ftdm_log(FTDM_LOG_DEBUG, "Got call start from s%dc%d mapped to freetdm logical s%dc%d, physical s%dc%d\n", + event->span, event->chan, + ftdmchan->span_id, ftdmchan->chan_id, + ftdmchan->physical_span_id, ftdmchan->physical_chan_id); + + ftdmchan->sflags = 0; + ftdm_set_string(ftdmchan->caller_data.cid_num.digits, (char *)event->calling.digits); + ftdm_set_string(ftdmchan->caller_data.cid_name, (char *)event->calling.digits); + ftdm_set_string(ftdmchan->caller_data.ani.digits, (char *)event->calling.digits); + ftdm_set_string(ftdmchan->caller_data.dnis.digits, (char *)event->called.digits); + ftdm_set_string(ftdmchan->caller_data.rdnis.digits, (char *)event->rdnis.digits); + + if (strlen(event->calling_name)) { + ftdm_set_string(ftdmchan->caller_data.cid_name, (char *)event->calling_name); + } + + ftdmchan->caller_data.cid_num.plan = event->calling.npi; + ftdmchan->caller_data.cid_num.type = event->calling.ton; + + ftdmchan->caller_data.ani.plan = event->calling.npi; + ftdmchan->caller_data.ani.type = event->calling.ton; + + ftdmchan->caller_data.dnis.plan = event->called.npi; + ftdmchan->caller_data.dnis.type = event->called.ton; + + ftdmchan->caller_data.rdnis.plan = event->rdnis.npi; + ftdmchan->caller_data.rdnis.type = event->rdnis.ton; + + ftdmchan->caller_data.screen = event->calling.screening_ind; + ftdmchan->caller_data.pres = event->calling.presentation_ind; + + if (event->custom_data_size) { + char* p = NULL; + + p = strstr((char*)event->custom_data,"PRI001-ANI2-"); + if (p!=NULL) { + int ani2 = 0; + sscanf(p, "PRI001-ANI2-%d", &ani2); + snprintf(ftdmchan->caller_data.aniII, 5, "%.2d", ani2); + } + } + + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING); + return; + + error: + sangomabc_exec_command(mcon, + event->span, + event->chan, + 0, + SIGBOOST_EVENT_CALL_START_NACK, + 0, 0); + +} + +static void handle_call_loop_start(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_status_t res = FTDM_FAIL; + ftdm_channel_t *ftdmchan; + + if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 0))) { + ftdm_log(FTDM_LOG_CRIT, "CANNOT START LOOP, CHAN NOT AVAILABLE %d:%d\n", event->span+1,event->chan+1); + return; + } + + if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_CRIT, "CANNOT START LOOP, CANT OPEN CHAN %d:%d\n", event->span+1,event->chan+1); + return; + } + + ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_IN_LOOP, 0, res); + if (res != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_CRIT, "yay, could not set the state of the channel to IN_LOOP, loop will fail\n"); + ftdm_channel_done(ftdmchan); + return; + } + ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_LOOP, NULL); +} + +static void handle_call_loop_stop(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_channel_t *ftdmchan; + ftdm_status_t res = FTDM_FAIL; + if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 1))) { + ftdm_log(FTDM_LOG_CRIT, "CANNOT STOP LOOP, INVALID CHAN REQUESTED %d:%d\n", event->span+1,event->chan+1); + return; + } + if (ftdmchan->state != FTDM_CHANNEL_STATE_IN_LOOP) { + ftdm_log(FTDM_LOG_ERROR, "Got stop loop request in a channel that is not in loop, ignoring ...\n"); + return; + } + ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_LOOP, NULL); + /* even when we did not sent a msg we set this flag to avoid sending call stop in the DOWN state handler */ + ftdm_set_flag(ftdmchan, SFLAG_SENT_FINAL_MSG); + ftdm_set_state_r(ftdmchan, FTDM_CHANNEL_STATE_DOWN, 0, res); +} + +/** + * \brief Handler for heartbeat event + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_heartbeat(sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + int err; + + err = sangomabc_connection_writep(mcon, (sangomabc_event_t*)event); + + if (err <= 0) { + ftdm_log(FTDM_LOG_CRIT, "Failed to tx on boost connection [%s]: %s\n", strerror(errno)); + } + return; +} + +/** + * \brief Handler for restart ack event + * \param mcon sangoma boost connection + * \param span Span where event was fired + * \param event Event to handle + */ +static void handle_restart_ack(sangomabc_connection_t *mcon, ftdm_span_t *span, sangomabc_short_event_t *event) +{ + ftdm_log(FTDM_LOG_DEBUG, "RECV RESTART ACK\n"); +} + +/** + * \brief Handler for restart event + * \param mcon sangoma boost connection + * \param span Span where event was fired + * \param event Event to handle + */ +static void handle_restart(sangomabc_connection_t *mcon, ftdm_span_t *span, sangomabc_short_event_t *event) +{ + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + + mcon->rxseq_reset = 0; + ftdm_set_flag((&sangoma_boost_data->mcon), MSU_FLAG_DOWN); + ftdm_set_flag_locked(span, FTDM_SPAN_SUSPENDED); + ftdm_set_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RESTARTING); + +} + +/** + * \brief Handler for incoming digit event + * \param mcon sangoma boost connection + * \param span Span where event was fired + * \param event Event to handle + */ +static void handle_incoming_digit(sangomabc_connection_t *mcon, ftdm_span_t *span, sangomabc_event_t *event) +{ + ftdm_channel_t *ftdmchan = NULL; + char digits[MAX_DIALED_DIGITS + 2] = ""; + + if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t *)event, 1))) { + ftdm_log(FTDM_LOG_ERROR, "Invalid channel\n"); + return; + } + + if (event->called_number_digits_count == 0) { + ftdm_log(FTDM_LOG_WARNING, "Error Incoming digit with len %s %d [w%dg%d]\n", + event->called_number_digits, + event->called_number_digits_count, + event->span+1, event->chan+1); + return; + } + + ftdm_log(FTDM_LOG_WARNING, "Incoming digit with len %s %d [w%dg%d]\n", + event->called_number_digits, + event->called_number_digits_count, + event->span+1, event->chan+1); + + memcpy(digits, event->called_number_digits, event->called_number_digits_count); + ftdm_channel_queue_dtmf(ftdmchan, digits); + + return; +} + + +/** + * \brief Checks if span has state changes pending and processes + * \param span Span where event was fired + * \param event Event to handle + * \return The locked FTDM channel associated to the event if any, NULL otherwise + */ +static ftdm_channel_t* event_process_states(ftdm_span_t *span, sangomabc_short_event_t *event) +{ + ftdm_channel_t *ftdmchan = NULL; + + switch (event->event_id) { + case SIGBOOST_EVENT_CALL_START_NACK: + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + if (event->call_setup_id) { + return NULL; + } + //if event->span and event->chan is valid, fall-through + case SIGBOOST_EVENT_CALL_START: + case SIGBOOST_EVENT_CALL_START_ACK: + case SIGBOOST_EVENT_CALL_STOPPED: + case SIGBOOST_EVENT_CALL_PROGRESS: + case SIGBOOST_EVENT_CALL_ANSWERED: + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + case SIGBOOST_EVENT_DIGIT_IN: + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + case SIGBOOST_EVENT_CALL_RELEASED: + if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 1))) { + ftdm_log(FTDM_LOG_DEBUG, "PROCESS STATES CANT FIND CHAN %d:%d\n", event->span+1,event->chan+1); + return NULL; + } + break; + case SIGBOOST_EVENT_HEARTBEAT: + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + case SIGBOOST_EVENT_SYSTEM_RESTART: + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + return NULL; + default: + ftdm_log(FTDM_LOG_CRIT, "Unhandled event id:%d\n", event->event_id); + return NULL; + } + + ftdm_mutex_lock(ftdmchan->mutex); + advance_chan_states(ftdmchan); + return ftdmchan; +} + +/** + * \brief Handler for sangoma boost event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static int parse_sangoma_event(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + ftdm_channel_t* ftdmchan = NULL; + + if (!ftdm_running()) { + ftdm_log(FTDM_LOG_WARNING, "System is shutting down.\n"); + return -1; + } + + ftdm_assert_return(event->call_setup_id <= MAX_REQ_ID, -1, "Unexpected call setup id\n"); + + /* process all pending state changes for that channel before + * processing the new boost event */ + ftdmchan = event_process_states(span, event); + + switch(event->event_id) { + case SIGBOOST_EVENT_CALL_START: + handle_call_start(span, mcon, (sangomabc_event_t*)event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + handle_call_stop(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_RELEASED: + handle_call_released(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + handle_call_start_ack(mcon, event); + break; + case SIGBOOST_EVENT_CALL_PROGRESS: + handle_call_progress(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + handle_call_start_nack(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + handle_call_answer(span, mcon, event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + handle_heartbeat(mcon, event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + handle_call_done(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + handle_call_done(span, mcon, event); + nack_map[event->call_setup_id] = 0; + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + handle_call_loop_start(span, mcon, event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + handle_call_loop_stop(span, mcon, event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + handle_restart_ack(mcon, span, event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART: + handle_restart(mcon, span, event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + //handle_gap_abate(event); + break; + case SIGBOOST_EVENT_DIGIT_IN: + handle_incoming_digit(mcon, span, (sangomabc_event_t*)event); + break; + default: + ftdm_log(FTDM_LOG_WARNING, "No handler implemented for [%s]\n", sangomabc_event_id_name(event->event_id)); + break; + } + + if(ftdmchan != NULL) { + advance_chan_states(ftdmchan); + ftdm_mutex_unlock(ftdmchan->mutex); + } + + return 0; + +} + +/** + * \brief Handler for channel state change + * \param ftdmchan Channel to handle + */ +static __inline__ void state_advance(ftdm_channel_t *ftdmchan) +{ + + ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; + sangomabc_connection_t *mcon = &sangoma_boost_data->mcon; + ftdm_sigmsg_t sig; + ftdm_status_t status; + + ftdm_log(FTDM_LOG_DEBUG, "%d:%d PROCESSING STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state)); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + + switch (ftdmchan->state) { + case FTDM_CHANNEL_STATE_DOWN: + { + int call_stopped_ack_sent = 0; + ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; + + if (ftdm_test_sflag(ftdmchan, SFLAG_FREE_REQ_ID)) { + release_request_id_span_chan(ftdmchan->physical_span_id-1, ftdmchan->physical_chan_id-1); + } + + if (!ftdm_test_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG)) { + ftdm_set_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); + + if (ftdmchan->call_data && ((uint32_t)(intptr_t)ftdmchan->call_data == SIGBOOST_EVENT_CALL_START_NACK)) { + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0, 0); + + } else { + /* we got a call stop msg, time to reply with call stopped ack */ + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0, 0); + call_stopped_ack_sent = 1; + } + } + ftdmchan->sflags = 0; + ftdmchan->call_data = NULL; + if (sangoma_boost_data->sigmod && call_stopped_ack_sent) { + /* we dont want to call ftdm_channel_done just yet until call released is received */ + ftdm_log(FTDM_LOG_DEBUG, "Waiting for call release confirmation before declaring chan %d:%d as available \n", + ftdmchan->span_id, ftdmchan->chan_id); + } else { + ftdm_channel_done(ftdmchan); + } + } + break; + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + if (!ftdm_test_sflag(ftdmchan, SFLAG_SENT_ACK)) { + ftdm_set_sflag(ftdmchan, SFLAG_SENT_ACK); + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0, SIGBOOST_PROGRESS_MEDIA); + } + } + } + break; + case FTDM_CHANNEL_STATE_PROGRESS: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_PROGRESS; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + if (!ftdm_test_sflag(ftdmchan, SFLAG_SENT_ACK)) { + ftdm_set_sflag(ftdmchan, SFLAG_SENT_ACK); + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0, SIGBOOST_PROGRESS_RING); + } + } + } + break; + case FTDM_CHANNEL_STATE_IDLE: + case FTDM_CHANNEL_STATE_HOLD: + { + /* twiddle */ + } + break; + case FTDM_CHANNEL_STATE_RING: + { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_START; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } + } + break; + case FTDM_CHANNEL_STATE_RESTART: + { + sig.event_id = FTDM_SIGEVENT_RESTART; + status = ftdm_span_send_signal(ftdmchan->span, &sig); + ftdm_set_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + break; + case FTDM_CHANNEL_STATE_UP: + { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + sig.event_id = FTDM_SIGEVENT_UP; + if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } + } else { + if (!(ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA))) { + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0, 0); + } + + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_ANSWERED, + 0, 0); + } + } + break; + case FTDM_CHANNEL_STATE_DIALING: + { + } + break; + case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: + { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + } + break; + case FTDM_CHANNEL_STATE_HANGUP: + { + ftdm_set_sflag_locked(ftdmchan, SFLAG_HANGUP); + + if (ftdm_test_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG) || ftdm_test_sflag(ftdmchan, SFLAG_TERMINATING)) { + ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + } else { + ftdm_set_sflag_locked(ftdmchan, SFLAG_SENT_FINAL_MSG); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_ANSWERED) || + ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || + ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA) || + ftdm_test_sflag(ftdmchan, SFLAG_RECVD_ACK)) { + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_STOPPED, + ftdmchan->caller_data.hangup_cause, 0); + } else { + sangomabc_exec_command(mcon, + BOOST_SPAN(ftdmchan), + BOOST_CHAN(ftdmchan), + 0, + SIGBOOST_EVENT_CALL_START_NACK, + ftdmchan->caller_data.hangup_cause, 0); + } + } + } + break; + case FTDM_CHANNEL_STATE_TERMINATING: + { + ftdm_set_sflag_locked(ftdmchan, SFLAG_TERMINATING); + sig.event_id = FTDM_SIGEVENT_STOP; + status = ftdm_span_send_signal(ftdmchan->span, &sig); + } + break; + case FTDM_CHANNEL_STATE_IN_LOOP: + { + /* nothing to do, we sent the FTDM_COMMAND_ENABLE_LOOP command in handle_call_loop_start() right away */ + } + break; + default: + break; + } +} + +static __inline__ void advance_chan_states(ftdm_channel_t *ftdmchan) +{ + while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + state_advance(ftdmchan); + ftdm_channel_complete_state(ftdmchan); + } +} + +/** + * \brief Initialises outgoing requests array + */ +static __inline__ void init_outgoing_array(void) +{ + memset(&OUTBOUND_REQUESTS, 0, sizeof(OUTBOUND_REQUESTS)); + +} + +/** + * \brief Checks current state on a span + * \param span Span to check status on + */ +static __inline__ void check_state(ftdm_span_t *span) +{ + ftdm_channel_t *ftdmchan = NULL; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + int susp = ftdm_test_flag(span, FTDM_SPAN_SUSPENDED); + + if (susp && ftdm_check_state_all(span, FTDM_CHANNEL_STATE_DOWN)) { + susp = 0; + } + + if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE) || susp) { + uint32_t j; + ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE); + if (susp) { + for (j = 0; j <= span->chan_count; j++) { + ftdm_mutex_lock(span->channels[j]->mutex); + ftdm_clear_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE); + ftdm_channel_set_state(span->channels[j], FTDM_CHANNEL_STATE_RESTART, 0); + state_advance(span->channels[j]); + ftdm_channel_complete_state(span->channels[j]); + ftdm_mutex_unlock(span->channels[j]->mutex); + } + } else { + while ((ftdmchan = ftdm_queue_dequeue(span->pendingchans))) { + /* it can happen that someone else processed the chan states + * but without taking the chan out of the queue, so check th + * flag before advancing the state */ + ftdm_mutex_lock(ftdmchan->mutex); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + state_advance(ftdmchan); + ftdm_channel_complete_state(ftdmchan); + } + ftdm_mutex_unlock(ftdmchan->mutex); + } + } + } + + if (ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RESTARTING)) { + if (ftdm_check_state_all(span, FTDM_CHANNEL_STATE_DOWN)) { + sangomabc_exec_command(&sangoma_boost_data->mcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART_ACK, + 0, 0); + ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RESTARTING); + ftdm_clear_flag_locked(span, FTDM_SPAN_SUSPENDED); + ftdm_clear_flag((&sangoma_boost_data->mcon), MSU_FLAG_DOWN); + init_outgoing_array(); + } + } +} + + +/** + * \brief Checks for events on a span + * \param span Span to check for events + */ +static __inline__ ftdm_status_t check_events(ftdm_span_t *span, int ms_timeout) +{ + ftdm_status_t status; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + + status = ftdm_span_poll_event(span, ms_timeout); + + switch(status) { + case FTDM_SUCCESS: + { + ftdm_event_t *event; + while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) { + switch (event->enum_id) { + case FTDM_OOB_ALARM_TRAP: + if (sangoma_boost_data->sigmod) { + sangoma_boost_data->sigmod->on_hw_link_status_change(event->channel, FTDM_HW_LINK_DISCONNECTED); + } + break; + case FTDM_OOB_ALARM_CLEAR: + if (sangoma_boost_data->sigmod) { + sangoma_boost_data->sigmod->on_hw_link_status_change(event->channel, FTDM_HW_LINK_CONNECTED); + } + break; + } + } + } + break; + case FTDM_FAIL: + { + if (!ftdm_running()) { + break; + } + ftdm_log(FTDM_LOG_ERROR, "Boost Check Event Failure Failure: %s\n", span->last_error); + return FTDM_FAIL; + } + break; + default: + break; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Main thread function for sangoma boost span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *ftdm_sangoma_events_run(ftdm_thread_t *me, void *obj) +{ + ftdm_span_t *span = (ftdm_span_t *) obj; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + unsigned errs = 0; + + while (ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING) && ftdm_running()) { + if (check_events(span,100) != FTDM_SUCCESS) { + if (errs++ > 50) { + ftdm_log(FTDM_LOG_ERROR, "Too many event errors, quitting sangoma events thread\n"); + return NULL; + } + } + } + + return NULL; +} + +static ftdm_status_t ftdm_boost_connection_open(ftdm_span_t *span) +{ + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + if (sangoma_boost_data->sigmod) { + if (sangoma_boost_data->sigmod->start_span(span) != FTDM_SUCCESS) { + return FTDM_FAIL; + } + ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RESTARTING); + ftdm_clear_flag_locked(span, FTDM_SPAN_SUSPENDED); + ftdm_clear_flag((&sangoma_boost_data->mcon), MSU_FLAG_DOWN); + } + + sangoma_boost_data->pcon = sangoma_boost_data->mcon; + + /* when sigmod is present, all arguments: local_ip etc, are ignored by sangomabc_connection_open */ + if (sangomabc_connection_open(&sangoma_boost_data->mcon, + sangoma_boost_data->mcon.cfg.local_ip, + sangoma_boost_data->mcon.cfg.local_port, + sangoma_boost_data->mcon.cfg.remote_ip, + sangoma_boost_data->mcon.cfg.remote_port) < 0) { + ftdm_log(FTDM_LOG_ERROR, "Error: Opening MCON Socket [%d] %s\n", sangoma_boost_data->mcon.socket, strerror(errno)); + return FTDM_FAIL; + } + + if (sangomabc_connection_open(&sangoma_boost_data->pcon, + sangoma_boost_data->pcon.cfg.local_ip, + ++sangoma_boost_data->pcon.cfg.local_port, + sangoma_boost_data->pcon.cfg.remote_ip, + ++sangoma_boost_data->pcon.cfg.remote_port) < 0) { + ftdm_log(FTDM_LOG_ERROR, "Error: Opening PCON Socket [%d] %s\n", sangoma_boost_data->pcon.socket, strerror(errno)); + return FTDM_FAIL; + } + + /* try to create the boost sockets interrupt objects */ + if (ftdm_interrupt_create(&sangoma_boost_data->pcon.sock_interrupt, sangoma_boost_data->pcon.socket) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Span %s could not create its boost msock interrupt!\n", span->name); + return FTDM_FAIL; + } + + if (ftdm_interrupt_create(&sangoma_boost_data->mcon.sock_interrupt, sangoma_boost_data->mcon.socket) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Span %s could not create its boost psock interrupt!\n", span->name); + return FTDM_FAIL; + } + + return FTDM_SUCCESS; +} + +/*! + \brief wait for a boost event + \return -1 on error, 0 on timeout, 1 when there are events + */ +static int ftdm_boost_wait_event(ftdm_span_t *span) +{ + ftdm_status_t res; + ftdm_interrupt_t *ints[3]; + int numints; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + + ftdm_queue_get_interrupt(span->pendingchans, &ints[0]); + numints = 1; + /* if in queue mode wait for both the pendingchans queue and the boost msg queue */ + if (sangoma_boost_data->sigmod) { + ftdm_queue_get_interrupt(sangoma_boost_data->boost_queue, &ints[1]); + numints = 2; + } +#ifndef __WINDOWS__ + else { + /* socket mode ... */ + ints[1] = sangoma_boost_data->mcon.sock_interrupt; + ints[2] = sangoma_boost_data->pcon.sock_interrupt; + numints = 3; + sangoma_boost_data->iteration = 0; + } +#endif + res = ftdm_interrupt_multiple_wait(ints, numints, -1); + if (FTDM_SUCCESS != res) { + ftdm_log(FTDM_LOG_CRIT, "Unexpected return value from interrupt waiting: %d\n", res); + return -1; + } + return 0; +} + + +static sangomabc_event_t *ftdm_boost_read_event(ftdm_span_t *span) +{ + sangomabc_event_t *event = NULL; + sangomabc_connection_t *mcon, *pcon; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + + mcon = &sangoma_boost_data->mcon; + pcon = &sangoma_boost_data->pcon; + + event = sangomabc_connection_readp(pcon, sangoma_boost_data->iteration); + + /* if there is no event and this is not a sigmod-driven span it's time to try the other connection for events */ + if (!event && !sangoma_boost_data->sigmod) { + event = sangomabc_connection_read(mcon, sangoma_boost_data->iteration); + } + + return event; +} + +/** + * \brief Main thread function for sangoma boost span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *ftdm_sangoma_boost_run(ftdm_thread_t *me, void *obj) +{ + ftdm_span_t *span = (ftdm_span_t *) obj; + sangomabc_connection_t *mcon, *pcon; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + + mcon = &sangoma_boost_data->mcon; + pcon = &sangoma_boost_data->pcon; + + /* sigmod overrides socket functionality if not null */ + if (sangoma_boost_data->sigmod) { + mcon->span = span; + pcon->span = span; + /* everything could be retrieved through span, but let's use shortcuts */ + mcon->sigmod = sangoma_boost_data->sigmod; + pcon->sigmod = sangoma_boost_data->sigmod; + mcon->boost_queue = sangoma_boost_data->boost_queue; + pcon->boost_queue = sangoma_boost_data->boost_queue; + } + + if (ftdm_boost_connection_open(span) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "ftdm_boost_connection_open failed\n"); + goto end; + } + + init_outgoing_array(); + if (!sangoma_boost_data->sigmod) { + sangomabc_exec_commandp(pcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + ftdm_set_flag(mcon, MSU_FLAG_DOWN); + } + + while (ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING)) { + sangomabc_event_t *event = NULL; + + if (!ftdm_running()) { + if (!sangoma_boost_data->sigmod) { + sangomabc_exec_commandp(pcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + ftdm_set_flag(mcon, MSU_FLAG_DOWN); + } + ftdm_log(FTDM_LOG_DEBUG, "ftdm is no longer running\n"); + break; + } + + if (ftdm_boost_wait_event(span) < 0) { + ftdm_log(FTDM_LOG_ERROR, "ftdm_boost_wait_event failed\n"); + goto error; + } + + while ((event = ftdm_boost_read_event(span))) { + parse_sangoma_event(span, pcon, (sangomabc_short_event_t*)event); + sangoma_boost_data->iteration++; + } + + check_state(span); + } + + goto end; + +error: + ftdm_log(FTDM_LOG_CRIT, "Boost event processing Error!\n"); + +end: + if (!sangoma_boost_data->sigmod) { + sangomabc_connection_close(&sangoma_boost_data->mcon); + sangomabc_connection_close(&sangoma_boost_data->pcon); + } + + ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING); + + ftdm_log(FTDM_LOG_DEBUG, "Sangoma Boost thread ended.\n"); + return NULL; +} + +/** + * \brief Loads sangoma boost signaling module + * \param fio FreeTDM IO interface + * \return Success + */ +static FIO_SIG_LOAD_FUNCTION(ftdm_sangoma_boost_init) +{ + g_boost_modules_hash = create_hashtable(10, ftdm_hash_hashfromstring, ftdm_hash_equalkeys); + if (!g_boost_modules_hash) { + return FTDM_FAIL; + } + ftdm_mutex_create(&request_mutex); + ftdm_mutex_create(&g_boost_modules_mutex); + memset(&g_trunkgroups[0], 0, sizeof(g_trunkgroups)); + return FTDM_SUCCESS; +} + +static FIO_SIG_UNLOAD_FUNCTION(ftdm_sangoma_boost_destroy) +{ + ftdm_hash_iterator_t *i = NULL; + boost_sigmod_interface_t *sigmod = NULL; + const void *key = NULL; + void *val = NULL; + ftdm_dso_lib_t lib; + + for (i = hashtable_first(g_boost_modules_hash); i; i = hashtable_next(i)) { + hashtable_this(i, &key, NULL, &val); + if (key && val) { + sigmod = val; + lib = sigmod->pvt; + ftdm_dso_destroy(&lib); + } + } + + hashtable_destroy(g_boost_modules_hash); + ftdm_mutex_destroy(&request_mutex); + ftdm_mutex_destroy(&g_boost_modules_mutex); + return FTDM_SUCCESS; +} + +static ftdm_status_t ftdm_sangoma_boost_start(ftdm_span_t *span) +{ + int err; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + ftdm_set_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING); + err=ftdm_thread_create_detached(ftdm_sangoma_boost_run, span); + if (err) { + ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING); + return err; + } + // launch the events thread to handle HW DTMF and possibly + // other events in the future + err=ftdm_thread_create_detached(ftdm_sangoma_events_run, span); + if (err) { + ftdm_clear_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING); + } + return err; +} + +static ftdm_status_t ftdm_sangoma_boost_stop(ftdm_span_t *span) +{ + int cnt = 10; + ftdm_status_t status = FTDM_SUCCESS; + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + if (sangoma_boost_data->sigmod) { + + /* FIXME: we should make sure the span thread is stopped (use pthread_kill or freetdm thread kill function) */ + /* I think stopping the span before destroying the queue makes sense + otherwise may be boost events would still arrive when the queue is already destroyed! */ + status = sangoma_boost_data->sigmod->stop_span(span); + + ftdm_queue_enqueue(sangoma_boost_data->boost_queue, NULL); + while(ftdm_test_flag(sangoma_boost_data, FTDM_SANGOMA_BOOST_RUNNING) && cnt-- > 0) { + ftdm_log(FTDM_LOG_DEBUG, "Waiting for boost thread\n"); + ftdm_sleep(500); + } + ftdm_queue_destroy(&sangoma_boost_data->boost_queue); + return status; + } + return status; +} + +static ftdm_state_map_t boost_state_map = { + { + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_ANY_STATE}, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_DIALING, FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_HOLD, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HOLD, FTDM_END}, + {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, + FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_UP, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_DIALING, FTDM_END}, + {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_DIALING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_UP, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_HANGUP, FTDM_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_UP, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END} + }, + + /****************************************/ + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_ANY_STATE}, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RESTART, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DOWN}, + {FTDM_CHANNEL_STATE_IN_LOOP, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_IN_LOOP}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + {FTDM_CHANNEL_STATE_RING, FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_RING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA,FTDM_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_HANGUP, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_DOWN, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_UP, FTDM_END}, + {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + }, + + + } +}; + +static BOOST_WRITE_MSG_FUNCTION(ftdm_boost_write_msg) +{ + sangomabc_short_event_t *shortmsg = NULL; + ftdm_sangoma_boost_data_t *sangoma_boost_data = NULL; + sangomabc_queue_element_t *element = NULL; + + ftdm_assert_return(msg != NULL, FTDM_FAIL, "Boost message to write was null"); + + if (!span) { + shortmsg = msg; + ftdm_log(FTDM_LOG_ERROR, "Unexpected boost message %d\n", shortmsg->event_id); + return FTDM_FAIL; + } + /* duplicate the event and enqueue it */ + element = ftdm_calloc(1, sizeof(*element)); + if (!element) { + return FTDM_FAIL; + } + memcpy(element->boostmsg, msg, msglen); + element->size = msglen; + + sangoma_boost_data = span->signal_data; + return ftdm_queue_enqueue(sangoma_boost_data->boost_queue, element); +} + +static BOOST_SIG_STATUS_CB_FUNCTION(ftdm_boost_sig_status_change) +{ + ftdm_sigmsg_t sig; + ftdm_log(FTDM_LOG_NOTICE, "%d:%d Signaling link status changed to %s\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_signaling_status2str(status)); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = ftdmchan->chan_id; + sig.span_id = ftdmchan->span_id; + sig.channel = ftdmchan; + sig.event_id = FTDM_SIGEVENT_SIGSTATUS_CHANGED; + sig.raw_data = &status; + ftdm_span_send_signal(ftdmchan->span, &sig); + return; +} + +static FIO_CHANNEL_SET_SIG_STATUS_FUNCTION(sangoma_boost_set_channel_sig_status) +{ + ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; + if (!sangoma_boost_data->sigmod) { + ftdm_log(FTDM_LOG_ERROR, "Cannot set signaling status in boost channel with no signaling module configured\n"); + return FTDM_FAIL; + } + if (!sangoma_boost_data->sigmod->set_channel_sig_status) { + ftdm_log(FTDM_LOG_ERROR, "Cannot set signaling status in boost channel: method not implemented\n"); + return FTDM_NOTIMPL; + } + return sangoma_boost_data->sigmod->set_channel_sig_status(ftdmchan, status); +} + +static FIO_CHANNEL_GET_SIG_STATUS_FUNCTION(sangoma_boost_get_channel_sig_status) +{ + ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; + if (!sangoma_boost_data->sigmod) { + ftdm_log(FTDM_LOG_ERROR, "Cannot get signaling status in boost channel with no signaling module configured\n"); + return FTDM_FAIL; + } + if (!sangoma_boost_data->sigmod->get_channel_sig_status) { + ftdm_log(FTDM_LOG_ERROR, "Cannot get signaling status in boost channel: method not implemented\n"); + return FTDM_NOTIMPL; + } + return sangoma_boost_data->sigmod->get_channel_sig_status(ftdmchan, status); +} + +static FIO_SPAN_SET_SIG_STATUS_FUNCTION(sangoma_boost_set_span_sig_status) +{ + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + if (!sangoma_boost_data->sigmod) { + ftdm_log(FTDM_LOG_ERROR, "Cannot set signaling status in boost span with no signaling module configured\n"); + return FTDM_FAIL; + } + if (!sangoma_boost_data->sigmod->set_span_sig_status) { + ftdm_log(FTDM_LOG_ERROR, "Cannot set signaling status in boost span: method not implemented\n"); + return FTDM_NOTIMPL; + } + return sangoma_boost_data->sigmod->set_span_sig_status(span, status); +} + +static FIO_SPAN_GET_SIG_STATUS_FUNCTION(sangoma_boost_get_span_sig_status) +{ + ftdm_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + if (!sangoma_boost_data->sigmod) { + ftdm_log(FTDM_LOG_ERROR, "Cannot get signaling status in boost span with no signaling module configured\n"); + return FTDM_FAIL; + } + if (!sangoma_boost_data->sigmod->get_span_sig_status) { + ftdm_log(FTDM_LOG_ERROR, "Cannot get signaling status in boost span: method not implemented\n"); + return FTDM_NOTIMPL; + } + return sangoma_boost_data->sigmod->get_span_sig_status(span, status); +} + +/* TODO: move these ones to a common private header so other ISDN mods can use them */ +static void ftdm_span_set_npi(const char *npi_string, uint8_t *target) +{ + if (!strcasecmp(npi_string, "isdn") || !strcasecmp(npi_string, "e164")) { + *target = FTDM_NPI_ISDN; + } else if (!strcasecmp(npi_string, "data")) { + *target = FTDM_NPI_DATA; + } else if (!strcasecmp(npi_string, "telex")) { + *target = FTDM_NPI_TELEX; + } else if (!strcasecmp(npi_string, "national")) { + *target = FTDM_NPI_NATIONAL; + } else if (!strcasecmp(npi_string, "private")) { + *target = FTDM_NPI_PRIVATE; + } else if (!strcasecmp(npi_string, "reserved")) { + *target = FTDM_NPI_RESERVED; + } else if (!strcasecmp(npi_string, "unknown")) { + *target = FTDM_NPI_UNKNOWN; + } else { + ftdm_log(FTDM_LOG_WARNING, "Invalid NPI value (%s)\n", npi_string); + *target = FTDM_NPI_UNKNOWN; + } +} + +static void ftdm_span_set_ton(const char *ton_string, uint8_t *target) +{ + if (!strcasecmp(ton_string, "national")) { + *target = FTDM_TON_NATIONAL; + } else if (!strcasecmp(ton_string, "international")) { + *target = FTDM_TON_INTERNATIONAL; + } else if (!strcasecmp(ton_string, "local")) { + *target = FTDM_TON_SUBSCRIBER_NUMBER; + } else if (!strcasecmp(ton_string, "unknown")) { + *target = FTDM_TON_UNKNOWN; + } else { + ftdm_log(FTDM_LOG_WARNING, "Invalid TON value (%s)\n", ton_string); + *target = FTDM_TON_UNKNOWN; + } +} + + +/** + * \brief Initialises an sangoma boost span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_boost_configure_span) +{ +#define FAIL_CONFIG_RETURN(retstatus) \ + if (sangoma_boost_data) \ + ftdm_safe_free(sangoma_boost_data); \ + if (err) \ + ftdm_safe_free(err) \ + if (hash_locked) \ + ftdm_mutex_unlock(g_boost_modules_mutex); \ + if (lib) \ + ftdm_dso_destroy(&lib); \ + return retstatus; + + boost_sigmod_interface_t *sigmod_iface = NULL; + ftdm_sangoma_boost_data_t *sangoma_boost_data = NULL; + const char *local_ip = "127.0.0.65", *remote_ip = "127.0.0.66"; + const char *sigmod = NULL; + int local_port = 53000, remote_port = 53000; + const char *var = NULL, *val = NULL; + int hash_locked = 0; + ftdm_dso_lib_t lib = NULL; + char path[255] = ""; + char *err = NULL; + unsigned paramindex = 0; + ftdm_status_t rc = FTDM_SUCCESS; + + for (; ftdm_parameters[paramindex].var; paramindex++) { + var = ftdm_parameters[paramindex].var; + val = ftdm_parameters[paramindex].val; + if (!strcasecmp(var, "sigmod")) { + sigmod = val; + } else if (!strcasecmp(var, "local_ip")) { + local_ip = val; + } else if (!strcasecmp(var, "remote_ip")) { + remote_ip = val; + } else if (!strcasecmp(var, "local_port")) { + local_port = atoi(val); + } else if (!strcasecmp(var, "remote_port")) { + remote_port = atoi(val); + } else if (!strcasecmp(var, "outbound-called-ton")) { + ftdm_span_set_ton(val, &span->default_caller_data.dnis.type); + } else if (!strcasecmp(var, "outbound-called-npi")) { + ftdm_span_set_npi(val, &span->default_caller_data.dnis.plan); + } else if (!strcasecmp(var, "outbound-calling-ton")) { + ftdm_span_set_ton(val, &span->default_caller_data.cid_num.type); + } else if (!strcasecmp(var, "outbound-calling-npi")) { + ftdm_span_set_npi(val, &span->default_caller_data.cid_num.plan); + } else if (!strcasecmp(var, "outbound-rdnis-ton")) { + ftdm_span_set_ton(val, &span->default_caller_data.rdnis.type); + } else if (!strcasecmp(var, "outbound-rdnis-npi")) { + ftdm_span_set_npi(val, &span->default_caller_data.rdnis.plan); + } else if (!sigmod) { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + FAIL_CONFIG_RETURN(FTDM_FAIL); + } + } + + if (!sigmod) { + if (!local_ip && local_port && remote_ip && remote_port && sig_cb) { + ftdm_set_string(span->last_error, "missing Sangoma boost IP parameters"); + FAIL_CONFIG_RETURN(FTDM_FAIL); + } + } + + sangoma_boost_data = ftdm_calloc(1, sizeof(*sangoma_boost_data)); + if (!sangoma_boost_data) { + FAIL_CONFIG_RETURN(FTDM_FAIL); + } + + /* WARNING: be sure to release this mutex on errors inside this if() */ + ftdm_mutex_lock(g_boost_modules_mutex); + hash_locked = 1; + if (sigmod && !(sigmod_iface = hashtable_search(g_boost_modules_hash, (void *)sigmod))) { + ftdm_build_dso_path(sigmod, path, sizeof(path)); + lib = ftdm_dso_open(path, &err); + if (!lib) { + ftdm_log(FTDM_LOG_ERROR, "Error loading Sangoma boost signaling module '%s': %s\n", path, err); + snprintf(span->last_error, sizeof(span->last_error), "Failed to load sangoma boost signaling module %s", path); + + FAIL_CONFIG_RETURN(FTDM_FAIL); + } + if (!(sigmod_iface = (boost_sigmod_interface_t *)ftdm_dso_func_sym(lib, BOOST_INTERFACE_NAME_STR, &err))) { + ftdm_log(FTDM_LOG_ERROR, "Failed to read Sangoma boost signaling module interface '%s': %s\n", path, err); + snprintf(span->last_error, sizeof(span->last_error), "Failed to read Sangoma boost signaling module interface '%s': %s", path, err); + + FAIL_CONFIG_RETURN(FTDM_FAIL); + } + rc = sigmod_iface->on_load(); + if (rc != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to load Sangoma boost signaling module interface '%s': on_load method failed (%d)\n", path, rc); + FAIL_CONFIG_RETURN(FTDM_FAIL); + } + sigmod_iface->pvt = lib; + sigmod_iface->set_write_msg_cb(ftdm_boost_write_msg); + sigmod_iface->set_sig_status_cb(ftdm_boost_sig_status_change); + hashtable_insert(g_boost_modules_hash, (void *)sigmod_iface->name, sigmod_iface, HASHTABLE_FLAG_NONE); + lib = NULL; /* destroying the lib will be done when going down and NOT on FAIL_CONFIG_RETURN */ + } + ftdm_mutex_unlock(g_boost_modules_mutex); + hash_locked = 0; + + if (sigmod_iface) { + /* try to create the boost queue */ + if (ftdm_queue_create(&sangoma_boost_data->boost_queue, BOOST_QUEUE_SIZE) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Span %s could not create its boost queue!\n", span->name); + FAIL_CONFIG_RETURN(FTDM_FAIL); + } + ftdm_log(FTDM_LOG_NOTICE, "Span %s will use Sangoma Boost Signaling Module %s\n", span->name, sigmod_iface->name); + sangoma_boost_data->sigmod = sigmod_iface; + sigmod_iface->configure_span(span, ftdm_parameters); + } else { + ftdm_log(FTDM_LOG_NOTICE, "Span %s will use boost socket mode\n", span->name); + ftdm_set_string(sangoma_boost_data->mcon.cfg.local_ip, local_ip); + sangoma_boost_data->mcon.cfg.local_port = local_port; + ftdm_set_string(sangoma_boost_data->mcon.cfg.remote_ip, remote_ip); + sangoma_boost_data->mcon.cfg.remote_port = remote_port; + } + span->signal_cb = sig_cb; + span->start = ftdm_sangoma_boost_start; + span->stop = ftdm_sangoma_boost_stop; + span->signal_data = sangoma_boost_data; + span->signal_type = FTDM_SIGTYPE_SANGOMABOOST; + span->outgoing_call = sangoma_boost_outgoing_call; + span->channel_request = sangoma_boost_channel_request; + span->get_channel_sig_status = sangoma_boost_get_channel_sig_status; + span->set_channel_sig_status = sangoma_boost_set_channel_sig_status; + span->get_span_sig_status = sangoma_boost_get_span_sig_status; + span->set_span_sig_status = sangoma_boost_set_span_sig_status; + span->state_map = &boost_state_map; + span->suggest_chan_id = 0; + if (sigmod_iface) { + /* the core will do the hunting */ + span->channel_request = NULL; + } + ftdm_set_flag_locked(span, FTDM_SPAN_SUSPENDED); + return FTDM_SUCCESS; +} + +/** + * \brief FreeTDM sangoma boost signaling module definition + */ +EX_DECLARE_DATA ftdm_module_t ftdm_module = { + /*.name =*/ "sangoma_boost", + /*.io_load =*/ NULL, + /*.io_unload =*/ NULL, + /*.sig_load = */ ftdm_sangoma_boost_init, + /*.sig_configure =*/ NULL, + /*.sig_unload = */ftdm_sangoma_boost_destroy, + /*.configure_span_signaling = */ ftdm_sangoma_boost_configure_span +}; + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c new file mode 100644 index 0000000000..c5cb97119f --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * 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. + */ + +#if HAVE_NETDB_H +#include +#endif + +#include "freetdm.h" +#include "sangoma_boost_client.h" + +#ifndef HAVE_GETHOSTBYNAME_R +extern int gethostbyname_r (const char *__name, + struct hostent *__result_buf, + char *__buf, size_t __buflen, + struct hostent **__result, + int *__h_errnop); +#endif + +struct sangomabc_map { + uint32_t event_id; + const char *name; +}; + +static struct sangomabc_map sangomabc_table[] = { + {SIGBOOST_EVENT_CALL_START, "CALL_START"}, + {SIGBOOST_EVENT_CALL_START_ACK, "CALL_START_ACK"}, + {SIGBOOST_EVENT_CALL_START_NACK, "CALL_START_NACK"}, + {SIGBOOST_EVENT_CALL_PROGRESS, "CALL PROGRESS"}, + {SIGBOOST_EVENT_CALL_START_NACK_ACK, "CALL_START_NACK_ACK"}, + {SIGBOOST_EVENT_CALL_ANSWERED, "CALL_ANSWERED"}, + {SIGBOOST_EVENT_CALL_STOPPED, "CALL_STOPPED"}, + {SIGBOOST_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"}, + {SIGBOOST_EVENT_CALL_RELEASED, "CALL_RELEASED"}, + {SIGBOOST_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"}, + {SIGBOOST_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"}, + {SIGBOOST_EVENT_HEARTBEAT, "HEARTBEAT"}, + {SIGBOOST_EVENT_INSERT_CHECK_LOOP, "LOOP START"}, + {SIGBOOST_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"}, + {SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE, "AUTO_CALL_GAP_ABATE"}, + {SIGBOOST_EVENT_DIGIT_IN, "DIGIT_IN"} +}; + + + +static void sangomabc_print_event_call(sangomabc_connection_t *mcon, sangomabc_event_t *event, int priority, int dir, const char *file, const char *func, int line) +{ + if (event->event_id == SIGBOOST_EVENT_HEARTBEAT) + return; + + ftdm_log(file, func, line, FTDM_LOG_LEVEL_DEBUG, "%s EVENT (%s): %s:(%X) [w%dg%d] CSid=%i Seq=%i Cn=[%s] Cd=[%s] Ci=[%s] Rdnis=[%s]\n", + dir ? "TX":"RX", + priority ? "P":"N", + sangomabc_event_id_name(event->event_id), + event->event_id, + event->span, + event->chan, + event->call_setup_id, + event->fseqno, + strlen(event->calling_name)?event->calling_name:"N/A", + (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), + (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A"), + event->isup_in_rdnis); + +} +static void sangomabc_print_event_short(sangomabc_connection_t *mcon, sangomabc_short_event_t *event, int priority, int dir, const char *file, const char *func, int line) +{ + if (event->event_id == SIGBOOST_EVENT_HEARTBEAT) + return; + ftdm_log(file, func, line, FTDM_LOG_LEVEL_DEBUG, "%s EVENT (%s): %s:(%X) [s%dc%d] Rc=%i CSid=%i Seq=%i \n", + dir ? "TX":"RX", + priority ? "P":"N", + sangomabc_event_id_name(event->event_id), + event->event_id, + event->span, + event->chan, + event->release_cause, + event->call_setup_id, + event->fseqno); +} + + +static int create_conn_socket(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) +{ +#ifndef WIN32 + int rc; + struct hostent *result, *local_result; + char buf[512], local_buf[512]; + int err = 0, local_err = 0; + + if (mcon->sigmod) { + ftdm_log(FTDM_LOG_WARNING, "I should not be called on a sigmod-managed connection!\n"); + return 0; + } + + memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp)); + memset(&mcon->local_hp, 0, sizeof(mcon->local_hp)); +#ifdef HAVE_NETINET_SCTP_H + ftdm_log(FTDM_LOG_DEBUG, "Creating SCTP socket L=%s:%d R=%s:%d\n", + local_ip, local_port, ip, port); + mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); +#else + ftdm_log(FTDM_LOG_DEBUG, "Creating UDP socket L=%s:%d R=%s:%d\n", + local_ip, local_port, ip, port); + mcon->socket = socket(AF_INET, SOCK_DGRAM, 0); +#endif + + if (mcon->socket >= 0) { + int flag; + + flag = 1; +#ifdef HAVE_GETHOSTBYNAME_R_FIVE + gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &err); + gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_err); + if (!err && !local_err) { +#else + gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_result, &local_err); + if (result && local_result) { +#endif + mcon->remote_addr.sin_family = mcon->remote_hp.h_addrtype; + memcpy((char *) &mcon->remote_addr.sin_addr.s_addr, mcon->remote_hp.h_addr_list[0], mcon->remote_hp.h_length); + mcon->remote_addr.sin_port = htons(port); + + mcon->local_addr.sin_family = mcon->local_hp.h_addrtype; + memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length); + mcon->local_addr.sin_port = htons(local_port); + +#ifdef HAVE_NETINET_SCTP_H + setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY, + (char *)&flag, sizeof(int)); +#endif + + if ((rc = bind(mcon->socket, + (struct sockaddr *) &mcon->local_addr, + sizeof(mcon->local_addr))) < 0) { + close(mcon->socket); + mcon->socket = -1; + } else { +#ifdef HAVE_NETINET_SCTP_H + rc=listen(mcon->socket, 100); + if (rc) { + close(mcon->socket); + mcon->socket = -1; + } +#endif + } + } + } + + return mcon->socket; +#else + return 0; +#endif // ifndef WIN32 +} + +int sangomabc_connection_close(sangomabc_connection_t *mcon) +{ +#ifndef WIN32 + if (mcon->sigmod) { + ftdm_log(FTDM_LOG_WARNING, "I should not be called on a sigmod-managed connection!\n"); + return 0; + } + if (mcon->socket > -1) { + close(mcon->socket); + } + + if (mcon->mutex) { + ftdm_mutex_lock(mcon->mutex); + ftdm_mutex_unlock(mcon->mutex); + ftdm_mutex_destroy(&mcon->mutex); + } + memset(mcon, 0, sizeof(*mcon)); + mcon->socket = -1; +#endif + return 0; +} + +int sangomabc_connection_open(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) +{ + ftdm_mutex_create(&mcon->mutex); + if (mcon->sigmod) { + /*value of mcon->socket will be ignored in sigmod mode */ + return 0; + } +#ifndef WIN32 + create_conn_socket(mcon, local_ip, local_port, ip, port); + return mcon->socket; +#else + return 0; +#endif +} + + +int sangomabc_exec_command(sangomabc_connection_t *mcon, int span, int chan, int id, int cmd, int cause, int flags) +{ + sangomabc_short_event_t oevent; + int retry = 5; + + sangomabc_event_init(&oevent, cmd, chan, span); + oevent.release_cause = (uint8_t)cause; + oevent.flags = flags; + + if (cmd == SIGBOOST_EVENT_SYSTEM_RESTART || cmd == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { + mcon->rxseq_reset = 1; + mcon->txseq = 0; + mcon->rxseq = 0; + mcon->txwindow = 0; + } + + if (id >= 0) { + oevent.call_setup_id = (uint16_t)id; + } + + while (sangomabc_connection_write(mcon, (sangomabc_event_t*)&oevent) <= 0) { + if (--retry <= 0) { + ftdm_log(FTDM_LOG_CRIT, "Failed to tx on boost socket: %s\n", strerror(errno)); + return -1; + } else { + ftdm_log(FTDM_LOG_WARNING, "Failed to tx on boost socket: %s :retry %i\n", strerror(errno), retry); + ftdm_sleep(1); + } + } + + return 0; +} + + +int sangomabc_exec_commandp(sangomabc_connection_t *pcon, int span, int chan, int id, int cmd, int cause) +{ + sangomabc_short_event_t oevent; + int retry = 5; + + sangomabc_event_init(&oevent, cmd, chan, span); + oevent.release_cause = (uint8_t)cause; + + if (id >= 0) { + oevent.call_setup_id = (uint16_t)id; + } + + while (sangomabc_connection_writep(pcon, (sangomabc_event_t*)&oevent) <= 0) { + if (--retry <= 0) { + ftdm_log(FTDM_LOG_CRIT, "Failed to tx on boost socket: %s\n", strerror(errno)); + return -1; + } else { + ftdm_log(FTDM_LOG_WARNING, "Failed to tx on boost socket: %s :retry %i\n", strerror(errno), retry); + ftdm_sleep(1); + } + } + + return 0; +} + +sangomabc_event_t *__sangomabc_connection_read(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line) +{ +#ifndef WIN32 + unsigned int fromlen = sizeof(struct sockaddr_in); +#endif + int bytes = 0; + int msg_ok = 0; + sangomabc_queue_element_t *e = NULL; + + if (mcon->sigmod) { + e = ftdm_queue_dequeue(mcon->boost_queue); + if (e) { + bytes = e->size; + memcpy(&mcon->event, e->boostmsg, bytes); + ftdm_safe_free(e); + } + } +#ifndef WIN32 + else { + bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, + (struct sockaddr *) &mcon->local_addr, &fromlen); + } +#endif + if (bytes <= 0) { + return NULL; + } + + if (mcon->event.version != SIGBOOST_VERSION) { + ftdm_log(FTDM_LOG_CRIT, "Invalid Boost Version %i Expecting %i\n",mcon->event.version, SIGBOOST_VERSION); + } + + if ((bytes >= MIN_SIZE_CALLSTART_MSG) && boost_full_event(mcon->event.event_id)) { + msg_ok=1; + + } else if (bytes == sizeof(sangomabc_short_event_t)) { + msg_ok=1; + + } else { + msg_ok=0; + } + + if (msg_ok) { + if (sangomabc_test_flag(mcon, MSU_FLAG_DOWN)) { + if (mcon->event.event_id != SIGBOOST_EVENT_SYSTEM_RESTART && + mcon->event.event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && + mcon->event.event_id != SIGBOOST_EVENT_HEARTBEAT) { + ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Not reading packets when connection is down. [%s]\n", + sangomabc_event_id_name(mcon->event.event_id)); + return NULL; + } + } + + if (boost_full_event(mcon->event.event_id)) { + sangomabc_print_event_call(mcon, &mcon->event, 0, 0, file, func, line); + } else { + sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)&mcon->event, 0, 0, file, func, line); + } + +#if 0 +/* NC: NOT USED ANY MORE */ + if (mcon->rxseq_reset) { + //if (mcon->event.event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { + ftdm_log(FTDM_LOG_DEBUG, "Rx sync ok\n"); + mcon->rxseq = mcon->event.fseqno; + return &mcon->event; + //} + errno=EAGAIN; + ftdm_log(FTDM_LOG_DEBUG, "Waiting for rx sync...\n"); + return NULL; + } +#endif + + mcon->txwindow = mcon->txseq - mcon->event.bseqno; + mcon->rxseq++; + +#if 0 + if (mcon->rxseq != mcon->event.fseqno) { + ftdm_log(FTDM_LOG_CRIT, "Invalid Sequence Number Expect=%i Rx=%i\n", mcon->rxseq, mcon->event.fseqno); + return NULL; + } +#endif + + return &mcon->event; + } else { + if (iteration == 0) { + ftdm_log(FTDM_LOG_CRIT, "NC - Invalid Event length from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); + return NULL; + } + } + + return NULL; +} + +sangomabc_event_t *__sangomabc_connection_readp(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line) +{ +#ifndef WIN32 + unsigned int fromlen = sizeof(struct sockaddr_in); +#endif + int bytes = 0; + + if (mcon->sigmod) { + /* priority stuff is handled just the same when there is a sigmod */ + return sangomabc_connection_read(mcon, iteration); + } +#ifndef WIN32 + else { + bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, (struct sockaddr *) &mcon->local_addr, &fromlen); + } +#endif + if (bytes <= 0) { + return NULL; + } + + if (mcon->event.version != SIGBOOST_VERSION) { + ftdm_log(FTDM_LOG_CRIT, "Invalid Boost Version %i Expecting %i\n",mcon->event.version, SIGBOOST_VERSION); + } + + if (bytes == sizeof(sangomabc_short_event_t)) { + + if (boost_full_event(mcon->event.event_id)) { + sangomabc_print_event_call(mcon, &mcon->event, 1, 0, file, func, line); + } else { + sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)&mcon->event, 1, 0, file, func, line); + } + + return &mcon->event; + } else { + if (iteration == 0) { + ftdm_log(FTDM_LOG_CRIT, "Critical Error: PQ Invalid Event lenght from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); + return NULL; + } + } + + return NULL; +} + + +int __sangomabc_connection_write(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line) +{ + int err = 0; + int event_size=MIN_SIZE_CALLSTART_MSG+event->isup_in_rdnis_size; + + ftdm_assert_return(event != NULL, -1, "No event!"); + ftdm_assert_return(mcon->socket >= 0, -1, "No mcon->socket!"); + ftdm_assert_return(mcon->mutex != NULL, -1, "No mcon->mutex!"); + + ftdm_assert_return(event->span <= FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN, -1, "Invalid span when writing boost event\n"); + ftdm_assert_return(event->chan <= FTDM_MAX_CHANNELS_PHYSICAL_SPAN, -1, "Invalid chan when writing boost event\n"); + + if (!boost_full_event(event->event_id)) { + event_size=sizeof(sangomabc_short_event_t); + } + + if (sangomabc_test_flag(mcon, MSU_FLAG_DOWN)) { + if (event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART && + event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && + event->event_id != SIGBOOST_EVENT_HEARTBEAT) { + ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Not writing packets when connection is down. [%s]\n", + sangomabc_event_id_name(event->event_id)); + return 0; + } + } + + ftdm_mutex_lock(mcon->mutex); + if (event->event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { + mcon->txseq=0; + mcon->rxseq=0; + event->fseqno=0; + } else { + event->fseqno = mcon->txseq++; + } + event->bseqno = mcon->rxseq; + event->version = SIGBOOST_VERSION; + + if (boost_full_event(event->event_id)) { + sangomabc_print_event_call(mcon, event, 0, 1, file, func, line); + } else { + sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)event, 0, 1, file, func, line); + } + + if (mcon->sigmod) { + mcon->sigmod->write_msg(mcon->span, event, event_size); + err = event_size; + } +#ifndef WIN32 + else { + err = sendto(mcon->socket, event, event_size, 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); + } +#endif + + ftdm_mutex_unlock(mcon->mutex); + + ftdm_assert_return(err == event_size, -1, "Failed to send the boost message completely!"); + + return err; +} + + +int __sangomabc_connection_writep(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line) +{ + int err = 0; + int event_size=sizeof(sangomabc_event_t); + + if (!mcon->sigmod) { + ftdm_assert_return(event != NULL, -1, "No event!"); + ftdm_assert_return(mcon->socket >= 0, -1, "No mcon->socket!"); + ftdm_assert_return(mcon->mutex != NULL, -1, "No mcon->mutex!"); + } + + if (!boost_full_event(event->event_id)) { + event_size=sizeof(sangomabc_short_event_t); + } + + ftdm_mutex_lock(mcon->mutex); + event->version = SIGBOOST_VERSION; + if (mcon->sigmod) { + mcon->sigmod->write_msg(mcon->span, event, event_size); + err = event_size; + + } +#ifndef WIN32 + else { + err = sendto(mcon->socket, event, event_size, 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); + } +#endif + ftdm_mutex_unlock(mcon->mutex); + + ftdm_assert_return(err == event_size, -1, "Failed to send boost message completely!"); + + if (boost_full_event(event->event_id)) { + sangomabc_print_event_call(mcon, event, 1, 1, file, func, line); + } else { + sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)event, 1, 1, file, func, line); + } + + return err; +} + + +void sangomabc_call_init(sangomabc_event_t *event, const char *calling, const char *called, int setup_id) +{ + memset(event, 0, sizeof(sangomabc_event_t)); + event->event_id = SIGBOOST_EVENT_CALL_START; + + if (calling) { + strncpy((char*)event->calling_number_digits, calling, sizeof(event->calling_number_digits)-1); + event->calling_number_digits_count = (uint8_t)strlen(calling); + } + + if (called) { + strncpy((char*)event->called_number_digits, called, sizeof(event->called_number_digits)-1); + event->called_number_digits_count = (uint8_t)strlen(called); + } + + event->call_setup_id = (uint16_t)setup_id; + +} + +void sangomabc_event_init(sangomabc_short_event_t *event, sangomabc_event_id_t event_id, int chan, int span) +{ + memset(event, 0, sizeof(sangomabc_short_event_t)); + event->event_id = event_id; + event->chan = (uint8_t)chan; + event->span = (uint8_t)span; +} + +const char *sangomabc_event_id_name(uint32_t event_id) +{ + unsigned int x; + const char *ret = NULL; + + for (x = 0 ; x < sizeof(sangomabc_table)/sizeof(struct sangomabc_map); x++) { + if (sangomabc_table[x].event_id == event_id) { + ret = sangomabc_table[x].name; + break; + } + } + + return ret; +} + + +/* 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: + */ + diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.h b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.h new file mode 100644 index 0000000000..12c3f12b8c --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_client.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * 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 _SANGOMABC_H +#define _SANGOMABC_H + +#include "sangoma_boost_interface.h" + +#include +#include +#ifndef WIN32 +#include +#include +#include +#ifdef HAVE_NETINET_SCTP_H +#include +#endif +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include "sigboost.h" + +#define sangomabc_test_flag(p,flag) ((p)->flags & (flag)) + +#define sangomabc_set_flag(p,flag) do { \ + ((p)->flags |= (flag)); \ + } while (0) + +#define sangomabc_clear_flag(p,flag) do { \ + ((p)->flags &= ~(flag)); \ + } while (0) + +#define sangomabc_copy_flags(dest,src,flagz) do { \ + (dest)->flags &= ~(flagz); \ + (dest)->flags |= ((src)->flags & (flagz)); \ + } while (0) + +typedef t_sigboost_callstart sangomabc_event_t; +typedef t_sigboost_short sangomabc_short_event_t; +typedef uint32_t sangomabc_event_id_t; + +typedef struct sangomabc_ip_cfg +{ + char local_ip[25]; + int local_port; + char remote_ip[25]; + int remote_port; +}sangomabc_ip_cfg_t; + +typedef enum { + MSU_FLAG_EVENT = (1 << 0), + MSU_FLAG_DOWN = (1 << 1) +} sangomabc_flag_t; + + +struct sangomabc_connection { + ftdm_socket_t socket; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + sangomabc_event_t event; + struct hostent remote_hp; + struct hostent local_hp; + unsigned int flags; + ftdm_mutex_t *mutex; + FILE *log; + unsigned int txseq; + unsigned int rxseq; + unsigned int txwindow; + unsigned int rxseq_reset; + sangomabc_ip_cfg_t cfg; + /* boost signaling mod interface pointer (if not working in TCP mode) */ + boost_sigmod_interface_t *sigmod; + ftdm_queue_t *boost_queue; + ftdm_interrupt_t *sock_interrupt; + ftdm_span_t *span; +}; + +typedef struct sangomabc_connection sangomabc_connection_t; + +typedef struct sangomabc_queue_element { + unsigned char boostmsg[sizeof(sangomabc_event_t)]; + ftdm_size_t size; +} sangomabc_queue_element_t; + +/* disable nagle's algorythm */ +static __inline__ void sctp_no_nagle(int socket) +{ +#ifdef HAVE_NETINET_SCTP_H + int flag = 1; + setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int)); +#endif +} + +int sangomabc_connection_close(sangomabc_connection_t *mcon); +int sangomabc_connection_open(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port); +sangomabc_event_t *__sangomabc_connection_read(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line); +sangomabc_event_t *__sangomabc_connection_readp(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line); +int __sangomabc_connection_write(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line); +int __sangomabc_connection_writep(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line); +#define sangomabc_connection_write(_m,_e) __sangomabc_connection_write(_m, _e, __FILE__, __FUNCTION__, __LINE__) +#define sangomabc_connection_writep(_m,_e) __sangomabc_connection_writep(_m, _e, __FILE__, __FUNCTION__, __LINE__) +#define sangomabc_connection_read(_m,_e) __sangomabc_connection_read(_m, _e, __FILE__, __FUNCTION__, __LINE__) +#define sangomabc_connection_readp(_m,_e) __sangomabc_connection_readp(_m, _e, __FILE__, __FUNCTION__, __LINE__) +void sangomabc_event_init(sangomabc_short_event_t *event, sangomabc_event_id_t event_id, int chan, int span); +void sangomabc_call_init(sangomabc_event_t *event, const char *calling, const char *called, int setup_id); +const char *sangomabc_event_id_name(uint32_t event_id); +int sangomabc_exec_command(sangomabc_connection_t *mcon, int span, int chan, int id, int cmd, int cause, int flags); +int sangomabc_exec_commandp(sangomabc_connection_t *pcon, int span, int chan, int id, int cmd, int cause); + +#endif + + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_interface.h b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_interface.h new file mode 100644 index 0000000000..b555e24338 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sangoma_boost_interface.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2009, Sangoma Technologies + * Moises Silva + * 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 SANGOMA_BOOST_INTERFACE_H +#define SANGOMA_BOOST_INTERFACE_H + +#include "freetdm.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + \brief Callback used to notify signaling status changes on a channel + \param ftdmchan The freetdm channel where the signaling status just changed + \param status The new signaling status + */ +#define BOOST_SIG_STATUS_CB_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status) +typedef void (*boost_sig_status_cb_func_t) BOOST_SIG_STATUS_CB_ARGS; +#define BOOST_SIG_STATUS_CB_FUNCTION(name) void name BOOST_SIG_STATUS_CB_ARGS + +/*! + \brief Write a boost msg to a boost endpoint + \param span The freetdm span where this msg was generated + \param msg The generic message pointer, owned by the caller + \param msglen The length of the provided structure pointed by msg + \return FTDM_SUCCESS or FTDM_FAIL + + The msg buffer is owned by the caller and it should + be either t_sigboost_callstart or t_sigboost_short + the endpoint receiving the msg will first cast to + t_sigboost_short, check the event type, and if needed. + */ +#define BOOST_WRITE_MSG_ARGS (ftdm_span_t *span, void *msg, ftdm_size_t msglen) +typedef ftdm_status_t (*boost_write_msg_func_t) BOOST_WRITE_MSG_ARGS; +#define BOOST_WRITE_MSG_FUNCTION(name) ftdm_status_t name BOOST_WRITE_MSG_ARGS + +/*! + \brief Set the callback to be used by a signaling module to write boost messages + \param callback The callback to be used by the signaling module + + The provided callback will be used for the signaling boost module to notify the + user with boost messages. + */ +#define BOOST_SET_WRITE_MSG_CB_ARGS (boost_write_msg_func_t callback) +typedef void (*boost_set_write_msg_cb_func_t) BOOST_SET_WRITE_MSG_CB_ARGS; +#define BOOST_SET_WRITE_MSG_CB_FUNCTION(name) void name BOOST_SET_WRITE_MSG_CB_ARGS + +/*! + \brief Notify hardware status change + \param ftdmchan The freetdm channel + \param status The hw status + \return FTDM_SUCCESS or FTDM_FAIL + */ +#define BOOST_ON_HW_LINK_STATUS_CHANGE_ARGS (ftdm_channel_t *ftdmchan, ftdm_channel_hw_link_status_t status) +typedef void (*boost_on_hw_link_status_change_func_t) BOOST_ON_HW_LINK_STATUS_CHANGE_ARGS; +#define BOOST_ON_HW_LINK_STATUS_CHANGE_FUNCTION(name) void name BOOST_ON_HW_LINK_STATUS_CHANGE_ARGS + +/*! + \brief Set signaling status callback used by the signaling module to report signaling status changes + \param callback The callback to be used by the signaling module + + The provided callback will be used for the signaling boost module to notify the + user with signaling link status changes. + */ +#define BOOST_SET_SIG_STATUS_CB_ARGS (boost_sig_status_cb_func_t callback) +typedef void (*boost_set_sig_status_cb_func_t) BOOST_SET_SIG_STATUS_CB_ARGS; +#define BOOST_SET_SIG_STATUS_CB_FUNCTION(name) void name BOOST_SET_SIG_STATUS_CB_ARGS + +/*! + \brief Get the signaling status on the given channel. + \param ftdmchan The freetdm channel + \param status The status pointer where the current signaling status will be set + */ +#define BOOST_GET_CHANNEL_SIG_STATUS_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t *status) +typedef ftdm_status_t (*boost_get_channel_sig_status_func_t) BOOST_GET_CHANNEL_SIG_STATUS_ARGS; +#define BOOST_GET_CHANNEL_SIG_STATUS_FUNCTION(name) ftdm_status_t name BOOST_GET_CHANNEL_SIG_STATUS_ARGS + +/*! + \brief Set the signaling status on the given channel. + \param ftdmchan The freetdm channel + \param status The new status for the channel + \return FTDM_SUCCESS or FTDM_FAIL + */ +#define BOOST_SET_CHANNEL_SIG_STATUS_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status) +typedef ftdm_status_t (*boost_set_channel_sig_status_func_t) BOOST_SET_CHANNEL_SIG_STATUS_ARGS; +#define BOOST_SET_CHANNEL_SIG_STATUS_FUNCTION(name) ftdm_status_t name BOOST_SET_CHANNEL_SIG_STATUS_ARGS + +/*! + \brief Get the signaling status on the given span. + \param span The freetdm span + \param status The status pointer where the current signaling status will be set + */ +#define BOOST_GET_SPAN_SIG_STATUS_ARGS (ftdm_span_t *span, ftdm_signaling_status_t *status) +typedef ftdm_status_t (*boost_get_span_sig_status_func_t) BOOST_GET_SPAN_SIG_STATUS_ARGS; +#define BOOST_GET_SPAN_SIG_STATUS_FUNCTION(name) ftdm_status_t name BOOST_GET_SPAN_SIG_STATUS_ARGS + +/*! + \brief Set the signaling status on the given span. + \param ftdmchan The freetdm span + \param status The new status for the span + \return FTDM_SUCCESS or FTDM_FAIL + */ +#define BOOST_SET_SPAN_SIG_STATUS_ARGS (ftdm_span_t *span, ftdm_signaling_status_t status) +typedef ftdm_status_t (*boost_set_span_sig_status_func_t) BOOST_SET_SPAN_SIG_STATUS_ARGS; +#define BOOST_SET_SPAN_SIG_STATUS_FUNCTION(name) ftdm_status_t name BOOST_SET_SPAN_SIG_STATUS_ARGS + +/*! + \brief Configure the given span signaling + \param span The freetdm span + \param parameters The array of configuration key,value pairs (must be null terminated) + \return FTDM_SUCCESS or FTDM_FAIL + */ +#define BOOST_CONFIGURE_SPAN_ARGS (ftdm_span_t *span, ftdm_conf_parameter_t *parameters) +typedef ftdm_status_t (*boost_configure_span_func_t) BOOST_CONFIGURE_SPAN_ARGS; +#define BOOST_CONFIGURE_SPAN_FUNCTION(name) ftdm_status_t name BOOST_CONFIGURE_SPAN_ARGS + +/*! + \brief Start the given span + \param span The freetdm span + \return FTDM_SUCCESS or FTDM_FAIL + */ +#define BOOST_START_SPAN_ARGS (ftdm_span_t *span) +typedef ftdm_status_t (*boost_start_span_func_t) BOOST_START_SPAN_ARGS; +#define BOOST_START_SPAN_FUNCTION(name) ftdm_status_t name BOOST_START_SPAN_ARGS + +/*! + \brief Stop the given span + \param span The freetdm span + \return FTDM_SUCCESS or FTDM_FAIL + */ +#define BOOST_STOP_SPAN_ARGS (ftdm_span_t *span) +typedef ftdm_status_t (*boost_stop_span_func_t) BOOST_START_SPAN_ARGS; +#define BOOST_STOP_SPAN_FUNCTION(name) ftdm_status_t name BOOST_STOP_SPAN_ARGS + +/*! + \brief Called when the module is being loaded BEFORE calling anything else + \return FTDM_SUCCESS or FTDM_FAIL + */ +#define BOOST_ON_LOAD_ARGS (void) +typedef ftdm_status_t (*boost_on_load_func_t) BOOST_ON_LOAD_ARGS; +#define BOOST_ON_LOAD_FUNCTION(name) ftdm_status_t name BOOST_ON_LOAD_ARGS + +/*! + \brief Called when the module is being unloaded, last chance to stop everything! + */ +#define BOOST_ON_UNLOAD_ARGS (void) +typedef ftdm_status_t (*boost_on_unload_func_t) BOOST_ON_UNLOAD_ARGS; +#define BOOST_ON_UNLOAD_FUNCTION(name) ftdm_status_t name BOOST_ON_UNLOAD_ARGS + +/*! + \brief The boost signaling module interface + */ +typedef struct boost_sigmod_interface_s { + /*! \brief Module name */ + const char *name; + /*! \brief write boost message function */ + boost_write_msg_func_t write_msg; + /*! \brief set the user write boost message function */ + boost_set_write_msg_cb_func_t set_write_msg_cb; + /*! \brief set the user signaling status function */ + boost_set_sig_status_cb_func_t set_sig_status_cb; + /*! \brief get channel signaling status */ + boost_get_channel_sig_status_func_t get_channel_sig_status; + /*! \brief set channel signaling status */ + boost_set_channel_sig_status_func_t set_channel_sig_status; + /*! \brief get span signaling status */ + boost_get_span_sig_status_func_t get_span_sig_status; + /*! \brief set span signaling status */ + boost_set_span_sig_status_func_t set_span_sig_status; + /*! \brief set notify hardware link status change */ + boost_on_hw_link_status_change_func_t on_hw_link_status_change; + /*! \brief configure span signaling */ + boost_configure_span_func_t configure_span; + /*! \brief start freetdm span */ + boost_start_span_func_t start_span; + /*! \brief stop freetdm span */ + boost_stop_span_func_t stop_span; + /*! \brief the module was just loaded */ + boost_on_load_func_t on_load; + /*! \brief the module is about to be unloaded */ + boost_on_unload_func_t on_unload; + /*! \brief private pointer for the interface user */ + void *pvt; +} boost_sigmod_interface_t; + +#ifdef __cplusplus +} // extern C +#endif + +#define BOOST_INTERFACE_NAME boost_sigmod_interface +#define BOOST_INTERFACE_NAME_STR "boost_sigmod_interface" +/* use this in your sig boost module to declare your interface */ +#ifndef WIN32 +#define BOOST_INTERFACE boost_sigmod_interface_t BOOST_INTERFACE_NAME +#else +#define BOOST_INTERFACE __declspec(dllexport) boost_sigmod_interface_t BOOST_INTERFACE_NAME +#endif +#endif + + +/* 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: + */ + diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sigboost.h b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sigboost.h new file mode 100644 index 0000000000..05ca52a487 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/sigboost.h @@ -0,0 +1,203 @@ +/**************************************************************************** + * sigboost.h $Revision: 1.13 $ + * + * Definitions for the sigboost interface. + * + * WARNING WARNING WARNING + * + * This file is used by sangoma_mgd and perhaps other programs. Any changes + * to this file must be coordinated with other user programs, + * + * Copyright (C) 2005 Xygnada Technology, Inc. + * +****************************************************************************/ +#ifndef _SIGBOOST_H_ +#define _SIGBOOST_H_ + +#define SIGBOOST_VERSION 103 + +// handy to define integer types that actually work on both Lin and Win +#include + +enum e_sigboost_event_id_values +{ + SIGBOOST_EVENT_CALL_START = 0x80, /*128*/ + SIGBOOST_EVENT_CALL_START_ACK = 0x81, /*129*/ + SIGBOOST_EVENT_CALL_START_NACK = 0x82, /*130*/ + SIGBOOST_EVENT_CALL_START_NACK_ACK = 0x83, /*131*/ + SIGBOOST_EVENT_CALL_ANSWERED = 0x84, /*132*/ + SIGBOOST_EVENT_CALL_STOPPED = 0x85, /*133*/ + SIGBOOST_EVENT_CALL_STOPPED_ACK = 0x86, /*134*/ + SIGBOOST_EVENT_SYSTEM_RESTART = 0x87, /*135*/ + SIGBOOST_EVENT_SYSTEM_RESTART_ACK = 0x88, /*136*/ + /* CALL_RELEASED is aimed to fix a race condition that became obvious + * when the boost socket was replaced by direct function calls + * and the channel hunting was moved to freetdm, the problem is + * we can get CALL_STOPPED msg and reply with CALL_STOPPED_ACK + * but the signaling module will still (in PRI) send RELEASE and + * wait for RELEASE_COMPLETE from the isdn network before + * marking the channel as available, therefore freetdm should + * also not mark the channel as available until CALL_RELEASED + * is received, for socket mode we can continue working as usual + * with CALL_STOPPED being the last step because the hunting is + * done in the signaling module. + * */ + SIGBOOST_EVENT_CALL_RELEASED = 0x51, /* 81 */ + SIGBOOST_EVENT_CALL_PROGRESS = 0x50, /*decimal 80*/ + /* Following IDs are ss7boost to sangoma_mgd only. */ + SIGBOOST_EVENT_HEARTBEAT = 0x89, /*137*/ + SIGBOOST_EVENT_INSERT_CHECK_LOOP = 0x8a, /*138*/ + SIGBOOST_EVENT_REMOVE_CHECK_LOOP = 0x8b, /*139*/ + SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE = 0x8c, /*140*/ + SIGBOOST_EVENT_DIGIT_IN = 0x8d, /*141*/ +}; +enum e_sigboost_release_cause_values +{ + SIGBOOST_RELEASE_CAUSE_UNDEFINED = 0, + SIGBOOST_RELEASE_CAUSE_NORMAL = 16, + /* probable elimination */ + //SIGBOOST_RELEASE_CAUSE_BUSY = 0x91, /* 145 */ + //SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST = 0x92, /* 146 */ + //SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET = 0x93, /* 147 */ + //SIGBOOST_RELEASE_CAUSE_NOANSWER = 0x94, /* 148 */ +}; + +enum e_sigboost_call_setup_ack_nack_cause_values +{ + //SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 34, /* Q.850 value - don't use */ + SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 117, /* non Q.850 value indicates local all ckt busy + causing sangoma_mgd to perform automatic call + gapping*/ + SIGBOOST_CALL_SETUP_NACK_TEST_CKT_BUSY = 17, /* Q.850 value */ + SIGBOOST_CALL_SETUP_NACK_INVALID_NUMBER = 28, /* Q.850 value */ + SIGBOOST_CALL_SETUP_CSUPID_DBL_USE = 200, /* unused Q.850 value */ +}; + + +enum e_sigboost_huntgroup_values +{ + SIGBOOST_HUNTGRP_SEQ_ASC = 0x00, /* sequential with lowest available first */ + SIGBOOST_HUNTGRP_SEQ_DESC = 0x01, /* sequential with highest available first */ + SIGBOOST_HUNTGRP_RR_ASC = 0x02, /* round-robin with lowest available first */ + SIGBOOST_HUNTGRP_RR_DESC = 0x03, /* round-robin with highest available first */ +}; + +enum e_sigboost_event_info_par_values +{ + SIGBOOST_EVI_SPARE = 0x00, + SIGBOOST_EVI_ALERTING = 0x01, + SIGBOOST_EVI_PROGRESS = 0x02, +}; + +enum e_sigboost_progress_flags +{ + SIGBOOST_PROGRESS_RING = (1 << 0), + SIGBOOST_PROGRESS_MEDIA = (1 << 1) +}; + +#define MAX_DIALED_DIGITS 31 + +/* Next two defines are used to create the range of values for call_setup_id + * in the t_sigboost structure. + * 0..((CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN) - 1) */ +#define CORE_MAX_SPANS 200 +#define CORE_MAX_CHAN_PER_SPAN 32 +#define MAX_PENDING_CALLS CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN +/* 0..(MAX_PENDING_CALLS-1) is range of call_setup_id below */ + +/* Should only be used by server */ +#define MAX_CALL_SETUP_ID 0xFFFF + +#define SIZE_CUSTOM 900 +#define SIZE_RDNIS SIZE_CUSTOM + + +#pragma pack(1) + +typedef struct +{ + uint8_t capability; + uint8_t uil1p; +} t_sigboost_bearer; + +typedef struct +{ + uint8_t digits_count; + char digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ + uint8_t npi; + uint8_t ton; + uint8_t screening_ind; + uint8_t presentation_ind; +}t_sigboost_digits; + +typedef struct +{ + uint16_t version; + uint32_t event_id; + /* delete sequence numbers - SCTP does not need them */ + uint32_t fseqno; + uint32_t bseqno; + uint16_t call_setup_id; + uint32_t trunk_group; + uint8_t span; + uint8_t chan; + uint32_t flags; + /* struct timeval tv; */ + t_sigboost_digits called; + t_sigboost_digits calling; + t_sigboost_digits rdnis; + /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ + char calling_name[MAX_DIALED_DIGITS + 1]; + t_sigboost_bearer bearer; + uint8_t hunt_group; + uint16_t custom_data_size; + char custom_data[SIZE_CUSTOM]; /* it's a null terminated string */ + +} t_sigboost_callstart; + +#define called_number_digits_count called.digits_count +#define called_number_digits called.digits +#define calling_number_digits_count calling.digits_count +#define calling_number_digits calling.digits +#define calling_number_screening_ind calling.screening_ind +#define calling_number_presentation calling.presentation_ind + +#define isup_in_rdnis_size custom_data_size +#define isup_in_rdnis custom_data + + +#define MIN_SIZE_CALLSTART_MSG sizeof(t_sigboost_callstart) - SIZE_CUSTOM + +typedef struct +{ + uint16_t version; + uint32_t event_id; + /* delete sequence numbers - SCTP does not need them */ + uint32_t fseqno; + uint32_t bseqno; + uint16_t call_setup_id; + uint32_t trunk_group; + uint8_t span; + uint8_t chan; + uint32_t flags; + /* struct timeval tv; */ + uint8_t release_cause; +} t_sigboost_short; +#pragma pack() + + +static __inline__ int boost_full_event(int event_id) +{ + switch (event_id) { + case SIGBOOST_EVENT_CALL_START: + case SIGBOOST_EVENT_DIGIT_IN: + case SIGBOOST_EVENT_CALL_PROGRESS: + return 1; + default: + break; + } + + return 0; +} + +#endif diff --git a/libs/freetdm/src/ftmod/ftmod_skel/ftmod_skel.c b/libs/freetdm/src/ftmod/ftmod_skel/ftmod_skel.c new file mode 100644 index 0000000000..62c4942e7c --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_skel/ftmod_skel.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "ftdm_skel.h" + +static FIO_CONFIGURE_FUNCTION(skel_configure) +{ + return FTDM_FAIL; +} + +static FIO_CONFIGURE_SPAN_FUNCTION(skel_configure_span) +{ + return FTDM_FAIL; +} + +static FIO_OPEN_FUNCTION(skel_open) +{ + return FTDM_FAIL; +} + +static FIO_CLOSE_FUNCTION(skel_close) +{ + return FTDM_FAIL; +} + +static FIO_WAIT_FUNCTION(skel_wait) +{ + return FTDM_FAIL; +} + +static FIO_READ_FUNCTION(skel_read) +{ + return FTDM_FAIL; +} + +static FIO_WRITE_FUNCTION(skel_write) +{ + return FTDM_FAIL; +} + +static FIO_COMMAND_FUNCTION(skel_command) +{ + return FTDM_FAIL; +} + +static FIO_SPAN_POLL_EVENT_FUNCTION(skel_poll_event) +{ + return FTDM_FAIL; +} + +static FIO_SPAN_NEXT_EVENT_FUNCTION(skel_next_event) +{ + return FTDM_FAIL; +} + +static FIO_CHANNEL_DESTROY_FUNCTION(skel_channel_destroy) +{ + return FTDM_FAIL; +} + +static FIO_SPAN_DESTROY_FUNCTION(skel_span_destroy) +{ + return FTDM_FAIL; +} + +static FIO_GET_ALARMS_FUNCTION(skel_get_alarms) +{ + return FTDM_FAIL; +} + +static ftdm_io_interface_t skel_interface; + +static FIO_IO_LOAD_FUNCTION(skel_init) +{ + assert(fio != NULL); + memset(&skel_interface, 0, sizeof(skel_interface)); + + skel_interface.name = "skel"; + skel_interface.configure = skel_configure; + skel_interface.configure_span = skel_configure_span; + skel_interface.open = skel_open; + skel_interface.close = skel_close; + skel_interface.wait = skel_wait; + skel_interface.read = skel_read; + skel_interface.write = skel_write; + skel_interface.command = skel_command; + skel_interface.poll_event = skel_poll_event; + skel_interface.next_event = skel_next_event; + skel_interface.channel_destroy = skel_channel_destroy; + skel_interface.span_destroy = skel_span_destroy; + skel_interface.get_alarms = skel_get_alarms; + *fio = &skel_interface; + + return FTDM_SUCCESS; +} + +static FIO_IO_UNLOAD_FUNCTION(skel_destroy) +{ + return FTDM_SUCCESS; +} + + +ftdm_module_t ftdm_module = { + "skel", + skel_init, + skel_destroy, +}; + + +/* 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: + */ + diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.2008.vcproj new file mode 100644 index 0000000000..bfc50b7468 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.2008.vcproj @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c new file mode 100644 index 0000000000..46be23f60b --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c @@ -0,0 +1,1238 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + * Contributors: + * + * Moises Silva + * David Yat Sin + * Nenad Corbic + * + */ + +#ifdef __sun +#include +#include +#endif +#include "freetdm.h" +#ifndef __WINDOWS__ +#include +#include +#endif +#include "libsangoma.h" + +#if defined(__WINDOWS__) +/*! Backward compatible defines - current code is all using the old names*/ +#define sangoma_open_tdmapi_span_chan sangoma_open_api_span_chan +#define sangoma_open_tdmapi_span sangoma_open_api_span +#define sangoma_open_tdmapi_ctrl sangoma_open_api_ctrl +#define sangoma_tdm_get_fe_status sangoma_get_fe_status +#define sangoma_socket_close sangoma_close +#define sangoma_tdm_get_hw_coding sangoma_get_hw_coding +#define sangoma_tdm_set_fe_status sangoma_set_fe_status +#define sangoma_tdm_get_link_status sangoma_get_link_status +#define sangoma_tdm_flush_bufs sangoma_flush_bufs +#define sangoma_tdm_cmd_exec sangoma_cmd_exec +#define sangoma_tdm_read_event sangoma_read_event +#define sangoma_readmsg_tdm sangoma_readmsg +#define sangoma_readmsg_socket sangoma_readmsg +#define sangoma_sendmsg_socket sangoma_writemsg +#define sangoma_writemsg_tdm sangoma_writemsg +#define sangoma_create_socket_intr sangoma_open_api_span_chan +#endif + +/*! Starting with libsangoma 3 we can use the new libsangoma waitable API, the poor souls of those using a release where LIBSANGOMA version + * is defined but the version is not higher or equal to 3.0.0 will be forced to upgrade + * */ +#ifdef LIBSANGOMA_VERSION +#if LIBSANGOMA_VERSION_CODE < LIBSANGOMA_VERSION(3,0,0) +#undef LIBSANGOMA_VERSION +#endif +#endif + +/** + * \brief Wanpipe flags + */ +typedef enum { + WP_RINGING = (1 << 0) +} wp_flag_t; + +/** + * \brief Wanpipe globals + */ +static struct { + uint32_t codec_ms; + uint32_t wink_ms; + uint32_t flash_ms; + uint32_t ring_on_ms; + uint32_t ring_off_ms; +} wp_globals; + +/* a bunch of this stuff should go into the wanpipe_tdm_api_iface.h */ + +FIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event); +FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event); + +#define WP_INVALID_SOCKET -1 + +/** + * \brief Poll for event on a wanpipe socket + * \param fd Wanpipe socket descriptor + * \param timeout Time to wait for event + * \param flags Sangoma event flags + * \return -1 on failure, wanpipe event flags on success + * + * a cross platform way to poll on an actual pollset (span and/or list of spans) will probably also be needed for analog + * so we can have one analong handler thread that will deal with all the idle analog channels for events + * the alternative would be for the driver to provide one socket for all of the oob events for all analog channels + */ +static __inline__ int tdmv_api_wait_socket(ftdm_channel_t *ftdmchan, int timeout, int *flags) +{ + +#ifdef LIBSANGOMA_VERSION + int err; + uint32_t inflags = *flags; + uint32_t outflags = 0; + sangoma_wait_obj_t *sangoma_wait_obj = ftdmchan->mod_data; + + err = sangoma_waitfor(sangoma_wait_obj, inflags, &outflags, timeout); + *flags = 0; + if (err == SANG_STATUS_SUCCESS) { + *flags = outflags; + err = 1; /* ideally should be the number of file descriptors with something to read */ + } + if (err == SANG_STATUS_APIPOLL_TIMEOUT) { + err = 0; + } + return err; +#else + struct pollfd pfds[1]; + int res; + + memset(&pfds[0], 0, sizeof(pfds[0])); + pfds[0].fd = ftdmchan->sockfd; + pfds[0].events = *flags; + res = poll(pfds, 1, timeout); + *flags = 0; + + if (pfds[0].revents & POLLERR) { + res = -1; + } + + if (res > 0) { + *flags = pfds[0].revents; + } + + return res; +#endif + +} + +/** + * \brief Opens a sangoma channel socket (TDM API) + * \param span Span number + * \param chan Channel number + * \return 0 on success, wanpipe error code on failure + */ +static __inline__ sng_fd_t tdmv_api_open_span_chan(int span, int chan) +{ + return sangoma_open_tdmapi_span_chan(span, chan); +} + +#ifdef LIBSANGOMA_VERSION +static __inline__ sng_fd_t __tdmv_api_open_span_chan(int span, int chan) +{ + return __sangoma_open_tdmapi_span_chan(span, chan); +} +#endif + +static ftdm_io_interface_t wanpipe_interface; + +/** + * \brief Inverts bit string + * \param cas_bits CAS bit string + * \return Swapped bits + */ +static unsigned char wanpipe_swap_bits(unsigned char cas_bits) +{ + unsigned char swapped_bits = 0x0; + if (cas_bits & 0x8) { + swapped_bits |= 0x1; + } + if (cas_bits & 0x4) { + swapped_bits |= 0x2; + } + if (cas_bits & 0x2) { + swapped_bits |= 0x4; + } + if (cas_bits & 0x1) { + swapped_bits |= 0x8; + } + return swapped_bits; +} + +/** + * \brief Initialises a range of wanpipe channels + * \param span FreeTDM span + * \param spanno Wanpipe span number + * \param start Initial wanpipe channel number + * \param end Final wanpipe channel number + * \param type FreeTDM channel type + * \param name FreeTDM span name + * \param number FreeTDM span number + * \param cas_bits CAS bits + * \return number of spans configured + */ +static unsigned wp_open_range(ftdm_span_t *span, unsigned spanno, unsigned start, unsigned end, ftdm_chan_type_t type, char *name, char *number, unsigned char cas_bits) +{ + unsigned configured = 0, x; +#ifdef LIBSANGOMA_VERSION + sangoma_status_t sangstatus; + sangoma_wait_obj_t *sangoma_wait_obj; +#endif + + if (type == FTDM_CHAN_TYPE_CAS) { + ftdm_log(FTDM_LOG_DEBUG, "Configuring Wanpipe CAS channels with abcd == 0x%X\n", cas_bits); + } + for(x = start; x < end; x++) { + ftdm_channel_t *chan; + ftdm_socket_t sockfd = WP_INVALID_SOCKET; + const char *dtmf = "none"; + if (!strncasecmp(span->name, "smg_prid_nfas", 8) && span->trunk_type == FTDM_TRUNK_T1 && x == 24) { +#ifdef LIBSANGOMA_VERSION + sockfd = __tdmv_api_open_span_chan(spanno, x); +#else + ftdm_log(FTDM_LOG_ERROR, "span %d channel %d cannot be configured as smg_prid_nfas, you need to compile freetdm with newer libsangoma\n", spanno, x); +#endif + } else { + sockfd = tdmv_api_open_span_chan(spanno, x); + } + + if (sockfd == WP_INVALID_SOCKET) { + ftdm_log(FTDM_LOG_ERROR, "Failed to open wanpipe device span %d channel %d\n", spanno, x); + continue; + } + + if (ftdm_span_add_channel(span, sockfd, type, &chan) == FTDM_SUCCESS) { + wanpipe_tdm_api_t tdm_api; + memset(&tdm_api, 0, sizeof(tdm_api)); +#ifdef LIBSANGOMA_VERSION + /* we need SANGOMA_DEVICE_WAIT_OBJ_SIG and not SANGOMA_DEVICE_WAIT_OBJ alone because we need to call + * sangoma_wait_obj_sig to wake up any I/O waiters when closing the channel (typically on ftdm shutdown) + * this adds an extra pair of file descriptors to the waitable object + * */ + sangstatus = sangoma_wait_obj_create(&sangoma_wait_obj, sockfd, SANGOMA_DEVICE_WAIT_OBJ_SIG); + if (sangstatus != SANG_STATUS_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "failure create waitable object for s%dc%d\n", spanno, x); + continue; + } + chan->mod_data = sangoma_wait_obj; +#endif + + chan->physical_span_id = spanno; + chan->physical_chan_id = x; + chan->rate = 8000; + + if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO || type == FTDM_CHAN_TYPE_B) { + int err; + + dtmf = "software"; + + /* FIXME: Handle Error Condition Check for return code */ + err = sangoma_tdm_get_hw_coding(chan->sockfd, &tdm_api); + + if (tdm_api.wp_tdm_cmd.hw_tdm_coding) { + chan->native_codec = chan->effective_codec = FTDM_CODEC_ALAW; + } else { + chan->native_codec = chan->effective_codec = FTDM_CODEC_ULAW; + } + + //err = sangoma_tdm_get_hw_dtmf(chan->sockfd, &tdm_api); + //if (err > 0) { + err = sangoma_tdm_enable_dtmf_events(chan->sockfd, &tdm_api); + if (err == 0) { + ftdm_channel_set_feature(chan, FTDM_CHANNEL_FEATURE_DTMF_DETECT); + dtmf = "hardware"; + } + //} + } + +#ifdef LIBSANGOMA_VERSION + if (type == FTDM_CHAN_TYPE_FXS) { + if (sangoma_tdm_disable_ring_trip_detect_events(chan->sockfd, &tdm_api)) { + /* we had problems of on-hook/off-hook detection due to how ring trip events were handled + * if this fails, I believe we will still work ok as long as we dont handle them incorrectly */ + ftdm_log(FTDM_LOG_WARNING, "Failed to disable ring trip events in channel s%dc%d\n", spanno, x); + } + } +#endif +#if 0 + if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO) { + /* Enable FLASH/Wink Events */ + int err=sangoma_set_rm_rxflashtime(chan->sockfd, &tdm_api, wp_globals.flash_ms); + if (err == 0) { + ftdm_log(FTDM_LOG_ERROR, "flash enabled s%dc%d\n", spanno, x); + } else { + ftdm_log(FTDM_LOG_ERROR, "flash disabled s%dc%d\n", spanno, x); + } + } +#endif + + if (type == FTDM_CHAN_TYPE_CAS || type == FTDM_CHAN_TYPE_EM) { +#ifdef LIBSANGOMA_VERSION + sangoma_tdm_write_rbs(chan->sockfd,&tdm_api,chan->physical_chan_id, wanpipe_swap_bits(cas_bits)); + + /* this should probably be done for old libsangoma but I am not sure if the API is available and I'm lazy to check, + The poll rate is hard coded to 100 per second (done in the driver, is the max rate of polling allowed by wanpipe) + */ + if (sangoma_tdm_enable_rbs_events(chan->sockfd, &tdm_api, 100)) { + ftdm_log(FTDM_LOG_ERROR, "Failed to enable RBS/CAS events in device %d:%d fd:%d\n", chan->span_id, chan->chan_id, sockfd); + continue; + } + /* probably done by the driver but lets write defensive code this time */ + sangoma_flush_bufs(chan->sockfd, &tdm_api); +#else + /* + * With wanpipe 3.4.4.2 I get failure even though the events are enabled, /var/log/messages said: + * wanpipe4: WARNING: Event type 9 is already pending! + * wanpipe4: Failed to add new fe event 09 ch_map=FFFFFFFF! + * may be we should not send an error until that is fixed in the driver + */ + if (sangoma_tdm_enable_rbs_events(chan->sockfd, &tdm_api, 100)) { + ftdm_log(FTDM_LOG_ERROR, "Failed to enable RBS/CAS events in device %d:%d fd:%d\n", chan->span_id, chan->chan_id, sockfd); + } + /* probably done by the driver but lets write defensive code this time */ + sangoma_tdm_flush_bufs(chan->sockfd, &tdm_api); + sangoma_tdm_write_rbs(chan->sockfd,&tdm_api, wanpipe_swap_bits(cas_bits)); +#endif + } + + if (!ftdm_strlen_zero(name)) { + ftdm_copy_string(chan->chan_name, name, sizeof(chan->chan_name)); + } + + if (!ftdm_strlen_zero(number)) { + ftdm_copy_string(chan->chan_number, number, sizeof(chan->chan_number)); + } + configured++; + ftdm_log(FTDM_LOG_INFO, "configuring device s%dc%d as FreeTDM device %d:%d fd:%d DTMF: %s\n", + spanno, x, chan->span_id, chan->chan_id, sockfd, dtmf); + + } else { + ftdm_log(FTDM_LOG_ERROR, "ftdm_span_add_channel failed for wanpipe span %d channel %d\n", spanno, x); + } + } + + return configured; +} + +/** + * \brief Process configuration variable for a Wanpipe profile + * \param category Wanpipe profile name + * \param var Variable name + * \param val Variable value + * \param lineno Line number from configuration file + * \return Success + */ +static FIO_CONFIGURE_FUNCTION(wanpipe_configure) +{ + int num; + + if (!strcasecmp(category, "defaults")) { + if (!strcasecmp(var, "codec_ms")) { + num = atoi(val); + if (num < 10 || num > 60) { + ftdm_log(FTDM_LOG_WARNING, "invalid codec ms at line %d\n", lineno); + } else { + wp_globals.codec_ms = num; + } + } else if (!strcasecmp(var, "wink_ms")) { + num = atoi(val); + if (num < 50 || num > 3000) { + ftdm_log(FTDM_LOG_WARNING, "invalid wink ms at line %d\n", lineno); + } else { + wp_globals.wink_ms = num; + } + } else if (!strcasecmp(var, "flash_ms")) { + num = atoi(val); + if (num < 50 || num > 3000) { + ftdm_log(FTDM_LOG_WARNING, "invalid flash ms at line %d\n", lineno); + } else { + wp_globals.flash_ms = num; + } + } else if (!strcasecmp(var, "ring_on_ms")) { + num = atoi(val); + if (num < 500 || num > 5000) { + ftdm_log(FTDM_LOG_WARNING, "invalid ring_on_ms at line %d (valid range 500 to 5000)\n", lineno); + } else { + wp_globals.ring_on_ms = num; + } + } else if (!strcasecmp(var, "ring_off_ms")) { + num = atoi(val); + if (num < 500 || num > 5000) { + ftdm_log(FTDM_LOG_WARNING, "invalid ring_off_ms at line %d (valid range 500 to 5000)\n", lineno); + } else { + wp_globals.ring_off_ms = num; + } + } + } + + return FTDM_SUCCESS; +} + +/** + * \brief Initialises an freetdm Wanpipe span from a configuration string + * \param span FreeTDM span + * \param str Configuration string + * \param type FreeTDM span type + * \param name FreeTDM span name + * \param number FreeTDM span number + * \return Success or failure + */ +static FIO_CONFIGURE_SPAN_FUNCTION(wanpipe_configure_span) +{ + int items, i; + char *mydata, *item_list[10]; + char *sp, *ch, *mx; + unsigned char cas_bits = 0; + int channo; + int spanno; + int top = 0; + unsigned configured = 0; + + assert(str != NULL); + + + mydata = ftdm_strdup(str); + assert(mydata != NULL); + + + items = ftdm_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0]))); + + for(i = 0; i < items; i++) { + sp = item_list[i]; + if ((ch = strchr(sp, ':'))) { + *ch++ = '\0'; + } + + if (!(sp && ch)) { + ftdm_log(FTDM_LOG_ERROR, "No valid wanpipe span and channel was specified\n"); + continue; + } + + channo = atoi(ch); + spanno = atoi(sp); + + if (channo < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid channel number %d\n", channo); + continue; + } + + if (spanno < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid span number %d\n", channo); + continue; + } + + if ((mx = strchr(ch, '-'))) { + mx++; + top = atoi(mx) + 1; + } else { + top = channo + 1; + } + + + if (top < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid range number %d\n", top); + continue; + } + if (FTDM_CHAN_TYPE_CAS == type && ftdm_config_get_cas_bits(ch, &cas_bits)) { + ftdm_log(FTDM_LOG_ERROR, "Failed to get CAS bits in CAS channel\n"); + continue; + } + configured += wp_open_range(span, spanno, channo, top, type, name, number, cas_bits); + + } + + ftdm_safe_free(mydata); + + return configured; +} + +/** + * \brief Opens Wanpipe channel + * \param ftdmchan Channel to open + * \return Success or failure + */ +static FIO_OPEN_FUNCTION(wanpipe_open) +{ + + wanpipe_tdm_api_t tdm_api; + + memset(&tdm_api,0,sizeof(tdm_api)); + sangoma_tdm_flush_bufs(ftdmchan->sockfd, &tdm_api); +#ifdef LIBSANGOMA_VERSION + sangoma_flush_event_bufs(ftdmchan->sockfd, &tdm_api); +#endif + + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921 || ftdmchan->type == FTDM_CHAN_TYPE_DQ931) { + ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_NONE; + } else { + ftdmchan->effective_codec = ftdmchan->native_codec; + + sangoma_tdm_set_usr_period(ftdmchan->sockfd, &tdm_api, wp_globals.codec_ms); + + ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL); + ftdmchan->effective_interval = ftdmchan->native_interval = wp_globals.codec_ms; + ftdmchan->packet_len = ftdmchan->native_interval * 8; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Closes Wanpipe channel + * \param ftdmchan Channel to close + * \return Success + */ +static FIO_CLOSE_FUNCTION(wanpipe_close) +{ +#ifdef LIBSANGOMA_VERSION + sangoma_wait_obj_t *waitobj = ftdmchan->mod_data; + /* kick any I/O waiters */ + sangoma_wait_obj_signal(waitobj); +#endif + return FTDM_SUCCESS; +} + +/** + * \brief Executes an FreeTDM command on a Wanpipe channel + * \param ftdmchan Channel to execute command on + * \param command FreeTDM command to execute + * \param obj Object (unused) + * \return Success or failure + */ +static FIO_COMMAND_FUNCTION(wanpipe_command) +{ + wanpipe_tdm_api_t tdm_api; + int err = 0; + + memset(&tdm_api, 0, sizeof(tdm_api)); + + switch(command) { + case FTDM_COMMAND_OFFHOOK: + { + err=sangoma_tdm_txsig_offhook(ftdmchan->sockfd,&tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "OFFHOOK Failed"); + return FTDM_FAIL; + } + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK); + } + break; + case FTDM_COMMAND_ONHOOK: + { + err=sangoma_tdm_txsig_onhook(ftdmchan->sockfd,&tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ONHOOK Failed"); + return FTDM_FAIL; + } + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK); + } + break; + case FTDM_COMMAND_GENERATE_RING_ON: + { + err=sangoma_tdm_txsig_start(ftdmchan->sockfd,&tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring Failed"); + return FTDM_FAIL; + } + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING); + ftdm_set_pflag_locked(ftdmchan, WP_RINGING); + ftdmchan->ring_time = ftdm_current_time_in_ms() + wp_globals.ring_on_ms; + } + break; + case FTDM_COMMAND_GENERATE_RING_OFF: + { + err=sangoma_tdm_txsig_offhook(ftdmchan->sockfd,&tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring-off Failed"); + return FTDM_FAIL; + } + ftdm_clear_pflag_locked(ftdmchan, WP_RINGING); + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING); + } + break; + case FTDM_COMMAND_GET_INTERVAL: + { + err=sangoma_tdm_get_usr_period(ftdmchan->sockfd, &tdm_api); + if (err > 0 ) { + FTDM_COMMAND_OBJ_INT = err; + err=0; + } + } + break; + case FTDM_COMMAND_ENABLE_ECHOCANCEL: + { + err=sangoma_tdm_enable_hwec(ftdmchan->sockfd, &tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HWEC Enable Failed"); + return FTDM_FAIL; + } + } + break; + case FTDM_COMMAND_DISABLE_ECHOCANCEL: + { + err=sangoma_tdm_disable_hwec(ftdmchan->sockfd, &tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HWEC Disable Failed"); + return FTDM_FAIL; + } + } + break; + case FTDM_COMMAND_ENABLE_LOOP: + { +#ifdef WP_API_FEATURE_LOOP + err=sangoma_tdm_enable_loop(ftdmchan->sockfd, &tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Loop Enable Failed"); + return FTDM_FAIL; + } +#endif + } + case FTDM_COMMAND_DISABLE_LOOP: + { +#ifdef WP_API_FEATURE_LOOP + err=sangoma_tdm_disable_loop(ftdmchan->sockfd, &tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Loop Disable Failed"); + return FTDM_FAIL; + } +#endif + } + case FTDM_COMMAND_SET_INTERVAL: + { + err=sangoma_tdm_set_usr_period(ftdmchan->sockfd, &tdm_api, FTDM_COMMAND_OBJ_INT); + ftdmchan->packet_len = ftdmchan->native_interval * (ftdmchan->effective_codec == FTDM_CODEC_SLIN ? 16 : 8); + } + break; + case FTDM_COMMAND_SET_CAS_BITS: + { +#ifdef LIBSANGOMA_VERSION + err = sangoma_tdm_write_rbs(ftdmchan->sockfd,&tdm_api, ftdmchan->physical_chan_id, wanpipe_swap_bits(FTDM_COMMAND_OBJ_INT)); +#else + err = sangoma_tdm_write_rbs(ftdmchan->sockfd, &tdm_api, wanpipe_swap_bits(FTDM_COMMAND_OBJ_INT)); +#endif + } + break; + case FTDM_COMMAND_GET_CAS_BITS: + { +#ifdef LIBSANGOMA_VERSION + unsigned char rbsbits; + err = sangoma_tdm_read_rbs(ftdmchan->sockfd, &tdm_api, ftdmchan->physical_chan_id, &rbsbits); + if (!err) { + FTDM_COMMAND_OBJ_INT = wanpipe_swap_bits(rbsbits); + } +#else + // does sangoma_tdm_read_rbs is available here? + FTDM_COMMAND_OBJ_INT = ftdmchan->rx_cas_bits; +#endif + } + break; + case FTDM_COMMAND_SET_LINK_STATUS: + { + ftdm_channel_hw_link_status_t status = FTDM_COMMAND_OBJ_INT; + char sangoma_status = status == FTDM_HW_LINK_CONNECTED ? FE_CONNECTED : FE_DISCONNECTED; + err = sangoma_tdm_set_fe_status(ftdmchan->sockfd, &tdm_api, sangoma_status); + } + break; + case FTDM_COMMAND_GET_LINK_STATUS: + { + unsigned char sangoma_status = 0; + err = sangoma_tdm_get_fe_status(ftdmchan->sockfd, &tdm_api, &sangoma_status); + if (!err) { + FTDM_COMMAND_OBJ_INT = sangoma_status == FE_CONNECTED ? FTDM_HW_LINK_CONNECTED : FTDM_HW_LINK_DISCONNECTED; + } + } + break; + default: + break; + }; + + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + return FTDM_FAIL; + } + + + return FTDM_SUCCESS; +} + +/** + * \brief Reads data from a Wanpipe channel + * \param ftdmchan Channel to read from + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success, failure or timeout + */ +static FIO_READ_FUNCTION(wanpipe_read) +{ + int rx_len = 0; + wp_tdm_api_rx_hdr_t hdrframe; + + memset(&hdrframe, 0, sizeof(hdrframe)); + + rx_len = sangoma_readmsg_tdm(ftdmchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (int)*datalen,0); + *datalen = rx_len; + + if (rx_len == 0 || rx_len == -17) { + return FTDM_TIMEOUT; + } + + if (rx_len < 0) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + return FTDM_FAIL; + } + + + return FTDM_SUCCESS; +} + +/** + * \brief Writes data to a Wanpipe channel + * \param ftdmchan Channel to write to + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success or failure + */ +static FIO_WRITE_FUNCTION(wanpipe_write) +{ + int bsent; + wp_tdm_api_tx_hdr_t hdrframe; + + /* Do we even need the headerframe here? on windows, we don't even pass it to the driver */ + memset(&hdrframe, 0, sizeof(hdrframe)); + if (*datalen == 0) { + return FTDM_SUCCESS; + } + bsent = sangoma_writemsg_tdm(ftdmchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (unsigned short)(*datalen),0); + + /* should we be checking if bsent == *datalen here? */ + if (bsent > 0) { + *datalen = bsent; + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +/** + * \brief Waits for an event on a Wanpipe channel + * \param ftdmchan Channel to open + * \param flags Type of event to wait for + * \param to Time to wait (in ms) + * \return Success, failure or timeout + */ + +static FIO_WAIT_FUNCTION(wanpipe_wait) +{ + int32_t inflags = 0; + int result; + + if (*flags & FTDM_READ) { + inflags |= POLLIN; + } + + if (*flags & FTDM_WRITE) { + inflags |= POLLOUT; + } + + if (*flags & FTDM_EVENTS) { + inflags |= POLLPRI; + } + + result = tdmv_api_wait_socket(ftdmchan, to, &inflags); + + *flags = FTDM_NO_FLAGS; + + if (result < 0){ + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Poll failed"); + return FTDM_FAIL; + } + + if (result == 0) { + return FTDM_TIMEOUT; + } + + if (inflags & POLLIN) { + *flags |= FTDM_READ; + } + + if (inflags & POLLOUT) { + *flags |= FTDM_WRITE; + } + + if (inflags & POLLPRI) { + *flags |= FTDM_EVENTS; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Checks for events on a Wanpipe span + * \param span Span to check for events + * \param ms Time to wait for event + * \return Success if event is waiting or failure if not + */ +FIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event) +{ +#ifdef LIBSANGOMA_VERSION + sangoma_status_t sangstatus; + sangoma_wait_obj_t *pfds[FTDM_MAX_CHANNELS_SPAN] = { 0 }; + uint32_t inflags[FTDM_MAX_CHANNELS_SPAN]; + uint32_t outflags[FTDM_MAX_CHANNELS_SPAN]; +#else + struct pollfd pfds[FTDM_MAX_CHANNELS_SPAN]; +#endif + uint32_t i, j = 0, k = 0, l = 0; + int r; + + for(i = 1; i <= span->chan_count; i++) { + ftdm_channel_t *ftdmchan = span->channels[i]; +#ifdef LIBSANGOMA_VERSION + if (!ftdmchan->mod_data) { + continue; /* should never happen but happens when shutting down */ + } + pfds[j] = ftdmchan->mod_data; + inflags[j] = POLLPRI; +#else + memset(&pfds[j], 0, sizeof(pfds[j])); + pfds[j].fd = span->channels[i]->sockfd; + pfds[j].events = POLLPRI; +#endif + + /* The driver probably should be able to do this wink/flash/ringing by itself this is sort of a hack to make it work! */ + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_WINK) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_FLASH)) { + l = 5; + } + + j++; + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RINGING)) { + l = 5; + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RINGING) && ftdm_current_time_in_ms() >= ftdmchan->ring_time) { + wanpipe_tdm_api_t tdm_api; + int err; + memset(&tdm_api, 0, sizeof(tdm_api)); + if (ftdm_test_pflag(ftdmchan, WP_RINGING)) { + err = sangoma_tdm_txsig_offhook(ftdmchan->sockfd,&tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring-off Failed"); + ftdm_log(FTDM_LOG_ERROR, "sangoma_tdm_txsig_offhook failed\n"); + return FTDM_FAIL; + } + ftdm_clear_pflag_locked(ftdmchan, WP_RINGING); + ftdmchan->ring_time = ftdm_current_time_in_ms() + wp_globals.ring_off_ms; + } else { + err=sangoma_tdm_txsig_start(ftdmchan->sockfd,&tdm_api); + if (err) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring Failed"); + ftdm_log(FTDM_LOG_ERROR, "sangoma_tdm_txsig_start failed\n"); + return FTDM_FAIL; + } + ftdm_set_pflag_locked(ftdmchan, WP_RINGING); + ftdmchan->ring_time = ftdm_current_time_in_ms() + wp_globals.ring_on_ms; + } + } + } + + if (l) { + ms = l; + } +#ifdef LIBSANGOMA_VERSION + sangstatus = sangoma_waitfor_many(pfds, inflags, outflags, j, ms); + if (SANG_STATUS_APIPOLL_TIMEOUT == sangstatus) { + r = 0; + } else if (SANG_STATUS_SUCCESS == sangstatus) { + r = 1; /* hopefully we never need how many changed -_- */ + } else { + ftdm_log(FTDM_LOG_ERROR, "sangoma_waitfor_many failed: %d, %s\n", sangstatus, strerror(errno)); + r = -1; + } +#else + r = poll(pfds, j, ms); +#endif + + if (r == 0) { + return l ? FTDM_SUCCESS : FTDM_TIMEOUT; + } else if (r < 0) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return FTDM_FAIL; + } + + for(i = 1; i <= span->chan_count; i++) { + ftdm_channel_t *ftdmchan = span->channels[i]; + +#ifdef LIBSANGOMA_VERSION + if (outflags[i-1] & POLLPRI) { +#else + if (pfds[i-1].revents & POLLPRI) { +#endif + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_EVENT); + ftdmchan->last_event_time = ftdm_current_time_in_ms(); + k++; + } + } + /* when k is 0 it might be that an async wanpipe device signal was delivered */ + return FTDM_SUCCESS; +} + +/** + * \brief Gets alarms from a Wanpipe Channel + * \param ftdmchan Channel to get alarms from + * \return Success or failure + */ +static FIO_GET_ALARMS_FUNCTION(wanpipe_get_alarms) +{ + wanpipe_tdm_api_t tdm_api; + unsigned int alarms = 0; + int err; + + memset(&tdm_api,0,sizeof(tdm_api)); + +#ifdef LIBSANGOMA_VERSION + if ((err = sangoma_tdm_get_fe_alarms(ftdmchan->sockfd, &tdm_api, &alarms))) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ioctl failed (%s)", strerror(errno)); + snprintf(ftdmchan->span->last_error, sizeof(ftdmchan->span->last_error), "ioctl failed (%s)", strerror(errno)); + return FTDM_FAIL; + } +#else + if ((err = sangoma_tdm_get_fe_alarms(ftdmchan->sockfd, &tdm_api)) < 0){ + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ioctl failed (%s)", strerror(errno)); + snprintf(ftdmchan->span->last_error, sizeof(ftdmchan->span->last_error), "ioctl failed (%s)", strerror(errno)); + return FTDM_FAIL; + } + alarms = tdm_api.wp_tdm_cmd.fe_alarms; +#endif + ftdmchan->alarm_flags = FTDM_ALARM_NONE; + + if (alarms & WAN_TE_BIT_ALARM_RED) { + ftdmchan->alarm_flags |= FTDM_ALARM_RED; + alarms &= ~WAN_TE_BIT_ALARM_RED; + } + + if (alarms & WAN_TE_BIT_ALARM_AIS) { + ftdmchan->alarm_flags |= FTDM_ALARM_AIS; + ftdmchan->alarm_flags |= FTDM_ALARM_BLUE; + alarms &= ~WAN_TE_BIT_ALARM_AIS; + } + + if (alarms & WAN_TE_BIT_ALARM_RAI) { + ftdmchan->alarm_flags |= FTDM_ALARM_RAI; + ftdmchan->alarm_flags |= FTDM_ALARM_YELLOW; + alarms &= ~WAN_TE_BIT_ALARM_RAI; + } + + /* still missing to map: + * FTDM_ALARM_RECOVER + * FTDM_ALARM_LOOPBACK + * FTDM_ALARM_NOTOPEN + * */ + + /* if we still have alarms that we did not map, set the general alarm */ + if (alarms) { + ftdm_log(FTDM_LOG_DEBUG, "Unmapped wanpipe alarms: %d\n", alarms); + ftdmchan->alarm_flags |= FTDM_ALARM_GENERAL; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Retrieves an event from a wanpipe span + * \param span Span to retrieve event from + * \param event FreeTDM event to return + * \return Success or failure + */ +FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event) +{ + uint32_t i,err; + ftdm_oob_event_t event_id; + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->last_event_time && !ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) { + uint32_t diff = (uint32_t)(ftdm_current_time_in_ms() - span->channels[i]->last_event_time); + /* XX printf("%u %u %u\n", diff, (unsigned)ftdm_current_time_in_ms(), (unsigned)span->channels[i]->last_event_time); */ + if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_WINK)) { + if (diff > wp_globals.wink_ms) { + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_WINK); + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_FLASH); + ftdm_set_flag_locked(span->channels[i], FTDM_CHANNEL_OFFHOOK); + event_id = FTDM_OOB_OFFHOOK; + goto event; + } + } + + if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_FLASH)) { + if (diff > wp_globals.flash_ms) { + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_FLASH); + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_WINK); + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_OFFHOOK); + event_id = FTDM_OOB_ONHOOK; + + if (span->channels[i]->type == FTDM_CHAN_TYPE_FXO) { + ftdm_channel_t *ftdmchan = span->channels[i]; + wanpipe_tdm_api_t tdm_api; + memset(&tdm_api, 0, sizeof(tdm_api)); + + sangoma_tdm_txsig_onhook(ftdmchan->sockfd,&tdm_api); + } + goto event; + } + } + } + if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) { + wanpipe_tdm_api_t tdm_api; + ftdm_channel_t *ftdmchan = span->channels[i]; + memset(&tdm_api, 0, sizeof(tdm_api)); + ftdm_clear_flag(span->channels[i], FTDM_CHANNEL_EVENT); + + err = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api); + if (err != FTDM_SUCCESS) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return FTDM_FAIL; + } + + ftdm_log(FTDM_LOG_DEBUG, "read wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); + switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) { + + case WP_TDMAPI_EVENT_LINK_STATUS: + { + switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) { + case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED: + event_id = FTDM_OOB_ALARM_CLEAR; + break; + default: + event_id = FTDM_OOB_ALARM_TRAP; + break; + }; + } + break; + + case WP_TDMAPI_EVENT_RXHOOK: + { + if (span->channels[i]->type == FTDM_CHAN_TYPE_FXS) { + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_OFFHOOK : FTDM_OOB_ONHOOK; + if (event_id == FTDM_OOB_OFFHOOK) { + if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_FLASH)) { + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_FLASH); + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_WINK); + event_id = FTDM_OOB_FLASH; + goto event; + } else { + ftdm_set_flag_locked(span->channels[i], FTDM_CHANNEL_WINK); + } + } else { + if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_WINK)) { + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_WINK); + ftdm_clear_flag_locked(span->channels[i], FTDM_CHANNEL_FLASH); + event_id = FTDM_OOB_WINK; + goto event; + } else { + ftdm_set_flag_locked(span->channels[i], FTDM_CHANNEL_FLASH); + } + } + continue; + } else { + int err; + ftdm_channel_t *ftdmchan = span->channels[i]; + err=sangoma_tdm_txsig_onhook(ftdmchan->sockfd,&tdm_api); + if (err) { + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "ONHOOK Failed"); + return FTDM_FAIL; + } + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_ONHOOK : FTDM_OOB_NOOP; + } + } + break; + case WP_TDMAPI_EVENT_RING_DETECT: + { + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_RING_START : FTDM_OOB_RING_STOP; + } + break; + /* + disabled this ones when configuring, we don't need them, do we? + case WP_TDMAPI_EVENT_RING_TRIP_DETECT: + { + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_ONHOOK : FTDM_OOB_OFFHOOK; + } + break; + */ + case WP_TDMAPI_EVENT_RBS: + { + event_id = FTDM_OOB_CAS_BITS_CHANGE; + span->channels[i]->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits); + } + break; + case WP_TDMAPI_EVENT_DTMF: + { + char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 }; + event_id = FTDM_OOB_NOOP; + + if (tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT) { + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_MUTE); + } + + if (tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_type == WAN_EC_TONE_STOP) { + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_MUTE); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE)) { + ftdm_log(FTDM_LOG_DEBUG, "Queuing wanpipe DTMF: %c\n", tmp_dtmf[0]); + ftdm_channel_queue_dtmf(ftdmchan, tmp_dtmf); + } + } + } + break; + case WP_TDMAPI_EVENT_ALARM: + { + ftdm_log(FTDM_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm); + event_id = FTDM_OOB_ALARM_TRAP; + } + break; + default: + { + ftdm_log(FTDM_LOG_WARNING, "Unhandled wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); + event_id = FTDM_OOB_INVALID; + } + break; + } + + event: + + span->channels[i]->last_event_time = 0; + span->event_header.e_type = FTDM_EVENT_OOB; + span->event_header.enum_id = event_id; + span->event_header.channel = span->channels[i]; + *event = &span->event_header; + return FTDM_SUCCESS; + } + } + + return FTDM_FAIL; + +} + +/** + * \brief Destroys a Wanpipe Channel + * \param ftdmchan Channel to destroy + * \return Success + */ +static FIO_CHANNEL_DESTROY_FUNCTION(wanpipe_channel_destroy) +{ +#ifdef LIBSANGOMA_VERSION + if (ftdmchan->mod_data) { + sangoma_wait_obj_t *sangoma_wait_obj; + sangoma_wait_obj = ftdmchan->mod_data; + ftdmchan->mod_data = NULL; + sangoma_wait_obj_delete(&sangoma_wait_obj); + } +#endif + + if (ftdmchan->sockfd > -1) { + close(ftdmchan->sockfd); + ftdmchan->sockfd = WP_INVALID_SOCKET; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Loads wanpipe IO module + * \param fio FreeTDM IO interface + * \return Success + */ +static FIO_IO_LOAD_FUNCTION(wanpipe_init) +{ + assert(fio != NULL); + memset(&wanpipe_interface, 0, sizeof(wanpipe_interface)); + + wp_globals.codec_ms = 20; + wp_globals.wink_ms = 150; + wp_globals.flash_ms = 750; + wp_globals.ring_on_ms = 2000; + wp_globals.ring_off_ms = 4000; + wanpipe_interface.name = "wanpipe"; + wanpipe_interface.configure_span = wanpipe_configure_span; + wanpipe_interface.configure = wanpipe_configure; + wanpipe_interface.open = wanpipe_open; + wanpipe_interface.close = wanpipe_close; + wanpipe_interface.command = wanpipe_command; + wanpipe_interface.wait = wanpipe_wait; + wanpipe_interface.read = wanpipe_read; + wanpipe_interface.write = wanpipe_write; + wanpipe_interface.poll_event = wanpipe_poll_event; + wanpipe_interface.next_event = wanpipe_next_event; + wanpipe_interface.channel_destroy = wanpipe_channel_destroy; + wanpipe_interface.get_alarms = wanpipe_get_alarms; + *fio = &wanpipe_interface; + + return FTDM_SUCCESS; +} + +/** + * \brief Unloads wanpipe IO module + * \return Success + */ +static FIO_IO_UNLOAD_FUNCTION(wanpipe_destroy) +{ + memset(&wanpipe_interface, 0, sizeof(wanpipe_interface)); + return FTDM_SUCCESS; +} + +/** + * \brief FreeTDM wanpipe IO module definition + */ +EX_DECLARE_DATA ftdm_module_t ftdm_module = { + "wanpipe", + wanpipe_init, + wanpipe_destroy, +}; + +/* 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 + */ diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ozmod_wanpipe.2005.vcproj b/libs/freetdm/src/ftmod/ftmod_wanpipe/ozmod_wanpipe.2005.vcproj new file mode 100644 index 0000000000..abebf30427 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ozmod_wanpipe.2005.vcproj @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/wanpipe_tdm_api_iface.h b/libs/freetdm/src/ftmod/ftmod_wanpipe/wanpipe_tdm_api_iface.h new file mode 100644 index 0000000000..3801ff9c8c --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/wanpipe_tdm_api_iface.h @@ -0,0 +1,351 @@ +/***************************************************************************** +* wanpipe_tdm_api_iface.h +* +* WANPIPE(tm) AFT TE1 Hardware Support +* +* Authors: Nenad Corbic +* +* Copyright (c) 2007 - 08, Sangoma Technologies +* 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 nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY ``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 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. + +* ============================================================================ +* Oct 04, 2005 Nenad Corbic Initial version. +* +* Jul 25, 2006 David Rokhvarg Ported to Windows. +*****************************************************************************/ + +#ifndef __WANPIPE_TDM_API_IFACE_H_ +#define __WANPIPE_TDM_API_IFACE_H_ + + +#if defined(__WINDOWS__) +typedef HANDLE sng_fd_t; +#else +typedef int sng_fd_t; +#endif + +/* Indicate to library that new features exist */ +#define WP_TDM_FEATURE_DTMF_EVENTS 1 +#define WP_TDM_FEATURE_FE_ALARM 1 +#define WP_TDM_FEATURE_EVENTS 1 +#define WP_TDM_FEATURE_LINK_STATUS 1 + +enum wanpipe_tdm_api_cmds { + + SIOC_WP_TDM_GET_USR_MTU_MRU, /* 0x00 */ + + SIOC_WP_TDM_SET_USR_PERIOD, /* 0x01 */ + SIOC_WP_TDM_GET_USR_PERIOD, /* 0x02 */ + + SIOC_WP_TDM_SET_HW_MTU_MRU, /* 0x03 */ + SIOC_WP_TDM_GET_HW_MTU_MRU, /* 0x04 */ + + SIOC_WP_TDM_SET_CODEC, /* 0x05 */ + SIOC_WP_TDM_GET_CODEC, /* 0x06 */ + + SIOC_WP_TDM_SET_POWER_LEVEL, /* 0x07 */ + SIOC_WP_TDM_GET_POWER_LEVEL, /* 0x08 */ + + SIOC_WP_TDM_TOGGLE_RX, /* 0x09 */ + SIOC_WP_TDM_TOGGLE_TX, /* 0x0A */ + + SIOC_WP_TDM_GET_HW_CODING, /* 0x0B */ + SIOC_WP_TDM_SET_HW_CODING, /* 0x0C */ + + SIOC_WP_TDM_GET_FULL_CFG, /* 0x0D */ + + SIOC_WP_TDM_SET_EC_TAP, /* 0x0E */ + SIOC_WP_TDM_GET_EC_TAP, /* 0x0F */ + + SIOC_WP_TDM_ENABLE_RBS_EVENTS, /* 0x10 */ + SIOC_WP_TDM_DISABLE_RBS_EVENTS, /* 0x11 */ + SIOC_WP_TDM_WRITE_RBS_BITS, /* 0x12 */ + + SIOC_WP_TDM_GET_STATS, /* 0x13 */ + SIOC_WP_TDM_FLUSH_BUFFERS, /* 0x14 */ + + SIOC_WP_TDM_READ_EVENT, /* 0x15 */ + + SIOC_WP_TDM_SET_EVENT, /* 0x16 */ + + SIOC_WP_TDM_SET_RX_GAINS, /* 0x17 */ + SIOC_WP_TDM_SET_TX_GAINS, /* 0x18 */ + SIOC_WP_TDM_CLEAR_RX_GAINS, /* 0x19 */ + SIOC_WP_TDM_CLEAR_TX_GAINS, /* 0x1A */ + + SIOC_WP_TDM_GET_FE_ALARMS, /* 0x1B */ + + SIOC_WP_TDM_ENABLE_HWEC, /* 0x1C */ + SIOC_WP_TDM_DISABLE_HWEC, /* 0x1D */ + + SIOC_WP_TDM_SET_FE_STATUS, /* 0x1E */ + SIOC_WP_TDM_GET_FE_STATUS, /* 0x1F */ + + SIOC_WP_TDM_GET_HW_DTMF, /* 0x20 */ + + SIOC_WP_TDM_NOTSUPP /* */ + +}; + +#define SIOC_WP_TDM_GET_LINK_STATUS SIOC_WP_TDM_GET_FE_STATUS + +enum wanpipe_tdm_api_events { + WP_TDMAPI_EVENT_NONE, + WP_TDMAPI_EVENT_RBS, + WP_TDMAPI_EVENT_ALARM, + WP_TDMAPI_EVENT_DTMF, + WP_TDMAPI_EVENT_RM_DTMF, + WP_TDMAPI_EVENT_RXHOOK, + WP_TDMAPI_EVENT_RING, + WP_TDMAPI_EVENT_RING_DETECT, + WP_TDMAPI_EVENT_RING_TRIP_DETECT, + WP_TDMAPI_EVENT_TONE, + WP_TDMAPI_EVENT_TXSIG_KEWL, + WP_TDMAPI_EVENT_TXSIG_START, + WP_TDMAPI_EVENT_TXSIG_OFFHOOK, + WP_TDMAPI_EVENT_TXSIG_ONHOOK, + WP_TDMAPI_EVENT_ONHOOKTRANSFER, + WP_TDMAPI_EVENT_SETPOLARITY, + WP_TDMAPI_EVENT_BRI_CHAN_LOOPBACK, + WP_TDMAPI_EVENT_LINK_STATUS +}; + +#define WP_TDMAPI_EVENT_FE_ALARM WP_TDMAPI_EVENT_ALARM + + +#define WP_TDMAPI_EVENT_ENABLE 0x01 +#define WP_TDMAPI_EVENT_DISABLE 0x02 +#define WP_TDMAPI_EVENT_MODE_DECODE(mode) \ + ((mode) == WP_TDMAPI_EVENT_ENABLE) ? "Enable" : \ + ((mode) == WP_TDMAPI_EVENT_DISABLE) ? "Disable" : \ + "(Unknown mode)" + +#define WPTDM_A_BIT WAN_RBS_SIG_A +#define WPTDM_B_BIT WAN_RBS_SIG_B +#define WPTDM_C_BIT WAN_RBS_SIG_C +#define WPTDM_D_BIT WAN_RBS_SIG_D + +#define WP_TDMAPI_EVENT_RXHOOK_OFF 0x01 +#define WP_TDMAPI_EVENT_RXHOOK_ON 0x02 +#define WP_TDMAPI_EVENT_RXHOOK_DECODE(state) \ + ((state) == WP_TDMAPI_EVENT_RXHOOK_OFF) ? "Off-hook" : \ + ((state) == WP_TDMAPI_EVENT_RXHOOK_ON) ? "On-hook" : \ + "(Unknown state)" + +#define WP_TDMAPI_EVENT_RING_PRESENT 0x01 +#define WP_TDMAPI_EVENT_RING_STOP 0x02 +#define WP_TDMAPI_EVENT_RING_DECODE(state) \ + ((state) == WP_TDMAPI_EVENT_RING_PRESENT) ? "Ring Present" : \ + ((state) == WP_TDMAPI_EVENT_RING_STOP) ? "Ring Stop" : \ + "(Unknown state)" + +#define WP_TDMAPI_EVENT_RING_TRIP_PRESENT 0x01 +#define WP_TDMAPI_EVENT_RING_TRIP_STOP 0x02 +#define WP_TDMAPI_EVENT_RING_TRIP_DECODE(state) \ + ((state) == WP_TDMAPI_EVENT_RING_TRIP_PRESENT) ? "Ring Present" : \ + ((state) == WP_TDMAPI_EVENT_RING_TRIP_STOP) ? "Ring Stop" : \ + "(Unknown state)" +/*Link Status */ +#define WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED 0x01 +#define WP_TDMAPI_EVENT_LINK_STATUS_DISCONNECTED 0x02 +#define WP_TDMAPI_EVENT_LINK_STATUS_DECODE(status) \ + ((status) == WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED) ? "Connected" : \ + ((status) == WP_TDMAPI_EVENT_LINK_STATUS_DISCONNECTED) ? "Disconnected" : \ + "Unknown" +#define WP_TDMAPI_EVENT_TONE_DIAL 0x01 +#define WP_TDMAPI_EVENT_TONE_BUSY 0x02 +#define WP_TDMAPI_EVENT_TONE_RING 0x03 +#define WP_TDMAPI_EVENT_TONE_CONGESTION 0x04 + +/* BRI channels list */ +#define WAN_BRI_BCHAN1 0x01 +#define WAN_BRI_BCHAN2 0x02 +#define WAN_BRI_DCHAN 0x03 + + +typedef struct { + + u_int8_t type; + u_int8_t mode; + u_int32_t time_stamp; + u_int8_t channel; + u_int32_t chan_map; + u_int8_t span; + union { + struct { + u_int8_t alarm; + } te1_alarm; + struct { + u_int8_t rbs_bits; + } te1_rbs; + struct { + u_int8_t state; + u_int8_t sig; + } rm_hook; + struct { + u_int8_t state; + } rm_ring; + struct { + u_int8_t type; + } rm_tone; + struct { + u_int8_t digit; /* DTMF: digit */ + u_int8_t port; /* DTMF: SOUT/ROUT */ + u_int8_t type; /* DTMF: PRESET/STOP */ + } dtmf; + struct { + u_int16_t polarity; + u_int16_t ohttimer; + } rm_common; + struct{ + u_int16_t status; + } linkstatus; + } wp_tdm_api_event_u; +#define wp_tdm_api_event_type type +#define wp_tdm_api_event_mode mode +#define wp_tdm_api_event_alarm wp_tdm_api_event_u.te1_alarm.alarm +#define wp_tdm_api_event_alarm wp_tdm_api_event_u.te1_alarm.alarm +#define wp_tdm_api_event_rbs_bits wp_tdm_api_event_u.te1_rbs.rbs_bits +#define wp_tdm_api_event_hook_state wp_tdm_api_event_u.rm_hook.state +#define wp_tdm_api_event_hook_sig wp_tdm_api_event_u.rm_hook.sig +#define wp_tdm_api_event_ring_state wp_tdm_api_event_u.rm_ring.state +#define wp_tdm_api_event_tone_type wp_tdm_api_event_u.rm_tone.type +#define wp_tdm_api_event_dtmf_digit wp_tdm_api_event_u.dtmf.digit +#define wp_tdm_api_event_dtmf_type wp_tdm_api_event_u.dtmf.type +#define wp_tdm_api_event_dtmf_port wp_tdm_api_event_u.dtmf.port +#define wp_tdm_api_event_ohttimer wp_tdm_api_event_u.rm_common.ohttimer +#define wp_tdm_api_event_polarity wp_tdm_api_event_u.rm_common.polarity +#define wp_tdm_api_event_link_status wp_tdm_api_event_u.linkstatus.status +} wp_tdm_api_event_t; + +typedef struct { + union { + unsigned char reserved[16]; + }wp_rx_hdr_u; +} wp_tdm_api_rx_hdr_t; + +typedef struct { + wp_tdm_api_rx_hdr_t hdr; + unsigned char data[1]; +} wp_tdm_api_rx_element_t; + +typedef struct { + union { + struct { + unsigned char _rbs_rx_bits; + unsigned int _time_stamp; + }wp_tx; + unsigned char reserved[16]; + }wp_tx_hdr_u; +#define wp_api_time_stamp wp_tx_hdr_u.wp_tx._time_stamp +} wp_tdm_api_tx_hdr_t; + +typedef struct { + wp_tdm_api_tx_hdr_t hdr; + unsigned char data[1]; +} wp_tdm_api_tx_element_t; + + + +typedef struct wp_tdm_chan_stats +{ + unsigned int rx_packets; /* total packets received */ + unsigned int tx_packets; /* total packets transmitted */ + unsigned int rx_bytes; /* total bytes received */ + unsigned int tx_bytes; /* total bytes transmitted */ + unsigned int rx_errors; /* bad packets received */ + unsigned int tx_errors; /* packet transmit problems */ + unsigned int rx_dropped; /* no space in linux buffers */ + unsigned int tx_dropped; /* no space available in linux */ + unsigned int multicast; /* multicast packets received */ +#if !defined(__WINDOWS__) + unsigned int collisions; +#endif + /* detailed rx_errors: */ + unsigned int rx_length_errors; + unsigned int rx_over_errors; /* receiver ring buff overflow */ + unsigned int rx_crc_errors; /* recved pkt with crc error */ + unsigned int rx_frame_errors; /* recv'd frame alignment error */ +#if !defined(__WINDOWS__) + unsigned int rx_fifo_errors; /* recv'r fifo overrun */ +#endif + unsigned int rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ +#if !defined(__WINDOWS__) + unsigned int tx_aborted_errors; + unsigned int tx_carrier_errors; +#endif + unsigned int tx_fifo_errors; + unsigned int tx_heartbeat_errors; + unsigned int tx_window_errors; + +}wp_tdm_chan_stats_t; + + + +typedef struct wanpipe_tdm_api_cmd{ + unsigned int cmd; + unsigned int hw_tdm_coding; /* Set/Get HW TDM coding: uLaw muLaw */ + unsigned int hw_mtu_mru; /* Set/Get HW TDM MTU/MRU */ + unsigned int usr_period; /* Set/Get User Period in ms */ + unsigned int tdm_codec; /* Set/Get TDM Codec: SLinear */ + unsigned int power_level; /* Set/Get Power level treshold */ + unsigned int rx_disable; /* Enable/Disable Rx */ + unsigned int tx_disable; /* Enable/Disable Tx */ + unsigned int usr_mtu_mru; /* Set/Get User TDM MTU/MRU */ + unsigned int ec_tap; /* Echo Cancellation Tap */ + unsigned int rbs_poll; /* Enable/Disable RBS Polling */ + unsigned int rbs_rx_bits; /* Rx RBS Bits */ + unsigned int rbs_tx_bits; /* Tx RBS Bits */ + unsigned int hdlc; /* HDLC based device */ + unsigned int idle_flag; /* IDLE flag to Tx */ + unsigned int fe_alarms; /* FE Alarms detected */ + wp_tdm_chan_stats_t stats; /* TDM Statistics */ + /* Do NOT add anything above this! Important for binary backward compatibility. */ + wp_tdm_api_event_t event; /* TDM Event */ + unsigned int data_len; + void *data; + unsigned char fe_status; /* FE status - Connected or Disconnected */ + unsigned int hw_dtmf; /* HW DTMF enabled */ +}wanpipe_tdm_api_cmd_t; + +typedef struct wanpipe_tdm_api_event{ + int (*wp_rbs_event)(sng_fd_t fd, unsigned char rbs_bits); + int (*wp_dtmf_event)(sng_fd_t fd, unsigned char dtmf, unsigned char type, unsigned char port); + int (*wp_rxhook_event)(sng_fd_t fd, unsigned char hook_state); + int (*wp_ring_detect_event)(sng_fd_t fd, unsigned char ring_state); + int (*wp_ring_trip_detect_event)(sng_fd_t fd, unsigned char ring_state); + int (*wp_fe_alarm_event)(sng_fd_t fd, unsigned char fe_alarm_event); + int (*wp_link_status_event)(sng_fd_t fd, unsigned char link_status_event); +}wanpipe_tdm_api_event_t; + +typedef struct wanpipe_tdm_api{ + wanpipe_tdm_api_cmd_t wp_tdm_cmd; + wanpipe_tdm_api_event_t wp_tdm_event; +}wanpipe_tdm_api_t; + + +#endif diff --git a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c new file mode 100644 index 0000000000..0ab9553307 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c @@ -0,0 +1,1232 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "ftmod_zt.h" + +/** + * \brief Zaptel globals + */ +static struct { + uint32_t codec_ms; + uint32_t wink_ms; + uint32_t flash_ms; + uint32_t eclevel; + uint32_t etlevel; + float rxgain; + float txgain; +} zt_globals; + +/** + * \brief General IOCTL codes + */ +struct ioctl_codes { + int GET_BLOCKSIZE; + int SET_BLOCKSIZE; + int FLUSH; + int SYNC; + int GET_PARAMS; + int SET_PARAMS; + int HOOK; + int GETEVENT; + int IOMUX; + int SPANSTAT; + int MAINT; + int GETCONF; + int SETCONF; + int CONFLINK; + int CONFDIAG; + int GETGAINS; + int SETGAINS; + int SPANCONFIG; + int CHANCONFIG; + int SET_BUFINFO; + int GET_BUFINFO; + int AUDIOMODE; + int ECHOCANCEL; + int HDLCRAWMODE; + int HDLCFCSMODE; + int SPECIFY; + int SETLAW; + int SETLINEAR; + int GETCONFMUTE; + int ECHOTRAIN; + int SETTXBITS; + int GETRXBITS; +}; + +/** + * \brief Zaptel IOCTL codes + */ +static struct ioctl_codes zt_ioctl_codes = { + .GET_BLOCKSIZE = ZT_GET_BLOCKSIZE, + .SET_BLOCKSIZE = ZT_SET_BLOCKSIZE, + .FLUSH = ZT_FLUSH, + .SYNC = ZT_SYNC, + .GET_PARAMS = ZT_GET_PARAMS, + .SET_PARAMS = ZT_SET_PARAMS, + .HOOK = ZT_HOOK, + .GETEVENT = ZT_GETEVENT, + .IOMUX = ZT_IOMUX, + .SPANSTAT = ZT_SPANSTAT, + .MAINT = ZT_MAINT, + .GETCONF = ZT_GETCONF, + .SETCONF = ZT_SETCONF, + .CONFLINK = ZT_CONFLINK, + .CONFDIAG = ZT_CONFDIAG, + .GETGAINS = ZT_GETGAINS, + .SETGAINS = ZT_SETGAINS, + .SPANCONFIG = ZT_SPANCONFIG, + .CHANCONFIG = ZT_CHANCONFIG, + .SET_BUFINFO = ZT_SET_BUFINFO, + .GET_BUFINFO = ZT_GET_BUFINFO, + .AUDIOMODE = ZT_AUDIOMODE, + .ECHOCANCEL = ZT_ECHOCANCEL, + .HDLCRAWMODE = ZT_HDLCRAWMODE, + .HDLCFCSMODE = ZT_HDLCFCSMODE, + .SPECIFY = ZT_SPECIFY, + .SETLAW = ZT_SETLAW, + .SETLINEAR = ZT_SETLINEAR, + .GETCONFMUTE = ZT_GETCONFMUTE, + .ECHOTRAIN = ZT_ECHOTRAIN, + .SETTXBITS = ZT_SETTXBITS, + .GETRXBITS = ZT_GETRXBITS +}; + +/** + * \brief Dahdi IOCTL codes + */ +static struct ioctl_codes dahdi_ioctl_codes = { + .GET_BLOCKSIZE = DAHDI_GET_BLOCKSIZE, + .SET_BLOCKSIZE = DAHDI_SET_BLOCKSIZE, + .FLUSH = DAHDI_FLUSH, + .SYNC = DAHDI_SYNC, + .GET_PARAMS = DAHDI_GET_PARAMS, + .SET_PARAMS = DAHDI_SET_PARAMS, + .HOOK = DAHDI_HOOK, + .GETEVENT = DAHDI_GETEVENT, + .IOMUX = DAHDI_IOMUX, + .SPANSTAT = DAHDI_SPANSTAT, + .MAINT = DAHDI_MAINT, + .GETCONF = DAHDI_GETCONF, + .SETCONF = DAHDI_SETCONF, + .CONFLINK = DAHDI_CONFLINK, + .CONFDIAG = DAHDI_CONFDIAG, + .GETGAINS = DAHDI_GETGAINS, + .SETGAINS = DAHDI_SETGAINS, + .SPANCONFIG = DAHDI_SPANCONFIG, + .CHANCONFIG = DAHDI_CHANCONFIG, + .SET_BUFINFO = DAHDI_SET_BUFINFO, + .GET_BUFINFO = DAHDI_GET_BUFINFO, + .AUDIOMODE = DAHDI_AUDIOMODE, + .ECHOCANCEL = DAHDI_ECHOCANCEL, + .HDLCRAWMODE = DAHDI_HDLCRAWMODE, + .HDLCFCSMODE = DAHDI_HDLCFCSMODE, + .SPECIFY = DAHDI_SPECIFY, + .SETLAW = DAHDI_SETLAW, + .SETLINEAR = DAHDI_SETLINEAR, + .GETCONFMUTE = DAHDI_GETCONFMUTE, + .ECHOTRAIN = DAHDI_ECHOTRAIN, + .SETTXBITS = DAHDI_SETTXBITS, + .GETRXBITS = DAHDI_GETRXBITS +}; + +#define ZT_INVALID_SOCKET -1 +static struct ioctl_codes codes; +static const char *ctlpath = NULL; +static const char *chanpath = NULL; + +static const char dahdi_ctlpath[] = "/dev/dahdi/ctl"; +static const char dahdi_chanpath[] = "/dev/dahdi/channel"; + +static const char zt_ctlpath[] = "/dev/ftdm/ctl"; +static const char zt_chanpath[] = "/dev/ftdm/channel"; + +static ftdm_socket_t CONTROL_FD = ZT_INVALID_SOCKET; + +FIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event); +FIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event); + +/** + * \brief Initialises codec, and rx/tx gains + * \param g Structure for gains to be initialised + * \param rxgain RX gain value + * \param txgain TX gain value + * \param codec Codec + */ +static void zt_build_gains(struct zt_gains *g, float rxgain, float txgain, int codec) +{ + int j; + int k; + float linear_rxgain = pow(10.0, rxgain / 20.0); + float linear_txgain = pow(10.0, txgain / 20.0); + + switch (codec) { + case FTDM_CODEC_ALAW: + for (j = 0; j < (sizeof(g->receive_gain) / sizeof(g->receive_gain[0])); j++) { + if (rxgain) { + k = (int) (((float) alaw_to_linear(j)) * linear_rxgain); + if (k > 32767) k = 32767; + if (k < -32767) k = -32767; + g->receive_gain[j] = linear_to_alaw(k); + } else { + g->receive_gain[j] = j; + } + if (txgain) { + k = (int) (((float) alaw_to_linear(j)) * linear_txgain); + if (k > 32767) k = 32767; + if (k < -32767) k = -32767; + g->transmit_gain[j] = linear_to_alaw(k); + } else { + g->transmit_gain[j] = j; + } + } + break; + case FTDM_CODEC_ULAW: + for (j = 0; j < (sizeof(g->receive_gain) / sizeof(g->receive_gain[0])); j++) { + if (rxgain) { + k = (int) (((float) ulaw_to_linear(j)) * linear_rxgain); + if (k > 32767) k = 32767; + if (k < -32767) k = -32767; + g->receive_gain[j] = linear_to_ulaw(k); + } else { + g->receive_gain[j] = j; + } + if (txgain) { + k = (int) (((float) ulaw_to_linear(j)) * linear_txgain); + if (k > 32767) k = 32767; + if (k < -32767) k = -32767; + g->transmit_gain[j] = linear_to_ulaw(k); + } else { + g->transmit_gain[j] = j; + } + } + break; + } +} + +/** + * \brief Initialises a range of ftdmtel channels + * \param span FreeTDM span + * \param start Initial wanpipe channel number + * \param end Final wanpipe channel number + * \param type FreeTDM channel type + * \param name FreeTDM span name + * \param number FreeTDM span number + * \param cas_bits CAS bits + * \return number of spans configured + */ +static unsigned zt_open_range(ftdm_span_t *span, unsigned start, unsigned end, ftdm_chan_type_t type, char *name, char *number, unsigned char cas_bits) +{ + unsigned configured = 0, x; + zt_params_t ztp; + + memset(&ztp, 0, sizeof(ztp)); + + if (type == FTDM_CHAN_TYPE_CAS) { + ftdm_log(FTDM_LOG_DEBUG, "Configuring CAS channels with abcd == 0x%X\n", cas_bits); + } + for(x = start; x < end; x++) { + ftdm_channel_t *ftdmchan; + ftdm_socket_t sockfd = ZT_INVALID_SOCKET; + int len; + + sockfd = open(chanpath, O_RDWR); + if (sockfd != ZT_INVALID_SOCKET && ftdm_span_add_channel(span, sockfd, type, &ftdmchan) == FTDM_SUCCESS) { + + if (ioctl(sockfd, codes.SPECIFY, &x)) { + ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s chan %d fd %d (%s)\n", chanpath, x, sockfd, strerror(errno)); + close(sockfd); + continue; + } + + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) { + struct zt_bufferinfo binfo; + memset(&binfo, 0, sizeof(binfo)); + binfo.txbufpolicy = 0; + binfo.rxbufpolicy = 0; + binfo.numbufs = 32; + binfo.bufsize = 1024; + if (ioctl(sockfd, codes.SET_BUFINFO, &binfo)) { + ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd); + close(sockfd); + continue; + } + } + + if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO) { + struct zt_chanconfig cc; + memset(&cc, 0, sizeof(cc)); + cc.chan = cc.master = x; + + switch(type) { + case FTDM_CHAN_TYPE_FXS: + { + switch(span->start_type) { + case FTDM_ANALOG_START_KEWL: + cc.sigtype = ZT_SIG_FXOKS; + break; + case FTDM_ANALOG_START_LOOP: + cc.sigtype = ZT_SIG_FXOLS; + break; + case FTDM_ANALOG_START_GROUND: + cc.sigtype = ZT_SIG_FXOGS; + break; + default: + break; + } + } + break; + case FTDM_CHAN_TYPE_FXO: + { + switch(span->start_type) { + case FTDM_ANALOG_START_KEWL: + cc.sigtype = ZT_SIG_FXSKS; + break; + case FTDM_ANALOG_START_LOOP: + cc.sigtype = ZT_SIG_FXSLS; + break; + case FTDM_ANALOG_START_GROUND: + cc.sigtype = ZT_SIG_FXSGS; + break; + default: + break; + } + } + break; + default: + break; + } + + if (ioctl(CONTROL_FD, codes.CHANCONFIG, &cc)) { + ftdm_log(FTDM_LOG_WARNING, "this ioctl fails on older ftdmtel but is harmless if you used ztcfg\n[device %s chan %d fd %d (%s)]\n", chanpath, x, CONTROL_FD, strerror(errno)); + } + } + + if (type == FTDM_CHAN_TYPE_CAS) { + struct zt_chanconfig cc; + memset(&cc, 0, sizeof(cc)); + cc.chan = cc.master = x; + cc.sigtype = ZT_SIG_CAS; + cc.idlebits = cas_bits; + if (ioctl(CONTROL_FD, codes.CHANCONFIG, &cc)) { + ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d err:%s", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd, strerror(errno)); + close(sockfd); + continue; + } + } + + if (ftdmchan->type != FTDM_CHAN_TYPE_DQ921 && ftdmchan->type != FTDM_CHAN_TYPE_DQ931) { + len = zt_globals.codec_ms * 8; + if (ioctl(ftdmchan->sockfd, codes.SET_BLOCKSIZE, &len)) { + ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d err:%s\n", + chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd, strerror(errno)); + close(sockfd); + continue; + } + + ftdmchan->packet_len = len; + ftdmchan->effective_interval = ftdmchan->native_interval = ftdmchan->packet_len / 8; + + if (ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + ftdmchan->packet_len *= 2; + } + } + + if (ioctl(sockfd, codes.GET_PARAMS, &ztp) < 0) { + ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd); + close(sockfd); + continue; + } + + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) { + if ( + (ztp.sig_type != ZT_SIG_HDLCRAW) && + (ztp.sig_type != ZT_SIG_HDLCFCS) && + (ztp.sig_type != ZT_SIG_HARDHDLC) + ) { + ftdm_log(FTDM_LOG_ERROR, "Failure configuring device %s as FreeTDM device %d:%d fd:%d, hardware signaling is not HDLC, fix your Zap/DAHDI configuration!\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd); + close(sockfd); + continue; + } + } + + ftdm_log(FTDM_LOG_INFO, "configuring device %s channel %d as FreeTDM device %d:%d fd:%d\n", chanpath, x, ftdmchan->span_id, ftdmchan->chan_id, sockfd); + + ftdmchan->rate = 8000; + ftdmchan->physical_span_id = ztp.span_no; + ftdmchan->physical_chan_id = ztp.chan_no; + + if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO || type == FTDM_CHAN_TYPE_EM || type == FTDM_CHAN_TYPE_B) { + if (ztp.g711_type == ZT_G711_ALAW) { + ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_ALAW; + } else if (ztp.g711_type == ZT_G711_MULAW) { + ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_ULAW; + } else { + int type; + + if (ftdmchan->span->trunk_type == FTDM_TRUNK_E1) { + type = FTDM_CODEC_ALAW; + } else { + type = FTDM_CODEC_ULAW; + } + + ftdmchan->native_codec = ftdmchan->effective_codec = type; + + } + } + + ztp.wink_time = zt_globals.wink_ms; + ztp.flash_time = zt_globals.flash_ms; + + if (ioctl(sockfd, codes.SET_PARAMS, &ztp) < 0) { + ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd); + close(sockfd); + continue; + } + + if (!ftdm_strlen_zero(name)) { + ftdm_copy_string(ftdmchan->chan_name, name, sizeof(ftdmchan->chan_name)); + } + if (!ftdm_strlen_zero(number)) { + ftdm_copy_string(ftdmchan->chan_number, number, sizeof(ftdmchan->chan_number)); + } + configured++; + } else { + ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s\n", chanpath); + } + } + + + + return configured; +} + +/** + * \brief Initialises an freetdm ftdmtel span from a configuration string + * \param span FreeTDM span + * \param str Configuration string + * \param type FreeTDM span type + * \param name FreeTDM span name + * \param number FreeTDM span number + * \return Success or failure + */ +static FIO_CONFIGURE_SPAN_FUNCTION(zt_configure_span) +{ + + int items, i; + char *mydata, *item_list[10]; + char *ch, *mx; + unsigned char cas_bits = 0; + int channo; + int top = 0; + unsigned configured = 0; + + assert(str != NULL); + + + mydata = ftdm_strdup(str); + assert(mydata != NULL); + + + items = ftdm_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0]))); + + for(i = 0; i < items; i++) { + ch = item_list[i]; + + if (!(ch)) { + ftdm_log(FTDM_LOG_ERROR, "Invalid input\n"); + continue; + } + + channo = atoi(ch); + + if (channo < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid channel number %d\n", channo); + continue; + } + + if ((mx = strchr(ch, '-'))) { + mx++; + top = atoi(mx) + 1; + } else { + top = channo + 1; + } + + + if (top < 0) { + ftdm_log(FTDM_LOG_ERROR, "Invalid range number %d\n", top); + continue; + } + if (FTDM_CHAN_TYPE_CAS == type && ftdm_config_get_cas_bits(ch, &cas_bits)) { + ftdm_log(FTDM_LOG_ERROR, "Failed to get CAS bits in CAS channel\n"); + continue; + } + configured += zt_open_range(span, channo, top, type, name, number, cas_bits); + + } + + ftdm_safe_free(mydata); + + return configured; + +} + +/** + * \brief Process configuration variable for a ftdmtel profile + * \param category Wanpipe profile name + * \param var Variable name + * \param val Variable value + * \param lineno Line number from configuration file + * \return Success + */ +static FIO_CONFIGURE_FUNCTION(zt_configure) +{ + + int num; + float fnum; + + if (!strcasecmp(category, "defaults")) { + if (!strcasecmp(var, "codec_ms")) { + num = atoi(val); + if (num < 10 || num > 60) { + ftdm_log(FTDM_LOG_WARNING, "invalid codec ms at line %d\n", lineno); + } else { + zt_globals.codec_ms = num; + } + } else if (!strcasecmp(var, "wink_ms")) { + num = atoi(val); + if (num < 50 || num > 3000) { + ftdm_log(FTDM_LOG_WARNING, "invalid wink ms at line %d\n", lineno); + } else { + zt_globals.wink_ms = num; + } + } else if (!strcasecmp(var, "flash_ms")) { + num = atoi(val); + if (num < 50 || num > 3000) { + ftdm_log(FTDM_LOG_WARNING, "invalid flash ms at line %d\n", lineno); + } else { + zt_globals.flash_ms = num; + } + } else if (!strcasecmp(var, "echo_cancel_level")) { + num = atoi(val); + if (num < 0 || num > 256) { + ftdm_log(FTDM_LOG_WARNING, "invalid echo can val at line %d\n", lineno); + } else { + zt_globals.eclevel = num; + } + + } else if (!strcasecmp(var, "rxgain")) { + fnum = (float)atof(val); + if (fnum < -100.0 || fnum > 100.0) { + ftdm_log(FTDM_LOG_WARNING, "invalid rxgain val at line %d\n", lineno); + } else { + zt_globals.rxgain = fnum; + ftdm_log(FTDM_LOG_INFO, "Setting rxgain val to %f\n", fnum); + } + + } else if (!strcasecmp(var, "txgain")) { + fnum = (float)atof(val); + if (fnum < -100.0 || fnum > 100.0) { + ftdm_log(FTDM_LOG_WARNING, "invalid txgain val at line %d\n", lineno); + } else { + zt_globals.txgain = fnum; + ftdm_log(FTDM_LOG_INFO, "Setting txgain val to %f\n", fnum); + } + + } + } + + return FTDM_SUCCESS; +} + +/** + * \brief Opens a ftdmtel channel + * \param ftdmchan Channel to open + * \return Success or failure + */ +static FIO_OPEN_FUNCTION(zt_open) +{ + ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL); + + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921 || ftdmchan->type == FTDM_CHAN_TYPE_DQ931) { + ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_NONE; + } else { + int blocksize = zt_globals.codec_ms * (ftdmchan->rate / 1000); + int err; + if ((err = ioctl(ftdmchan->sockfd, codes.SET_BLOCKSIZE, &blocksize))) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + return FTDM_FAIL; + } else { + ftdmchan->effective_interval = ftdmchan->native_interval; + ftdmchan->packet_len = blocksize; + ftdmchan->native_codec = ftdmchan->effective_codec; + } + + if (ftdmchan->type == FTDM_CHAN_TYPE_B) { + int one = 1; + if (ioctl(ftdmchan->sockfd, codes.AUDIOMODE, &one)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + ftdm_log(FTDM_LOG_ERROR, "%s\n", ftdmchan->last_error); + return FTDM_FAIL; + } + } else if (ftdmchan->type == FTDM_CHAN_TYPE_FXS || ftdmchan->type == FTDM_CHAN_TYPE_FXO || ftdmchan->type == FTDM_CHAN_TYPE_EM) { + int len = zt_globals.eclevel; + if (ioctl(ftdmchan->sockfd, codes.ECHOCANCEL, &len)) { + ftdm_log(FTDM_LOG_WARNING, "Echo cancel not available for %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id); + //snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + //ftdm_log(FTDM_LOG_ERROR, "%s\n", ftdmchan->last_error); + //return FTDM_FAIL; + } else { + len = zt_globals.etlevel; + if (ioctl(ftdmchan->sockfd, codes.ECHOTRAIN, &len)) { + ftdm_log(FTDM_LOG_WARNING, "Echo training not available for %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id); + //snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + //ftdm_log(FTDM_LOG_ERROR, "%s\n", ftdmchan->last_error); + //return FTDM_FAIL; + } + } + } + if(zt_globals.rxgain || zt_globals.txgain) { + struct zt_gains gains; + memset(&gains, 0, sizeof(gains)); + + gains.chan_no = ftdmchan->physical_chan_id; + zt_build_gains(&gains, zt_globals.rxgain, zt_globals.txgain, ftdmchan->native_codec); + + if(zt_globals.rxgain) + ftdm_log(FTDM_LOG_INFO, "Setting rxgain to %f on channel %d\n", zt_globals.rxgain, gains.chan_no); + + if(zt_globals.txgain) + ftdm_log(FTDM_LOG_INFO, "Setting txgain to %f on channel %d\n", zt_globals.txgain, gains.chan_no); + + if (ioctl(ftdmchan->sockfd, codes.SETGAINS, &gains) < 0) { + ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, ftdmchan->sockfd); + } + } + + int len = zt_globals.eclevel; + ftdm_log(FTDM_LOG_INFO, "Setting echo cancel to %d taps for %d:%d\n", len, ftdmchan->span_id, ftdmchan->chan_id); + if (ioctl(ftdmchan->sockfd, codes.ECHOCANCEL, &len)) { + ftdm_log(FTDM_LOG_WARNING, "Echo cancel not available for %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id); + } else { + len = zt_globals.etlevel; + if (ioctl(ftdmchan->sockfd, codes.ECHOTRAIN, &len)) { + ftdm_log(FTDM_LOG_WARNING, "Echo training not available for %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id); + } + } + + } + return FTDM_SUCCESS; +} + +/** + * \brief Closes ftdmtel channel + * \param ftdmchan Channel to close + * \return Success + */ +static FIO_CLOSE_FUNCTION(zt_close) +{ + return FTDM_SUCCESS; +} + +/** + * \brief Executes an FreeTDM command on a ftdmtel channel + * \param ftdmchan Channel to execute command on + * \param command FreeTDM command to execute + * \param obj Object (unused) + * \return Success or failure + */ +static FIO_COMMAND_FUNCTION(zt_command) +{ + zt_params_t ztp; + int err = 0; + + memset(&ztp, 0, sizeof(ztp)); + + switch(command) { + case FTDM_COMMAND_ENABLE_ECHOCANCEL: + { + int level = FTDM_COMMAND_OBJ_INT; + err = ioctl(ftdmchan->sockfd, codes.ECHOCANCEL, &level); + FTDM_COMMAND_OBJ_INT = level; + } + case FTDM_COMMAND_DISABLE_ECHOCANCEL: + { + int level = 0; + err = ioctl(ftdmchan->sockfd, codes.ECHOCANCEL, &level); + FTDM_COMMAND_OBJ_INT = level; + } + break; + case FTDM_COMMAND_ENABLE_ECHOTRAIN: + { + int level = FTDM_COMMAND_OBJ_INT; + err = ioctl(ftdmchan->sockfd, codes.ECHOTRAIN, &level); + FTDM_COMMAND_OBJ_INT = level; + } + case FTDM_COMMAND_DISABLE_ECHOTRAIN: + { + int level = 0; + err = ioctl(ftdmchan->sockfd, codes.ECHOTRAIN, &level); + FTDM_COMMAND_OBJ_INT = level; + } + break; + case FTDM_COMMAND_OFFHOOK: + { + int command = ZT_OFFHOOK; + if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "OFFHOOK Failed"); + return FTDM_FAIL; + } + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK); + } + break; + case FTDM_COMMAND_ONHOOK: + { + int command = ZT_ONHOOK; + if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ONHOOK Failed"); + return FTDM_FAIL; + } + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK); + } + break; + case FTDM_COMMAND_FLASH: + { + int command = ZT_FLASH; + if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "FLASH Failed"); + return FTDM_FAIL; + } + } + break; + case FTDM_COMMAND_WINK: + { + int command = ZT_WINK; + if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "WINK Failed"); + return FTDM_FAIL; + } + } + break; + case FTDM_COMMAND_GENERATE_RING_ON: + { + int command = ZT_RING; + if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring Failed"); + return FTDM_FAIL; + } + ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING); + } + break; + case FTDM_COMMAND_GENERATE_RING_OFF: + { + int command = ZT_RINGOFF; + if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring-off failed"); + return FTDM_FAIL; + } + ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING); + } + break; + case FTDM_COMMAND_GET_INTERVAL: + { + + if (!(err = ioctl(ftdmchan->sockfd, codes.GET_BLOCKSIZE, &ftdmchan->packet_len))) { + ftdmchan->native_interval = ftdmchan->packet_len / 8; + if (ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + ftdmchan->packet_len *= 2; + } + FTDM_COMMAND_OBJ_INT = ftdmchan->native_interval; + } + } + break; + case FTDM_COMMAND_SET_INTERVAL: + { + int interval = FTDM_COMMAND_OBJ_INT; + int len = interval * 8; + + if (!(err = ioctl(ftdmchan->sockfd, codes.SET_BLOCKSIZE, &len))) { + ftdmchan->packet_len = len; + ftdmchan->effective_interval = ftdmchan->native_interval = ftdmchan->packet_len / 8; + + if (ftdmchan->effective_codec == FTDM_CODEC_SLIN) { + ftdmchan->packet_len *= 2; + } + } + } + break; + case FTDM_COMMAND_SET_CAS_BITS: + { + int bits = FTDM_COMMAND_OBJ_INT; + err = ioctl(ftdmchan->sockfd, codes.SETTXBITS, &bits); + } + break; + case FTDM_COMMAND_GET_CAS_BITS: + { + err = ioctl(ftdmchan->sockfd, codes.GETRXBITS, &ftdmchan->rx_cas_bits); + if (!err) { + FTDM_COMMAND_OBJ_INT = ftdmchan->rx_cas_bits; + } + } + break; + case FTDM_COMMAND_FLUSH_TX_BUFFERS: + { + int flushmode = ZT_FLUSH_WRITE; + err = ioctl(ftdmchan->sockfd, codes.FLUSH, &flushmode); + } + break; + case FTDM_COMMAND_FLUSH_RX_BUFFERS: + { + int flushmode = ZT_FLUSH_READ; + err = ioctl(ftdmchan->sockfd, codes.FLUSH, &flushmode); + } + break; + case FTDM_COMMAND_FLUSH_BUFFERS: + { + int flushmode = ZT_FLUSH_BOTH; + err = ioctl(ftdmchan->sockfd, codes.FLUSH, &flushmode); + } + break; + default: + err = FTDM_NOTIMPL; + break; + }; + + if (err && err != FTDM_NOTIMPL) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); + return FTDM_FAIL; + } + + + return err == 0 ? FTDM_SUCCESS : err; +} + +/** + * \brief Gets alarms from a ftdmtel Channel + * \param ftdmchan Channel to get alarms from + * \return Success or failure + */ +static FIO_GET_ALARMS_FUNCTION(zt_get_alarms) +{ + struct zt_spaninfo info; + + memset(&info, 0, sizeof(info)); + info.span_no = ftdmchan->physical_span_id; + + if (ioctl(CONTROL_FD, codes.SPANSTAT, &info)) { + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ioctl failed (%s)", strerror(errno)); + snprintf(ftdmchan->span->last_error, sizeof(ftdmchan->span->last_error), "ioctl failed (%s)", strerror(errno)); + return FTDM_FAIL; + } + + ftdmchan->alarm_flags = info.alarms; + + return FTDM_SUCCESS; +} + +/** + * \brief Waits for an event on a ftdmtel channel + * \param ftdmchan Channel to open + * \param flags Type of event to wait for + * \param to Time to wait (in ms) + * \return Success, failure or timeout + */ +static FIO_WAIT_FUNCTION(zt_wait) +{ + int32_t inflags = 0; + int result; + struct pollfd pfds[1]; + + if (*flags & FTDM_READ) { + inflags |= POLLIN; + } + + if (*flags & FTDM_WRITE) { + inflags |= POLLOUT; + } + + if (*flags & FTDM_EVENTS) { + inflags |= POLLPRI; + } + + + memset(&pfds[0], 0, sizeof(pfds[0])); + pfds[0].fd = ftdmchan->sockfd; + pfds[0].events = inflags; + result = poll(pfds, 1, to); + *flags = 0; + + if (pfds[0].revents & POLLERR) { + result = -1; + } + + if (result > 0) { + inflags = pfds[0].revents; + } + + *flags = FTDM_NO_FLAGS; + + if (result < 0){ + snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Poll failed"); + return FTDM_FAIL; + } + + if (result == 0) { + return FTDM_TIMEOUT; + } + + if (inflags & POLLIN) { + *flags |= FTDM_READ; + } + + if (inflags & POLLOUT) { + *flags |= FTDM_WRITE; + } + + if (inflags & POLLPRI) { + *flags |= FTDM_EVENTS; + } + + return FTDM_SUCCESS; + +} + +/** + * \brief Checks for events on a ftdmtel span + * \param span Span to check for events + * \param ms Time to wait for event + * \return Success if event is waiting or failure if not + */ +FIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event) +{ + struct pollfd pfds[FTDM_MAX_CHANNELS_SPAN]; + uint32_t i, j = 0, k = 0; + int r; + + for(i = 1; i <= span->chan_count; i++) { + memset(&pfds[j], 0, sizeof(pfds[j])); + pfds[j].fd = span->channels[i]->sockfd; + pfds[j].events = POLLPRI; + j++; + } + + r = poll(pfds, j, ms); + + if (r == 0) { + return FTDM_TIMEOUT; + } else if (r < 0 || (pfds[i-1].revents & POLLERR)) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return FTDM_FAIL; + } + + for(i = 1; i <= span->chan_count; i++) { + if (pfds[i-1].revents & POLLPRI) { + ftdm_set_flag(span->channels[i], FTDM_CHANNEL_EVENT); + span->channels[i]->last_event_time = ftdm_current_time_in_ms(); + k++; + } + } + + if (!k) { + snprintf(span->last_error, sizeof(span->last_error), "no matching descriptor"); + } + + return k ? FTDM_SUCCESS : FTDM_FAIL; +} + +/** + * \brief Retrieves an event from a ftdmtel span + * \param span Span to retrieve event from + * \param event FreeTDM event to return + * \return Success or failure + */ +FIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event) +{ + uint32_t i, event_id = 0; + ftdm_oob_event_t zt_event_id = 0; + + for(i = 1; i <= span->chan_count; i++) { + if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) { + ftdm_clear_flag(span->channels[i], FTDM_CHANNEL_EVENT); + if (ioctl(span->channels[i]->sockfd, codes.GETEVENT, &zt_event_id) == -1) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return FTDM_FAIL; + } + + switch(zt_event_id) { + case ZT_EVENT_RINGEROFF: + { + return FTDM_FAIL; + } + break; + case ZT_EVENT_RINGERON: + { + return FTDM_FAIL; + } + break; + case ZT_EVENT_RINGBEGIN: + { + event_id = FTDM_OOB_RING_START; + } + break; + case ZT_EVENT_ONHOOK: + { + event_id = FTDM_OOB_ONHOOK; + } + break; + case ZT_EVENT_WINKFLASH: + { + if (span->channels[i]->state == FTDM_CHANNEL_STATE_DOWN || span->channels[i]->state == FTDM_CHANNEL_STATE_DIALING) { + event_id = FTDM_OOB_WINK; + } else { + event_id = FTDM_OOB_FLASH; + } + } + break; + case ZT_EVENT_RINGOFFHOOK: + { + if (span->channels[i]->type == FTDM_CHAN_TYPE_FXS || (span->channels[i]->type == FTDM_CHAN_TYPE_EM && span->channels[i]->state != FTDM_CHANNEL_STATE_UP)) { + ftdm_set_flag_locked(span->channels[i], FTDM_CHANNEL_OFFHOOK); + event_id = FTDM_OOB_OFFHOOK; + } else if (span->channels[i]->type == FTDM_CHAN_TYPE_FXO) { + event_id = FTDM_OOB_RING_START; + } + } + break; + case ZT_EVENT_ALARM: + { + event_id = FTDM_OOB_ALARM_TRAP; + } + break; + case ZT_EVENT_NOALARM: + { + event_id = FTDM_OOB_ALARM_CLEAR; + } + break; + case ZT_EVENT_BITSCHANGED: + { + event_id = FTDM_OOB_CAS_BITS_CHANGE; + int bits = 0; + int err = ioctl(span->channels[i]->sockfd, codes.GETRXBITS, &bits); + if (err) { + return FTDM_FAIL; + } + span->channels[i]->rx_cas_bits = bits; + } + break; + default: + { + ftdm_log(FTDM_LOG_WARNING, "Unhandled event %d for %d:%d\n", zt_event_id, span->span_id, i); + event_id = FTDM_OOB_INVALID; + } + break; + } + + span->channels[i]->last_event_time = 0; + span->event_header.e_type = FTDM_EVENT_OOB; + span->event_header.enum_id = event_id; + span->event_header.channel = span->channels[i]; + *event = &span->event_header; + return FTDM_SUCCESS; + } + } + + return FTDM_FAIL; + +} + +/** + * \brief Reads data from a ftdmtel channel + * \param ftdmchan Channel to read from + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success, failure or timeout + */ +static FIO_READ_FUNCTION(zt_read) +{ + ftdm_ssize_t r = 0; + int errs = 0; + + while (errs++ < 30) { + if ((r = read(ftdmchan->sockfd, data, *datalen)) > 0) { + break; + } + ftdm_sleep(10); + if (r == 0) { + errs--; + } + } + + if (r > 0) { + *datalen = r; + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) { + *datalen -= 2; + } + return FTDM_SUCCESS; + } + + return r == 0 ? FTDM_TIMEOUT : FTDM_FAIL; +} + +/** + * \brief Writes data to a ftdmtel channel + * \param ftdmchan Channel to write to + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success or failure + */ +static FIO_WRITE_FUNCTION(zt_write) +{ + ftdm_ssize_t w = 0; + ftdm_size_t bytes = *datalen; + + if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) { + memset(data+bytes, 0, 2); + bytes += 2; + } + + w = write(ftdmchan->sockfd, data, bytes); + + if (w >= 0) { + *datalen = w; + return FTDM_SUCCESS; + } + + return FTDM_FAIL; +} + +/** + * \brief Destroys a ftdmtel Channel + * \param ftdmchan Channel to destroy + * \return Success + */ +static FIO_CHANNEL_DESTROY_FUNCTION(zt_channel_destroy) +{ + close(ftdmchan->sockfd); + ftdmchan->sockfd = ZT_INVALID_SOCKET; + + return FTDM_SUCCESS; +} + +/** + * \brief Global FreeTDM IO interface for ftdmtel + */ +static ftdm_io_interface_t zt_interface; + +/** + * \brief Loads ftdmtel IO module + * \param fio FreeTDM IO interface + * \return Success or failure + */ +static FIO_IO_LOAD_FUNCTION(zt_init) +{ + assert(fio != NULL); + struct stat statbuf; + memset(&zt_interface, 0, sizeof(zt_interface)); + memset(&zt_globals, 0, sizeof(zt_globals)); + + if (!stat(zt_ctlpath, &statbuf)) { + ftdm_log(FTDM_LOG_NOTICE, "Using Zaptel control device\n"); + ctlpath = zt_ctlpath; + chanpath = zt_chanpath; + memcpy(&codes, &zt_ioctl_codes, sizeof(codes)); + } else if (!stat(dahdi_ctlpath, &statbuf)) { + ftdm_log(FTDM_LOG_NOTICE, "Using DAHDI control device\n"); + ctlpath = dahdi_ctlpath; + chanpath = dahdi_chanpath; + memcpy(&codes, &dahdi_ioctl_codes, sizeof(codes)); + } else { + ftdm_log(FTDM_LOG_ERROR, "No DAHDI or Zap control device found in /dev/\n"); + return FTDM_FAIL; + } + if ((CONTROL_FD = open(ctlpath, O_RDWR)) < 0) { + ftdm_log(FTDM_LOG_ERROR, "Cannot open control device %s: %s\n", ctlpath, strerror(errno)); + return FTDM_FAIL; + } + + zt_globals.codec_ms = 20; + zt_globals.wink_ms = 150; + zt_globals.flash_ms = 750; + zt_globals.eclevel = 64; + zt_globals.etlevel = 0; + + zt_interface.name = "zt"; + zt_interface.configure = zt_configure; + zt_interface.configure_span = zt_configure_span; + zt_interface.open = zt_open; + zt_interface.close = zt_close; + zt_interface.command = zt_command; + zt_interface.wait = zt_wait; + zt_interface.read = zt_read; + zt_interface.write = zt_write; + zt_interface.poll_event = zt_poll_event; + zt_interface.next_event = zt_next_event; + zt_interface.channel_destroy = zt_channel_destroy; + zt_interface.get_alarms = zt_get_alarms; + *fio = &zt_interface; + + return FTDM_SUCCESS; +} + +/** + * \brief Unloads ftdmtel IO module + * \return Success + */ +static FIO_IO_UNLOAD_FUNCTION(zt_destroy) +{ + close(CONTROL_FD); + memset(&zt_interface, 0, sizeof(zt_interface)); + return FTDM_SUCCESS; +} + +/** + * \brief FreeTDM ftdmtel IO module definition + */ +ftdm_module_t ftdm_module = { + "zt", + zt_init, + zt_destroy, +}; + +/* 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: + */ diff --git a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.h b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.h new file mode 100644 index 0000000000..cf5479dba1 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.h @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 FTDM_ZT_H +#define FTDM_ZT_H +#include "freetdm.h" +#include +#include + +#ifdef __sun +#include +#include +#include +#endif + +/* Hardware interface structures and defines */ +/* Based on documentation of the structures required for the hardware interface */ +/* from http://wiki.freeswitch.org/wiki/Zapata_ftdmtel_interface */ + +/* Structures */ + +/* Used with ioctl: ZT_GET_PARAMS and ZT_SET_PARAMS */ +struct zt_params { + int chan_no; /* Channel Number */ + int span_no; /* Span Number */ + int chan_position; /* Channel Position */ + int sig_type; /* Signal Type (read-only) */ + int sig_cap; /* Signal Cap (read-only) */ + int receive_offhook; /* Receive is offhook (read-only) */ + int receive_bits; /* Number of bits in receive (read-only) */ + int transmit_bits; /* Number of bits in transmit (read-only) */ + int transmit_hook_sig; /* Transmit Hook Signal (read-only) */ + int receive_hook_sig; /* Receive Hook Signal (read-only) */ + int g711_type; /* Member of zt_g711_t (read-only) */ + int idlebits; /* bits for the idle state (read-only) */ + char chan_name[40]; /* Channel Name */ + int prewink_time; + int preflash_time; + int wink_time; + int flash_time; + int start_time; + int receive_wink_time; + int receive_flash_time; + int debounce_time; + int pulse_break_time; + int pulse_make_time; + int pulse_after_time; + /* latest version of this struct include chan_alarms field */ + uint32_t chan_alarms; +}; + +typedef struct zt_params zt_params_t; + +/* Used with ioctl: ZT_CONFLINK, ZT_GETCONF and ZT_SETCONF */ +struct zt_confinfo { + int chan_no; /* Channel Number, 0 for current */ + int conference_number; + int conference_mode; +}; + +/* Used with ioctl: ZT_GETGAINS and ZT_SETGAINS */ +struct zt_gains { + int chan_no; /* Channel Number, 0 for current */ + unsigned char receive_gain[256]; /* Receive gain table */ + unsigned char transmit_gain[256]; /* Transmit gain table */ +}; + +/* Used with ioctl: ZT_SPANSTAT */ +struct zt_spaninfo { + int span_no; /* span number (-1 to use name) */ + char name[20]; /* Name of span */ + char description[40]; /* Description of span */ + int alarms; /* alarms status */ + int transmit_level; /* Transmit level */ + int receive_level; /* Receive level */ + int bpv_count; /* Current BPV count */ + int crc4_count; /* Current CRC4 error count */ + int ebit_count; /* Current E-bit error count */ + int fas_count; /* Current FAS error count */ + int irq_misses; /* Current IRQ misses */ + int sync_src; /* Span # of sync source (0 = free run) */ + int configured_chan_count; /* Count of channels configured on the span */ + int channel_count; /* Total count of channels on the span */ + int span_count; /* Total count of ftdmtel spans on the system*/ + /* end v1 of the struct */ + /* as long as we don't use the fields below we should be ok regardless of the ftdmtel/dahdi version */ + int lbo; /* Line Build Out */ + int lineconfig; /* framing/coding */ + /* end of v2 of the struct */ + char lboname[40]; /* Line Build Out in text form */ + char location[40]; /* span's device location in system */ + char manufacturer[40]; /* manufacturer of span's device */ + char devicetype[40]; /* span's device type */ + int irq; /* span's device IRQ */ + int linecompat; /* signaling modes possible on this span */ + char spantype[6]; /* type of span in text form */ +}; + +struct zt_maintinfo { + int span_no; /* span number */ + int command; /* Maintenance mode to set (from zt_maintenance_mode_t) */ +}; + +struct zt_lineconfig { +/* Used in ZT_SPANCONFIG */ + int span; /* Which span number (0 to use name) */ + char name[20]; /* Name of span to use */ + int lbo; /* line build-outs */ + int lineconfig; /* line config parameters (framing, coding) */ + int sync; /* what level of sync source we are */ +}; + +struct zt_chanconfig { +/* Used in ZT_CHANCONFIG */ + int chan; /* Channel we're applying this to (0 to use name) */ + char name[40]; /* Name of channel to use */ + int sigtype; /* Signal type */ + int deflaw; /* Default law (ZT_LAW_DEFAULT, ZT_LAW_MULAW, or ZT_LAW_ALAW */ + int master; /* Master channel if sigtype is ZT_SLAVE */ + int idlebits; /* Idle bits (if this is a CAS channel) or channel to monitor (if this is DACS channel) */ + char netdev_name[16]; /* name for the hdlc network device */ +}; + +struct zt_bufferinfo { +/* used in ZT_SET_BUFINFO and ZT_GET_BUFINFO */ + int txbufpolicy; /* Policy for handling receive buffers */ + int rxbufpolicy; /* Policy for handling receive buffers */ + int numbufs; /* How many buffers to use */ + int bufsize; /* How big each buffer is */ + int readbufs; /* How many read buffers are full (read-only) */ + int writebufs; /* How many write buffers are full (read-only) */ +}; + +/* Enumerations */ + +/* Values in zt_params structure for member g711_type */ +typedef enum { + ZT_G711_DEFAULT = 0, /* Default mulaw/alaw from the span */ + ZT_G711_MULAW = 1, + ZT_G711_ALAW = 2 +} zt_g711_t; + +typedef enum { + ZT_EVENT_NONE = 0, + ZT_EVENT_ONHOOK = 1, + ZT_EVENT_RINGOFFHOOK = 2, + ZT_EVENT_WINKFLASH = 3, + ZT_EVENT_ALARM = 4, + ZT_EVENT_NOALARM = 5, + ZT_EVENT_ABORT = 6, + ZT_EVENT_OVERRUN = 7, + ZT_EVENT_BADFCS = 8, + ZT_EVENT_DIALCOMPLETE = 9, + ZT_EVENT_RINGERON = 10, + ZT_EVENT_RINGEROFF = 11, + ZT_EVENT_HOOKCOMPLETE = 12, + ZT_EVENT_BITSCHANGED = 13, + ZT_EVENT_PULSE_START = 14, + ZT_EVENT_TIMER_EXPIRED = 15, + ZT_EVENT_TIMER_PING = 16, + ZT_EVENT_POLARITY = 17, + ZT_EVENT_RINGBEGIN = 18 +} zt_event_t; + +typedef enum { + ZT_FLUSH_READ = 1, + ZT_FLUSH_WRITE = 2, + ZT_FLUSH_BOTH = (ZT_FLUSH_READ | ZT_FLUSH_WRITE), + ZT_FLUSH_EVENT = 4, + ZT_FLUSH_ALL = (ZT_FLUSH_READ | ZT_FLUSH_WRITE | ZT_FLUSH_EVENT) +} zt_flush_t; + +typedef enum { + ZT_IOMUX_READ = 1, + ZT_IOMUX_WRITE = 2, + ZT_IOMUX_WRITEEMPTY = 4, + ZT_IOMUX_SIGEVENT = 8, + ZT_IOMUX_NOWAIT = 256 +} zt_iomux_t; + +typedef enum { + ZT_ONHOOK = 0, + ZT_OFFHOOK = 1, + ZT_WINK = 2, + ZT_FLASH = 3, + ZT_START = 4, + ZT_RING = 5, + ZT_RINGOFF = 6 +} zt_hookstate_t; + +typedef enum { + ZT_MAINT_NONE = 0, /* Normal Mode */ + ZT_MAINT_LOCALLOOP = 1, /* Local Loopback */ + ZT_MAINT_REMOTELOOP = 2, /* Remote Loopback */ + ZT_MAINT_LOOPUP = 3, /* Send Loopup Code */ + ZT_MAINT_LOOPDOWN = 4, /* Send Loopdown Code */ + ZT_MAINT_LOOPSTOP = 5 /* Stop Sending Loop Codes */ +} zt_maintenance_mode_t; + +typedef enum { +/* Signalling type */ +ZT_SIG_NONE = 0, /* chan not configured. */ + +ZT_SIG_FXSLS = ((1 << 0) | (1 << 13)), /* FXS, Loopstart */ +ZT_SIG_FXSGS = ((1 << 1) | (1 << 13)), /* FXS, Groundstart */ +ZT_SIG_FXSKS = ((1 << 2) | (1 << 13)), /* FXS, Kewlstart */ +ZT_SIG_FXOLS = ((1 << 3) | (1 << 12)), /* FXO, Loopstart */ +ZT_SIG_FXOGS = ((1 << 4) | (1 << 12)), /* FXO, Groupstart */ +ZT_SIG_FXOKS = ((1 << 5) | (1 << 12)), /* FXO, Kewlstart */ +ZT_SIG_EM = (1 << 6), /* E&M */ +ZT_SIG_CLEAR = (1 << 7), +ZT_SIG_HDLCRAW = ((1 << 8) | ZT_SIG_CLEAR), +ZT_SIG_HDLCFCS = ((1 << 9) | ZT_SIG_HDLCRAW), +ZT_SIG_CAS = (1 << 15), +ZT_SIG_HARDHDLC = ((1 << 19) | ZT_SIG_CLEAR), +} zt_sigtype_t; + +typedef enum { +ZT_DBIT = 1, +ZT_CBIT = 2, +ZT_BBIT = 4, +ZT_ABIT = 8 +} zt_cas_bit_t; + +/* Defines */ + +#define ZT_MAX_BLOCKSIZE 8192 +#define ZT_DEFAULT_MTU_MRU 2048 + +/* ioctl defines */ + +#define ZT_CODE 'J' +#define DAHDI_CODE 0xDA + + +#define ZT_GET_BLOCKSIZE _IOR (ZT_CODE, 1, int) /* Get Transfer Block Size. */ +#define ZT_SET_BLOCKSIZE _IOW (ZT_CODE, 2, int) /* Set Transfer Block Size. */ +#define ZT_FLUSH _IOW (ZT_CODE, 3, int) /* Flush Buffer(s) and stop I/O */ +#define ZT_SYNC _IOW (ZT_CODE, 4, int) /* Wait for Write to Finish */ +#define ZT_GET_PARAMS _IOR (ZT_CODE, 5, struct zt_params) /* Get channel parameters */ +#define ZT_SET_PARAMS _IOW (ZT_CODE, 6, struct zt_params) /* Set channel parameters */ +#define ZT_HOOK _IOW (ZT_CODE, 7, int) /* Set Hookswitch Status */ +#define ZT_GETEVENT _IOR (ZT_CODE, 8, int) /* Get Signalling Event */ +#define ZT_IOMUX _IOWR (ZT_CODE, 9, int) /* Wait for something to happen (IO Mux) */ +#define ZT_SPANSTAT _IOWR (ZT_CODE, 10, struct zt_spaninfo) /* Get Span Status */ +#define ZT_MAINT _IOW (ZT_CODE, 11, struct zt_maintinfo)/* Set Maintenance Mode for a span */ +#define ZT_GETCONF _IOWR (ZT_CODE, 12, struct zt_confinfo) /* Get Conference Mode */ +#define ZT_SETCONF _IOWR (ZT_CODE, 13, struct zt_confinfo) /* Set Conference Mode */ +#define ZT_CONFLINK _IOW (ZT_CODE, 14, struct zt_confinfo) /* Setup or Remove Conference Link */ +#define ZT_CONFDIAG _IOR (ZT_CODE, 15, int) /* Display Conference Diagnostic Information on Console */ + +#define ZT_GETGAINS _IOWR (ZT_CODE, 16, struct zt_gains) /* Get Channel audio gains */ +#define ZT_SETGAINS _IOWR (ZT_CODE, 17, struct zt_gains) /* Set Channel audio gains */ +#define ZT_SPANCONFIG _IOW (ZT_CODE, 18, struct zt_lineconfig)/* Set Line (T1) Configurations and start system */ +#define ZT_CHANCONFIG _IOW (ZT_CODE, 19, struct zt_chanconfig)/* Set Channel Configuration */ +#define ZT_SET_BUFINFO _IOW (ZT_CODE, 27, struct zt_bufferinfo)/* Set buffer policy */ +#define ZT_GET_BUFINFO _IOR (ZT_CODE, 28, struct zt_bufferinfo)/* Get current buffer info */ +#define ZT_AUDIOMODE _IOW (ZT_CODE, 32, int) /* Set a clear channel into audio mode */ +#define ZT_ECHOCANCEL _IOW (ZT_CODE, 33, int) /* Control Echo Canceller */ +#define ZT_HDLCRAWMODE _IOW (ZT_CODE, 36, int) /* Set a clear channel into HDLC w/out FCS checking/calculation mode */ +#define ZT_HDLCFCSMODE _IOW (ZT_CODE, 37, int) /* Set a clear channel into HDLC w/ FCS mode */ + +/* Specify a channel on /dev/ftdm/chan -- must be done before any other ioctl's and is only valid on /dev/ftdm/chan */ +#define ZT_SPECIFY _IOW (ZT_CODE, 38, int) + +/* Temporarily set the law on a channel to ZT_LAW_DEFAULT, ZT_LAW_ALAW, or ZT_LAW_MULAW. Is reset on close. */ +#define ZT_SETLAW _IOW (ZT_CODE, 39, int) + +/* Temporarily set the channel to operate in linear mode when non-zero or default law if 0 */ +#define ZT_SETLINEAR _IOW (ZT_CODE, 40, int) + +#define ZT_GETCONFMUTE _IOR (ZT_CODE, 49, int) /* Get Conference to mute mode */ +#define ZT_ECHOTRAIN _IOW (ZT_CODE, 50, int) /* Control Echo Trainer */ + +/* Set/Get CAS bits */ +#define ZT_SETTXBITS _IOW (ZT_CODE, 43, int) +#define ZT_GETRXBITS _IOR (ZT_CODE, 45, int) + +#define DAHDI_GET_BLOCKSIZE _IOR (DAHDI_CODE, 1, int) /* Get Transfer Block Size. */ +#define DAHDI_SET_BLOCKSIZE _IOW (DAHDI_CODE, 1, int) /* Set Transfer Block Size. */ +#define DAHDI_FLUSH _IOW (DAHDI_CODE, 3, int) /* Flush Buffer(s) and stop I/O */ +#define DAHDI_SYNC _IO (DAHDI_CODE, 4) /* Wait for Write to Finish */ +#define DAHDI_GET_PARAMS _IOR (DAHDI_CODE, 5, struct zt_params) /* Get channel parameters */ +#define DAHDI_SET_PARAMS _IOW (DAHDI_CODE, 5, struct zt_params) /* Set channel parameters */ +#define DAHDI_HOOK _IOW (DAHDI_CODE, 7, int) /* Set Hookswitch Status */ +#define DAHDI_GETEVENT _IOR (DAHDI_CODE, 8, int) /* Get Signalling Event */ +#define DAHDI_IOMUX _IOWR (DAHDI_CODE, 9, int) /* Wait for something to happen (IO Mux) */ +#define DAHDI_SPANSTAT _IOWR (DAHDI_CODE, 10, struct zt_spaninfo) /* Get Span Status */ +#define DAHDI_MAINT _IOW (DAHDI_CODE, 11, struct zt_maintinfo) /* Set Maintenance Mode for a span */ +#define DAHDI_GETCONF _IOR (DAHDI_CODE, 12, struct zt_confinfo) /* Get Conference Mode */ +#define DAHDI_SETCONF _IOW (DAHDI_CODE, 12, struct zt_confinfo) /* Set Conference Mode */ +#define DAHDI_CONFLINK _IOW (DAHDI_CODE, 14, struct zt_confinfo) /* Setup or Remove Conference Link */ +#define DAHDI_CONFDIAG _IOR (DAHDI_CODE, 15, int) /* Display Conference Diagnostic Information on Console */ + +#define DAHDI_GETGAINS _IOR (DAHDI_CODE, 16, struct zt_gains) /* Get Channel audio gains */ +#define DAHDI_SETGAINS _IOW (DAHDI_CODE, 16, struct zt_gains) /* Set Channel audio gains */ +#define DAHDI_SPANCONFIG _IOW (DAHDI_CODE, 18, struct zt_lineconfig)/* Set Line (T1) Configurations and start system */ +#define DAHDI_CHANCONFIG _IOW (DAHDI_CODE, 19, struct zt_chanconfig)/* Set Channel Configuration */ +#define DAHDI_SET_BUFINFO _IOW (DAHDI_CODE, 27, struct zt_bufferinfo)/* Set buffer policy */ +#define DAHDI_GET_BUFINFO _IOR (DAHDI_CODE, 27, struct zt_bufferinfo)/* Get current buffer info */ +#define DAHDI_AUDIOMODE _IOW (DAHDI_CODE, 32, int) /* Set a clear channel into audio mode */ +#define DAHDI_ECHOCANCEL _IOW (DAHDI_CODE, 33, int) /* Control Echo Canceller */ +#define DAHDI_HDLCRAWMODE _IOW (DAHDI_CODE, 36, int) /* Set a clear channel into HDLC w/out FCS checking/calculation mode */ +#define DAHDI_HDLCFCSMODE _IOW (DAHDI_CODE, 37, int) /* Set a clear channel into HDLC w/ FCS mode */ + +/* Specify a channel on /dev/dahdi/chan -- must be done before any other ioctl's and is only valid on /dev/dahdi/chan */ +#define DAHDI_SPECIFY _IOW (DAHDI_CODE, 38, int) + +/* Temporarily set the law on a channel to DAHDI_LAW_DEFAULT, DAHDI_LAW_ALAW, or DAHDI_LAW_MULAW. Is reset on close. */ +#define DAHDI_SETLAW _IOW (DAHDI_CODE, 39, int) + +/* Temporarily set the channel to operate in linear mode when non-zero or default law if 0 */ +#define DAHDI_SETLINEAR _IOW (DAHDI_CODE, 40, int) + +#define DAHDI_GETCONFMUTE _IOR (DAHDI_CODE, 49, int) /* Get Conference to mute mode */ +#define DAHDI_ECHOTRAIN _IOW (DAHDI_CODE, 50, int) /* Control Echo Trainer */ + +/* Set/Get CAS bits */ +#define DAHDI_SETTXBITS _IOW (DAHDI_CODE, 43, int) +#define DAHDI_GETRXBITS _IOR (DAHDI_CODE, 43, int) + + +#endif + +/* 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: + */ diff --git a/libs/freetdm/src/g711.c b/libs/freetdm/src/g711.c new file mode 100644 index 0000000000..37511c3a9c --- /dev/null +++ b/libs/freetdm/src/g711.c @@ -0,0 +1,104 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g711.c - A-law and u-law transcoding routines + * + * Written by Steve Underwood + * + * Copyright (C) 2006 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * $Id: g711.c,v 1.1 2006/06/07 15:46:39 steveu Exp $ + */ + +/*! \file */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#ifdef HAVE_TGMATH_H +#include +#endif +#endif + +#include "g711.h" + +/* Copied from the CCITT G.711 specification */ +static const uint8_t ulaw_to_alaw_table[256] = + { + 42, 43, 40, 41, 46, 47, 44, 45, 34, 35, 32, 33, 38, 39, 36, 37, + 58, 59, 56, 57, 62, 63, 60, 61, 50, 51, 48, 49, 54, 55, 52, 53, + 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 26, + 27, 24, 25, 30, 31, 28, 29, 18, 19, 16, 17, 22, 23, 20, 21, 106, + 104, 105, 110, 111, 108, 109, 98, 99, 96, 97, 102, 103, 100, 101, 122, 120, + 126, 127, 124, 125, 114, 115, 112, 113, 118, 119, 116, 117, 75, 73, 79, 77, + 66, 67, 64, 65, 70, 71, 68, 69, 90, 91, 88, 89, 94, 95, 92, 93, + 82, 82, 83, 83, 80, 80, 81, 81, 86, 86, 87, 87, 84, 84, 85, 85, + 170, 171, 168, 169, 174, 175, 172, 173, 162, 163, 160, 161, 166, 167, 164, 165, + 186, 187, 184, 185, 190, 191, 188, 189, 178, 179, 176, 177, 182, 183, 180, 181, + 138, 139, 136, 137, 142, 143, 140, 141, 130, 131, 128, 129, 134, 135, 132, 154, + 155, 152, 153, 158, 159, 156, 157, 146, 147, 144, 145, 150, 151, 148, 149, 234, + 232, 233, 238, 239, 236, 237, 226, 227, 224, 225, 230, 231, 228, 229, 250, 248, + 254, 255, 252, 253, 242, 243, 240, 241, 246, 247, 244, 245, 203, 201, 207, 205, + 194, 195, 192, 193, 198, 199, 196, 197, 218, 219, 216, 217, 222, 223, 220, 221, + 210, 210, 211, 211, 208, 208, 209, 209, 214, 214, 215, 215, 212, 212, 213, 213 + }; + +/* These transcoding tables are copied from the CCITT G.711 specification. To achieve + optimal results, do not change them. */ + +static const uint8_t alaw_to_ulaw_table[256] = + { + 42, 43, 40, 41, 46, 47, 44, 45, 34, 35, 32, 33, 38, 39, 36, 37, + 57, 58, 55, 56, 61, 62, 59, 60, 49, 50, 47, 48, 53, 54, 51, 52, + 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 5, + 26, 27, 24, 25, 30, 31, 28, 29, 18, 19, 16, 17, 22, 23, 20, 21, + 98, 99, 96, 97, 102, 103, 100, 101, 93, 93, 92, 92, 95, 95, 94, 94, + 116, 118, 112, 114, 124, 126, 120, 122, 106, 107, 104, 105, 110, 111, 108, 109, + 72, 73, 70, 71, 76, 77, 74, 75, 64, 65, 63, 63, 68, 69, 66, 67, + 86, 87, 84, 85, 90, 91, 88, 89, 79, 79, 78, 78, 82, 83, 80, 81, + 170, 171, 168, 169, 174, 175, 172, 173, 162, 163, 160, 161, 166, 167, 164, 165, + 185, 186, 183, 184, 189, 190, 187, 188, 177, 178, 175, 176, 181, 182, 179, 180, + 138, 139, 136, 137, 142, 143, 140, 141, 130, 131, 128, 129, 134, 135, 132, 133, + 154, 155, 152, 153, 158, 159, 156, 157, 146, 147, 144, 145, 150, 151, 148, 149, + 226, 227, 224, 225, 230, 231, 228, 229, 221, 221, 220, 220, 223, 223, 222, 222, + 244, 246, 240, 242, 252, 254, 248, 250, 234, 235, 232, 233, 238, 239, 236, 237, + 200, 201, 198, 199, 204, 205, 202, 203, 192, 193, 191, 191, 196, 197, 194, 195, + 214, 215, 212, 213, 218, 219, 216, 217, 207, 207, 206, 206, 210, 211, 208, 209 + }; + +uint8_t alaw_to_ulaw(uint8_t alaw) +{ + return alaw_to_ulaw_table[alaw]; +} +/*- End of function --------------------------------------------------------*/ + +uint8_t ulaw_to_alaw(uint8_t ulaw) +{ + return ulaw_to_alaw_table[ulaw]; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ + +/* 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: + */ + diff --git a/libs/freetdm/src/hashtable.c b/libs/freetdm/src/hashtable.c new file mode 100644 index 0000000000..1f08abc2b8 --- /dev/null +++ b/libs/freetdm/src/hashtable.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2002, Christopher Clark + * 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 "hashtable.h" +#include "hashtable_private.h" +#include +#include +#include +#include + +/* + Credit for primes table: Aaron Krowne + http://br.endernet.org/~akrowne/ + http://planetmath.org/encyclopedia/GoodHashTablePrimes.html +*/ +static const unsigned int primes[] = { + 53, 97, 193, 389, + 769, 1543, 3079, 6151, + 12289, 24593, 49157, 98317, + 196613, 393241, 786433, 1572869, + 3145739, 6291469, 12582917, 25165843, + 50331653, 100663319, 201326611, 402653189, + 805306457, 1610612741 +}; +const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]); +const float max_load_factor = 0.65f; + +/*****************************************************************************/ +FT_DECLARE(struct hashtable *) +create_hashtable(unsigned int minsize, + unsigned int (*hashf) (void*), + int (*eqf) (void*,void*)) +{ + struct hashtable *h; + unsigned int pindex, size = primes[0]; + /* Check requested hashtable isn't too large */ + if (minsize > (1u << 30)) return NULL; + /* Enforce size as prime */ + for (pindex=0; pindex < prime_table_length; pindex++) { + if (primes[pindex] > minsize) { size = primes[pindex]; break; } + } + h = (struct hashtable *)ftdm_malloc(sizeof(struct hashtable)); + if (NULL == h) return NULL; /*oom*/ + h->table = (struct entry **)ftdm_malloc(sizeof(struct entry*) * size); + if (NULL == h->table) { ftdm_safe_free(h); return NULL; } /*oom*/ + memset(h->table, 0, size * sizeof(struct entry *)); + h->tablelength = size; + h->primeindex = pindex; + h->entrycount = 0; + h->hashfn = hashf; + h->eqfn = eqf; + h->loadlimit = (unsigned int) ceil(size * max_load_factor); + return h; +} + +/*****************************************************************************/ +unsigned int +hash(struct hashtable *h, void *k) +{ + /* Aim to protect against poor hash functions by adding logic here + * - logic taken from java 1.4 hashtable source */ + unsigned int i = h->hashfn(k); + i += ~(i << 9); + i ^= ((i >> 14) | (i << 18)); /* >>> */ + i += (i << 4); + i ^= ((i >> 10) | (i << 22)); /* >>> */ + return i; +} + +/*****************************************************************************/ +static int +hashtable_expand(struct hashtable *h) +{ + /* Double the size of the table to accomodate more entries */ + struct entry **newtable; + struct entry *e; + struct entry **pE; + unsigned int newsize, i, index; + /* Check we're not hitting max capacity */ + if (h->primeindex == (prime_table_length - 1)) return 0; + newsize = primes[++(h->primeindex)]; + + newtable = (struct entry **)ftdm_malloc(sizeof(struct entry*) * newsize); + if (NULL != newtable) + { + memset(newtable, 0, newsize * sizeof(struct entry *)); + /* This algorithm is not 'stable'. ie. it reverses the list + * when it transfers entries between the tables */ + for (i = 0; i < h->tablelength; i++) { + while (NULL != (e = h->table[i])) { + h->table[i] = e->next; + index = indexFor(newsize,e->h); + e->next = newtable[index]; + newtable[index] = e; + } + } + ftdm_safe_free(h->table); + h->table = newtable; + } + /* Plan B: realloc instead */ + else + { + newtable = (struct entry **) + realloc(h->table, newsize * sizeof(struct entry *)); + if (NULL == newtable) { (h->primeindex)--; return 0; } + h->table = newtable; + memset(newtable[h->tablelength], 0, newsize - h->tablelength); + for (i = 0; i < h->tablelength; i++) { + for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { + index = indexFor(newsize,e->h); + if (index == i) + { + pE = &(e->next); + } + else + { + *pE = e->next; + e->next = newtable[index]; + newtable[index] = e; + } + } + } + } + h->tablelength = newsize; + h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); + return -1; +} + +/*****************************************************************************/ +FT_DECLARE(unsigned int) +hashtable_count(struct hashtable *h) +{ + return h->entrycount; +} + +/*****************************************************************************/ +FT_DECLARE(int) +hashtable_insert(struct hashtable *h, void *k, void *v, hashtable_flag_t flags) +{ + /* This method allows duplicate keys - but they shouldn't be used */ + unsigned int index; + struct entry *e; + if (++(h->entrycount) > h->loadlimit) + { + /* Ignore the return value. If expand fails, we should + * still try cramming just this value into the existing table + * -- we may not have memory for a larger table, but one more + * element may be ok. Next time we insert, we'll try expanding again.*/ + hashtable_expand(h); + } + e = (struct entry *)ftdm_malloc(sizeof(struct entry)); + if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ + e->h = hash(h,k); + index = indexFor(h->tablelength,e->h); + e->k = k; + e->v = v; + e->flags = flags; + e->next = h->table[index]; + h->table[index] = e; + return -1; +} + +/*****************************************************************************/ +FT_DECLARE(void *) /* returns value associated with key */ +hashtable_search(struct hashtable *h, void *k) +{ + struct entry *e; + unsigned int hashvalue, index; + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hashvalue); + e = h->table[index]; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +FT_DECLARE(void *) /* returns value associated with key */ +hashtable_remove(struct hashtable *h, void *k) +{ + /* TODO: consider compacting the table when the load factor drops enough, + * or provide a 'compact' method. */ + + struct entry *e; + struct entry **pE; + void *v; + unsigned int hashvalue, index; + + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hash(h,k)); + pE = &(h->table[index]); + e = *pE; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) + { + *pE = e->next; + h->entrycount--; + v = e->v; + if (e->flags & HASHTABLE_FLAG_FREE_KEY) { + freekey(e->k); + } + ftdm_safe_free(e); + return v; + } + pE = &(e->next); + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +/* destroy */ +FT_DECLARE(void) +hashtable_destroy(struct hashtable *h) +{ + unsigned int i; + struct entry *e, *f; + struct entry **table = h->table; + + for (i = 0; i < h->tablelength; i++) + { + e = table[i]; + while (NULL != e) + { f = e; e = e->next; if (f->flags & HASHTABLE_FLAG_FREE_KEY) freekey(f->k); if (f->flags & HASHTABLE_FLAG_FREE_VALUE) ftdm_safe_free(f->v); ftdm_safe_free(f); } + } + + ftdm_safe_free(h->table); + ftdm_safe_free(h); +} + +FT_DECLARE(struct hashtable_iterator *) hashtable_next(struct hashtable_iterator *i) +{ + + if (i->e) { + if ((i->e = i->e->next) != 0) { + return i; + } else { + i->pos++; + } + } + + while(i->pos < i->h->tablelength && !i->h->table[i->pos]) { + i->pos++; + } + + if (i->pos >= i->h->tablelength) { + return NULL; + } + + if ((i->e = i->h->table[i->pos]) != 0) { + return i; + } + + return NULL; +} + +FT_DECLARE(struct hashtable_iterator *) hashtable_first(struct hashtable *h) +{ + h->iterator.pos = 0; + h->iterator.e = NULL; + h->iterator.h = h; + return hashtable_next(&h->iterator); +} + + + +FT_DECLARE(void) hashtable_this(struct hashtable_iterator *i, const void **key, int *klen, void **val) +{ + if (i->e) { + if (key) { + *key = i->e->k; + } + if (klen) { + *klen = (int)strlen(i->e->k); + } + if (val) { + *val = i->e->v; + } + } else { + if (key) { + *key = NULL; + } + if (klen) { + *klen = 0; + } + if (val) { + *val = NULL; + } + } +} + + +/* 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: + */ + diff --git a/libs/freetdm/src/hashtable_itr.c b/libs/freetdm/src/hashtable_itr.c new file mode 100644 index 0000000000..a31f5b44c5 --- /dev/null +++ b/libs/freetdm/src/hashtable_itr.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2002, 2004, Christopher Clark + * 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 "hashtable.h" +#include "hashtable_private.h" +#include "hashtable_itr.h" +#include /* defines NULL */ + +/*****************************************************************************/ +/* hashtable_iterator - iterator constructor */ + +struct hashtable_itr * +hashtable_iterator(struct hashtable *h) +{ + unsigned int i, tablelength; + struct hashtable_itr *itr = ftdm_malloc(sizeof(struct hashtable_itr)); + if (NULL == itr) return NULL; + itr->h = h; + itr->e = NULL; + itr->parent = NULL; + tablelength = h->tablelength; + itr->index = tablelength; + if (0 == h->entrycount) return itr; + + for (i = 0; i < tablelength; i++) + { + if (NULL != h->table[i]) + { + itr->e = h->table[i]; + itr->index = i; + break; + } + } + return itr; +} + +/*****************************************************************************/ +/* advance - advance the iterator to the next element + * returns zero if advanced to end of table */ + +int +hashtable_iterator_advance(struct hashtable_itr *itr) +{ + unsigned int j,tablelength; + struct entry **table; + struct entry *next; + if (NULL == itr->e) return 0; /* stupidity check */ + + next = itr->e->next; + if (NULL != next) + { + itr->parent = itr->e; + itr->e = next; + return -1; + } + tablelength = itr->h->tablelength; + itr->parent = NULL; + if (tablelength <= (j = ++(itr->index))) + { + itr->e = NULL; + return 0; + } + table = itr->h->table; + while (NULL == (next = table[j])) + { + if (++j >= tablelength) + { + itr->index = tablelength; + itr->e = NULL; + return 0; + } + } + itr->index = j; + itr->e = next; + return -1; +} + +/*****************************************************************************/ +/* remove - remove the entry at the current iterator position + * and advance the iterator, if there is a successive + * element. + * If you want the value, read it before you remove: + * beware memory leaks if you don't. + * Returns zero if end of iteration. */ + +int +hashtable_iterator_remove(struct hashtable_itr *itr) +{ + struct entry *remember_e, *remember_parent; + int ret; + + /* Do the removal */ + if (NULL == (itr->parent)) + { + /* element is head of a chain */ + itr->h->table[itr->index] = itr->e->next; + } else { + /* element is mid-chain */ + itr->parent->next = itr->e->next; + } + /* itr->e is now outside the hashtable */ + remember_e = itr->e; + itr->h->entrycount--; + freekey(remember_e->k); + + /* Advance the iterator, correcting the parent */ + remember_parent = itr->parent; + ret = hashtable_iterator_advance(itr); + if (itr->parent == remember_e) { itr->parent = remember_parent; } + ftdm_safe_free(remember_e); + return ret; +} + +/*****************************************************************************/ +int /* returns zero if not found */ +hashtable_iterator_search(struct hashtable_itr *itr, + struct hashtable *h, void *k) +{ + struct entry *e, *parent; + unsigned int hashvalue, index; + + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hashvalue); + + e = h->table[index]; + parent = NULL; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) + { + itr->index = index; + itr->e = e; + itr->parent = parent; + itr->h = h; + return -1; + } + parent = e; + e = e->next; + } + return 0; +} + + +/* 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: + */ + diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h new file mode 100644 index 0000000000..0900c62800 --- /dev/null +++ b/libs/freetdm/src/include/freetdm.h @@ -0,0 +1,968 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + * Contributors: + * + * Moises Silva + * + */ + +#ifndef FREETDM_H +#define FREETDM_H + + +#if !defined(_XOPEN_SOURCE) && !defined(__FreeBSD__) +#define _XOPEN_SOURCE 600 +#endif + +#ifndef HAVE_STRINGS_H +#define HAVE_STRINGS_H 1 +#endif +#ifndef HAVE_SYS_SOCKET_H +#define HAVE_SYS_SOCKET_H 1 +#endif + +#ifndef __WINDOWS__ +#if defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64) +#define __WINDOWS__ +#endif +#endif + +#ifdef _MSC_VER +#if defined(FT_DECLARE_STATIC) +#define FT_DECLARE(type) type __stdcall +#define FT_DECLARE_NONSTD(type) type __cdecl +#define FT_DECLARE_DATA +#elif defined(FREETDM_EXPORTS) +#define FT_DECLARE(type) __declspec(dllexport) type __stdcall +#define FT_DECLARE_NONSTD(type) __declspec(dllexport) type __cdecl +#define FT_DECLARE_DATA __declspec(dllexport) +#else +#define FT_DECLARE(type) __declspec(dllimport) type __stdcall +#define FT_DECLARE_NONSTD(type) __declspec(dllimport) type __cdecl +#define FT_DECLARE_DATA __declspec(dllimport) +#endif +#define EX_DECLARE_DATA __declspec(dllexport) +#else +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(HAVE_VISIBILITY) +#define FT_DECLARE(type) __attribute__((visibility("default"))) type +#define FT_DECLARE_NONSTD(type) __attribute__((visibility("default"))) type +#define FT_DECLARE_DATA __attribute__((visibility("default"))) +#else +#define FT_DECLARE(type) type +#define FT_DECLARE_NONSTD(type) type +#define FT_DECLARE_DATA +#endif +#define EX_DECLARE_DATA +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#if (_MSC_VER >= 1400) /* VC8+ */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#ifndef strncasecmp +#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#undef HAVE_STRINGS_H +#undef HAVE_SYS_SOCKET_H +/* disable warning for zero length array in a struct */ +/* this will cause errors on c99 and ansi compliant compilers and will need to be fixed in the wanpipe header files */ +#pragma warning(disable:4706) +#pragma comment(lib, "Winmm") +#endif + +#define FTDM_THREAD_STACKSIZE 240 * 1024 +#define FTDM_ENUM_NAMES(_NAME, _STRINGS) static const char * _NAME [] = { _STRINGS , NULL }; +#define FTDM_STR2ENUM_P(_FUNC1, _FUNC2, _TYPE) FT_DECLARE(_TYPE) _FUNC1 (const char *name); FT_DECLARE(const char *) _FUNC2 (_TYPE type); +#define FTDM_STR2ENUM(_FUNC1, _FUNC2, _TYPE, _STRINGS, _MAX) \ + FT_DECLARE(_TYPE) _FUNC1 (const char *name) \ + { \ + int i; \ + _TYPE t = _MAX ; \ + \ + for (i = 0; i < _MAX ; i++) { \ + if (!strcasecmp(name, _STRINGS[i])) { \ + t = (_TYPE) i; \ + break; \ + } \ + } \ + \ + return t; \ + } \ + FT_DECLARE(const char *) _FUNC2 (_TYPE type) \ + { \ + if (type > _MAX) { \ + type = _MAX; \ + } \ + return _STRINGS[(int)type]; \ + } \ + +#define ftdm_true(expr) \ + (expr && ( !strcasecmp(expr, "yes") || \ + !strcasecmp(expr, "on") || \ + !strcasecmp(expr, "true") || \ + !strcasecmp(expr, "enabled") || \ + !strcasecmp(expr, "active") || \ + atoi(expr))) ? 1 : 0 + + +#include +#ifndef __WINDOWS__ +#include +#endif + +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#include +#include "ftdm_types.h" +#include "hashtable.h" +#include "ftdm_config.h" +#include "g711.h" +#include "libteletone.h" +#include "ftdm_buffer.h" +#include "ftdm_threadmutex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __WINDOWS__ +#define ftdm_sleep(x) Sleep(x) +#else +#define ftdm_sleep(x) usleep(x * 1000) +#endif + +#ifdef NDEBUG +#undef assert +#define assert(_Expression) ((void)(_Expression)) +#endif + +#define FTDM_MAX_CHANNELS_PHYSICAL_SPAN 32 +#define FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN 32 +#define FTDM_MAX_CHANNELS_SPAN FTDM_MAX_CHANNELS_PHYSICAL_SPAN * FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN +#define FTDM_MAX_SPANS_INTERFACE 128 + +#define FTDM_MAX_CHANNELS_GROUP 1024 +#define FTDM_MAX_GROUPS_INTERFACE FTDM_MAX_SPANS_INTERFACE + +#define GOTO_STATUS(label,st) status = st; goto label ; + +#define ftdm_copy_string(x,y,z) strncpy(x, y, z - 1) +#define ftdm_set_string(x,y) strncpy(x, y, sizeof(x)-1) +#define ftdm_strlen_zero(s) (!s || *s == '\0') +#define ftdm_strlen_zero_buf(s) (*s == '\0') + + +#define ftdm_channel_test_feature(obj, flag) ((obj)->features & flag) +#define ftdm_channel_set_feature(obj, flag) (obj)->features |= (flag) +#define ftdm_channel_clear_feature(obj, flag) (obj)->features &= ~(flag) +#define ftdm_channel_set_member_locked(obj, _m, _v) ftdm_mutex_lock(obj->mutex); obj->_m = _v; ftdm_mutex_unlock(obj->mutex) + +/*! + \brief Test for the existance of a flag on an arbitary object + \command obj the object to test + \command flag the or'd list of flags to test + \return true value if the object has the flags defined +*/ +#define ftdm_test_flag(obj, flag) ((obj)->flags & flag) +#define ftdm_test_pflag(obj, flag) ((obj)->pflags & flag) +#define ftdm_test_sflag(obj, flag) ((obj)->sflags & flag) + + +#define ftdm_set_alarm_flag(obj, flag) (obj)->alarm_flags |= (flag) +#define ftdm_clear_alarm_flag(obj, flag) (obj)->alarm_flags &= ~(flag) +#define ftdm_test_alarm_flag(obj, flag) ((obj)->alarm_flags & flag) + +/*! + \brief Set a flag on an arbitrary object + \command obj the object to set the flags on + \command flag the or'd list of flags to set +*/ +#define ftdm_set_flag(obj, flag) (obj)->flags |= (flag) +#define ftdm_set_flag_locked(obj, flag) assert(obj->mutex != NULL); \ + ftdm_mutex_lock(obj->mutex); \ + (obj)->flags |= (flag); \ + ftdm_mutex_unlock(obj->mutex); + +#define ftdm_set_pflag(obj, flag) (obj)->pflags |= (flag) +#define ftdm_set_pflag_locked(obj, flag) assert(obj->mutex != NULL); \ + ftdm_mutex_lock(obj->mutex); \ + (obj)->pflags |= (flag); \ + ftdm_mutex_unlock(obj->mutex); + +#define ftdm_set_sflag(obj, flag) (obj)->sflags |= (flag) +#define ftdm_set_sflag_locked(obj, flag) assert(obj->mutex != NULL); \ + ftdm_mutex_lock(obj->mutex); \ + (obj)->sflags |= (flag); \ + ftdm_mutex_unlock(obj->mutex); + +/*! + \brief Clear a flag on an arbitrary object while locked + \command obj the object to test + \command flag the or'd list of flags to clear +*/ +#define ftdm_clear_flag(obj, flag) (obj)->flags &= ~(flag) + +#define ftdm_clear_flag_locked(obj, flag) assert(obj->mutex != NULL); ftdm_mutex_lock(obj->mutex); (obj)->flags &= ~(flag); ftdm_mutex_unlock(obj->mutex); + +#define ftdm_clear_pflag(obj, flag) (obj)->pflags &= ~(flag) + +#define ftdm_clear_pflag_locked(obj, flag) assert(obj->mutex != NULL); ftdm_mutex_lock(obj->mutex); (obj)->pflags &= ~(flag); ftdm_mutex_unlock(obj->mutex); + +#define ftdm_clear_sflag(obj, flag) (obj)->sflags &= ~(flag) + +#define ftdm_clear_sflag_locked(obj, flag) assert(obj->mutex != NULL); ftdm_mutex_lock(obj->mutex); (obj)->sflags &= ~(flag); ftdm_mutex_unlock(obj->mutex); + + +#define ftdm_set_state_locked(obj, s) if ( obj->state == s ) { \ + ftdm_log(FTDM_LOG_WARNING, "Why bother changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(obj->state), ftdm_channel_state2str(s)); \ + } else if (ftdm_test_flag(obj, FTDM_CHANNEL_READY)) { \ + ftdm_channel_state_t st = obj->state; \ + ftdm_channel_set_state(obj, s, 1); \ + if (obj->state == s) ftdm_log(FTDM_LOG_DEBUG, "Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(st), ftdm_channel_state2str(s)); \ + else ftdm_log(FTDM_LOG_WARNING, "VETO Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(st), ftdm_channel_state2str(s)); \ + } + +#define ftdm_set_state(obj, s) if ( obj->state == s ) { \ + ftdm_log(FTDM_LOG_WARNING, "Why bother changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(obj->state), ftdm_channel_state2str(s)); \ + } else if (ftdm_test_flag(obj, FTDM_CHANNEL_READY)) { \ + ftdm_channel_state_t st = obj->state; \ + ftdm_channel_set_state(obj, s, 0); \ + if (obj->state == s) ftdm_log(FTDM_LOG_DEBUG, "Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(st), ftdm_channel_state2str(s)); \ + else ftdm_log(FTDM_LOG_WARNING, "VETO Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(st), ftdm_channel_state2str(s)); \ + } + +#ifdef _MSC_VER +/* The while(0) below throws a conditional expression is constant warning */ +#pragma warning(disable:4127) +#endif + +#define ftdm_set_state_locked_wait(obj, s) \ + do { \ + int __safety = 100; \ + ftdm_set_state_locked(obj, s); \ + while(__safety-- && ftdm_test_flag(obj, FTDM_CHANNEL_STATE_CHANGE)) { \ + ftdm_sleep(10); \ + } \ + if(!__safety) { \ + ftdm_log(FTDM_LOG_CRIT, "State change not completed\n"); \ + } \ + } while(0); + +#define ftdm_wait_for_flag_cleared(obj, flag, time) \ + do { \ + int __safety = time; \ + while(__safety-- && ftdm_test_flag(obj, flag)) { \ + ftdm_mutex_unlock(obj->mutex); \ + ftdm_sleep(10); \ + ftdm_mutex_lock(obj->mutex); \ + } \ + if(!__safety) { \ + ftdm_log(FTDM_LOG_CRIT, "flag %d was never cleared\n", flag); \ + } \ + } while(0); + +#define ftdm_set_state_wait(obj, s) \ + do { \ + ftdm_channel_set_state(obj, s, 0); \ + ftdm_wait_for_flag_cleared(obj, FTDM_CHANNEL_STATE_CHANGE, 100); \ + } while(0); + + +typedef enum { + FTDM_STATE_CHANGE_FAIL, + FTDM_STATE_CHANGE_SUCCESS, + FTDM_STATE_CHANGE_SAME, +} ftdm_state_change_result_t; + +#define ftdm_set_state_r(obj, s, l, r) if ( obj->state == s ) { \ + if (s != FTDM_CHANNEL_STATE_HANGUP) ftdm_log(FTDM_LOG_WARNING, "Why bother changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(obj->state), ftdm_channel_state2str(s)); r = FTDM_STATE_CHANGE_SAME; \ + } else if (ftdm_test_flag(obj, FTDM_CHANNEL_READY)) { \ + int st = obj->state; \ + r = (ftdm_channel_set_state(obj, s, l) == FTDM_SUCCESS) ? FTDM_STATE_CHANGE_SUCCESS : FTDM_STATE_CHANGE_FAIL; \ + if (obj->state == s) {ftdm_log(FTDM_LOG_DEBUG, "Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(st), ftdm_channel_state2str(s));} \ + else ftdm_log(FTDM_LOG_WARNING, "VETO Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, ftdm_channel_state2str(st), ftdm_channel_state2str(s)); \ + } + + +#define ftdm_is_dtmf(key) ((key > 47 && key < 58) || (key > 64 && key < 69) || (key > 96 && key < 101) || key == 35 || key == 42 || key == 87 || key == 119) + +/*! + \brief Copy flags from one arbitrary object to another + \command dest the object to copy the flags to + \command src the object to copy the flags from + \command flags the flags to copy +*/ +#define ftdm_copy_flags(dest, src, flags) (dest)->flags &= ~(flags); (dest)->flags |= ((src)->flags & (flags)) + +struct ftdm_stream_handle { + ftdm_stream_handle_write_function_t write_function; + ftdm_stream_handle_raw_write_function_t raw_write_function; + void *data; + void *end; + ftdm_size_t data_size; + ftdm_size_t data_len; + ftdm_size_t alloc_len; + ftdm_size_t alloc_chunk; +}; + + +FT_DECLARE_NONSTD(ftdm_status_t) ftdm_console_stream_raw_write(ftdm_stream_handle_t *handle, uint8_t *data, ftdm_size_t datalen); +FT_DECLARE_NONSTD(ftdm_status_t) ftdm_console_stream_write(ftdm_stream_handle_t *handle, const char *fmt, ...); + +#define FTDM_CMD_CHUNK_LEN 1024 +#define FTDM_STANDARD_STREAM(s) memset(&s, 0, sizeof(s)); s.data = malloc(FTDM_CMD_CHUNK_LEN); \ + assert(s.data); \ + memset(s.data, 0, FTDM_CMD_CHUNK_LEN); \ + s.end = s.data; \ + s.data_size = FTDM_CMD_CHUNK_LEN; \ + s.write_function = ftdm_console_stream_write; \ + s.raw_write_function = ftdm_console_stream_raw_write; \ + s.alloc_len = FTDM_CMD_CHUNK_LEN; \ + s.alloc_chunk = FTDM_CMD_CHUNK_LEN + +struct ftdm_event { + ftdm_event_type_t e_type; + uint32_t enum_id; + ftdm_channel_t *channel; + void *data; +}; + +typedef struct ftdm_queue ftdm_queue_t; +typedef ftdm_status_t (*ftdm_queue_create_func_t)(ftdm_queue_t **queue, ftdm_size_t capacity); +typedef ftdm_status_t (*ftdm_queue_enqueue_func_t)(ftdm_queue_t *queue, void *obj); +typedef void *(*ftdm_queue_dequeue_func_t)(ftdm_queue_t *queue); +typedef ftdm_status_t (*ftdm_queue_wait_func_t)(ftdm_queue_t *queue, int ms); +typedef ftdm_status_t (*ftdm_queue_get_interrupt_func_t)(ftdm_queue_t *queue, ftdm_interrupt_t **interrupt); +typedef ftdm_status_t (*ftdm_queue_destroy_func_t)(ftdm_queue_t **queue); +typedef struct ftdm_queue_handler { + ftdm_queue_create_func_t create; + ftdm_queue_enqueue_func_t enqueue; + ftdm_queue_dequeue_func_t dequeue; + ftdm_queue_wait_func_t wait; + ftdm_queue_get_interrupt_func_t get_interrupt; + ftdm_queue_destroy_func_t destroy; +} ftdm_queue_handler_t; +FT_DECLARE_DATA extern ftdm_queue_handler_t g_ftdm_queue_handler; + +/*! brief create a new queue */ +#define ftdm_queue_create(queue, capacity) g_ftdm_queue_handler.create(queue, capacity) + +/*! Enqueue an object */ +#define ftdm_queue_enqueue(queue, obj) g_ftdm_queue_handler.enqueue(queue, obj) + +/*! dequeue an object from the queue */ +#define ftdm_queue_dequeue(queue) g_ftdm_queue_handler.dequeue(queue) + +/*! wait ms milliseconds for a queue to have available objects, -1 to wait forever */ +#define ftdm_queue_wait(queue, ms) g_ftdm_queue_handler.wait(queue, ms) + +/*! get the internal interrupt object (to wait for elements to be added from the outside bypassing ftdm_queue_wait) */ +#define ftdm_queue_get_interrupt(queue, ms) g_ftdm_queue_handler.get_interrupt(queue, ms) + +/*! destroy the queue */ +#define ftdm_queue_destroy(queue) g_ftdm_queue_handler.destroy(queue) + + +#define FTDM_TOKEN_STRLEN 128 +#define FTDM_MAX_TOKENS 10 + +static __inline__ char *ftdm_clean_string(char *s) +{ + char *p; + + for (p = s; p && *p; p++) { + uint8_t x = (uint8_t) *p; + if (x < 32 || x > 127) { + *p = ' '; + } + } + + return s; +} + +struct ftdm_bitstream { + uint8_t *data; + uint32_t datalen; + uint32_t byte_index; + uint8_t bit_index; + int8_t endian; + uint8_t top; + uint8_t bot; + uint8_t ss; + uint8_t ssv; +}; + +struct ftdm_fsk_data_state { + dsp_fsk_handle_t *fsk1200_handle; + uint8_t init; + uint8_t *buf; + size_t bufsize; + ftdm_size_t blen; + ftdm_size_t bpos; + ftdm_size_t dlen; + ftdm_size_t ppos; + int checksum; +}; + +struct ftdm_fsk_modulator { + teletone_dds_state_t dds; + ftdm_bitstream_t bs; + uint32_t carrier_bits_start; + uint32_t carrier_bits_stop; + uint32_t chan_sieze_bits; + uint32_t bit_factor; + uint32_t bit_accum; + uint32_t sample_counter; + int32_t samples_per_bit; + int32_t est_bytes; + fsk_modem_types_t modem_type; + ftdm_fsk_data_state_t *fsk_data; + ftdm_fsk_write_sample_t write_sample_callback; + void *user_data; + int16_t sample_buffer[64]; +}; + +/** + * Type Of Number (TON) + */ +typedef enum { + FTDM_TON_UNKNOWN = 0, + FTDM_TON_INTERNATIONAL, + FTDM_TON_NATIONAL, + FTDM_TON_NETWORK_SPECIFIC, + FTDM_TON_SUBSCRIBER_NUMBER, + FTDM_TON_ABBREVIATED_NUMBER, + FTDM_TON_RESERVED, + FTDM_TON_INVALID = 255 +} ftdm_ton_t; + +/** + * Numbering Plan Identification (NPI) + */ +typedef enum { + FTDM_NPI_UNKNOWN = 0, + FTDM_NPI_ISDN = 1, + FTDM_NPI_DATA = 3, + FTDM_NPI_TELEX = 4, + FTDM_NPI_NATIONAL = 8, + FTDM_NPI_PRIVATE = 9, + FTDM_NPI_RESERVED = 10, + FTDM_NPI_INVALID = 255 +} ftdm_npi_t; + +typedef struct { + char digits[25]; + uint8_t type; + uint8_t plan; +} ftdm_number_t; + +typedef enum { + FTDM_CALLER_STATE_DIALING, + FTDM_CALLER_STATE_SUCCESS, + FTDM_CALLER_STATE_FAIL +} ftdm_caller_state_t; + +struct ftdm_caller_data { + char cid_date[8]; + char cid_name[80]; + ftdm_number_t cid_num; + ftdm_number_t ani; + ftdm_number_t dnis; + ftdm_number_t rdnis; + char aniII[25]; + uint8_t screen; + uint8_t pres; + char collected[25]; + int CRV; + int hangup_cause; + uint8_t raw_data[1024]; + uint32_t raw_data_len; + uint32_t flags; + ftdm_caller_state_t call_state; + uint32_t chan_id; +}; + +typedef enum { + FTDM_TYPE_NONE, + FTDM_TYPE_SPAN = 0xFF, + FTDM_TYPE_CHANNEL +} ftdm_data_type_t; + +/* 2^8 table size, one for each byte value */ +#define FTDM_GAINS_TABLE_SIZE 256 +struct ftdm_channel { + ftdm_data_type_t data_type; + uint32_t span_id; + uint32_t chan_id; + uint32_t physical_span_id; + uint32_t physical_chan_id; + uint32_t rate; + uint32_t extra_id; + ftdm_chan_type_t type; + ftdm_socket_t sockfd; + uint32_t flags; + uint32_t pflags; + uint32_t sflags; + ftdm_alarm_flag_t alarm_flags; + ftdm_channel_feature_t features; + ftdm_codec_t effective_codec; + ftdm_codec_t native_codec; + uint32_t effective_interval; + uint32_t native_interval; + uint32_t packet_len; + ftdm_channel_state_t state; + ftdm_channel_state_t last_state; + ftdm_channel_state_t init_state; + ftdm_mutex_t *mutex; + teletone_dtmf_detect_state_t dtmf_detect; + uint32_t buffer_delay; + ftdm_event_t event_header; + char last_error[256]; + fio_event_cb_t event_callback; + uint32_t skip_read_frames; + ftdm_buffer_t *dtmf_buffer; + ftdm_buffer_t *gen_dtmf_buffer; + ftdm_buffer_t *pre_buffer; + ftdm_buffer_t *digit_buffer; + ftdm_buffer_t *fsk_buffer; + ftdm_mutex_t *pre_buffer_mutex; + uint32_t dtmf_on; + uint32_t dtmf_off; + char *dtmf_hangup_buf; + teletone_generation_session_t tone_session; + ftdm_time_t last_event_time; + ftdm_time_t ring_time; + char tokens[FTDM_MAX_TOKENS+1][FTDM_TOKEN_STRLEN]; + uint8_t needed_tones[FTDM_TONEMAP_INVALID]; + uint8_t detected_tones[FTDM_TONEMAP_INVALID]; + ftdm_tonemap_t last_detected_tone; + uint32_t token_count; + char chan_name[128]; + char chan_number[32]; + ftdm_filehandle_t fds[2]; + ftdm_fsk_data_state_t fsk; + uint8_t fsk_buf[80]; + uint32_t ring_count; + void *mod_data; + void *call_data; + struct ftdm_caller_data caller_data; + struct ftdm_span *span; + struct ftdm_io_interface *fio; + ftdm_hash_t *variable_hash; + unsigned char rx_cas_bits; + uint32_t pre_buffer_size; + unsigned char rxgain_table[FTDM_GAINS_TABLE_SIZE]; + unsigned char txgain_table[FTDM_GAINS_TABLE_SIZE]; + float rxgain; + float txgain; +}; + + +struct ftdm_sigmsg { + ftdm_signal_event_t event_id; + uint32_t chan_id; + uint32_t span_id; + ftdm_channel_t *channel; + void *raw_data; + uint32_t raw_data_len; +}; + + +struct ftdm_span { + ftdm_data_type_t data_type; + char *name; + uint32_t span_id; + uint32_t chan_count; + ftdm_span_flag_t flags; + struct ftdm_io_interface *fio; + fio_event_cb_t event_callback; + ftdm_mutex_t *mutex; + ftdm_trunk_type_t trunk_type; + ftdm_analog_start_type_t start_type; + ftdm_signal_type_t signal_type; + void *signal_data; + fio_signal_cb_t signal_cb; + ftdm_event_t event_header; + char last_error[256]; + char tone_map[FTDM_TONEMAP_INVALID+1][FTDM_TONEMAP_LEN]; + teletone_tone_map_t tone_detect_map[FTDM_TONEMAP_INVALID+1]; + teletone_multi_tone_t tone_finder[FTDM_TONEMAP_INVALID+1]; + ftdm_channel_t *channels[FTDM_MAX_CHANNELS_SPAN+1]; + fio_channel_outgoing_call_t outgoing_call; + fio_channel_set_sig_status_t set_channel_sig_status; + fio_channel_get_sig_status_t get_channel_sig_status; + fio_span_set_sig_status_t set_span_sig_status; + fio_span_get_sig_status_t get_span_sig_status; + fio_channel_request_t channel_request; + ftdm_span_start_t start; + ftdm_span_stop_t stop; + void *mod_data; + char *type; + char *dtmf_hangup; + size_t dtmf_hangup_len; + int suggest_chan_id; + ftdm_state_map_t *state_map; + ftdm_caller_data_t default_caller_data; + ftdm_queue_t *pendingchans; + struct ftdm_span *next; +}; + +struct ftdm_group { + char *name; + uint32_t group_id; + uint32_t chan_count; + ftdm_channel_t *channels[FTDM_MAX_CHANNELS_GROUP]; + uint32_t last_used_index; + ftdm_mutex_t *mutex; + struct ftdm_group *next; +}; + +FT_DECLARE_DATA extern ftdm_logger_t ftdm_log; + +typedef enum { + FTDM_CRASH_NEVER = 0, + FTDM_CRASH_ON_ASSERT +} ftdm_crash_policy_t; + +FT_DECLARE_DATA extern ftdm_crash_policy_t g_ftdm_crash_policy; + +typedef void *(*ftdm_malloc_func_t)(void *pool, ftdm_size_t len); +typedef void *(*ftdm_calloc_func_t)(void *pool, ftdm_size_t elements, ftdm_size_t len); +typedef void (*ftdm_free_func_t)(void *pool, void *ptr); +typedef struct ftdm_memory_handler { + void *pool; + ftdm_malloc_func_t malloc; + ftdm_calloc_func_t calloc; + ftdm_free_func_t free; +} ftdm_memory_handler_t; + +FT_DECLARE_DATA extern ftdm_memory_handler_t g_ftdm_mem_handler; + +struct ftdm_io_interface { + const char *name; + fio_configure_span_t configure_span; + fio_configure_t configure; + fio_open_t open; + fio_close_t close; + fio_channel_destroy_t channel_destroy; + fio_span_destroy_t span_destroy; + fio_get_alarms_t get_alarms; + fio_command_t command; + fio_wait_t wait; + fio_read_t read; + fio_write_t write; + fio_span_poll_event_t poll_event; + fio_span_next_event_t next_event; + fio_api_t api; +}; + +/*! \brief Override the default queue handler */ +FT_DECLARE(ftdm_status_t) ftdm_global_set_queue_handler(ftdm_queue_handler_t *handler); + +/*! \brief Duplicate string */ +FT_DECLARE(char *) ftdm_strdup(const char *str); +FT_DECLARE(char *) ftdm_strndup(const char *str, ftdm_size_t inlen); + +FT_DECLARE(ftdm_size_t) ftdm_fsk_modulator_generate_bit(ftdm_fsk_modulator_t *fsk_trans, int8_t bit, int16_t *buf, ftdm_size_t buflen); +FT_DECLARE(int32_t) ftdm_fsk_modulator_generate_carrier_bits(ftdm_fsk_modulator_t *fsk_trans, uint32_t bits); +FT_DECLARE(void) ftdm_fsk_modulator_generate_chan_sieze(ftdm_fsk_modulator_t *fsk_trans); +FT_DECLARE(void) ftdm_fsk_modulator_send_data(ftdm_fsk_modulator_t *fsk_trans); +#define ftdm_fsk_modulator_send_all(_it) ftdm_fsk_modulator_generate_chan_sieze(_it); \ + ftdm_fsk_modulator_generate_carrier_bits(_it, _it->carrier_bits_start); \ + ftdm_fsk_modulator_send_data(_it); \ + ftdm_fsk_modulator_generate_carrier_bits(_it, _it->carrier_bits_stop) + +FT_DECLARE(ftdm_status_t) ftdm_fsk_modulator_init(ftdm_fsk_modulator_t *fsk_trans, + fsk_modem_types_t modem_type, + uint32_t sample_rate, + ftdm_fsk_data_state_t *fsk_data, + float db_level, + uint32_t carrier_bits_start, + uint32_t carrier_bits_stop, + uint32_t chan_sieze_bits, + ftdm_fsk_write_sample_t write_sample_callback, + void *user_data); +FT_DECLARE(int8_t) ftdm_bitstream_get_bit(ftdm_bitstream_t *bsp); +FT_DECLARE(void) ftdm_bitstream_init(ftdm_bitstream_t *bsp, uint8_t *data, uint32_t datalen, ftdm_endian_t endian, uint8_t ss); +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_parse(ftdm_fsk_data_state_t *state, ftdm_size_t *type, char **data, ftdm_size_t *len); +FT_DECLARE(ftdm_status_t) ftdm_fsk_demod_feed(ftdm_fsk_data_state_t *state, int16_t *data, size_t samples); +FT_DECLARE(ftdm_status_t) ftdm_fsk_demod_destroy(ftdm_fsk_data_state_t *state); +FT_DECLARE(int) ftdm_fsk_demod_init(ftdm_fsk_data_state_t *state, int rate, uint8_t *buf, size_t bufsize); +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_init(ftdm_fsk_data_state_t *state, uint8_t *data, uint32_t datalen); +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_mdmf(ftdm_fsk_data_state_t *state, ftdm_mdmf_type_t type, const uint8_t *data, uint32_t datalen); +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_checksum(ftdm_fsk_data_state_t *state); +FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_sdmf(ftdm_fsk_data_state_t *state, const char *date, char *number); +FT_DECLARE(ftdm_status_t) ftdm_channel_outgoing_call(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_set_sig_status(ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status); +FT_DECLARE(ftdm_status_t) ftdm_channel_get_sig_status(ftdm_channel_t *ftdmchan, ftdm_signaling_status_t *status); +FT_DECLARE(ftdm_status_t) ftdm_span_set_sig_status(ftdm_span_t *span, ftdm_signaling_status_t status); +FT_DECLARE(ftdm_status_t) ftdm_span_get_sig_status(ftdm_span_t *span, ftdm_signaling_status_t *status); +FT_DECLARE(void) ftdm_channel_rotate_tokens(ftdm_channel_t *ftdmchan); +FT_DECLARE(void) ftdm_channel_clear_detected_tones(ftdm_channel_t *ftdmchan); +FT_DECLARE(void) ftdm_channel_clear_needed_tones(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_get_alarms(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_send_fsk_data(ftdm_channel_t *ftdmchan, ftdm_fsk_data_state_t *fsk_data, float db_level); +FT_DECLARE(ftdm_status_t) ftdm_channel_clear_token(ftdm_channel_t *ftdmchan, const char *token); +FT_DECLARE(void) ftdm_channel_replace_token(ftdm_channel_t *ftdmchan, const char *old_token, const char *new_token); +FT_DECLARE(ftdm_status_t) ftdm_channel_add_token(ftdm_channel_t *ftdmchan, char *token, int end); +FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int lock); +FT_DECLARE(ftdm_status_t) ftdm_span_load_tones(ftdm_span_t *span, const char *mapname); +FT_DECLARE(ftdm_size_t) ftdm_channel_dequeue_dtmf(ftdm_channel_t *ftdmchan, char *dtmf, ftdm_size_t len); +FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, const char *dtmf); +FT_DECLARE(void) ftdm_channel_flush_dtmf(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void); +FT_DECLARE(ftdm_status_t) ftdm_span_poll_event(ftdm_span_t *span, uint32_t ms); +FT_DECLARE(ftdm_status_t) ftdm_span_next_event(ftdm_span_t *span, ftdm_event_t **event); +FT_DECLARE(ftdm_status_t) ftdm_span_find(uint32_t id, ftdm_span_t **span); +FT_DECLARE(ftdm_status_t) ftdm_span_create(ftdm_io_interface_t *fio, ftdm_span_t **span, const char *name); +FT_DECLARE(ftdm_status_t) ftdm_span_close_all(void); +FT_DECLARE(ftdm_status_t) ftdm_span_add_channel(ftdm_span_t *span, ftdm_socket_t sockfd, ftdm_chan_type_t type, ftdm_channel_t **chan); +FT_DECLARE(ftdm_status_t) ftdm_span_set_event_callback(ftdm_span_t *span, fio_event_cb_t event_callback); +FT_DECLARE(ftdm_status_t) ftdm_channel_add_to_group(const char* name, ftdm_channel_t* ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_remove_from_group(ftdm_group_t* group, ftdm_channel_t* ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_group_find(uint32_t id, ftdm_group_t **group); +FT_DECLARE(ftdm_status_t) ftdm_group_find_by_name(const char *name, ftdm_group_t **group); +FT_DECLARE(ftdm_status_t) ftdm_group_create(ftdm_group_t **group, const char *name); +FT_DECLARE(ftdm_status_t) ftdm_channel_set_event_callback(ftdm_channel_t *ftdmchan, fio_event_cb_t event_callback); +FT_DECLARE(ftdm_status_t) ftdm_channel_open(uint32_t span_id, uint32_t chan_id, ftdm_channel_t **ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_span_channel_use_count(ftdm_span_t *span, uint32_t *count); +FT_DECLARE(ftdm_status_t) ftdm_group_channel_use_count(ftdm_group_t *group, uint32_t *count); +FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_group(uint32_t group_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_close(ftdm_channel_t **ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_use(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_command_t command, void *obj); +FT_DECLARE(ftdm_status_t) ftdm_channel_wait(ftdm_channel_t *ftdmchan, ftdm_wait_flag_t *flags, int32_t to); +FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen); +FT_DECLARE(void) ftdm_generate_sln_silence(int16_t *data, uint32_t samples, uint32_t divisor); +FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t datasize, ftdm_size_t *datalen); +FT_DECLARE(ftdm_status_t) ftdm_channel_add_var(ftdm_channel_t *ftdmchan, const char *var_name, const char *value); +FT_DECLARE(const char *) ftdm_channel_get_var(ftdm_channel_t *ftdmchan, const char *var_name); +FT_DECLARE(ftdm_status_t) ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_global_init(void); +FT_DECLARE(ftdm_status_t) ftdm_global_configuration(void); +FT_DECLARE(ftdm_status_t) ftdm_global_destroy(void); +FT_DECLARE(ftdm_status_t) ftdm_global_set_memory_handler(ftdm_memory_handler_t *handler); +FT_DECLARE(void) ftdm_global_set_crash_policy(ftdm_crash_policy_t policy); +FT_DECLARE(void) ftdm_global_set_logger(ftdm_logger_t logger); +FT_DECLARE(void) ftdm_global_set_default_logger(int level); +FT_DECLARE(uint32_t) ftdm_separate_string(char *buf, char delim, char **array, int arraylen); +FT_DECLARE(void) print_bits(uint8_t *b, int bl, char *buf, int blen, int e, uint8_t ss); +FT_DECLARE(void) print_hex_bytes(uint8_t *data, ftdm_size_t dlen, char *buf, ftdm_size_t blen); +FT_DECLARE_NONSTD(int) ftdm_hash_equalkeys(void *k1, void *k2); +FT_DECLARE_NONSTD(uint32_t) ftdm_hash_hashfromstring(void *ky); +FT_DECLARE(uint32_t) ftdm_running(void); +FT_DECLARE(ftdm_status_t) ftdm_channel_complete_state(ftdm_channel_t *ftdmchan); +FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *ftdmchan); +FT_DECLARE(int) ftdm_load_modules(void); +FT_DECLARE(ftdm_status_t) ftdm_unload_modules(void); +FT_DECLARE(ftdm_status_t) ftdm_configure_span(const char *type, ftdm_span_t *span, fio_signal_cb_t sig_cb, ...); +FT_DECLARE(ftdm_status_t) ftdm_configure_span_signaling(const char *type, ftdm_span_t *span, fio_signal_cb_t sig_cb, ftdm_conf_parameter_t *parameters); +FT_DECLARE(ftdm_status_t) ftdm_span_start(ftdm_span_t *span); +FT_DECLARE(ftdm_status_t) ftdm_span_stop(ftdm_span_t *span); +FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t *sigmsg); +FT_DECLARE(char *) ftdm_build_dso_path(const char *name, char *path, ftdm_size_t len); +FT_DECLARE(ftdm_status_t) ftdm_global_add_io_interface(ftdm_io_interface_t *io_interface); +FT_DECLARE(int) ftdm_load_module(const char *name); +FT_DECLARE(int) ftdm_load_module_assume(const char *name); +FT_DECLARE(ftdm_status_t) ftdm_span_find_by_name(const char *name, ftdm_span_t **span); +FT_DECLARE(char *) ftdm_api_execute(const char *type, const char *cmd); +FT_DECLARE(int) ftdm_vasprintf(char **ret, const char *fmt, va_list ap); +FT_DECLARE(ftdm_status_t) ftdm_channel_set_caller_data(ftdm_channel_t *ftdmchan, ftdm_caller_data_t *caller_data); + +FIO_CODEC_FUNCTION(fio_slin2ulaw); +FIO_CODEC_FUNCTION(fio_ulaw2slin); +FIO_CODEC_FUNCTION(fio_slin2alaw); +FIO_CODEC_FUNCTION(fio_alaw2slin); +FIO_CODEC_FUNCTION(fio_ulaw2alaw); +FIO_CODEC_FUNCTION(fio_alaw2ulaw); + +#ifdef DEBUG_LOCKS +#define ftdm_mutex_lock(_x) printf("++++++lock %s:%d\n", __FILE__, __LINE__) && _ftdm_mutex_lock(_x) +#define ftdm_mutex_trylock(_x) printf("++++++try %s:%d\n", __FILE__, __LINE__) && _ftdm_mutex_trylock(_x) +#define ftdm_mutex_unlock(_x) printf("------unlock %s:%d\n", __FILE__, __LINE__) && _ftdm_mutex_unlock(_x) +#else +#define ftdm_mutex_lock(_x) _ftdm_mutex_lock(_x) +#define ftdm_mutex_trylock(_x) _ftdm_mutex_trylock(_x) +#define ftdm_mutex_unlock(_x) _ftdm_mutex_unlock(_x) +#endif + +/*! + \brief Assert condition +*/ +#define ftdm_assert(assertion, msg) \ + if (!(assertion)) { \ + ftdm_log(FTDM_LOG_CRIT, msg); \ + if (g_ftdm_crash_policy & FTDM_CRASH_ON_ASSERT) { \ + ftdm_abort(); \ + } \ + } + +/*! + \brief Assert condition and return +*/ +#define ftdm_assert_return(assertion, retval, msg) \ + if (!(assertion)) { \ + ftdm_log(FTDM_LOG_CRIT, msg); \ + if (g_ftdm_crash_policy & FTDM_CRASH_ON_ASSERT) { \ + ftdm_abort(); \ + } else { \ + return retval; \ + } \ + } + +/*! + \brief Allocate uninitialized memory + \command chunksize the chunk size +*/ +#define ftdm_malloc(chunksize) g_ftdm_mem_handler.malloc(g_ftdm_mem_handler.pool, chunksize) + +/*! + \brief Allocate initialized memory + \command chunksize the chunk size +*/ +#define ftdm_calloc(elements, chunksize) g_ftdm_mem_handler.calloc(g_ftdm_mem_handler.pool, elements, chunksize) + +/*! + \brief Free chunk of memory + \command chunksize the chunk size +*/ +#define ftdm_free(chunk) g_ftdm_mem_handler.free(g_ftdm_mem_handler.pool, chunk) + +/*! + \brief Free a pointer and set it to NULL unless it already is NULL + \command it the pointer +*/ +#define ftdm_safe_free(it) if (it) { ftdm_free(it); it = NULL; } + +/*! + \brief Socket the given socket + \command it the socket +*/ +#define ftdm_socket_close(it) if (it > -1) { close(it); it = -1;} + +#define ftdm_array_len(array) sizeof(array)/sizeof(array[0]) + +static __inline__ void ftdm_abort(void) +{ +#ifdef __cplusplus + ::abort(); +#else + abort(); +#endif +} + +static __inline__ void ftdm_set_state_all(ftdm_span_t *span, ftdm_channel_state_t state) +{ + uint32_t j; + ftdm_mutex_lock(span->mutex); + for(j = 1; j <= span->chan_count; j++) { + ftdm_set_state_locked((span->channels[j]), state); + } + ftdm_mutex_unlock(span->mutex); +} + +static __inline__ int ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state) +{ + uint32_t j; + for(j = 1; j <= span->chan_count; j++) { + if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) { + return 0; + } + } + + return 1; +} + +static __inline__ void ftdm_set_flag_all(ftdm_span_t *span, uint32_t flag) +{ + uint32_t j; + ftdm_mutex_lock(span->mutex); + for(j = 1; j <= span->chan_count; j++) { + ftdm_set_flag_locked((span->channels[j]), flag); + } + ftdm_mutex_unlock(span->mutex); +} + +static __inline__ void ftdm_clear_flag_all(ftdm_span_t *span, uint32_t flag) +{ + uint32_t j; + ftdm_mutex_lock(span->mutex); + for(j = 1; j <= span->chan_count; j++) { + ftdm_clear_flag_locked((span->channels[j]), flag); + } + ftdm_mutex_unlock(span->mutex); +} + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif + +/* 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: + */ diff --git a/libs/freetdm/src/include/fsk.h b/libs/freetdm/src/include/fsk.h new file mode 100644 index 0000000000..66da140df9 --- /dev/null +++ b/libs/freetdm/src/include/fsk.h @@ -0,0 +1,121 @@ +/* + * bell202.h + * + * Copyright (c) 2005 Robert Krten. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This module contains the manifest constants and declarations for + * the Bell-202 1200 baud FSK modem. + * + * 2005 03 20 R. Krten created +*/ + +#ifndef __FSK_H__ +#define __FSK_H__ +#include "uart.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int freq_space; /* Frequency of the 0 bit */ + int freq_mark; /* Frequency of the 1 bit */ + int baud_rate; /* baud rate for the modem */ +} fsk_modem_definition_t; + +/* Must be kept in sync with fsk_modem_definitions array in fsk.c */ +/* V.23 definitions: http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-V.23 */ +typedef enum { + FSK_V23_FORWARD_MODE1 = 0, /* Maximum 600 bps for long haul */ + FSK_V23_FORWARD_MODE2, /* Standard 1200 bps V.23 */ + FSK_V23_BACKWARD, /* 75 bps return path for V.23 */ + FSK_BELL202 /* Bell 202 half-duplex 1200 bps */ +} fsk_modem_types_t; + +typedef enum { + FSK_STATE_CHANSEIZE = 0, + FSK_STATE_CARRIERSIG, + FSK_STATE_DATA +} fsk_state_t; + +typedef struct dsp_fsk_attr_s +{ + int sample_rate; /* sample rate in HZ */ + bithandler_func_t bithandler; /* bit handler */ + void *bithandler_arg; /* arbitrary ID passed to bithandler as first argument */ + bytehandler_func_t bytehandler; /* byte handler */ + void *bytehandler_arg; /* arbitrary ID passed to bytehandler as first argument */ +} dsp_fsk_attr_t; + +typedef struct +{ + fsk_state_t state; + dsp_fsk_attr_t attr; /* attributes structure */ + double *correlates[4]; /* one for each of sin/cos for mark/space */ + int corrsize; /* correlate size (also number of samples in ring buffer) */ + double *buffer; /* sample ring buffer */ + int ringstart; /* ring buffer start offset */ + double cellpos; /* bit cell position */ + double celladj; /* bit cell adjustment for each sample */ + int previous_bit; /* previous bit (for detecting a transition to sync-up cell position) */ + int current_bit; /* current bit */ + int last_bit; + int downsampling_count; /* number of samples to skip */ + int current_downsample; /* current skip count */ + int conscutive_state_bits; /* number of bits in a row that matches the pattern for the current state */ +} dsp_fsk_handle_t; + +/* + * Function prototypes + * + * General calling order is: + * a) create the attributes structure (dsp_fsk_attr_init) + * b) initialize fields in the attributes structure (dsp_fsk_attr_set_*) + * c) create a Bell-202 handle (dsp_fsk_create) + * d) feed samples through the handler (dsp_fsk_sample) +*/ + +void dsp_fsk_attr_init(dsp_fsk_attr_t *attributes); + +bithandler_func_t dsp_fsk_attr_get_bithandler(dsp_fsk_attr_t *attributes, void **bithandler_arg); +void dsp_fsk_attr_set_bithandler(dsp_fsk_attr_t *attributes, bithandler_func_t bithandler, void *bithandler_arg); +bytehandler_func_t dsp_fsk_attr_get_bytehandler(dsp_fsk_attr_t *attributes, void **bytehandler_arg); +void dsp_fsk_attr_set_bytehandler(dsp_fsk_attr_t *attributes, bytehandler_func_t bytehandler, void *bytehandler_arg); +int dsp_fsk_attr_get_samplerate(dsp_fsk_attr_t *attributes); +int dsp_fsk_attr_set_samplerate(dsp_fsk_attr_t *attributes, int samplerate); + +dsp_fsk_handle_t * dsp_fsk_create(dsp_fsk_attr_t *attributes); +void dsp_fsk_destroy(dsp_fsk_handle_t **handle); + +void dsp_fsk_sample(dsp_fsk_handle_t *handle, double normalized_sample); + +extern fsk_modem_definition_t fsk_modem_definitions[]; + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif + diff --git a/libs/freetdm/src/include/ftdm_buffer.h b/libs/freetdm/src/include/ftdm_buffer.h new file mode 100644 index 0000000000..e069e2d44b --- /dev/null +++ b/libs/freetdm/src/include/ftdm_buffer.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 FTDM_BUFFER_H +#define FTDM_BUFFER_H + +#include "freetdm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup ftdm_buffer Buffer Routines + * @ingroup buffer + * The purpose of this module is to make a plain buffering interface that can be used for read/write buffers + * throughout the application. + * @{ + */ +struct ftdm_buffer; +typedef struct ftdm_buffer ftdm_buffer_t; + +/*! \brief Allocate a new dynamic ftdm_buffer + * \param buffer returned pointer to the new buffer + * \param blocksize length to realloc by as data is added + * \param start_len ammount of memory to reserve initially + * \param max_len length the buffer is allowed to grow to + * \return status + */ +FT_DECLARE(ftdm_status_t) ftdm_buffer_create(ftdm_buffer_t **buffer, ftdm_size_t blocksize, ftdm_size_t start_len, ftdm_size_t max_len); + +/*! \brief Get the length of a ftdm_buffer_t + * \param buffer any buffer of type ftdm_buffer_t + * \return int size of the buffer. + */ +FT_DECLARE(ftdm_size_t) ftdm_buffer_len(ftdm_buffer_t *buffer); + +/*! \brief Get the freespace of a ftdm_buffer_t + * \param buffer any buffer of type ftdm_buffer_t + * \return int freespace in the buffer. + */ +FT_DECLARE(ftdm_size_t) ftdm_buffer_freespace(ftdm_buffer_t *buffer); + +/*! \brief Get the in use amount of a ftdm_buffer_t + * \param buffer any buffer of type ftdm_buffer_t + * \return int ammount of buffer curently in use + */ +FT_DECLARE(ftdm_size_t) ftdm_buffer_inuse(ftdm_buffer_t *buffer); + +/*! \brief Read data from a ftdm_buffer_t up to the ammount of datalen if it is available. Remove read data from buffer. + * \param buffer any buffer of type ftdm_buffer_t + * \param data pointer to the read data to be returned + * \param datalen amount of data to be returned + * \return int ammount of data actually read + */ +FT_DECLARE(ftdm_size_t) ftdm_buffer_read(ftdm_buffer_t *buffer, void *data, ftdm_size_t datalen); + +/*! \brief Read data endlessly from a ftdm_buffer_t + * \param buffer any buffer of type ftdm_buffer_t + * \param data pointer to the read data to be returned + * \param datalen amount of data to be returned + * \return int ammount of data actually read + * \note Once you have read all the data from the buffer it will loop around. + */ +FT_DECLARE(ftdm_size_t) ftdm_buffer_read_loop(ftdm_buffer_t *buffer, void *data, ftdm_size_t datalen); + +/*! \brief Assign a number of loops to read + * \param buffer any buffer of type ftdm_buffer_t + * \param loops the number of loops (-1 for infinite) + */ +FT_DECLARE(void) ftdm_buffer_set_loops(ftdm_buffer_t *buffer, int32_t loops); + +/*! \brief Write data into a ftdm_buffer_t up to the length of datalen + * \param buffer any buffer of type ftdm_buffer_t + * \param data pointer to the data to be written + * \param datalen amount of data to be written + * \return int amount of buffer used after the write, or 0 if no space available + */ +FT_DECLARE(ftdm_size_t) ftdm_buffer_write(ftdm_buffer_t *buffer, const void *data, ftdm_size_t datalen); + +/*! \brief Remove data from the buffer + * \param buffer any buffer of type ftdm_buffer_t + * \param datalen amount of data to be removed + * \return int size of buffer, or 0 if unable to toss that much data + */ +FT_DECLARE(ftdm_size_t) ftdm_buffer_toss(ftdm_buffer_t *buffer, ftdm_size_t datalen); + +/*! \brief Remove all data from the buffer + * \param buffer any buffer of type ftdm_buffer_t + */ +FT_DECLARE(void) ftdm_buffer_zero(ftdm_buffer_t *buffer); + +/*! \brief Destroy the buffer + * \param buffer buffer to destroy + * \note only neccessary on dynamic buffers (noop on pooled ones) + */ +FT_DECLARE(void) ftdm_buffer_destroy(ftdm_buffer_t **buffer); + +/*! \brief Seek to offset from the beginning of the buffer + * \param buffer buffer to seek + * \param datalen offset in bytes + * \return new position + */ +FT_DECLARE(ftdm_size_t) ftdm_buffer_seek(ftdm_buffer_t *buffer, ftdm_size_t datalen); + +/** @} */ + +FT_DECLARE(ftdm_size_t) ftdm_buffer_zwrite(ftdm_buffer_t *buffer, const void *data, ftdm_size_t datalen); + +#ifdef __cplusplus +} +#endif + +#endif +/* 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: + */ diff --git a/libs/freetdm/src/include/ftdm_config.h b/libs/freetdm/src/include/ftdm_config.h new file mode 100644 index 0000000000..0f96a8f872 --- /dev/null +++ b/libs/freetdm/src/include/ftdm_config.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + */ + +/** + * @defgroup config Config File Parser + * @ingroup config + * This module implements a basic interface and file format parser + * + *
+ *
+ * EXAMPLE 
+ * 
+ * [category1]
+ * var1 => val1
+ * var2 => val2
+ * \# lines that begin with \# are comments
+ * \#var3 => val3
+ * 
+ * @{ + */ + +#ifndef FTDM_CONFIG_H +#define FTDM_CONFIG_H + +#include "freetdm.h" +#define FTDM_URL_SEPARATOR "://" + + +#ifdef WIN32 +#define FTDM_PATH_SEPARATOR "\\" +#ifndef FTDM_CONFIG_DIR +#define FTDM_CONFIG_DIR "c:\\freetdm" +#endif +#define ftdm_is_file_path(file) (*(file +1) == ':' || *file == '/' || strstr(file, SWITCH_URL_SEPARATOR)) +#else +#define FTDM_PATH_SEPARATOR "/" +#ifndef FTDM_CONFIG_DIR +#define FTDM_CONFIG_DIR "/etc/freetdm" +#endif +#define ftdm_is_file_path(file) ((*file == '/') || strstr(file, SWITCH_URL_SEPARATOR)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ftdm_config ftdm_config_t; + +/*! \brief A simple file handle representing an open configuration file **/ +struct ftdm_config { + /*! FILE stream buffer to the opened file */ + FILE *file; + /*! path to the file */ + char path[512]; + /*! current category */ + char category[256]; + /*! current section */ + char section[256]; + /*! buffer of current line being read */ + char buf[1024]; + /*! current line number in file */ + int lineno; + /*! current category number in file */ + int catno; + /*! current section number in file */ + int sectno; + + int lockto; +}; + +/*! + \brief Open a configuration file + \param cfg (ftdm_config_t *) config handle to use + \param file_path path to the file + \return 1 (true) on success 0 (false) on failure +*/ +int ftdm_config_open_file(ftdm_config_t * cfg, const char *file_path); + +/*! + \brief Close a previously opened configuration file + \param cfg (ftdm_config_t *) config handle to use +*/ +void ftdm_config_close_file(ftdm_config_t * cfg); + +/*! + \brief Retrieve next name/value pair from configuration file + \param cfg (ftdm_config_t *) config handle to use + \param var pointer to aim at the new variable name + \param val pointer to aim at the new value +*/ +int ftdm_config_next_pair(ftdm_config_t * cfg, char **var, char **val); + +/*! + \brief Retrieve the CAS bits from a configuration string value + \param strvalue pointer to the configuration string value (expected to be in format whatever:xxxx) + \param outbits pointer to aim at the CAS bits +*/ +FT_DECLARE (int) ftdm_config_get_cas_bits(char *strvalue, unsigned char *outbits); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif +/* 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: + */ diff --git a/libs/freetdm/src/include/ftdm_dso.h b/libs/freetdm/src/include/ftdm_dso.h new file mode 100644 index 0000000000..c4c4b705d1 --- /dev/null +++ b/libs/freetdm/src/include/ftdm_dso.h @@ -0,0 +1,51 @@ +/* + * Cross Platform dso/dll load abstraction + * Copyright(C) 2008 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + + +#ifndef _FTDM_DSO_H +#define _FTDM_DSO_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*ftdm_func_ptr_t) (void); +typedef void * ftdm_dso_lib_t; + +FT_DECLARE(void) ftdm_dso_destroy(ftdm_dso_lib_t *lib); +FT_DECLARE(ftdm_dso_lib_t) ftdm_dso_open(const char *path, char **err); +FT_DECLARE(void *) ftdm_dso_func_sym(ftdm_dso_lib_t lib, const char *sym, char **err); + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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 + */ + diff --git a/libs/freetdm/src/include/ftdm_m3ua.h b/libs/freetdm/src/include/ftdm_m3ua.h new file mode 100644 index 0000000000..1bf830853c --- /dev/null +++ b/libs/freetdm/src/include/ftdm_m3ua.h @@ -0,0 +1,134 @@ +/* + * ftdm_m3ua.h + * freetdm + * + * Created by Shane Burrell on 4/3/08. + * Copyright 2008 Shane Burrell. All rights reserved. + * + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * + * 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 "m3ua_client.h" +#include "freetdm.h" + +#ifdef __cplusplus +extern "C" { +#endif +enum e_sigboost_event_id_values +{ + SIGBOOST_EVENT_CALL_START = 0x80, /*128*/ + SIGBOOST_EVENT_CALL_START_ACK = 0x81, /*129*/ + SIGBOOST_EVENT_CALL_START_NACK = 0x82, /*130*/ + SIGBOOST_EVENT_CALL_START_NACK_ACK = 0x83, /*131*/ + SIGBOOST_EVENT_CALL_ANSWERED = 0x84, /*132*/ + SIGBOOST_EVENT_CALL_STOPPED = 0x85, /*133*/ + SIGBOOST_EVENT_CALL_STOPPED_ACK = 0x86, /*134*/ + SIGBOOST_EVENT_SYSTEM_RESTART = 0x87, /*135*/ + SIGBOOST_EVENT_SYSTEM_RESTART_ACK = 0x88, /*136*/ + /* Following IDs are ss7boost to sangoma_mgd only. */ + SIGBOOST_EVENT_HEARTBEAT = 0x89, /*137*/ + SIGBOOST_EVENT_INSERT_CHECK_LOOP = 0x8a, /*138*/ + SIGBOOST_EVENT_REMOVE_CHECK_LOOP = 0x8b, /*139*/ + SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE = 0x8c, /*140*/ +}; +enum e_sigboost_release_cause_values +{ + SIGBOOST_RELEASE_CAUSE_UNDEFINED = 0, + SIGBOOST_RELEASE_CAUSE_NORMAL = 16, + SIGBOOST_RELEASE_CAUSE_BUSY = 17, + /* probable elimination */ + //SIGBOOST_RELEASE_CAUSE_BUSY = 0x91, /* 145 */ + //SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST = 0x92, /* 146 */ + //SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET = 0x93, /* 147 */ + //SIGBOOST_RELEASE_CAUSE_NOANSWER = 0x94, /* 148 */ +}; + +enum e_sigboost_call_setup_ack_nack_cause_values +{ + SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 117, /* unused Q.850 value */ + SIGBOOST_CALL_SETUP_NACK_TEST_CKT_BUSY = 118, /* unused Q.850 value */ + SIGBOOST_CALL_SETUP_NACK_INVALID_NUMBER = 28, + /* probable elimination */ + //SIGBOOST_CALL_SETUP_RESERVED = 0x00, + //SIGBOOST_CALL_SETUP_CIRCUIT_RESET = 0x10, + //SIGBOOST_CALL_SETUP_NACK_CKT_START_TIMEOUT = 0x11, + //SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP = 0x17, +}; +typedef enum { + M3UA_SPAN_SIGNALING_M3UA, + M3UA_SPAN_SIGNALING_SS7BOX, + +} M3UA_TSpanSignaling; +#define M3UA_SPAN_STRINGS "M3UA", "SS7BOX" +FTDM_STR2ENUM_P(m3ua_str2span, m3ua_span2str, M3UA_TSpanSignaling) + + + +typedef enum { + FTDM_M3UA_RUNNING = (1 << 0) +} ftdm_m3uat_flag_t; + +/*typedef struct m3ua_data { + m3uac_connection_t mcon; + m3uac_connection_t pcon; + fio_signal_cb_t signal_cb; + uint32_t flags; +} m3ua_data_t; + +*/ +/*typedef struct mu3a_link { + ss7bc_connection_t mcon; + ss7bc_connection_t pcon; + fio_signal_cb_t signal_cb; + uint32_t flags; +} ftdm_m3ua_data_t; +*/ + +ftdm_status_t m3ua_init(ftdm_io_interface_t **zint); +ftdm_status_t m3ua_destroy(void); +ftdm_status_t m3ua_start(ftdm_span_t *span); + +#ifdef __cplusplus +} +#endif + +/* 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: + */ + diff --git a/libs/freetdm/src/include/ftdm_threadmutex.h b/libs/freetdm/src/include/ftdm_threadmutex.h new file mode 100644 index 0000000000..f67d2840fe --- /dev/null +++ b/libs/freetdm/src/include/ftdm_threadmutex.h @@ -0,0 +1,68 @@ +/* + * Cross Platform Thread/Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + * Contributors: + * + * Moises Silva + * + */ + + +#ifndef _FTDM_THREADMUTEX_H +#define _FTDM_THREADMUTEX_H + +#include "freetdm.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ftdm_mutex ftdm_mutex_t; +typedef struct ftdm_thread ftdm_thread_t; +typedef struct ftdm_interrupt ftdm_interrupt_t; +typedef void *(*ftdm_thread_function_t) (ftdm_thread_t *, void *); + +FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached(ftdm_thread_function_t func, void *data); +FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached_ex(ftdm_thread_function_t func, void *data, ftdm_size_t stack_size); +FT_DECLARE(void) ftdm_thread_override_default_stacksize(ftdm_size_t size); +FT_DECLARE(ftdm_status_t) ftdm_mutex_create(ftdm_mutex_t **mutex); +FT_DECLARE(ftdm_status_t) ftdm_mutex_destroy(ftdm_mutex_t **mutex); +FT_DECLARE(ftdm_status_t) _ftdm_mutex_lock(ftdm_mutex_t *mutex); +FT_DECLARE(ftdm_status_t) _ftdm_mutex_trylock(ftdm_mutex_t *mutex); +FT_DECLARE(ftdm_status_t) _ftdm_mutex_unlock(ftdm_mutex_t *mutex); +FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **cond, ftdm_socket_t device); +FT_DECLARE(ftdm_status_t) ftdm_interrupt_destroy(ftdm_interrupt_t **cond); +FT_DECLARE(ftdm_status_t) ftdm_interrupt_signal(ftdm_interrupt_t *cond); +FT_DECLARE(ftdm_status_t) ftdm_interrupt_wait(ftdm_interrupt_t *cond, int ms); +FT_DECLARE(ftdm_status_t) ftdm_interrupt_multiple_wait(ftdm_interrupt_t *interrupts[], ftdm_size_t size, int ms); + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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: + */ + diff --git a/libs/freetdm/src/include/ftdm_types.h b/libs/freetdm/src/include/ftdm_types.h new file mode 100644 index 0000000000..7260358c8f --- /dev/null +++ b/libs/freetdm/src/include/ftdm_types.h @@ -0,0 +1,708 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + * Contributors: + * + * Moises Silva + * + */ + +#ifndef FTDM_TYPES_H +#define FTDM_TYPES_H +#include "fsk.h" + +#define FTDM_INVALID_SOCKET -1 +#ifdef WIN32 +#include +typedef HANDLE ftdm_socket_t; +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +typedef intptr_t ftdm_ssize_t; +typedef int ftdm_filehandle_t; +#else +#include +#include +#include +#include +typedef int ftdm_socket_t; +typedef ssize_t ftdm_ssize_t; +typedef int ftdm_filehandle_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#define TAG_END NULL + +typedef size_t ftdm_size_t; +struct ftdm_io_interface; + +#define FTDM_COMMAND_OBJ_INT *((int *)obj) +#define FTDM_COMMAND_OBJ_CHAR_P (char *)obj +#define FTDM_COMMAND_OBJ_FLOAT *(float *)obj +#define FTDM_FSK_MOD_FACTOR 0x10000 +#define FTDM_DEFAULT_DTMF_ON 250 +#define FTDM_DEFAULT_DTMF_OFF 50 + +#define FTDM_END -1 +#define FTDM_ANY_STATE -1 + +typedef uint64_t ftdm_time_t; + +typedef enum { + FTDM_ENDIAN_BIG = 1, + FTDM_ENDIAN_LITTLE = -1 +} ftdm_endian_t; + +typedef enum { + FTDM_CID_TYPE_SDMF = 0x04, + FTDM_CID_TYPE_MDMF = 0x80 +} ftdm_cid_type_t; + +typedef enum { + MDMF_DATETIME = 1, + MDMF_PHONE_NUM = 2, + MDMF_DDN = 3, + MDMF_NO_NUM = 4, + MDMF_PHONE_NAME = 7, + MDMF_NO_NAME = 8, + MDMF_ALT_ROUTE = 9, + MDMF_INVALID = 10 +} ftdm_mdmf_type_t; +#define MDMF_STRINGS "X", "DATETIME", "PHONE_NUM", "DDN", "NO_NUM", "X", "X", "PHONE_NAME", "NO_NAME", "ALT_ROUTE", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_mdmf_type, ftdm_mdmf_type2str, ftdm_mdmf_type_t) + +#define FTDM_TONEMAP_LEN 128 +typedef enum { + FTDM_TONEMAP_NONE, + FTDM_TONEMAP_DIAL, + FTDM_TONEMAP_RING, + FTDM_TONEMAP_BUSY, + FTDM_TONEMAP_FAIL1, + FTDM_TONEMAP_FAIL2, + FTDM_TONEMAP_FAIL3, + FTDM_TONEMAP_ATTN, + FTDM_TONEMAP_CALLWAITING_CAS, + FTDM_TONEMAP_CALLWAITING_SAS, + FTDM_TONEMAP_CALLWAITING_ACK, + FTDM_TONEMAP_INVALID +} ftdm_tonemap_t; +#define TONEMAP_STRINGS "NONE", "DIAL", "RING", "BUSY", "FAIL1", "FAIL2", "FAIL3", "ATTN", "CALLWAITING-CAS", "CALLWAITING-SAS", "CALLWAITING-ACK", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_tonemap, ftdm_tonemap2str, ftdm_tonemap_t) + +typedef enum { + FTDM_TRUNK_E1, + FTDM_TRUNK_T1, + FTDM_TRUNK_J1, + FTDM_TRUNK_BRI, + FTDM_TRUNK_BRI_PTMP, + FTDM_TRUNK_FXO, + FTDM_TRUNK_FXS, + FTDM_TRUNK_EM, + FTDM_TRUNK_NONE +} ftdm_trunk_type_t; +#define TRUNK_STRINGS "E1", "T1", "J1", "BRI", "BRI_PTMP", "FXO", "FXS", "EM", "NONE" +FTDM_STR2ENUM_P(ftdm_str2ftdm_trunk_type, ftdm_trunk_type2str, ftdm_trunk_type_t) + +typedef enum { + FTDM_ANALOG_START_KEWL, + FTDM_ANALOG_START_LOOP, + FTDM_ANALOG_START_GROUND, + FTDM_ANALOG_START_WINK, + FTDM_ANALOG_START_NA +} ftdm_analog_start_type_t; +#define START_TYPE_STRINGS "KEWL", "LOOP", "GROUND", "WINK", "NA" +FTDM_STR2ENUM_P(ftdm_str2ftdm_analog_start_type, ftdm_analog_start_type2str, ftdm_analog_start_type_t) + +typedef enum { + FTDM_OOB_ONHOOK, + FTDM_OOB_OFFHOOK, + FTDM_OOB_WINK, + FTDM_OOB_FLASH, + FTDM_OOB_RING_START, + FTDM_OOB_RING_STOP, + FTDM_OOB_ALARM_TRAP, + FTDM_OOB_ALARM_CLEAR, + FTDM_OOB_NOOP, + FTDM_OOB_CAS_BITS_CHANGE, + FTDM_OOB_INVALID +} ftdm_oob_event_t; +#define OOB_STRINGS "DTMF", "ONHOOK", "OFFHOOK", "WINK", "FLASH", "RING_START", "RING_STOP", "ALARM_TRAP", "ALARM_CLEAR", "NOOP", "CAS_BITS_CHANGE", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_oob_event, ftdm_oob_event2str, ftdm_oob_event_t) + +typedef enum { + FTDM_ALARM_NONE = 0, + FTDM_ALARM_RECOVER = (1 << 0), + FTDM_ALARM_LOOPBACK = (1 << 2), + FTDM_ALARM_YELLOW = (1 << 3), + FTDM_ALARM_RED = (1 << 4), + FTDM_ALARM_BLUE = (1 << 5), + FTDM_ALARM_NOTOPEN = ( 1 << 6), + FTDM_ALARM_AIS = ( 1 << 7), + FTDM_ALARM_RAI = ( 1 << 8), + FTDM_ALARM_GENERAL = ( 1 << 30) +} ftdm_alarm_flag_t; + +typedef enum { + FTDM_SIGTYPE_NONE, + FTDM_SIGTYPE_ISDN, + FTDM_SIGTYPE_RBS, + FTDM_SIGTYPE_ANALOG, + FTDM_SIGTYPE_SANGOMABOOST, + FTDM_SIGTYPE_M3UA, + FTDM_SIGTYPE_R2 +} ftdm_signal_type_t; + +/*! + \brief Signaling status on a given span or specific channel on protocols that support it + */ +typedef enum { + /* The signaling link is down (no d-chans up in the span/group, MFC-R2 bit pattern unidentified) */ + FTDM_SIG_STATE_DOWN, + /* The signaling link is suspended (MFC-R2 bit pattern blocked, ss7 blocked?) */ + FTDM_SIG_STATE_SUSPENDED, + /* The signaling link is ready and calls can be placed */ + FTDM_SIG_STATE_UP, + /* Invalid status */ + FTDM_SIG_STATE_INVALID +} ftdm_signaling_status_t; +#define SIGSTATUS_STRINGS "DOWN", "SUSPENDED", "UP", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_signaling_status, ftdm_signaling_status2str, ftdm_signaling_status_t) + +typedef enum { + FTDM_SIGEVENT_START, + FTDM_SIGEVENT_STOP, + FTDM_SIGEVENT_TRANSFER, + FTDM_SIGEVENT_ANSWER, + FTDM_SIGEVENT_UP, + FTDM_SIGEVENT_FLASH, + FTDM_SIGEVENT_PROGRESS, + FTDM_SIGEVENT_PROGRESS_MEDIA, + FTDM_SIGEVENT_NOTIFY, + FTDM_SIGEVENT_TONE_DETECTED, + FTDM_SIGEVENT_ALARM_TRAP, + FTDM_SIGEVENT_ALARM_CLEAR, + FTDM_SIGEVENT_MISC, + FTDM_SIGEVENT_COLLECTED_DIGIT, + FTDM_SIGEVENT_ADD_CALL, + FTDM_SIGEVENT_RESTART, + /* Signaling status changed (D-chan up, down, R2 blocked etc) */ + FTDM_SIGEVENT_SIGSTATUS_CHANGED, + FTDM_SIGEVENT_INVALID +} ftdm_signal_event_t; +#define SIGNAL_STRINGS "START", "STOP", "TRANSFER", "ANSWER", "UP", "FLASH", "PROGRESS", \ + "PROGRESS_MEDIA", "NOTIFY", "TONE_DETECTED", "ALARM_TRAP", "ALARM_CLEAR", "MISC", \ + "COLLECTED_DIGIT", "ADD_CALL", "RESTART", "SIGLINK_CHANGED", "HWSTATUS_CHANGED", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_signal_event, ftdm_signal_event2str, ftdm_signal_event_t) + +typedef enum { + FTDM_EVENT_NONE, + FTDM_EVENT_DTMF, + FTDM_EVENT_OOB, + FTDM_EVENT_COUNT +} ftdm_event_type_t; + +typedef enum { + FTDM_TOP_DOWN, + FTDM_BOTTOM_UP +} ftdm_direction_t; + +typedef enum { + FTDM_SUCCESS, + FTDM_FAIL, + FTDM_MEMERR, + FTDM_TIMEOUT, + FTDM_NOTIMPL, + FTDM_CHECKSUM_ERROR, + FTDM_STATUS_COUNT, + FTDM_BREAK +} ftdm_status_t; + +typedef enum { + FTDM_NO_FLAGS = 0, + FTDM_READ = (1 << 0), + FTDM_WRITE = (1 << 1), + FTDM_EVENTS = (1 << 2) +} ftdm_wait_flag_t; + +typedef enum { + FTDM_CODEC_ULAW = 0, + FTDM_CODEC_ALAW = 8, + FTDM_CODEC_SLIN = 10, + FTDM_CODEC_NONE = (1 << 30) +} ftdm_codec_t; + +typedef enum { + FTDM_TONE_DTMF = (1 << 0) +} ftdm_tone_type_t; + +typedef enum { + FTDM_COMMAND_NOOP, + FTDM_COMMAND_SET_INTERVAL, + FTDM_COMMAND_GET_INTERVAL, + FTDM_COMMAND_SET_CODEC, + FTDM_COMMAND_GET_CODEC, + FTDM_COMMAND_SET_NATIVE_CODEC, + FTDM_COMMAND_GET_NATIVE_CODEC, + FTDM_COMMAND_ENABLE_DTMF_DETECT, + FTDM_COMMAND_DISABLE_DTMF_DETECT, + FTDM_COMMAND_SEND_DTMF, + FTDM_COMMAND_SET_DTMF_ON_PERIOD, + FTDM_COMMAND_GET_DTMF_ON_PERIOD, + FTDM_COMMAND_SET_DTMF_OFF_PERIOD, + FTDM_COMMAND_GET_DTMF_OFF_PERIOD, + FTDM_COMMAND_GENERATE_RING_ON, + FTDM_COMMAND_GENERATE_RING_OFF, + FTDM_COMMAND_OFFHOOK, + FTDM_COMMAND_ONHOOK, + FTDM_COMMAND_FLASH, + FTDM_COMMAND_WINK, + FTDM_COMMAND_ENABLE_PROGRESS_DETECT, + FTDM_COMMAND_DISABLE_PROGRESS_DETECT, + FTDM_COMMAND_TRACE_INPUT, + FTDM_COMMAND_TRACE_OUTPUT, + FTDM_COMMAND_ENABLE_CALLERID_DETECT, + FTDM_COMMAND_DISABLE_CALLERID_DETECT, + FTDM_COMMAND_ENABLE_ECHOCANCEL, + FTDM_COMMAND_DISABLE_ECHOCANCEL, + FTDM_COMMAND_ENABLE_ECHOTRAIN, + FTDM_COMMAND_DISABLE_ECHOTRAIN, + FTDM_COMMAND_SET_CAS_BITS, + FTDM_COMMAND_GET_CAS_BITS, + FTDM_COMMAND_SET_RX_GAIN, + FTDM_COMMAND_GET_RX_GAIN, + FTDM_COMMAND_SET_TX_GAIN, + FTDM_COMMAND_GET_TX_GAIN, + FTDM_COMMAND_FLUSH_TX_BUFFERS, + FTDM_COMMAND_FLUSH_RX_BUFFERS, + FTDM_COMMAND_FLUSH_BUFFERS, + FTDM_COMMAND_SET_PRE_BUFFER_SIZE, + FTDM_COMMAND_SET_LINK_STATUS, + FTDM_COMMAND_GET_LINK_STATUS, + FTDM_COMMAND_ENABLE_LOOP, + FTDM_COMMAND_DISABLE_LOOP, + FTDM_COMMAND_COUNT +} ftdm_command_t; + +typedef enum { + FTDM_SPAN_CONFIGURED = (1 << 0), + FTDM_SPAN_READY = (1 << 1), + FTDM_SPAN_STATE_CHANGE = (1 << 2), + FTDM_SPAN_SUSPENDED = (1 << 3), + FTDM_SPAN_IN_THREAD = (1 << 4), + FTDM_SPAN_STOP_THREAD = (1 << 5) +} ftdm_span_flag_t; + +typedef enum { + FTDM_CHAN_TYPE_B, + FTDM_CHAN_TYPE_DQ921, + FTDM_CHAN_TYPE_DQ931, + FTDM_CHAN_TYPE_FXS, + FTDM_CHAN_TYPE_FXO, + FTDM_CHAN_TYPE_EM, + FTDM_CHAN_TYPE_CAS, + FTDM_CHAN_TYPE_COUNT +} ftdm_chan_type_t; + +#define FTDM_IS_VOICE_CHANNEL(ftdm_chan) ((ftdm_chan)->type != FTDM_CHAN_TYPE_DQ921 && (ftdm_chan)->type != FTDM_CHAN_TYPE_DQ931) + +#define CHAN_TYPE_STRINGS "B", "DQ921", "DQ931", "FXS", "FXO", "EM", "CAS", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_chan_type, ftdm_chan_type2str, ftdm_chan_type_t) + +typedef enum { + FTDM_CHANNEL_FEATURE_DTMF_DETECT = (1 << 0), + FTDM_CHANNEL_FEATURE_DTMF_GENERATE = (1 << 1), + FTDM_CHANNEL_FEATURE_CODECS = (1 << 2), + FTDM_CHANNEL_FEATURE_INTERVAL = (1 << 3), + FTDM_CHANNEL_FEATURE_CALLERID = (1 << 4), + FTDM_CHANNEL_FEATURE_PROGRESS = (1 << 5) +} ftdm_channel_feature_t; + +typedef enum { + FTDM_CHANNEL_STATE_DOWN, + FTDM_CHANNEL_STATE_HOLD, + FTDM_CHANNEL_STATE_SUSPENDED, + FTDM_CHANNEL_STATE_DIALTONE, + FTDM_CHANNEL_STATE_COLLECT, + FTDM_CHANNEL_STATE_RING, + FTDM_CHANNEL_STATE_BUSY, + FTDM_CHANNEL_STATE_ATTN, + FTDM_CHANNEL_STATE_GENRING, + FTDM_CHANNEL_STATE_DIALING, + FTDM_CHANNEL_STATE_GET_CALLERID, + FTDM_CHANNEL_STATE_CALLWAITING, + FTDM_CHANNEL_STATE_RESTART, + FTDM_CHANNEL_STATE_PROGRESS, + FTDM_CHANNEL_STATE_PROGRESS_MEDIA, + FTDM_CHANNEL_STATE_UP, + FTDM_CHANNEL_STATE_IDLE, + FTDM_CHANNEL_STATE_TERMINATING, + FTDM_CHANNEL_STATE_CANCEL, + FTDM_CHANNEL_STATE_HANGUP, + FTDM_CHANNEL_STATE_HANGUP_COMPLETE, + FTDM_CHANNEL_STATE_IN_LOOP, + FTDM_CHANNEL_STATE_INVALID +} ftdm_channel_state_t; +#define CHANNEL_STATE_STRINGS "DOWN", "HOLD", "SUSPENDED", "DIALTONE", "COLLECT", \ + "RING", "BUSY", "ATTN", "GENRING", "DIALING", "GET_CALLERID", "CALLWAITING", \ + "RESTART", "PROGRESS", "PROGRESS_MEDIA", "UP", "IDLE", "TERMINATING", "CANCEL", "HANGUP", "HANGUP_COMPLETE", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t) + +typedef enum { + FTDM_CHANNEL_CONFIGURED = (1 << 0), + FTDM_CHANNEL_READY = (1 << 1), + FTDM_CHANNEL_OPEN = (1 << 2), + FTDM_CHANNEL_DTMF_DETECT = (1 << 3), + FTDM_CHANNEL_SUPRESS_DTMF = (1 << 4), + FTDM_CHANNEL_TRANSCODE = (1 << 5), + FTDM_CHANNEL_BUFFER = (1 << 6), + FTDM_CHANNEL_EVENT = (1 << 7), + FTDM_CHANNEL_INTHREAD = (1 << 8), + FTDM_CHANNEL_WINK = (1 << 9), + FTDM_CHANNEL_FLASH = (1 << 10), + FTDM_CHANNEL_STATE_CHANGE = (1 << 11), + FTDM_CHANNEL_HOLD = (1 << 12), + FTDM_CHANNEL_INUSE = (1 << 13), + FTDM_CHANNEL_OFFHOOK = (1 << 14), + FTDM_CHANNEL_RINGING = (1 << 15), + FTDM_CHANNEL_PROGRESS_DETECT = (1 << 16), + FTDM_CHANNEL_CALLERID_DETECT = (1 << 17), + FTDM_CHANNEL_OUTBOUND = (1 << 18), + FTDM_CHANNEL_SUSPENDED = (1 << 19), + FTDM_CHANNEL_3WAY = (1 << 20), + FTDM_CHANNEL_PROGRESS = (1 << 21), + FTDM_CHANNEL_MEDIA = (1 << 22), + FTDM_CHANNEL_ANSWERED = (1 << 23), + FTDM_CHANNEL_MUTE = (1 << 24), + FTDM_CHANNEL_USE_RX_GAIN = (1 << 25), + FTDM_CHANNEL_USE_TX_GAIN = (1 << 26), + FTDM_CHANNEL_IN_ALARM = (1 << 27), +} ftdm_channel_flag_t; +#if defined(__cplusplus) && defined(WIN32) + // fix C2676 +__inline__ ftdm_channel_flag_t operator|=(ftdm_channel_flag_t a, int32_t b) { + a = (ftdm_channel_flag_t)(a | b); + return a; +} +__inline__ ftdm_channel_flag_t operator&=(ftdm_channel_flag_t a, int32_t b) { + a = (ftdm_channel_flag_t)(a & b); + return a; +} +#endif + +typedef enum { + ZSM_NONE, + ZSM_UNACCEPTABLE, + ZSM_ACCEPTABLE +} ftdm_state_map_type_t; + +typedef enum { + ZSD_INBOUND, + ZSD_OUTBOUND, +} ftdm_state_direction_t; + +#define FTDM_MAP_NODE_SIZE 512 +#define FTDM_MAP_MAX FTDM_CHANNEL_STATE_INVALID+2 + +struct ftdm_state_map_node { + ftdm_state_direction_t direction; + ftdm_state_map_type_t type; + ftdm_channel_state_t check_states[FTDM_MAP_MAX]; + ftdm_channel_state_t states[FTDM_MAP_MAX]; +}; +typedef struct ftdm_state_map_node ftdm_state_map_node_t; + +struct ftdm_state_map { + ftdm_state_map_node_t nodes[FTDM_MAP_NODE_SIZE]; +}; +typedef struct ftdm_state_map ftdm_state_map_t; + +typedef enum ftdm_channel_hw_link_status { + FTDM_HW_LINK_DISCONNECTED = 0, + FTDM_HW_LINK_CONNECTED +} ftdm_channel_hw_link_status_t; + +typedef struct ftdm_conf_parameter_s { + const char *var; + const char *val; +} ftdm_conf_parameter_t; + +typedef struct ftdm_channel ftdm_channel_t; +typedef struct ftdm_event ftdm_event_t; +typedef struct ftdm_sigmsg ftdm_sigmsg_t; +typedef struct ftdm_span ftdm_span_t; +typedef struct ftdm_group ftdm_group_t; +typedef struct ftdm_caller_data ftdm_caller_data_t; +typedef struct ftdm_io_interface ftdm_io_interface_t; + +struct ftdm_stream_handle; +typedef struct ftdm_stream_handle ftdm_stream_handle_t; + +typedef ftdm_status_t (*ftdm_stream_handle_raw_write_function_t) (ftdm_stream_handle_t *handle, uint8_t *data, ftdm_size_t datalen); +typedef ftdm_status_t (*ftdm_stream_handle_write_function_t) (ftdm_stream_handle_t *handle, const char *fmt, ...); + +#define FIO_CHANNEL_REQUEST_ARGS (ftdm_span_t *span, uint32_t chan_id, ftdm_direction_t direction, ftdm_caller_data_t *caller_data, ftdm_channel_t **ftdmchan) +#define FIO_CHANNEL_OUTGOING_CALL_ARGS (ftdm_channel_t *ftdmchan) +#define FIO_CHANNEL_SET_SIG_STATUS_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status) +#define FIO_CHANNEL_GET_SIG_STATUS_ARGS (ftdm_channel_t *ftdmchan, ftdm_signaling_status_t *status) +#define FIO_SPAN_SET_SIG_STATUS_ARGS (ftdm_span_t *span, ftdm_signaling_status_t status) +#define FIO_SPAN_GET_SIG_STATUS_ARGS (ftdm_span_t *span, ftdm_signaling_status_t *status) +#define FIO_SPAN_POLL_EVENT_ARGS (ftdm_span_t *span, uint32_t ms) +#define FIO_SPAN_NEXT_EVENT_ARGS (ftdm_span_t *span, ftdm_event_t **event) +#define FIO_SIGNAL_CB_ARGS (ftdm_sigmsg_t *sigmsg) +#define FIO_EVENT_CB_ARGS (ftdm_channel_t *ftdmchan, ftdm_event_t *event) +#define FIO_CODEC_ARGS (void *data, ftdm_size_t max, ftdm_size_t *datalen) +#define FIO_CONFIGURE_SPAN_ARGS (ftdm_span_t *span, const char *str, ftdm_chan_type_t type, char *name, char *number) +#define FIO_CONFIGURE_ARGS (const char *category, const char *var, const char *val, int lineno) +#define FIO_OPEN_ARGS (ftdm_channel_t *ftdmchan) +#define FIO_CLOSE_ARGS (ftdm_channel_t *ftdmchan) +#define FIO_CHANNEL_DESTROY_ARGS (ftdm_channel_t *ftdmchan) +#define FIO_SPAN_DESTROY_ARGS (ftdm_span_t *span) +#define FIO_COMMAND_ARGS (ftdm_channel_t *ftdmchan, ftdm_command_t command, void *obj) +#define FIO_WAIT_ARGS (ftdm_channel_t *ftdmchan, ftdm_wait_flag_t *flags, int32_t to) +#define FIO_GET_ALARMS_ARGS (ftdm_channel_t *ftdmchan) +#define FIO_READ_ARGS (ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen) +#define FIO_WRITE_ARGS (ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen) +#define FIO_IO_LOAD_ARGS (ftdm_io_interface_t **fio) +#define FIO_IO_UNLOAD_ARGS (void) +#define FIO_SIG_LOAD_ARGS (void) +#define FIO_SIG_CONFIGURE_ARGS (ftdm_span_t *span, fio_signal_cb_t sig_cb, va_list ap) +#define FIO_CONFIGURE_SPAN_SIGNALING_ARGS (ftdm_span_t *span, fio_signal_cb_t sig_cb, ftdm_conf_parameter_t *ftdm_parameters) +#define FIO_SIG_UNLOAD_ARGS (void) +#define FIO_API_ARGS (ftdm_stream_handle_t *stream, const char *data) + +typedef ftdm_status_t (*fio_channel_request_t) FIO_CHANNEL_REQUEST_ARGS ; +typedef ftdm_status_t (*fio_channel_outgoing_call_t) FIO_CHANNEL_OUTGOING_CALL_ARGS ; +typedef ftdm_status_t (*fio_channel_set_sig_status_t) FIO_CHANNEL_SET_SIG_STATUS_ARGS; +typedef ftdm_status_t (*fio_channel_get_sig_status_t) FIO_CHANNEL_GET_SIG_STATUS_ARGS; +typedef ftdm_status_t (*fio_span_set_sig_status_t) FIO_SPAN_SET_SIG_STATUS_ARGS; +typedef ftdm_status_t (*fio_span_get_sig_status_t) FIO_SPAN_GET_SIG_STATUS_ARGS; +typedef ftdm_status_t (*fio_span_poll_event_t) FIO_SPAN_POLL_EVENT_ARGS ; +typedef ftdm_status_t (*fio_span_next_event_t) FIO_SPAN_NEXT_EVENT_ARGS ; +typedef ftdm_status_t (*fio_signal_cb_t) FIO_SIGNAL_CB_ARGS ; +typedef ftdm_status_t (*fio_event_cb_t) FIO_EVENT_CB_ARGS ; +typedef ftdm_status_t (*fio_codec_t) FIO_CODEC_ARGS ; +typedef ftdm_status_t (*fio_configure_span_t) FIO_CONFIGURE_SPAN_ARGS ; +typedef ftdm_status_t (*fio_configure_t) FIO_CONFIGURE_ARGS ; +typedef ftdm_status_t (*fio_open_t) FIO_OPEN_ARGS ; +typedef ftdm_status_t (*fio_close_t) FIO_CLOSE_ARGS ; +typedef ftdm_status_t (*fio_channel_destroy_t) FIO_CHANNEL_DESTROY_ARGS ; +typedef ftdm_status_t (*fio_span_destroy_t) FIO_SPAN_DESTROY_ARGS ; +typedef ftdm_status_t (*fio_get_alarms_t) FIO_GET_ALARMS_ARGS ; +typedef ftdm_status_t (*fio_command_t) FIO_COMMAND_ARGS ; +typedef ftdm_status_t (*fio_wait_t) FIO_WAIT_ARGS ; +typedef ftdm_status_t (*fio_read_t) FIO_READ_ARGS ; +typedef ftdm_status_t (*fio_write_t) FIO_WRITE_ARGS ; +typedef ftdm_status_t (*fio_io_load_t) FIO_IO_LOAD_ARGS ; +typedef ftdm_status_t (*fio_sig_load_t) FIO_SIG_LOAD_ARGS ; +typedef ftdm_status_t (*fio_sig_configure_t) FIO_SIG_CONFIGURE_ARGS ; +typedef ftdm_status_t (*fio_configure_span_signaling_t) FIO_CONFIGURE_SPAN_SIGNALING_ARGS ; +typedef ftdm_status_t (*fio_io_unload_t) FIO_IO_UNLOAD_ARGS ; +typedef ftdm_status_t (*fio_sig_unload_t) FIO_SIG_UNLOAD_ARGS ; +typedef ftdm_status_t (*fio_api_t) FIO_API_ARGS ; + + +#define FIO_CHANNEL_REQUEST_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_REQUEST_ARGS +#define FIO_CHANNEL_OUTGOING_CALL_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_OUTGOING_CALL_ARGS +#define FIO_CHANNEL_SET_SIG_STATUS_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_SET_SIG_STATUS_ARGS +#define FIO_CHANNEL_GET_SIG_STATUS_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_GET_SIG_STATUS_ARGS +#define FIO_SPAN_SET_SIG_STATUS_FUNCTION(name) ftdm_status_t name FIO_SPAN_SET_SIG_STATUS_ARGS +#define FIO_SPAN_GET_SIG_STATUS_FUNCTION(name) ftdm_status_t name FIO_SPAN_GET_SIG_STATUS_ARGS +#define FIO_SPAN_POLL_EVENT_FUNCTION(name) ftdm_status_t name FIO_SPAN_POLL_EVENT_ARGS +#define FIO_SPAN_NEXT_EVENT_FUNCTION(name) ftdm_status_t name FIO_SPAN_NEXT_EVENT_ARGS +#define FIO_SIGNAL_CB_FUNCTION(name) ftdm_status_t name FIO_SIGNAL_CB_ARGS +#define FIO_EVENT_CB_FUNCTION(name) ftdm_status_t name FIO_EVENT_CB_ARGS +#define FIO_CODEC_FUNCTION(name) FT_DECLARE_NONSTD(ftdm_status_t) name FIO_CODEC_ARGS +#define FIO_CONFIGURE_SPAN_FUNCTION(name) ftdm_status_t name FIO_CONFIGURE_SPAN_ARGS +#define FIO_CONFIGURE_FUNCTION(name) ftdm_status_t name FIO_CONFIGURE_ARGS +#define FIO_OPEN_FUNCTION(name) ftdm_status_t name FIO_OPEN_ARGS +#define FIO_CLOSE_FUNCTION(name) ftdm_status_t name FIO_CLOSE_ARGS +#define FIO_CHANNEL_DESTROY_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_DESTROY_ARGS +#define FIO_SPAN_DESTROY_FUNCTION(name) ftdm_status_t name FIO_SPAN_DESTROY_ARGS +#define FIO_GET_ALARMS_FUNCTION(name) ftdm_status_t name FIO_GET_ALARMS_ARGS +#define FIO_COMMAND_FUNCTION(name) ftdm_status_t name FIO_COMMAND_ARGS +#define FIO_WAIT_FUNCTION(name) ftdm_status_t name FIO_WAIT_ARGS +#define FIO_READ_FUNCTION(name) ftdm_status_t name FIO_READ_ARGS +#define FIO_WRITE_FUNCTION(name) ftdm_status_t name FIO_WRITE_ARGS +#define FIO_IO_LOAD_FUNCTION(name) ftdm_status_t name FIO_IO_LOAD_ARGS +#define FIO_SIG_LOAD_FUNCTION(name) ftdm_status_t name FIO_SIG_LOAD_ARGS +#define FIO_SIG_CONFIGURE_FUNCTION(name) ftdm_status_t name FIO_SIG_CONFIGURE_ARGS +#define FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(name) ftdm_status_t name FIO_CONFIGURE_SPAN_SIGNALING_ARGS +#define FIO_IO_UNLOAD_FUNCTION(name) ftdm_status_t name FIO_IO_UNLOAD_ARGS +#define FIO_SIG_UNLOAD_FUNCTION(name) ftdm_status_t name FIO_SIG_UNLOAD_ARGS +#define FIO_API_FUNCTION(name) ftdm_status_t name FIO_API_ARGS + +#include "ftdm_dso.h" + +typedef struct { + char name[256]; + fio_io_load_t io_load; + fio_io_unload_t io_unload; + fio_sig_load_t sig_load; + fio_sig_configure_t sig_configure; + fio_sig_unload_t sig_unload; + /*! + \brief configure a given span signaling + \see sig_configure + This is just like sig_configure but receives + an array of paramters instead of va_list + I'd like to deprecate sig_configure and move + all modules to use sigparam_configure + */ + fio_configure_span_signaling_t configure_span_signaling; + ftdm_dso_lib_t lib; + char path[256]; +} ftdm_module_t; + +#ifndef __FUNCTION__ +#define __FUNCTION__ (const char *)__func__ +#endif + +#define FTDM_PRE __FILE__, __FUNCTION__, __LINE__ +#define FTDM_LOG_LEVEL_DEBUG 7 +#define FTDM_LOG_LEVEL_INFO 6 +#define FTDM_LOG_LEVEL_NOTICE 5 +#define FTDM_LOG_LEVEL_WARNING 4 +#define FTDM_LOG_LEVEL_ERROR 3 +#define FTDM_LOG_LEVEL_CRIT 2 +#define FTDM_LOG_LEVEL_ALERT 1 +#define FTDM_LOG_LEVEL_EMERG 0 + +#define FTDM_LOG_DEBUG FTDM_PRE, FTDM_LOG_LEVEL_DEBUG +#define FTDM_LOG_INFO FTDM_PRE, FTDM_LOG_LEVEL_INFO +#define FTDM_LOG_NOTICE FTDM_PRE, FTDM_LOG_LEVEL_NOTICE +#define FTDM_LOG_WARNING FTDM_PRE, FTDM_LOG_LEVEL_WARNING +#define FTDM_LOG_ERROR FTDM_PRE, FTDM_LOG_LEVEL_ERROR +#define FTDM_LOG_CRIT FTDM_PRE, FTDM_LOG_LEVEL_CRIT +#define FTDM_LOG_ALERT FTDM_PRE, FTDM_LOG_LEVEL_ALERT +#define FTDM_LOG_EMERG FTDM_PRE, FTDM_LOG_LEVEL_EMERG + +typedef struct ftdm_fsk_data_state ftdm_fsk_data_state_t; +typedef int (*ftdm_fsk_data_decoder_t)(ftdm_fsk_data_state_t *state); +typedef ftdm_status_t (*ftdm_fsk_write_sample_t)(int16_t *buf, ftdm_size_t buflen, void *user_data); +typedef void (*ftdm_logger_t)(const char *file, const char *func, int line, int level, const char *fmt, ...); +typedef struct hashtable ftdm_hash_t; +typedef struct hashtable_iterator ftdm_hash_iterator_t; +typedef struct key ftdm_hash_key_t; +typedef struct value ftdm_hash_val_t; +typedef struct ftdm_bitstream ftdm_bitstream_t; +typedef struct ftdm_fsk_modulator ftdm_fsk_modulator_t; +typedef ftdm_status_t (*ftdm_span_start_t)(ftdm_span_t *span); +typedef ftdm_status_t (*ftdm_span_stop_t)(ftdm_span_t *span); + +typedef enum { + FTDM_CAUSE_NONE = 0, + FTDM_CAUSE_UNALLOCATED = 1, + FTDM_CAUSE_NO_ROUTE_TRANSIT_NET = 2, + FTDM_CAUSE_NO_ROUTE_DESTINATION = 3, + FTDM_CAUSE_CHANNEL_UNACCEPTABLE = 6, + FTDM_CAUSE_CALL_AWARDED_DELIVERED = 7, + FTDM_CAUSE_NORMAL_CLEARING = 16, + FTDM_CAUSE_USER_BUSY = 17, + FTDM_CAUSE_NO_USER_RESPONSE = 18, + FTDM_CAUSE_NO_ANSWER = 19, + FTDM_CAUSE_SUBSCRIBER_ABSENT = 20, + FTDM_CAUSE_CALL_REJECTED = 21, + FTDM_CAUSE_NUMBER_CHANGED = 22, + FTDM_CAUSE_REDIRECTION_TO_NEW_DESTINATION = 23, + FTDM_CAUSE_EXCHANGE_ROUTING_ERROR = 25, + FTDM_CAUSE_DESTINATION_OUT_OF_ORDER = 27, + FTDM_CAUSE_INVALID_NUMBER_FORMAT = 28, + FTDM_CAUSE_FACILITY_REJECTED = 29, + FTDM_CAUSE_RESPONSE_TO_STATUS_ENQUIRY = 30, + FTDM_CAUSE_NORMAL_UNSPECIFIED = 31, + FTDM_CAUSE_NORMAL_CIRCUIT_CONGESTION = 34, + FTDM_CAUSE_NETWORK_OUT_OF_ORDER = 38, + FTDM_CAUSE_NORMAL_TEMPORARY_FAILURE = 41, + FTDM_CAUSE_SWITCH_CONGESTION = 42, + FTDM_CAUSE_ACCESS_INFO_DISCARDED = 43, + FTDM_CAUSE_REQUESTED_CHAN_UNAVAIL = 44, + FTDM_CAUSE_PRE_EMPTED = 45, + FTDM_CAUSE_FACILITY_NOT_SUBSCRIBED = 50, + FTDM_CAUSE_OUTGOING_CALL_BARRED = 52, + FTDM_CAUSE_INCOMING_CALL_BARRED = 54, + FTDM_CAUSE_BEARERCAPABILITY_NOTAUTH = 57, + FTDM_CAUSE_BEARERCAPABILITY_NOTAVAIL = 58, + FTDM_CAUSE_SERVICE_UNAVAILABLE = 63, + FTDM_CAUSE_BEARERCAPABILITY_NOTIMPL = 65, + FTDM_CAUSE_CHAN_NOT_IMPLEMENTED = 66, + FTDM_CAUSE_FACILITY_NOT_IMPLEMENTED = 69, + FTDM_CAUSE_SERVICE_NOT_IMPLEMENTED = 79, + FTDM_CAUSE_INVALID_CALL_REFERENCE = 81, + FTDM_CAUSE_INCOMPATIBLE_DESTINATION = 88, + FTDM_CAUSE_INVALID_MSG_UNSPECIFIED = 95, + FTDM_CAUSE_MANDATORY_IE_MISSING = 96, + FTDM_CAUSE_MESSAGE_TYPE_NONEXIST = 97, + FTDM_CAUSE_WRONG_MESSAGE = 98, + FTDM_CAUSE_IE_NONEXIST = 99, + FTDM_CAUSE_INVALID_IE_CONTENTS = 100, + FTDM_CAUSE_WRONG_CALL_STATE = 101, + FTDM_CAUSE_RECOVERY_ON_TIMER_EXPIRE = 102, + FTDM_CAUSE_MANDATORY_IE_LENGTH_ERROR = 103, + FTDM_CAUSE_PROTOCOL_ERROR = 111, + FTDM_CAUSE_INTERWORKING = 127, + FTDM_CAUSE_SUCCESS = 142, + FTDM_CAUSE_ORIGINATOR_CANCEL = 487, + FTDM_CAUSE_CRASH = 500, + FTDM_CAUSE_SYSTEM_SHUTDOWN = 501, + FTDM_CAUSE_LOSE_RACE = 502, + FTDM_CAUSE_MANAGER_REQUEST = 503, + FTDM_CAUSE_BLIND_TRANSFER = 600, + FTDM_CAUSE_ATTENDED_TRANSFER = 601, + FTDM_CAUSE_ALLOTTED_TIMEOUT = 602, + FTDM_CAUSE_USER_CHALLENGE = 603, + FTDM_CAUSE_MEDIA_TIMEOUT = 604 +} ftdm_call_cause_t; + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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: + */ + diff --git a/libs/freetdm/src/include/g711.h b/libs/freetdm/src/include/g711.h new file mode 100644 index 0000000000..6f4c1d2542 --- /dev/null +++ b/libs/freetdm/src/include/g711.h @@ -0,0 +1,395 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g711.h - In line A-law and u-law conversion routines + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * $Id: g711.h,v 1.1 2006/06/07 15:46:39 steveu Exp $ + */ + +/*! \file */ + +/*! \page g711_page A-law and mu-law handling + Lookup tables for A-law and u-law look attractive, until you consider the impact + on the CPU cache. If it causes a substantial area of your processor cache to get + hit too often, cache sloshing will severely slow things down. The main reason + these routines are slow in C, is the lack of direct access to the CPU's "find + the first 1" instruction. A little in-line assembler fixes that, and the + conversion routines can be faster than lookup tables, in most real world usage. + A "find the first 1" instruction is available on most modern CPUs, and is a + much underused feature. + + If an assembly language method of bit searching is not available, these routines + revert to a method that can be a little slow, so the cache thrashing might not + seem so bad :( + + Feel free to submit patches to add fast "find the first 1" support for your own + favourite processor. + + Look up tables are used for transcoding between A-law and u-law, since it is + difficult to achieve the precise transcoding procedure laid down in the G.711 + specification by other means. +*/ + +#if !defined(_G711_H_) +#define _G711_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif + typedef unsigned __int8 uint8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef unsigned __int16 uint16_t; +#else +#include +#endif + +#if defined(__i386__) + /*! \brief Find the bit position of the highest set bit in a word + \param bits The word to be searched + \return The bit number of the highest set bit, or -1 if the word is zero. */ + static __inline__ int top_bit(unsigned int bits) + { + int res; + + __asm__ __volatile__(" movl $-1,%%edx;\n" + " bsrl %%eax,%%edx;\n" + : "=d" (res) + : "a" (bits)); + return res; + } + /*- End of function --------------------------------------------------------*/ + + /*! \brief Find the bit position of the lowest set bit in a word + \param bits The word to be searched + \return The bit number of the lowest set bit, or -1 if the word is zero. */ + static __inline__ int bottom_bit(unsigned int bits) + { + int res; + + __asm__ __volatile__(" movl $-1,%%edx;\n" + " bsfl %%eax,%%edx;\n" + : "=d" (res) + : "a" (bits)); + return res; + } + /*- End of function --------------------------------------------------------*/ +#elif defined(__x86_64__) + static __inline__ int top_bit(unsigned int bits) + { + int res; + + __asm__ __volatile__(" movq $-1,%%rdx;\n" + " bsrq %%rax,%%rdx;\n" + : "=d" (res) + : "a" (bits)); + return res; + } + /*- End of function --------------------------------------------------------*/ + + static __inline__ int bottom_bit(unsigned int bits) + { + int res; + + __asm__ __volatile__(" movq $-1,%%rdx;\n" + " bsfq %%rax,%%rdx;\n" + : "=d" (res) + : "a" (bits)); + return res; + } + /*- End of function --------------------------------------------------------*/ +#else + static __inline__ int top_bit(unsigned int bits) + { + int i; + + if (bits == 0) + return -1; + i = 0; + if (bits & 0xFFFF0000) + { + bits &= 0xFFFF0000; + i += 16; + } + if (bits & 0xFF00FF00) + { + bits &= 0xFF00FF00; + i += 8; + } + if (bits & 0xF0F0F0F0) + { + bits &= 0xF0F0F0F0; + i += 4; + } + if (bits & 0xCCCCCCCC) + { + bits &= 0xCCCCCCCC; + i += 2; + } + if (bits & 0xAAAAAAAA) + { + bits &= 0xAAAAAAAA; + i += 1; + } + return i; + } + /*- End of function --------------------------------------------------------*/ + + static __inline__ int bottom_bit(unsigned int bits) + { + int i; + + if (bits == 0) + return -1; + i = 32; + if (bits & 0x0000FFFF) + { + bits &= 0x0000FFFF; + i -= 16; + } + if (bits & 0x00FF00FF) + { + bits &= 0x00FF00FF; + i -= 8; + } + if (bits & 0x0F0F0F0F) + { + bits &= 0x0F0F0F0F; + i -= 4; + } + if (bits & 0x33333333) + { + bits &= 0x33333333; + i -= 2; + } + if (bits & 0x55555555) + { + bits &= 0x55555555; + i -= 1; + } + return i; + } + /*- End of function --------------------------------------------------------*/ +#endif + + /* N.B. It is tempting to use look-up tables for A-law and u-law conversion. + * However, you should consider the cache footprint. + * + * A 64K byte table for linear to x-law and a 512 byte table for x-law to + * linear sound like peanuts these days, and shouldn't an array lookup be + * real fast? No! When the cache sloshes as badly as this one will, a tight + * calculation may be better. The messiest part is normally finding the + * segment, but a little inline assembly can fix that on an i386, x86_64 and + * many other modern processors. + */ + + /* + * Mu-law is basically as follows: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + + /*#define ULAW_ZEROTRAP*/ /* turn on the trap as per the MIL-STD */ +#define ULAW_BIAS 0x84 /* Bias for linear code. */ + + /*! \brief Encode a linear sample to u-law + \param linear The sample to encode. + \return The u-law value. + */ + static __inline__ uint8_t linear_to_ulaw(int linear) + { + uint8_t u_val; + int mask; + int seg; + + /* Get the sign and the magnitude of the value. */ + if (linear < 0) + { + linear = ULAW_BIAS - linear; + mask = 0x7F; + } + else + { + linear = ULAW_BIAS + linear; + mask = 0xFF; + } + + seg = top_bit(linear | 0xFF) - 7; + + /* + * Combine the sign, segment, quantization bits, + * and complement the code word. + */ + if (seg >= 8) + u_val = (uint8_t) (0x7F ^ mask); + else + u_val = (uint8_t) (((seg << 4) | ((linear >> (seg + 3)) & 0xF)) ^ mask); +#ifdef ULAW_ZEROTRAP + /* Optional ITU trap */ + if (u_val == 0) + u_val = 0x02; +#endif + return u_val; + } + /*- End of function --------------------------------------------------------*/ + + /*! \brief Decode an u-law sample to a linear value. + \param ulaw The u-law sample to decode. + \return The linear value. + */ + static __inline__ int16_t ulaw_to_linear(uint8_t ulaw) + { + int t; + + /* Complement to obtain normal u-law value. */ + ulaw = ~ulaw; + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = (((ulaw & 0x0F) << 3) + ULAW_BIAS) << (((int) ulaw & 0x70) >> 4); + return (int16_t) ((ulaw & 0x80) ? (ULAW_BIAS - t) : (t - ULAW_BIAS)); + } + /*- End of function --------------------------------------------------------*/ + + /* + * A-law is basically as follows: + * + * Linear Input Code Compressed Code + * ----------------- --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +#define ALAW_AMI_MASK 0x55 + + /*! \brief Encode a linear sample to A-law + \param linear The sample to encode. + \return The A-law value. + */ + static __inline__ uint8_t linear_to_alaw(int linear) + { + int mask; + int seg; + + if (linear >= 0) + { + /* Sign (bit 7) bit = 1 */ + mask = ALAW_AMI_MASK | 0x80; + } + else + { + /* Sign (bit 7) bit = 0 */ + mask = ALAW_AMI_MASK; + linear = -linear - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = top_bit(linear | 0xFF) - 7; + if (seg >= 8) + { + if (linear >= 0) + { + /* Out of range. Return maximum value. */ + return (uint8_t) (0x7F ^ mask); + } + /* We must be just a tiny step below zero */ + return (uint8_t) (0x00 ^ mask); + } + /* Combine the sign, segment, and quantization bits. */ + return (uint8_t) (((seg << 4) | ((linear >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask); + } + /*- End of function --------------------------------------------------------*/ + + /*! \brief Decode an A-law sample to a linear value. + \param alaw The A-law sample to decode. + \return The linear value. + */ + static __inline__ int16_t alaw_to_linear(uint8_t alaw) + { + int i; + int seg; + + alaw ^= ALAW_AMI_MASK; + i = ((alaw & 0x0F) << 4); + seg = (((int) alaw & 0x70) >> 4); + if (seg) + i = (i + 0x108) << (seg - 1); + else + i += 8; + return (int16_t) ((alaw & 0x80) ? i : -i); + } + /*- End of function --------------------------------------------------------*/ + + /*! \brief Transcode from A-law to u-law, using the procedure defined in G.711. + \param alaw The A-law sample to transcode. + \return The best matching u-law value. + */ + uint8_t alaw_to_ulaw(uint8_t alaw); + + /*! \brief Transcode from u-law to A-law, using the procedure defined in G.711. + \param alaw The u-law sample to transcode. + \return The best matching A-law value. + */ + uint8_t ulaw_to_alaw(uint8_t ulaw); + +#ifdef __cplusplus +} +#endif + +#endif +/*- End of file ------------------------------------------------------------*/ + +/* 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: + */ diff --git a/libs/freetdm/src/include/hashtable.h b/libs/freetdm/src/include/hashtable.h new file mode 100644 index 0000000000..f08ea817e2 --- /dev/null +++ b/libs/freetdm/src/include/hashtable.h @@ -0,0 +1,235 @@ +/* Copyright (C) 2002 Christopher Clark */ + +#ifndef __HASHTABLE_CWC22_H__ +#define __HASHTABLE_CWC22_H__ +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#endif +#include "freetdm.h" + +#ifdef __cplusplus +extern "C" { +#endif +struct hashtable; +struct hashtable_iterator; + +/* Example of use: + * + * struct hashtable *h; + * struct some_key *k; + * struct some_value *v; + * + * static unsigned int hash_from_key_fn( void *k ); + * static int keys_equal_fn ( void *key1, void *key2 ); + * + * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); + * k = (struct some_key *) malloc(sizeof(struct some_key)); + * v = (struct some_value *) malloc(sizeof(struct some_value)); + * + * (initialise k and v to suitable values) + * + * if (! hashtable_insert(h,k,v) ) + * { exit(-1); } + * + * if (NULL == (found = hashtable_search(h,k) )) + * { printf("not found!"); } + * + * if (NULL == (found = hashtable_remove(h,k) )) + * { printf("Not found\n"); } + * + */ + +/* Macros may be used to define type-safe(r) hashtable access functions, with + * methods specialized to take known key and value types as parameters. + * + * Example: + * + * Insert this at the start of your file: + * + * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); + * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); + * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); + * + * This defines the functions 'insert_some', 'search_some' and 'remove_some'. + * These operate just like hashtable_insert etc., with the same parameters, + * but their function signatures have 'struct some_key *' rather than + * 'void *', and hence can generate compile time errors if your program is + * supplying incorrect data as a key (and similarly for value). + * + * Note that the hash and key equality functions passed to create_hashtable + * still take 'void *' parameters instead of 'some key *'. This shouldn't be + * a difficult issue as they're only defined and passed once, and the other + * functions will ensure that only valid keys are supplied to them. + * + * The cost for this checking is increased code size and runtime overhead + * - if performance is important, it may be worth switching back to the + * unsafe methods once your program has been debugged with the safe methods. + * This just requires switching to some simple alternative defines - eg: + * #define insert_some hashtable_insert + * + */ + +/***************************************************************************** + * create_hashtable + + * @name create_hashtable + * @param minsize minimum initial size of hashtable + * @param hashfunction function for hashing keys + * @param key_eq_fn function for determining key equality + * @return newly created hashtable or NULL on failure + */ + +FT_DECLARE(struct hashtable *) +create_hashtable(unsigned int minsize, + unsigned int (*hashfunction) (void*), + int (*key_eq_fn) (void*,void*)); + +/***************************************************************************** + * hashtable_insert + + * @name hashtable_insert + * @param h the hashtable to insert into + * @param k the key - hashtable claims ownership and will free on removal + * @param v the value - does not claim ownership + * @return non-zero for successful insertion + * + * This function will cause the table to expand if the insertion would take + * the ratio of entries to table size over the maximum load factor. + * + * This function does not check for repeated insertions with a duplicate key. + * The value returned when using a duplicate key is undefined -- when + * the hashtable changes size, the order of retrieval of duplicate key + * entries is reversed. + * If in doubt, remove before insert. + */ + + +typedef enum { + HASHTABLE_FLAG_NONE = 0, + HASHTABLE_FLAG_FREE_KEY = (1 << 0), + HASHTABLE_FLAG_FREE_VALUE = (1 << 1) +} hashtable_flag_t; + +FT_DECLARE(int) +hashtable_insert(struct hashtable *h, void *k, void *v, hashtable_flag_t flags); + +#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ + int fnname (struct hashtable *h, keytype *k, valuetype *v) \ + { \ + return hashtable_insert(h,k,v); \ + } + +/***************************************************************************** + * hashtable_search + + * @name hashtable_search + * @param h the hashtable to search + * @param k the key to search for - does not claim ownership + * @return the value associated with the key, or NULL if none found + */ + +FT_DECLARE(void *) +hashtable_search(struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ + valuetype * fnname (struct hashtable *h, keytype *k) \ + { \ + return (valuetype *) (hashtable_search(h,k)); \ + } + +/***************************************************************************** + * hashtable_remove + + * @name hashtable_remove + * @param h the hashtable to remove the item from + * @param k the key to search for - does not claim ownership + * @return the value associated with the key, or NULL if none found + */ + +FT_DECLARE(void *) /* returns value */ +hashtable_remove(struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ + valuetype * fnname (struct hashtable *h, keytype *k) \ + { \ + return (valuetype *) (hashtable_remove(h,k)); \ + } + + +/***************************************************************************** + * hashtable_count + + * @name hashtable_count + * @param h the hashtable + * @return the number of items stored in the hashtable + */ +FT_DECLARE(unsigned int) +hashtable_count(struct hashtable *h); + + +/***************************************************************************** + * hashtable_destroy + + * @name hashtable_destroy + * @param h the hashtable + * @param free_values whether to call 'free' on the remaining values + */ + +FT_DECLARE(void) +hashtable_destroy(struct hashtable *h); + +FT_DECLARE(struct hashtable_iterator*) hashtable_first(struct hashtable *h); +FT_DECLARE(struct hashtable_iterator*) hashtable_next(struct hashtable_iterator *i); +FT_DECLARE(void) hashtable_this(struct hashtable_iterator *i, const void **key, int *klen, void **val); + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif /* __HASHTABLE_CWC22_H__ */ + +/* + * Copyright (c) 2002, Christopher Clark + * 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. + */ + +/* 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: + */ diff --git a/libs/freetdm/src/include/hashtable_itr.h b/libs/freetdm/src/include/hashtable_itr.h new file mode 100644 index 0000000000..59b73a5863 --- /dev/null +++ b/libs/freetdm/src/include/hashtable_itr.h @@ -0,0 +1,134 @@ +/* Copyright (C) 2002, 2004 Christopher Clark */ + +#ifndef __HASHTABLE_ITR_CWC22__ +#define __HASHTABLE_ITR_CWC22__ +#include "hashtable.h" +#include "hashtable_private.h" /* needed to enable inlining */ + +#ifdef __cplusplus +extern "C" { +#endif +/*****************************************************************************/ +/* This struct is only concrete here to allow the inlining of two of the + * accessor functions. */ +struct hashtable_itr +{ + struct hashtable *h; + struct entry *e; + struct entry *parent; + unsigned int index; +}; + + +/*****************************************************************************/ +/* hashtable_iterator + */ + +struct hashtable_itr * +hashtable_iterator(struct hashtable *h); + +/*****************************************************************************/ +/* hashtable_iterator_key + * - return the value of the (key,value) pair at the current position */ +extern __inline__ void * +hashtable_iterator_key(struct hashtable_itr *i); + +extern __inline__ void * +hashtable_iterator_key(struct hashtable_itr *i) +{ + return i->e->k; +} + +/*****************************************************************************/ +/* value - return the value of the (key,value) pair at the current position */ + +extern __inline__ void * +hashtable_iterator_value(struct hashtable_itr *i); + +extern __inline__ void * +hashtable_iterator_value(struct hashtable_itr *i) +{ + return i->e->v; +} + +/*****************************************************************************/ +/* advance - advance the iterator to the next element + * returns zero if advanced to end of table */ + +int +hashtable_iterator_advance(struct hashtable_itr *itr); + +/*****************************************************************************/ +/* remove - remove current element and advance the iterator to the next element + * NB: if you need the value to free it, read it before + * removing. ie: beware memory leaks! + * returns zero if advanced to end of table */ + +int +hashtable_iterator_remove(struct hashtable_itr *itr); + +/*****************************************************************************/ +/* search - overwrite the supplied iterator, to point to the entry + * matching the supplied key. + h points to the hashtable to be searched. + * returns zero if not found. */ +int +hashtable_iterator_search(struct hashtable_itr *itr, + struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ + int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ + { \ + return (hashtable_iterator_search(i,h,k)); \ + } + +#ifdef __cplusplus +} +#endif + +#endif /* __HASHTABLE_ITR_CWC22__*/ + +/* + * Copyright (c) 2002, 2004, Christopher Clark + * 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. + */ + +/* 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: + */ + diff --git a/libs/freetdm/src/include/hashtable_private.h b/libs/freetdm/src/include/hashtable_private.h new file mode 100644 index 0000000000..47988d8d8f --- /dev/null +++ b/libs/freetdm/src/include/hashtable_private.h @@ -0,0 +1,110 @@ +/* Copyright (C) 2002, 2004 Christopher Clark */ + +#ifndef __HASHTABLE_PRIVATE_CWC22_H__ +#define __HASHTABLE_PRIVATE_CWC22_H__ + +#include "hashtable.h" + +#ifdef __cplusplus +extern "C" { +#endif +/*****************************************************************************/ + +struct entry +{ + void *k, *v; + unsigned int h; + hashtable_flag_t flags; + struct entry *next; +}; + +struct hashtable_iterator { + unsigned int pos; + struct entry *e; + struct hashtable *h; +}; + +struct hashtable { + unsigned int tablelength; + struct entry **table; + unsigned int entrycount; + unsigned int loadlimit; + unsigned int primeindex; + unsigned int (*hashfn) (void *k); + int (*eqfn) (void *k1, void *k2); + struct hashtable_iterator iterator; +}; + +/*****************************************************************************/ +unsigned int +hash(struct hashtable *h, void *k); + +/*****************************************************************************/ +/* indexFor */ +static __inline__ unsigned int +indexFor(unsigned int tablelength, unsigned int hashvalue) { + return (hashvalue % tablelength); +} + +/* Only works if tablelength == 2^N */ +/*static inline unsigned int + indexFor(unsigned int tablelength, unsigned int hashvalue) + { + return (hashvalue & (tablelength - 1u)); + } +*/ + +/*****************************************************************************/ +#define freekey(X) free(X) +/*define freekey(X) ; */ + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ + +#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ + +/* + * Copyright (c) 2002, Christopher Clark + * 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. + */ +/* 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: + */ diff --git a/libs/freetdm/src/include/libteletone.h b/libs/freetdm/src/include/libteletone.h new file mode 100644 index 0000000000..18b95e7f3c --- /dev/null +++ b/libs/freetdm/src/include/libteletone.h @@ -0,0 +1,161 @@ +/* + * libteletone + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mftilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mftilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libteletone + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * libteletone.h -- Tone Generator/Detector + * + * + * + * Exception: + * The author hereby grants the use of this source code under the + * following license if and only if the source code is distributed + * as part of the freetdm library. Any use or distribution of this + * source code outside the scope of the freetdm library will nullify the + * following license and reinact the MPL 1.1 as stated above. + * + * Copyright (c) 2007, Anthony Minessale II + * 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 LIBTELETONE_H +#define LIBTELETONE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define TELETONE_MAX_DTMF_DIGITS 128 +#define TELETONE_MAX_TONES 18 +#define TELETONE_TONE_RANGE 127 + +typedef double teletone_process_t; + +/*! \file libteletone.h + \brief Top level include file + + This file should be included by applications using the library +*/ + +/*! \brief An abstraction to store a tone mapping */ +typedef struct { + /*! An array of tone frequencies */ + teletone_process_t freqs[TELETONE_MAX_TONES]; +} teletone_tone_map_t; + +#if !defined(M_PI) +/* C99 systems may not define M_PI */ +#define M_PI 3.14159265358979323846264338327 +#endif + +#ifdef _MSC_VER +typedef __int16 int16_t; +#endif + +#if (_MSC_VER >= 1400) // VC8+ +#define teletone_assert(expr) assert(expr);__analysis_assume( expr ) +#else +#define teletone_assert(expr) assert(expr) +#endif + +#ifdef _MSC_VER +#if defined(TT_DECLARE_STATIC) +#define TELETONE_API(type) type __stdcall +#define TELETONE_API_NONSTD(type) type __cdecl +#define TELETONE_API_DATA +#elif defined(TELETONE_EXPORTS) +#define TELETONE_API(type) __declspec(dllexport) type __stdcall +#define TELETONE_API_NONSTD(type) __declspec(dllexport) type __cdecl +#define TELETONE_API_DATA __declspec(dllexport) +#else +#define TELETONE_API(type) __declspec(dllimport) type __stdcall +#define TELETONE_API_NONSTD(type) __declspec(dllimport) type __cdecl +#define TELETONE_API_DATA __declspec(dllimport) +#endif +#else +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(HAVE_VISIBILITY) +#define TELETONE_API(type) __attribute__((visibility("default"))) type +#define TELETONE_API_NONSTD(type) __attribute__((visibility("default"))) type +#define TELETONE_API_DATA __attribute__((visibility("default"))) +#else +#define TELETONE_API(type) type +#define TELETONE_API_NONSTD(type) type +#define TELETONE_API_DATA +#endif +#endif + +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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: + */ diff --git a/libs/freetdm/src/include/libteletone_detect.h b/libs/freetdm/src/include/libteletone_detect.h new file mode 100644 index 0000000000..dc14356e3e --- /dev/null +++ b/libs/freetdm/src/include/libteletone_detect.h @@ -0,0 +1,240 @@ +/* + * libteletone + * Copyright (C) 2005/2006, Anthony Minessale II + * + * libteletone_detect.c Tone Detection Code + * + * Exception: + * The author hereby grants the use of this source code under the + * following license if and only if the source code is distributed + * as part of the freetdm library. Any use or distribution of this + * source code outside the scope of the freetdm library will nullify the + * following license and reinact the MPL 1.1 as stated above. + * + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + ********************************************************************************* + * + * Derived from tone_detect.h - General telephony tone detection, and specific + * detection of DTMF. + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * + */ + +#ifndef LIBTELETONE_DETECT_H +#define LIBTELETONE_DETECT_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + + /*! \file libteletone_detect.h + \brief Tone Detection Routines + + This module is responsible for tone detection specifics + */ + +#ifndef FALSE +#define FALSE 0 +#ifndef TRUE +#define TRUE (!FALSE) +#endif +#endif + + /* Basic DTMF specs: + * + * Minimum tone on = 40ms + * Minimum tone off = 50ms + * Maximum digit rate = 10 per second + * Normal twist <= 8dB accepted + * Reverse twist <= 4dB accepted + * S/N >= 15dB will detect OK + * Attenuation <= 26dB will detect OK + * Frequency tolerance +- 1.5% will detect, +-3.5% will reject + */ + +#define DTMF_THRESHOLD 8.0e7 +#define DTMF_NORMAL_TWIST 6.3 /* 8dB */ +#define DTMF_REVERSE_TWIST 2.5 /* 4dB */ +#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */ +#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */ +#define DTMF_2ND_HARMONIC_ROW 2.5 /* 4dB */ +#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */ +#define GRID_FACTOR 4 +#define BLOCK_LEN 102 +#define M_TWO_PI 2.0*M_PI + + /*! \brief A continer for the elements of a Goertzel Algorithm (The names are from his formula) */ + typedef struct { + float v2; + float v3; + double fac; + } teletone_goertzel_state_t; + + /*! \brief A container for a DTMF detection state.*/ + typedef struct { + int hit1; + int hit2; + int hit3; + int hit4; + int mhit; + + teletone_goertzel_state_t row_out[GRID_FACTOR]; + teletone_goertzel_state_t col_out[GRID_FACTOR]; + teletone_goertzel_state_t row_out2nd[GRID_FACTOR]; + teletone_goertzel_state_t col_out2nd[GRID_FACTOR]; + float energy; + + int current_sample; + char digits[TELETONE_MAX_DTMF_DIGITS + 1]; + int current_digits; + int detected_digits; + int lost_digits; + int digit_hits[16]; + } teletone_dtmf_detect_state_t; + + /*! \brief An abstraction to store the coefficient of a tone frequency */ + typedef struct { + float fac; + } teletone_detection_descriptor_t; + + /*! \brief A container for a single multi-tone detection + TELETONE_MAX_TONES dictates the maximum simultaneous tones that can be present + in a multi-tone representation. + */ + typedef struct { + int sample_rate; + + teletone_detection_descriptor_t tdd[TELETONE_MAX_TONES]; + teletone_goertzel_state_t gs[TELETONE_MAX_TONES]; + teletone_goertzel_state_t gs2[TELETONE_MAX_TONES]; + int tone_count; + + float energy; + int current_sample; + + int min_samples; + int total_samples; + + int positives; + int negatives; + int hits; + + int positive_factor; + int negative_factor; + int hit_factor; + + } teletone_multi_tone_t; + + + /*! + \brief Initilize a multi-frequency tone detector + \param mt the multi-frequency tone descriptor + \param map a representation of the multi-frequency tone + */ +TELETONE_API(void) teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *map); + + /*! + \brief Check a sample buffer for the presence of the mulit-frequency tone described by mt + \param mt the multi-frequency tone descriptor + \param sample_buffer an array aof 16 bit signed linear samples + \param samples the number of samples present in sample_buffer + \return true when the tone was detected or false when it is not + */ +TELETONE_API(int) teletone_multi_tone_detect (teletone_multi_tone_t *mt, + int16_t sample_buffer[], + int samples); + + /*! + \brief Initilize a DTMF detection state object + \param dtmf_detect_state the DTMF detection state to initilize + \param sample_rate the desired sample rate + */ +TELETONE_API(void) teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf_detect_state, int sample_rate); + + /*! + \brief Check a sample buffer for the presence of DTMF digits + \param dtmf_detect_state the detection state object to check + \param sample_buffer an array aof 16 bit signed linear samples + \param samples the number of samples present in sample_buffer + \return true when DTMF was detected or false when it is not + */ +TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, + int16_t sample_buffer[], + int samples); + /*! + \brief retrieve any collected digits into a string buffer + \param dtmf_detect_state the detection state object to check + \param buf the string buffer to write to + \param max the maximum length of buf + \return the number of characters written to buf + */ +TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, + char *buf, + int max); + + /*! + \brief Step through the Goertzel Algorithm for each sample in a buffer + \param goertzel_state the goertzel state to step the samples through + \param sample_buffer an array aof 16 bit signed linear samples + \param samples the number of samples present in sample_buffer + */ +TELETONE_API(void) teletone_goertzel_update(teletone_goertzel_state_t *goertzel_state, + int16_t sample_buffer[], + int samples); + + + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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: + */ diff --git a/libs/freetdm/src/include/libteletone_generate.h b/libs/freetdm/src/include/libteletone_generate.h new file mode 100644 index 0000000000..fce19e7255 --- /dev/null +++ b/libs/freetdm/src/include/libteletone_generate.h @@ -0,0 +1,281 @@ +/* + * libteletone + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Copyright (c) 2007, Anthony Minessale II + * 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 LIBTELETONE_GENERATE_H +#define LIBTELETONE_GENERATE_H +#ifdef __cplusplus +extern "C" { +#ifdef _doh +} +#endif +#endif + +#include +#include + +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#ifndef __inline__ +#define __inline__ inline +#endif +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif + +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +#else +#include +#endif +#include +#include +#include +#include +#if !defined(powf) && !defined(_WIN64) +extern float powf (float, float); +#endif +#include +#include +#ifndef _MSC_VER +#include +#endif +#include +#include +#include + +#define TELETONE_VOL_DB_MAX 0 +#define TELETONE_VOL_DB_MIN -63 +#define MAX_PHASE_TONES 4 + +struct teletone_dds_state { + uint32_t phase_rate[MAX_PHASE_TONES]; + uint32_t scale_factor; + uint32_t phase_accumulator; + teletone_process_t tx_level; +}; +typedef struct teletone_dds_state teletone_dds_state_t; + +#define SINE_TABLE_MAX 128 +#define SINE_TABLE_LEN (SINE_TABLE_MAX - 1) +#define MAX_PHASE_ACCUMULATOR 0x10000 * 0x10000 +/* 3.14 == the max power on ulaw (alaw is 3.17) */ +/* 3.02 represents twice the power */ +#define DBM0_MAX_POWER (3.14f + 3.02f) + +TELETONE_API_DATA extern int16_t TELETONE_SINES[SINE_TABLE_MAX]; + +static __inline__ int32_t teletone_dds_phase_rate(teletone_process_t tone, uint32_t rate) +{ + return (int32_t) ((tone * MAX_PHASE_ACCUMULATOR) / rate); +} + +static __inline__ int16_t teletone_dds_state_modulate_sample(teletone_dds_state_t *dds, uint32_t pindex) +{ + int32_t bitmask = dds->phase_accumulator, sine_index = (bitmask >>= 23) & SINE_TABLE_LEN; + int16_t sample; + + if (pindex >= MAX_PHASE_TONES) { + pindex = 0; + } + + if (bitmask & SINE_TABLE_MAX) { + sine_index = SINE_TABLE_LEN - sine_index; + } + + sample = TELETONE_SINES[sine_index]; + + if (bitmask & (SINE_TABLE_MAX * 2)) { + sample *= -1; + } + + dds->phase_accumulator += dds->phase_rate[pindex]; + return (int16_t) (sample * dds->scale_factor >> 15); +} + +static __inline__ void teletone_dds_state_set_tx_level(teletone_dds_state_t *dds, float tx_level) +{ + dds->scale_factor = (int) (powf(10.0f, (tx_level - DBM0_MAX_POWER) / 20.0f) * (32767.0f * 1.414214f)); + dds->tx_level = tx_level; +} + +static __inline__ void teletone_dds_state_reset_accum(teletone_dds_state_t *dds) +{ + dds->phase_accumulator = 0; +} + +static __inline__ int teletone_dds_state_set_tone(teletone_dds_state_t *dds, teletone_process_t tone, uint32_t rate, uint32_t pindex) +{ + if (pindex < MAX_PHASE_TONES) { + dds->phase_rate[pindex] = teletone_dds_phase_rate(tone, rate); + return 0; + } + + return -1; +} + + + +/*! \file libteletone_generate.h + \brief Tone Generation Routines + + This module is responsible for tone generation specifics +*/ + +typedef int16_t teletone_audio_t; +struct teletone_generation_session; +typedef int (*tone_handler)(struct teletone_generation_session *ts, teletone_tone_map_t *map); + +/*! \brief An abstraction to store a tone generation session */ +struct teletone_generation_session { + /*! An array of tone mappings to character mappings */ + teletone_tone_map_t TONES[TELETONE_TONE_RANGE]; + /*! The number of channels the output audio should be in */ + int channels; + /*! The Rate in hz of the output audio */ + int rate; + /*! The duration (in samples) of the output audio */ + int duration; + /*! The duration of silence to append after the initial audio is generated */ + int wait; + /*! The duration (in samples) of the output audio (takes prescedence over actual duration value) */ + int tmp_duration; + /*! The duration of silence to append after the initial audio is generated (takes prescedence over actual wait value)*/ + int tmp_wait; + /*! Number of loops to repeat a single instruction*/ + int loops; + /*! Number of loops to repeat the entire set of instructions*/ + int LOOPS; + /*! Number to mutiply total samples by to determine when to begin ascent or decent e.g. 0=beginning 4=(last 25%) */ + float decay_factor; + /*! Direction to perform volume increase/decrease 1/-1*/ + int decay_direction; + /*! Number of samples between increase/decrease of volume */ + int decay_step; + /*! Volume factor of the tone */ + float volume; + /*! Debug on/off */ + int debug; + /*! FILE stream to write debug data to */ + FILE *debug_stream; + /*! Extra user data to attach to the session*/ + void *user_data; + /*! Buffer for storing sample data (dynamic) */ + teletone_audio_t *buffer; + /*! Size of the buffer */ + int datalen; + /*! In-Use size of the buffer */ + int samples; + /*! Callback function called during generation */ + int dynamic; + tone_handler handler; +}; + +typedef struct teletone_generation_session teletone_generation_session_t; + + +/*! + \brief Assign a set of tones to a tone_session indexed by a paticular index/character + \param ts the tone generation session + \param index the index to map the tone to + \param ... up to TELETONE_MAX_TONES frequencies terminated by 0.0 + \return 0 +*/ +TELETONE_API(int) teletone_set_tone(teletone_generation_session_t *ts, int index, ...); + +/*! + \brief Assign a set of tones to a single tone map + \param map the map to assign the tones to + \param ... up to TELETONE_MAX_TONES frequencies terminated by 0.0 + \return 0 +*/ +TELETONE_API(int) teletone_set_map(teletone_tone_map_t *map, ...); + +/*! + \brief Initilize a tone generation session + \param ts the tone generation session to initilize + \param buflen the size of the buffer(in samples) to dynamically allocate + \param handler a callback function to execute when a tone generation instruction is complete + \param user_data optional user data to send + \return 0 +*/ +TELETONE_API(int) teletone_init_session(teletone_generation_session_t *ts, int buflen, tone_handler handler, void *user_data); + +/*! + \brief Free the buffer allocated by a tone generation session + \param ts the tone generation session to destroy + \return 0 +*/ +TELETONE_API(int) teletone_destroy_session(teletone_generation_session_t *ts); + +/*! + \brief Execute a single tone generation instruction + \param ts the tone generation session to consult for parameters + \param map the tone mapping to use for the frequencies + \return 0 +*/ +TELETONE_API(int) teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *map); + +/*! + \brief Execute a tone generation script and call callbacks after each instruction + \param ts the tone generation session to execute on + \param cmd the script to execute + \return 0 +*/ +TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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: + */ diff --git a/libs/freetdm/src/include/sangoma_tdm_api.h b/libs/freetdm/src/include/sangoma_tdm_api.h new file mode 100644 index 0000000000..062d4e219d --- /dev/null +++ b/libs/freetdm/src/include/sangoma_tdm_api.h @@ -0,0 +1,321 @@ +/***************************************************************************** + * sangoma_tdm_api.h Sangoma TDM API Portability functions + * + * Author(s): Anthony Minessale II + * Nenad Corbic + * Michael Jerris + * David Rokhvarg + * + * Copyright: (c) 2006 Nenad Corbic + * Anthony Minessale II + * (c) 1984-2007 Sangoma Technologies Inc. + * + * ============================================================================ + */ + +#ifndef _SANGOMA_TDM_API_H +#define _SANGOMA_TDM_API_H + +/* This entire block of defines and includes from this line, through #define FNAME_LEN probably dont belong here */ +/* most of them probably belong in wanpipe_defines.h, then each header file listed included below properly included */ +/* in the header files that depend on them, leaving only the include for wanpipe_tdm_api.h left in this file or */ +/* possibly integrating the rest of this file diretly into wanpipe_tdm_api.h */ +#ifndef __WINDOWS__ +#if defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) +#define __WINDOWS__ +#endif /* defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) */ +#endif /* ndef __WINDOWS__ */ + +#if defined(__WINDOWS__) +#if defined(_MSC_VER) +/* disable some warnings caused by wanpipe headers that will need to be fixed in those headers */ +#pragma warning(disable:4201 4214) + +/* sang_api.h(74) : warning C4201: nonstandard extension used : nameless struct/union */ + +/* wanpipe_defines.h(219) : warning C4214: nonstandard extension used : bit field types other than int */ +/* wanpipe_defines.h(220) : warning C4214: nonstandard extension used : bit field types other than int */ +/* this will break for any compilers that are strict ansi or strict c99 */ + +/* The following definition for that struct should resolve this warning and work for 32 and 64 bit */ +#if 0 +struct iphdr { + +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned ihl:4, + version:4; +#elif defined (__BIG_ENDIAN_BITFIELD) + unsigned version:4, + ihl:4; +#else +# error "unknown byteorder!" +#endif + unsigned tos:8; + unsigned tot_len:16; + unsigned id:16; + unsigned frag_off:16; + __u8 ttl; + __u8 protocol; + __u16 check; + __u32 saddr; + __u32 daddr; + /*The options start here. */ +}; +#endif /* #if 0 */ + +#define __inline__ __inline +#endif /* defined(_MSC_VER) */ +#include +/* do we like the name WP_INVALID_SOCKET or should it be changed? */ +#define WP_INVALID_SOCKET INVALID_HANDLE_VALUE +#else /* defined(__WINDOWS__) */ +#define WP_INVALID_SOCKET -1 +#include +#include +#include +#endif + +#include +#include +#include +#include +#ifdef __WINDOWS__ +#include +#include +#endif +#include + +#define FNAME_LEN 50 + + +#if defined(__WINDOWS__) +/* This might be broken on windows, as POLL_EVENT_TELEPHONY seems to be commented out in sang_api.h.. it should be added to POLLPRI */ +#define POLLPRI (POLL_EVENT_LINK_STATE | POLL_EVENT_LINK_CONNECT | POLL_EVENT_LINK_DISCONNECT) +#endif + +/* return -1 for error, 0 for timeout or 1 for success. *flags is set to the poll evetns POLLIN | POLLOUT | POLLPRI based on the result of the poll */ +/* on windows we actually have POLLPRI defined with several events, so we could theoretically poll */ +/* for specific events. Is there any way to do this on *nix as well? */ + +/* a cross platform way to poll on an actual pollset (span and/or list of spans) will probably also be needed for analog */ +/* so we can have one analong handler thread that will deal with all the idle analog channels for events */ +/* the alternative would be for the driver to provide one socket for all of the oob events for all analog channels */ +static __inline__ int tdmv_api_wait_socket(sng_fd_t fd, int timeout, int *flags) +{ +#if defined(__WINDOWS__) + DWORD ln; + API_POLL_STRUCT api_poll; + + memset(&api_poll, 0x00, sizeof(API_POLL_STRUCT)); + + api_poll.user_flags_bitmap = *flags; + api_poll.timeout = timeout; + + if (!DeviceIoControl( + fd, + IoctlApiPoll, + (LPVOID)NULL, + 0L, + (LPVOID)&api_poll, + sizeof(API_POLL_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL)) { + return -1; + } + + *flags = 0; + + switch(api_poll.operation_status) + { + case SANG_STATUS_RX_DATA_AVAILABLE: + break; + + case SANG_STATUS_RX_DATA_TIMEOUT: + return 0; + + default: + return -1; + } + + if (api_poll.poll_events_bitmap == 0){ + return -1; + } + + if (api_poll.poll_events_bitmap & POLL_EVENT_TIMEOUT) { + return 0; + } + + *flags = api_poll.poll_events_bitmap; + + return 1; +#else + struct pollfd pfds[1]; + int res; + + memset(&pfds[0], 0, sizeof(pfds[0])); + pfds[0].fd = fd; + pfds[0].events = *flags; + res = poll(pfds, 1, timeout); + *flags = 0; + + if (pfds[0].revents & POLLERR) { + res = -1; + } + + if (res > 0) { + *flags = pfds[0].revents; + } + + return res; +#endif +} + +/* on windows right now, there is no way to specify if we want to read events here or not, we allways get them here */ +/* we need some what to select if we are reading regular tdm msgs or events */ +/* need to either have 2 functions, 1 for events, 1 for regural read, or a flag on this function to choose */ +/* 2 functions preferred. Need implementation for the event function for both nix and windows that is threadsafe */ +static __inline__ int tdmv_api_readmsg_tdm(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen) +{ + /* What do we need to do here to avoid having to do all */ + /* the memcpy's on windows and still maintain api compat with nix */ + int rx_len=0; +#if defined(__WINDOWS__) + static RX_DATA_STRUCT rx_data; + api_header_t *pri; + wp_tdm_api_rx_hdr_t *tdm_api_rx_hdr; + wp_tdm_api_rx_hdr_t *user_buf = (wp_tdm_api_rx_hdr_t*)hdrbuf; + DWORD ln; + + if (hdrlen != sizeof(wp_tdm_api_rx_hdr_t)){ + return -1; + } + + if (!DeviceIoControl( + fd, + IoctlReadCommand, + (LPVOID)NULL, + 0L, + (LPVOID)&rx_data, + sizeof(RX_DATA_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + )){ + return -1; + } + + pri = &rx_data.api_header; + tdm_api_rx_hdr = (wp_tdm_api_rx_hdr_t*)rx_data.data; + + user_buf->wp_tdm_api_event_type = pri->operation_status; + + switch(pri->operation_status) + { + case SANG_STATUS_RX_DATA_AVAILABLE: + if (pri->data_length > datalen){ + break; + } + memcpy(databuf, rx_data.data, pri->data_length); + rx_len = pri->data_length; + break; + + default: + break; + } + +#else + struct msghdr msg; + struct iovec iov[2]; + + memset(&msg,0,sizeof(struct msghdr)); + + iov[0].iov_len=hdrlen; + iov[0].iov_base=hdrbuf; + + iov[1].iov_len=datalen; + iov[1].iov_base=databuf; + + msg.msg_iovlen=2; + msg.msg_iov=iov; + + rx_len = read(fd,&msg,datalen+hdrlen); + + if (rx_len <= sizeof(wp_tdm_api_rx_hdr_t)){ + return -EINVAL; + } + + rx_len-=sizeof(wp_tdm_api_rx_hdr_t); +#endif + return rx_len; +} + +static __inline__ int tdmv_api_writemsg_tdm(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen) +{ + /* What do we need to do here to avoid having to do all */ + /* the memcpy's on windows and still maintain api compat with nix */ + int bsent = 0; +#if defined(__WINDOWS__) + static TX_DATA_STRUCT local_tx_data; + api_header_t *pri; + DWORD ln; + + /* Are these really not needed or used??? What about for nix?? */ + (void)hdrbuf; + (void)hdrlen; + + pri = &local_tx_data.api_header; + + pri->data_length = datalen; + memcpy(local_tx_data.data, databuf, pri->data_length); + + if (!DeviceIoControl( + fd, + IoctlWriteCommand, + (LPVOID)&local_tx_data, + (ULONG)sizeof(TX_DATA_STRUCT), + (LPVOID)&local_tx_data, + sizeof(TX_DATA_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + )){ + return -1; + } + + if (local_tx_data.api_header.operation_status == SANG_STATUS_SUCCESS) { + bsent = datalen; + } +#else + struct msghdr msg; + struct iovec iov[2]; + + memset(&msg,0,sizeof(struct msghdr)); + + iov[0].iov_len = hdrlen; + iov[0].iov_base = hdrbuf; + + iov[1].iov_len = datalen; + iov[1].iov_base = databuf; + + msg.msg_iovlen = 2; + msg.msg_iov = iov; + + bsent = write(fd, &msg, datalen + hdrlen); + if (bsent > 0){ + bsent -= sizeof(wp_tdm_api_tx_hdr_t); + } +#endif + return bsent; +} + +#endif /* _SANGOMA_TDM_API_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: + */ + diff --git a/libs/freetdm/src/include/uart.h b/libs/freetdm/src/include/uart.h new file mode 100644 index 0000000000..b1b04c09a4 --- /dev/null +++ b/libs/freetdm/src/include/uart.h @@ -0,0 +1,83 @@ +/* + * uart.h + * + * Copyright (c) 2005 Robert Krten. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This module contains the manifest constants and declarations for + * the UART module. + * + * 2005 06 19 R. Krten created +*/ + +#ifndef __UART_H__ +#define __UART_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*bytehandler_func_t) (void *, int); +typedef void (*bithandler_func_t) (void *, int); + + +typedef struct dsp_uart_attr_s +{ + bytehandler_func_t bytehandler; /* byte handler */ + void *bytehandler_arg; /* arbitrary ID passed to bytehandler as first argument */ +} dsp_uart_attr_t; + +typedef struct +{ + dsp_uart_attr_t attr; + int have_start; /* wait for start bit to show up */ + int data; /* data buffer */ + int nbits; /* number of bits accumulated so far */ +} dsp_uart_handle_t; + +/* + * Function prototypes + * + * General calling order is: + * a) create the attributes structure (dsp_uart_attr_init) + * b) initialize fields in the attributes structure (dsp_uart_attr_set_*) + * c) create a Bell-202 handle (dsp_uart_create) + * d) feed bits through dsp_uart_bit_handler +*/ + +void dsp_uart_attr_init(dsp_uart_attr_t *attributes); + +bytehandler_func_t dsp_uart_attr_get_bytehandler(dsp_uart_attr_t *attributes, void **bytehandler_arg); +void dsp_uart_attr_set_bytehandler(dsp_uart_attr_t *attributes, bytehandler_func_t bytehandler, void *bytehandler_arg); + +dsp_uart_handle_t * dsp_uart_create(dsp_uart_attr_t *attributes); +void dsp_uart_destroy(dsp_uart_handle_t **handle); + +void dsp_uart_bit_handler(void *handle, int bit); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/libs/freetdm/src/isdn/5ESSStateNT.c b/libs/freetdm/src/isdn/5ESSStateNT.c new file mode 100644 index 0000000000..1eb9415422 --- /dev/null +++ b/libs/freetdm/src/isdn/5ESSStateNT.c @@ -0,0 +1,132 @@ +/***************************************************************************** + + FileName: 5ESSStateNT.c + + Contents: AT&T 5ESS ISDN State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + Copyright (c) 2007, Michael S. Collins, All rights reserved. + email:mcollins@fcnetwork.com + + 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 Case Labs, Ltd nor the names of its 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 "5ESS.h" + +/***************************************************************************** + Function: ATT5ESSCreateNT + + Description: Will create the AT&T 5ESS ISDN NT as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void ATT5ESSCreateNT(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingNT, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingNT, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectNT, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckNT, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressNT, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupNT, Q931Umes_Setup, Q931Pmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckNT, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeNT, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckNT, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectNT, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendNT, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckNT, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectNT, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationNT, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectNT, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseNT, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteNT, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartNT, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckNT, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlNT, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationNT, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyNT, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusNT, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryNT, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentNT, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityNT, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldNT, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckNT, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectNT, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterNT, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveNT, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckNT, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectNT, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* TODO define state table here */ +} diff --git a/libs/freetdm/src/isdn/5ESSStateTE.c b/libs/freetdm/src/isdn/5ESSStateTE.c new file mode 100644 index 0000000000..e2ededd9ac --- /dev/null +++ b/libs/freetdm/src/isdn/5ESSStateTE.c @@ -0,0 +1,291 @@ +/***************************************************************************** + + FileName: 5ESSStateTE.c + + Contents: AT&T 5ESS ISDN State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + This reference implementation uses a process per message, + meaning that each message must check call states. This + is easier for dialect maintenance as each message proc + can be replaced individually. A new TE variant only + need to copy the Q931CreateTE and replace those procs or + need to override. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + Copyright (c) 2007, Michael S. Collins, All rights reserved. + email:mcollins@fcnetwork.com + + 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 Case Labs, Ltd nor the names of its 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 "5ESS.h" +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: ATT5ESSCreateTE + + Description: Will create the AT&T 5ESS TE as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void ATT5ESSCreateTE(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingTE, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingTE, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, ATT5ESSProc0x07TE, ATT5ESSUmes_0x07, ATT5ESSPmes_0x07); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, ATT5ESSProc0x0fTE, ATT5ESSUmes_0x0f, ATT5ESSPmes_0x0f); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressTE, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupTE, ATT5ESSUmes_Setup, ATT5ESSPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckTE, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeTE, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckTE, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectTE, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendTE, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckTE, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectTE, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationTE, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectTE, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseTE, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteTE, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartTE, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckTE, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlTE, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationTE, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyTE, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusTE, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryTE, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentTE, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityTE, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldTE, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckTE, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectTE, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterTE, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveTE, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckTE, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectTE, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANGE_STATUS, i, Q931Pie_ChangeStatus, Q931Uie_ChangeStatus); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* State 0 Idle */ + Q931AddStateEntry(i, Q931_U0, Q931mes_RESUME, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_STATUS, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE_COMPLETE, 4); + + /* State 1 Call Initiating */ + Q931AddStateEntry(i, Q931_U1, Q931mes_DISCONNECT, 2); + Q931AddStateEntry(i, Q931_U1, Q931mes_SETUP_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_RELEASE_COMPLETE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CONNECT, 4); + + /* State 2 Overlap Sending */ + Q931AddStateEntry(i, Q931_U2, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U2, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_RELEASE, 2); + + /* State 3 Outgoing Call Proceeding */ + Q931AddStateEntry(i, Q931_U3, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_RELEASE, 2); + + /* State 4 Call Delivered */ + Q931AddStateEntry(i, Q931_U4, Q931mes_CONNECT, 4); + + /* State 6 Call Precent */ + Q931AddStateEntry(i, Q931_U6, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CALL_PROCEEDING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE_COMPLETE, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U6, Q931mes_DISCONNECT, 4); + + /* State 7 Call Received */ + Q931AddStateEntry(i, Q931_U7, Q931mes_CONNECT, 2); + + /* State 8 Connect request */ + Q931AddStateEntry(i, Q931_U8, Q931mes_CONNECT_ACKNOWLEDGE, 4); + + /* State 9 Incoming Call Proceeding */ + Q931AddStateEntry(i, Q931_U9, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_PROGRESS, 2); + + /* State 10 Active */ + Q931AddStateEntry(i, Q931_U10, Q931mes_SUSPEND, 2); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 4); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 2); + + /* State 11 Disconnect Request */ + Q931AddStateEntry(i, Q931_U11, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_NOTIFY, 4); + + /* State 12 Disconnect Ind */ + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 2); + + /* State 15 Suspend Request */ + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_REJECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_RELEASE, 4); + +/* TODO + Q931AddStateEntry(i, Q931_U17, + Q931AddStateEntry(i, Q931_U19, + Q931AddStateEntry(i, Q931_U25, +*/ +} + +/***************************************************************************** + + Function: ATT5ESSProc0x0fTE + +*****************************************************************************/ +L3INT ATT5ESSProc0x0fTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->ProtDisc == 8) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + if (iFrom == 4) { + /* TODO Add proc here*/ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom ==2) { + /* TODO Add proc here*/ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + + if (pMes->ProtDisc == 3 && pTrunk->autoServiceAck) { + printf("autoServiceAck is on, responding to Service Req from network...\n"); + Q931AckService(pTrunk, buf); + } + } + return ret; + +} + +/***************************************************************************** + + Function: ATT5ESSProc0x07TE + +*****************************************************************************/ +L3INT ATT5ESSProc0x07TE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->ProtDisc == 8) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + if (iFrom == 4) { + /* TODO Add proc here*/ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here*/ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; + +} diff --git a/libs/freetdm/src/isdn/5ESSmes.c b/libs/freetdm/src/isdn/5ESSmes.c new file mode 100644 index 0000000000..7cdd21e7cc --- /dev/null +++ b/libs/freetdm/src/isdn/5ESSmes.c @@ -0,0 +1,361 @@ +/***************************************************************************** + + FileName: 5ESSmes.c + + Contents: Pack/Unpack functions. These functions will unpack a 5ESS ISDN + message from the bit packed original format into structs + that contains variables sized by the user. It will also pack + the struct back into a Q.931 message as required. + + See 5ESS.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + Copyright (c) 2007, Michael S. Collins, All rights reserved. + email:mcollins@fcnetwork.com + + 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 Case Labs, Ltd nor the names of its 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 "5ESS.h" + +/***************************************************************************** + + Function: ATT5ESSUmes_Setup + +*****************************************************************************/ +L3INT ATT5ESSUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT ir = 0; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + L3UCHAR last_codeset = 0, codeset = 0; + L3UCHAR shift_nolock = 1; + + while (IOff < Size) { + + if (shift_nolock) { + codeset = last_codeset; + } + + if ((IBuf[IOff] & 0xF0) == Q931ie_SHIFT) { + shift_nolock = (IBuf[IOff] & 0x08); + if (shift_nolock) { + last_codeset = codeset; + } + codeset = ((IBuf[IOff] & 0x07)); + IOff++; + } + + if (codeset == 0) { + switch (IBuf[IOff]) + { + case Q931ie_SENDING_COMPLETE: + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_NETWORK_SPECIFIC_FACILITIES: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_SIGNAL: + case Q931ie_CALLING_PARTY_NUMBER: + case Q931ie_CALLING_PARTY_SUBADDRESS: + case Q931ie_CALLED_PARTY_NUMBER: + case Q931ie_CALLED_PARTY_SUBADDRESS: + case Q931ie_TRANSIT_NETWORK_SELECTION: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_FACILITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_REPEAT_INDICATOR: + if (ir < 2) { + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + ir++; + } else { + return Q931E_ILLEGAL_IE; + } + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else if (codeset == 6) { + switch (IBuf[IOff]) + { + case Q931ie_GENERIC_DIGITS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else if (codeset == 7) { + switch (IBuf[IOff]) + { + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else { + return Q931E_ILLEGAL_IE; + } + } + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: ATT5ESSPmes_Setup + + Decription: Pack a Q931mes_Generic into a real Q.931 message. The user will + set up a SETUP message and issue this to the stack where it + is processed by Q931ProcSetup that processes and validates + it before it actually sends it out. This function is called + to compute the real Q.931 message. + + Parameters: IBuf[IN] Ptr to un-packed struct + ISize[IN] Size of input buffer (unpacked message). + OBuf[OUT] Ptr to packed 'octet' wise message. + OSize[OUT] Size of packed message. + + Called By: Q931ProcSetup + +*****************************************************************************/ +L3INT ATT5ESSPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3INT rc = Q931E_NO_ERROR; + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)(pMes->SendComplete & 0x00ff); + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->RepeatInd)) { + OBuf[Octet++] = (L3UCHAR)(pMes->RepeatInd & 0x00ff); + } + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet))!=0) + return rc; + } else { + rc = Q931E_BEARERCAP; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Network specific facilities */ + if (Q931IsIEPresent(pMes->NetFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_NETWORK_SPECIFIC_FACILITIES](pTrunk, Q931GetIEPtr(pMes->NetFac,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Calling Party Number */ + if (Q931IsIEPresent(pMes->CallingNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CallingNum,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Calling Party Subaddress */ + if (Q931IsIEPresent(pMes->CallingSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CallingSub,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Called party subaddress */ + if (Q931IsIEPresent(pMes->CalledSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CalledSub,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Transit network selection */ + if (Q931IsIEPresent(pMes->TransNetSel)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_TRANSIT_NETWORK_SELECTION](pTrunk, Q931GetIEPtr(pMes->TransNetSel,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->LLRepeatInd)) { + rc = Q931E_UNKNOWN_IE;/* TODO */ + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + *OSize = Octet; + return rc; +} + + +/***************************************************************************** + + Function: ATT5ESSUmes_0x0f + +*****************************************************************************/ +L3INT ATT5ESSUmes_0x0f(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + if (mes->ProtDisc == 8) { + return Q931Umes_ConnectAck(pTrunk, IBuf, mes, IOff, Size); + } + + if (mes->ProtDisc == 3) { + return Q931Umes_Service(pTrunk, IBuf, mes, IOff, Size); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: ATT5ESSPmes_0x0f + +*****************************************************************************/ +L3INT ATT5ESSPmes_0x0f(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *mes = (Q931mes_Generic *)IBuf; + + if (mes->ProtDisc == 8) { + return Q931Pmes_ConnectAck(pTrunk, IBuf, ISize, OBuf, OSize); + } + + if (mes->ProtDisc == 3) { + return Q931Pmes_Service(pTrunk, IBuf, ISize, OBuf, OSize); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: ATT5ESSUmes_0x07 + +*****************************************************************************/ +L3INT ATT5ESSUmes_0x07(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + if (mes->ProtDisc == 8) { + return Q931Umes_Connect(pTrunk, IBuf, mes, IOff, Size); + } + + if (mes->ProtDisc == 3) { + return Q931Umes_ServiceAck(pTrunk, IBuf, mes, IOff, Size); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: ATT5ESSPmes_0x07 + +*****************************************************************************/ +L3INT ATT5ESSPmes_0x07(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *mes = (Q931mes_Generic *)IBuf; + + if (mes->ProtDisc == 8) { + return Q931Pmes_Connect(pTrunk, IBuf, ISize, OBuf, OSize); + } + + if (mes->ProtDisc == 3) { + return Q931Pmes_ServiceAck(pTrunk, IBuf, ISize, OBuf, OSize); + } + + return Q931E_UNKNOWN_MESSAGE; +} diff --git a/libs/freetdm/src/isdn/DMSStateNT.c b/libs/freetdm/src/isdn/DMSStateNT.c new file mode 100644 index 0000000000..e8814ba8ef --- /dev/null +++ b/libs/freetdm/src/isdn/DMSStateNT.c @@ -0,0 +1,126 @@ +/***************************************************************************** + + FileName: DMSStateNT.c + + Contents: DMS-100 ISDN State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "DMS.h" + +/***************************************************************************** + Function: DMSCreateNT + + Description: Will create the National ISDN NT as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void DMSCreateNT(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingNT, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingNT, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectNT, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckNT, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressNT, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupNT, DMSUmes_Setup, DMSPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckNT, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeNT, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckNT, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectNT, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendNT, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckNT, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectNT, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationNT, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectNT, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseNT, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteNT, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartNT, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckNT, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlNT, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationNT, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyNT, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusNT, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryNT, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentNT, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityNT, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldNT, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckNT, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectNT, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterNT, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveNT, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckNT, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectNT, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* TODO define state table here */ +} diff --git a/libs/freetdm/src/isdn/DMSStateTE.c b/libs/freetdm/src/isdn/DMSStateTE.c new file mode 100644 index 0000000000..cb740d3c2f --- /dev/null +++ b/libs/freetdm/src/isdn/DMSStateTE.c @@ -0,0 +1,284 @@ +/***************************************************************************** + + FileName: DMSStateTE.c + + Contents: DMS-100 ISDN State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + This reference implementation uses a process per message, + meaning that each message must check call states. This + is easier for dialect maintenance as each message proc + can be replaced individually. A new TE variant only + need to copy the Q931CreateTE and replace those procs or + need to override. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "DMS.h" +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: DMSCreateTE + + Description: Will create the National TE as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void DMSCreateTE(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingTE, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingTE, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, DMSProc0x07TE, DMSUmes_0x07, DMSPmes_0x07); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, DMSProc0x0fTE, DMSUmes_0x0f, DMSPmes_0x0f); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressTE, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupTE, DMSUmes_Setup, DMSPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckTE, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeTE, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckTE, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectTE, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendTE, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckTE, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectTE, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationTE, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectTE, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseTE, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteTE, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartTE, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckTE, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlTE, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationTE, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyTE, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusTE, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryTE, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentTE, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityTE, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldTE, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckTE, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectTE, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterTE, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveTE, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckTE, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectTE, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANGE_STATUS, i, Q931Pie_ChangeStatus, Q931Uie_ChangeStatus); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* State 0 Idle */ + Q931AddStateEntry(i, Q931_U0, Q931mes_RESUME, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_STATUS, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE_COMPLETE, 4); + + /* State 1 Call Initiating */ + Q931AddStateEntry(i, Q931_U1, Q931mes_DISCONNECT, 2); + Q931AddStateEntry(i, Q931_U1, Q931mes_SETUP_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_RELEASE_COMPLETE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CONNECT, 4); + + /* State 2 Overlap Sending */ + Q931AddStateEntry(i, Q931_U2, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U2, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_RELEASE, 2); + + /* State 3 Outgoing Call Proceeding */ + Q931AddStateEntry(i, Q931_U3, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_RELEASE, 2); + + /* State 4 Call Delivered */ + Q931AddStateEntry(i, Q931_U4, Q931mes_CONNECT, 4); + + /* State 6 Call Precent */ + Q931AddStateEntry(i, Q931_U6, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CALL_PROCEEDING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE_COMPLETE, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U6, Q931mes_DISCONNECT, 4); + + /* State 7 Call Received */ + Q931AddStateEntry(i, Q931_U7, Q931mes_CONNECT, 2); + + /* State 8 Connect request */ + Q931AddStateEntry(i, Q931_U8, Q931mes_CONNECT_ACKNOWLEDGE, 4); + + /* State 9 Incoming Call Proceeding */ + Q931AddStateEntry(i, Q931_U9, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_PROGRESS, 2); + + /* State 10 Active */ + Q931AddStateEntry(i, Q931_U10, Q931mes_SUSPEND, 2); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 4); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 2); + + /* State 11 Disconnect Request */ + Q931AddStateEntry(i, Q931_U11, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_NOTIFY, 4); + + /* State 12 Disconnect Ind */ + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 2); + + /* State 15 Suspend Request */ + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_REJECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_RELEASE, 4); + +/* TODO + Q931AddStateEntry(i, Q931_U17, + Q931AddStateEntry(i, Q931_U19, + Q931AddStateEntry(i, Q931_U25, +*/ +} + +/***************************************************************************** + + Function: DMSProc0x0fTE + +*****************************************************************************/ +L3INT DMSProc0x0fTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->ProtDisc == 8) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + if (iFrom == 4) { + /* TODO Add proc here*/ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here*/ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + + if (pMes->ProtDisc == 3 && pTrunk->autoServiceAck) { + Q931AckService(pTrunk, buf); + } + } + return ret; + +} + +/***************************************************************************** + + Function: DMSProc0x07TE + +*****************************************************************************/ +L3INT DMSProc0x07TE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->ProtDisc == 8) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + if (iFrom == 4) { + /* TODO Add proc here*/ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here*/ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; + +} diff --git a/libs/freetdm/src/isdn/DMSmes.c b/libs/freetdm/src/isdn/DMSmes.c new file mode 100644 index 0000000000..3ce82f2ded --- /dev/null +++ b/libs/freetdm/src/isdn/DMSmes.c @@ -0,0 +1,344 @@ +/***************************************************************************** + + FileName: DMSmes.c + + Contents: Pack/Unpack functions. These functions will unpack a DMS-100 ISDN + message from the bit packed original format into structs + that contains variables sized by the user. It will also pack + the struct back into a Q.931 message as required. + + See national.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "DMS.h" + +/***************************************************************************** + + Function: DMSUmes_Setup + +*****************************************************************************/ +L3INT DMSUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT ir = 0; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + L3UCHAR last_codeset = 0, codeset = 0; + L3UCHAR shift_lock = 1; + + while (IOff < Size) { + if (!shift_lock) { + codeset = last_codeset; + } + + if ((IBuf[IOff] & 0xF0) == Q931ie_SHIFT ) { + shift_lock = (IBuf[IOff] & 0x08); + if (shift_lock) { + last_codeset = codeset; + } + codeset = ((IBuf[IOff] & 0x07)); + IOff++; + } + + if (codeset == 0) { + switch (IBuf[IOff]) { + case Q931ie_SENDING_COMPLETE: + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_NETWORK_SPECIFIC_FACILITIES: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_SIGNAL: + case Q931ie_CALLING_PARTY_NUMBER: + case Q931ie_CALLING_PARTY_SUBADDRESS: + case Q931ie_CALLED_PARTY_NUMBER: + case Q931ie_CALLED_PARTY_SUBADDRESS: + case Q931ie_TRANSIT_NETWORK_SELECTION: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_REPEAT_INDICATOR: + if (ir < 2) { + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + ir++; + } else { + return Q931E_ILLEGAL_IE; + } + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else if (codeset == 6) { + switch (IBuf[IOff]) { + case Q931ie_GENERIC_DIGITS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + + } else { + return Q931E_ILLEGAL_IE; + } + } + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: DMSPmes_Setup + + Decription: Pack a Q931mes_Generic into a real Q.931 message. The user will + set up a SETUP message and issue this to the stack where it + is processed by Q931ProcSetup that processes and validates + it before it actually sends it out. This function is called + to compute the real Q.931 message. + + Parameters: IBuf[IN] Ptr to un-packed struct + ISize[IN] Size of input buffer (unpacked message). + OBuf[OUT] Ptr to packed 'octet' wise message. + OSize[OUT] Size of packed message. + + Called By: Q931ProcSetup + +*****************************************************************************/ +L3INT DMSPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3INT rc = Q931E_NO_ERROR; + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)(pMes->SendComplete & 0x00ff); + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->RepeatInd)) { + OBuf[Octet++] = (L3UCHAR)(pMes->RepeatInd & 0x00ff); + } + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } else { + rc = Q931E_BEARERCAP; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Network spesific facilities */ + if (Q931IsIEPresent(pMes->NetFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_NETWORK_SPECIFIC_FACILITIES](pTrunk, Q931GetIEPtr(pMes->NetFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Number */ + if (Q931IsIEPresent(pMes->CallingNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CallingNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Subaddress */ + if (Q931IsIEPresent(pMes->CallingSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CallingSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + + /* Called party subaddress */ + if (Q931IsIEPresent(pMes->CalledSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CalledSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Transit network selection */ + if (Q931IsIEPresent(pMes->TransNetSel)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_TRANSIT_NETWORK_SELECTION](pTrunk, Q931GetIEPtr(pMes->TransNetSel,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->LLRepeatInd)) { + rc = Q931E_UNKNOWN_IE;/* TODO */ + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + + +/***************************************************************************** + + Function: DMSUmes_0x0f + +*****************************************************************************/ +L3INT DMSUmes_0x0f(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + if (mes->ProtDisc == 8) { + return Q931Umes_ConnectAck(pTrunk, IBuf, mes, IOff, Size); + } + + if (mes->ProtDisc == 3) { + return Q931Umes_Service(pTrunk, IBuf, mes, IOff, Size); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: DMSPmes_0x0f + +*****************************************************************************/ +L3INT DMSPmes_0x0f(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *mes = (Q931mes_Generic *)IBuf; + + if (mes->ProtDisc == 8) { + return Q931Pmes_ConnectAck(pTrunk, IBuf, ISize, OBuf, OSize); + } + + if (mes->ProtDisc == 3) { + return Q931Pmes_Service(pTrunk, IBuf, ISize, OBuf, OSize); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: DMSUmes_0x07 + +*****************************************************************************/ +L3INT DMSUmes_0x07(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + if (mes->ProtDisc == 8) { + return Q931Umes_Connect(pTrunk, IBuf, mes, IOff, Size); + } + + if (mes->ProtDisc == 3) { + return Q931Umes_ServiceAck(pTrunk, IBuf, mes, IOff, Size); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: DMSPmes_0x07 + +*****************************************************************************/ +L3INT DMSPmes_0x07(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *mes = (Q931mes_Generic *)IBuf; + + if (mes->ProtDisc == 8) { + return Q931Pmes_Connect(pTrunk, IBuf, ISize, OBuf, OSize); + } + + if (mes->ProtDisc == 3) { + return Q931Pmes_ServiceAck(pTrunk, IBuf, ISize, OBuf, OSize); + } + + return Q931E_UNKNOWN_MESSAGE; +} diff --git a/libs/freetdm/src/isdn/EuroISDNStateNT.c b/libs/freetdm/src/isdn/EuroISDNStateNT.c new file mode 100644 index 0000000000..6b26c91e04 --- /dev/null +++ b/libs/freetdm/src/isdn/EuroISDNStateNT.c @@ -0,0 +1,44 @@ +/***************************************************************************** + + FileName: EuroISDNStateNT.c + + Contents: EuroISDN State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" diff --git a/libs/freetdm/src/isdn/EuroISDNStateTE.c b/libs/freetdm/src/isdn/EuroISDNStateTE.c new file mode 100644 index 0000000000..ef6b39e8e5 --- /dev/null +++ b/libs/freetdm/src/isdn/EuroISDNStateTE.c @@ -0,0 +1,58 @@ +/***************************************************************************** + + FileName: EuroISDNStateTE.c + + Contents: EuroISDN State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +/* + EuroISDN is a sub-set of Q.931. Q.931 is very generic as it embrase a lot, + while EuroISDN is more exact and make decitions on some of the + 'implementation options' in the original standard. EuroISDN will + however run smoothly under the generic space, so these functions are more + for show +*/ +#if 0 +static void EuroISDNCreateTE(L3UCHAR i) +{ + Q931CreateTE(i); +} +#endif diff --git a/libs/freetdm/src/isdn/Q921.c b/libs/freetdm/src/isdn/Q921.c new file mode 100644 index 0000000000..d1cbab6e6f --- /dev/null +++ b/libs/freetdm/src/isdn/Q921.c @@ -0,0 +1,3518 @@ +/***************************************************************************** + + FileName: q921.c + + Description: Contains the implementation of a Q.921 protocol + + Created: 27.dec.2000/JVB + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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. + +*****************************************************************************/ + +/**************************************************************************** + * Changes: + * + * - June-August 2008: Stefan Knoblich : + * Add PTMP TEI management (NT + TE mode) + * Add timers + * Add retransmit counters + * Add logging + * Various cleanups + * Queues, retransmission of I frames + * PTMP NT mode + * + * + * TODO: + * + * - Cleanup queueing, test retransmission + * + * - Q921Start() /-Stop() TEI acquire + release + * (move everything related into these functions) + * + * - Q.921 '97 Appendix I (and maybe III, IV) + * + * - More complete Appendix II + * + * - Test PTP mode + * + * - PTMP NT mode (in progress) + * + * - NT mode TEI management: (ab)use T202 for TEI Check Request retransmission + * + * - General cleanup (move all non-public declarations into private header file) + * + * - Statistics, per-Frame type debug message filter + * + ****************************************************************************/ + +#include +#include +#include +#include + +#include "freetdm.h" +#include "Q921.h" +#include "Q921priv.h" +#include "mfifo.h" + +#ifdef WIN32 +#pragma warning(disable:4100 4244) +#endif + +/****************************************************************************************************** + * Actual code below this line + ******************************************************************************************************/ + + +/** + * Q921StateNames + * \brief Static array of state name / value mappings + */ +static struct Q921StateName { + Q921State_t value; + const char *name; +} Q921StateNames[10] = { + { Q921_STATE_STOPPED, "Stopped" }, + { Q921_STATE_TEI_UNASSIGNED, "TEI Unassigned" }, + { Q921_STATE_TEI_AWAITING, "TEI Awaiting Assignment" }, + { Q921_STATE_TEI_ESTABLISH, "TEI Awaiting Establishment" }, + { Q921_STATE_TEI_ASSIGNED, "TEI Assigned" }, + { Q921_STATE_AWAITING_ESTABLISHMENT, "Awaiting Establishment" }, + { Q921_STATE_AWAITING_RELEASE, "Awaiting Release" }, + { Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, "Multiple Frame Mode Established" }, + { Q921_STATE_TIMER_RECOVERY, "Timer Recovery" }, + { 0, 0 } +}; + +/** + * Q921State2Name + * \brief Convert state value to name + * \param[in] state the state value + * \return the state name or "Unknown" + * + * \author Stefan Knoblich + */ +static const char *Q921State2Name(Q921State_t state) +{ + struct Q921StateName *p = Q921StateNames; + + while(p->name) { + if(p->value == state) + return p->name; + p++; + } + + return "Unknown"; +} + + +/** + * Q921SendEnquiry + */ +static int Q921SendEnquiry(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* send enquiry: begin */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + + Q921SendRNR(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 1); + } + else { + Q921SendRR(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 1); + } + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* "Start" T200 */ + Q921T200TimerReset(trunk, tei); + + /* send enquiry: end */ + return 1; +} + +/** + * Q921SendEnquiryResponse + */ +static int Q921SendEnquiryResponse(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* send enquiry: begin */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + + Q921SendRNR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + } + else { + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + /* send enquiry: end */ + return 1; +} + +/** + * Q921ResetExceptionConditions + * \brief Reset Q.921 Exception conditions procedure + * \param trunk Q.921 data structure + * \param tei TEI + * \todo Do something + */ +static void Q921ResetExceptionConditions(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* Clear peer receiver busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* Clear reject exception */ + Q921_CLEAR_FLAG(link, Q921_FLAG_REJECT); + + /* Clear own receiver busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_RECV_BUSY); + + /* Clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + return; +} + +/** + * Q921EstablishDataLink + * \brief Q.921 Establish data link procedure + * \param trunk Q.921 data structure + * \param tei TEI + * \return always 1 (success) + */ +static int Q921EstablishDataLink(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* reset exception conditions */ + Q921ResetExceptionConditions(trunk, tei); + + /* RC = 0 */ + link->N200 = 0; + + /* Send SABME */ + Q921SendSABME(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 1); + + /* Restart T200, stop T203 */ + Q921T200TimerReset(trunk, tei); + Q921T203TimerStop(trunk, tei); + + return 1; +} + +/** + * Q921NrErrorRecovery + * \brief NR(R) Error recovery procedure + * \param trunk Q.921 data structure + * \param tei TEI + * \return always 1 (success) + */ +static int Q921NrErrorRecovery(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* MDL Error indication (J) */ + + /* Establish datalink */ + Q921EstablishDataLink(trunk, tei); + + /* Clear L3 initiated */ + Q921_CLEAR_FLAG(link, Q921_FLAG_L3_INITIATED); + + return 1; +} + + +/** + * Q921InvokeRetransmission + * \brief I Frame retransmission procedure + * \param trunk Q.921 data structure + * \param tei TEI + * \param nr N(R) for retransmission + * \return always 1 (success) + */ +static int Q921InvokeRetransmission(L2TRUNK trunk, L2UCHAR tei, L2UCHAR nr) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + L2UCHAR *mes; + L2INT qpos, qnum, size = 0; + + qnum = MFIFOGetMesCount(link->IFrameResendQueue); + qpos = qnum - 1; + + /* + * slightly different than what is shown in the spec + * (Q.921 '97 Annex B, Figure B.9, page 104) + * + * what the above mentioned figure probably means is: + * "as long as V(S) != N(R), move the pointer marking + * the first frame to start resending at to the previous + * frame" + * + * if we actually implemented it as shown in the figure, we'd be + * resending frames in the wrong order (moving backwards in time) + * meaning we'd have to add an incoming queue to reorder the frames + * + */ + /* + * TODO: There's a "traditional" off-by-one error hidden in the original + * mfifo implementation + it's late, i'm tired and being lazy, + * so i'll probably have added another one :P + * + * wow, the first while loop sucks and can be removed + */ + while(link->vs != nr && qpos > 0) { /* ???? */ + /* V(S) = V(S) - 1 */ + Q921_DEC_COUNTER(link->vs); /* huh? backwards? */ + + /* next frame in queue (backtrack along I queue) ??? */ + qpos--; + } + + /* + * being lazy and trying to avoid mod 128 math this way... + */ + if(link->vs != nr && !qpos) { + /* fatal, we don't have enough history to resend all missing frames */ + /* TODO: how to handle this? */ + } + + /* + * resend frames in correct order (oldest missing frame first, + * contrary to what the spec figure shows) + */ + while(qpos < qnum) { + /* Grab frame's buffer ptr and size from queue */ + mes = MFIFOGetMesPtrOffset(link->IFrameResendQueue, &size, qpos); + if(mes) { + /* requeue frame (TODO: check queue full condition) */ + MFIFOWriteMes(link->IFrameQueue, mes, size); + + /* set I frame queued */ + } + + qpos++; + } + + return 1; +} + + +static int Q921AcknowledgePending(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + if(Q921_CHECK_FLAG(link, Q921_FLAG_ACK_PENDING)) { + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* send RR */ + Q921SendRR(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 0); + + return 1; + } + break; + + default: + break; + } + + return 0; +} + +/***************************************************************************** + + Function: Q921_InitTrunk + + Decription: Initialize a Q.921 trunk so it is ready for use. This + function MUST be called before you call any other functions. + +*****************************************************************************/ +int Q921_InitTrunk(L2TRUNK trunk, + L2UCHAR sapi, + L2UCHAR tei, + Q921NetUser_t NetUser, + Q921NetType_t NetType, + L2INT hsize, + Q921Tx21CB_t cb21, + Q921Tx23CB_t cb23, + void *priv21, + void *priv23) +{ + int numlinks = 0; + + trunk->sapi = sapi; + trunk->tei = tei; + trunk->NetUser = NetUser; + trunk->NetType = NetType; + trunk->Q921Tx21Proc = cb21; + trunk->Q921Tx23Proc = cb23; + trunk->PrivateData21 = priv21; + trunk->PrivateData23 = priv23; + trunk->Q921HeaderSpace = hsize; + + numlinks = Q921_IS_PTMP_NT(trunk) ? Q921_TEI_MAX : 1; + + if (trunk->initialized != INITIALIZED_MAGIC) { + MFIFOCreate(trunk->HDLCInQueue, Q921MAXHDLCSPACE, 10); + + /* + * Allocate space for per-link context(s) + */ + trunk->context = ftdm_malloc(numlinks * sizeof(struct Q921_Link)); + if(!trunk->context) + return -1; + + trunk->initialized = INITIALIZED_MAGIC; + } + + /* timeout default values */ + trunk->T200Timeout = 1000; /* 1 second */ + trunk->T203Timeout = 10000; /* 10 seconds */ + trunk->T202Timeout = 2000; /* 2 seconds */ + trunk->T201Timeout = 200000; /* 200 seconds */ + trunk->TM01Timeout = 10000; /* 10 seconds */ + + /* octet / retransmit counter default limits */ + trunk->N200Limit = 3; /* 3 retransmits */ + trunk->N201Limit = 260; /* 260 octets */ + trunk->N202Limit = 3; /* 3 retransmits */ + trunk->k = 7; /* 7 outstanding ACKs */ + + /* reset counters, timers, etc. */ + trunk->T202 = 0; + trunk->N202 = 0; + + /* Reset per-link contexts */ + memset(trunk->context, 0, numlinks * sizeof(struct Q921_Link)); + + /* clear tei map */ + memset(trunk->tei_map, 0, Q921_TEI_MAX + 1); + + if(Q921_IS_PTMP(trunk)) { + /* + * We're either the Network side (NT, TEI = 0) + * or user-side equipment (TE) which will get it's TEI via + * dynamic assignment + */ + trunk->tei = 0; + } + + return 0; +} + + +/** + * Q921Tx21Proc + * \brief Submit frame to layer 1 (for sending) + * \param[in] trunk Pointer to trunk struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + */ +static int Q921Tx21Proc(L2TRUNK trunk, L2UCHAR *Msg, L2INT size) +{ + Q921LogMesg(trunk, Q921_LOG_DEBUG, 0, Msg, size, "Sending frame"); + + return trunk->Q921Tx21Proc(trunk->PrivateData21, Msg, size); +} + + +/** + * Q921Tx23Proc + * \brief Submit frame to layer 3 + * \param[in] trunk Pointer to trunk struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + */ +static int Q921Tx23Proc(L2TRUNK trunk, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR *Msg, L2INT size) +{ + return trunk->Q921Tx23Proc(trunk->PrivateData23, ind, tei, Msg, size); +} + + +/** + * Q921LogProc + * \brief Used for logging, converts to string and submits to higher level log function via callback + * \param[in] trunk Pointer to trunk struct + * \param[in] level Q921 Loglevel + * \param[in] fmt format of logmessage + * \return >= 0 on success, < 0 on error + * + * \author Stefan Knoblich + */ +static int Q921Log(L2TRUNK trunk, Q921LogLevel_t level, const char *fmt, ...) +{ + char buf[Q921_LOGBUFSIZE]; + L2INT len; + va_list ap; + + if(!trunk->Q921LogProc) + return 0; + + if(trunk->loglevel < level) + return 0; + + va_start(ap, fmt); + + len = vsnprintf(buf, sizeof(buf)-1, fmt, ap); + if(len <= 0) { + /* TODO: error handling */ + return -1; + } + if(len >= sizeof(buf)) + len = sizeof(buf) - 1; + + buf[len] = '\0'; + + va_end(ap); + + return trunk->Q921LogProc(trunk->PrivateDataLog, level, buf, len); +} + + +static int print_hex(char *buf, int bsize, const unsigned char *in, const int len) +{ + static const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + int offset = 0; + int pos = 0; + int nr = 0; + + buf[pos++] = '['; + bsize -= 3; + + while((bsize - pos) > 0 && offset < len) { + buf[pos++] = hex[(in[offset] & 0xF0) >> 4]; + buf[pos++] = hex[(in[offset++] & 0x0F)]; + + if(++nr == 32 && offset < len && (bsize - pos) > 3) { + nr = 0; + buf[pos++] = ']'; + buf[pos++] = '\n'; + buf[pos++] = '['; + } + else if(offset < len) { + buf[pos++] = ' '; + } + } + + buf[pos++] = ']'; + buf[pos++] = '\n'; + buf[pos] = '\0'; + + return pos; +} + +#define APPEND_MSG(buf, off, lef, fmt, ...) \ + len = snprintf(buf + off, lef, fmt, ##__VA_ARGS__); \ + if(len > 0) { \ + off += len; \ + lef -= len; \ + } else { \ + goto out; \ + } + +/** + * Q921LogProcMesg + * \brief Used for logging, converts to string and submits to higher level log function via callback + * \param[in] trunk Pointer to trunk struct + * \param[in] level Q921 Loglevel + * \param[in] received direction of the message (received = 1, sending = 0) + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \param[in] fmt format of logmessage + * \return >= 0 on success, < 0 on error + * + * \author Stefan Knoblich + */ +static int Q921LogMesg(L2TRUNK trunk, Q921LogLevel_t level, L2UCHAR received, L2UCHAR *mes, L2INT size, const char *fmt, ...) +{ + char buf[Q921_LOGBUFSIZE]; + size_t len, left; + va_list ap; + + if(!trunk->Q921LogProc) + return 0; + + if(trunk->loglevel < level) + return 0; + + if(!mes) + return 0; + + memset(buf, 0, sizeof(buf)); + + left = sizeof(buf) - 1; + + va_start(ap, fmt); + + len = vsnprintf(buf, left, fmt, ap); + if(len > 0) + left -= len; + else { + /* TODO: error handling */ + return -1; + } + + va_end(ap); + + if(trunk->loglevel == Q921_LOG_DEBUG) { + char pbuf[1024]; + size_t pleft, poffset; + L2UCHAR sapi, tei, cr; + L2UCHAR *pmes = mes + trunk->Q921HeaderSpace; + struct Q921_Link *link; + + memset(pbuf, 0, sizeof(pbuf)); + + pleft = sizeof(pbuf); + poffset = 0; + + /* + * Decode packet + */ + sapi = (pmes[0] & 0xfc) >> 2; + cr = (pmes[0] & 0x02) >> 1; + tei = (pmes[1] & 0xfe) >> 1; + link = Q921_LINK_CONTEXT(trunk, tei); + + /* make cr actually useful */ + cr = (received) ? Q921_IS_COMMAND(trunk, cr) : Q921_IS_RESPONSE(trunk, cr); + + /* filter */ + if((pmes[2] & 0x01) == 0x00) { + ; + } + else if((pmes[2] & 0x03) == 0x01) { + ; //return 0; + } + else if((pmes[2] & 0x03) == 0x03) { + ; + } + + APPEND_MSG(pbuf, poffset, pleft, "\n----------------- Q.921 Packet [%s%s] ---------------\n", received ? "Incoming" : "Outgoing", + (tei == link->tei || tei == Q921_TEI_BCAST) ? "" : ", Ignored" ); + + /* common header */ + APPEND_MSG(pbuf, poffset, pleft, " SAPI: %u, TEI: %u, C/R: %s (%d)\n\n", sapi, tei, (cr) ? "Command" : "Response", (mes[0] & 0x02) >> 1 ); + + /* + * message specific + */ + if((pmes[2] & 0x01) == 0x00) { + /* + * I frame + */ + L2UCHAR pf = pmes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = pmes[3] >> 1; /* receive sequence number */ + L2UCHAR ns = pmes[2] >> 1; /* send sequence number */ + + APPEND_MSG(pbuf, poffset, pleft, " Type: I Frame\n P/F: %d, N(S): %d, N(R): %d [V(A): %d, V(R): %d, V(S): %d]\n", pf, ns, nr, + link->va, link->vr, link->vs); + + /* Dump content of I Frames for foreign TEIs */ + if(tei != link->tei) { + APPEND_MSG(pbuf, poffset, pleft, " CONTENT:\n"); + + len = print_hex(pbuf + poffset, (int)pleft, &pmes[4], size - (trunk->Q921HeaderSpace + 4)); + poffset += len; + pleft -= len; + } + } + else if((pmes[2] & 0x03) == 0x01) { + /* + * S frame + */ + L2UCHAR sv = (pmes[2] & 0x0c) >> 2; /* supervisory format id */ + L2UCHAR pf = pmes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = pmes[3] >> 1; /* receive sequence number */ + const char *type; + + switch(sv) { + case 0x00: /* RR : Receive Ready */ + type = "RR (Receive Ready)"; + break; + + case 0x02: /* RNR : Receive Not Ready */ + type = "RNR (Receiver Not Ready)"; + break; + + case 0x04: /* REJ : Reject */ + type = "REJ (Reject)"; + break; + + default: /* Invalid / Unknown */ + type = "Unknown"; + break; + } + + APPEND_MSG(pbuf, poffset, pleft, " Type: S Frame, SV: %s\n P/F: %d, N(R): %d [V(A): %d, V(R): %d, V(S): %d]\n", type, pf, nr, + link->va, link->vr, link->vs); + } + else if((pmes[2] & 0x03) == 0x03) { + /* + * U frame + */ + L2UCHAR m = (pmes[2] & 0xe0) >> 3 | (pmes[2] & 0x0c) >> 2; /* modifier function id */ + L2UCHAR pf = (pmes[2] & 0x10) >> 4; /* poll / final flag */ + const char *type; + + switch(m) { + case 0x00: + type = "UI (Unnumbered Information)"; + break; + + case 0x03: + type = "DM (Disconnected Mode)"; + break; + + case 0x08: + type = "DISC (Disconnect)"; + break; + + case 0x0c: + type = "UA (Unnumbered Acknowledgement)"; + break; + + case 0x0f: + type = "SABME"; + break; + + case 0x11: + type = "FRMR (Frame Reject)"; + break; + + case 0x17: + type = "XID (Exchange Identification)"; + break; + + default: + type = "Unknown"; + } + + + APPEND_MSG(pbuf, poffset, pleft, " Type: U Frame (%s)\n P/F: %d\n", type, pf); + + if(m == 0x00) { + switch(pmes[3]) { + case Q921_LAYER_ENT_ID_TEI: + type = "TEI Mgmt"; + break; + + case Q921_LAYER_ENT_ID_Q931: + type = "Q.931"; + break; + + default: + type = "Unknown"; + } + + if(pmes[3] == Q921_LAYER_ENT_ID_TEI) { + const char *command = ""; + + switch(pmes[6]) { + case Q921_TEI_ID_REQUEST: + command = "Request"; + break; + case Q921_TEI_ID_VERIFY: + command = "Verify"; + break; + case Q921_TEI_ID_CHECKREQ: + command = "Check req"; + break; + case Q921_TEI_ID_CHECKRESP: + command = "Check resp"; + break; + case Q921_TEI_ID_REMOVE: + command = "Remove"; + break; + case Q921_TEI_ID_ASSIGNED: + command = "Assign"; + break; + case Q921_TEI_ID_DENIED: + command = "Denied"; + break; + } + APPEND_MSG(pbuf, poffset, pleft, " ENT ID: %d (%s), COMMAND: %d (%s), RI: %#x, AI: %d\n", + pmes[3], type, pmes[6], command, (int)((pmes[4] << 8) | pmes[5]), pmes[7] >> 1); + } + else { + APPEND_MSG(pbuf, poffset, pleft, " ENT ID: %d (%s), MESSAGE CONTENT:\n", pmes[3], type); + + len = print_hex(pbuf + poffset, (int)pleft, &pmes[3], size - (trunk->Q921HeaderSpace + 3)); + poffset += len; + pleft -= len; + } + } + } + else { + /* + * Unknown + */ + strncat(pbuf + poffset, " -- unknown frame type --\n", pleft); + + len = (sizeof(pbuf) - poffset) - strlen(pbuf + poffset); + if(len > 0) { + poffset += len; + pleft -= len; + } else + goto out; + } + + APPEND_MSG(pbuf, poffset, pleft, "\n Q.921 state: \"%s\" (%d) [flags: %c%c%c%c]\n", Q921State2Name(link->state), link->state, + Q921_CHECK_FLAG(link, Q921_FLAG_ACK_PENDING) ? 'A' : '-', + Q921_CHECK_FLAG(link, Q921_FLAG_REJECT) ? 'R' : '-', + Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY) ? 'P' : '-', + Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY) ? 'B' : '-'); + + strncat(pbuf + poffset, "----------------------------------------------\n\n", pleft); + + len = (sizeof(pbuf) - poffset) - strlen(pbuf + poffset); + if(len > 0) { + poffset += len; + pleft -= len; + } else + goto out; + + + /* concat buffers together */ + len = strlen(pbuf); + if(len <= left) + strncat(buf, pbuf, left); + else + strncat(buf, "-- packet truncated --\n", left); + } + +out: + buf[sizeof(buf) - 1] = '\0'; + + return trunk->Q921LogProc(trunk->PrivateDataLog, level, buf, (int)strlen(buf)); +} + +/***************************************************************************** + + Function: Q921TimeTick + + Description: Called periodically from an external source to allow the + stack to process and maintain it's own timers. + + Return Value: none + +*****************************************************************************/ +static L2ULONG (*Q921GetTimeProc) (void) = NULL; /* callback for func reading time in ms */ +static L2ULONG tLast = {0}; + +static L2ULONG Q921GetTime(void) +{ + L2ULONG tNow = 0; + + if(Q921GetTimeProc) + { + tNow = Q921GetTimeProc(); + if(tNow < tLast) /* wrapped */ + { + /* TODO */ + } + tLast = tNow; + } + return tNow; +} + +/* + * T200 handling (per-TEI in PTMP NT mode, tei=0 otherwise) + */ +static void Q921T200TimerStart(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + if (!link->T200) { + link->T200 = Q921GetTime() + trunk->T200Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T200 (timeout: %d msecs) started for TEI %d\n", trunk->T200Timeout, tei); + } +} + +static void Q921T200TimerStop(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T200 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "T200 stopped for TEI %d\n", tei); +} + +static void Q921T200TimerReset(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T200 = Q921GetTime() + trunk->T200Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T200 (timeout: %d msecs) restarted for TEI %d\n", trunk->T200Timeout, tei); +} + +/* + * T203 handling (per-TEI in PTMP NT mode, tei=0 otherwise) + */ +static void Q921T203TimerStart(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + if (!link->T203) { + link->T203 = Q921GetTime() + trunk->T203Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T203 (timeout: %d msecs) started for TEI %d\n", trunk->T203Timeout, tei); + } +} + +static void Q921T203TimerStop(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T203 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "T203 stopped for TEI %d\n", tei); +} + +static void Q921T203TimerReset(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T203 = Q921GetTime() + trunk->T203Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T203 (timeout: %d msecs) restarted for TEI %d\n", trunk->T203Timeout, tei); +} + +/* + * T202 handling (TEI message timeout, TE mode only) + */ +static void Q921T202TimerStart(L2TRUNK trunk) +{ + if (!trunk->T202) { + trunk->T202 = Q921GetTime() + trunk->T202Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T202 (timeout: %d msecs) started\n", trunk->T202Timeout); + } +} + +static void Q921T202TimerStop(L2TRUNK trunk) +{ + trunk->T202 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "T202 stopped\n"); +} + +static void Q921T202TimerReset(L2TRUNK trunk) +{ + trunk->T202 = Q921GetTime() + trunk->T202Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T202 (timeout: %d msecs) restarted\n", trunk->T202Timeout); +} + +/* + * T201 handling (TEI management (NT side), per-TEI) + */ +static void Q921T201TimerStart(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + if (!link->T201) { + link->T201 = Q921GetTime() + trunk->T201Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T201 (timeout: %d msecs) started for TEI %d\n", trunk->T201Timeout, tei); + } +} + +static void Q921T201TimerStop(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T201 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "T201 stopped for TEI %d\n", tei); +} + +#ifdef __UNUSED_FOR_NOW__ +static void Q921T201TimerReset(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T201 = Q921GetTime() + trunk->T201Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T201 (timeout: %d msecs) restarted for TEI %d\n", trunk->T201Timeout, tei); +} +#endif + +/* + * TM01 handling (Datalink inactivity shutdown timer) + */ +static void Q921TM01TimerStart(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + if (!link->TM01) { + link->TM01 = Q921GetTime() + trunk->TM01Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "TM01 (timeout: %d msecs) started for TEI %d\n", trunk->TM01Timeout, tei); + } +} + +#ifdef __UNUSED_FOR_NOW__ +static void Q921TM01TimerStop(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->TM01 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "TM01 stopped for TEI %d\n", tei); +} +#endif + +static void Q921TM01TimerReset(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->TM01 = Q921GetTime() + trunk->TM01Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "TM01 (timeout: %d msecs) restarted for TEI %d\n", trunk->TM01Timeout, tei); +} + +/* + * Expiry callbacks + */ +static void Q921T200TimerExpire(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + struct Q921_Link *trlink = Q921_TRUNK_CONTEXT(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "T200 expired for TEI %d (trunk TEI %d)\n", tei, trlink->tei); + + /* Stop timer first */ + Q921T200TimerStop(trunk, tei); + + switch(link->state) { + case Q921_STATE_AWAITING_ESTABLISHMENT: + if(link->N200 >= trunk->N200Limit) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* MDL-Error indication (G) */ + Q921Log(trunk, Q921_LOG_ERROR, "Failed to establish Q.921 link in %d retries\n", link->N200); + + /* DL-Release indication */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE, tei, NULL, 0); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + } else { + /* Increment retry counter */ + link->N200++; + + /* Send SABME */ + Q921SendSABME(trunk, + trunk->sapi, + Q921_COMMAND(trunk), + tei, + 1); + + /* Start T200 */ + Q921T200TimerStart(trunk, tei); + } + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + link->N200 = 0; + + if(!Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY)) { + /* get last transmitted I frame */ + + /* V(S) = V(S) - 1 */ + Q921_DEC_COUNTER(link->vs); + + /* retransmit I frame */ + + /* V(S) = V(S) + 1 (done by Q921SendI() ) */ + //Q921_INC_COUNTER(link->vs); + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* Start T200 */ + Q921T200TimerStart(trunk, tei); + } else { + /* transmit enquiry */ + Q921SendEnquiry(trunk, tei); + } + + /* increment counter */ + link->N200++; + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TIMER_RECOVERY, tei); + break; + + case Q921_STATE_TIMER_RECOVERY: + if(link->N200 == trunk->N200Limit) { + /* MDL Error indication (I) */ + + /* Establish data link */ + Q921EstablishDataLink(trunk, tei); + + /* Clear L3 initiated */ + Q921_CLEAR_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } else { + if(link->vs == link->va) { + /* transmit enquiry */ + Q921SendEnquiry(trunk, tei); + + } else if(!Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY)) { + /* get last transmitted frame */ + + /* V(S) = V(S) - 1 */ + Q921_DEC_COUNTER(link->vs); + + /* retrans frame */ + + /* V(S) = V(S) + 1 (done by Q921SendI() ) */ + //Q921_INC_COUNTER(link->vs); + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* Start T200 */ + Q921T200TimerStart(trunk, tei); + } + + /* increment counter */ + link->N200++; + + /* no state change */ + } + break; + + default: + break; + } +} + +static void Q921T203TimerExpire(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + struct Q921_Link *trlink = Q921_TRUNK_CONTEXT(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "T203 expired for TEI %d (trunk TEI %d)\n", tei, trlink->tei); + + /* Stop Timer first */ + Q921T203TimerStop(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* Send Enquiry */ + Q921SendEnquiry(trunk, tei); + + /* RC = 0 */ + link->N200 = 0; + + /* no state change */ + break; + + default: + break; + } +} + +static void Q921T202TimerExpire(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + + Q921T202TimerReset(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "T202 expired for Q.921 trunk with TEI %d\n", link->tei); + + /* todo: implement resend counter */ + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: /* Tei identity verify timeout */ + Q921TeiSendVerifyRequest(trunk); + break; + + default: /* Tei assignment request timeout (TODO: refine) */ + + if(trunk->N202 >= trunk->N202Limit) { + /* Too many retransmits, reset counter, stop timer and handle case (TODO) */ + trunk->N202 = 0; + + Q921T202TimerStop(trunk); + + return; + } + Q921TeiSendAssignRequest(trunk); + + trunk->N202++; + } +} + +static void Q921T201TimerExpire(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + struct Q921_Link *trlink = Q921_TRUNK_CONTEXT(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "T201 expired for TEI %d (trunk TEI: %d)\n", tei, trlink->tei); + + Q921T201TimerStop(trunk, tei); + + /* NOTE: abusing N202 for this */ + if(link->N202 < trunk->N202Limit) { + /* send check request */ + Q921TeiSendCheckRequest(trunk, tei); + + /* increment counter */ + link->N202++; + } else { + /* put context in STOPPED state */ + Q921ChangeState(trunk, Q921_STATE_STOPPED, tei); + + /* NOTE: should we clear the link too? */ + memset(link, 0, sizeof(struct Q921_Link)); + + /* mark TEI free */ + trunk->tei_map[tei] = 0; + } +} + +#ifdef __UNUSED_FOR_NOW__ +static void Q921TM01TimerExpire(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + struct Q921_Link *trlink = Q921_TRUNK_CONTEXT(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "TM01 expired for TEI %d (trunk TEI: %d)\n", tei, trlink->tei); + + /* Restart TM01 */ + Q921TM01TimerReset(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: +/* + * NT-only, needs more support from L3 + */ +#if 0 + /* No activity, shutdown link */ + Q921SendDISC(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 1); + + /* clear I queue */ + MFIFOClear(link->IFrameQueue); + + /* change state */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_RELEASE, tei); +#endif + break; + + default: + break; + } +} +#endif + +/* + * Timer Tick function + */ +void Q921TimerTick(L2TRUNK trunk) +{ + struct Q921_Link *link; + L2ULONG tNow = Q921GetTime(); + int numlinks = Q921_IS_PTMP_NT(trunk) ? Q921_TEI_MAX : 1; + int x; + + for(x = 0; x <= numlinks; x++) { + link = Q921_LINK_CONTEXT(trunk, x); + + /* TODO: check if TEI is assigned and skip check if not (speedup!) */ + if(link->state == Q921_STATE_STOPPED) + continue; + + if (link->T200 && tNow > link->T200) { + Q921T200TimerExpire(trunk, link->tei); + } + if (link->T203 && tNow > link->T203) { + Q921T203TimerExpire(trunk, link->tei); + } + + if(Q921_IS_PTMP_NT(trunk) && link->tei) { + if (link->T201 && tNow > link->T201) { + Q921T201TimerExpire(trunk, link->tei); + } + } + + if(!Q921_IS_PTMP_NT(trunk)) { + if (trunk->T202 && tNow > trunk->T202) { + Q921T202TimerExpire(trunk); + } + } + + /* Send enqueued I frame, if available */ + Q921SendQueuedIFrame(trunk, link->tei); + + /* Send ack if pending */ + Q921AcknowledgePending(trunk, link->tei); + } + +} + +void Q921SetGetTimeCB(L2ULONG (*callback)(void)) +{ + Q921GetTimeProc = callback; +} + +/***************************************************************************** + + Function: Q921QueueHDLCFrame + + Description: Called to receive and queue an incoming HDLC frame. Will + queue this in Q921HDLCInQueue. The called must either call + Q921Rx12 directly afterwards or signal Q921Rx12 to be called + later. Q921Rx12 will read from the same queue and process + the frame. + + This function assumes that the message contains header + space. This is removed for internal Q921 processing, but + must be keept for I frames. + + Parameters: trunk trunk # + b ptr to frame; + size size of frame in bytes + +*****************************************************************************/ +int Q921QueueHDLCFrame(L2TRUNK trunk, L2UCHAR *b, L2INT size) +{ + return MFIFOWriteMes(trunk->HDLCInQueue, b, size); +} + +/** + * Q921EnqueueI + * \brief Put I frame into transmit queue + * + */ +static int Q921EnqueueI(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, Tei); + + /* I frame header */ + mes[trunk->Q921HeaderSpace+0] = ((Sapi << 2) & 0xfc) | ((cr << 1) & 0x02); + mes[trunk->Q921HeaderSpace+1] = (Tei << 1) | 0x01; + mes[trunk->Q921HeaderSpace+2] = 0x00; + mes[trunk->Q921HeaderSpace+3] = (pf & 0x01); + + Q921Log(trunk, Q921_LOG_DEBUG, "Enqueueing I frame for TEI %d [%d]\n", link->tei, Tei); + + /* transmit queue, (TODO: check for full condition!) */ + MFIFOWriteMes(link->IFrameQueue, mes, size); + + /* try to send queued frame */ + Q921SendQueuedIFrame(trunk, link->tei); + + return 1; +} + +/** + * Q921SendQueuedIFrame + * \brief Try to transmit queued I frame (if available) + */ +static int Q921SendQueuedIFrame(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + L2INT size = 0; + L2UCHAR *mes; + + if(MFIFOGetMesCount(link->IFrameQueue) == 0) { + return 0; + } + + /* Link ready? */ + if(link->state != Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + return 0; + } + + /* peer receiver busy? */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY)) { + return 0; + } + + /* V(S) = V(A) + k? */ + if(link->vs == ((link->va + trunk->k) % 128)) { + Q921Log(trunk, Q921_LOG_WARNING, "Maximum number (%d) of outstanding I frames reached for TEI %d\n", trunk->k, tei); + return 0; + } + + mes = MFIFOGetMesPtr(link->IFrameQueue, &size); + if(mes) { + /* Fill in + update counter values */ + mes[trunk->Q921HeaderSpace+2] = link->vs << 1; + mes[trunk->Q921HeaderSpace+3] |= link->vr << 1; + + if(MFIFOGetMesCount(link->IFrameQueue) == 0) { + /* clear I frame queued */ + } + + /* Send I frame */ + Q921Tx21Proc(trunk, mes, size); + + /* V(S) = V(S) + 1 */ + Q921_INC_COUNTER(link->vs); + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* T200 running? */ + if(!link->T200) { + /* Stop T203, Start T200 */ + Q921T200TimerStart(trunk, tei); + Q921T203TimerStop(trunk, tei); + } + + /* put frame into resend queue */ + MFIFOWriteMesOverwrite(link->IFrameResendQueue, mes, size); + + /* dequeue frame */ + MFIFOKillNext(link->IFrameQueue); + + /* Restart TM01 */ + if(Q921_IS_NT(trunk)) { + Q921TM01TimerReset(trunk, tei); + } + + /* no state change */ + return 1; + } + + return 0; +} + +/** + * Q921SendS + * \brief Prepare and send S frame + */ +static int Q921SendS(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR sv, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, Tei); + + if(!Q921_IS_READY(link)) { + /* don't even bother trying */ + Q921Log(trunk, Q921_LOG_DEBUG, "Link not ready, discarding S frame for TEI %d\n", Tei); + return 0; + } + + /* S frame header */ + mes[trunk->Q921HeaderSpace+0] = ((Sapi << 2) & 0xfc) | ((cr << 1) & 0x02); + mes[trunk->Q921HeaderSpace+1] = (Tei << 1) | 0x01; + mes[trunk->Q921HeaderSpace+2] = ((sv << 2) & 0x0c) | 0x01; + mes[trunk->Q921HeaderSpace+3] = (link->vr << 1) | (pf & 0x01); + + return Q921Tx21Proc(trunk, mes, size); +} + + +/** + * Q921SendU + * \brief Prepare and send U frame + */ +static int Q921SendU(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR m, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, Tei); + + /* U frame header */ + mes[trunk->Q921HeaderSpace+0] = ((Sapi << 2) & 0xfc) | ((cr << 1) & 0x02); + mes[trunk->Q921HeaderSpace+1] = (Tei << 1) | 0x01; + mes[trunk->Q921HeaderSpace+2] = ((m << 3) & 0xe0) | ((pf << 4) & 0x10) | ((m << 2) & 0x0c) | 0x03; + + /* link not ready? enqueue non-TEI-mgmt UI (DL-UNIT DATA) frames */ + if(m == 0x00 && Sapi != Q921_SAPI_TEI && link->state < Q921_STATE_TEI_ASSIGNED) { + + /* write frame to queue */ + MFIFOWriteMes(link->UIFrameQueue, mes, size); + + Q921Log(trunk, Q921_LOG_DEBUG, "Link not ready, UI Frame of size %d bytes queued for TEI %d\n", size, Tei); + return 1; + } + + return Q921Tx21Proc(trunk, mes, size); +} + +/** + * TODO: NT mode handling? Need a way to get Link context from Q.931 + */ +int Q921Rx32(L2TRUNK trunk, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR * Mes, L2INT Size) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); /* TODO: need real link tei for NT mode */ + L2INT res = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "Got frame from Q.931, type: %d, tei: %d, size: %d\n", ind, tei, Size); + + switch(ind) { + case Q921_DL_ESTABLISH: + /* + * Hmm... + */ + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + if(!Q921_IS_NT(trunk)) { + /* establish data link */ + Q921EstablishDataLink(trunk, link->tei); + + /* Set layer 3 initiated */ + Q921_SET_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, link->tei); + } + break; + + case Q921_STATE_AWAITING_ESTABLISHMENT: + if(!Q921_IS_NT(trunk)) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* Set layer 3 initiated */ + Q921_SET_FLAG(link, Q921_FLAG_L3_INITIATED); + } + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + if(!Q921_IS_NT(trunk)) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* establish data link */ + Q921EstablishDataLink(trunk, link->tei); + + /* Set layer 3 initiated */ + Q921_SET_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, link->tei); + } + break; + + default: + break; + } + break; + + case Q921_DL_RELEASE: + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + /* send DL-RELEASE confirm */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE, tei, NULL, 0); + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + if(!Q921_IS_NT(trunk)) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* RC = 0 */ + link->N200 = 0; + + /* send DISC command */ + Q921SendDISC(trunk, trunk->sapi, Q921_COMMAND(trunk), link->tei, 1); + + /* Stop T203, restart T200 */ + if(link->state == Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + Q921T203TimerStop(trunk, link->tei); + } + Q921T200TimerReset(trunk, link->tei); + + /* change state */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_RELEASE, link->tei); + } + break; + + default: + break; + } + break; + + case Q921_DL_DATA: /* DL-DATA request */ + res = Q921EnqueueI(trunk, + trunk->sapi, + Q921_COMMAND(trunk), + link->tei, + 0, + Mes, + Size); + + if(link->state < Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + /* Treat as implicit DL-ESTABLISH request */ + + /* establish data link */ + Q921EstablishDataLink(trunk, link->tei); + + /* Set layer 3 initiated */ + Q921_SET_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, link->tei); + } + break; + + case Q921_DL_UNIT_DATA: /* DL-UNIT DATA request */ + res = Q921SendUN(trunk, + trunk->sapi, + Q921_COMMAND(trunk), + Q921_TEI_BCAST, + 0, + Mes, + Size); + /* NOTE: Let the other side initiate link establishment */ + break; + + default: + break; + } + + return res; +} +/***************************************************************************** + + Function: Q921SendRR + + Description: Compose and send Receive Ready. + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P/F fiels octet 5 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ + +static int Q921SendRR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x00, mes, trunk->Q921HeaderSpace+4); +} + +/***************************************************************************** + + Function: Q921SendRNR + + Description: Compose and send Receive Nor Ready + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P/F fiels octet 5 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendRNR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x01, mes, trunk->Q921HeaderSpace+4); +} + +/***************************************************************************** + + Function: Q921SendREJ + + Description: Compose and Send Reject. + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P/F fiels octet 5 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendREJ(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x03, mes, trunk->Q921HeaderSpace+4); +} + +/***************************************************************************** + + Function: Q921SendSABME + + Description: Compose and send SABME + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P fiels octet 4 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendSABME(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x0f, mes, trunk->Q921HeaderSpace+3); +} + + +/** + * Q921Start + * \brief Start trunk + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success; <= 0 on error + */ +int Q921Start(L2TRUNK trunk) +{ + int x, numlinks = Q921_IS_PTMP_NT(trunk) ? Q921_TEI_MAX : 1; + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + + if(trunk->initialized != INITIALIZED_MAGIC) + return 0; + + memset(trunk->context, 0, numlinks * sizeof(struct Q921_Link)); + + /* Common init part */ + for(x = 0; x <= numlinks; x++) { + link = Q921_LINK_CONTEXT(trunk, x); + + link->state = Q921_STATE_TEI_UNASSIGNED; + link->tei = 0; + + /* Initialize per-TEI I + UI queues */ + MFIFOCreate(link->UIFrameQueue, Q921MAXHDLCSPACE, 10); + MFIFOCreate(link->IFrameQueue, Q921MAXHDLCSPACE, 10); + MFIFOCreate(link->IFrameResendQueue, Q921MAXHDLCSPACE, 10); + } + + if(Q921_IS_PTMP_TE(trunk)) { + link->state = Q921_STATE_TEI_UNASSIGNED; + link->tei = 0; + } + else if(Q921_IS_PTMP_NT(trunk)) { + link = Q921_TRUNK_CONTEXT(trunk); + + link->state = Q921_STATE_TEI_ASSIGNED; + link->tei = trunk->tei; + + /* clear tei map */ + memset(trunk->tei_map, 0, Q921_TEI_MAX + 1); + } + else { + link->state = Q921_STATE_TEI_ASSIGNED; + link->tei = trunk->tei; + } + + Q921Log(trunk, Q921_LOG_DEBUG, "Starting trunk %p (sapi: %d, tei: %d, mode: %s %s)\n", + trunk, + trunk->sapi, + link->tei, + Q921_IS_PTMP(trunk) ? "PTMP" : "PTP", + Q921_IS_TE(trunk) ? "TE" : "NT"); + + if(Q921_IS_PTP(trunk)) { + Q921Log(trunk, Q921_LOG_DEBUG, "Sending SABME\n"); + + return Q921SendSABME(trunk, + trunk->sapi, + Q921_COMMAND(trunk), + link->tei, + 1); + + } else if(Q921_IS_PTMP_NT(trunk)) { + + Q921Log(trunk, Q921_LOG_DEBUG, "Revoking all TEIs\n"); + + return Q921TeiSendRemoveRequest(trunk, Q921_TEI_BCAST); /* Revoke all TEIs in use */ + } else { + + Q921Log(trunk, Q921_LOG_DEBUG, "Requesting TEI\n"); + + return Q921TeiSendAssignRequest(trunk); + } +} + + +/** + * Q921Stop + * \brief Stop trunk + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +int Q921Stop(L2TRUNK trunk) +{ + struct Q921_Link *link; + int x, numlinks; + + if(!trunk) + return -1; + + link = Q921_TRUNK_CONTEXT(trunk); + numlinks = Q921_IS_PTMP_NT(trunk) ? Q921_TEI_MAX : 1; + + if(Q921_IS_STOPPED(link)) + return 0; + + /* Release TEI */ + if(Q921_IS_PTMP_TE(trunk)) { + /* send verify request */ + Q921TeiSendVerifyRequest(trunk); + + /* drop TEI */ + link->tei = 0; + } + + /* Stop timers, stop link, flush queues */ + for(x = 0; x <= numlinks; x++) { + Q921T200TimerStop(trunk, x); + Q921T203TimerStop(trunk, x); + Q921T201TimerStop(trunk, x); + + /* Change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_STOPPED, x); + + /* Flush per-tei I/UI queues */ + MFIFOClear(link->UIFrameQueue); + MFIFOClear(link->IFrameQueue); + MFIFOClear(link->IFrameResendQueue); + } + Q921T202TimerStop(trunk); + + /* Flush HDLC queue */ + MFIFOClear(trunk->HDLCInQueue); + + return 0; +} + + +/***************************************************************************** + + Function: Q921SendDM + + Description: Compose and Send DM (Disconnected Mode) + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf F fiels octet 4 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendDM(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x03, mes, trunk->Q921HeaderSpace+3); +} + +/***************************************************************************** + + Function: Q921SendDISC + + Description: Compose and Send Disconnect + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P fiels octet 4 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendDISC(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x08, mes, trunk->Q921HeaderSpace+3); +} + +/***************************************************************************** + + Function: Q921SendUA + + Description: Compose and Send UA + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf F fiels octet 4 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendUA(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x0c, mes, trunk->Q921HeaderSpace+3); +} + +static int Q921SendUN(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf, L2UCHAR *mes, L2INT size) +{ + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x00, mes, size+trunk->Q921HeaderSpace+3); +} + + +/** + * Q921ProcSABME + * \brief Handle incoming SABME + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcSABME(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + /* send UA */ + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), /* or command? */ + tei, pf); + + /* clear counters */ + link->vr=0; + link->vs=0; + link->va=0; + + /* TODO: send DL-Establish indication to Q.931 */ + Q921Tx23Proc(trunk, Q921_DL_ESTABLISH, tei, NULL, 0); + + /* start T203 */ + Q921T203TimerStart(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + break; + + case Q921_STATE_AWAITING_ESTABLISHMENT: + /* send UA */ + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* no state change */ + break; + + case Q921_STATE_AWAITING_RELEASE: + /* send DM */ + Q921SendDM(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* no state change */ + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + /* send UA */ + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* clear exception conditions */ + Q921ResetExceptionConditions(trunk, tei); + + /* send MDL-Error indication */ + + /* V(S) == V(A) ? */ + if(link->vs != link->va) { + /* clear I queue */ + MFIFOClear(link->IFrameQueue); + + /* DL-Establish indication */ + Q921Tx23Proc(trunk, Q921_DL_ESTABLISH, tei, NULL, 0); + } + + /* clear counters */ + link->vr=0; + link->vs=0; + link->va=0; + + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + /* state change only if in TIMER_RECOVERY state */ + if(link->state == Q921_STATE_TIMER_RECOVERY) + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + break; + + default: + break; + } + + return 1; +} + + +/** + * Q921ProcDM + * \brief Handle incoming DM + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcDM(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + if(!pf) { + /* to next state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_AWAITING_ESTABLISHMENT: + case Q921_STATE_AWAITING_RELEASE: + if(pf) { + if(link->state == Q921_STATE_AWAITING_ESTABLISHMENT) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + } + + /* Send DL-Release indication to Q.931 */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE, tei, NULL, 0); + + /* Stop T200 */ + Q921T200TimerStop(trunk, tei); + + /* Change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + } + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + if(pf) { + /* MDL-Error indication (B) */ + + /* no state change */ + } else { + /* MDL-Error indication (E) */ + + /* establish data link */ + Q921EstablishDataLink(trunk, tei); + + /* clear L3 initiated */ + Q921_CLEAR_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action?) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + if(pf) { + /* MDL Error indication (B) */ + } else { + /* MDL Error indication (E) */ + } + + /* establish data link */ + Q921EstablishDataLink(trunk, tei); + + /* clear layer 3 initiated */ + Q921_CLEAR_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + break; + + default: + break; + } + + return 1; +} + +/** + * Q921ProcUA + * \brief Handle incoming UA + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcUA(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + case Q921_STATE_TIMER_RECOVERY: + /* MDL Error indication (C, D) */ + Q921Log(trunk, Q921_LOG_ERROR, "Received UA frame in invalid state\n"); + break; + + case Q921_STATE_AWAITING_ESTABLISHMENT: + if(pf) { + /* TODO: other fancy stuff (see docs) */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_L3_INITIATED)) { /* layer3 initiated */ + link->vr = 0; + + /* DL-Establish confirm */ + Q921Tx23Proc(trunk, Q921_DL_ESTABLISH_CONFIRM, tei, NULL, 0); + + } else if(link->vs != link->va) { + + /* discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* DL-Establish indication */ + Q921Tx23Proc(trunk, Q921_DL_ESTABLISH, tei, NULL, 0); + } + + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + link->vs = 0; + link->va = 0; + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + } else { + /* MDL Error indication (C, D) */ + Q921Log(trunk, Q921_LOG_ERROR, "Received UA frame is not a response to a request\n"); + + /* no state change */ + } + break; + + case Q921_STATE_AWAITING_RELEASE: + if(pf) { + /* DL Release confirm */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE_CONFIRM, tei, NULL, 0); + + /* Stop T200 */ + Q921T200TimerStop(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + } else { + /* MDL Error indication (D) */ + Q921Log(trunk, Q921_LOG_ERROR, "Received UA frame is not a response to a request\n"); + + /* no state change */ + } + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* MDL Error indication (C, D) */ + Q921Log(trunk, Q921_LOG_ERROR, "Received UA frame in invalid state\n"); + + /* no state change */ + break; + + default: + break; + } + + return 1; +} + + +/** + * Q921ProcDISC + * \brief Handle incoming DISC + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcDISC(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + case Q921_STATE_AWAITING_ESTABLISHMENT: + /* Send DM */ + Q921SendDM(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* no state change */ + break; + + case Q921_STATE_AWAITING_RELEASE: + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* no state change */ + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* send UA */ + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* DL Release indication */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE, tei, NULL, 0); + + /* Stop T200 */ + Q921T200TimerStop(trunk, tei); + + if(link->state == Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + /* Stop T203 */ + Q921T203TimerStop(trunk, tei); + } + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + break; + + default: + Q921Log(trunk, Q921_LOG_ERROR, "Invalid DISC received in state \"%s\" (%d)", Q921State2Name(link->state), link->state); + break; + } + + return 1; +} + + +/** + * Q921ProcRR + * \brief Handle incoming RR + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcRR(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR cr = (mes[0] & 0x02) >> 1; + L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = (mes[3] >> 1); +// L2UCHAR sapi = (mes[0] & 0xfc) >> 2; + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* clear receiver peer busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + if (Q921_IS_COMMAND(trunk, cr)) { /* if this is a command */ + if(pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + } else { + if(pf) { + /* MDL Error indication */ + } + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + if(nr == link->vs) { + /* V(A) = N(R) */ + link->va = nr; + + /* Stop T200, restart T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerReset(trunk, tei); + + } else if(nr == link->va) { + + /* do nothing */ + + } else { + /* V(A) = N(R) */ + link->va = nr; + + /* Restart T200 */ + Q921T200TimerReset(trunk, tei); + } + /* no state change */ + + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + /* clear receiver peer busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command + P? */ + if(Q921_IS_COMMAND(trunk, cr) && pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + /* V(A) = N(R) */ + link->va = nr; + + if(!Q921_IS_COMMAND(trunk, cr) && pf) { + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + /* Invoke retransmission */ + Q921InvokeRetransmission(trunk, tei, nr); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + } + /* no state change otherwise */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + default: + break; + } + return 1; +} + + +/** + * Q921ProcREJ + * \brief Handle incoming REJ + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcREJ(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR cr = (mes[0] & 0x02) >> 1; + L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = (mes[3] >> 1); +// L2UCHAR sapi = (mes[0] & 0xfc) >> 2; + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* clear receiver peer busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command? */ + if(Q921_IS_COMMAND(trunk, cr)) { + if(pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + } else { + if(pf) { + /* MDL Error indication (A) */ + } + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + /* V(A) = N(R) */ + link->va = nr; + + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + /* Invoke retransmission of frame >N(R) (?) */ + Q921InvokeRetransmission(trunk, tei, nr); + + /* no state change */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + /* clear receiver peer busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command + P ? */ + if(Q921_IS_COMMAND(trunk, cr) && pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + /* V(A) = N(R) */ + link->va = nr; + + if(!Q921_IS_COMMAND(trunk, cr) && pf) { + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + /* Invoke retransmission */ + Q921InvokeRetransmission(trunk, tei, nr); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + } + /* no state change otherwise */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + default: + break; + } + + return 1; +} + + +/** + * Q921ProcRNR + * \brief Handle incoming RNR + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcRNR(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR cr = (mes[0] & 0x02) >> 1; + L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = (mes[3] >> 1); +// L2UCHAR sapi = (mes[0] & 0xfc) >> 2; + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* set peer receiver busy */ + Q921_SET_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command? */ + if(Q921_IS_COMMAND(trunk, cr)) { + if(pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + } else { + if(pf) { + /* MDL Error indication (A) */ + } + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + /* V(A) = N(R) */ + link->va = nr; + + /* Stop T203, restart T200 */ + Q921T200TimerReset(trunk, tei); + Q921T203TimerStop(trunk, tei); + + /* no state change */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + /* set peer receiver busy */ + Q921_SET_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command + P? */ + if(Q921_IS_COMMAND(trunk, cr) && pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + /* V(A) = N(R) */ + link->va = nr; + + if(!Q921_IS_COMMAND(trunk, cr) && pf) { + /* Restart T200 */ + Q921T200TimerReset(trunk, tei); + + /* Invoke retransmission */ + Q921InvokeRetransmission(trunk, tei, nr); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + } + /* no state change otherwise */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + default: + break; + } + + return 1; +} + +#if 0 +static int Q921SetReceiverBusy(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + if(!Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + /* set own receiver busy */ + Q921_SET_FLAG(link, Q921_FLAG_RECV_BUSY); + + /* send RR response */ + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), link->tei, 0); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + if(!Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + /* set own receiver busy */ + Q921_SET_FLAG(link, Q921_FLAG_RECV_BUSY); + + /* send RNR response */ + Q921SendRNR(trunk, trunk->sapi, Q921_RESPONSE(trunk), link->tei, 0); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + break; + + default: + break; + } + + return 0; +} + +static int Q921ClearReceiverBusy(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + if(Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + /* clear own receiver busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_RECV_BUSY); + + /* send RNR response */ + Q921SendRNR(trunk, trunk->sapi, Q921_RESPONSE(trunk), link->tei, 0); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + break; + + default: + break; + } + + return 0; +} +#endif + +static int Q921ProcIFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + /* common fields: get sapi, tei and cr */ +// L2UCHAR sapi = (mes[0] & 0xfc) >> 2; +// L2UCHAR cr = (mes[0] & 0x02) >> 1; + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = mes[3] >> 1; /* receive sequence number */ + L2UCHAR ns = mes[2] >> 1; /* send sequence number */ + L2UCHAR discard = 0; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* Ignore I frames in earlier states */ + if(link->state < Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + Q921Log(trunk, Q921_LOG_NOTICE, "I frame in invalid state ignored\n"); + return 0; + } + + /* Receiver busy? */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + /* discard information */ + discard = 1; + + if(pf) { + /* send RNR Response */ + Q921SendRNR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + + /* Clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + } + else { + if(ns != link->vr) { + /* discard information */ + discard = 1; + + if(Q921_CHECK_FLAG(link, Q921_FLAG_REJECT) && pf) { + + /* Send RR response */ + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + else if(!Q921_CHECK_FLAG(link, Q921_FLAG_REJECT)){ + + /* set reject exception */ + Q921_SET_FLAG(link, Q921_FLAG_REJECT); + + /* Send REJ response */ + Q921SendREJ(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, pf); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + } + else { + /* V(R) = V(R) + 1 */ + Q921_INC_COUNTER(link->vr); + + /* clear reject exception */ + Q921_CLEAR_FLAG(link, Q921_FLAG_REJECT); + + /* DL-Data indication */ + Q921Tx23Proc(trunk, Q921_DL_DATA, tei, mes, size); + + if(pf) { + /* Send RR response */ + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + else if(!Q921_CHECK_FLAG(link, Q921_FLAG_ACK_PENDING)) { + /* ack pending */ + + /* Send RR response */ + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 0); + + /* set ack pending*/ + Q921_SET_FLAG(link, Q921_FLAG_ACK_PENDING); + } + } + } + + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + if(link->va <= nr && nr <= link->vs) { + if(Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY)) { + link->va = nr; + } + else if(nr == link->vs) { + /* V(A) = N(R) */ + link->va = nr; + + /* stop t200, restart t203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerReset(trunk, tei); + } + else if(nr != link->va) { + /* V(A) = N(R) */ + link->va = nr; + + /* restart T200 */ + Q921T200TimerReset(trunk, tei); + } + + /* Restart TM01 */ + if(Q921_IS_NT(trunk)) { + Q921TM01TimerReset(trunk, tei); + } + } + else { + /* N(R) error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + if(link->va <= nr && nr <= link->vs) { + /* V(A) = N(R) */ + link->va = nr; + + /* Restart TM01 */ + if(Q921_IS_NT(trunk)) { + Q921TM01TimerReset(trunk, tei); + } + } + else { + /* N(R) error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + default: + break; + } + + return 0; +} + + +static int Q921ProcSFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR sv = (mes[2] & 0x0c) >> 2; /* supervisory format id */ + //L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + //L2UCHAR nr = mes[3] >> 1; /* receive sequence number */ + L2INT res = -1; + + switch(sv) { + case 0x00: /* RR : Receive Ready */ + res = Q921ProcRR(trunk, mes, size); + break; + + case 0x02: /* RNR : Receive Not Ready */ + res = Q921ProcRNR(trunk, mes, size); + break; + + case 0x04: /* REJ : Reject */ + res = Q921ProcREJ(trunk, mes, size); + break; + + default: /* Invalid / Unknown */ + Q921Log(trunk, Q921_LOG_ERROR, "Invalid S frame type %d\n", sv); + break; + } + + return res; +} + + + +static int Q921ProcUFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR m = (mes[2] & 0xe0) >> 3 | (mes[2] & 0x0c) >> 2; /* modifier function id */ +// L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2INT res = -1; + + switch(m) { + case 0x00: /* UN : Unnumbered Information */ + if(mes[3] == Q921_LAYER_ENT_ID_TEI) + { + if(!Q921_IS_PTMP(trunk)) { + /* wtf? nice try */ + return res; + } + + switch(mes[6]) { + case Q921_TEI_ID_REQUEST: /* (TE ->) NT */ + res = Q921TeiProcAssignRequest(trunk, mes, size); + break; + + case Q921_TEI_ID_ASSIGNED: /* (NT ->) TE */ + case Q921_TEI_ID_DENIED: + res = Q921TeiProcAssignResponse(trunk, mes, size); + break; + + case Q921_TEI_ID_CHECKREQ: /* (NT ->) TE */ + res = Q921TeiProcCheckRequest(trunk, mes, size); + break; + + case Q921_TEI_ID_CHECKRESP: /* (TE ->) NT */ + res = Q921TeiProcCheckResponse(trunk, mes, size); + break; + + case Q921_TEI_ID_REMOVE: /* (NT ->) TE */ + res = Q921TeiProcRemoveRequest(trunk, mes, size); + break; + + case Q921_TEI_ID_VERIFY: /* (TE ->) NT */ + res = Q921TeiProcVerifyRequest(trunk, mes, size); + break; + + default: /* Invalid / Unknown */ + Q921Log(trunk, Q921_LOG_ERROR, "Invalid UN message from TEI management/endpoint\n"); + break; + } + } + else if(mes[3] == Q921_LAYER_ENT_ID_Q931) { + + Q921Log(trunk, Q921_LOG_DEBUG, "UI Frame for Layer 3 received\n"); + + res = Q921Tx23Proc(trunk, Q921_DL_UNIT_DATA, 0, mes, size); + } + break; + + case 0x03: /* DM : Disconnect Mode */ + res = Q921ProcDM(trunk, mes, size); + break; + + case 0x08: /* DISC : Disconnect */ + res = Q921ProcDISC(trunk, mes, size); + break; + + case 0x0c: /* UA : Unnumbered Acknowledgement */ + res = Q921ProcUA(trunk, mes, size); + break; + + case 0x0f: /* SABME : Set Asynchronous Balanced Mode Extend */ + res = Q921ProcSABME(trunk, mes, size); + break; + + case 0x11: /* FRMR : Frame Reject */ + case 0x17: /* XID : Exchange Identification */ + res = 0; + break; + + default: /* Unknown / Invalid */ + Q921Log(trunk, Q921_LOG_ERROR, "Invalid U frame type: %d\n", m); + break; + } + + return res; +} + + +/***************************************************************************** + + Function: Q921Rx12 + + Description: Called to process a message frame from layer 1. Will + identify the message and call the proper 'processor' for + layer 2 messages and forward I frames to the layer 3 entity. + + Q921Rx12 will check the input fifo for a message, and if a + message exist process one message before it exits. The caller + must either call Q921Rx12 polling or keep track on # + messages in the queue. + + Parameters: trunk trunk #. + + Return Value: # messages processed (always 1 or 0). + +*****************************************************************************/ +int Q921Rx12(L2TRUNK trunk) +{ + L2INT size; /* receive size & Q921 frame size*/ + L2UCHAR *smes = MFIFOGetMesPtr(trunk->HDLCInQueue, &size); + + if(smes) + { + struct Q921_Link *link; + L2UCHAR sapi, tei; + L2UCHAR *mes; + L2INT rs; + + rs = size - trunk->Q921HeaderSpace; + mes = &smes[trunk->Q921HeaderSpace]; + + Q921LogMesg(trunk, Q921_LOG_DEBUG, 1, mes, rs, "New packet received (%d bytes)", rs); + + /* common fields: get sapi, tei and cr */ + sapi = (mes[0] & 0xfc) >> 2; + tei = (mes[1] & 0xfe) >> 1; + link = Q921_LINK_CONTEXT(trunk, tei); + + if(Q921_IS_PTMP_TE(trunk) && ( + (link->state >= Q921_STATE_TEI_ASSIGNED && tei != link->tei && tei != Q921_TEI_BCAST) || /* Assigned TEI: Only BCAST and directed */ + (link->state == Q921_STATE_TEI_UNASSIGNED && tei != Q921_TEI_BCAST))) /* No assigned TEI: Only BCAST */ + { + /* Ignore Messages with foreign TEIs */ + goto out; + } + + if((mes[2] & 0x01) == 0x00) { /* I frame */ + Q921ProcIFrame(trunk, mes, rs); + } + else if((mes[2] & 0x03) == 0x01) { /* S frame */ + Q921ProcSFrame(trunk, mes, rs); + } + else if((mes[2] & 0x03) == 0x03) { /* U frame */ + Q921ProcUFrame(trunk, mes, rs); + } + else { + Q921Log(trunk, Q921_LOG_ERROR, "Invalid frame type: %d\n", (int)(mes[2] & 0x03)); + /* TODO: send FRMR or REJ */ + } + +out: + MFIFOKillNext(trunk->HDLCInQueue); + + return 1; + } + + return 0; +} + +/* + * Misc + */ +/** + * Q921SetLogCB + * \brief Set logging callback + * \param[in] trunk pointer to Q921 data struct + * \param[in] func pointer to logging callback function + * \param[in] priv pointer to private data + * + * \author Stefan Knoblich + */ +void Q921SetLogCB(L2TRUNK trunk, Q921LogCB_t func, void *priv) +{ + if(!trunk) + return; + + trunk->Q921LogProc = func; + trunk->PrivateDataLog = priv; +} + +/** + * Q921SetLogLevel + * \brief Set loglevel of Q.921 logging functions + * \param[in] trunk pointer to Q921 data struct + * \param[in] level new loglevel + * + * \author Stefan Knoblich + */ +void Q921SetLogLevel(L2TRUNK trunk, Q921LogLevel_t level) +{ + if(!trunk) + return; + + if (level < Q921_LOG_NONE) { + level = Q921_LOG_NONE; + } else if (level > Q921_LOG_DEBUG) { + level = Q921_LOG_DEBUG; + } + + trunk->loglevel = level; +} + + +/** + * Q921ChangeState + * \brief Change state, invoke neccessary actions + * \param[in] trunk pointer to Q921 data struct + * \param[in] state state to change to + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921ChangeState(L2TRUNK trunk, Q921State_t state, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + Q921State_t oldstate = link->state; + int res = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "Changing state from \"%s\" (%d) to \"%s\" (%d) for TEI %d\n", + Q921State2Name(oldstate), oldstate, + Q921State2Name(state), state, + tei); + + /* + * generic actions (depending on the target state only) + */ + switch(state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* Start TM01 */ + if(Q921_IS_NT(trunk)) { + Q921TM01TimerStart(trunk, tei); + } + break; + + default: + break; + } + + /* + * actions that depend on type of the old -> new state transition + */ + switch(oldstate) { + case Q921_STATE_STOPPED: + + switch(state) { + case Q921_STATE_TEI_UNASSIGNED: + if(Q921_IS_PTMP_TE(trunk)) { + res = Q921TeiSendAssignRequest(trunk); + } + break; + + case Q921_STATE_TEI_ASSIGNED: + if(Q921_IS_PTMP_NT(trunk)) { + res = Q921TeiSendRemoveRequest(trunk, Q921_TEI_BCAST); + } + break; + + default: + break; + } + break; + + default: + break; + } + + link->state = state; + + Q921Log(trunk, Q921_LOG_DEBUG, "Q921ChangeState() returns %d, new state is \"%s\" (%d) for TEI %d\n", res, Q921State2Name(state), state, tei); + + return res; +} + +/* + * TEI Management functions + * \note All TEI-mgmt UN frames are sent with cr = command! + */ +static int Q921TeiSend(L2TRUNK trunk, L2UCHAR type, L2USHORT ri, L2UCHAR ai) +{ + L2UCHAR mes[10]; + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + + mes[offset++] = Q921_LAYER_ENT_ID_TEI; /* layer management entity identifier */ + mes[offset++] = (ri & 0xff00) >> 8; /* reference number upper part */ + mes[offset++] = ri & 0xff; /* reference number lower part */ + mes[offset++] = type; /* message type: Identity Request */ + mes[offset++] = ai << 1 | 0x01; /* action indicator: TEI */ + + return Q921SendU(trunk, Q921_SAPI_TEI, Q921_COMMAND(trunk), Q921_TEI_BCAST, 0, 0x00, mes, offset); +} + + +/** + * Q921TeiSendAssignRequest + * \brief Ask for new TEI (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success, <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendAssignRequest(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2INT res; + + if (!Q921_IS_PTMP_TE(trunk)) /* only ptmp te mode*/ + return 0; + +#ifndef WIN32 + link->ri = (L2USHORT)(random() % 0xffff); +#else + link->ri = (L2USHORT)(rand() % 0xffff); //todo +#endif + + /* send TEI assign request */ + res = Q921TeiSend(trunk, Q921_TEI_ID_REQUEST, link->ri, Q921_TEI_BCAST); + + /* start T202 */ + Q921T202TimerStart(trunk); + + return res; +} + + +/** + * Q921TeiProcessAssignResponse + * \brief Process assign response (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcAssignResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2USHORT ri = 0; + + if (!Q921_IS_PTMP_TE(trunk)) /* PTMP TE only */ + return 0; + + ri = (mes[offset + 1] << 8) | mes[offset + 2]; + + if(ri != link->ri) { + /* hmmm ..., not our response i guess */ + return 0; + } + + switch(mes[offset + 3]) { + case Q921_TEI_ID_ASSIGNED: + /* Yay, use the new TEI and change state to assigned */ + link->tei = mes[offset + 4] >> 1; + + Q921Log(trunk, Q921_LOG_DEBUG, "Assigned TEI %d, setting state to TEI_ASSIGNED\n", link->tei); + + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, link->tei); + break; + + case Q921_TEI_ID_DENIED: + /* oops, what to do now? */ + if ((mes[offset + 4] >> 1) == Q921_TEI_BCAST) { + /* No more free TEIs? this is bad */ + + //Q921TeiSendVerifyRequest(trunk, Q921_TEI_BCAST); /* TODO: does this work ?? */ + } else { + /* other reason, this is fatal, shutdown link */ + } + + Q921Log(trunk, Q921_LOG_DEBUG, "TEI assignment has been denied, reason: %s\n", + ((mes[offset +4] >> 1) == Q921_TEI_BCAST) ? "No free TEIs available" : "Unknown"); + + Q921ChangeState(trunk, Q921_STATE_TEI_UNASSIGNED, link->tei); + break; + + default: + return 0; + } + + /* stop T202 */ + Q921T202TimerStop(trunk); + + return 1; +} + + +/** + * Q921TeiSendVerifyRequest + * \brief Verify TEI (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendVerifyRequest(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2INT res; + + if (!Q921_IS_PTMP_TE(trunk)) /* only ptmp te mode*/ + return 0; + + /* Request running? */ + if (trunk->T202) + return 0; + + /* Send TEI verify request */ + res = Q921TeiSend(trunk, Q921_TEI_ID_VERIFY, link->ri, link->tei); + + /* start T202 */ + Q921T202TimerStart(trunk); + + return res; +} + + +/** + * Q921TeiProcCheckRequest + * \brief Process Check Request (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcCheckRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2UCHAR tei = (mes[offset + 4] >> 1); /* action indicator => tei */ + L2INT res = 0; + + if (!Q921_IS_PTMP_TE(trunk)) /* ptmp te mode only */ + return 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "Received TEI Check request for TEI %d\n", tei); + + if (tei == Q921_TEI_BCAST || tei == link->tei) { + /* + * Broadcast TEI check or for our assigned TEI + */ + + /* send TEI check reponse */ + res = Q921TeiSend(trunk, Q921_TEI_ID_CHECKRESP, link->ri, link->tei); + + Q921T202TimerStop(trunk); + } + + return res; +} + + +/** + * Q921TeiProcRemoveRequest + * \brief Process remove Request (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcRemoveRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2UCHAR tei = (mes[offset + 4] >> 1); /* action indicator => tei */ + L2INT res = 0; + + if (!Q921_IS_PTMP_TE(trunk)) /* ptmp te mode only */ + return 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "Received TEI Remove request for TEI %d\n", tei); + + if (tei == Q921_TEI_BCAST || tei == link->tei) { + /* + * Broadcast TEI remove or for our assigned TEI + */ + + /* reset tei */ + link->tei = 0; + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_UNASSIGNED, link->tei); + + /* TODO: hmm, request new one ? */ + res = Q921TeiSendAssignRequest(trunk); + } + return res; +} + + +/** + * Q921TeiProcAssignRequest + * \brief Process assign request from peer (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcAssignRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2USHORT ri = 0; + L2UCHAR tei = 0; + + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + ri = (mes[offset + 1] << 8) | mes[offset + 2]; + tei = mes[offset + 4] >> 1; + + if(tei == Q921_TEI_BCAST) { + int x; + + /* dynamically allocate TEI */ + for(x = Q921_TEI_DYN_MIN, tei = 0; x <= Q921_TEI_MAX; x++) { + if(!trunk->tei_map[x]) { + tei = x; + break; + } + } + } + else if(!(tei > 0 && tei < Q921_TEI_DYN_MIN)) { + /* reject TEIs that are not in the static area */ + Q921TeiSendDenyResponse(trunk, 0, ri); + + return 0; + } + + if(!tei) { + /* no free TEI found */ + Q921TeiSendDenyResponse(trunk, Q921_TEI_BCAST, ri); + } + else { + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* mark used */ + trunk->tei_map[tei] = 1; + + /* assign tei */ + link->tei = tei; + + /* put context in TEI ASSIGNED state */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + + /* send assign response */ + Q921TeiSendAssignedResponse(trunk, tei, ri); + + /* Start T201 */ + Q921T201TimerStart(trunk, tei); + } + return 0; +} + +/** + * Q921TeiSendCheckRequest + * \brief Send check request (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] tei TEI to check + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendCheckRequest(L2TRUNK trunk, L2UCHAR tei) +{ + L2INT res = 0; + + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + /* send TEI check request */ + res = Q921TeiSend(trunk, Q921_TEI_ID_CHECKREQ, 0, tei); + + /* (Re-)Start T201 timer */ + Q921T201TimerStart(trunk, tei); + + return res; +} + +/** + * Q921TeiProcCheckResponse + * \brief Process Check Response (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcCheckResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link; + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2USHORT ri = 0; + L2UCHAR tei = 0; + + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT mode only */ + return 0; + + ri = (mes[offset + 1] << 8) | mes[offset + 2]; + tei = mes[offset + 4] >> 1; + + /* restart T201 */ + Q921T201TimerStop(trunk, tei); + + /* reset counter */ + link = Q921_LINK_CONTEXT(trunk, tei); + link->N202 = 0; + + if(!(tei > 0 && tei < Q921_TEI_MAX) || !trunk->tei_map[tei]) { + /* TODO: Should we send a DISC first? */ + + /* TEI not assigned? Invalid TEI? */ + Q921TeiSendRemoveRequest(trunk, tei); + + /* change state */ + Q921ChangeState(trunk, Q921_STATE_STOPPED, tei); + + /* clear */ + memset(link, 0, sizeof(struct Q921_Link)); + } else { + /* Start T201 */ + Q921T201TimerStart(trunk, tei); + } + + return 0; +} + + +/** + * Q921TeiProcVerifyRequest + * \brief Process Verify Request (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcVerifyRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR resp[25]; + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2UCHAR tei = 0; + + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT mode only */ + return 0; + + tei = mes[offset + 4] >> 1; + + /* todo: handle response... verify assigned TEI */ + resp[offset + 0] = 0; + + return 0; +} + +/** + * Q921TeiSendDenyResponse + * \brief Send Deny Response (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendDenyResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri) +{ + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + return Q921TeiSend(trunk, Q921_TEI_ID_DENIED, ri, tei); +} + + +/** + * Q921TeiSendAssignedResponse + * \brief Send Assigned Response (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] tei TEI to assign + * \param[in] ri RI of request + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendAssignedResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri) +{ + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + return Q921TeiSend(trunk, Q921_TEI_ID_ASSIGNED, ri, tei); +} + +/** + * Q921TeiSendRemoveRequest + * \brief Send Remove Request (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] tei TEI to remove + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendRemoveRequest(L2TRUNK trunk, L2UCHAR tei) +{ + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + return Q921TeiSend(trunk, Q921_TEI_ID_REMOVE, 0, tei); +} diff --git a/libs/freetdm/src/isdn/Q931.c b/libs/freetdm/src/isdn/Q931.c new file mode 100644 index 0000000000..b245c144d8 --- /dev/null +++ b/libs/freetdm/src/isdn/Q931.c @@ -0,0 +1,888 @@ +/***************************************************************************** + + FileName: Q931.c + + Contents: Implementation of Q.931 stack main interface functions. + See q931.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q921.h" +#include "Q931.h" +#include "national.h" +#include "DMS.h" +#include "5ESS.h" + + +/***************************************************************************** + + Dialect function pointers tables. + + The following function pointer arrays define pack/unpack functions and + processing furnctions for the different Q.931 based dialects. + + The arrays are initialized with pointers to dummy functions and later + overrided with pointers to actual functions as new dialects are added. + + The initial Q.931 will as an example define 2 dialects as it treats User + and Network mode as separate ISDN dialects. + + The API messages Q931AddProc, Q931AddMes, Q931AddIE are used to initialize + these table entries during system inititialization of a stack. + +*****************************************************************************/ +q931proc_func_t *Q931Proc[Q931MAXDLCT][Q931MAXMES]; + +q931umes_func_t *Q931Umes[Q931MAXDLCT][Q931MAXMES]; +q931pmes_func_t *Q931Pmes[Q931MAXDLCT][Q931MAXMES]; + +q931uie_func_t *Q931Uie[Q931MAXDLCT][Q931MAXIE]; +q931pie_func_t *Q931Pie[Q931MAXDLCT][Q931MAXIE]; + +q931timeout_func_t *Q931Timeout[Q931MAXDLCT][Q931MAXTIMER]; +q931timer_t Q931Timer[Q931MAXDLCT][Q931MAXTIMER]; + +void (*Q931CreateDialectCB[Q931MAXDLCT])(L3UCHAR iDialect) = { NULL, NULL }; + +Q931State Q931st[Q931MAXSTATE]; + +/***************************************************************************** + + Core system tables and variables. + +*****************************************************************************/ + +L3INT Q931L4HeaderSpace = {0}; /* header space to be ignoder/inserted */ + /* at head of each message. */ + +L3INT Q931L2HeaderSpace = {4}; /* Q921 header space, sapi, tei etc */ + +/***************************************************************************** + + Main interface callback functions. + +*****************************************************************************/ + +Q931ErrorCB_t Q931ErrorProc; /* callback for error messages. */ +L3ULONG (*Q931GetTimeProc) (void) = NULL; /* callback for func reading time in ms */ + +/***************************************************************************** + + Function: Q931SetL4HeaderSpace + + Description: Set the # of bytes to be inserted/ignored at the head of + each message. Q931 will issue a message with space for header + and the user will use this to fill in whatever header info + is required to support the architecture used. + +*****************************************************************************/ +void Q931SetL4HeaderSpace(L3INT space) +{ + Q931L4HeaderSpace = space; +} + +/***************************************************************************** + + Function: Q931SetL2HeaderSpace + + Description: Set the # of bytes to be inserted/ignored at the head of + each message. Q931 will issue a message with space for header + and the user will use this to fill in whatever header info + is required to support the architecture used. + +*****************************************************************************/ +void Q931SetL2HeaderSpace(L3INT space) +{ + Q931L2HeaderSpace = space; +} + +/***************************************************************************** + + Function: Q931ProcDummy + + Description: Dummy function for message processing. + +*****************************************************************************/ +L3INT Q931ProcDummy(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b,L3INT c) +{ + return Q931E_INTERNAL; +} + +/***************************************************************************** + + Function: Q931UmesDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931UmesDummy(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size) +{ + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: Q931UieDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931UieDummy(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + return Q931E_UNKNOWN_IE; +} + +/***************************************************************************** + + Function: Q931PmesDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931PmesDummy(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: Q931PieDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931PieDummy(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + return Q931E_UNKNOWN_IE; +} + +/***************************************************************************** + + Function: Q931TxDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931TxDummy(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT n) +{ + return Q931E_MISSING_CB; +} + +/***************************************************************************** + + Function: Q931ErrorDummy + + Description: Dummy function for error processing + +*****************************************************************************/ +L3INT Q931ErrorDummy(void *priv, L3INT a, L3INT b, L3INT c) +{ + return 0; +} + +/***************************************************************************** + + Function: Q931Initialize + + Description: This function Initialize the stack. + + Will set up the trunk array, channel + arrays and initialize Q931 function arrays before it finally + set up EuroISDN processing with User as dialect 0 and + Network as dialect 1. + + Note: Initialization of other stacks should be inserted after + the initialization of EuroISDN. + +*****************************************************************************/ +void Q931Initialize() +{ + L3INT x,y; + + /* Secure the callbacks to default procs */ + Q931ErrorProc = Q931ErrorDummy; + + /* The user will only add the message handlers and IE handlers he need, + * so we need to initialize every single entry to a default function + * that will throw an appropriate error if they are ever called. + */ + for (x = 0; x < Q931MAXDLCT; x++) { + for (y = 0; y < Q931MAXMES; y++) { + Q931Proc[x][y] = Q931ProcDummy; + Q931Umes[x][y] = Q931UmesDummy; + Q931Pmes[x][y] = Q931PmesDummy; + } + for (y = 0; y < Q931MAXIE; y++) { + Q931Pie[x][y] = Q931PieDummy; + Q931Uie[x][y] = Q931UieDummy; + } + for (y = 0; y < Q931MAXTIMER; y++) { + Q931Timeout[x][y] = Q931TimeoutDummy; + Q931Timer[x][y] = 0; + } + } + + if (Q931CreateDialectCB[Q931_Dialect_Q931 + Q931_TE] == NULL) + Q931AddDialect(Q931_Dialect_Q931 + Q931_TE, Q931CreateTE); + + if (Q931CreateDialectCB[Q931_Dialect_Q931 + Q931_NT] == NULL) + Q931AddDialect(Q931_Dialect_Q931 + Q931_NT, Q931CreateNT); + + if (Q931CreateDialectCB[Q931_Dialect_National + Q931_TE] == NULL) + Q931AddDialect(Q931_Dialect_National + Q931_TE, nationalCreateTE); + + if (Q931CreateDialectCB[Q931_Dialect_National + Q931_NT] == NULL) + Q931AddDialect(Q931_Dialect_National + Q931_NT, nationalCreateNT); + + if (Q931CreateDialectCB[Q931_Dialect_DMS + Q931_TE] == NULL) + Q931AddDialect(Q931_Dialect_DMS + Q931_TE, DMSCreateTE); + + if (Q931CreateDialectCB[Q931_Dialect_DMS + Q931_NT] == NULL) + Q931AddDialect(Q931_Dialect_DMS + Q931_NT, DMSCreateNT); + + if (Q931CreateDialectCB[Q931_Dialect_5ESS + Q931_TE] == NULL) + Q931AddDialect(Q931_Dialect_5ESS + Q931_TE, ATT5ESSCreateTE); + + if (Q931CreateDialectCB[Q931_Dialect_5ESS + Q931_NT] == NULL) + Q931AddDialect(Q931_Dialect_5ESS + Q931_NT, ATT5ESSCreateNT); + + /* The last step we do is to call the callbacks to create the dialects */ + for (x = 0; x < Q931MAXDLCT; x++) { + if (Q931CreateDialectCB[x] != NULL) { + Q931CreateDialectCB[x]((L3UCHAR)x); + } + } +} + +/** + * Q931TimerTick + * \brief Periodically called to update and check for expired timers + * \param pTrunk Q.931 trunk + */ +void Q931TimerTick(Q931_TrunkInfo_t *pTrunk) +{ + struct Q931_Call *call = NULL; + L3ULONG now = 0; + L3INT x; + + /* TODO: Loop through all active calls, check timers and call timout procs + * if timers are expired. + * Implement a function array so each dialect can deal with their own + * timeouts. + */ + now = Q931GetTime(); + + for (x = 0; x < Q931MAXCALLPERTRUNK; x++) { + call = &pTrunk->call[x]; + + if (!call->InUse || !call->Timer || !call->TimerID) + continue; + + if (call->Timer <= now) { + /* Stop Timer */ + Q931StopTimer(pTrunk, x, call->TimerID); + + /* Invoke dialect timeout callback */ + Q931Timeout[pTrunk->Dialect][call->TimerID](pTrunk, x); + } + } +} + +/***************************************************************************** + + Function: Q931Rx23 + + Description: Receive message from layer 2 (LAPD). Receiving a message + is always done in 2 steps. First the message must be + interpreted and translated to a static struct. Secondly + the message is processed and responded to. + + The Q.931 message contains a static header that is + interpreted in this function. The rest is interpreted + in a sub function according to mestype. + + Parameters: pTrunk [IN] Ptr to trunk info. + buf [IN] Ptr to buffer containing message. + Size [IN] Size of message. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931Rx23(Q931_TrunkInfo_t *pTrunk, L3INT ind, L3UCHAR tei, L3UCHAR * buf, L3INT Size) +{ + L3UCHAR *Mes = NULL; + L3INT RetCode = Q931E_NO_ERROR; + Q931mes_Generic *m = (Q931mes_Generic *) pTrunk->L3Buf; + L3INT ISize; + L3INT IOff = 0; + L3INT L2HSize = Q931L2HeaderSpace; + + switch (ind) { + case Q921_DL_UNIT_DATA: /* DL-UNITDATA indication (UI frame, 3 byte header) */ + L2HSize = 3; + + case Q921_DL_DATA: /* DL-DATA indication (I frame, 4 byte header) */ + /* Reset our decode buffer */ + memset(pTrunk->L3Buf, 0, sizeof(pTrunk->L3Buf)); + + /* L2 Header Offset */ + Mes = &buf[L2HSize]; + + /* Protocol Discriminator */ + m->ProtDisc = Mes[IOff++]; + + /* CRV */ + m->CRVFlag = (Mes[IOff + 1] >> 7) & 0x01; + m->CRV = Q931Uie_CRV(pTrunk, Mes, m->buf, &IOff, &ISize); + + /* Message Type */ + m->MesType = Mes[IOff++]; + + /* Store tei */ + m->Tei = tei; + + /* d'oh a little ugly but this saves us from: + * a) doing Q.921 work in the lower levels (extracting the TEI ourselves) + * b) adding a tei parameter to _all_ Proc functions + */ + if (tei) { + L3INT callIndex = 0; + + /* Find the call using CRV */ + RetCode = Q931FindCRV(pTrunk, m->CRV, &callIndex); + if (RetCode == Q931E_NO_ERROR && !pTrunk->call[callIndex].Tei) { + pTrunk->call[callIndex].Tei = tei; + } + } + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Received message from Q.921 (ind %d, tei %d, size %d)\nMesType: %d, CRVFlag %d (%s), CRV %d (Dialect: %d)\n", ind, m->Tei, Size, + m->MesType, m->CRVFlag, m->CRVFlag ? "Terminator" : "Originator", m->CRV, pTrunk->Dialect); + + RetCode = Q931Umes[pTrunk->Dialect][m->MesType](pTrunk, Mes, (Q931mes_Generic *)pTrunk->L3Buf, IOff, Size - L2HSize); + if (RetCode >= Q931E_NO_ERROR) { + RetCode = Q931Proc[pTrunk->Dialect][m->MesType](pTrunk, pTrunk->L3Buf, 2); + } + break; + + default: + break; + } + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Tx34 + + Description: Called from the stack to send a message to layer 4. + + Parameters: Mes[IN] Ptr to message buffer. + Size[IN] Message size in bytes. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931Tx34(Q931_TrunkInfo_t *pTrunk, L3UCHAR * Mes, L3INT Size) +{ + Q931Log(pTrunk, Q931_LOG_DEBUG, "Sending message to Layer4 (size: %d)\n", Size); + + if (pTrunk->Q931Tx34CBProc) { + return pTrunk->Q931Tx34CBProc(pTrunk->PrivateData34, Mes, Size); + } + return Q931E_MISSING_CB; +} + +/***************************************************************************** + + Function: Q931Rx43 + + Description: Receive message from Layer 4 (application). + + Parameters: pTrunk[IN] Trunk #. + buf[IN] Message Pointer. + Size[IN] Message size in bytes. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931Rx43(Q931_TrunkInfo_t *pTrunk,L3UCHAR * buf, L3INT Size) +{ + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + L3INT RetCode = Q931E_NO_ERROR; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Receiving message from Layer4 (size: %d, type: %d)\n", Size, ptr->MesType); + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Q931Rx43 return code: %d\n", RetCode); + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Tx32 + + Description: Called from the stack to send a message to L2. The input is + always a non-packed message so it will first make a proper + call to create a packed message before it transmits that + message to layer 2. + + Parameters: pTrunk[IN] Trunk # + buf[IN] Ptr to message buffer. + Size[IN] Message size in bytes. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931Tx32Data(Q931_TrunkInfo_t *pTrunk, L3UCHAR bcast, L3UCHAR * Mes, L3INT Size) +{ + Q931mes_Generic *ptr = (Q931mes_Generic*)Mes; + L3INT RetCode = Q931E_NO_ERROR; + L3INT iDialect = pTrunk->Dialect; + L3INT Offset = bcast ? (Q931L2HeaderSpace - 1) : Q931L2HeaderSpace; + L3INT OSize; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Sending message to Q.921 (size: %d)\n", Size); + + memset(pTrunk->L2Buf, 0, sizeof(pTrunk->L2Buf)); + + /* Call pack function through table. */ + RetCode = Q931Pmes[iDialect][ptr->MesType](pTrunk, (Q931mes_Generic *)Mes, Size, &pTrunk->L2Buf[Offset], &OSize); + if (RetCode >= Q931E_NO_ERROR) { + L3INT callIndex; + L3UCHAR tei = 0; + + if (ptr->CRV) { + /* Find the call using CRV */ + RetCode = Q931FindCRV(pTrunk, ptr->CRV, &callIndex); + if (RetCode != Q931E_NO_ERROR) + return RetCode; + + tei = pTrunk->call[callIndex].Tei; + } + + if (pTrunk->Q931Tx32CBProc) { + RetCode = pTrunk->Q931Tx32CBProc(pTrunk->PrivateData32, bcast ? Q921_DL_UNIT_DATA : Q921_DL_DATA, tei, pTrunk->L2Buf, OSize + Offset); + } else { + RetCode = Q931E_MISSING_CB; + } + } + + return RetCode; +} + + +/***************************************************************************** + + Function: Q931SetError + + Description: Called from the stack to indicate an error. + + Parameters: ErrID ID of ie or message causing error. + ErrPar1 Error parameter 1 + ErrPar2 Error parameter 2. + + +*****************************************************************************/ +void Q931SetError(Q931_TrunkInfo_t *pTrunk,L3INT ErrID, L3INT ErrPar1, L3INT ErrPar2) +{ + if (pTrunk->Q931ErrorCBProc) { + pTrunk->Q931ErrorCBProc(pTrunk->PrivateData34, ErrID, ErrPar1, ErrPar2); + } else { + Q931ErrorProc(pTrunk->PrivateData34, ErrID, ErrPar1, ErrPar2); + } +} + +void Q931SetDefaultErrorCB(Q931ErrorCB_t Q931ErrorPar) +{ + Q931ErrorProc = Q931ErrorPar; +} + +/***************************************************************************** + + Function: Q931CreateCRV + + Description: Create a CRV entry and return it's index. The function will + locate a free entry in the call tables allocate it and + allocate a unique CRV value attached to it. + + Parameters: pTrunk [IN] Trunk number + callindex [OUT] return call table index. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. +****************************************************************************/ +L3INT Q931CreateCRV(Q931_TrunkInfo_t *pTrunk, L3INT * callIndex) +{ + L3INT CRV = Q931GetUniqueCRV(pTrunk); + + return Q931AllocateCRV(pTrunk, CRV, callIndex); +} + + +L3INT Q931ReleaseCRV(Q931_TrunkInfo_t *pTrunk, L3INT CRV) +{ + int callIndex; + + if ((Q931FindCRV(pTrunk, CRV, &callIndex)) == Q931E_NO_ERROR) { + pTrunk->call[callIndex].InUse = 0; + return Q931E_NO_ERROR; + } + + return Q931E_INVALID_CRV; +} + +/***************************************************************************** + + Function: Q931AllocateCRV + + Description: Allocate a call table entry and assigns the given CRV value + to it. + + Parameters: pTrunk [IN] Trunk number + iCRV [IN] Call Reference Value. + callindex [OUT] return call table index. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931AllocateCRV(Q931_TrunkInfo_t *pTrunk, L3INT iCRV, L3INT * callIndex) +{ + L3INT x; + for (x = 0; x < Q931MAXCALLPERTRUNK; x++) { + if (!pTrunk->call[x].InUse) { + pTrunk->call[x].CRV = iCRV; + pTrunk->call[x].BChan = 255; + pTrunk->call[x].State = 0; /* null state - idle */ + pTrunk->call[x].TimerID = 0; /* no timer running */ + pTrunk->call[x].Timer = 0; + pTrunk->call[x].InUse = 1; /* mark as used */ + *callIndex = x; + return Q931E_NO_ERROR; + } + } + return Q931E_TOMANYCALLS; +} + +/***************************************************************************** + + Function: Q931GetCallState + + Description: Look up CRV and return current call state. A non existing + CRV is the same as state zero (0). + + Parameters: pTrunk [IN] Trunk number. + iCRV [IN] CRV + + Return Value: Call State. + +*****************************************************************************/ +L3INT Q931GetCallState(Q931_TrunkInfo_t *pTrunk, L3INT iCRV) +{ + L3INT x; + for (x = 0; x < Q931MAXCALLPERTRUNK; x++) { + if (pTrunk->call[x].InUse) { + if (pTrunk->call[x].CRV == iCRV) { + return pTrunk->call[x].State; + } + } + } + return 0; /* assume state zero for non existing CRV's */ +} + +/** + * Q931StartTimer + * \brief Start a timer + * \param pTrunk Q.931 trunk + * \param callindex Index of the call + * \param iTimerID ID of timer + * \return always 0 + */ +L3INT Q931StartTimer(Q931_TrunkInfo_t *pTrunk, L3INT callIndex, L3USHORT iTimerID) +{ +#if 0 + L3ULONG duration = Q931Timer[pTrunk->Dialect][iTimerID]; + + if (duration) { + pTrunk->call[callIndex].Timer = Q931GetTime() + duration; + pTrunk->call[callIndex].TimerID = iTimerID; + } +#endif + return 0; +} + +/** + * Q931StopTimer + * \brief Stop a timer + * \param pTrunk Q.931 trunk + * \param callindex Index of the call + * \param iTimerID ID of timer + * \return always 0 + */ +L3INT Q931StopTimer(Q931_TrunkInfo_t *pTrunk, L3INT callindex, L3USHORT iTimerID) +{ + if (pTrunk->call[callindex].TimerID == iTimerID) + pTrunk->call[callindex].TimerID = 0; + + return 0; +} + +L3INT Q931SetState(Q931_TrunkInfo_t *pTrunk, L3INT callIndex, L3INT iState) +{ + pTrunk->call[callIndex].State = iState; + + return 0; +} + +L3ULONG Q931GetTime() +{ + L3ULONG tNow = 0; + static L3ULONG tLast = 0; + + if (Q931GetTimeProc != NULL) { + tNow = Q931GetTimeProc(); + if (tNow < tLast) { /* wrapped */ + /* TODO */ + } + tLast = tNow; + } + return tNow; +} + +void Q931SetGetTimeCB(L3ULONG (*callback)(void)) +{ + Q931GetTimeProc = callback; +} + +L3INT Q931FindCRV(Q931_TrunkInfo_t *pTrunk, L3INT crv, L3INT *callindex) +{ + L3INT x; + for (x = 0; x < Q931MAXCALLPERTRUNK; x++) { + if (pTrunk->call[x].InUse) { + if (pTrunk->call[x].CRV == crv) { + *callindex = x; + return Q931E_NO_ERROR; + } + } + } + return Q931E_INVALID_CRV; +} + + +void Q931AddDialect(L3UCHAR i, void (*callback)(L3UCHAR iD )) +{ + if (i < Q931MAXDLCT) { + Q931CreateDialectCB[i] = callback; + } +} + +/***************************************************************************** + Function: Q931AddStateEntry + + Description: Find an empty entry in the dialects state table and add this + entry. +*****************************************************************************/ +void Q931AddStateEntry(L3UCHAR iD, L3INT iState, L3INT iMes, L3UCHAR cDir) +{ + int x; + for (x = 0; x < Q931MAXSTATE; x++) { + if (Q931st[x].Message == 0) { + Q931st[x].State = iState; + Q931st[x].Message = iMes; + Q931st[x].Direction = cDir; + /* TODO Sort table and use bsearch */ + return; + } + } +} + +/***************************************************************************** + Function: Q931IsEventLegal + + Description: Check state table for matching criteria to indicate if this + Message is legal in this state or not. + + Note: Someone write a bsearch or invent something smart here + please - sequential is ok for now. +*****************************************************************************/ +L3BOOL Q931IsEventLegal(L3UCHAR iD, L3INT iState, L3INT iMes, L3UCHAR cDir) +{ + int x; + /* TODO Sort table and use bsearch */ + for (x = 0; x < Q931MAXSTATE; x++) { + if (Q931st[x].State == iState && Q931st[x].Message == iMes && + Q931st[x].Direction == cDir) { + return L3TRUE; + } + } + return L3FALSE; +} + +/***************************************************************************** + Function: q931_error_to_name() + + Description: Check state table for matching criteria to indicate if this + Message is legal in this state or not. + + Note: Someone write a bsearch or invent something smart here + please - sequential is ok for now. +*****************************************************************************/ +static const char *q931_error_names[] = { + "Q931E_NO_ERROR", /* 0 */ + + "Q931E_UNKNOWN_MESSAGE", /* -3001 */ + "Q931E_ILLEGAL_IE", /* -3002 */ + "Q931E_UNKNOWN_IE", /* -3003 */ + "Q931E_BEARERCAP", /* -3004 */ + "Q931E_HLCOMP", /* -3005 */ + "Q931E_LLCOMP", /* -3006 */ + "Q931E_INTERNAL", /* -3007 */ + "Q931E_MISSING_CB", /* -3008 */ + "Q931E_UNEXPECTED_MESSAGE", /* -3009 */ + "Q931E_ILLEGAL_MESSAGE", /* -3010 */ + "Q931E_TOMANYCALLS", /* -3011 */ + "Q931E_INVALID_CRV", /* -3012 */ + "Q931E_CALLID", /* -3013 */ + "Q931E_CALLSTATE", /* -3014 */ + "Q931E_CALLEDSUB", /* -3015 */ + "Q931E_CALLEDNUM", /* -3016 */ + "Q931E_CALLINGNUM", /* -3017 */ + "Q931E_CALLINGSUB", /* -3018 */ + "Q931E_CAUSE", /* -3019 */ + "Q931E_CHANID", /* -3020 */ + "Q931E_DATETIME", /* -3021 */ + "Q931E_DISPLAY", /* -3022 */ + "Q931E_KEYPADFAC", /* -3023 */ + "Q931E_NETFAC", /* -3024 */ + "Q931E_NOTIFIND", /* -3025 */ + "Q931E_PROGIND", /* -3026 */ + "Q931E_RESTARTIND", /* -3027 */ + "Q931E_SEGMENT", /* -3028 */ + "Q931E_SIGNAL", /* -3029 */ + "Q931E_GENERIC_DIGITS" /* -3030 */ + +}; + +#define Q931_MAX_ERROR 30 + +const char *q931_error_to_name(q931_error_t error) +{ + int index = 0; + if ((int)error < 0) { + index = (((int)error * -1) -3000); + } + if (index < 0 || index > Q931_MAX_ERROR) { + return ""; + } + return q931_error_names[index]; +} +/* + * Logging + */ +#include + +L3INT Q931Log(Q931_TrunkInfo_t *trunk, Q931LogLevel_t level, const char *fmt, ...) +{ + char buf[Q931_LOGBUFSIZE]; + L3INT len; + va_list ap; + + if (!trunk->Q931LogCBProc) + return 0; + + if (trunk->loglevel < level) + return 0; + + va_start(ap, fmt); + + len = vsnprintf(buf, sizeof(buf)-1, fmt, ap); + if (len <= 0) { + /* TODO: error handling */ + return -1; + } + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + + buf[len] = '\0'; + + va_end(ap); + + return trunk->Q931LogCBProc(trunk->PrivateDataLog, level, buf, len); +} + +/** + * Q921SetLogCB + * \brief Set Logging callback function and private data + */ +void Q931SetLogCB(Q931_TrunkInfo_t *trunk, Q931LogCB_t func, void *priv) +{ + trunk->Q931LogCBProc = func; + trunk->PrivateDataLog = priv; +} + +/** + * Q921SetLogLevel + * \brief Set Loglevel + */ +void Q931SetLogLevel(Q931_TrunkInfo_t *trunk, Q931LogLevel_t level) +{ + if(!trunk) + return; + + if (level < Q931_LOG_NONE) { + level = Q931_LOG_NONE; + } else if (level > Q931_LOG_DEBUG) { + level = Q931_LOG_DEBUG; + } + + trunk->loglevel = level; +} + +/** + * Q931TimeoutDummy + * \brief Dummy handler for timeouts + * \param pTrunk Q.931 trunk + * \param callIndex Index of call + */ +L3INT Q931TimeoutDummy(Q931_TrunkInfo_t *pTrunk, L3INT callIndex) +{ + Q931Log(pTrunk, Q931_LOG_DEBUG, "Timer %d of call %d (CRV: %d) timed out\n", pTrunk->call[callIndex].TimerID, callIndex, pTrunk->call[callIndex].CRV); + + return 0; +} diff --git a/libs/freetdm/src/isdn/Q931StateNT.c b/libs/freetdm/src/isdn/Q931StateNT.c new file mode 100644 index 0000000000..8d05fb6d03 --- /dev/null +++ b/libs/freetdm/src/isdn/Q931StateNT.c @@ -0,0 +1,1218 @@ +/***************************************************************************** + + FileName: q931StateNT.c + + Contents: Q.931 State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: Q931CreateNT + + Description: Will create the Q931 NT as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void Q931CreateNT(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingNT, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingNT, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectNT, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckNT, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressNT, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupNT, Q931Umes_Setup, Q931Pmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckNT, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeNT, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckNT, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectNT, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendNT, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckNT, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectNT, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationNT, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectNT, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseNT, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteNT, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartNT, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckNT, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlNT, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationNT, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyNT, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusNT, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryNT, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentNT, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityNT, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldNT, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckNT, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectNT, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterNT, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveNT, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckNT, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectNT, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_REDIRECTING_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message + * procs can when search this to find out if the message/state + * combination is legale. If not, the proc for unexpected message apply. + */ + + /* TODO define state table here */ + + /* Timer default values */ + Q931SetTimerDefault(i, Q931_TIMER_T301, 180000); /* T301: 180s */ + Q931SetTimerDefault(i, Q931_TIMER_T302, 15000); /* T302: 15s */ + Q931SetTimerDefault(i, Q931_TIMER_T303, 4000); /* T303: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T304, 20000); /* T304: 20s */ + Q931SetTimerDefault(i, Q931_TIMER_T305, 30000); /* T305: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T306, 30000); /* T306: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T307, 180000); /* T307: 180s */ + Q931SetTimerDefault(i, Q931_TIMER_T308, 4000); /* T308: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T309, 60000); /* T309: 60s */ + Q931SetTimerDefault(i, Q931_TIMER_T310, 10000); /* T310: 10s */ + Q931SetTimerDefault(i, Q931_TIMER_T312, 12000); /* T312: 12s */ + Q931SetTimerDefault(i, Q931_TIMER_T314, 4000); /* T314: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T316, 120000); /* T316: 120s */ + Q931SetTimerDefault(i, Q931_TIMER_T317, 90000); /* T317: 90s */ + Q931SetTimerDefault(i, Q931_TIMER_T320, 30000); /* T320: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T321, 30000); /* T321: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T322, 4000); /* T322: 4s */ +} + +/***************************************************************************** + + Function: Q931ProcAlertingNT + +*****************************************************************************/ +L3INT Q931ProcAlertingNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* Reset 4 sec timer. */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcCallProceedingNT + +*****************************************************************************/ +L3INT Q931ProcCallProceedingNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcConnectNT + +*****************************************************************************/ +L3INT Q931ProcConnectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcConnectAckNT + +*****************************************************************************/ +L3INT Q931ProcConnectAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcProgressNT + +*****************************************************************************/ +L3INT Q931ProcProgressNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSetupNT + + Description: Process a SETUP message. + + *****************************************************************************/ +L3INT Q931ProcSetupNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT rc = 0; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Reject SETUP on existing calls */ + if (Q931GetCallState(pTrunk, pMes->CRV) != Q931_U0) { + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 81); + return Q931E_UNEXPECTED_MESSAGE; + } + + /* outgoing call */ + if (iFrom == 4) { + ret = Q931CreateCRV(pTrunk, &callIndex); + if (ret) + return ret; + + pMes->CRV = pTrunk->call[callIndex].CRV; + + /* + * Outgoing SETUP message will be broadcasted in PTMP mode + */ + ret = Q931Tx32Data(pTrunk, Q931_IS_PTP(pTrunk) ? 0 : 1, buf, pMes->Size); + if (ret) + return ret; + + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + Q931SetState(pTrunk, callIndex, Q931_U1); + } + /* incoming call */ + else { + /* Locate free CRV entry and store info */ + ret = Q931AllocateCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) { + /* Not possible to allocate CRV entry, so must reject call */ + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 42); + return ret; + } + + /* store TEI in call */ + pTrunk->call[callIndex].Tei = pMes->Tei; + + /* Send setup indication to user */ + ret = Q931Tx34(pTrunk, (L3UCHAR*)pMes, pMes->Size); + if (ret != Q931E_NO_ERROR) { + return ret; + } else { + /* Must be full queue, meaning we can't process the call */ + /* so we must disconnect */ + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 81); + return ret; + } +#if 0 + /* TODO: Unreachable code??? */ + /* Set state U6 */ + Q931SetState(pTrunk, callIndex, Q931_U6); + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); +#endif + } + + return rc; +} + +/***************************************************************************** + + Function: Q931ProcSetupAckNT + + Description: Used to acknowedge a SETUP. Usually the first initial + response recevide back used to buy some time. + + Note that ChanID (B Channel Assignment) might come here from + NT side. + +*****************************************************************************/ +L3INT Q931ProcSetupAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeNT + +*****************************************************************************/ +L3INT Q931ProcResumeNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeAckNT + +*****************************************************************************/ +L3INT Q931ProcResumeAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeRejectNT + +*****************************************************************************/ +L3INT Q931ProcResumeRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendNT + +*****************************************************************************/ +L3INT Q931ProcSuspendNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendAckNT + +*****************************************************************************/ +L3INT Q931ProcSuspendAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendRejectNT + +*****************************************************************************/ +L3INT Q931ProcSuspendRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcInformationNT + +*****************************************************************************/ +L3INT Q931ProcUserInformationNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcDisconnectNT + +*****************************************************************************/ +L3INT Q931ProcDisconnectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcReleaseNT + +*****************************************************************************/ +L3INT Q931ProcReleaseNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcReleaseCompleteNT + +*****************************************************************************/ +L3INT Q931ProcReleaseCompleteNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRestartNT + +*****************************************************************************/ +L3INT Q931ProcRestartNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRestartAckNT + +*****************************************************************************/ +L3INT Q931ProcRestartAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcCongestionControlNT + +*****************************************************************************/ +L3INT Q931ProcCongestionControlNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcInformationNT + +*****************************************************************************/ +L3INT Q931ProcInformationNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcNotifyNT + +*****************************************************************************/ +L3INT Q931ProcNotifyNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcStatusNT + +*****************************************************************************/ +L3INT Q931ProcStatusNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcStatusEnquiryNT + +*****************************************************************************/ +L3INT Q931ProcStatusEnquiryNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSegmentNT + +*****************************************************************************/ +L3INT Q931ProcSegmentNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/****************************************************************************/ +/******************* Q.932 - Supplementary Services *************************/ +/****************************************************************************/ + +/***************************************************************************** + + Function: Q932ProcFacilityNT + +*****************************************************************************/ +L3INT Q932ProcFacilityNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcHoldNT + +*****************************************************************************/ +L3INT Q932ProcHoldNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcHoldAckNT + +*****************************************************************************/ +L3INT Q932ProcHoldAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcHoldRejectNT + +*****************************************************************************/ +L3INT Q932ProcHoldRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRegisterTE + +*****************************************************************************/ +L3INT Q932ProcRegisterNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveNT + +*****************************************************************************/ +L3INT Q932ProcRetrieveNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRetrieveAckNT + +*****************************************************************************/ +L3INT Q932ProcRetrieveAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRetrieveRejectNT + +*****************************************************************************/ +L3INT Q932ProcRetrieveRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} diff --git a/libs/freetdm/src/isdn/Q931StateTE.c b/libs/freetdm/src/isdn/Q931StateTE.c new file mode 100644 index 0000000000..562dd0b458 --- /dev/null +++ b/libs/freetdm/src/isdn/Q931StateTE.c @@ -0,0 +1,1310 @@ +/***************************************************************************** + + FileName: q931StateTE.c + + Contents: Q.931 State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + This reference implementation uses a process per message, + meaning that each message must check call states. This + is easier for dialect maintenance as each message proc + can be replaced individually. A new TE variant only + need to copy the Q931CreateTE and replace those procs or + need to override. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: Q931CreateTE + + Description: Will create the Q931 TE as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void Q931CreateTE(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingTE, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingTE, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectTE, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckTE, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressTE, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupTE, Q931Umes_Setup, Q931Pmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckTE, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeTE, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckTE, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectTE, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendTE, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckTE, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectTE, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationTE, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectTE, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseTE, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteTE, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartTE, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckTE, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlTE, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationTE, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyTE, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusTE, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryTE, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentTE, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityTE, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldTE, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckTE, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectTE, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterTE, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveTE, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckTE, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectTE, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_REDIRECTING_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message + * procs can when search this to find out if the message/state + * combination is legale. If not, the proc for unexpected message apply. + */ + + /* State 0 Idle */ + Q931AddStateEntry(i, Q931_U0, Q931mes_RESUME, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_STATUS, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE_COMPLETE, 4); + + /* State 1 Call Initiating */ + Q931AddStateEntry(i, Q931_U1, Q931mes_DISCONNECT, 2); + Q931AddStateEntry(i, Q931_U1, Q931mes_SETUP_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_RELEASE_COMPLETE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CONNECT, 4); + + /* State 2 Overlap Sending */ + Q931AddStateEntry(i, Q931_U2, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U2, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_RELEASE, 2); + + /* State 3 Outgoing Call Proceeding */ + Q931AddStateEntry(i, Q931_U3, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_RELEASE, 2); + + /* State 4 Call Delivered */ + Q931AddStateEntry(i, Q931_U4, Q931mes_CONNECT, 4); + + /* State 6 Call Precent */ + Q931AddStateEntry(i, Q931_U6, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CALL_PROCEEDING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE_COMPLETE, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U6, Q931mes_DISCONNECT, 4); + + /* State 7 Call Received */ + Q931AddStateEntry(i, Q931_U7, Q931mes_CONNECT, 2); + + /* State 8 Connect request */ + Q931AddStateEntry(i, Q931_U8, Q931mes_CONNECT_ACKNOWLEDGE, 4); + + /* State 9 Incoming Call Proceeding */ + Q931AddStateEntry(i, Q931_U9, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_PROGRESS, 2); + + /* State 10 Active */ + Q931AddStateEntry(i, Q931_U10, Q931mes_SUSPEND, 2); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 4); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 2); + + /* State 11 Disconnect Request */ + Q931AddStateEntry(i, Q931_U11, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_NOTIFY, 4); + + /* State 12 Disconnect Ind */ + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 2); + + /* State 15 Suspend Request */ + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_REJECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_RELEASE, 4); + +/* TODO + Q931AddStateEntry(i, Q931_U17, + Q931AddStateEntry(i, Q931_U19, + Q931AddStateEntry(i, Q931_U25, +*/ + + /* Timer default values */ + Q931SetTimerDefault(i, Q931_TIMER_T301, 180000); /* T301: 180s */ + Q931SetTimerDefault(i, Q931_TIMER_T302, 15000); /* T302: 15s */ + Q931SetTimerDefault(i, Q931_TIMER_T303, 4000); /* T303: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T304, 30000); /* T304: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T305, 30000); /* T305: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T308, 4000); /* T308: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T309, 60000); /* T309: 60s */ + Q931SetTimerDefault(i, Q931_TIMER_T310, 60000); /* T310: 60s */ + Q931SetTimerDefault(i, Q931_TIMER_T313, 4000); /* T313: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T314, 4000); /* T314: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T316, 120000); /* T316: 120s */ + Q931SetTimerDefault(i, Q931_TIMER_T317, 90000); /* T317: 90s */ + Q931SetTimerDefault(i, Q931_TIMER_T318, 4000); /* T318: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T319, 4000); /* T319: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T321, 30000); /* T321: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T322, 4000); /* T322: 4s */ +} + +/***************************************************************************** + + Function: Q931ProcAlertingTE + +*****************************************************************************/ +L3INT Q931ProcAlertingTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* Reset 4 sec timer. */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcCallProceedingTE + +*****************************************************************************/ +L3INT Q931ProcCallProceedingTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcConnectTE + +*****************************************************************************/ +L3INT Q931ProcConnectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + if (pTrunk->autoConnectAck) { + Q931AckConnect(pTrunk, buf); + } + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcConnectAckTE + +*****************************************************************************/ +L3INT Q931ProcConnectAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcProgressTE + +*****************************************************************************/ +L3INT Q931ProcProgressTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSetupTE + +*****************************************************************************/ +L3INT Q931ProcSetupTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT rc = 0; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Reject SETUP on existing calls */ + if (Q931GetCallState(pTrunk, pMes->CRV) != Q931_U0) { + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 81); + return Q931E_UNEXPECTED_MESSAGE; + } + + /* outgoing call */ + if (iFrom == 4) { + ret = Q931CreateCRV(pTrunk, &callIndex); + if (ret) + return ret; + pMes->CRV = pTrunk->call[callIndex].CRV; + + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + if (ret) + return ret; + + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + + /* TODO: Add this back when we get the state stuff more filled out */ + /*Q931SetState(pTrunk, callIndex, Q931_U1);*/ + } + /* incoming call */ + else { + /* Locate free CRV entry and store info */ + ret = Q931AllocateCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) { + /* Not possible to allocate CRV entry, so must reject call */ + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 42); + return ret; + } + + /* Send setup indication to user */ + ret = Q931Tx34(pTrunk, (L3UCHAR*)pMes, pMes->Size); + if (ret != Q931E_NO_ERROR) { + if (pTrunk->autoSetupAck) { + Q931AckSetup(pTrunk, buf); + } + return ret; + } else { + /* Must be full queue, meaning we can't process the call */ + /* so we must disconnect */ + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 81); + return ret; + } +#if 0 + /* TODO: Unreachable code??? */ + /* Set state U6 */ + Q931SetState(pTrunk, callIndex, Q931_U6); + + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); +#endif + } + return rc; +} + +/***************************************************************************** + + Function: Q931ProcSetupAckTE + + Description: Used to acknowedge a SETUP. Usually the first initial + response recevide back used to buy some time. L4 sending this + should only be passed on. L2 sending this means that we set + a new timer (and pass it to L4). + + Note that ChanID (B Channel Assignment) might come here from + NT side. + +*****************************************************************************/ +L3INT Q931ProcSetupAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeTE + +*****************************************************************************/ +L3INT Q931ProcResumeTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic * pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (Q931GetCallState(pTrunk, pMes->CRV) == Q931_U0 && iFrom ==4) { + /* Call reference selection */ + ret = Q931CreateCRV(pTrunk, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + pMes->CRV = pTrunk->call[callIndex].CRV; + + /* Send RESUME to network */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + if (ret != Q931E_NO_ERROR) + return ret; + + /* Start timer T318 */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T318); + + /* set state U17 */ + Q931SetState(pTrunk, callIndex, Q931_U17); + } else { + return Q931E_ILLEGAL_MESSAGE; + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeAckTE + +*****************************************************************************/ +L3INT Q931ProcResumeAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeRejectTE + +*****************************************************************************/ +L3INT Q931ProcResumeRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendTE + +*****************************************************************************/ +L3INT Q931ProcSuspendTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendAckTE + +*****************************************************************************/ +L3INT Q931ProcSuspendAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendRejectTE + +*****************************************************************************/ +L3INT Q931ProcSuspendRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcInformationTE + +*****************************************************************************/ +L3INT Q931ProcUserInformationTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcDisconnectTE + +*****************************************************************************/ +L3INT Q931ProcDisconnectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Processing DISCONNECT message from %s for CRV: %d (%#hx)\n", + iFrom == 4 ? "Local" : "Remote", pMes->CRV, pMes->CRV); + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcReleaseTE + +*****************************************************************************/ +L3INT Q931ProcReleaseTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT state = Q931GetCallState(pTrunk, pMes->CRV); + L3INT ret = Q931E_NO_ERROR; + + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (state == Q931_U0 && iFrom == 2) { + Q931Tx34(pTrunk, buf, pMes->Size); + ret = Q931ReleaseComplete(pTrunk, buf); + } else { + ret = Q931ProcUnexpectedMessage(pTrunk, buf, iFrom); + } + if (pMes->CRV && iFrom == 2) { + /* Find the call using CRV */ + if ((Q931FindCRV(pTrunk, pMes->CRV, &callIndex)) != Q931E_NO_ERROR) + return ret; + pTrunk->call[callIndex].InUse = 0; + } + + return ret; +} + +/***************************************************************************** + + Function: Q931ProcReleaseCompleteTE + +*****************************************************************************/ +L3INT Q931ProcReleaseCompleteTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } else { + if (pMes->CRV) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + pTrunk->call[callIndex].InUse = 0; + + /* TODO: experimental, send RELEASE_COMPLETE message */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRestartTE + +*****************************************************************************/ +L3INT Q931ProcRestartTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->CRV) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + + /* TODO chack against state table for illegal or unexpected message here */ + + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + + if (pTrunk->autoRestartAck) { + Q931AckRestart(pTrunk, buf); + } + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRestartAckTE + +*****************************************************************************/ +L3INT Q931ProcRestartAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->CRV) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + + /* TODO chack against state table for illegal or unexpected message here */ + + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcCongestionControlTE + +*****************************************************************************/ +L3INT Q931ProcCongestionControlTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcInformationTE + +*****************************************************************************/ +L3INT Q931ProcInformationTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcNotifyTE + +*****************************************************************************/ +L3INT Q931ProcNotifyTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcStatusTE + +*****************************************************************************/ +L3INT Q931ProcStatusTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcStatusEnquiryTE + +*****************************************************************************/ +L3INT Q931ProcStatusEnquiryTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSegmentTE + +*****************************************************************************/ +L3INT Q931ProcSegmentTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/****************************************************************************/ +/******************* Q.932 - Supplementary Services *************************/ +/****************************************************************************/ + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcFacilityTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcHoldTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcHoldAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcHoldRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRegisterTE + +*****************************************************************************/ +L3INT Q932ProcRegisterTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcRetrieveTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRetrieveAckTE + +*****************************************************************************/ +L3INT Q932ProcRetrieveAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRetrieveRejectTE + +*****************************************************************************/ +L3INT Q932ProcRetrieveRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} diff --git a/libs/freetdm/src/isdn/Q931api.c b/libs/freetdm/src/isdn/Q931api.c new file mode 100644 index 0000000000..ed0d13836b --- /dev/null +++ b/libs/freetdm/src/isdn/Q931api.c @@ -0,0 +1,598 @@ +/***************************************************************************** + + FileName: Q931api.c + + Contents: api (Application Programming Interface) functions. + See q931.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" +#include "memory.h" + +extern L3INT Q931L4HeaderSpace; + +/* +L3INT Q931CreateMesIndex(L3INT mc) +{ + if(mc < 0 || mc > 127 ) + return Q931E_INTERNAL; + + if(Q931MesCount >127) + return Q931E_INTERNAL; + + Q931MesIndex[mc] = Q931MesCount ++; + + return Q931E_NO_ERROR; +} +*/ +/* +L3INT Q931CreateIEIndex(L3INT iec) +{ + if(iec < 0 || iec > 127 ) + return Q931E_INTERNAL; + + if(Q931IECount > 127) + return Q931E_INTERNAL; + + Q931IEIndex[iec] = Q931IECount ++; + + return Q931E_NO_ERROR; +} +*/ + +L3INT Q931Api_InitTrunk(Q931_TrunkInfo_t *pTrunk, + Q931Dialect_t Dialect, + Q931NetUser_t NetUser, + Q931_TrunkType_t TrunkType, + Q931Tx34CB_t Q931Tx34CBProc, + Q931Tx32CB_t Q931Tx32CBProc, + Q931ErrorCB_t Q931ErrorCBProc, + void *PrivateData32, + void *PrivateData34) +{ + int y, dchannel, maxchans, has_sync = 0; + + switch(TrunkType) + { + case Q931_TrType_E1: + dchannel = 16; + maxchans = 31; + has_sync = 1; + break; + + case Q931_TrType_T1: + case Q931_TrType_J1: + dchannel = 24; + maxchans = 24; + break; + + case Q931_TrType_BRI: + case Q931_TrType_BRI_PTMP: + dchannel = 3; + maxchans = 3; + break; + + default: + return 0; + } + + pTrunk->Q931Tx34CBProc = Q931Tx34CBProc; + pTrunk->Q931Tx32CBProc = Q931Tx32CBProc; + pTrunk->Q931ErrorCBProc = Q931ErrorCBProc; + pTrunk->PrivateData32 = PrivateData32; + pTrunk->PrivateData34 = PrivateData34; + + pTrunk->LastCRV = 0; + pTrunk->Dialect = Dialect + NetUser; + pTrunk->Enabled = 0; + pTrunk->TrunkType = TrunkType; + pTrunk->NetUser = NetUser; + pTrunk->TrunkState = 0; + pTrunk->autoRestartAck = 0; + for(y=0; y < Q931MAXCHPERTRUNK; y++) + { + pTrunk->ch[y].Available = 1; + + if(has_sync && y == 0) + { + pTrunk->ch[y].ChanType = Q931_ChType_Sync; + } + else if(y == dchannel) + { + pTrunk->ch[y].ChanType = Q931_ChType_D; + } + else if(y > maxchans) + { + pTrunk->ch[y].ChanType = Q931_ChType_NotUsed; + } + else + { + pTrunk->ch[y].ChanType = Q931_ChType_B; + } + } + + for(y=0; y < Q931MAXCALLPERTRUNK; y++) + { + pTrunk->call[y].InUse = 0; + + } + return 1; +} + +void Q931SetMesProc(L3UCHAR mes, L3UCHAR dialect, q931proc_func_t *Q931ProcFunc, q931umes_func_t *Q931UmesFunc, q931pmes_func_t *Q931PmesFunc) +{ + if(Q931ProcFunc != NULL) + Q931Proc[dialect][mes] = Q931ProcFunc; + if(Q931UmesFunc != NULL) + Q931Umes[dialect][mes] = Q931UmesFunc; + if(Q931PmesFunc != NULL) + Q931Pmes[dialect][mes] = Q931PmesFunc; +} + +void Q931SetIEProc(L3UCHAR iec, L3UCHAR dialect, q931pie_func_t *Q931PieProc, q931uie_func_t *Q931UieProc) +{ + if(Q931PieProc != NULL) + Q931Pie[dialect][iec] = Q931PieProc; + if(Q931UieProc != NULL) + Q931Uie[dialect][iec] = Q931UieProc; +} + +void Q931SetTimeoutProc(L3UCHAR dialect, L3UCHAR timer, q931timeout_func_t *Q931TimeoutProc) +{ + if(Q931Timeout != NULL) + Q931Timeout[dialect][timer] = Q931TimeoutProc; +} + +void Q931SetTimerDefault(L3UCHAR dialect, L3UCHAR timer, q931timer_t timeout) +{ + Q931Timer[dialect][timer] = timeout; +} + +L3INT Q931GetMesSize(Q931mes_Generic *pMes) +{ + + L3UCHAR *p = &pMes->buf[0]; + L3INT Size = (L3INT)(p - (L3UCHAR *)pMes); + return Size; +} + +/***************************************************************************** + + Function: q931AppendIE + + Description: Append IE to the message. + + Parameters: pm Ptr to message. + pi Ptr to information element + + Return Value ie setting + +*****************************************************************************/ + +ie Q931AppendIE( L3UCHAR *pm, L3UCHAR *pi) +{ + ie IE = 0; + Q931mes_Generic * pMes= (Q931mes_Generic *)pm; + Q931ie_BearerCap * pIE= (Q931ie_BearerCap *)pi; + L3INT iISize = pIE->Size; + + L3UCHAR *pBuf = &pMes->buf[0]; + L3INT Off = (L3INT)(pMes->Size - (pBuf - pm)); + IE = (ie)(Off | 0x8000); + + memcpy(&pm[pMes->Size], pi, iISize); + + pMes->Size += iISize; + + return IE; +} + +/***************************************************************************** +*****************************************************************************/ +static L3INT crv={1}; + +L3INT Q931GetUniqueCRV(Q931_TrunkInfo_t *pTrunk) +{ + L3INT max = (Q931_IS_BRI(pTrunk)) ? Q931_BRI_MAX_CRV : Q931_PRI_MAX_CRV; + + crv++; + crv = (crv <= max) ? crv : 1; + + return crv; +} + +L3INT Q931InitMesGeneric(Q931mes_Generic *pMes) +{ + memset(pMes, 0, sizeof(*pMes)); + pMes->ProtDisc = 0x08; + pMes->Size = Q931GetMesSize(pMes); + + return 0; +} + +L3INT Q931InitMesResume(Q931mes_Generic * pMes) +{ + pMes->ProtDisc = 0x08; + pMes->CRV = 0; /* CRV to be allocated, might be receive*/ + pMes->MesType = Q931mes_RESUME; + + pMes->Size = Q931GetMesSize(pMes); + pMes->CallID = 0; /* Channel Identification */ + return 0; +} + +L3INT Q931InitMesRestartAck(Q931mes_Generic * pMes) +{ + pMes->ProtDisc = 0x08; + pMes->CRV = 0; /* CRV to be allocated, might be receive*/ + pMes->MesType = Q931mes_RESTART_ACKNOWLEDGE; + + pMes->Size = Q931GetMesSize(pMes); + pMes->ChanID = 0; /* Channel Identification */ + pMes->Display = 0; + pMes->RestartInd = 0; + pMes->RestartWin = 0; + return 0; +} + +L3INT Q931InitIEBearerCap(Q931ie_BearerCap *pIE) +{ + pIE->IEId = Q931ie_BEARER_CAPABILITY; + pIE->Size = sizeof(Q931ie_BearerCap); + pIE->CodStand = 0; + pIE->ITC = 0; + pIE->TransMode = 0; + pIE->ITR = 0x10; + pIE->RateMul = 0; + + pIE->Layer1Ident = 0; + pIE->UIL1Prot = 0; /* User Information Layer 1 Protocol */ + pIE->SyncAsync = 0; /* Sync/Async */ + pIE->Negot = 0; + pIE->UserRate = 0; + pIE->InterRate = 0; /* Intermediate Rate */ + pIE->NIConTx = 0; + pIE->NIConRx = 0; + pIE->FlowCtlTx = 0; /* Flow control on Tx */ + pIE->FlowCtlRx = 0; /* Flow control on Rx */ + pIE->HDR = 0; + pIE->MultiFrame = 0; /* Multi frame support */ + pIE->Mode = 0; + pIE->LLInegot = 0; + pIE->Assignor = 0; /* Assignor/assignee */ + pIE->InBandNeg = 0; /* In-band/out-band negot. */ + pIE->NumStopBits = 0; /* Number of stop bits */ + pIE->NumDataBits = 0; /* Number of data bits. */ + pIE->Parity = 0; + pIE->DuplexMode = 0; + pIE->ModemType = 0; + pIE->Layer2Ident = 0; + pIE->UIL2Prot = 0; /* User Information Layer 2 Protocol */ + pIE->Layer3Ident = 0; + pIE->UIL3Prot = 0; /* User Information Layer 3 Protocol */ + pIE->AL3Info1 = 0; + pIE->AL3Info2 = 0; + + return 0; +} + +L3INT Q931InitIEChanID(Q931ie_ChanID *pIE) +{ + pIE->IEId = Q931ie_CHANNEL_IDENTIFICATION; + pIE->Size = sizeof(Q931ie_ChanID); + pIE->IntIDPresent = 0; /* Int. id. present */ + pIE->IntType = 0; /* Int. type */ + pIE->PrefExcl = 0; /* Pref./Excl. */ + pIE->DChanInd = 0; /* D-channel ind. */ + pIE->InfoChanSel = 0; /* Info. channel selection */ + pIE->InterfaceID = 0; /* Interface identifier */ + pIE->CodStand = 0; /* Code standard */ + pIE->NumMap = 0; /* Number/Map */ + pIE->ChanMapType = 0; /* Channel type/Map element type */ + pIE->ChanSlot = 0; /* Channel number/Slot map */ + + return 0; +} + +L3INT Q931InitIEProgInd(Q931ie_ProgInd * pIE) +{ + pIE->IEId = Q931ie_PROGRESS_INDICATOR; + pIE->Size = sizeof(Q931ie_ProgInd); + pIE->CodStand = 0; /* Coding standard */ + pIE->Location = 0; /* Location */ + pIE->ProgDesc = 0; /* Progress description */ + + return 0; +} + +L3INT Q931InitIENetFac(Q931ie_NetFac * pIE) +{ + pIE->IEId = Q931ie_NETWORK_SPECIFIC_FACILITIES; + pIE->Size = sizeof(Q931ie_NetFac); + pIE->LenNetID = 0; /* Length of network facilities id. */ + pIE->TypeNetID = 0; /* Type of network identification */ + pIE->NetIDPlan = 0; /* Network identification plan. */ + pIE->NetFac = 0; /* Network specific facility spec. */ + pIE->NetID[0] = 0; + return 0; +} + +L3INT Q931InitIEDisplay(Q931ie_Display * pIE) +{ + pIE->IEId = Q931ie_DISPLAY; + pIE->Size = sizeof(Q931ie_Display); + pIE->Display[0] = 0; + return 0; +} + +L3INT Q931InitIEDateTime(Q931ie_DateTime * pIE) +{ + pIE->IEId = Q931ie_DATETIME; + pIE->Size = sizeof(Q931ie_DateTime); + pIE->Year = 0; /* Year */ + pIE->Month = 0; /* Month */ + pIE->Day = 0; /* Day */ + pIE->Hour = 0; /* Hour */ + pIE->Minute = 0; /* Minute */ + pIE->Second = 0; /* Second */ + + return 0; +} + +L3INT Q931InitIEKeypadFac(Q931ie_KeypadFac * pIE) +{ + pIE->IEId = Q931ie_KEYPAD_FACILITY; + pIE->Size = sizeof(Q931ie_KeypadFac); + pIE->KeypadFac[0] = 0; + return 0; +} + +L3INT Q931InitIESignal(Q931ie_Signal * pIE) +{ + pIE->IEId = Q931ie_SIGNAL; + pIE->Size = sizeof(Q931ie_Signal); + pIE->Signal = 0; + return 0; +} + +L3INT Q931InitIECallingNum(Q931ie_CallingNum * pIE) +{ + pIE->IEId = Q931ie_CALLING_PARTY_NUMBER; + pIE->Size = sizeof(Q931ie_CallingNum); + pIE->TypNum = 0; /* Type of number */ + pIE->NumPlanID = 0; /* Numbering plan identification */ + pIE->PresInd = 0; /* Presentation indicator */ + pIE->ScreenInd = 0; /* Screening indicator */ + pIE->Digit[0] = 0; /* Number digits (IA5) */ + + return 0; +} + +L3INT Q931InitIECallingSub(Q931ie_CallingSub * pIE) +{ + pIE->IEId = Q931ie_CALLING_PARTY_SUBADDRESS; + pIE->Size = sizeof(Q931ie_CallingSub); + pIE->TypNum = 0; /* Type of subaddress */ + pIE->OddEvenInd = 0; /* Odd/Even indicator */ + pIE->Digit[0] = 0; /* Digits */ + + return 0; +} + +L3INT Q931InitIECalledNum(Q931ie_CalledNum * pIE) +{ + pIE->IEId = Q931ie_CALLED_PARTY_NUMBER; + pIE->Size = sizeof(Q931ie_CalledNum); + pIE->TypNum = 0; /* Type of Number */ + pIE->NumPlanID = 0; /* Numbering plan identification */ + pIE->Digit[0] = 0; /* Digit (IA5) */ + + return 0; +} + +L3INT Q931InitIECalledSub(Q931ie_CalledSub * pIE) +{ + pIE->IEId = Q931ie_CALLED_PARTY_SUBADDRESS; + pIE->Size = sizeof(Q931ie_CalledSub); + pIE->TypNum = 0; /* Type of subaddress */ + pIE->OddEvenInd = 0; /* Odd/Even indicator */ + pIE->Digit[0] = 0; /* Digits */ + + return 0; +} + +L3INT Q931InitIETransNetSel(Q931ie_TransNetSel * pIE) +{ + pIE->IEId = Q931ie_TRANSIT_NETWORK_SELECTION; + pIE->Size = sizeof(Q931ie_TransNetSel); + pIE->Type = 0; /* Type of network identifier */ + pIE->NetIDPlan = 0; /* Network idetification plan */ + pIE->NetID[0] = 0; /* Network identification(IA5) */ + + return 0; +} + +L3INT Q931InitIELLComp(Q931ie_LLComp * pIE) +{ + pIE->IEId = Q931ie_LOW_LAYER_COMPATIBILITY; + pIE->Size = sizeof(Q931ie_LLComp); + + pIE->CodStand = 0; /* Coding standard */ + pIE->ITransCap = 0; /* Information transfer capability */ + pIE->NegotInd = 0; /* Negot indic. */ + pIE->TransMode = 0; /* Transfer Mode */ + pIE->InfoRate = 0; /* Information transfer rate */ + pIE->RateMul = 0; /* Rate multiplier */ + pIE->Layer1Ident = 0; /* Layer 1 ident. */ + pIE->UIL1Prot = 0; /* User information layer 1 protocol */ + pIE->SyncAsync = 0; /* Synch/asynch */ + pIE->Negot = 0; /* Negot */ + pIE->UserRate = 0; /* User rate */ + pIE->InterRate = 0; /* Intermediate rate */ + pIE->NIConTx = 0; /* NIC on Tx */ + pIE->NIConRx = 0; /* NIC on Rx */ + pIE->FlowCtlTx = 0; /* Flow control on Tx */ + pIE->FlowCtlRx = 0; /* Flow control on Rx */ + pIE->HDR = 0; /* Hdr/no hdr */ + pIE->MultiFrame = 0; /* Multiframe */ + pIE->ModeL1 = 0; /* Mode L1 */ + pIE->NegotLLI = 0; /* Negot. LLI */ + pIE->Assignor = 0; /* Assignor/Assignor ee */ + pIE->InBandNeg = 0; /* In-band negot. */ + pIE->NumStopBits = 0; /* Number of stop bits */ + pIE->NumDataBits = 0; /* Number of data bits */ + pIE->Parity = 0; /* Parity */ + pIE->DuplexMode = 0; /* Duplex Mode */ + pIE->ModemType = 0; /* Modem type */ + pIE->Layer2Ident = 0; /* Layer 2 ident. */ + pIE->UIL2Prot = 0; /* User information layer 2 protocol */ + pIE->ModeL2 = 0; /* ModeL2 */ + pIE->Q933use = 0; /* Q.9333 use */ + pIE->UsrSpcL2Prot = 0; /* User specified layer 2 protocol info */ + pIE->WindowSize = 0; /* Window size (k) */ + pIE->Layer3Ident = 0; /* Layer 3 ident */ + pIE->OptL3Info = 0; /* Optional layer 3 protocol info. */ + pIE->ModeL3 = 0; /* Mode of operation */ +#if 0 + pIE->ModeX25op = 0; /* Mode of operation X.25 */ +#endif + pIE->DefPackSize = 0; /* Default packet size */ + pIE->PackWinSize = 0; /* Packet window size */ + pIE->AddL3Info = 0; /* Additional Layer 3 protocol info */ + + return 0; +} + +L3INT Q931InitIEHLComp(Q931ie_HLComp * pIE) +{ + pIE->IEId = Q931ie_HIGH_LAYER_COMPATIBILITY; + pIE->Size = sizeof(Q931ie_HLComp); + + return 0; +} + +L3INT Q931ProcUnknownMessage(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom) +{ + /* TODO: Unhandled paramaters */ + (void)pTrunk; + (void)b; + (void)iFrom; + + return 0; +} + +L3INT Q931ProcUnexpectedMessage(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom) +{ + /* TODO: Unhandled paramaters */ + (void)pTrunk; + (void)b; + (void)iFrom; + + return 0; +} + +L3INT Q931Disconnect(Q931_TrunkInfo_t *pTrunk, L3INT iTo, L3INT iCRV, L3INT iCause) +{ + /* TODO: Unhandled paramaters */ + (void)pTrunk; + (void)iTo; + (void)iCRV; + (void)iCause; + + return 0; +} + +L3INT Q931ReleaseComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_RELEASE_COMPLETE; + ptr->CRVFlag = !(ptr->CRVFlag); + return Q931Tx32Data(pTrunk,0,buf,ptr->Size); +} + +L3INT Q931AckRestart(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + L3INT RetCode; + + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_RESTART_ACKNOWLEDGE; + //if (ptr->CRV) { + ptr->CRVFlag = !(ptr->CRVFlag); + //} + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + return RetCode; +} + +L3INT Q931AckSetup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + L3INT RetCode; + + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_SETUP_ACKNOWLEDGE; + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + return RetCode; +} + +L3INT Q931AckConnect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + L3INT RetCode; + + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_CONNECT_ACKNOWLEDGE; + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + return RetCode; +} + +L3INT Q931AckService(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + L3INT RetCode; + + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_SERVICE_ACKNOWLEDGE; + if (ptr->CRV) { + ptr->CRVFlag = !(ptr->CRVFlag); + } + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + return RetCode; +} + +Q931_ENUM_NAMES(DIALECT_TYPE_NAMES, DIALECT_STRINGS) +Q931_STR2ENUM(q931_str2Q931Dialect_type, q931_Q931Dialect_type2str, Q931Dialect_t, DIALECT_TYPE_NAMES, Q931_Dialect_Count) diff --git a/libs/freetdm/src/isdn/Q931ie.c b/libs/freetdm/src/isdn/Q931ie.c new file mode 100644 index 0000000000..62312ee654 --- /dev/null +++ b/libs/freetdm/src/isdn/Q931ie.c @@ -0,0 +1,3074 @@ +/***************************************************************************** + + FileName: Q931ie.c + + Contents: Information Element Pack/Unpack functions. + + These functions will pack out a Q931 message from the bit + packed original format into structs that are easier to process + and pack the same structs back into bit fields when sending + messages out. + + The messages contains a short for each possible IE. The MSB + bit flags the precense of an IE, while the remaining bits + are the offset into a buffer to find the actual IE. + + Each IE are supported by 3 functions: + + Q931Pie_XXX Pack struct into Q.931 IE + Q931Uie_XXX Unpack Q.931 IE into struct + Q931InitIEXXX Initialize IE (see Q931api.c). + + Dialect Note: This file will only contain standard DSS1 IE. Other IE as + used in QSIG, NI2, Q.932 etc are located in separate files. + + See q931.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#if (_MSC_VER >= 1400) /* VC8+ */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#ifndef strncasecmp +#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#endif + +/***************************************************************************** + + Macro: Q931MoreIE + + Description: Local helper macro detecting if there is more IE space left + based on the 3 standard parameters Octet, Off and IESpace. + This can be used to test if the IE is completed to avoid + that the header of the next IE is interpreted as a part of + the current IE. + +*****************************************************************************/ +#define Q931MoreIE() (Octet + Off - 2 < IESize) + +#define Q931IESizeTest(x) {\ + if (Octet + Off - 2 != IESize) {\ + Q931SetError(pTrunk, x, Octet, Off);\ + return x;\ + }\ +} + +/***************************************************************************** + + Function: Q931ReadExt + + Description: Many of the octets in the standard have an MSB 'ext.1'. This + means that the octet usually is the latest octet, but that a + futhure standard may extend the octet. A stack must be able + to handle such extensions by skipping the extension octets. + + This function will increase the offset counter with 1 for + each octet with an MSB of zero. This will allow the stack to + skip extensions wihout knowing anything about them. + + Parameters: IBuf ptr to octet array. + Off Starting offset counter + + Return Value: New offset value. + +*****************************************************************************/ + +L3INT Q931ReadExt(L3UCHAR * IBuf, L3INT Off) +{ + L3INT c = 0; + while ((IBuf[c] & 0x80) == 0) { + c++; + } + return Off + c; +} + +/***************************************************************************** + + Function: Q931Uie_BearerCap + + Description: Unpack a bearer capability ie. + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ + +L3INT Q931Uie_BearerCap(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_BearerCap *pie = (Q931ie_BearerCap*)OBuf; + ie *pIE = &pMsg->BearerCap; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->CodStand = ieGetOctet((IBuf[Octet] & 0x60) >> 5); + pie->ITC = ieGetOctet(IBuf[Octet] & 0x1f); + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + /* Octet 4 */ + pie->TransMode = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->ITR = ieGetOctet(IBuf[Octet + Off] & 0x1f); + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Octet++; + + /* Octet 4.1. Rate multiplier is only present if ITR = Multirate */ + if (pie->ITR == 0x18) { + pie->RateMul = ieGetOctet(IBuf[Octet + Off] & 0x7f); + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Off ++; + } + + /* Octet 5 */ + if ((IBuf[Octet + Off] & 0x60) == 0x20 && Q931MoreIE()) { + pie->Layer1Ident = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->UIL1Prot = ieGetOctet(IBuf[Octet + Off] & 0x1f); + Octet++; + + /* Octet 5a. The octet may be present if ITC is unrestrictd digital info + * and UIL1Prot is either V.110, I.460 and X.30 or V.120. It may also + * be present if ITC = 3.1 kHz audio and UIL1Prot is G.711. + * Bit 8 of Octet 5 = 0 indicates that 5a is present. + */ + + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (((pie->ITC == 0x08) && (pie->UIL1Prot == 0x01 || pie->UIL1Prot == 0x08)) + || ((pie->ITC == 0x10) && (pie->UIL1Prot == 0x02 || pie->UIL1Prot == 0x03))) { + pie->SyncAsync = ieGetOctet((IBuf[Octet + Off] & 0x40) >> 6); + pie->Negot = ieGetOctet((IBuf[Octet + Off] & 0x20) >> 5); + pie->UserRate = ieGetOctet(IBuf[Octet + Off] & 0x1f); + Off ++; + } + else { + /* We have detected bit 8 = 0, but no setting that require the */ + /* additional octets ??? */ + Q931SetError(pTrunk, Q931E_BEARERCAP, 5,Off); + return Q931E_BEARERCAP; + } + + /* Octet 5b. Two different structures used. */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL1Prot == 0x01) { /* ITU V.110, I.460 and X.30 */ + pie->InterRate = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->NIConTx = ieGetOctet((IBuf[Octet + Off] & 0x10) >> 4); + pie->NIConRx = ieGetOctet((IBuf[Octet + Off] & 0x08) >> 3); + pie->FlowCtlTx = ieGetOctet((IBuf[Octet + Off] & 0x04) >> 2); + pie->FlowCtlRx = ieGetOctet((IBuf[Octet + Off] & 0x20) >> 1); + Off++; + } + else if (pie->UIL1Prot == 0x08) { /* ITU V.120 */ + pie->HDR = ieGetOctet((IBuf[Octet + Off] & 0x40) >> 6); + pie->MultiFrame = ieGetOctet((IBuf[Octet + Off] & 0x20) >> 5); + pie->Mode = ieGetOctet((IBuf[Octet + Off] & 0x10) >> 4); + pie->LLInegot = ieGetOctet((IBuf[Octet + Off] & 0x08) >> 3); + pie->Assignor = ieGetOctet((IBuf[Octet + Off] & 0x04) >> 2); + pie->InBandNeg = ieGetOctet((IBuf[Octet + Off] & 0x02) >> 1); + Off++; + } + else { + Q931SetError(pTrunk,Q931E_BEARERCAP, 5,Off); + return Q931E_BEARERCAP; + } + + /* Octet 5c */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->NumStopBits = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->NumDataBits = ieGetOctet((IBuf[Octet + Off] & 0x18) >> 3); + pie->Parity = ieGetOctet(IBuf[Octet + Off] & 0x07); + Off++; + + /* Octet 5d */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->DuplexMode = ieGetOctet((IBuf[Octet + Off] & 0x40) >> 6); + pie->ModemType = ieGetOctet(IBuf[Octet + Off] & 0x3f); + Off ++; + } + } + } + } + } + + /* Octet 6 */ + if ((IBuf[Octet + Off] & 0x60) == 0x40 && Q931MoreIE()) { + pie->Layer2Ident = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->UIL2Prot = ieGetOctet(IBuf[Octet + Off] & 0x1f); + + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Octet ++; + } + + /* Octet 7 */ + if ((IBuf[Octet + Off] & 0x60) == 0x60 && Q931MoreIE()) { + pie->Layer3Ident = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->UIL3Prot = ieGetOctet(IBuf[Octet + Off] & 0x1f); + Octet++; + + /* Octet 7a */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL3Prot == 0x0c) { + pie->AL3Info1 = ieGetOctet(IBuf[Octet + Off] & 0x0f); + Off++; + + /* Octet 7b */ + if (IsQ931Ext(IBuf[Octet + Off])) { + pie->AL3Info2 = ieGetOctet(IBuf[Octet + Off] & 0x0f); + Off++; + } + } + else { + Q931SetError(pTrunk,Q931E_BEARERCAP, 7, Off); + return Q931E_BEARERCAP; + + } + } + } + + Q931IESizeTest(Q931E_BEARERCAP); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_BearerCap); + pie->Size = sizeof(Q931ie_BearerCap); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_BearerCap + + Description: Packing a Q.931 Bearer Capability element from a generic + struct into a packed octet structure in accordance with the + standard. + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ + +L3INT Q931Pie_BearerCap(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_BearerCap *pIE = (Q931ie_BearerCap*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; /* remember current offset */ + L3INT li; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Encoding Bearer Capability IE\n"); + + OBuf[(*Octet)++] = Q931ie_BEARER_CAPABILITY ; + li = (*Octet)++; /* remember length position */ + + /* Octet 3 - Coding standard / Information transfer capability */ + OBuf[(*Octet)++] = 0x80 | ((pIE->CodStand << 5) & 0x60) | (pIE->ITC & 0x1f); + + /* Octet 4 - Transfer mode / Information transfer rate */ + OBuf[(*Octet)++] = 0x80 | ((pIE->TransMode << 5) & 0x60) | (pIE->ITR & 0x1f); + + if (pIE->ITR == 0x18) { + /* Octet 4.1 - Rate Multiplier */ + OBuf[(*Octet)++] = 0x80 | (pIE->RateMul & 0x7f); + } + + /* Octet 5 - Layer 1 Ident / User information layer 1 protocol */ + if (pIE->Layer1Ident == 0x01) { + if (((pIE->ITC == 0x08) && (pIE->UIL1Prot == 0x01 || pIE->UIL1Prot == 0x08)) || + ((pIE->ITC == 0x10) && (pIE->UIL1Prot == 0x02 || pIE->UIL1Prot == 0x03))) { + OBuf[(*Octet)++] = 0x00 | ((pIE->Layer1Ident << 5) & 0x60) | (pIE->UIL1Prot & 0x15); + + /* Octet 5a - SyncAsync/Negot/UserRate */ + OBuf[(*Octet)++] = 0x00 | ((pIE->SyncAsync << 6) & 0x40) | ((pIE->Negot << 5) & 0x20) | (pIE->UserRate & 0x1f); + + /* Octet 5b - one of two types */ + if (pIE->UIL1Prot == 0x01) { /* ITU V.110, I.460 and X.30 */ + /* Octet 5b - Intermed rate/ Nic on Tx/Nix on Rx/FlowCtlTx/FlowCtlRx */ + OBuf[(*Octet)++] = 0x00 + | ((pIE->InterRate << 6) & 0x60) + | ((pIE->NIConTx << 4) & 0x10) + | ((pIE->NIConRx << 3) & 0x08) + | ((pIE->FlowCtlTx << 2) & 0x04) + | ((pIE->FlowCtlRx << 1) & 0x02); + } + else if (pIE->UIL1Prot == 0x08) { /* ITU V.120 */ + /* Octet 5b - HDR/Multiframe/Mode/LLINegot/Assignor/Inbandneg*/ + OBuf[(*Octet)++] = 0x00 + | ((pIE->InterRate << 6) & 0x60) + | ((pIE->MultiFrame << 5) & 0x20) + | ((pIE->Mode << 4) & 0x10) + | ((pIE->LLInegot << 3) & 0x08) + | ((pIE->Assignor << 2) & 0x04) + | ((pIE->InBandNeg << 1) & 0x02); + } + + /* Octet 5c - NumStopBits/NumStartBits/Parity */ + OBuf[(*Octet)++] = 0x00 + | ((pIE->NumStopBits << 5) & 0x60) + | ((pIE->NumDataBits << 3) & 0x18) + | (pIE->Parity & 0x07); + + /* Octet 5d - Duplex Mode/Modem Type */ + OBuf[(*Octet)++] = 0x80 | ((pIE->DuplexMode << 6) & 0x40) | (pIE->ModemType & 0x3f); + } + else { + OBuf[(*Octet)++] = 0x80 | ((pIE->Layer1Ident << 5) & 0x60) | (pIE->UIL1Prot & 0x1f); + } + } + + /* Octet 6 - Layer2Ident/User information layer 2 prtocol */ + if (pIE->Layer2Ident == 0x02) { + OBuf[(*Octet)++] = 0x80 | ((pIE->Layer2Ident << 5) & 0x60) | (pIE->UIL2Prot & 0x1f); + } + + /* Octet 7 - Layer 3 Ident/ User information layer 3 protocol */ + if (pIE->Layer3Ident == 0x03) { + if (pIE->UIL3Prot == 0x0c) { + OBuf[(*Octet)++] = 0x00 | ((pIE->Layer3Ident << 5) & 0x60) | (pIE->UIL3Prot & 0x1f); + + /* Octet 7a - Additional information layer 3 msb */ + OBuf[(*Octet)++] = 0x00 | (pIE->AL3Info1 & 0x0f); + + /* Octet 7b - Additional information layer 3 lsb */ + OBuf[(*Octet)++] = 0x80 | (pIE->AL3Info2 & 0x0f); + } + else { + OBuf[(*Octet)++] = 0x80 | ((pIE->Layer3Ident << 5) & 0x60) | (pIE->UIL3Prot & 0x1f); + } + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CallID + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CallID(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CallID *pie = (Q931ie_CallID*)OBuf; + ie *pIE = &pMsg->CallID; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x = 0; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + do { + pie->CallId[x] = IBuf[Octet + Off] & 0x7f; + Off++; + x++; + } while (Q931MoreIE()); + + Q931IESizeTest(Q931E_CALLID); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallID) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CallID) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CallID + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ + +L3INT Q931Pie_CallID(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CallID *pIE = (Q931ie_CallID*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet;/* remember current offset */ + L3INT li; + L3INT sCI = pIE->Size - sizeof(Q931ie_CallID) + 1; + L3INT x; + + OBuf[(*Octet)++] = Q931ie_CALL_IDENTITY ; + li = (*Octet)++; /* remember length position */ + + for (x = 0; x < sCI; x++) { + OBuf[(*Octet)++] = pIE->CallId[x]; + } + + OBuf[(*Octet) - 1] |= 0x80; /* set complete flag at last octet*/ + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CallState + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CallState(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CallState *pie = (Q931ie_CallState*)OBuf; + ie *pIE = &pMsg->CallState; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->CodStand = (IBuf[Octet + Off] >> 6) & 0x03; + pie->CallState = IBuf[Octet + Off] & 0x3f; + Octet++; + + Q931IESizeTest(Q931E_CALLSTATE); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallState); + pie->Size = sizeof(Q931ie_CallState); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CallState + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CallState(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CallState *pIE = (Q931ie_CallState*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; /* remember current offset */ + L3INT li; + + OBuf[(*Octet)++] = Q931ie_CALL_STATE; + li = (*Octet)++; /* remember length position */ + + OBuf[(*Octet)++] = (pIE->CodStand << 6) | (pIE->CallState & 0x3f); + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CalledSub + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CalledSub(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CalledSub *pie = (Q931ie_CalledSub*)OBuf; + ie *pIE = &pMsg->CalledSub; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->TypNum = (IBuf[Octet + Off] >> 4) & 0x07; + pie->OddEvenInd = (IBuf[Octet + Off] >> 3) & 0x01; + Octet++; + + /* Octet 4 */ + x = 0; + do { + pie->Digit[x] = IBuf[Octet + Off] & 0x7f; + Off++; + x++; + } while (Q931MoreIE() && x < 20); + + Q931IESizeTest(Q931E_CALLEDSUB); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CalledSub) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CalledSub) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CalledSub + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CalledSub(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CalledSub *pIE = (Q931ie_CalledSub*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT sN = pIE->Size - sizeof(Q931ie_CalledSub) + 1; + L3INT x; + + /* Octet 1 */ + OBuf[(*Octet)++] = Q931ie_CALLED_PARTY_SUBADDRESS; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TypNum << 4) | (pIE->OddEvenInd << 3); + + /* Octet 4 */ + for (x = 0; xDigit[x]; + } + + OBuf[(*Octet) - 1] |= 0x80; /* Terminate bit */ + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CalledNum + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CalledNum(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CalledNum *pie = (Q931ie_CalledNum*)OBuf; + ie *pIE = &pMsg->CalledNum; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; /* # digits in this case */ + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->TypNum = (IBuf[Octet + Off] >> 4) & 0x07; + pie->NumPlanID = IBuf[Octet + Off] & 0x0f; + Octet++; + + /* Octet 4*/ + x = 0; + do { + pie->Digit[x] = IBuf[Octet + Off] & 0x7f; + Off++; + x++; + } while ((IBuf[Octet + Off]&0x80) == 0 && Q931MoreIE()); + + pie->Digit[x] = '\0'; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CalledNum) + x; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CalledNum) + x); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CalledNum + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CalledNum(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CalledNum *pIE = (Q931ie_CalledNum*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT sN = pIE->Size - sizeof(Q931ie_CalledNum); + L3INT x; + + /* Octet 1 */ + OBuf[(*Octet)++] = Q931ie_CALLED_PARTY_NUMBER; + + /* Octet 2 */ + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TypNum << 4) | (pIE->NumPlanID); + + /* Octet 4 */ + for (x = 0; xDigit[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CallingNum + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CallingNum(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CallingNum *pie = (Q931ie_CallingNum*)OBuf; + ie *pIE = &pMsg->CallingNum; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->TypNum = (IBuf[Octet + Off] >> 4) & 0x07; + pie->NumPlanID = IBuf[Octet + Off] & 0x0f; + + /* Octet 3a */ + if ((IBuf[Octet + Off] & 0x80) == 0) { + Off++; + pie->PresInd = (IBuf[Octet + Off] >> 5) & 0x03; + pie->ScreenInd = IBuf[Octet + Off] & 0x03; + } + Octet++; + + /* Octet 4 */ + x = 0; + while (Q931MoreIE()) { + pie->Digit[x++] = IBuf[Octet + Off] & 0x7f; + + if ((IBuf[Octet + Off] & 0x80) != 0) { + break; + } + Off++; + } + pie->Digit[x] = '\0'; + + Q931IESizeTest(Q931E_CALLINGNUM); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallingNum) + x; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CallingNum) + x); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CallingNum + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CallingNum(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CallingNum *pIE = (Q931ie_CallingNum*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT sN = pIE->Size - sizeof(Q931ie_CallingNum); + L3INT x; + + /* Octet 1 */ + OBuf[(*Octet)++] = Q931ie_CALLING_PARTY_NUMBER; + + /* Octet 2 */ + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x00 | (pIE->TypNum << 4) | (pIE->NumPlanID); + + /* Octet 4 */ + OBuf[(*Octet)++] = 0x80; + + /* Octet 5 */ + for (x = 0; xDigit[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CallingSub + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CallingSub(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CallingSub *pie = (Q931ie_CallingSub*)OBuf; + ie *pIE = &pMsg->CallingSub; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->TypNum = (IBuf[Octet + Off] >> 4) & 0x07; + pie->OddEvenInd = (IBuf[Octet + Off] >> 3) & 0x01; + Octet++; + + /* Octet 4*/ + x = 0; + do { + pie->Digit[x] = IBuf[Octet + Off] & 0x7f; + Off++; + x++; + } while (Q931MoreIE() && x < 20); + + Q931IESizeTest(Q931E_CALLINGSUB); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallingSub) + x -1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CallingSub) + x -1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CallingSub + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CallingSub(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CallingSub *pIE = (Q931ie_CallingSub*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT sN = pIE->Size - sizeof(Q931ie_CallingSub) + 1; + L3INT x; + + /* Octet 1 */ + OBuf[(*Octet)++] = Q931ie_CALLING_PARTY_SUBADDRESS; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TypNum << 4) | (pIE->OddEvenInd << 3); + + /* Octet 4 */ + for (x = 0; xDigit[x]; + } + + OBuf[(*Octet) - 1] |= 0x80; /* Terminate bit */ + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_Cause + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_Cause(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_Cause *pie = (Q931ie_Cause*)OBuf; + ie *pIE = &pMsg->Cause; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2*/ + IESize = IBuf[Octet++]; + + /* Octet 3*/ + pie->CodStand = (IBuf[Octet + Off]>>5) & 0x03; + pie->Location = IBuf[Octet + Off] & 0x0f; + + /* Octet 3a */ + if ((IBuf[Octet + Off] & 0x80) == 0) { + Off++; + pie->Recom = IBuf[Octet + Off] & 0x7f; + } + Octet++; + + /* Octet 4 */ + pie->Value = IBuf[Octet + Off] & 0x7f; + Octet++; + + /* Consume optional Diagnostic bytes */ + while (Q931MoreIE()) { + Off++; + }; + + Q931IESizeTest(Q931E_CAUSE); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_Cause); + pie->Size = sizeof(Q931ie_Cause); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_Cause + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_Cause(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_Cause *pIE = (Q931ie_Cause*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_CAUSE; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->CodStand<<5) | pIE->Location; + + /* Octet 3a - currently not supported in send */ + + /* Octet 4 */ + OBuf[(*Octet)++] = 0x80 | pIE->Value; + + /* Octet 5 - diagnostics not supported in send */ + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CongLevel + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CongLevel(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CongLevel *pie = (Q931ie_CongLevel*)OBuf; + ie *pIE = &pMsg->CongestionLevel; + L3INT Off = 0; + L3INT Octet = 0; + + *pIE = 0; + + pie->IEId = IBuf[Octet] & 0xf0; + pie->CongLevel = IBuf[Octet] & 0x0f; + Octet ++; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CongLevel); + pie->Size = sizeof(Q931ie_CongLevel); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CongLevel + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CongLevel(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CongLevel *pIE = (Q931ie_CongLevel*)IBuf; + L3INT rc = 0; + /* L3INT Beg = *Octet; */ + + OBuf[(*Octet)++] = Q931ie_CONGESTION_LEVEL | pIE->CongLevel; + + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_ChanID + + Parameters: IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Uie_ChanID(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_ChanID *pie = (Q931ie_ChanID*)OBuf; + ie *pIE = &pMsg->ChanID; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; +//18 04 e1 80 83 01 + *pIE = 0; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Decoding ChanID IE\n"); + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->IntIDPresent = (IBuf[Octet] >> 6) & 0x01; + pie->IntType = (IBuf[Octet] >> 5) & 0x01; + pie->PrefExcl = (IBuf[Octet] >> 3) & 0x01; + pie->DChanInd = (IBuf[Octet] >> 2) & 0x01; + pie->InfoChanSel = IBuf[Octet] & 0x03; + + Off = Q931ReadExt(&IBuf[Octet++], Off); + + /* Octet 3.1 */ + if (pie->IntIDPresent) { + pie->InterfaceID = IBuf[Octet + Off] & 0x7f; + + /* Temp fix. Interface id can be extended using the extension bit */ + /* this will read the octets, but do nothing with them. this is done */ + /* because the usage of this field is a little unclear */ + /* 30.jan.2001/JVB */ + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Off++; + } + + if ((Octet + Off - 2) != IESize) { + /* Octet 3.2 */ + if (pie->IntType == 1) { /* PRI etc */ + pie->CodStand = (IBuf[Octet + Off] >> 5) & 0x03; + pie->NumMap = (IBuf[Octet + Off] >> 4) & 0x01; + pie->ChanMapType = IBuf[Octet + Off] & 0x0f; + Off++; + + /* Octet 3.3 */ + /* Temp fix. Assume B channel. H channels not supported */ + pie->ChanSlot = IBuf[Octet + Off] & 0x7f; + + /* Some dialects don't follow the extension coding properly for this, but this should be safe for all */ + if ((Octet + Off - 1) != IESize) { + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + } + Off++; + } + } + + Q931IESizeTest(Q931E_CHANID); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_ChanID); + pie->Size = sizeof(Q931ie_ChanID); + + if (pTrunk->loglevel == Q931_LOG_DEBUG) { + const char *iface; + char tmp[100] = ""; + + if (!pie->IntType) { + switch (pie->InfoChanSel) { + case 0x0: + iface = "None"; + break; + case 0x1: + iface = "B1"; + break; + case 0x2: + iface = "B2"; + break; + default: + iface = "Any Channel"; + } + + snprintf(tmp, sizeof(tmp)-1, "InfoChanSel: %d (%s)", pie->InfoChanSel, iface); + } + + Q931Log(pTrunk, Q931_LOG_DEBUG, + "\n-------------------------- Q.931 Channel ID ------------------------\n" + " Pref/Excl: %s, Interface Type: %s\n" + " %s\n" + "--------------------------------------------------------------------\n\n", + ((pie->PrefExcl) ? "Preferred" : "Exclusive"), + ((pie->IntType) ? "PRI/Other" : "BRI"), + tmp); + } + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_ChanID + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_ChanID(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_ChanID *pIE = (Q931ie_ChanID*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; /* remember current offset */ + L3INT li; + + OBuf[(*Octet)++] = Q931ie_CHANNEL_IDENTIFICATION; + li = (*Octet)++; /* remember length position */ + + /* Octet 3 flags & BRI chan # */ + OBuf[(*Octet)++] = 0x80 + | ((pIE->IntIDPresent << 6) & 0x40) + | ((pIE->IntType << 5) & 0x20) + | ((pIE->PrefExcl << 3) & 0x08) + | (pIE->InfoChanSel & 0x03); + + /* Octet 3.1 - Interface Identifier */ + if (pIE->IntIDPresent) { + OBuf[(*Octet)++] = 0x80 | (pIE->InterfaceID & 0x7f); + } + + /* Octet 3.2 & 3.3 - PRI */ + if (pIE->IntType) { + OBuf[(*Octet)++] = 0x80 + | ((pIE->CodStand << 5) & 0x60) + | ((pIE->NumMap << 4) & 0x10) + | (pIE->ChanMapType & 0x0f); /* TODO: support all possible channel map types */ + + /* Octet 3.3 Channel number */ + switch (pIE->ChanMapType) { + case 0x6: /* Slot map: H0 Channel Units */ /* unsupported, Octets 3.3.1 - 3.3.3 */ + return Q931E_CHANID; + + case 0x8: /* Slot map: H11 Channel Units */ + case 0x9: /* Slot map: H12 Channel Units */ + default: /* Channel number */ + OBuf[(*Octet)++] = 0x80 | (pIE->ChanSlot & 0x7f); + break; + } + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + + +/***************************************************************************** + + Function: Q931Uie_CRV + + Description: Reading CRV. + + The CRV is currently returned in the return value that + Q921Rx23 will assign to the CRV field in the unpacked + message. CRV is basically 2 bytes etc, but the spec allows + the use of longer CRV values. + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: CRV + +*****************************************************************************/ +L3USHORT Q931Uie_CRV(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff) +{ + L3USHORT CRV = 0; + L3INT Octet = *IOff; + L3INT l = IBuf[Octet++]; + + if (l == 1) { /* One octet CRV */ + CRV = IBuf[Octet++] & 0x7F; + } + else if (l == 2) { /* two octet CRV */ + CRV = (IBuf[Octet++] & 0x7f) << 8; + CRV |= IBuf[Octet++]; + } + else { + /* Long CRV is not used, so we skip this */ + /* TODO: is it right to set to 0 here? */ + CRV = 0; + Octet += l; + } + + *IOff = Octet; + return CRV; +} + +/***************************************************************************** + + Function: Q931Uie_DateTime + + Parameters: pTrunk [IN] Ptr to trunk information. + pIE [OUT] ptr to Information Element id. + IBuf [IN] ptr to a packed ie. + OBuf [OUT] ptr to buffer for Unpacked ie. + IOff [IN\OUT] Input buffer offset + OOff [IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_DateTime(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_DateTime * pie = (Q931ie_DateTime*)OBuf; + ie *pIE = &pMsg->DateTime; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize = 0; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 - Year */ + pie->Year = IBuf[Octet++]; + + /* Octet 4 - Month */ + pie->Month = IBuf[Octet++]; + + /* Octet 5 - Day */ + pie->Day = IBuf[Octet++]; + + /******************************************************************* + The remaining part of the IE are optioinal, but only the length + can now tell us wherever these fields are present or not + (always remember: IESize does not include ID and Size octet) + ********************************************************************/ + pie->Format = 0; + + /* Octet 6 - Hour (optional)*/ + if (IESize >= 4) { + pie->Format = 1; + pie->Hour = IBuf[Octet++]; + + /* Octet 7 - Minute (optional)*/ + if (IESize >= 5) { + pie->Format = 2; + pie->Minute = IBuf[Octet++]; + + /* Octet 8 - Second (optional)*/ + if (IESize >= 6) { + pie->Format = 3; + pie->Second = IBuf[Octet++]; + } + } + } + + Q931IESizeTest(Q931E_DATETIME); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_DateTime); + pie->Size = sizeof(Q931ie_DateTime); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_DateTime + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_DateTime(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_DateTime *pIE = (Q931ie_DateTime*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_DATETIME; + li = (*Octet)++; + + OBuf[(*Octet)++] = pIE->Year; + OBuf[(*Octet)++] = pIE->Month; + OBuf[(*Octet)++] = pIE->Day; + if (pIE->Format >= 1) { + OBuf[(*Octet)++] = pIE->Hour; + + if (pIE->Format >= 2) { + OBuf[(*Octet)++] = pIE->Minute; + + if (pIE->Format >= 3) { + OBuf[(*Octet)++] = pIE->Second; + } + } + } + + OBuf[li] = (L3UCHAR)((*Octet)-Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_Display + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_Display(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_Display *pie = (Q931ie_Display*)OBuf; + ie *pIE = &pMsg->Display; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + L3INT x; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + IESize = IBuf[Octet++]; + + for (x = 0; xDisplay[x] = IBuf[Octet + Off] & 0x7f; + Off++; + } + + Q931IESizeTest(Q931E_DISPLAY); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_Display) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_Display) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_Display + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_Display(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_Display *pIE = (Q931ie_Display*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT DSize; + L3INT x; + + OBuf[(*Octet)++] = Q931ie_DISPLAY; + li = (*Octet)++; + + DSize = pIE->Size - sizeof(Q931ie_Display); + + for (x = 0; x< DSize; x++) { + + OBuf[(*Octet)++] = pIE->Display[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_HLComp + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_HLComp(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_HLComp * pie = (Q931ie_HLComp*)OBuf; + ie *pIE = &pMsg->HLComp; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet */ + IESize = IBuf[Octet++]; + + /* Octet 3*/ + pie->CodStand = (IBuf[Octet + Off] >>5) & 0x03; + pie->Interpret = (IBuf[Octet + Off] >>2) & 0x07; + pie->PresMeth = IBuf[Octet + Off] & 0x03; + Octet++; + + /* Octet 4 */ + pie->HLCharID = IBuf[Octet + Off] & 0x7f; + Octet++; + + /* Octet 4a*/ + if ((IBuf[Octet + Off - 1] & 0x80) == 0 && Q931MoreIE()) { + if (pie->HLCharID == 0x5e || pie->HLCharID == 0x5f) { + pie->EHLCharID = IBuf[Octet + Off] & 0x7f; + Off++; + } + else if ( pie->HLCharID >= 0xc3 && pie->HLCharID <= 0xcf) { + pie->EVideoTlfCharID = IBuf[Octet + Off] & 0x7f; + Off++; + } + else { + /* error Octet 4a indicated, but invalid value in Octet 4. */ + Q931SetError(pTrunk,Q931E_HLCOMP, 4, Off); + return Q931E_HLCOMP; + } + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + } + + Q931IESizeTest(Q931E_HLCOMP); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_HLComp); + pie->Size = sizeof(Q931ie_HLComp); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_HLComp + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_HLComp(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_HLComp *pIE = (Q931ie_HLComp*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_HIGH_LAYER_COMPATIBILITY; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | ((pIE->CodStand << 5) & 0x60) | ((pIE->Interpret << 2) & 0x1c) | (pIE->PresMeth & 0x03); + + /* Octet 4 */ + OBuf[(*Octet)++] = pIE->HLCharID; + + /* Octet 4a */ + if (pIE->HLCharID == 0x5e || pIE->HLCharID == 0x5f) { + OBuf[(*Octet)++] = 0x80 | (pIE->EHLCharID & 0x7f); + } + else if ( pIE->HLCharID >= 0xc3 && pIE->HLCharID <= 0xcf) { + OBuf[(*Octet)++] = 0x80 | (pIE->EVideoTlfCharID & 0x7f); + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_KeypadFac + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_KeypadFac(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_KeypadFac *pie = (Q931ie_KeypadFac*)OBuf; + ie *pIE = &pMsg->KeypadFac; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + L3INT x; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + IESize = IBuf[Octet++]; + + for (x = 0; xKeypadFac[x] = IBuf[Octet + Off] & 0x7f; + Off++; + } + + Q931IESizeTest(Q931E_KEYPADFAC); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_KeypadFac) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_KeypadFac) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_KeypadFac + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_KeypadFac(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_KeypadFac *pIE = (Q931ie_KeypadFac*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT DSize; + L3INT x; + + OBuf[(*Octet)++] = Q931ie_KEYPAD_FACILITY; + li = (*Octet)++; + + DSize = pIE->Size - sizeof(Q931ie_KeypadFac) + 1; + + for (x = 0; x< DSize; x++) { + OBuf[(*Octet)++] = pIE->KeypadFac[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_LLComp + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_LLComp(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_LLComp *pie = (Q931ie_LLComp*)OBuf; + ie *pIE = &pMsg->LLComp; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->CodStand = (IBuf[Octet + Off] >> 5) & 0x03; + pie->ITransCap = IBuf[Octet + Off] & 0x1f; + Octet++; + + /* Octet 3a*/ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->NegotInd = (IBuf[Octet + Off] >> 6) & 0x01; + Off++; + } + + /* Octet 4 */ + pie->TransMode = (IBuf[Octet + Off] >> 5) & 0x03; + pie->InfoRate = IBuf[Octet + Off] & 0x1f; + + Octet++; + + /* Octet 4.1 */ + if (pie->InfoRate == 0x14) { /* Mutirate */ + pie->RateMul = IBuf[Octet + Off] & 0x7f; + Off++; + } + + /* Octet 5 - Layer 1 Ident */ + if ((IBuf[Octet + Off] & 0x60) == 0x20) { /* Layer 1 Ident ? */ + pie->Layer1Ident = (IBuf[Octet + Off] >> 5) & 0x03; + pie->UIL1Prot = IBuf[Octet + Off] & 0x1f; + Octet++; + + /* Octet 5a */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->SyncAsync = (IBuf[Octet + Off] >> 6) & 0x01; + pie->Negot = (IBuf[Octet + Off] >> 5) & 0x01; + pie->UserRate = IBuf[Octet + Off] & 0x1f; + Off++; + + /* Octet 5b - 2 options */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL1Prot == 0x01) { /* V.110, I.460 and X.30*/ + pie->InterRate = (IBuf[Octet + Off] >> 5) & 0x03; + pie->NIConTx = (IBuf[Octet + Off] >> 4) & 0x01; + pie->NIConRx = (IBuf[Octet + Off] >> 3) & 0x01; + pie->FlowCtlTx = (IBuf[Octet + Off] >> 2) & 0x01; + pie->FlowCtlRx = (IBuf[Octet + Off] >> 1) & 0x01; + Off++; + } + else if (pie->UIL1Prot == 0x80) { /* V.120 */ + pie->HDR = (IBuf[Octet + Off] >> 6) & 0x01; + pie->MultiFrame = (IBuf[Octet + Off] >> 5) & 0x01; + pie->ModeL1 = (IBuf[Octet + Off] >> 4) & 0x01; + pie->NegotLLI = (IBuf[Octet + Off] >> 3) & 0x01; + pie->Assignor = (IBuf[Octet + Off] >> 2) & 0x01; + pie->InBandNeg = (IBuf[Octet + Off] >> 1) & 0x01; + Off++; + } + else if (pie->UIL1Prot == 0x07) { /* non standard */ + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Off++; + } + else { + Q931SetError(pTrunk,Q931E_LLCOMP, 5,2); + return Q931E_LLCOMP; + } + + /* Octet 5c */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->NumStopBits = (IBuf[Octet + Off] >> 5) & 0x03; + pie->NumDataBits = (IBuf[Octet + Off] >> 3) & 0x03; + pie->Parity = IBuf[Octet + Off] & 0x07; + Off++; + + /* Octet 5d */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->DuplexMode = (IBuf[Octet + Off] >> 6) & 0x01; + pie->ModemType = IBuf[Octet + Off] & 0x3f; + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Off++; + } + } + } + } + } + + /* Octet 6 - Layer 2 Ident */ + if ((IBuf[Octet + Off] & 0x60) == 0x40) { /* Layer 1 Ident ? */ + pie->Layer2Ident = (IBuf[Octet + Off] >>5) & 0x03; + pie->UIL2Prot = IBuf[Octet + Off] & 0x1f; + Octet++; + + /* Octet 6a */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL2Prot == 0x10) { /* 2nd 6a */ + pie->UsrSpcL2Prot = IBuf[Octet + Off] & 0x7f; + Off++; + } + else { /* assume 1st 6a */ + pie->ModeL2 = (IBuf[Octet + Off] >> 5) & 0x03; + pie->Q933use = IBuf[Octet + Off] & 0x03; + Off++; + } + /* Octet 6b */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->WindowSize = IBuf[Octet + Off] & 0x7f; + Off++; + } + } + } + + /* Octet 7 - layer 3 Ident */ + if ((IBuf[Octet + Off] & 0x60) == 0x60) { /* Layer 3 Ident ? */ + pie->Layer3Ident = (IBuf[Octet + Off] >> 5) & 0x03; + pie->UIL3Prot = IBuf[Octet + Off] & 0x1f; + Octet++; + + /* Octet 7a */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL3Prot == 0x0b) { + /* Octet 7a + 7b AddL3Info */ + pie->AddL3Info = ((IBuf[Octet + Off] << 4) & 0xf0) + | (IBuf[Octet + Off + 1] & 0x0f); + Off += 2; + } + else { + if (pie->UIL3Prot == 0x1f) { + pie->ModeL3 = (IBuf[Octet + Off] >> 5) & 0x03; + Off++; + } + else { + pie->OptL3Info = IBuf[Octet + Off] & 0x7f; + Off++; + } + + /* Octet 7b*/ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->DefPackSize = IBuf[Octet + Off] & 0x0f; + Off++; + + /* Octet 7c */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->PackWinSize= IBuf[Octet + Off] & 0x7f; + } + } + } + } + } + + Q931IESizeTest(Q931E_LLCOMP); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_LLComp); + pie->Size = sizeof(Q931ie_LLComp); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_LLComp + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_LLComp(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_LLComp *pIE = (Q931ie_LLComp*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_LOW_LAYER_COMPATIBILITY; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = (pIE->CodStand << 6) | pIE->ITransCap; + + /* Octet 3a */ + OBuf[(*Octet)++] = 0x80 | (pIE->NegotInd << 6); + + /* Octet 4 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TransMode << 5) | pIE->InfoRate; + + /* Octet 4.1 */ + if (pIE->InfoRate == 0x18) { + OBuf[(*Octet)++] = 0x80 | pIE->RateMul; + } + + /* Octet 5 */ + if (pIE->Layer1Ident == 0x01) { + OBuf[(*Octet)++] = (pIE->Layer1Ident << 5) | pIE->UIL1Prot; + + /* Octet 5a */ + if ((pIE->ITransCap == 0x08 && (pIE->UIL1Prot == 0x01 || pIE->UIL1Prot == 0x08)) + || (pIE->ITransCap == 0x10 && (pIE->UIL1Prot == 0x02 || pIE->UIL1Prot == 0x03))) { + OBuf[(*Octet)++] = (pIE->SyncAsync<<6) | (pIE->Negot<<5) | pIE->UserRate; + + /* Octet 5b*/ + if (pIE->UIL1Prot == 0x01) { + OBuf[(*Octet)++] = (pIE->InterRate << 5) + | (pIE->NIConTx << 4) + | (pIE->NIConTx << 3) + | (pIE->FlowCtlTx << 2) + | (pIE->FlowCtlRx << 1); + } + else if (pIE->UIL1Prot == 0x08) { + OBuf[(*Octet)++] = (pIE->HDR << 6) + | (pIE->MultiFrame << 5) + | (pIE->ModeL1 << 4) + | (pIE->NegotLLI << 3) + | (pIE->Assignor << 2) + | (pIE->InBandNeg << 1); + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + + /* How to detect wherever 5c and 5d is to present is not clear + * but they have been inculded as 'standard' + * Octet 5c + */ + if (pIE->UIL1Prot == 0x01 || pIE->UIL1Prot == 0x08) { + OBuf[(*Octet)++] = (pIE->NumStopBits << 5) | (pIE->NumDataBits << 3) | pIE->Parity ; + + /* Octet 5d */ + OBuf[(*Octet)++] = 0x80 | (pIE->DuplexMode << 6) | pIE->ModemType; + } + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + } + + /* Octet 6 */ + if (pIE->Layer2Ident == 0x02) { + OBuf[(*Octet)++] = (pIE->Layer2Ident << 5) | pIE->UIL2Prot; + + /* Octet 6a*/ + if (pIE->UIL2Prot == 0x02 /* Q.921/I.441 */ + || pIE->UIL2Prot == 0x06 /* X.25 link layer */ + || pIE->UIL2Prot == 0x07 /* X.25 multilink */ + || pIE->UIL2Prot == 0x09 /* HDLC ARM */ + || pIE->UIL2Prot == 0x0a /* HDLC NRM */ + || pIE->UIL2Prot == 0x0b /* HDLC ABM */ + || pIE->UIL2Prot == 0x0d /* X.75 SLP */ + || pIE->UIL2Prot == 0x0e /* Q.922 */ + || pIE->UIL2Prot == 0x11) { /* ISO/ECE 7776 DTE-DCE */ + OBuf[(*Octet)++] = (pIE->ModeL2 << 5) | pIE->Q933use; + + /* Octet 6b */ + OBuf[(*Octet)++] = 0x80 | pIE->WindowSize; + } + else if (pIE->UIL2Prot == 0x10) { /* User Specific */ + OBuf[(*Octet)++] = 0x80 | pIE->UsrSpcL2Prot; + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + } + + /* Octet 7 */ + if (pIE->Layer3Ident == 0x03) { + OBuf[(*Octet)++] = (pIE->Layer3Ident << 5) | pIE->UIL3Prot; + + /* Octet 7a - 3 different ones */ + if (pIE->UIL3Prot == 0x10) { + OBuf[(*Octet++)] = 0x80 | pIE->OptL3Info; + } + else if (pIE->UIL3Prot == 0x06 + || pIE->UIL3Prot == 0x07 + || pIE->UIL3Prot == 0x08) { + OBuf[(*Octet)++] = pIE->ModeL3 << 5; + + /* Octet 7b note 7 */ + OBuf[(*Octet)++] = pIE->DefPackSize; + + /* Octet 7c note 7 */ + OBuf[(*Octet)++] = 0x80 | pIE->PackWinSize; + } + else if (pIE->UIL3Prot == 0x0b) { + OBuf[(*Octet)++] = (pIE->AddL3Info >> 4) & 0x0f; + OBuf[(*Octet)++] = 0x80 | (pIE->AddL3Info & 0x0f); + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + } + else { + Q931SetError(pTrunk,Q931E_LLCOMP, 7,0); + rc = Q931E_LLCOMP; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_NetFac + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_NetFac(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_NetFac *pie = (Q931ie_NetFac*)OBuf; + ie *pIE = &pMsg->NetFac; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + pie->LenNetID = IBuf[Octet + Off]; /* full octet is used */ + Octet++; + + if (pie->LenNetID > 0) { + /* Octet 3.1 */ + pie->TypeNetID = (IBuf[Octet + Off] >> 4) & 0x0f; + pie->NetIDPlan = IBuf[Octet + Off] & 0x0f; + Off = Q931ReadExt(&IBuf[Octet], Off); + Off++; + + /* Octet 3.2*/ + for (x = 0; x < pie->LenNetID; x++) { + pie->NetID[x] = IBuf[Octet + Off] & 0x7f; + Off++; + } + } + + /* Octet 4*/ + pie->NetFac = IBuf[Octet + Off]; /* Full Octet is used */ + Octet++; + + Q931IESizeTest(Q931E_NETFAC); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_NetFac) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_NetFac) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_NetFac + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_NetFac(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_NetFac *pIE = (Q931ie_NetFac*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + L3INT x; + + OBuf[(*Octet)++] = Q931ie_NETWORK_SPECIFIC_FACILITIES; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = pIE->LenNetID; + + if (pIE->LenNetID > 0) { + /* Octet 3.1 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TypeNetID << 4) | pIE->NetIDPlan; + + /* Octet 3.2 */ + for (x = 0; x LenNetID; x++) { + OBuf[(*Octet)++] = pIE->NetID[x]; + } + } + + /* Octet 4 */ + OBuf[(*Octet)++] = pIE->NetFac; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_NotifInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_NotifInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_NotifInd *pie = (Q931ie_NotifInd*)OBuf; + ie *pIE = &pMsg->NotifInd; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2*/ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Notification = IBuf[Octet + Off] & 0x7f; + + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + Q931IESizeTest(Q931E_NOTIFIND); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_NotifInd); + pie->Size = sizeof(Q931ie_NotifInd); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_NotifInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_NotifInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_NotifInd *pIE = (Q931ie_NotifInd*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_NOTIFICATION_INDICATOR; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = pIE->Notification; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_ProgInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_ProgInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_ProgInd *pie = (Q931ie_ProgInd*)OBuf; + ie *pIE = &pMsg->ProgInd; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->CodStand = (IBuf[Octet + Off] >> 5) & 0x03; + pie->Location = IBuf[Octet + Off] & 0x0f; + + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + /* Octet 4 */ + pie->ProgDesc = IBuf[Octet + Off] & 0x7f; + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + Q931IESizeTest(Q931E_PROGIND); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_ProgInd); + pie->Size = sizeof(Q931ie_ProgInd); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_ProgInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset L3INTo OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_ProgInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_ProgInd *pIE = (Q931ie_ProgInd*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_PROGRESS_INDICATOR; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->CodStand << 5) | pIE->Location; + + /* Octet 4 */ + OBuf[(*Octet)++] = 0x80 | pIE->ProgDesc; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_RepeatInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_RepeatInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_RepeatInd *pie = (Q931ie_RepeatInd*)OBuf; + ie *pIE = &pMsg->RepeatInd; + L3INT Off = 0; + L3INT Octet = 0; + + *pIE = 0; + + pie->IEId = IBuf[Octet] & 0xf0; + pie->RepeatInd = IBuf[Octet] & 0x0f; + Octet ++; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_RepeatInd); + pie->Size = sizeof(Q931ie_RepeatInd); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_RepeatInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_RepeatInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_RepeatInd *pIE = (Q931ie_RepeatInd*)IBuf; + L3INT rc = 0; + /* L3INT Beg = *Octet; */ + + OBuf[(*Octet)++] = Q931ie_REPEAT_INDICATOR | pIE->RepeatInd; + + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_RevChargeInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_RevChargeInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + ie iE; + /* ie *pIE = &pMsg->RevChargeInd; */ + Q931SetIE(iE, *OOff); + + return iE; +} + +/***************************************************************************** + + Function: Q931Pie_RevChargeInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_RevChargeInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Uie_RestartInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_RestartInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_RestartInd *pie = (Q931ie_RestartInd*)OBuf; + ie *pIE = &pMsg->RestartInd; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Class = IBuf[Octet + Off] & 0x07; + pie->Spare = IBuf[Octet + Off] & 0x78; + + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + Q931IESizeTest(Q931E_RESTARTIND); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_RestartInd); + pie->Size = sizeof(Q931ie_RestartInd); + + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_RestartInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_RestartInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_RestartInd *pIE = (Q931ie_RestartInd*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_RESTART_INDICATOR; + li = (*Octet)++; + + /* Octet 3*/ + OBuf[(*Octet)++] = 0x80 | pIE->Class ; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_Segment + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_Segment(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_Segment *pie = (Q931ie_Segment*)OBuf; + ie *pIE = &pMsg->Segment; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + Octet++; + + /* Octet 2*/ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->FSI = (IBuf[Octet + Off] & 0x80) >> 7; + pie->NumSegRem = IBuf[Octet + Off] & 0x7f; + Octet++; + + /* Octet 4 */ + pie->SegType = IBuf[Octet + Off] & 0x7f; + Octet++; + + Q931IESizeTest(Q931E_SEGMENT); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_Segment); + pie->Size = sizeof(Q931ie_Segment); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_Segment + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_Segment(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_Segment *pIE = (Q931ie_Segment*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_SEGMENTED_MESSAGE; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = (pIE->FSI << 7) | pIE->NumSegRem; + + /* Octet 4 */ + OBuf[(*Octet)++] = pIE->SegType; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_SendComplete + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_SendComplete(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_SendComplete *pie = (Q931ie_SendComplete*)OBuf; + ie *pIE = &pMsg->SendComplete; + L3INT Off = 0; + L3INT Octet = 0; + + *pIE = 0; + Octet++; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_SendComplete); + pie->Size = sizeof(Q931ie_SendComplete); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_ProgInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_SendComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + /* Q931ie_SendComplete * pIE = (Q931ie_SendComplete*)IBuf; */ + L3INT rc = Q931E_NO_ERROR; + /* L3INT Beg = *Octet; */ + + OBuf[(*Octet)++] = 0x80 | (L3UCHAR)Q931ie_SENDING_COMPLETE; + + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_Signal + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_Signal(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_Signal *pie = (Q931ie_Signal*)OBuf; + ie *pIE = &pMsg->Signal; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Signal = IBuf[Octet + Off]; + Octet++; + + Q931IESizeTest(Q931E_SIGNAL); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_Signal); + pie->Size = sizeof(Q931ie_Signal); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_Signal + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_Signal(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_Signal *pIE = (Q931ie_Signal*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_SIGNAL; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = pIE->Signal; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_TransNetSel + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_TransNetSel(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_TransNetSel *pie = (Q931ie_TransNetSel*)OBuf; + ie *pIE = &pMsg->TransNetSel; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x = 0; + L3INT l; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + l = IBuf[Octet++] - 3; + + /* Octet 3 */ + pie->Type = (IBuf[Octet + Off] >> 4) & 0x07; + + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + for (x = 0; x < l; x++) { + pie->NetID[x] = IBuf[Octet + Off] & 0x7f; + Off++; + } + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_TransNetSel) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_TransNetSel) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_TransNetSel + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_TransNetSel(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_TransNetSel *pIE = (Q931ie_TransNetSel*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + L3INT x; + L3INT l; + + OBuf[(*Octet)++] = Q931ie_TRANSIT_NETWORK_SELECTION; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->Type << 4) | pIE->NetIDPlan; + + /* Octet 4 */ + l = pIE->Size - sizeof(Q931ie_TransNetSel) + 1; + for (x = 0; x < l; x++) { + OBuf[(*Octet)++] = pIE->NetID[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_UserUser + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_UserUser(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_UserUser *pie = (Q931ie_UserUser*)OBuf; + ie *pIE = &pMsg->UserUser; + L3INT Off = 0; + L3INT Octet = 0; + L3INT l; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + l = IBuf[Octet++] - 1; + + /* Octet 3 */ + pie->ProtDisc = IBuf[Octet++]; + + for (Off = 0; Off < l; Off++) { + pie->User[Off] = IBuf[Octet + Off]; + } + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_UserUser) + Off - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_UserUser) + Off - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_UserUser + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_UserUser(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_UserUser *pIE = (Q931ie_UserUser*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + L3INT x; + L3INT l; + + OBuf[(*Octet)++] = Q931ie_USER_USER; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = pIE->ProtDisc; + + /* Octet 4 */ + l = pIE->Size - sizeof(Q931ie_UserUser) + 1; + for (x = 0; x < l; x++) { + OBuf[(*Octet)++] = pIE->User[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_GenericDigits + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_GenericDigits(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_GenericDigits *pie = (Q931ie_GenericDigits*)OBuf; + ie *pIE = &pMsg->GenericDigits; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Type = (IBuf[Octet]) & 0x1F; + pie->Encoding = (IBuf[Octet] >> 5) & 0x07; + Octet++; + + /* Octet 4*/ + if (pie->Encoding == 0) { /* BCD Even */ + x = 0; + do { + pie->Digit[x++] = IBuf[Octet + Off] & 0x0f; + pie->Digit[x++] = (IBuf[Octet + Off] >> 4) & 0x0f; + Off++; + } while (Q931MoreIE()); + } else if (pie->Encoding == 1) { /* BCD Odd */ + x = 0; + do { + pie->Digit[x++] = IBuf[Octet + Off] & 0x0f; + if (Q931MoreIE()) { + pie->Digit[x] = (IBuf[Octet + Off] >> 4) & 0x0f; + } + x++; + Off++; + } while (Q931MoreIE()); + } else if (pie->Encoding == 2) { /* IA5 */ + x = 0; + do { + pie->Digit[x++] = IBuf[Octet + Off] & 0x7f; + Off++; + } while (Q931MoreIE()); + } else { + /* Binary encoding type unkown */ + Q931SetError(pTrunk, Q931E_GENERIC_DIGITS, Octet, Off); + return Q931E_GENERIC_DIGITS; + } + + Q931IESizeTest(Q931E_GENERIC_DIGITS); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallingSub) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CallingSub) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_GenericDigits + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ + +L3INT Q931Pie_GenericDigits(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + OBuf[(*Octet)++] = (Q931ie_GENERIC_DIGITS & 0xFF); + OBuf[(*Octet)++] = 0; + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Uie_ChangeStatus + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_ChangeStatus(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_ChangeStatus *pie = (Q931ie_ChangeStatus*)OBuf; + ie *pIE = &pMsg->ChangeStatus; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Preference = (IBuf[Octet + Off] >> 6) & 0x01; + pie->Spare = IBuf[Octet + Off] & 0x38; + pie->NewStatus = IBuf[Octet + Off] & 0x07; + Octet++; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_ChangeStatus); + pie->Size = sizeof(Q931ie_ChangeStatus); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_ChangeStatus + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_ChangeStatus(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_ChangeStatus *pIE = (Q931ie_ChangeStatus*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_CHANGE_STATUS; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | pIE->NewStatus | ((pIE->Preference & 0x01) << 6); + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + + + +L3INT Q931Uie_Generic(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff) +{ + L3INT Octet = 0; + L3UCHAR id = 0; + + /* id */ + id = IBuf[Octet++]; + + /* Length */ + Octet += IBuf[Octet]; + Octet++; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Discarding IE %#hhx with length %d\n", id, Octet - 2); + + *IOff += Octet; + return Q931E_NO_ERROR; +} + +L3INT Q931Pie_Generic(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + /* do nothing */ + return Q931E_NO_ERROR; +} diff --git a/libs/freetdm/src/isdn/Q931mes.c b/libs/freetdm/src/isdn/Q931mes.c new file mode 100644 index 0000000000..8e34a170e1 --- /dev/null +++ b/libs/freetdm/src/isdn/Q931mes.c @@ -0,0 +1,1870 @@ +/***************************************************************************** + + FileName: Q931mes.c + + Contents: Pack/Unpack functions. These functions will unpack a Q931 + message from the bit packed original format into structs + that contains variables sized by the user. It will also pack + the struct back into a Q.931 message as required. + + See q931.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +/** + * Q931MesgHeader + * \brief Create Q.931 Message header + */ +L3INT Q931MesgHeader(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *mes, L3UCHAR *OBuf, L3INT Size, L3INT *IOff) +{ + L3INT Octet = *IOff; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Creating Q.931 Message Header:\n ProtDisc %d (%#x), CRV %d (%#x), CRVflag: %d (%#x), MesType: %d (%#x)\n", + mes->ProtDisc, mes->ProtDisc, mes->CRV, mes->CRV, mes->CRVFlag, mes->CRVFlag, mes->MesType, mes->MesType); + + OBuf[Octet++] = mes->ProtDisc; /* Protocol discriminator */ + if (!Q931_IS_BRI(pTrunk)) { + OBuf[Octet++] = 2; /* length is 2 octets */ + OBuf[Octet++] = (L3UCHAR)((mes->CRV >> 8) & 0x7f) | ((mes->CRVFlag << 7) & 0x80); /* msb */ + OBuf[Octet++] = (L3UCHAR) (mes->CRV & 0xff); /* lsb */ + } else { + OBuf[Octet++] = 1; /* length is 1 octet */ + OBuf[Octet++] = (L3UCHAR) (mes->CRV & 0x7f) | ((mes->CRVFlag << 7) & 0x80); /* CRV & flag */ + } + OBuf[Octet++] = mes->MesType; /* message header */ + + *IOff = Octet; + return 0; +} + + +/***************************************************************************** + + Function: Q931Umes_Alerting + +*****************************************************************************/ + +L3INT Q931Umes_Alerting(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Alerting + +*****************************************************************************/ +L3INT Q931Pmes_Alerting(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_CallProceeding + +*****************************************************************************/ +L3INT Q931Umes_CallProceeding(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_CallProceeding + +*****************************************************************************/ +L3INT Q931Pmes_CallProceeding(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_CongestionControl + +*****************************************************************************/ +L3INT Q931Umes_CongestionControl(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(mes); + NoWarning(IBuf); + + return RetCode; +} + + +/***************************************************************************** + + Function: Q931Pmes_CongestionControl + +*****************************************************************************/ +L3INT Q931Pmes_CongestionControl(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + *OSize = 0; + return RetCode; +} + +/***************************************************************************** + + Function: Q931Umes_Connect + +*****************************************************************************/ +L3INT Q931Umes_Connect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_SIGNAL: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_CONNECTED_NUMBER: /* not actually used, seen while testing BRI PTMP TE */ + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + + default: + Q931Log(pTrunk, Q931_LOG_ERROR, "Illegal IE %#hhx in Connect Message\n", IBuf[IOff]); + + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Connect + +*****************************************************************************/ +L3INT Q931Pmes_Connect(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ConnectAck + +*****************************************************************************/ +L3INT Q931Umes_ConnectAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + + +/***************************************************************************** + + Function: Q931Pmes_ConnectAck + +*****************************************************************************/ +L3INT Q931Pmes_ConnectAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Disconnect + +*****************************************************************************/ +L3INT Q931Umes_Disconnect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + case Q931ie_FACILITY: + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Disconnect + +*****************************************************************************/ +L3INT Q931Pmes_Disconnect(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Information + +*****************************************************************************/ +L3INT Q931Umes_Information(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_SENDING_COMPLETE: + case Q931ie_DISPLAY: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_CALLED_PARTY_NUMBER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Information + +*****************************************************************************/ +L3INT Q931Pmes_Information(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)(pMes->SendComplete & 0x00ff); + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Notify + +*****************************************************************************/ +L3INT Q931Umes_Notify(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_NOTIFICATION_INDICATOR: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Notify + +*****************************************************************************/ +L3INT Q931Pmes_Notify(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Progress + +*****************************************************************************/ +L3INT Q931Umes_Progress(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CAUSE: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Progress + +*****************************************************************************/ +L3INT Q931Pmes_Progress(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Release + +*****************************************************************************/ +L3INT Q931Umes_Release(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Release + +*****************************************************************************/ +L3INT Q931Pmes_Release(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ReleaseComplete + +*****************************************************************************/ +L3INT Q931Umes_ReleaseComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_ReleaseComplete + +*****************************************************************************/ +L3INT Q931Pmes_ReleaseComplete(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Restart + +*****************************************************************************/ +L3INT Q931Umes_Restart(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_DISPLAY: + case Q931ie_RESTART_INDICATOR: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Restart + +*****************************************************************************/ +L3INT Q931Pmes_Restart(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* ChanID */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* RestartInd */ + if (Q931IsIEPresent(pMes->RestartInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_RESTART_INDICATOR](pTrunk, Q931GetIEPtr(pMes->RestartInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_RestartAck + +*****************************************************************************/ +L3INT Q931Umes_RestartAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size) +{ + Q931mes_Generic *mes = (Q931mes_Generic*)OBuf; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_DISPLAY: + case Q931ie_RESTART_INDICATOR: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_RestartAck + +*****************************************************************************/ +L3INT Q931Pmes_RestartAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* ChanID */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* RestartInd */ + if (Q931IsIEPresent(pMes->RestartInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_RESTART_INDICATOR](pTrunk, Q931GetIEPtr(pMes->RestartInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Resume + +*****************************************************************************/ +L3INT Q931Umes_Resume(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CALL_IDENTITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Resume + +*****************************************************************************/ +L3INT Q931Pmes_Resume(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Call Identity */ + if (Q931IsIEPresent(pMes->CallID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALL_IDENTITY](pTrunk, Q931GetIEPtr(pMes->CallID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ResumeAck + +*****************************************************************************/ +L3INT Q931Umes_ResumeAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + + +/***************************************************************************** + + Function: Q931Pmes_ResumeAck + +*****************************************************************************/ +L3INT Q931Pmes_ResumeAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ResumeReject + +*****************************************************************************/ +L3INT Q931Umes_ResumeReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + + +/***************************************************************************** + + Function: Q931Pmes_ResumeReject + +*****************************************************************************/ +L3INT Q931Pmes_ResumeReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +L3INT Q931Umes_Segment(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT OOff) +{ + L3INT i = IOff; + + return IOff - i; +} + +L3INT Q931Pmes_Segment(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + *OSize = 0; + return RetCode; +} + +/***************************************************************************** + + Function: Q931Umes_Setup + +*****************************************************************************/ +L3INT Q931Umes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT ir = 0; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_SENDING_COMPLETE: + IOff++; + break; + + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_NETWORK_SPECIFIC_FACILITIES: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_SIGNAL: + case Q931ie_CALLING_PARTY_NUMBER: + case Q931ie_CALLING_PARTY_SUBADDRESS: + case Q931ie_CALLED_PARTY_NUMBER: + case Q931ie_CALLED_PARTY_SUBADDRESS: + case Q931ie_TRANSIT_NETWORK_SELECTION: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_FACILITY: + case Q931ie_USER_USER: + case Q931ie_REDIRECTING_NUMBER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + + case Q931ie_REPEAT_INDICATOR: + if (ir < 2) { + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + ir++; + } else { + return Q931E_ILLEGAL_IE; + } + break; + + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Setup + + Decription: Pack a Q931mes_Generic into a real Q.931 message. The user will + set up a SETUP message and issue this to the stack where it + is processed by Q931ProcSetup that processes and validates + it before it actually sends it out. This function is called + to compute the real Q.931 message. + + Parameters: IBuf[IN] Ptr to un-packed struct + ISize[IN] Size of input buffer (unpacked message). + OBuf[OUT] Ptr to packed 'octet' wise message. + OSize[OUT] Size of packed message. + + Called By: Q931ProcSetup + +*****************************************************************************/ +L3INT Q931Pmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)Q931ie_SENDING_COMPLETE & 0xff; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->RepeatInd)) { + OBuf[Octet++] = (L3UCHAR)Q931ie_REPEAT_INDICATOR & 0xff; + } + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } else { + rc = Q931E_BEARERCAP; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Network spesific facilities */ + if (Q931IsIEPresent(pMes->NetFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_NETWORK_SPECIFIC_FACILITIES](pTrunk, Q931GetIEPtr(pMes->NetFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Number */ + if (Q931IsIEPresent(pMes->CallingNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CallingNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Subaddress */ + if (Q931IsIEPresent(pMes->CallingSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CallingSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called party subaddress */ + if (Q931IsIEPresent(pMes->CalledSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CalledSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Transit network selection */ + if (Q931IsIEPresent(pMes->TransNetSel)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_TRANSIT_NETWORK_SELECTION](pTrunk, Q931GetIEPtr(pMes->TransNetSel,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->LLRepeatInd)) { + rc = Q931E_UNKNOWN_IE;/* TODO */ + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_SetupAck + +*****************************************************************************/ +L3INT Q931Umes_SetupAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_SetupAck + +*****************************************************************************/ +L3INT Q931Pmes_SetupAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Status + +*****************************************************************************/ +L3INT Q931Umes_Status(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_CALL_STATE: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + + +/***************************************************************************** + + Function: Q931Pmes_Status + +*****************************************************************************/ +L3INT Q931Pmes_Status(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Call State */ + if (Q931IsIEPresent(pMes->CallState)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALL_STATE](pTrunk, Q931GetIEPtr(pMes->CallState,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_StatusEnquiry + +*****************************************************************************/ +L3INT Q931Umes_StatusEnquiry(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_StatusEnquiry + +*****************************************************************************/ +L3INT Q931Pmes_StatusEnquiry(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Suspend + +*****************************************************************************/ +L3INT Q931Umes_Suspend(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CALL_IDENTITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Suspend + +*****************************************************************************/ +L3INT Q931Pmes_Suspend(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Call Identity */ + if (Q931IsIEPresent(pMes->CallID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALL_IDENTITY](pTrunk, Q931GetIEPtr(pMes->CallID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_SuspendAck + +*****************************************************************************/ +L3INT Q931Umes_SuspendAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_SuspendAck + +*****************************************************************************/ +L3INT Q931Pmes_SuspendAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_SuspendReject + +*****************************************************************************/ +L3INT Q931Umes_SuspendReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_SuspendReject + +*****************************************************************************/ +L3INT Q931Pmes_SuspendReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_UserInformation + +*****************************************************************************/ +L3INT Q931Umes_UserInformation(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT I, L3INT O) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(mes); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Pmes_UserInformation + +*****************************************************************************/ +L3INT Q931Pmes_UserInformation(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + *OSize = 0; + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Umes_Service + +*****************************************************************************/ +L3INT Q931Umes_Service(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_CHANGE_STATUS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Service + +*****************************************************************************/ +L3INT Q931Pmes_Service(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Display */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + if (Q931IsIEPresent(pMes->ChangeStatus)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANGE_STATUS](pTrunk, Q931GetIEPtr(pMes->ChangeStatus,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ServiceAck + +*****************************************************************************/ +L3INT Q931Umes_ServiceAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_CHANGE_STATUS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_ServiceAck + +*****************************************************************************/ +L3INT Q931Pmes_ServiceAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + if (Q931IsIEPresent(pMes->ChangeStatus)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANGE_STATUS](pTrunk, Q931GetIEPtr(pMes->ChangeStatus,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} diff --git a/libs/freetdm/src/isdn/Q932mes.c b/libs/freetdm/src/isdn/Q932mes.c new file mode 100644 index 0000000000..574499a241 --- /dev/null +++ b/libs/freetdm/src/isdn/Q932mes.c @@ -0,0 +1,286 @@ +/***************************************************************************** + + FileName: Q932mes.c + + Contents: Q.932 Message Encoders/Decoders + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +/***************************************************************************** + + Function: Q932Umes_Facility + +*****************************************************************************/ + +L3INT Q932Umes_Facility(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Facility + +*****************************************************************************/ +L3INT Q932Pmes_Facility(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_Hold + +*****************************************************************************/ + +L3INT Q932Umes_Hold(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Hold + +*****************************************************************************/ +L3INT Q932Pmes_Hold(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_HoldAck + +*****************************************************************************/ + +L3INT Q932Umes_HoldAck(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_HoldAck + +*****************************************************************************/ +L3INT Q932Pmes_HoldAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_HoldReject + +*****************************************************************************/ + +L3INT Q932Umes_HoldReject(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_HoldReject + +*****************************************************************************/ +L3INT Q932Pmes_HoldReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_Register + +*****************************************************************************/ + +L3INT Q932Umes_Register(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Register + +*****************************************************************************/ +L3INT Q932Pmes_Register(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_Retrieve + +*****************************************************************************/ + +L3INT Q932Umes_Retrieve(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Retrieve + +*****************************************************************************/ +L3INT Q932Pmes_Retrieve(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_RetrieveAck + +*****************************************************************************/ + +L3INT Q932Umes_RetrieveAck(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_RetrieveAck + +*****************************************************************************/ +L3INT Q932Pmes_RetrieveAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_RetrieveReject + +*****************************************************************************/ + +L3INT Q932Umes_RetrieveReject(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_RetrieveReject + +*****************************************************************************/ +L3INT Q932Pmes_RetrieveReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} diff --git a/libs/freetdm/src/isdn/include/5ESS.h b/libs/freetdm/src/isdn/include/5ESS.h new file mode 100644 index 0000000000..882b257395 --- /dev/null +++ b/libs/freetdm/src/isdn/include/5ESS.h @@ -0,0 +1,103 @@ +/****************************************************************************** + + FileName: 5ESS.h + + Contents: Header and definition for the AT&T 5ESS ISDN dialect. The + header contains the following parts: + + - Definition of codes + - Definition of information elements (5ESSie_). + - Definition of messages (5ESSmes_). + - Function prototypes. + + Description: The AT&T 5ESS ISDN dialect here covers ???? + + Related Files: 5ESS.h AT&T 5ESS ISDN Definitions + 5ESSie.c AT&T 5ESS ISDN IE encoders/coders (not extant yet) + See Q931ie.c for IE encoders/coders + 5ESSStateTE.c AT&T 5ESS ISDN TE State Engine + 5ESSStateNT.c AT&T 5ESS ISDN NT State Engine + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + Copyright (c) 2007, Michael S. Collins, All rights reserved. + email:mcollins@fcnetwork.com + + 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 Case Labs, Ltd nor the names of its 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 _5ESS_NL +#define _5ESS_NL + +#include "Q931.h" + +/***************************************************************************** + + Q.931 Message codes + Only 5ESS specific message and ie types + here the rest are inherited from Q931.h + +*****************************************************************************/ + + +/***************************************************************************** + + Q.931 Message Pack/Unpack functions. Implemented in 5ESSmes.c + Note: Because C variables may not begin with numeric digit, all identifiers + are prefixed with "ATT5ESS" instead of a bare "5ESS" + +*****************************************************************************/ +L3INT ATT5ESSUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT ATT5ESSPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT ATT5ESSUmes_SetupAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT ATT5ESSPmes_SetupAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +L3INT ATT5ESSUmes_0x07(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT ATT5ESSPmes_0x07(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT ATT5ESSUmes_0x0f(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT ATT5ESSPmes_0x0f(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + + + +/***************************************************************************** + + Q.931 Process Function Prototyping. Implemented in 5ESSStateTE.c + +*****************************************************************************/ +L3INT ATT5ESSProc0x0fTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom); +L3INT ATT5ESSProc0x07TE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom); + + +void ATT5ESSCreateTE(L3UCHAR i); +void ATT5ESSCreateNT(L3UCHAR i); + +#endif /* _5ESS_NL */ diff --git a/libs/freetdm/src/isdn/include/DMS.h b/libs/freetdm/src/isdn/include/DMS.h new file mode 100644 index 0000000000..605a61aa6e --- /dev/null +++ b/libs/freetdm/src/isdn/include/DMS.h @@ -0,0 +1,91 @@ +/****************************************************************************** + + FileName: national.h + + Contents: Header and definition for the National ISDN dialect. The + header contents the following parts: + - Definition of codes + - Definition of information elements (nationalie_). + - Definition of messages (nationalmes_). + - Function prototypes. + + Description: The National ISDN dialect here covers ???? + + Related Files: national.h National ISDN Definitions + nationalie.c National ISDN IE encoders/coders + nationalStateTE.c National ISDN TE State Engine + nationalStateNT.c National ISDN NT State Engine + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 _DMS_NL +#define _DMS_NL + +#include "Q931.h" + +/***************************************************************************** + + Q.931 Message codes + Only National specific message and ie types + here the rest are inherited from national.h + +*****************************************************************************/ + + +/***************************************************************************** + + Q.931 Message Pack/Unpack functions. Implemented in nationalmes.c + +*****************************************************************************/ +L3INT DMSUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT DMSPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT DMSUmes_0x07(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT DMSPmes_0x07(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT DMSUmes_0x0f(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT DMSPmes_0x0f(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +/***************************************************************************** + + Q.931 Process Function Prototyping. Implemented in nationalStateTE.c + +*****************************************************************************/ + +L3INT DMSProc0x0fTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom); +L3INT DMSProc0x07TE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom); + +void DMSCreateTE(L3UCHAR i); +void DMSCreateNT(L3UCHAR i); + +#endif /* _DMS_NL */ diff --git a/libs/freetdm/src/isdn/include/Q921.h b/libs/freetdm/src/isdn/include/Q921.h new file mode 100644 index 0000000000..cb21a157ea --- /dev/null +++ b/libs/freetdm/src/isdn/include/Q921.h @@ -0,0 +1,227 @@ +/***************************************************************************** + + FileName: q921.h + + Description: Contains headers of a Q.921 protocol. + + Note: This header file is the only include file that should be + acessed by users of the Q.921 stack. + + Interface: The Q.921 stack contains 2 layers. + + - One interface layer. + - One driver layer. + + The interface layer contains the interface functions required + for a layer 2 stack to be able to send and receive messages. + + The driver layer will simply feed bytes into the ship as + required and queue messages received out from the ship. + + Q921TimeTick The Q.921 like any other blackbox + modules contains no thread by it's own + and must therefore be called regularly + by an external 'thread' to do maintenance + etc. + + Q921Rx32 Receive message from layer 3. Called by + the layer 3 stack to send a message. + + + NOTE: The following are not yet implemented + + OnQ921Error Function called every if an error is + detected. + + OnQ921Log Function called if logging is active. + + + Maintenance/Configuration interface + Logging + DL_ message passing to layer 3 + Timers + Api commands to tell 921 to stop and start for a trunk + + Created: 27.dec.2000/JVB + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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. + +*****************************************************************************/ + +/**************************************************************************** + * Changes: + * + * - June,July 2008: Stefan Knoblich : + * Add PTMP TEI management + * Add timers + * Add retransmit counters + * Add logging + * Various cleanups + * + ****************************************************************************/ + +#ifndef _Q921 +#define _Q921 + +#define Q921MAXHDLCSPACE 3000 +#define L2UCHAR unsigned char /* Min 8 bit */ +#define L2USHORT unsigned short /* 16 bit */ +#define L2INT int /* Min 16 bit signed */ +#define L2ULONG unsigned long /* Min 32 bit */ +#define L2TRUNK Q921Data_t * + +#define Q921_TEI_BCAST 127 +#define Q921_TEI_MAX Q921_TEI_BCAST + +#define Q921_TEI_DYN_MIN 64 +#define Q921_TEI_DYN_MAX 126 + + +typedef enum /* Network/User Mode */ +{ + Q921_TE=0, /* 0 : User Mode */ + Q921_NT=1 /* 1 : Network Mode */ +} Q921NetUser_t; + +typedef enum /* Type of connection */ +{ + Q921_PTP=0, /* 0 : Point-To-Point */ + Q921_PTMP=1 /* 1 : Point-To-Multipoint */ +} Q921NetType_t; + +typedef enum +{ + Q921_LOG_NONE = -1, + Q921_LOG_EMERG = 0, + Q921_LOG_ALERT, + Q921_LOG_CRIT, + Q921_LOG_ERROR, + Q921_LOG_WARNING, + Q921_LOG_NOTICE, + Q921_LOG_INFO, + Q921_LOG_DEBUG +} Q921LogLevel_t; + + +/* + * Messages for L2 <-> L3 communication + */ +typedef enum { + Q921_DL_ESTABLISH = 0, + Q921_DL_ESTABLISH_CONFIRM, + Q921_DL_RELEASE, + Q921_DL_RELEASE_CONFIRM, + Q921_DL_DATA, + Q921_DL_UNIT_DATA +} Q921DLMsg_t; + +typedef int (*Q921Tx21CB_t) (void *, L2UCHAR *, L2INT); +typedef int (*Q921Tx23CB_t) (void *, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR *, L2INT); +typedef int (*Q921LogCB_t) (void *, Q921LogLevel_t, char *, L2INT); + +struct Q921_Link; + +typedef struct Q921Data +{ + L2INT initialized; + + L2UCHAR sapi; /*!< User assigned SAPI */ + L2UCHAR tei; /*!< User assigned TEI value */ + + L2INT Q921HeaderSpace; + Q921NetUser_t NetUser; + Q921NetType_t NetType; + + struct Q921_Link *context; /*!< per-TEI / link context space */ + + /* timers */ + L2ULONG T202; /*!< PTMP TE mode TEI retransmit timer */ + L2ULONG T200Timeout; + L2ULONG T201Timeout; + L2ULONG T202Timeout; + L2ULONG T203Timeout; + + L2ULONG TM01Timeout; + + /* counters */ + L2ULONG N200Limit; /*!< max retransmit */ + + L2ULONG N202; /*!< PTMP TE mode retransmit counter */ + L2ULONG N202Limit; /*!< PTMP TE mode max retransmit */ + + L2ULONG N201Limit; /*!< max number of octets */ + L2ULONG k; /*!< max number of unacknowledged I frames */ + + /* callbacks and callback data pointers */ + Q921Tx21CB_t Q921Tx21Proc; + Q921Tx23CB_t Q921Tx23Proc; + void *PrivateData21; + void *PrivateData23; + + /* logging */ + Q921LogLevel_t loglevel; /*!< trunk loglevel */ + Q921LogCB_t Q921LogProc; /*!< log callback procedure */ + void *PrivateDataLog; /*!< private data pointer for log proc */ + + /* tei mgmt */ + L2UCHAR tei_map[Q921_TEI_MAX]; /*!< */ + + L2UCHAR HDLCInQueue[Q921MAXHDLCSPACE]; /*!< HDLC input queue */ +} Q921Data_t; + +/* + * Public functions + */ +int Q921_InitTrunk(L2TRUNK trunk, + L2UCHAR sapi, + L2UCHAR tei, + Q921NetUser_t NetUser, + Q921NetType_t NetType, + L2INT hsize, + Q921Tx21CB_t cb21, + Q921Tx23CB_t cb23, + void *priv21, + void *priv23); +int Q921Start(L2TRUNK trunk); +int Q921Stop(L2TRUNK trunk); + +void Q921SetLogCB(L2TRUNK trunk, Q921LogCB_t func, void *priv); +void Q921SetLogLevel(L2TRUNK trunk, Q921LogLevel_t level); + +int Q921Rx12(L2TRUNK trunk); +int Q921Rx32(L2TRUNK trunk, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR * Mes, L2INT Size); + +int Q921QueueHDLCFrame(L2TRUNK trunk, L2UCHAR *b, L2INT size); + +void Q921SetGetTimeCB(L2ULONG (*callback)(void)); +void Q921TimerTick(L2TRUNK trunk); + +#endif diff --git a/libs/freetdm/src/isdn/include/Q921priv.h b/libs/freetdm/src/isdn/include/Q921priv.h new file mode 100644 index 0000000000..d2d838c399 --- /dev/null +++ b/libs/freetdm/src/isdn/include/Q921priv.h @@ -0,0 +1,321 @@ +/***************************************************************************** + + FileName: Q921priv.h + + Description: Private declarations + + Created: 08.Aug.2008/STKN + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2008, Stefan Knoblich, axsentis GmbH. All rights reserved. + email:s.knoblich@axsentis.de + + 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 Case Labs, Ltd nor the names of its 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 _Q921_PRIV_H_ +#define _Q921_PRIV_H_ + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#if (_MSC_VER >= 1400) /* VC8+ */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#ifndef strncasecmp +#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#endif + +typedef enum /* Q.921 States */ +{ + Q921_STATE_STOPPED = 0, /* Trunk stopped */ + Q921_STATE_TEI_UNASSIGNED = 1, /* TEI unassigned */ + Q921_STATE_TEI_AWAITING, /* Assign awaiting TEI */ + Q921_STATE_TEI_ESTABLISH, /* Establish awaiting TEI */ + Q921_STATE_TEI_ASSIGNED, /* TEI assigned */ + Q921_STATE_AWAITING_ESTABLISHMENT, /* Awaiting establishment */ + Q921_STATE_AWAITING_RELEASE, /* Awaiting release */ + Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, /* Multiple frame established */ + Q921_STATE_TIMER_RECOVERY /* Timer recovery */ +} Q921State_t; + +/* + * Flags + */ +enum Q921_Flags { + Q921_FLAG_L3_INITIATED = (1 << 0), + + Q921_FLAG_UI_FRAME_QUEUED = (1 << 1), + Q921_FLAG_I_FRAME_QUEUED = (1 << 2), + + Q921_FLAG_ACK_PENDING = (1 << 3), + Q921_FLAG_REJECT = (1 << 4), + + Q921_FLAG_RECV_BUSY = (1 << 5), + Q921_FLAG_PEER_RECV_BUSY = (1 << 6) +}; + +#define Q921_SET_FLAG(x, f) ((x)->flags |= f) +#define Q921_CHECK_FLAG(x, f) ((x)->flags & f) +#define Q921_CLEAR_FLAG(x, f) ((x)->flags &= ~f) + + +/* + * dynamic TEI handling + */ +#define Q921_SAPI_TEI 63 /* SAPI for all TEI Messages */ +#define Q921_LAYER_ENT_ID_TEI 0x0f /* UN Layer Management Entity ID for TEI Mgmt */ +#define Q921_LAYER_ENT_ID_Q931 0x08 /* Q.931 Layer Management Entity ID */ + + +typedef enum { + Q921_TEI_ID_REQUEST = 1, + Q921_TEI_ID_ASSIGNED, + Q921_TEI_ID_DENIED, + Q921_TEI_ID_CHECKREQ, + Q921_TEI_ID_CHECKRESP, + Q921_TEI_ID_REMOVE, + Q921_TEI_ID_VERIFY +} Q921TeiMessageType_t; + + +/** + * Per-Datalink context + */ +struct Q921_Link { + L2UCHAR tei; /*!< This endpoint's TEI */ + + L2UCHAR va; + L2UCHAR vs; + L2UCHAR vr; + + L2INT flags; + Q921State_t state; + + L2ULONG N202; /*!< PTMP TE mode retransmit counter */ + L2ULONG N200; /*!< retransmit counter (per-TEI in PTMP NT mode) */ + + L2ULONG TM01; /*!< Datalink inactivity disconnect timer */ + + L2ULONG T200; + L2ULONG T201; /*!< PTMP NT mode timer */ + L2ULONG T203; + + L2USHORT ri; /*!< random id for TEI request mgmt */ + + /* I + UI Frame queue */ + L2UCHAR UIFrameQueue[Q921MAXHDLCSPACE]; + L2UCHAR IFrameQueue[Q921MAXHDLCSPACE]; + L2UCHAR IFrameResendQueue[Q921MAXHDLCSPACE]; +}; + + +#define Q921_LINK_CONTEXT(tr, tei) \ + (Q921_IS_PTMP_NT(tr) && tei != Q921_TEI_BCAST) ? ((struct Q921_Link *)&(tr)->context[tei]) : (tr)->context + +#define Q921_TRUNK_CONTEXT(tr) \ + (tr)->context + +#define Q921_LOGBUFSIZE 2000 +#define INITIALIZED_MAGIC 42 + +/* + * Helper macros + */ +#define Q921_INC_COUNTER(x) (x = (x + 1) % 128) +#define Q921_DEC_COUNTER(x) (x = (x) ? (x - 1) : 127) + +#define Q921_UFRAME_HEADER_SIZE 3 +#define Q921_UFRAME_DATA_OFFSET(tr) ((tr)->Q921HeaderSpace + Q921_UFRAME_HEADER_SIZE) + +#define Q921_SFRAME_HEADER_SIZE 4 +#define Q921_SFRAME_DATA_OFFSET(tr) ((tr)->Q921HeaderSpace + Q921_SFRAME_HEADER_SIZE) + +#define Q921_IFRAME_HEADER_SIZE 4 +#define Q921_IFRAME_DATA_OFFSET(tr) ((tr)->Q921HeaderSpace + Q921_IFRAME_HEADER_SIZE) + +#define Q921_IS_TE(x) ((x)->NetUser == Q921_TE) +#define Q921_IS_NT(x) ((x)->NetUser == Q921_NT) + +#define Q921_IS_STOPPED(tr) ((tr)->state == Q921_STATE_STOPPED) + +/* TODO: rework this one */ +#define Q921_IS_READY(tr) ((tr)->state >= Q921_STATE_TEI_ASSIGNED) + +#define Q921_IS_PTMP(x) ((x)->NetType == Q921_PTMP) +#define Q921_IS_PTMP_TE(x) ((x)->NetType == Q921_PTMP && (x)->NetUser == Q921_TE) +#define Q921_IS_PTMP_NT(x) ((x)->NetType == Q921_PTMP && (x)->NetUser == Q921_NT) + +#define Q921_IS_PTP(x) ((x)->NetType == Q921_PTP) +#define Q921_IS_PTP_TE(x) ((x)->NetType == Q921_PTP && (x)->NetUser == Q921_TE) +#define Q921_IS_PTP_NT(x) ((x)->NetType == Q921_PTP && (x)->NetUser == Q921_NT) + +/* Make life a little easier */ +#define Q921_COMMAND(x) ((x)->NetUser == Q921_TE ? 0 : 1) +#define Q921_RESPONSE(x) ((x)->NetUser == Q921_TE ? 1 : 0) + +#define Q921_IS_COMMAND(tr, x) ((x) == (Q921_IS_TE(tr) ? 1 : 0)) +#define Q921_IS_RESPONSE(tr, x) ((x) == (Q921_IS_TE(tr) ? 0 : 1)) + + +/******************************************************************************* + * Private functions + *******************************************************************************/ + +/* + * L1 / L2 Interface + */ +static int Q921Tx21Proc(L2TRUNK trunk, L2UCHAR *Msg, L2INT size); +static int Q921Tx23Proc(L2TRUNK trunk, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR *Msg, L2INT size); + + +/* + * Timers + */ +static L2ULONG Q921GetTime(void); + +static void Q921T200TimerStart(L2TRUNK trunk, L2UCHAR tei); +static void Q921T200TimerStop(L2TRUNK trunk, L2UCHAR tei); +static void Q921T200TimerReset(L2TRUNK trunk, L2UCHAR tei); +static void Q921T200TimerExpire(L2TRUNK trunk, L2UCHAR tei); + +static void Q921T201TimerStart(L2TRUNK trunk, L2UCHAR tei); +static void Q921T201TimerStop(L2TRUNK trunk, L2UCHAR tei); +/* static void Q921T201TimerReset(L2TRUNK trunk, L2UCHAR tei); - Unused for now */ +static void Q921T201TimerExpire(L2TRUNK trunk, L2UCHAR tei); + +static void Q921T202TimerStart(L2TRUNK trunk); +static void Q921T202TimerStop(L2TRUNK trunk); +static void Q921T202TimerReset(L2TRUNK trunk); +static void Q921T202TimerExpire(L2TRUNK trunk); + +static void Q921T203TimerStart(L2TRUNK trunk, L2UCHAR tei); +static void Q921T203TimerStop(L2TRUNK trunk, L2UCHAR tei); +static void Q921T203TimerReset(L2TRUNK trunk, L2UCHAR tei); +static void Q921T203TimerExpire(L2TRUNK trunk, L2UCHAR tei); + +static void Q921TM01TimerStart(L2TRUNK trunk, L2UCHAR tei); +/* static void Q921TM01TimerStop(L2TRUNK trunk, L2UCHAR tei); - Unused for now */ +static void Q921TM01TimerReset(L2TRUNK trunk, L2UCHAR tei); +/* static void Q921TM01TimerExpire(L2TRUNK trunk, L2UCHAR tei); - Unused for now */ + +/* + * Frame encoding + */ +static int Q921SendS(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR sv, L2UCHAR *mes, L2INT size); +static int Q921SendU(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR m, L2UCHAR *mes, L2INT size); + +static int Q921SendRNR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendREJ(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendSABME(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendDM(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendDISC(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendUA(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendUN(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf, L2UCHAR *mes, L2INT size); +static int Q921SendRR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); + +/* + * Frame decoding + */ +static int Q921ProcIFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcSFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcUFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size); + +static int Q921ProcSABME(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcDM(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcUA(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcDISC(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcRR(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcRNR(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcREJ(L2TRUNK trunk, L2UCHAR *mes, L2INT size); + + +/* + * (Common) procedures defined in the Q.921 SDL + */ +static int Q921SendEnquiry(L2TRUNK trunk, L2UCHAR tei); +static int Q921SendEnquiryResponse(L2TRUNK trunk, L2UCHAR tei); +static void Q921ResetExceptionConditions(L2TRUNK trunk, L2UCHAR tei); +static int Q921EstablishDataLink(L2TRUNK trunk, L2UCHAR tei); +static int Q921NrErrorRecovery(L2TRUNK trunk, L2UCHAR tei); +static int Q921InvokeRetransmission(L2TRUNK trunk, L2UCHAR tei, L2UCHAR nr); +static int Q921AcknowledgePending(L2TRUNK trunk, L2UCHAR tei); +/* +static int Q921SetReceiverBusy(L2TRUNK trunk); +static int Q921ClearReceiverBusy(L2TRUNK trunk); +*/ + +/* + * Queueing + */ +static int Q921SendQueuedIFrame(L2TRUNK trunk, L2UCHAR tei); +static int Q921EnqueueI(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR *mes, L2INT size); + +/* + * TEI management + */ +static int Q921TeiSendAssignRequest(L2TRUNK trunk); +static int Q921TeiProcAssignResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiSendVerifyRequest(L2TRUNK trunk); +static int Q921TeiProcCheckRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiProcRemoveRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiProcAssignRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiProcCheckResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiProcVerifyRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiSendRemoveRequest(L2TRUNK trunk, L2UCHAR tei); +static int Q921TeiSendDenyResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri); +static int Q921TeiSendAssignedResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri); +static int Q921TeiSendCheckRequest(L2TRUNK trunk, L2UCHAR tei); + +/* + * Logging + */ +static int Q921Log(L2TRUNK trunk, Q921LogLevel_t level, const char *fmt, ...); +static int Q921LogMesg(L2TRUNK trunk, Q921LogLevel_t level, L2UCHAR received, L2UCHAR *mes, L2INT size, const char *fmt, ...); + +/* + * State handling + */ +static int Q921ChangeState(L2TRUNK trunk, Q921State_t state, L2UCHAR tei); + +#endif diff --git a/libs/freetdm/src/isdn/include/Q931.h b/libs/freetdm/src/isdn/include/Q931.h new file mode 100644 index 0000000000..0dbafd9f11 --- /dev/null +++ b/libs/freetdm/src/isdn/include/Q931.h @@ -0,0 +1,1175 @@ +/****************************************************************************** + + FileName: Q931.h + + Contents: Header and definition for the ITU-T Q.931 stack. The + header contents the following parts: + + - Definition of codes + - Definition of information elements (Q931ie_). + - Definition of messages (Q931mes_). + - Definitian of variables (var_). + - Function prototypes. + + Description: The Q.931 stack provided here covers ITU-T Q.931 w/Q.932 + supplementary services for both PRI, BRI and variants. + The stack is generic and designed to deal with variants as + needed. + + The stack uses the following interface functions: + + - Q931Initialize Initialize the Q.931 stack. + - Q931Rx23 Receive a message from layer 2 + - Q931Tx32 Send a message to layer 2 + - Q931Rx43 Receive a message from layer 4 or above. + - Q931Tx34 Send a message to layer 4 or above. + - Q931TimeTick Periodical timer processing. + - Q931ErrorProc Callback for stack error message. + + The protocol is a module with no external dependencies and + can easely be ported to any operating system like Windows, + Linux, rtos and others. + + Related Files: Q931.h Q.931 Definitions + Q931.c Q.931 Interface Functions. + Q931api.c Low level L4 API functions. + + Q932.h Q.932 Suplementary Services + Q932mes.c Q.932 encoders/coders + + Q931mes.c Q.931 Message encoders/coders + Q931ie.c Q.931 IE encoders/coders + Q931StateTE.c Generic Q.931 TE State Engine + Q931StateNT.c Generic Q.931 NT State Engine + + Design Note 1: For each variant please add separate files starting with + the variant short-name as follows: + + .h Spesific headers needed. + mes.c Message encoders/decores. + ie.c IE encoders/decoders. + StateTE.c TE side state engine. + StateNT.c NT side state engine. + + Design Note 2: The stack is deliberatly made non-threading. Use 1 + thread per Trunk, but lock access from the timertick + and rx, tx functions. And make sure the callbacks only + dump messages to a queue, no time-consuming processing + inside stack processing. + + All stack processing is async 'fire and forget', meaning + that there are not, and should not be any time-consuming + processing within the stack-time. The best way to thread + a stack is to use one single thread that signal 5 queues. + + - Incoming L2 queue. + - Incoming L4 queue. + - Outgoing L2 queue. + - Outgoing L4 queue. + - Error/Trace queue. + + Design Note 3: DSP optimization. The L3 (Rx23) can be called directly + from a HDLC receiver without usage of queues for optimized + processing. But keep in mind that Q.931 calls Tx34 or Tx32 + as part of receiving a message from Layer 2. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 _Q931_NL +#define _Q931_NL + +/* uncomment the #define below to add x.25 support to the Q.931 */ +/* #define Q931_X25_SUPPORT */ + +#include "stdio.h" + +#ifdef _MSC_VER +#pragma warning(disable:4100) +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#endif +#include + + +/***************************************************************************** + + Enum helper macros + +*****************************************************************************/ +#define Q931_ENUM_NAMES(_NAME, _STRINGS) static const char * _NAME [] = { _STRINGS , NULL }; +#define Q931_STR2ENUM_P(_FUNC1, _FUNC2, _TYPE) _TYPE _FUNC1 (const char *name); const char * _FUNC2 (_TYPE type); +#define Q931_STR2ENUM(_FUNC1, _FUNC2, _TYPE, _STRINGS, _MAX) \ + _TYPE _FUNC1 (const char *name) \ + { \ + int i; \ + _TYPE t = _MAX ; \ + \ + for (i = 0; i < _MAX ; i++) { \ + if (!strcasecmp(name, _STRINGS[i])) { \ + t = (_TYPE) i; \ + break; \ + } \ + } \ + \ + return t; \ + } \ + const char * _FUNC2 (_TYPE type) \ + { \ + if (type > _MAX) { \ + type = _MAX; \ + } \ + return _STRINGS[(int)type]; \ + } \ + +/***************************************************************************** + + Error Codes + +*****************************************************************************/ +typedef enum { + Q931E_NO_ERROR = 0, + + Q931E_UNKNOWN_MESSAGE = -3001, + Q931E_ILLEGAL_IE = -3002, + Q931E_UNKNOWN_IE = -3003, + Q931E_BEARERCAP = -3004, + Q931E_HLCOMP = -3005, + Q931E_LLCOMP = -3006, + Q931E_INTERNAL = -3007, + Q931E_MISSING_CB = -3008, + Q931E_UNEXPECTED_MESSAGE = -3009, + Q931E_ILLEGAL_MESSAGE = -3010, + Q931E_TOMANYCALLS = -3011, + Q931E_INVALID_CRV = -3012, + Q931E_CALLID = -3013, + Q931E_CALLSTATE = -3014, + Q931E_CALLEDSUB = -3015, + Q931E_CALLEDNUM = -3016, + Q931E_CALLINGNUM = -3017, + Q931E_CALLINGSUB = -3018, + Q931E_CAUSE = -3019, + Q931E_CHANID = -3020, + Q931E_DATETIME = -3021, + Q931E_DISPLAY = -3022, + Q931E_KEYPADFAC = -3023, + Q931E_NETFAC = -3024, + Q931E_NOTIFIND = -3025, + Q931E_PROGIND = -3026, + Q931E_RESTARTIND = -3027, + Q931E_SEGMENT = -3028, + Q931E_SIGNAL = -3029, + Q931E_GENERIC_DIGITS = -3030 +} q931_error_t; + +/* The q931_error_t enum should be kept in sync with the q931_error_names array in Q931.c */ + +const char *q931_error_to_name(q931_error_t error); + +/***************************************************************************** + + Some speed optimization can be achieved by changing all variables to the + word size of your processor. A 32 bit processor has to do a lot of extra + work to read a packed 8 bit integer. Changing all fields to 32 bit integer + will result in usage of some extra space, but will speed up the stack. + + The stack has been designed to allow L3UCHAR etc. to be any size of 8 bit + or larger. + +*****************************************************************************/ + +#define L3UCHAR unsigned char /* Min 8 bit */ +#define L3USHORT unsigned short /* Min 16 bit unsigned */ +#define L3UINT unsigned int /* Min 16 bit unsigned */ +#define L3INT int /* Min 16 bit signed */ +#define L3ULONG unsigned long /* Min 32 bit */ +#define L3BOOL char /* Min 1 bit, valuse 0 & 1 only */ + +#define L3TRUE 1 +#define L3FALSE 0 + +/***************************************************************************** + + Global defines. + +*****************************************************************************/ + +typedef L3USHORT ie; /* Special data type to hold a dynamic */ + /* or optional information element as */ + /* part of a message struct. MSB = 1 */ + /* indicate that the ie is present, the */ + /* last 15 bits is an offset ( or the */ + /* value for single octet ) to the */ + /* struct holding the ie. Offset = 0 */ + /* is buf[1] etc. */ + /* ie == 0xffff indicates error */ + +/***************************************************************************** + + MAXTRUNKS sets how many physical trunks this system might have. This + number should be keept at a minimum since it will use global space. + + It is recommended that you leave MAXCHPERTRUNK as is + +*****************************************************************************/ + +#define Q931_LOGBUFSIZE 1024 /* size of logging buffer */ + +#define Q931L4BUF 1000 /* size of message buffer */ + +#define Q931L2BUF 300 /* size of message buffer */ + +#define Q931MAXTRUNKS 4 /* Total number of trunks that will be */ + /* processed by this instance of the */ + /* stack */ + +#define Q931MAXCHPERTRUNK 32 /* Number of channels per trunk. The */ + /* stack uses a static set of 32 */ + /* channels regardless if it is E1, T1 */ + /* or BRI that actually is used. */ + +#define Q931MAXCALLPERTRUNK (Q931MAXCHPERTRUNK * 2) + /* Number of max active CRV per trunk. */ + /* Q.931 can have more calls than there */ + /* are channels. */ + + +#define Q931_IS_BRI(x) ((x)->TrunkType == Q931_TrType_BRI || (x)->TrunkType == Q931_TrType_BRI_PTMP) +#define Q931_IS_PRI(x) (!Q931_IS_BRI(x)) + +#define Q931_IS_PTP(x) ((x)->TrunkType != Q931_TrType_BRI_PTMP) +#define Q931_IS_PTMP(X) ((x)->TrunkType == Q931_TrType_BRI_PTMP) + +#define Q931_BRI_MAX_CRV 127 +#define Q931_PRI_MAX_CRV 32767 + +/***************************************************************************** + + The following defines control the dialect switch tables and should only be + changed when a new dialect needs to be inserted into the stack. + + This stack uses an array of functions to know which function to call as + it receives a SETUP message etc. A new dialect can when choose to use + the proc etc. for standard Q.931 or insert a modified proc. + + This technique has also been used to distinguish between user and network + mode to make the code as easy to read and maintainable as possible. + + A message and IE index have been used to save space. These indexes allowes + the message or IE code to be used directly and will give back a new index + into the table. + +*****************************************************************************/ + +/* WARNING! Initialize Q931CreateDialectCB[] will NULL when increasing the */ +/* Q931MAXDLCT value to avoid Q931Initialize from crashing if one entry is */ +/* not used. */ +#define Q931MAXDLCT 8 /* Max dialects included in this */ + /* compile. User and Network count as */ + /* one dialect each. */ + +#define Q931MAXMES 128 /* Number of messages */ +#define Q931MAXIE 255 /* Number of IE */ +#define Q931MAXUSEDIE 50 /* Maximum number of ie types per Dialect */ +#define Q931MAXCODESETS 7 /* Maximum number of codests (by spec, 0-7) */ +#define Q931MAXSTATE 100 /* Size of state tables */ +#define Q931MAXTIMER 25 /* Maximum number of timers */ + +/***************************************************************************** + + Call States for ITU-T Q.931 TE (User Mode) + +*****************************************************************************/ + +#define Q931_U0 0 +#define Q931_U1 1 +#define Q931_U2 2 +#define Q931_U3 3 +#define Q931_U4 4 +#define Q931_U6 6 +#define Q931_U7 7 +#define Q931_U8 8 +#define Q931_U9 9 +#define Q931_U10 10 +#define Q931_U11 11 +#define Q931_U12 12 +#define Q931_U15 15 +#define Q931_U17 17 +#define Q931_U19 19 +#define Q931_U25 25 + +/***************************************************************************** + + Call States for ITU-T Q.931 NT (Network Mode) + +*****************************************************************************/ +#define Q931_N0 (0x0100 | 0) +#define Q931_N1 (0x0100 | 1) +#define Q931_N2 (0x0100 | 2) +#define Q931_N3 (0x0100 | 3) +#define Q931_N4 (0x0100 | 4) +#define Q931_N6 (0x0100 | 6) +#define Q931_N7 (0x0100 | 7) +#define Q931_N8 (0x0100 | 8) +#define Q931_N9 (0x0100 | 9) +#define Q931_N10 (0x0100 | 11) +#define Q931_N11 (0x0100 | 11) +#define Q931_N12 (0x0100 | 12) +#define Q931_N15 (0x0100 | 15) +#define Q931_N17 (0x0100 | 17) +#define Q931_N19 (0x0100 | 19) +#define Q931_N22 (0x0100 | 22) +#define Q931_N25 (0x0100 | 25) + +/***************************************************************************** + + Q.931 Message codes + +*****************************************************************************/ + +#define Q931mes_ALERTING 0x01 /* 0000 0001 */ +#define Q931mes_CALL_PROCEEDING 0x02 /* 0000 0010 */ +#define Q931mes_CONNECT 0x07 /* 0000 0111 */ +#define Q931mes_CONNECT_ACKNOWLEDGE 0x0f /* 0000 1111 */ +#define Q931mes_PROGRESS 0x03 /* 0000 0011 */ +#define Q931mes_SETUP 0x05 /* 0000 0101 */ +#define Q931mes_SETUP_ACKNOWLEDGE 0x0d /* 0000 1101 */ +#define Q931mes_RESUME 0x26 /* 0010 0110 */ +#define Q931mes_RESUME_ACKNOWLEDGE 0x2e /* 0010 1110 */ +#define Q931mes_RESUME_REJECT 0x22 /* 0010 0010 */ +#define Q931mes_SUSPEND 0x25 /* 0010 0101 */ +#define Q931mes_SUSPEND_ACKNOWLEDGE 0x2d /* 0010 1101 */ +#define Q931mes_SUSPEND_REJECT 0x21 /* 0010 0001 */ +#define Q931mes_USER_INFORMATION 0x20 /* 0010 0000 */ +#define Q931mes_DISCONNECT 0x45 /* 0100 0101 */ +#define Q931mes_RELEASE 0x4d /* 0100 1101 */ +#define Q931mes_RELEASE_COMPLETE 0x5a /* 0101 1010 */ +#define Q931mes_RESTART 0x46 /* 0100 0110 */ +#define Q931mes_RESTART_ACKNOWLEDGE 0x4e /* 0100 1110 */ +#define Q931mes_CONGESTION_CONTROL 0x79 /* 0111 1001 */ +#define Q931mes_INFORMATION 0x7b /* 0111 1011 */ +#define Q931mes_NOTIFY 0x6e /* 0110 1110 */ +#define Q931mes_STATUS 0x7d /* 0111 1101 */ +#define Q931mes_STATUS_ENQUIRY 0x75 /* 0111 0101 */ +#define Q931mes_SEGMENT 0x60 /* 0110 0000 */ + +#define Q931mes_SERVICE 0x0f /* 0000 1111 */ +#define Q931mes_SERVICE_ACKNOWLEDGE 0x07 /* 0000 0111 */ + + +/** + * Generic Q.931 Timers + */ +enum { + Q931_TIMER_T300 = 1, /* */ + Q931_TIMER_T301, + Q931_TIMER_T302, + Q931_TIMER_T303, + Q931_TIMER_T304, + Q931_TIMER_T305, + Q931_TIMER_T306, + Q931_TIMER_T307, + Q931_TIMER_T308, + Q931_TIMER_T309, + Q931_TIMER_T310, + Q931_TIMER_T311, + Q931_TIMER_T312, + Q931_TIMER_T313, + Q931_TIMER_T314, + Q931_TIMER_T315, + Q931_TIMER_T316, + Q931_TIMER_T317, + Q931_TIMER_T318, + Q931_TIMER_T319, + Q931_TIMER_T320, + Q931_TIMER_T321, + Q931_TIMER_T322, +}; + +/** + * Q.931 ToN + */ +enum { + Q931_TON_UNKNOWN = 0x00, + Q931_TON_INTERNATIONAL = 0x01, + Q931_TON_NATIONAL = 0x02, + Q931_TON_NETWORK_SPECIFIC = 0x03, + Q931_TON_SUBSCRIBER_NUMBER = 0x04, + Q931_TON_ABBREVIATED_NUMBER = 0x06, + Q931_TON_RESERVED = 0x07 +}; + +/** + * Q.931 Numbering Plan + */ +enum { + Q931_NUMPLAN_UNKNOWN = 0x00, + Q931_NUMPLAN_E164 = 0x01, + Q931_NUMPLAN_X121 = 0x03, + Q931_NUMPLAN_F69 = 0x04, + Q931_NUMPLAN_NATIONAL = 0x08, + Q931_NUMPLAN_PRIVATE = 0x09, + Q931_NUMPLAN_RESERVED = 0x0e +}; + +/** + * Q.931 Presentation Indicator + */ +enum { + Q931_PRES_ALLOWED = 0x00, + Q931_PRES_RESTRICTED = 0x01, + Q931_PRES_NOT_AVAILABLE = 0x02, + Q931_PRES_RESERVED = 0x03 +}; + +/** + * Q.931 Screening Indicator + */ +enum { + Q931_SCREEN_USER_NOT_SCREENED = 0x00, + Q931_SCREEN_USER_VERIFIED_PASSED = 0x01, + Q931_SCREEN_USER_VERIFIED_FAILED = 0x02, + Q931_SCREEN_NETWORK = 0x03 +}; + +/** + * Q.931 Coding Standard + */ +enum { + Q931_CODING_ITU = 0x00, + Q931_CODING_ISO = 0x01, + Q931_CODING_NATIONAL = 0x02, + Q931_CODING_NETWORK = 0x03 +}; + +/** + * Q.931 High layer characteristik id + */ +enum { + Q931_HLCHAR_TELEPHONY = 0x01, + Q931_HLCHAR_FAX_G23 = 0x04, + Q931_HLCHAR_FAX_G4 = 0x21, + Q931_HLCHAR_FAX_G4II = 0x24, + Q931_HLCHAR_T102 = 0x32, + Q931_HLCHAR_T101 = 0x33, + Q931_HLCHAR_F60 = 0x35, + Q931_HLCHAR_X400 = 0x38, + Q931_HLCHAR_X200 = 0x41 +}; + +/** + * Q.931 User information layer 1 protocol + */ +enum { + Q931_UIL1P_V110 = 0x01, + Q931_UIL1P_I460 = 0x01, + Q931_UIL1P_X30 = 0x01, + + Q931_UIL1P_G711U = 0x02, + Q931_UIL1P_G711A = 0x03, + Q931_UIL1P_G721 = 0x04, + + Q931_UIL1P_H221 = 0x05, + Q931_UIL1P_H242 = 0x05, + + Q931_UIL1P_H223 = 0x06, + Q931_UIL1P_H245 = 0x06, + + Q931_UIL1P_RATE_ADAP = 0x07, + + Q931_UIL1P_V120 = 0x08, + Q931_UIL1P_X31 = 0x09 +}; + +/** + * Q.931 Information Transfer Capability + */ +enum { + Q931_ITC_SPEECH = 0x00, + Q931_ITC_UNRESTRICTED = 0x08, + Q931_ITC_RESTRICTED = 0x09, + Q931_ITC_3K1_AUDIO = 0x10, + Q931_ITC_UNRESTRICTED_TONES = 0x11, + Q931_ITC_VIDEO = 0x18 +}; + +/** + * Q.931 Information transfer rate + */ +enum { + Q931_ITR_PACKET = 0x00, + Q931_ITR_64K = 0x10, + Q931_ITR_128K = 0x11, + Q931_ITR_384K = 0x13, + Q931_ITR_1536K = 0x15, + Q931_ITR_1920K = 0x17, + Q931_ITR_MULTI = 0x18 +}; + +/***************************************************************************** + + Struct: Q931mes_Header + + Description: Used to read the header & message code. + +*****************************************************************************/ +typedef struct { + L3UINT Size; /* Size of message in bytes */ + L3UCHAR ProtDisc; /* Protocol Discriminator */ + L3UCHAR MesType; /* Message type */ + L3UCHAR CRVFlag; /* Call reference value flag */ + L3INT CRV; /* Call reference value */ + +} Q931mes_Header; + +/***************************************************************************** + + Struct: Q931mes_Generic + + Description: Generic header containing all IE's. This is not used, but is + provided in case a proprietary variant needs it. + +*****************************************************************************/ +typedef struct { + L3UINT Size; /* Size of message in bytes */ + L3UCHAR ProtDisc; /* Protocol Discriminator */ + L3UCHAR MesType; /* Message type */ + L3UCHAR CRVFlag; /* Call reference value flag */ + L3INT CRV; /* Call reference value */ + + /* WARNING: don't touch anything above this line (TODO: use Q931mes_Header directly to make sure it's the same) */ + + L3UCHAR Tei; /* TEI */ + + ie Shift; + ie MoreData; + ie SendComplete; + ie CongestionLevel; + ie RepeatInd; + + ie Segment; /* Segmented message */ + ie BearerCap; /* Bearer Capability */ + ie Cause; /* Cause */ + ie CallState; /* Call State */ + ie CallID; /* Call Identity */ + ie ChanID; /* Channel Identification */ + ie ChangeStatus; /* Change Staus */ + ie ProgInd; /* Progress Indicator */ + ie NetFac; /* Network Spesific Facilities */ + ie NotifInd; /* Notification Indicator */ + ie Display; /* Display */ + ie DateTime; /* Date/Time */ + ie KeypadFac; /* Keypad Facility */ + ie Signal; /* Signal */ + ie InfoRate; /* Information rate */ + ie EndEndTxDelay; /* End to End Transmit Delay */ + ie TransDelSelInd; /* Transmit Delay Sel. and Ind. */ + ie PackParam; /* Packed Layer Binary parameters */ + ie PackWinSize; /* Packet Layer Window Size */ + ie PackSize; /* Packed Size */ + ie ClosedUserGrp; /* Closed User Group */ + ie RevChargeInd; /* Reverse Charging Indicator */ + ie CalledNum; /* Called Party Number */ + ie CalledSub; /* Called Party subaddress */ + ie CallingNum; /* Calling Party Number */ + ie CallingSub; /* Calling Party Subaddress */ + ie RedirNum; /* Redirection Number */ + ie TransNetSel; /* Transmit Network Selection */ + ie LLRepeatInd; /* Repeat Indicator 2 LLComp */ + ie RestartWin; /* Restart Window */ + ie RestartInd; /* Restart Indicator */ + ie LLComp; /* Low Layer Compatibility */ + ie HLComp; /* High Layer Compatibility */ + ie UserUser; /* User-user */ + ie Escape; /* Escape for extension */ + ie Switchhook; + ie FeatAct; + ie FeatInd; + ie GenericDigits; + + L3UCHAR buf[1]; /* Buffer for IE's */ + +} Q931mes_Generic; + + +/***************************************************************************** + + Struct: Q931_TrunkInfo + + Description: TrunkInfo is the struct entry used to store Q.931 related + information and state for E1/T1/J1 trunks and associated + channels in the system. + + The user should store this information outside this stack + and needs to feed the interface functions with a pointer to + the TrunkInfo entry. + +*****************************************************************************/ +typedef struct Q931_TrunkInfo Q931_TrunkInfo_t; + +typedef enum { + Q931_LOG_NONE = -1, + Q931_LOG_EMERG, + Q931_LOG_ALERT, + Q931_LOG_CRIT, + Q931_LOG_ERROR, + Q931_LOG_WARNING, + Q931_LOG_NOTICE, + Q931_LOG_INFO, + Q931_LOG_DEBUG +} Q931LogLevel_t; + +typedef L3INT (*Q931Tx34CB_t) (void *,L3UCHAR *, L3INT); +typedef L3INT (*Q931Tx32CB_t) (void *, L3INT, L3UCHAR, L3UCHAR *, L3INT); +typedef L3INT (*Q931ErrorCB_t) (void *,L3INT,L3INT,L3INT); +typedef L3INT (*Q931LogCB_t) (void *, Q931LogLevel_t, char *, L3INT); + +typedef enum { /* Network/User Mode. */ + Q931_TE=0, /* 0 : User Mode */ + Q931_NT=1 /* 1 : Network Mode */ +} Q931NetUser_t; + +typedef enum { /* Dialect enum */ + Q931_Dialect_Q931 = 0, + Q931_Dialect_National = 2, + Q931_Dialect_DMS = 4, + Q931_Dialect_5ESS = 6, /* Coming soon to a PRI stack near you! */ + + Q931_Dialect_Count +} Q931Dialect_t; +#define DIALECT_STRINGS "q931", "", "national", "", "dms", "", "5ess", "" +Q931_STR2ENUM_P(q931_str2Q931Dialect_type, q931_Q931Dialect_type2str, Q931Dialect_t) + +typedef enum { /* Trunk Line Type. */ + Q931_TrType_E1 = 0, /* 0 : E1 Trunk */ + Q931_TrType_T1 = 1, /* 1 : T1 Trunk */ + Q931_TrType_J1 = 2, /* 2 : J1 Trunk */ + Q931_TrType_BRI = 3, /* 3 : BRI Trunk */ + Q931_TrType_BRI_PTMP = 4 /* 4 : BRI PTMP Trunk */ +} Q931_TrunkType_t; + +typedef enum { /* Trunk State */ + Q931_TrState_NoAlignment=0, /* Trunk not aligned */ + Q931_TrState_Aligning=1, /* Aligning in progress */ + Q931_TrState_Aligned=2 /* Trunk Aligned */ +} Q931_TrunkState_t; + +typedef enum { + Q931_ChType_NotUsed=0, /* Unused Channel */ + Q931_ChType_B=1, /* B Channel (Voice) */ + Q931_ChType_D=2, /* D Channel (Signalling) */ + Q931_ChType_Sync=3 /* Sync Channel */ +} Q931_ChanType_t; + +struct Q931_Call +{ + L3UCHAR InUse; /* Indicate if entry is in use. */ + /* 0 = Not in Use */ + /* 1 = Active Call. */ + + L3UCHAR Tei; /* Associated TEI */ + + L3UCHAR BChan; /* Associated B Channel. */ + /* 0 - 31 valid B chan */ + /* 255 = Not allocated */ + + L3INT CRV; /* Associated CRV. */ + + L3UINT State; /* Call State. */ + /* 0 is Idle, but other values are */ + /* defined per dialect. */ + /* Default usage is 1-99 for TE and */ + /* 101 - 199 for NT. */ + + L3ULONG Timer; /* Timer in ms. The TimeTick will check */ + /* if this has exceeded the timeout, and */ + /* if so call the timers timeout proc. */ + + L3USHORT TimerID; /* Timer Identification/State */ + /* actual values defined by dialect */ + /* 0 : No timer running */ + /* ITU-T Q.931:301 - 322 Timer running */ +}; + +struct Q931_TrunkInfo +{ + Q931NetUser_t NetUser; /* Network/User Mode. */ + Q931_TrunkType_t TrunkType; /* Trunk Line Type. */ + Q931Dialect_t Dialect; /* Q.931 Based dialect index. */ + + Q931Tx34CB_t Q931Tx34CBProc; + Q931Tx32CB_t Q931Tx32CBProc; + Q931ErrorCB_t Q931ErrorCBProc; + Q931LogCB_t Q931LogCBProc; + void *PrivateData32; + void *PrivateData34; + void *PrivateDataLog; + + Q931LogLevel_t loglevel; + + L3UCHAR Enabled; /* Enabled/Disabled */ + /* 0 = Disabled */ + /* 1 = Enabled */ + + Q931_TrunkState_t TrunkState; + + L3INT LastCRV; /* Last used crv for the trunk. */ + + L3UCHAR L3Buf[Q931L4BUF]; /* message buffer for messages to be */ + /* send from Q.931 L4. */ + + L3UCHAR L2Buf[Q931L2BUF]; /* buffer for messages send to L2. */ + + /* The auto flags below switch on/off automatic Ack messages. SETUP ACK */ + /* as an example can be sent by the stack in response to SETUP to buy */ + /* time in processing on L4. Setting this to true will cause the stack */ + /* to automatically send this. */ + + L3BOOL autoSetupAck; /* Indicate if the stack should send */ + /* SETUP ACK or not. 0=No, 1 = Yes. */ + + L3BOOL autoConnectAck; /* Indicate if the stack should send */ + /* CONNECT ACT or not. 0=No, 1=Yes. */ + + L3BOOL autoRestartAck; /* Indicate if the stack should send */ + /* RESTART ACK or not. 0=No, 1=Yes. */ + + L3BOOL autoServiceAck; /* Indicate if the stack should send */ + /* SERVICE ACK or not. 0=No, 1=Yes. */ + + /* channel array holding info per channel. Usually defined to 32 */ + /* channels to fit an E1 since T1/J1 and BRI will fit inside a E1. */ + struct _charray + { + Q931_ChanType_t ChanType; /* Unused, B, D, Sync */ + + L3UCHAR Available; /* Channel Available Flag */ + /* 0 : Avaiabled */ + /* 1 : Used */ + + L3INT CRV; /* Associated CRV */ + + } ch[Q931MAXCHPERTRUNK]; + + /* Active Call information indentified by CRV. See Q931AllocateCRV for */ + /* initialization of call table. */ + struct Q931_Call call[Q931MAXCALLPERTRUNK]; +}; + +/***************************************************************************** + + Struct: Q931State + + Description: Define a Q931 State, legal events and next state for each + event. Used to simplify the state engine logic. Each state + engine defines its own state table and the logic need only + call a helper function to check if the message is legal + at this stage. + +*****************************************************************************/ +typedef struct +{ + L3INT State; + L3INT Message; + L3UCHAR Direction; +} Q931State; + +/***************************************************************************** + + Proc table external references. + + The proc tables are defined in Q931.c and initialized in Q931Initialize. + +*****************************************************************************/ +typedef L3INT (q931proc_func_t) (Q931_TrunkInfo_t *pTrunk, L3UCHAR *, L3INT); + +typedef L3INT (q931umes_func_t) (Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +typedef L3INT (q931pmes_func_t) (Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +typedef L3INT (q931uie_func_t) (Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff); +typedef L3INT (q931pie_func_t) (Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); + +typedef L3INT (q931timeout_func_t) (Q931_TrunkInfo_t *pTrunk, L3INT callIndex); +typedef L3ULONG q931timer_t; + +extern q931proc_func_t *Q931Proc[Q931MAXDLCT][Q931MAXMES]; + +extern q931umes_func_t *Q931Umes[Q931MAXDLCT][Q931MAXMES]; +extern q931pmes_func_t *Q931Pmes[Q931MAXDLCT][Q931MAXMES]; + +extern q931uie_func_t *Q931Uie[Q931MAXDLCT][Q931MAXIE]; +extern q931pie_func_t *Q931Pie[Q931MAXDLCT][Q931MAXIE]; + +extern q931timeout_func_t *Q931Timeout[Q931MAXDLCT][Q931MAXTIMER]; +extern q931timer_t Q931Timer[Q931MAXDLCT][Q931MAXTIMER]; + + +/***************************************************************************** + + Macro: GetIETotoSize + + Syntax: L3INT GetIETotSize(InfoElem ie); + + Description: Compute the total size in bytes of an info element including + size of 'header'. + +*****************************************************************************/ +#define Q931GetIETotSize(ie) (((ie.InfoID & 0x80) != 0) ? 1 : ie.LenIE) + 2) + +/***************************************************************************** + + Macro: IsIEPresent + + Syntax: BOOL IsIEPresent(ie InfoElement); + + Description: Return TRUE if the Information Element is included. + +*****************************************************************************/ +#define Q931IsIEPresent(x) ((x & 0x8000) != 0) + +/***************************************************************************** + + Macro: GetIEOffset and GetIEValue + + Syntax: L3INT GetIEOffset(ie InfoElement) + L3INT GetIEValue(ie InfoElement) + + Description: Returns the offset (or the value )to the Information Element. + + Note: GetIEValue assumes that the 15 lsb bit is the value of a + single octet information element. This macro can not be used + on a variable information element. + +*****************************************************************************/ +#define Q931GetIEOffset(x) (x & 0x7fff) +#define Q931GetIEValue(x) (x & 0x7fff) + +/***************************************************************************** + + Macro: Q931GetIEPtr + + Syntax: void * Q931GetIEPtr(ie InfoElement, L3UCHAR * Buf); + + Description: Compute a Ptr to the information element. + +*****************************************************************************/ +#define Q931GetIEPtr(ie,buf) ((void *)&buf[Q931GetIEOffset(ie)]) + +/***************************************************************************** + + Macro: SetIE + + Syntax: void SetIE(ie InfoElement, L3INT Offset); + + Description: Set an information element. + +*****************************************************************************/ +#define Q931SetIE(x,o) { x = (ie)(o) | 0x8000; } + +/***************************************************************************** + + Macro: IsQ931Ext + + Syntax BOOL IsQ931Ext(L3UCHAR c) + + Description: Return true Check if the msb (bit 8) is 0. This indicate + that the octet is extended. + +*****************************************************************************/ +#define IsQ931Ext(x) ((x & 0x80) == 0) + +/***************************************************************************** + + Macro: ieGetOctet + + Syntax: unsigned L3UCHAR ieGetOctet(L3INT e) + + Description: Macro to fetch one byte from an integer. Mostly used to + avoid warnings. + +*****************************************************************************/ +#define ieGetOctet(x) ((L3UCHAR)(x)) + +/***************************************************************************** + + Macro: NoWarning + + Syntax: void NoWarning(x) + + Description: Macro to suppress unreferenced formal parameter warnings + + Used during creation of the stack since the stack is + developed for Warning Level 4 and this creates a lot of + warning for the initial empty functions. + +*****************************************************************************/ +#define NoWarning(x) (x = x) + +/***************************************************************************** + + External references. See Q931.c for details. + +*****************************************************************************/ + +#include "Q931ie.h" + +#include "Q932.h" + +/***************************************************************************** + + Q.931 Message Pack/Unpack functions. Implemented in Q931mes.c + +*****************************************************************************/ +L3INT Q931Pmes_Alerting(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_CallProceeding(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Connect(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ConnectAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Progress(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_SetupAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Resume(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ResumeAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ResumeReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Suspend(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_SuspendAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_SuspendReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_UserInformation(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Disconnect(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Release(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ReleaseComplete(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Restart(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_RestartAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_CongestionControl(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Information(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Notify(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Segment(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Status(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_StatusEnquiry(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Service(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ServiceAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + + +L3INT Q931Umes_Alerting(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_CallProceeding(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Connect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_ConnectAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Progress(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_SetupAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Resume(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_ResumeAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_ResumeReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Suspend(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_SuspendAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_SuspendReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_UserInformation(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Disconnect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Release(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_ReleaseComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Restart(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_RestartAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_CongestionControl(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Information(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Notify(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Segment(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Status(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_StatusEnquiry(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Service(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT Q931Umes_ServiceAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); + + +/***************************************************************************** + + Q.931 Process Function Prototyping. Implemented in Q931StateTE.c + +*****************************************************************************/ +L3INT Q931ProcAlertingTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcCallProceedingTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcConnectTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcConnectAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcProgressTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSetupTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSetupAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeRejectTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendRejectTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcUserInformationTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcDisconnectTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcReleaseTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcReleaseCompleteTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcRestartTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcRestartAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcCongestionControlTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcInformationTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcNotifyTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcStatusTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcStatusEnquiryTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSegmentTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); + +L3INT Q931ProcAlertingNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcCallProceedingNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcConnectNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcConnectAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcProgressNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSetupNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSetupAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeRejectNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendRejectNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcUserInformationNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcDisconnectNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcReleaseNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcReleaseCompleteNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcRestartNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcRestartAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcCongestionControlNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcInformationNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcNotifyNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcStatusNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcStatusEnquiryNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSegmentNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); + +L3INT Q931ProcUnknownMessage(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcUnexpectedMessage(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); + +/***************************************************************************** + + Interface Function Prototypes. Implemented in Q931.c + +*****************************************************************************/ +void Q931TimerTick(Q931_TrunkInfo_t *pTrunk); +L3INT Q931Rx23(Q931_TrunkInfo_t *pTrunk, L3INT ind, L3UCHAR tei, L3UCHAR * Mes, L3INT Size); +L3INT Q931Tx32Data(Q931_TrunkInfo_t *pTrunk, L3UCHAR bcast, L3UCHAR * Mes, L3INT Size); +L3INT Q931Rx43(Q931_TrunkInfo_t *pTrunk, L3UCHAR * Mes, L3INT Size); +L3INT Q931Tx34(Q931_TrunkInfo_t *pTrunk, L3UCHAR * Mes, L3INT Size); +void Q931SetError(Q931_TrunkInfo_t *pTrunk,L3INT ErrID, L3INT ErrPar1, L3INT ErrPar2); + +void Q931SetDefaultErrorCB(Q931ErrorCB_t Q931ErrorPar); + +void Q931CreateTE(L3UCHAR i); +void Q931CreateNT(L3UCHAR i); +void Q931SetMesCreateCB(L3INT (*callback)(void)); +void Q931SetDialectCreateCB(L3INT (*callback)(L3INT)); +void Q931SetHeaderSpace(L3INT space); + +void Q931SetMesProc(L3UCHAR mes, L3UCHAR dialect, q931proc_func_t *Q931ProcFunc, q931umes_func_t *Q931UmesFunc, q931pmes_func_t *Q931PmesFunc); +void Q931SetIEProc(L3UCHAR iec, L3UCHAR dialect, q931pie_func_t *Q931PieProc, q931uie_func_t *Q931UieProc); +void Q931SetTimeoutProc(L3UCHAR dialect, L3UCHAR timer, q931timeout_func_t *Q931TimeoutProc); +void Q931SetTimerDefault(L3UCHAR dialect, L3UCHAR timer, q931timer_t timeout); + +void Q931Initialize(void); +void Q931AddDialect(L3UCHAR iDialect, void (*Q931CreateDialectCB)(L3UCHAR iDialect)); +L3INT Q931InitMesSetup(Q931mes_Generic *p); +L3INT Q931InitMesRestartAck(Q931mes_Generic * pMes); +L3INT Q931InitMesGeneric(Q931mes_Generic *pMes); + +L3INT Q931CreateCRV(Q931_TrunkInfo_t *pTrunk, L3INT * callIndex); +L3INT Q931ReleaseCRV(Q931_TrunkInfo_t *pTrunk, L3INT CRV); +L3INT Q931AllocateCRV(Q931_TrunkInfo_t *pTrunk, L3INT iCRV, L3INT * callIndex); +L3INT Q931FindCRV(Q931_TrunkInfo_t *pTrunk, L3INT crv, L3INT *callindex); +L3INT Q931GetCallState(Q931_TrunkInfo_t *pTrunk, L3INT iCRV); +L3INT Q931StartTimer(Q931_TrunkInfo_t *pTrunk, L3INT callIndex, L3USHORT iTimer); +L3INT Q931StopTimer(Q931_TrunkInfo_t *pTrunk, L3INT callindex, L3USHORT iTimer); +L3INT Q931SetState(Q931_TrunkInfo_t *pTrunk, L3INT callIndex, L3INT iState); +L3ULONG Q931GetTime(void); +void Q931SetGetTimeCB(L3ULONG (*callback)(void)); +void Q931AddStateEntry(L3UCHAR iD, L3INT iState, L3INT iMes, L3UCHAR cDir); +L3BOOL Q931IsEventLegal(L3UCHAR iD, L3INT iState, L3INT iMes, L3UCHAR cDir); + +/***************************************************************************** + + Q.931 Low Level API Function Prototyping. Implemented in Q931API.c + +*****************************************************************************/ +ie Q931AppendIE(L3UCHAR *pm, L3UCHAR *pi); +L3INT Q931GetUniqueCRV(Q931_TrunkInfo_t *pTrunk); + +L3INT Q931InitIEBearerCap(Q931ie_BearerCap *p); +L3INT Q931InitIEChanID(Q931ie_ChanID *p); +L3INT Q931InitIEProgInd(Q931ie_ProgInd *p); +L3INT Q931InitIENetFac(Q931ie_NetFac * pIE); +L3INT Q931InitIEDisplay(Q931ie_Display * pIE); +L3INT Q931InitIEDateTime(Q931ie_DateTime * pIE); +L3INT Q931InitIEKeypadFac(Q931ie_KeypadFac * pIE); +L3INT Q931InitIESignal(Q931ie_Signal * pIE); +L3INT Q931InitIECallingNum(Q931ie_CallingNum * pIE); +L3INT Q931InitIECallingSub(Q931ie_CallingSub * pIE); +L3INT Q931InitIECalledNum(Q931ie_CalledNum * pIE); +L3INT Q931InitIECalledSub(Q931ie_CalledSub * pIE); +L3INT Q931InitIETransNetSel(Q931ie_TransNetSel * pIE); +L3INT Q931InitIELLComp(Q931ie_LLComp * pIE); +L3INT Q931InitIEHLComp(Q931ie_HLComp * pIE); + +L3INT Q931Disconnect(Q931_TrunkInfo_t *pTrunk, L3INT iTo, L3INT iCRV, L3INT iCause); +L3INT Q931ReleaseComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); +L3INT Q931AckRestart(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); +L3INT Q931AckConnect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); +L3INT Q931AckSetup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); +L3INT Q931AckService(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); + +L3INT Q931Api_InitTrunk(Q931_TrunkInfo_t *pTrunk, + Q931Dialect_t Dialect, + Q931NetUser_t NetUser, + Q931_TrunkType_t TrunkType, + Q931Tx34CB_t Q931Tx34CBProc, + Q931Tx32CB_t Q931Tx32CBProc, + Q931ErrorCB_t Q931ErrorCBProc, + void *PrivateData32, + void *PrivateData34); + +L3INT Q931GetMesSize(Q931mes_Generic *pMes); +L3INT Q931InitMesResume(Q931mes_Generic * pMes); + +L3INT Q931Log(Q931_TrunkInfo_t *trunk, Q931LogLevel_t level, const char *fmt, ...); +void Q931SetLogCB(Q931_TrunkInfo_t *trunk, Q931LogCB_t func, void *priv); +void Q931SetLogLevel(Q931_TrunkInfo_t *trunk, Q931LogLevel_t level); + +void Q931SetL4HeaderSpace(L3INT space); +void Q931SetL2HeaderSpace(L3INT space); +L3INT Q931ProcDummy(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b,L3INT c); +L3INT Q931UmesDummy(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT Q931UieDummy(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff); +L3INT Q931PmesDummy(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931PieDummy(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); +L3INT Q931TxDummy(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT n); +L3INT Q931ErrorDummy(void *priv, L3INT a, L3INT b, L3INT c); +L3INT Q931TimeoutDummy(Q931_TrunkInfo_t *pTrunk, L3INT callIndex); + +L3INT Q931MesgHeader(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *mes, L3UCHAR *OBuf, L3INT Size, L3INT *IOff); + +#endif /* _Q931_NL */ diff --git a/libs/freetdm/src/isdn/include/Q931ie.h b/libs/freetdm/src/isdn/include/Q931ie.h new file mode 100644 index 0000000000..65dba4e261 --- /dev/null +++ b/libs/freetdm/src/isdn/include/Q931ie.h @@ -0,0 +1,1205 @@ +/****************************************************************************** + + FileName: Q931ie.h + + Contents: Header and definition for the ITU-T Q.931 ie + structures and functions + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 _Q931IE_NL +#define _Q931IE_NL + +/* Codesets */ + +typedef enum { + + Q931_CODESET_0 = ( 0 ), + Q931_CODESET_1 = ( 1 << 8 ), + Q931_CODESET_2 = ( 2 << 8 ), + Q931_CODESET_3 = ( 3 << 8 ), + Q931_CODESET_4 = ( 4 << 8 ), + Q931_CODESET_5 = ( 5 << 8 ), + Q931_CODESET_6 = ( 6 << 8 ), + Q931_CODESET_7 = ( 7 << 8 ) + +} q931_codeset_t; + +/* Single octet information elements */ +#define Q931ie_SHIFT 0x90 /* 1001 ---- */ +#define Q931ie_MORE_DATA 0xa0 /* 1010 ---- */ +#define Q931ie_SENDING_COMPLETE 0xa1 /* 1010 0001 */ +#define Q931ie_CONGESTION_LEVEL 0xb0 /* 1011 ---- */ +#define Q931ie_REPEAT_INDICATOR 0xd0 /* 1101 ---- */ + +/* Variable Length Information Elements */ +#define Q931ie_SEGMENTED_MESSAGE 0x00 /* 0000 0000 */ +#define Q931ie_CHANGE_STATUS 0x01 /* 0000 0001 */ +#define Q931ie_BEARER_CAPABILITY 0x04 /* 0000 0100 */ +#define Q931ie_CAUSE 0x08 /* 0000 1000 */ +#define Q931ie_CALL_IDENTITY 0x10 /* 0001 0000 */ +#define Q931ie_CALL_STATE 0x14 /* 0001 0100 */ +#define Q931ie_CHANNEL_IDENTIFICATION 0x18 /* 0001 1000 */ +#define Q931ie_PROGRESS_INDICATOR 0x1e /* 0001 1110 */ +#define Q931ie_NETWORK_SPECIFIC_FACILITIES 0x20 /* 0010 0000 */ +#define Q931ie_NOTIFICATION_INDICATOR 0x27 /* 0010 0111 */ +#define Q931ie_DISPLAY 0x28 /* 0010 1000 */ +#define Q931ie_DATETIME 0x29 /* 0010 1001 */ +#define Q931ie_KEYPAD_FACILITY 0x2c /* 0010 1100 */ +#define Q931ie_SIGNAL 0x34 /* 0011 0100 */ +#define Q931ie_SWITCHOOK 0x36 /* 0011 0110 */ +#define Q931ie_FEATURE_ACTIVATION 0x38 /* 0011 1000 */ +#define Q931ie_FEATURE_INDICATION 0x39 /* 0011 1001 */ +#define Q931ie_INFORMATION_RATE 0x40 /* 0100 0000 */ +#define Q931ie_END_TO_END_TRANSIT_DELAY 0x42 /* 0100 0010 */ +#define Q931ie_TRANSIT_DELAY_SELECTION_AND_IND 0x43 /* 0100 0011 */ +#define Q931ie_PACKED_LAYER_BIMARY_PARAMETERS 0x44 /* 0100 0100 */ +#define Q931ie_PACKED_LAYER_WINDOW_SIZE 0x45 /* 0100 0101 */ +#define Q931ie_PACKED_SIZE 0x46 /* 0100 0110 */ +#define Q931ie_CALLING_PARTY_NUMBER 0x6c /* 0110 1100 */ +#define Q931ie_CALLING_PARTY_SUBADDRESS 0x6d /* 0110 1101 */ +#define Q931ie_CALLED_PARTY_NUMBER 0x70 /* 0111 0000 */ +#define Q931ie_CALLED_PARTY_SUBADDRESS 0x71 /* 0111 0001 */ +#define Q931ie_REDIRECTING_NUMBER 0x74 /* 0111 0100 */ +#define Q931ie_TRANSIT_NETWORK_SELECTION 0x78 /* 0111 1000 */ +#define Q931ie_RESTART_INDICATOR 0x79 /* 0111 1001 */ +#define Q931ie_LOW_LAYER_COMPATIBILITY 0x7c /* 0111 1100 */ +#define Q931ie_HIGH_LAYER_COMPATIBILITY 0x7d /* 0111 1101 */ +#define Q931ie_USER_USER 0x7e /* 0111 1110 */ +#define Q931ie_ESCAPE_FOR_EX 0x7f /* 0111 1111 */ + +/* Variable Length Codeset 6 Information Elements */ +#define Q931ie_GENERIC_DIGITS 0x37 /* 0011 0111 */ + +/* Variable Length Information Element to shut up BRI testing */ +#define Q931ie_CONNECTED_NUMBER 0x4c /* 0100 1101 */ +#define Q931ie_FACILITY 0x1c /* 0001 1101 */ + + +/***************************************************************************** + + Struct: Q931ie_BearerCap + + Description: Bearer Capability Information Element. + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00000100 for Bearer Capability */ + L3UCHAR Size; /* Length of Information Element */ + + L3UCHAR CodStand; /* Coding Standard. */ + /* 00 - ITU-T */ + /* 01 - ISO/IEC */ + /* 10 - National standard */ + /* 11 - Network side spesific */ + + L3UCHAR ITC; /* Information Transfer Capability */ + /* 00000 - Speech */ + /* 01000 - Unrestricted digital info */ + /* 01001 - Restricted digital info */ + /* 10000 - 3.1 kHz audio */ + /* 10001 - Unrestricted with tones */ + /* 11000 - Video */ + + L3UCHAR TransMode; /* Transfer Mode. */ + /* 00 - Circuit mode */ + /* 10 - Packet mode */ + + L3UCHAR ITR; /* Information Transfer Rate. */ + /* 00000 - Packed mode */ + /* 10000 - 64 kbit/s */ + /* 10001 - 2 x 64 kbit/s */ + /* 10011 - 384 kbit/s */ + /* 10101 - 1536 kbit/s */ + /* 10111 - 1920 kbit/s */ + /* 11000 - Multirat (64 kbit/s base) */ + + L3UCHAR RateMul; /* Rate Multiplier */ + + L3UCHAR Layer1Ident; /* Layer 1 Ident. */ + + L3UCHAR UIL1Prot; /* User Information Layer 1 Protocol */ + /* 00001 : ITU-T V.110, I.460 and X.30 */ + /* 00010 : G.711 my-law */ + /* 00011 : G.711 A-law */ + /* 00100 : G.721 */ + /* 00101 : H.221 and H.242 */ + /* 00110 : H.223 and H.245 */ + /* 00111 : Non ITU-T Standard */ + /* 01000 : ITU-T V.120 */ + /* 01001 : ITU-T X.31 HDLC flag stuff. */ + + L3UCHAR SyncAsync; /* Sync/Async */ + /* 0 : Syncronous data */ + /* 1 : Asyncronous data */ + + L3UCHAR Negot; /* Negotiation */ + /* 0 : In-band negotiation not possib. */ + /* 1 : In-band negotiation possible */ + + L3UCHAR UserRate; /* User rate */ + /* 00000 : I.460, V.110, X,30 */ + /* 00001 : 0.6 kbit/s x.1 */ + /* 00010 : 1.2 kbit/s */ + /* 00011 : 2.4 kbit/s */ + /* 00100 : 3.6 kbit/s */ + /* 00101 : 4.8 kbit/s */ + /* 00110 : 7.2 kbit/s */ + /* 00111 : 8 kbit/s I.460 */ + /* 01000 : 9.6 kbit/s */ + /* 01001 : 14.4 kbit/s */ + /* 01010 : 16 kbit/s */ + /* 01011 : 19.2 kbit/s */ + /* 01100 : 32 kbit/s */ + /* 01101 : 38.4 kbit/s */ + /* 01110 : 48 kbit/s */ + /* 01111 : 56 kbit/s */ + /* 10000 : 57.6 kbit/s */ + /* 10010 : 28.8 kbit/s */ + /* 10100 : 24 kbit/s */ + /* 10101 : 0.1345 kbit/s */ + /* 10110 : 0.100 kbit/s */ + /* 10111 : 0.075/1.2 kbit/s */ + /* 11000 : 1.2/0.075/kbit/s */ + /* 11001 : 0.050 kbit/s */ + /* 11010 : 0.075 kbit/s */ + /* 11011 : 0.110 kbit/s */ + /* 11100 : 0.150 kbit/s */ + /* 11101 : 0.200 kbit/s */ + /* 11110 : 0.300 kbit/s */ + /* 11111 : 12 kbit/s */ + + L3UCHAR InterRate; /* Intermediate Rate */ + /* 00 : Not used */ + /* 01 : 8 kbit/s */ + /* 10 : 16 kbit/s */ + /* 11 : 32 kbit/s */ + + L3UCHAR NIConTx; /* Network Indepentend Clock on transmit*/ + /* 0 : Not required to send data clc */ + /* 1 : Send data w/NIC clc */ + + L3UCHAR NIConRx; /* NIC on Rx */ + /* 0 : Cannot accept indep. clc */ + /* 1 : data with indep. clc accepted */ + + L3UCHAR FlowCtlTx; /* Flow control on Tx */ + /* 0 : Send Flow ctrl not required */ + /* 1 : Send flow ctrl required */ + + L3UCHAR FlowCtlRx; /* Flow control on Rx */ + /* 0 : cannot use receive flow ctrl */ + /* 1 : Receive flow ctrl accepted */ + L3UCHAR HDR; /* HDR/No HDR */ + L3UCHAR MultiFrame; /* Multi frame support */ + /* 0 : multiframe not supported */ + /* 1 : multiframe supported */ + + L3UCHAR Mode; /* Mode of operation */ + /* 0 : bit transparent mode of operat. */ + /* 1 : protocol sesitive mode of op. */ + + L3UCHAR LLInegot; /* Logical link id negotiation (oct. 5b)*/ + /* 0 : default LLI=256 only */ + /* 1 : Full protocol negotiation */ + + L3UCHAR Assignor; /* Assignor/assignee */ + /* 0 : Default Asignee */ + /* 1 : Assignor only */ + + L3UCHAR InBandNeg; /* In-band/out-band negot. */ + /* 0 : negot done w/ USER INFO mes */ + /* 1 : negot done in-band w/link zero */ + + L3UCHAR NumStopBits; /* Number of stop bits */ + /* 00 : Not used */ + /* 01 : 1 bit */ + /* 10 : 1.5 bits */ + /* 11 : 2 bits */ + + L3UCHAR NumDataBits; /* Number of data bits. */ + /* 00 : not used */ + /* 01 : 5 bits */ + /* 10 : 7 bits */ + /* 11 : 8 bits */ + + L3UCHAR Parity; /* Parity Information */ + /* 000 : Odd */ + /* 010 : Even */ + /* 011 : None */ + /* 100 : Forced to 0 */ + /* 101 : Forced to 1 */ + + L3UCHAR DuplexMode; /* Mode duplex */ + /* 0 : Half duplex */ + /* 1 : Full duplex */ + + L3UCHAR ModemType; /* Modem type, see Q.931 p 64 */ + + L3UCHAR Layer2Ident; /* Layer 2 Ident */ + + L3UCHAR UIL2Prot; /* User Information Layer 2 Protocol */ + /* 00010 : Q.921/I.441 */ + /* 00110 : X.25 */ + /* 01100 : LAN logical link */ + + L3UCHAR Layer3Ident; /* Layer 3 ident. */ + + L3UCHAR UIL3Prot; /* User Information Layer 3 Protocol */ + /* 00010 : Q.931 */ + /* 00110 : X.25 */ + /* 01011 : ISO/IEC TR 9577 */ + + L3UCHAR AL3Info1; /* additional layer 3 info 1 */ + + L3UCHAR AL3Info2; /* additional layer 3 info 2 */ + +} Q931ie_BearerCap; + +/***************************************************************************** + + Struct: Q931ie_CallID + + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00010000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CallId[1]; /* Call identity */ + +} Q931ie_CallID; + +/***************************************************************************** + + Struct: Q931ie_CallState + + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00010100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding Standard */ + L3UCHAR CallState; /* Call State Value */ + +} Q931ie_CallState; + +/***************************************************************************** + + Struct: Q931ie_Cause + + Description: Cause IE as described in Q.850 + +*****************************************************************************/ +typedef struct { + + L3UCHAR IEId; /* 00010100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding Standard */ + L3UCHAR Location; /* Location */ + L3UCHAR Recom; /* Recommendation */ + L3UCHAR Value; /* Cause Value */ + L3UCHAR Diag[1]; /* Optional Diagnostics Field */ + +} Q931ie_Cause; + +/***************************************************************************** + + Struct: Q931ie_CalledNum + +*****************************************************************************/ +typedef struct { + + L3UCHAR IEId; /* 01110000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypNum; /* Type of Number */ + L3UCHAR NumPlanID; /* Numbering plan identification */ + L3UCHAR Digit[1]; /* Digit (IA5) */ + +} Q931ie_CalledNum; + +/***************************************************************************** + + Struct: Q931ie_CalledSub + + Description: Called party subaddress + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01110001 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypNum; /* Type of subaddress */ + L3UCHAR OddEvenInd; /* Odd/Even indicator */ + L3UCHAR Digit[1]; /* digits */ + +} Q931ie_CalledSub; + +/***************************************************************************** + + Struct: Q931ie_CallingNum + + Description: Calling party number + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01101100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypNum; /* Type of number */ + L3UCHAR NumPlanID; /* Numbering plan identification */ + L3UCHAR PresInd; /* Presentation indicator */ + L3UCHAR ScreenInd; /* Screening indicator */ + L3UCHAR Digit[1]; /* Number digits (IA5) */ + +} Q931ie_CallingNum; + +/***************************************************************************** + + Struct: Q931ie_CallingSub + + Description: Calling party subaddress + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01101101 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypNum; /* Type of subaddress */ + L3UCHAR OddEvenInd; /* Odd/Even indicator */ + L3UCHAR Digit[1]; /* digits */ + +} Q931ie_CallingSub; + +/***************************************************************************** + + Struct: Q931ie_ChanID + + Description: Channel identification + + Channel Identificationis one of the IE elements that differ + between BRI and PRI. IntType = 1 = BRI and ChanSlot is used + for channel number, while InfoChanSel is used for BRI. + + ChanID is one of the most important IE as it is passed + either though SETUP or CALL PROCEEDING to select the channel + to be used. + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00011000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR IntIDPresent; /* Int. id. present */ + L3UCHAR IntType; /* Interface Type */ + /* 0 : Basic Interface (BRI) */ + /* 1 : Other interfaces, PRI etc. */ + + L3UCHAR PrefExcl; /* Pref./Excl. */ + /* 0 : Indicated channel is preffered */ + /* 1 : Exclusive, no other accepted */ + + L3UCHAR DChanInd; /* D-channel ind. */ + /* 0 : chan is NOT D chan. */ + /* 1 : chan is D chan */ + + L3UCHAR InfoChanSel; /* Info. channel selection */ + /* 00 : No channel */ + /* 01 : B1 channel */ + /* 10 : B2 channel */ + /* 11 : Any channel */ + + L3UCHAR InterfaceID; /* Interface identifier */ + + L3UCHAR CodStand; /* Code standard */ + /* 00 : ITU-T standardization coding */ + /* 01 : ISO/IEC Standard */ + /* 10 : National Standard */ + /* 11 : Standard def. by network. */ + + L3UCHAR NumMap; /* Number/Map */ + /* 0 : chan is in following octet */ + /* 1 : chan is indicated by slot map */ + + L3UCHAR ChanMapType; /* Channel type/Map element type */ + /* 0011 : B Channel units */ + /* 0110 : H0 channel units */ + /* 1000 : H11 channel units */ + /* 1001 : H12 channel units */ + + L3UCHAR ChanSlot; /* Channel number */ + +} Q931ie_ChanID; + +/***************************************************************************** + + Struct: Q931ie_DateTime + + Description: Date/time + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00101001 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Year; /* Year */ + L3UCHAR Month; /* Month */ + L3UCHAR Day; /* Day */ + L3UCHAR Hour; /* Hour */ + L3UCHAR Minute; /* Minute */ + L3UCHAR Second; /* Second */ + L3UCHAR Format; /* Indicate presense of Hour, Min & sec */ + /* 0 : Only Date */ + /* 1 : Hour present */ + /* 2 : Hour and Minute present */ + /* 3 : Hour, Minute and Second present */ +} Q931ie_DateTime; + +/***************************************************************************** + + Struct: Q931ie_Display + + Description: Display + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00101000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Display[1]; /* Display information (IA5) */ + +} Q931ie_Display; + +/***************************************************************************** + + Struct: Q931ie_HLComp + + Description: High layer compatibility + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01111101 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding standard */ + L3UCHAR Interpret; /* Interpretation */ + L3UCHAR PresMeth; /* Presentation methor of prot. profile */ + L3UCHAR HLCharID; /* High layer characteristics id. */ + L3UCHAR EHLCharID; /* Extended high layer character. id. */ + L3UCHAR EVideoTlfCharID; /* Ext. videotelephony char. id. */ + +} Q931ie_HLComp; + +/***************************************************************************** + + Struct: Q931ie_KeypadFac + + Description: Keypad facility + +*****************************************************************************/ + +typedef struct { + L3UCHAR IEId; /* 00101100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR KeypadFac[1]; /* dynamic buffer */ + +} Q931ie_KeypadFac; + +/***************************************************************************** + + Struct: Q931ie_LLComp + + Description: Low layer compatibility + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01111100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding standard */ + /* 00 - ITU-T */ + /* 01 - ISO/IEC */ + /* 10 - National standard */ + /* 11 - Network side spesific */ + + L3UCHAR ITransCap; /* Information transfer capability */ + /* 00000 - Speech */ + /* 01000 - Unrestricted digital info */ + /* 01001 - Restricted digital info */ + /* 10000 - 3.1 kHz audio */ + /* 10001 - Unrestricted with tones */ + /* 11000 - Video */ + + L3UCHAR NegotInd; /* Negot indic. */ + /* 0 : Out-band neg. not possib. */ + /* 1 : Out-band neg. possible */ + + L3UCHAR TransMode; /* Transfer Mode */ + /* 00 : Circuit Mode */ + /* 10 : Packed Mode */ + + L3UCHAR InfoRate; /* Information transfer rate */ + /* 00000 - Packed mode */ + /* 10000 - 64 kbit/s */ + /* 10001 - 2 x 64 kbit/s */ + /* 10011 - 384 kbit/s */ + /* 10101 - 1536 kbit/s */ + /* 10111 - 1920 kbit/s */ + /* 11000 - Multirat (64 kbit/s base) */ + + L3UCHAR RateMul; /* Rate multiplier */ + L3UCHAR Layer1Ident; /* Layer 1 ident. */ + L3UCHAR UIL1Prot; /* User information layer 1 protocol */ + /* 00001 : ITU-T V.110, I.460 and X.30 */ + /* 00010 : G.711 my-law */ + /* 00011 : G.711 A-law */ + /* 00100 : G.721 */ + /* 00101 : H.221 and H.242 */ + /* 00110 : H.223 and H.245 */ + /* 00111 : Non ITU-T Standard */ + /* 01000 : ITU-T V.120 */ + /* 01001 : ITU-T X.31 HDLC flag stuff. */ + + L3UCHAR SyncAsync; /* Synch/asynch */ + /* 0 : Syncronous data */ + /* 1 : Asyncronous data */ + + L3UCHAR Negot; /* Negot */ + /* 0 : In-band negotiation not possib. */ + /* 1 : In-band negotiation possible */ + + L3UCHAR UserRate; /* User rate */ + /* 00000 : I.460, V.110, X,30 */ + /* 00001 : 0.6 kbit/s x.1 */ + /* 00010 : 1.2 kbit/s */ + /* 00011 : 2.4 kbit/s */ + /* 00100 : 3.6 kbit/s */ + /* 00101 : 4.8 kbit/s */ + /* 00110 : 7.2 kbit/s */ + /* 00111 : 8 kbit/s I.460 */ + /* 01000 : 9.6 kbit/s */ + /* 01001 : 14.4 kbit/s */ + /* 01010 : 16 kbit/s */ + /* 01011 : 19.2 kbit/s */ + /* 01100 : 32 kbit/s */ + /* 01101 : 38.4 kbit/s */ + /* 01110 : 48 kbit/s */ + /* 01111 : 56 kbit/s */ + /* 10000 : 57.6 kbit/s */ + /* 10010 : 28.8 kbit/s */ + /* 10100 : 24 kbit/s */ + /* 10101 : 0.1345 kbit/s */ + /* 10110 : 0.100 kbit/s */ + /* 10111 : 0.075/1.2 kbit/s */ + /* 11000 : 1.2/0.075/kbit/s */ + /* 11001 : 0.050 kbit/s */ + /* 11010 : 0.075 kbit/s */ + /* 11011 : 0.110 kbit/s */ + /* 11100 : 0.150 kbit/s */ + /* 11101 : 0.200 kbit/s */ + /* 11110 : 0.300 kbit/s */ + /* 11111 : 12 kbit/s */ + + L3UCHAR InterRate; /* Intermediate rate */ + /* 00 : Not used */ + /* 01 : 8 kbit/s */ + /* 10 : 16 kbit/s */ + /* 11 : 32 kbit/s */ + + L3UCHAR NIConTx; /* Network Indepentend Clock on transmit*/ + /* 0 : Not required to send data clc */ + /* 1 : Send data w/NIC clc */ + + L3UCHAR NIConRx; /* NIC on Rx */ + /* 0 : Cannot accept indep. clc */ + /* 1 : data with indep. clc accepted */ + + L3UCHAR FlowCtlTx; /* Flow control on Tx */ + /* 0 : Send Flow ctrl not required */ + /* 1 : Send flow ctrl required */ + + L3UCHAR FlowCtlRx; /* Flow control on Rx */ + /* 0 : cannot use receive flow ctrl */ + /* 1 : Receive flow ctrl accepted */ + L3UCHAR HDR; /* HDR/No HDR */ + L3UCHAR MultiFrame; /* Multi frame support */ + /* 0 : multiframe not supported */ + /* 1 : multiframe supported */ + + L3UCHAR ModeL1; /* Mode L1 */ + /* 0 : bit transparent mode of operat. */ + /* 1 : protocol sesitive mode of op. */ + + L3UCHAR NegotLLI; /* Negot. LLI */ + /* 0 : default LLI=256 only */ + /* 1 : Full protocol negotiation */ + + L3UCHAR Assignor; /* Assignor/Assignor ee */ + /* 0 : Default Asignee */ + /* 1 : Assignor only */ + + L3UCHAR InBandNeg; /* In-band negot. */ + /* 0 : negot done w/ USER INFO mes */ + /* 1 : negot done in-band w/link zero */ + + L3UCHAR NumStopBits; /* Number of stop bits */ + /* 00 : Not used */ + /* 01 : 1 bit */ + /* 10 : 1.5 bits */ + /* 11 : 2 bits */ + + L3UCHAR NumDataBits; /* Number of data bits. */ + /* 00 : not used */ + /* 01 : 5 bits */ + /* 10 : 7 bits */ + /* 11 : 8 bits */ + + L3UCHAR Parity; /* Parity Information */ + /* 000 : Odd */ + /* 010 : Even */ + /* 011 : None */ + /* 100 : Forced to 0 */ + /* 101 : Forced to 1 */ + + L3UCHAR DuplexMode; /* Mode duplex */ + /* 0 : Half duplex */ + /* 1 : Full duplex */ + + L3UCHAR ModemType; /* Modem type, see Q.931 p 89 */ + + L3UCHAR Layer2Ident; /* Layer 2 ident. */ + + L3UCHAR UIL2Prot; /* User information layer 2 protocol */ + /* 00001 : Basic mode ISO 1745 */ + /* 00010 : Q.921/I.441 */ + /* 00110 : X.25 single link */ + /* 00111 : X.25 multilink */ + /* 01000 : Extended LAPB T.71 */ + /* 01001 : HDLC ARM */ + /* 01010 : HDLC NRM */ + /* 01011 : HDLC ABM */ + /* 01100 : LAN logical link */ + /* 01101 : X.75 SLP */ + /* 01110 : Q.922 */ + /* 01111 : Q.922 core aspect */ + /* 10000 : User specified */ + /* 10001 : ISO/IEC 7776 DTE-DCE */ + + L3UCHAR ModeL2; /* Mode */ + /* 01 : Normal Mode of operation */ + /* 10 : Extended mode of operation */ + + L3UCHAR Q933use; /* Q.9333 use */ + + L3UCHAR UsrSpcL2Prot; /* User specified layer 2 protocol info */ + + L3UCHAR WindowSize; /* Window size (k) */ + + L3UCHAR Layer3Ident; /* Layer 3 ident */ + + L3UCHAR UIL3Prot; /* User Information Layer 3 protocol */ + /* 00010 : Q.931 */ + /* 00110 : X.25 */ + /* 00111 : 8208 */ + /* 01000 : X.233 ... */ + /* 01001 : 6473 */ + /* 01010 : T.70 */ + /* 01011 : ISO/IEC TR 9577 */ + /* 10000 : User specified */ + L3UCHAR OptL3Info; /* Optional Leyer 3 info */ + + L3UCHAR ModeL3; /* Mode of operation */ + /* 01 : Normal packed seq. numbering */ + /* 10 : Extended packed seq. numbering */ + + L3UCHAR DefPackSize; /* Default packet size */ + + L3UCHAR PackWinSize; /* Packet window size */ + + L3UCHAR AddL3Info; /* Additional Layer 3 protocol info */ + +} Q931ie_LLComp; + +/***************************************************************************** + + Struct: Q931ie_NetFac; + + Description: Network-specific facilities + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00100000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR LenNetID; /* Length of network facilities id. */ + L3UCHAR TypeNetID; /* Type of network identification */ + L3UCHAR NetIDPlan; /* Network identification plan. */ + L3UCHAR NetFac; /* Network specific facility spec. */ + L3UCHAR NetID[1]; /* Network id. (IA5) */ + +} Q931ie_NetFac; + +/***************************************************************************** + + Struct: Q931ie_NotifInd; + + Description: Notification Indicator + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00100000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Notification; /* Notification descriptor */ + +} Q931ie_NotifInd; + +/***************************************************************************** + + Struct: Q931ie_ProgInd + + Description: Progress indicator + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00011110 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding standard */ + L3UCHAR Location; /* Location */ + L3UCHAR ProgDesc; /* Progress description */ + +} Q931ie_ProgInd; + +/***************************************************************************** + + Struct; Q931ie_Segment + + Description: Segmented message + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00000000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR FSI; /* First segment indicator */ + L3UCHAR NumSegRem; /* Number of segments remaining */ + L3UCHAR SegType; /* Segment message type */ + +} Q931ie_Segment; + + +typedef struct { + + L3UCHAR IEId; /* 00000000 */ + L3UCHAR Size; /* Length of Information Element */ + +} Q931ie_SendComplete; + +/***************************************************************************** + + Struct: Q931ie_Signal + + Description: Signal + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00000000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Signal; /* Signal value */ + /* 00000000 Dial tone on */ + /* 00000001 Ring back tone on */ + /* 00000010 Intercept tone on */ + /* 00000011 Network congestion on */ + /* 00000100 Busy tone on */ + /* 00000101 Confirm tone on */ + /* 00000110 Answer tone on */ + /* 00000111 Call waiting tone */ + /* 00001000 Off-hook warning tone */ + /* 00001001 Pre-emption tone on */ + /* 00111111 Tones off */ + /* 01000000 Alerting on - pattern 0 */ + /* 01000001 Alerting on - pattern 1 */ + /* 01000010 Alerting on - pattern 2 */ + /* 01000011 Alerting on - pattern 3 */ + /* 01000100 Alerting on - pattern 4 */ + /* 01000101 Alerting on - pattern 5 */ + /* 01000110 Alerting on - pattern 6 */ + /* 01000111 Alerting on - pattern 7 */ + /* 01001111 Alerting off */ +} Q931ie_Signal; + +/***************************************************************************** + + Struct: Q931ie_TransDelSelInd + + description: Transit delay selection and indication + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 00000000 */ + L3UCHAR Size; /* Length of Information Element */ + L3ULONG TxDSIValue; /* Trans. delay sel. & ind. value */ + +} Q931ie_TransDelSelInd; +#endif + +/***************************************************************************** + + Struct: Q931ie_TransNetSel + + Description: Transit network selection + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01111000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Type; /* Type of network identifier */ + L3UCHAR NetIDPlan; /* Network idetification plan */ + L3UCHAR NetID[1]; /* Network identification(IA5) */ + +} Q931ie_TransNetSel; + +/***************************************************************************** + + Struct: Q931ie_UserUser + + Description: User-user + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01111110 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR ProtDisc; /* Protocol discriminator */ + L3UCHAR User[1]; /* User information */ + +} Q931ie_UserUser; + +/***************************************************************************** + + Struct: Q931ie_ClosedUserGrp + + Description: Closed user group + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000111 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CUGInd; /* CUG indication */ + L3UCHAR CUG[1]; /* CUG index code (IA5) */ + +} Q931ie_ClosedUserGrp; +#endif + +/***************************************************************************** + + Struct: Q931ie_CongLevel + + Description: Congestion Level + +*****************************************************************************/ +typedef struct { + + L3UCHAR IEId; /* 01000111 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CongLevel; /* Conguestion Level */ + +} Q931ie_CongLevel; + +/***************************************************************************** + + Struct: Q931ie_EndEndTxDelay + + Description: End to end transit delay + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000010 */ + L3UCHAR Size; /* Length of Information Element */ + L3ULONG CumTxDelay; /* Cumulative transit delay value */ + L3ULONG ReqTxDelay; /* Requested end to end transit delay */ + L3ULONG MaxTxDelay; /* Maximum transit delay */ + +} Q931ie_EndEndTxDelay; +#endif + +/***************************************************************************** + + Struct: Q931ie_InfoRate + + Description: Information Rate + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01100000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR InInfoRate; /* Incoming information rate */ + L3UCHAR OutInfoRate; /* Outgoing information rate */ + L3UCHAR MinInInfoRate; /* Minimum incoming information rate */ + L3UCHAR MinOutInfoRate; /* Minimum outgoing information rate */ + +} Q931ie_InfoRate; +#endif + +/***************************************************************************** + + Struct: Q931ie_PackParam + + Description: Packed layer binary parameters + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR FastSel; /* Fast selected */ + L3UCHAR ExpData; /* Exp. data */ + L3UCHAR DelConf; /* Delivery conf */ + L3UCHAR Modulus; /* Modulus */ + +} Q931ie_PackParam; +#endif + +/***************************************************************************** + + Struct: Q931ie_PackWinSize + + Description: Packed window size + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000101 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR ForwardValue; /* Forward value */ + L3UCHAR BackwardValue; /* Backward value */ + +} Q931ie_PackWinSize; +#endif + +/***************************************************************************** + + Struct: Q931ie_PackSize + + Description: Packet size + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000110 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR ForwardValue; /* Forward value */ + L3UCHAR BackwardValue; /* Backward value */ + +} Q931ie_PackSize; +#endif + +/***************************************************************************** + + Struct: Q931ie_RedirNum + + Description: Redirecting number + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01110100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypeNum; /* Type of number */ + L3UCHAR NumPlanID; /* Number plan identification */ + L3UCHAR PresInd; /* Presentation indicator */ + L3UCHAR ScreenInd; /* Screening indicator */ + L3UCHAR Reason; /* Reason for redirection */ + L3UCHAR Digit[1]; /* Number digits (IA5) */ + +} Q931ie_RedirNum; +#endif + +typedef struct { + + L3UCHAR IEId; /* 01110100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR RepeatInd; /* 0010 Prioritized list for selecting */ + /* one possible. */ +} Q931ie_RepeatInd; + +typedef struct { + + L3UCHAR IEId; /* 01110100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Spare; /* Spare */ + L3UCHAR Class; /* Class */ + /* 000 Indicate channels */ + /* 110 Single interface */ + /* 111 All interfaces */ +} Q931ie_RestartInd; + +typedef struct { + + L3UCHAR IEId; /* 01110100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Preference; /* Preference 0 = reserved, 1 = channel */ + L3UCHAR Spare; /* Spare */ + L3UCHAR NewStatus; /* NewStatus */ + /* 000 In service */ + /* 001 Maintenance */ + /* 010 Out of service */ +} Q931ie_ChangeStatus; + +/***************************************************************************** + + Struct: Q931ie_GenericDigits + + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00110111 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Type; /* Type of number */ + L3UCHAR Encoding; /* Encoding of number */ + L3UCHAR Digit[1]; /* Number digits (IA5) */ + +} Q931ie_GenericDigits; + + +/***************************************************************************** + + Q.931 Information Element Pack/Unpack functions. Implemented in Q931ie.c + +*****************************************************************************/ +q931pie_func_t Q931Pie_ChangeStatus; +q931pie_func_t Q931Pie_BearerCap; +q931pie_func_t Q931Pie_ChanID; +q931pie_func_t Q931Pie_ProgInd; +q931pie_func_t Q931Pie_Display; +q931pie_func_t Q931Pie_Signal; +q931pie_func_t Q931Pie_HLComp; +q931pie_func_t Q931Pie_Segment; +q931pie_func_t Q931Pie_DateTime; +q931pie_func_t Q931Pie_Cause; +q931pie_func_t Q931Pie_SendComplete; +q931pie_func_t Q931Pie_KeypadFac; +q931pie_func_t Q931Pie_NotifInd; +q931pie_func_t Q931Pie_CallID; +q931pie_func_t Q931Pie_RepeatInd; +q931pie_func_t Q931Pie_NetFac; +q931pie_func_t Q931Pie_CallingNum; +q931pie_func_t Q931Pie_CallingSub; +q931pie_func_t Q931Pie_CalledNum; +q931pie_func_t Q931Pie_CalledSub; +q931pie_func_t Q931Pie_CalledNum; +q931pie_func_t Q931Pie_TransNetSel; +q931pie_func_t Q931Pie_LLComp; +q931pie_func_t Q931Pie_CallState; +q931pie_func_t Q931Pie_RestartInd; +q931pie_func_t Q931Pie_UserUser; + +q931pie_func_t Q931Pie_GenericDigits; + +L3USHORT Q931Uie_CRV(Q931_TrunkInfo_t *pTrunk,L3UCHAR * IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff); + +q931uie_func_t Q931Uie_ChangeStatus; +q931uie_func_t Q931Uie_BearerCap; +q931uie_func_t Q931Uie_ChanID; +q931uie_func_t Q931Uie_ProgInd; +q931uie_func_t Q931Uie_Display; +q931uie_func_t Q931Uie_Signal; +q931uie_func_t Q931Uie_HLComp; +q931uie_func_t Q931Uie_Segment; +q931uie_func_t Q931Uie_DateTime; +q931uie_func_t Q931Uie_Cause; +q931uie_func_t Q931Uie_SendComplete; +q931uie_func_t Q931Uie_KeypadFac; +q931uie_func_t Q931Uie_NotifInd; +q931uie_func_t Q931Uie_CallID; +q931uie_func_t Q931Uie_RepeatInd; +q931uie_func_t Q931Uie_NetFac; +q931uie_func_t Q931Uie_CallingNum; +q931uie_func_t Q931Uie_CallingSub; +q931uie_func_t Q931Uie_CalledNum; +q931uie_func_t Q931Uie_CalledSub; +q931uie_func_t Q931Uie_TransNetSel; +q931uie_func_t Q931Uie_LLComp; +q931uie_func_t Q931Uie_CallState; +q931uie_func_t Q931Uie_RestartInd; +q931uie_func_t Q931Uie_UserUser; + +q931uie_func_t Q931Uie_GenericDigits; + + +L3INT Q931ReadExt(L3UCHAR * IBuf, L3INT Off); +L3INT Q931Uie_CongLevel(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff); +L3INT Q931Pie_CongLevel(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); +L3INT Q931Uie_RevChargeInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff); +L3INT Q931Pie_RevChargeInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); + +L3INT Q931Uie_Generic(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff); +L3INT Q931Pie_Generic(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); + +#endif /* _Q931IE_NL */ diff --git a/libs/freetdm/src/isdn/include/Q932.h b/libs/freetdm/src/isdn/include/Q932.h new file mode 100644 index 0000000000..9b69a34a02 --- /dev/null +++ b/libs/freetdm/src/isdn/include/Q932.h @@ -0,0 +1,95 @@ +/***************************************************************************** + + FileName: Q932.h + + Contents: Header w/structs for Q932 Suplementary Services. + + NB: Do NOT include this header directly, include Q931.h + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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. +*****************************************************************************/ + +/***************************************************************************** + Q.932 Additional Message codes +*****************************************************************************/ + +#define Q932mes_HOLD 0x24 /* 0010 0100 */ +#define Q932mes_HOLD_ACKNOWLEDGE 0x28 /* 0010 1000 */ +#define Q932mes_HOLD_REJECT 0x30 /* 0011 0000 */ +#define Q932mes_RETRIEVE 0x31 /* 0011 0001 */ +#define Q932mes_RETRIEVE_ACKNOWLEDGE 0x33 /* 0011 0011 */ +#define Q932mes_RETRIEVE_REJECT 0x37 /* 0011 0111 */ +#define Q932mes_FACILITY 0x62 /* 0110 0010 */ +#define Q932mes_REGISTER 0x64 /* 0110 0100 */ + +/***************************************************************************** + Q.932 Additional EI Codes +*****************************************************************************/ +#define Q932ie_FACILITY 0x1c /* 0001 1100 */ + +/***************************************************************************** + Function Prototypes. +*****************************************************************************/ +L3INT Q932ProcFacilityTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRegisterTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); + +L3INT Q932ProcFacilityNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRegisterNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); + +L3INT Q932Pmes_Facility(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_Hold(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_HoldAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_HoldReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_Register(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_Retrieve(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_RetrieveAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_RetrieveReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +L3INT Q932Umes_Facility(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_Hold(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_HoldAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_HoldReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_Register(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_Retrieve(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_RetrieveAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_RetrieveReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); diff --git a/libs/freetdm/src/isdn/include/mfifo.h b/libs/freetdm/src/isdn/include/mfifo.h new file mode 100644 index 0000000000..4c1d850d81 --- /dev/null +++ b/libs/freetdm/src/isdn/include/mfifo.h @@ -0,0 +1,85 @@ +/***************************************************************************** + + Filename: mfifo.h + + Contents: header for MFIFO + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 _MFIFO +#define _MFIFO + +/***************************************************************************** + + Struct: MINDEX + + Description: Message Index used to index a dynamic size Message FIFO. + +*****************************************************************************/ +typedef struct _mindex { + int offset; /* offset to message in buf */ + int size; /* size of message in bytes */ +} MINDEX; + +/***************************************************************************** + + Struct: MFIFO + + Description: Message FIFO. Provides a dynamic sized message based FIFO + queue. + +*****************************************************************************/ +typedef struct { + int first; /* first out */ + int last; /* last in + 1 */ + int bsize; /* buffer size */ + unsigned char *buf; /* ptr to start of buffer */ + int ixsize; /* index size */ + MINDEX ix[1]; /* message index */ +} MFIFO; + +/***************************************************************************** + Function prototypes. +*****************************************************************************/ +int MFIFOCreate(unsigned char *buf, int size, int index); +void MFIFOClear(unsigned char * buf); +int MFIFOGetLBOffset(unsigned char *buf); +int MFIFOGetFBOffset(unsigned char *buf); +void MFIFOWriteIX(unsigned char *buf, unsigned char *mes, int size, int ix, int off); +int MFIFOWriteMes(unsigned char *buf, unsigned char *mes, int size); +unsigned char * MFIFOGetMesPtr(unsigned char *buf, int *size); +void MFIFOKillNext(unsigned char *buf); + +unsigned char * MFIFOGetMesPtrOffset(unsigned char *buf, int *size, const int pos); +int MFIFOGetMesCount(unsigned char *buf); +int MFIFOWriteMesOverwrite(unsigned char *buf, unsigned char *mes, int size); + +#endif diff --git a/libs/freetdm/src/isdn/include/national.h b/libs/freetdm/src/isdn/include/national.h new file mode 100644 index 0000000000..cff26aa4a4 --- /dev/null +++ b/libs/freetdm/src/isdn/include/national.h @@ -0,0 +1,86 @@ +/****************************************************************************** + + FileName: national.h + + Contents: Header and definition for the National ISDN dialect. The + header contents the following parts: + - Definition of codes + - Definition of information elements (nationalie_). + - Definition of messages (nationalmes_). + - Function prototypes. + + Description: The National ISDN dialect here covers ???? + + Related Files: national.h National ISDN Definitions + nationalie.c National ISDN IE encoders/coders + nationalStateTE.c National ISDN TE State Engine + nationalStateNT.c National ISDN NT State Engine + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 _national_NATIONAL_NL +#define _national_NATIONAL_NL + +#include "Q931.h" + +/***************************************************************************** + + Q.931 Message codes + Only National specific message and ie types + here the rest are inherited from national.h + +*****************************************************************************/ + + +/***************************************************************************** + + Q.931 Message Pack/Unpack functions. Implemented in nationalmes.c + +*****************************************************************************/ +L3INT nationalUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT nationalPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +#include "DMS.h" + +/***************************************************************************** + + Q.931 Process Function Prototyping. Implemented in nationalStateTE.c + +*****************************************************************************/ + +void nationalCreateTE(L3UCHAR i); +void nationalCreateNT(L3UCHAR i); + +#endif /* _national_NATIONAL_NL */ diff --git a/libs/freetdm/src/isdn/mfifo.c b/libs/freetdm/src/isdn/mfifo.c new file mode 100644 index 0000000000..28528f9b64 --- /dev/null +++ b/libs/freetdm/src/isdn/mfifo.c @@ -0,0 +1,399 @@ +/***************************************************************************** + + Filename: mfifo.c + + Description: mfifo is a message orriented fifo system with support of + both message and byte per byte retriaval of messages. + + The fifo has been designed with two usages in mind: + + - Queueing of frames for hdlc and feeding out byte per byte + with the possibility of re-sending of frames etc. + + - fifo for messages of dynamic size. + + The fifo is allocated on top of any buffer and creates an + index of message in the queue. The user can write/read + messages or write messages and read the message one byte + at the time. + + Interface: + MFIFOCreate Create/reset/initialize fifo. + MFIFOClear Clear FIFO. + MFIFOWriteMes Write message into fifo + * MFIFOReadMes Read message from fifo. + MFIFOGetMesPtr Get ptr to next message. + MFIFOKillNext Kill next message. + + * currently not implemented. + + Note: The message will always be saved continuously. If there is not + sufficient space at the end of the buffer, the fifo will skip + the last bytes and save the message at the top of the buffer. + + This is required to allow direct ptr access to messages + stored in the queue. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "mfifo.h" +#include +#include + +/***************************************************************************** + + Function: MFIFOCreate + + Description: Creates a fifo on top of an existing buffer. + + Parameters: buf ptr to buffer. + size size of buffer in bytes. + index size of index entries (max no messages). + + Return value: 0 if failure, 1 if ok. + +*****************************************************************************/ +int MFIFOCreate(unsigned char *buf, int size, int index) +{ + MFIFO *mf = (MFIFO *)buf; + + mf->first = mf->last = 0; + mf->ixsize = index; + mf->buf = &buf[sizeof(MFIFO) + (sizeof(MINDEX) * index)]; + + if (mf->buf > &buf[size]) + return 0; + + mf->bsize = size - sizeof(MFIFO) - (sizeof(MINDEX) * index); + + return 1; +} + +/***************************************************************************** + + Function: MFIFOClear + + Description: Clear the FIFO + + Paremeters: buf ptr to fifo + + Return Value: none + +*****************************************************************************/ +void MFIFOClear(unsigned char * buf) +{ + MFIFO *mf = (MFIFO *)buf; + + mf->first = mf->last = 0; +} + +/***************************************************************************** + + Function: MFIFOGetLBOffset + + Description: Helper function caclulating offset to the 'first out' byte. + + Paremeters: buf ptr to fifo + + Return Value: offset. + +*****************************************************************************/ +int MFIFOGetLBOffset(unsigned char *buf) +{ + MFIFO *mf = (MFIFO *)buf; + + if (mf->last != mf->first) + return mf->ix[mf->last].offset; + + return 0; +} + +/***************************************************************************** + + Function: MFIFOGetFBOffset + + Description: Helper function calculating the offset to the 'first in' + byte in the buffer. This is the position the next byte + entering the fifo will occupy. + + Paremeters: buf ptr to fifo + + Return Value: offset + +*****************************************************************************/ +int MFIFOGetFBOffset(unsigned char *buf) +{ + MFIFO *mf = (MFIFO *)buf; + int x; + + if (mf->last == mf->first) + return 0; + + x = mf->first - 1; + + if (x < 0) + x = mf->ixsize - 1; + + return mf->ix[x].offset + mf->ix[x].size; +} + +/***************************************************************************** + + Function: MFIFOWriteIX + + Description: Helper function writing a calculated entry. The function + will perform a memcpy to move the message and set the index + values as well as increase the 'first in' index. + + Paremeters: buf ptr to fifo + mes ptr to message + size size of message in bytes. + ix index to index entry. + off offset to position to receive the message + + Return Value: none + +*****************************************************************************/ +void MFIFOWriteIX(unsigned char *buf, unsigned char *mes, int size, int ix, int off) +{ + MFIFO *mf = (MFIFO *)buf; + int x; + + memcpy(&mf->buf[off], mes, size); + mf->ix[ix].offset = off; + mf->ix[ix].size = size; + + x = mf->first + 1; + + if (x >= mf->ixsize) + x = 0; + + mf->first = x; +} + +/***************************************************************************** + + Function: MFIFOWriteMes + + Description: + + Paremeters: + + Return Value: + +*****************************************************************************/ +int MFIFOWriteMes(unsigned char *buf, unsigned char *mes, int size) +{ + MFIFO *mf = (MFIFO *)buf; + int of, ol, x; + + x = mf->first + 1; + + if (x >= mf->ixsize) + x = 0; + + if (x == mf->last) + return 0; /* full queue */ + + of = MFIFOGetFBOffset(buf); + ol = MFIFOGetLBOffset(buf); + if (mf->last == mf->first) { /* empty queue */ + mf->first = mf->last = 0; /* optimize */ + + MFIFOWriteIX(buf, mes, size, mf->first, 0); + return 1; + } + else if (of > ol) { + if (mf->bsize - of >= size) { + MFIFOWriteIX(buf, mes, size, mf->first, of); + return 1; + } + else if (ol > size) { + MFIFOWriteIX(buf, mes, size, mf->first, ol); + return 1; + } + } + else if (ol - of > size) { + MFIFOWriteIX(buf, mes, size, mf->first, of); + return 1; + } + + return 0; +} + +/***************************************************************************** + + Function: MFIFOGetMesPtr + + Description: + + Paremeters: + + Return Value: + +*****************************************************************************/ +unsigned char * MFIFOGetMesPtr(unsigned char *buf, int *size) +{ + MFIFO *mf = (MFIFO *)buf; + + if (mf->first == mf->last) { + return NULL; + } + + *size = mf->ix[mf->last].size; + return &mf->buf[mf->ix[mf->last].offset]; +} + +/***************************************************************************** + + Function: MFIFOKillNext + + Description: + + Paremeters: + + Return Value: + +*****************************************************************************/ +void MFIFOKillNext(unsigned char *buf) +{ + MFIFO *mf = (MFIFO *)buf; + int x; + + if (mf->first != mf->last) { + x = mf->last + 1; + if (x >= mf->ixsize) { + x = 0; + } + + mf->last = x; + } +} + + +/* + * Queue-style accessor functions + */ + +/** + * MFIFOGetMesPtrOffset + * \brief Get pointer to and size of message at position x + */ +unsigned char * MFIFOGetMesPtrOffset(unsigned char *buf, int *size, const int pos) +{ + MFIFO *mf = (MFIFO *)buf; + int x; + + if (mf->first == mf->last) { + return NULL; + } + + if (pos < 0 || pos >= mf->ixsize) { + return NULL; + } + + x = pos - mf->last; + if (x < 0) { + x += (mf->ixsize - 1); + } + + *size = mf->ix[x].size; + return &mf->buf[mf->ix[x].offset]; +} + + +/** + * MFIFOGetMesCount + * \brief How many messages are currently in the buffer? + */ +int MFIFOGetMesCount(unsigned char *buf) +{ + MFIFO *mf = (MFIFO *)buf; + + if (mf->first == mf->last) { + return 0; + } + else if (mf->first > mf->last) { + return mf->first - mf->last; + } + else { + return (mf->ixsize - mf->last) + mf->first; + } +} + +/** + * MFIFOWriteMesOverwrite + * \brief Same as MFIFOWriteMes but old frames will be overwritten if the fifo is full + */ +int MFIFOWriteMesOverwrite(unsigned char *buf, unsigned char *mes, int size) +{ + MFIFO *mf = (MFIFO *)buf; + int of, ol, x; + + x = mf->first + 1; + + if (x >= mf->ixsize) + x = 0; + + if (x == mf->last) { + /* advance last pointer */ + mf->last++; + + if (mf->last >= mf->ixsize) + mf->last = 0; + } + + of = MFIFOGetFBOffset(buf); + ol = MFIFOGetLBOffset(buf); + + if (mf->last == mf->first) { /* empty queue */ + mf->first = mf->last = 0; /* optimize */ + + MFIFOWriteIX(buf, mes, size, mf->first, 0); + return 1; + } + else if (of > ol) { + if (mf->bsize - of >= size) { + MFIFOWriteIX(buf, mes, size, mf->first, of); + return 1; + } + else if (ol > size) { + MFIFOWriteIX(buf, mes, size, mf->first, ol); + return 1; + } + } + else if (ol - of > size) { + MFIFOWriteIX(buf, mes, size, mf->first, of); + return 1; + } + return 0; +} diff --git a/libs/freetdm/src/isdn/nationalStateNT.c b/libs/freetdm/src/isdn/nationalStateNT.c new file mode 100644 index 0000000000..b150999650 --- /dev/null +++ b/libs/freetdm/src/isdn/nationalStateNT.c @@ -0,0 +1,130 @@ +/***************************************************************************** + + FileName: nationalStateNT.c + + Contents: National ISDN State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "national.h" + +/***************************************************************************** + Function: nationalCreateNT + + Description: Will create the National ISDN NT as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void nationalCreateNT(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingNT, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingNT, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectNT, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckNT, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressNT, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupNT, nationalUmes_Setup, nationalPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckNT, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeNT, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckNT, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectNT, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendNT, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckNT, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectNT, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationNT, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectNT, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseNT, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteNT, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartNT, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckNT, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlNT, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationNT, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyNT, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusNT, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryNT, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentNT, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityNT, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldNT, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckNT, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectNT, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterNT, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveNT, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckNT, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectNT, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_REDIRECTING_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* TODO define state table here */ +} diff --git a/libs/freetdm/src/isdn/nationalStateTE.c b/libs/freetdm/src/isdn/nationalStateTE.c new file mode 100644 index 0000000000..46731d0910 --- /dev/null +++ b/libs/freetdm/src/isdn/nationalStateTE.c @@ -0,0 +1,217 @@ +/***************************************************************************** + + FileName: nationalStateTE.c + + Contents: National ISDN State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + This reference implementation uses a process per message, + meaning that each message must check call states. This + is easier for dialect maintenance as each message proc + can be replaced individually. A new TE variant only + need to copy the Q931CreateTE and replace those procs or + need to override. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "national.h" +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: nationalCreateTE + + Description: Will create the National TE as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void nationalCreateTE(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingTE, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingTE, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, DMSProc0x07TE, DMSUmes_0x07, DMSPmes_0x07); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, DMSProc0x0fTE, DMSUmes_0x0f, DMSPmes_0x0f); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressTE, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupTE, nationalUmes_Setup, nationalPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckTE, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeTE, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckTE, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectTE, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendTE, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckTE, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectTE, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationTE, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectTE, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseTE, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteTE, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartTE, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckTE, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlTE, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationTE, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyTE, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusTE, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryTE, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentTE, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityTE, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldTE, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckTE, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectTE, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterTE, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveTE, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckTE, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectTE, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANGE_STATUS, i, Q931Pie_ChangeStatus, Q931Uie_ChangeStatus); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_REDIRECTING_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message + * procs can when search this to find out if the message/state + * combination is legale. If not, the proc for unexpected message apply. + */ + + /* State 0 Idle */ + Q931AddStateEntry(i, Q931_U0, Q931mes_RESUME, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_STATUS, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE_COMPLETE, 4); + + /* State 1 Call Initiating */ + Q931AddStateEntry(i, Q931_U1, Q931mes_DISCONNECT, 2); + Q931AddStateEntry(i, Q931_U1, Q931mes_SETUP_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_RELEASE_COMPLETE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CONNECT, 4); + + /* State 2 Overlap Sending */ + Q931AddStateEntry(i, Q931_U2, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U2, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_RELEASE, 2); + + /* State 3 Outgoing Call Proceeding */ + Q931AddStateEntry(i, Q931_U3, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_RELEASE, 2); + + /* State 4 Call Delivered */ + Q931AddStateEntry(i, Q931_U4, Q931mes_CONNECT, 4); + + /* State 6 Call Precent */ + Q931AddStateEntry(i, Q931_U6, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CALL_PROCEEDING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE_COMPLETE, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U6, Q931mes_DISCONNECT, 4); + + /* State 7 Call Received */ + Q931AddStateEntry(i, Q931_U7, Q931mes_CONNECT, 2); + + /* State 8 Connect request */ + Q931AddStateEntry(i, Q931_U8, Q931mes_CONNECT_ACKNOWLEDGE, 4); + + /* State 9 Incoming Call Proceeding */ + Q931AddStateEntry(i, Q931_U9, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_PROGRESS, 2); + + /* State 10 Active */ + Q931AddStateEntry(i, Q931_U10, Q931mes_SUSPEND, 2); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 4); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 2); + + /* State 11 Disconnect Request */ + Q931AddStateEntry(i, Q931_U11, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_NOTIFY, 4); + + /* State 12 Disconnect Ind */ + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 2); + + /* State 15 Suspend Request */ + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_REJECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_RELEASE, 4); + +/* TODO + Q931AddStateEntry(i, Q931_U17, + Q931AddStateEntry(i, Q931_U19, + Q931AddStateEntry(i, Q931_U25, +*/ +} diff --git a/libs/freetdm/src/isdn/nationalmes.c b/libs/freetdm/src/isdn/nationalmes.c new file mode 100644 index 0000000000..6d8350fbcd --- /dev/null +++ b/libs/freetdm/src/isdn/nationalmes.c @@ -0,0 +1,269 @@ +/***************************************************************************** + + FileName: nationalmes.c + + Contents: Pack/Unpack functions. These functions will unpack a National ISDN + message from the bit packed original format into structs + that contains variables sized by the user. It will also pack + the struct back into a Q.931 message as required. + + See national.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "national.h" + +/***************************************************************************** + + Function: nationalUmes_Setup + +*****************************************************************************/ +L3INT nationalUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT ir = 0; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + L3UCHAR last_codeset = 0, codeset = 0; + L3UCHAR shift_lock = 1; + + while (IOff < Size) { + if (!shift_lock) { + codeset = last_codeset; + } + + if ((IBuf[IOff] & 0xF0) == Q931ie_SHIFT ) { + shift_lock = (IBuf[IOff] & 0x08); + if (shift_lock) { + last_codeset = codeset; + } + codeset = ((IBuf[IOff] & 0x07)); + IOff++; + } + + if (codeset == 0) { + switch (IBuf[IOff]) { + case Q931ie_SENDING_COMPLETE: + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_NETWORK_SPECIFIC_FACILITIES: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_SIGNAL: + case Q931ie_CALLING_PARTY_NUMBER: + case Q931ie_CALLING_PARTY_SUBADDRESS: + case Q931ie_CALLED_PARTY_NUMBER: + case Q931ie_CALLED_PARTY_SUBADDRESS: + case Q931ie_TRANSIT_NETWORK_SELECTION: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_FACILITY: + case Q931ie_REDIRECTING_NUMBER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_REPEAT_INDICATOR: + if (ir < 2) { + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + ir++; + } else { + return Q931E_ILLEGAL_IE; + } + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else if (codeset == 6) { + switch (IBuf[IOff]) { + case Q931ie_GENERIC_DIGITS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + + } else { + return Q931E_ILLEGAL_IE; + } + } + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: nationalPmes_Setup + + Decription: Pack a Q931mes_Generic into a real Q.931 message. The user will + set up a SETUP message and issue this to the stack where it + is processed by Q931ProcSetup that processes and validates + it before it actually sends it out. This function is called + to compute the real Q.931 message. + + Parameters: IBuf[IN] Ptr to un-packed struct + ISize[IN] Size of input buffer (unpacked message). + OBuf[OUT] Ptr to packed 'octet' wise message. + OSize[OUT] Size of packed message. + + Called By: Q931ProcSetup + +*****************************************************************************/ +L3INT nationalPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3INT rc = Q931E_NO_ERROR; + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)(pMes->SendComplete & 0x00ff); + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->RepeatInd)) { + OBuf[Octet++] = (L3UCHAR)(pMes->RepeatInd & 0x00ff); + } + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + else { + rc = Q931E_BEARERCAP; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Network spesific facilities */ + if (Q931IsIEPresent(pMes->NetFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_NETWORK_SPECIFIC_FACILITIES](pTrunk, Q931GetIEPtr(pMes->NetFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Number */ + if (Q931IsIEPresent(pMes->CallingNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CallingNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Subaddress */ + if (Q931IsIEPresent(pMes->CallingSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CallingSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called party subaddress */ + if (Q931IsIEPresent(pMes->CalledSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CalledSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Transit network selection */ + if (Q931IsIEPresent(pMes->TransNetSel)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_TRANSIT_NETWORK_SELECTION](pTrunk, Q931GetIEPtr(pMes->TransNetSel,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->LLRepeatInd)) { + rc = Q931E_UNKNOWN_IE;/* TODO */ + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} diff --git a/libs/freetdm/src/libteletone_detect.c b/libs/freetdm/src/libteletone_detect.c new file mode 100644 index 0000000000..61fef90149 --- /dev/null +++ b/libs/freetdm/src/libteletone_detect.c @@ -0,0 +1,427 @@ +/* + * libteletone + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Much less efficient expansion interface was added to allow for the detection of + * a single arbitrary tone combination which may also exceed 2 simultaneous tones. + * (controlled by compile time constant TELETONE_MAX_TONES) + * + * Copyright (C) 2006 Anthony Minessale II + * + * libteletone_detect.c Tone Detection Code + * + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + ********************************************************************************* + * + * Derived from tone_detect.c - General telephony tone detection, and specific + * detection of DTMF. + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * + * + */ + +#include + +#ifndef _MSC_VER +#include +#endif +#include +#include +#include +#include + + +static teletone_detection_descriptor_t dtmf_detect_row[GRID_FACTOR]; +static teletone_detection_descriptor_t dtmf_detect_col[GRID_FACTOR]; +static teletone_detection_descriptor_t dtmf_detect_row_2nd[GRID_FACTOR]; +static teletone_detection_descriptor_t dtmf_detect_col_2nd[GRID_FACTOR]; + +static float dtmf_row[] = {697.0f, 770.0f, 852.0f, 941.0f}; +static float dtmf_col[] = {1209.0f, 1336.0f, 1477.0f, 1633.0f}; + +static char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; + +static void goertzel_init(teletone_goertzel_state_t *goertzel_state, teletone_detection_descriptor_t *tdesc) { + goertzel_state->v2 = goertzel_state->v3 = 0.0; + goertzel_state->fac = tdesc->fac; +} + +TELETONE_API(void) teletone_goertzel_update(teletone_goertzel_state_t *goertzel_state, + int16_t sample_buffer[], + int samples) +{ + int i; + float v1; + + for (i = 0; i < samples; i++) { + v1 = goertzel_state->v2; + goertzel_state->v2 = goertzel_state->v3; + goertzel_state->v3 = (float)(goertzel_state->fac*goertzel_state->v2 - v1 + sample_buffer[i]); + } +} +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +#define teletone_goertzel_result(gs) (double)(((gs)->v3 * (gs)->v3 + (gs)->v2 * (gs)->v2 - (gs)->v2 * (gs)->v3 * (gs)->fac)) + +TELETONE_API(void) teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf_detect_state, int sample_rate) +{ + int i; + float theta; + + dtmf_detect_state->hit1 = dtmf_detect_state->hit2 = 0; + + for (i = 0; i < GRID_FACTOR; i++) { + theta = (float)(M_TWO_PI*(dtmf_row[i]/(float)sample_rate)); + dtmf_detect_row[i].fac = (float)(2.0*cos(theta)); + + theta = (float)(M_TWO_PI*(dtmf_col[i]/(float)sample_rate)); + dtmf_detect_col[i].fac = (float)(2.0*cos(theta)); + + theta = (float)(M_TWO_PI*(dtmf_row[i]*2.0/(float)sample_rate)); + dtmf_detect_row_2nd[i].fac = (float)(2.0*cos(theta)); + + theta = (float)(M_TWO_PI*(dtmf_col[i]*2.0/(float)sample_rate)); + dtmf_detect_col_2nd[i].fac = (float)(2.0*cos(theta)); + + goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]); + + dtmf_detect_state->energy = 0.0; + } + dtmf_detect_state->current_sample = 0; + dtmf_detect_state->detected_digits = 0; + dtmf_detect_state->lost_digits = 0; + dtmf_detect_state->digits[0] = '\0'; + dtmf_detect_state->mhit = 0; +} + +TELETONE_API(void) teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *map) +{ + float theta = 0; + int x = 0; + + if (!mt->sample_rate) { + mt->sample_rate = 8000; + } + + if (!mt->min_samples) { + mt->min_samples = 102; + } + + mt->min_samples *= (mt->sample_rate / 8000); + + if (!mt->positive_factor) { + mt->positive_factor = 2; + } + + if(!mt->negative_factor) { + mt->negative_factor = 10; + } + + if (!mt->hit_factor) { + mt->hit_factor = 2; + } + + for(x = 0; x < TELETONE_MAX_TONES; x++) { + if ((int) map->freqs[x] == 0) { + break; + } + mt->tone_count++; + theta = (float)(M_TWO_PI*(map->freqs[x]/(float)mt->sample_rate)); + mt->tdd[x].fac = (float)(2.0 * cos(theta)); + goertzel_init (&mt->gs[x], &mt->tdd[x]); + goertzel_init (&mt->gs2[x], &mt->tdd[x]); + } + +} + +TELETONE_API(int) teletone_multi_tone_detect (teletone_multi_tone_t *mt, + int16_t sample_buffer[], + int samples) +{ + int sample, limit = 0, j, x = 0; + float v1, famp; + float eng_sum = 0, eng_all[TELETONE_MAX_TONES] = {0.0}; + int gtest = 0, see_hit = 0; + + for (sample = 0; sample >= 0 && sample < samples; sample = limit) { + mt->total_samples++; + + if ((samples - sample) >= (mt->min_samples - mt->current_sample)) { + limit = sample + (mt->min_samples - mt->current_sample); + } else { + limit = samples; + } + if (limit < 0 || limit > samples) { + limit = samples; + } + + for (j = sample; j < limit; j++) { + famp = sample_buffer[j]; + + mt->energy += famp*famp; + + for(x = 0; x < TELETONE_MAX_TONES && x < mt->tone_count; x++) { + v1 = mt->gs[x].v2; + mt->gs[x].v2 = mt->gs[x].v3; + mt->gs[x].v3 = (float)(mt->gs[x].fac * mt->gs[x].v2 - v1 + famp); + + v1 = mt->gs2[x].v2; + mt->gs2[x].v2 = mt->gs2[x].v3; + mt->gs2[x].v3 = (float)(mt->gs2[x].fac*mt->gs2[x].v2 - v1 + famp); + } + } + + mt->current_sample += (limit - sample); + if (mt->current_sample < mt->min_samples) { + continue; + } + + eng_sum = 0; + for(x = 0; x < TELETONE_MAX_TONES && x < mt->tone_count; x++) { + eng_all[x] = (float)(teletone_goertzel_result (&mt->gs[x])); + eng_sum += eng_all[x]; + } + + gtest = 0; + for(x = 0; x < TELETONE_MAX_TONES && x < mt->tone_count; x++) { + gtest += teletone_goertzel_result (&mt->gs2[x]) < eng_all[x] ? 1 : 0; + } + + if ((gtest >= 2 || gtest == mt->tone_count) && eng_sum > 42.0 * mt->energy) { + if(mt->negatives) { + mt->negatives--; + } + mt->positives++; + + if(mt->positives >= mt->positive_factor) { + mt->hits++; + } + if (mt->hits >= mt->hit_factor) { + see_hit++; + mt->positives = mt->negatives = mt->hits = 0; + } + } else { + mt->negatives++; + if(mt->positives) { + mt->positives--; + } + if(mt->negatives > mt->negative_factor) { + mt->positives = mt->hits = 0; + } + } + + /* Reinitialise the detector for the next block */ + for(x = 0; x < TELETONE_MAX_TONES && x < mt->tone_count; x++) { + goertzel_init (&mt->gs[x], &mt->tdd[x]); + goertzel_init (&mt->gs2[x], &mt->tdd[x]); + } + + mt->energy = 0.0; + mt->current_sample = 0; + } + + return see_hit; +} + + +TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, + int16_t sample_buffer[], + int samples) +{ + float row_energy[GRID_FACTOR]; + float col_energy[GRID_FACTOR]; + float famp; + float v1; + int i; + int j; + int sample; + int best_row; + int best_col; + char hit; + int limit; + + hit = 0; + for (sample = 0; sample < samples; sample = limit) { + /* BLOCK_LEN is optimised to meet the DTMF specs. */ + if ((samples - sample) >= (BLOCK_LEN - dtmf_detect_state->current_sample)) { + limit = sample + (BLOCK_LEN - dtmf_detect_state->current_sample); + } else { + limit = samples; + } + + for (j = sample; j < limit; j++) { + int x = 0; + famp = sample_buffer[j]; + + dtmf_detect_state->energy += famp*famp; + + for(x = 0; x < GRID_FACTOR; x++) { + v1 = dtmf_detect_state->row_out[x].v2; + dtmf_detect_state->row_out[x].v2 = dtmf_detect_state->row_out[x].v3; + dtmf_detect_state->row_out[x].v3 = (float)(dtmf_detect_state->row_out[x].fac*dtmf_detect_state->row_out[x].v2 - v1 + famp); + + v1 = dtmf_detect_state->col_out[x].v2; + dtmf_detect_state->col_out[x].v2 = dtmf_detect_state->col_out[x].v3; + dtmf_detect_state->col_out[x].v3 = (float)(dtmf_detect_state->col_out[x].fac*dtmf_detect_state->col_out[x].v2 - v1 + famp); + + v1 = dtmf_detect_state->col_out2nd[x].v2; + dtmf_detect_state->col_out2nd[x].v2 = dtmf_detect_state->col_out2nd[x].v3; + dtmf_detect_state->col_out2nd[x].v3 = (float)(dtmf_detect_state->col_out2nd[x].fac*dtmf_detect_state->col_out2nd[x].v2 - v1 + famp); + + v1 = dtmf_detect_state->row_out2nd[x].v2; + dtmf_detect_state->row_out2nd[x].v2 = dtmf_detect_state->row_out2nd[x].v3; + dtmf_detect_state->row_out2nd[x].v3 = (float)(dtmf_detect_state->row_out2nd[x].fac*dtmf_detect_state->row_out2nd[x].v2 - v1 + famp); + } + + } + + dtmf_detect_state->current_sample += (limit - sample); + if (dtmf_detect_state->current_sample < BLOCK_LEN) { + continue; + } + /* We are at the end of a DTMF detection block */ + /* Find the peak row and the peak column */ + row_energy[0] = teletone_goertzel_result (&dtmf_detect_state->row_out[0]); + col_energy[0] = teletone_goertzel_result (&dtmf_detect_state->col_out[0]); + + for (best_row = best_col = 0, i = 1; i < GRID_FACTOR; i++) { + row_energy[i] = teletone_goertzel_result (&dtmf_detect_state->row_out[i]); + if (row_energy[i] > row_energy[best_row]) { + best_row = i; + } + col_energy[i] = teletone_goertzel_result (&dtmf_detect_state->col_out[i]); + if (col_energy[i] > col_energy[best_col]) { + best_col = i; + } + } + hit = 0; + /* Basic signal level test and the twist test */ + if (row_energy[best_row] >= DTMF_THRESHOLD && + col_energy[best_col] >= DTMF_THRESHOLD && + col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST && + col_energy[best_col]*DTMF_NORMAL_TWIST > row_energy[best_row]) { + /* Relative peak test */ + for (i = 0; i < GRID_FACTOR; i++) { + if ((i != best_col && col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) || + (i != best_row && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) { + break; + } + } + /* ... and second harmonic test */ + if (i >= GRID_FACTOR && (row_energy[best_row] + col_energy[best_col]) > 42.0*dtmf_detect_state->energy && + teletone_goertzel_result (&dtmf_detect_state->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col] && + teletone_goertzel_result (&dtmf_detect_state->row_out2nd[best_row])*DTMF_2ND_HARMONIC_ROW < row_energy[best_row]) { + hit = dtmf_positions[(best_row << 2) + best_col]; + /* Look for two successive similar results */ + /* The logic in the next test is: + We need two successive identical clean detects, with + something different preceeding it. This can work with + back to back differing digits. More importantly, it + can work with nasty phones that give a very wobbly start + to a digit. */ + if (hit == dtmf_detect_state->hit3 && dtmf_detect_state->hit3 != dtmf_detect_state->hit2) { + dtmf_detect_state->mhit = hit; + dtmf_detect_state->digit_hits[(best_row << 2) + best_col]++; + dtmf_detect_state->detected_digits++; + if (dtmf_detect_state->current_digits < TELETONE_MAX_DTMF_DIGITS) { + dtmf_detect_state->digits[dtmf_detect_state->current_digits++] = hit; + dtmf_detect_state->digits[dtmf_detect_state->current_digits] = '\0'; + } + else + { + dtmf_detect_state->lost_digits++; + } + } + } + } + dtmf_detect_state->hit1 = dtmf_detect_state->hit2; + dtmf_detect_state->hit2 = dtmf_detect_state->hit3; + dtmf_detect_state->hit3 = hit; + /* Reinitialise the detector for the next block */ + for (i = 0; i < GRID_FACTOR; i++) { + goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]); + } + dtmf_detect_state->energy = 0.0; + dtmf_detect_state->current_sample = 0; + } + if ((!dtmf_detect_state->mhit) || (dtmf_detect_state->mhit != hit)) { + dtmf_detect_state->mhit = 0; + return(0); + } + return (hit); +} + + +TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, + char *buf, + int max) +{ + teletone_assert(dtmf_detect_state->current_digits <= TELETONE_MAX_DTMF_DIGITS); + + if (max > dtmf_detect_state->current_digits) { + max = dtmf_detect_state->current_digits; + } + if (max > 0) { + memcpy (buf, dtmf_detect_state->digits, max); + memmove (dtmf_detect_state->digits, dtmf_detect_state->digits + max, dtmf_detect_state->current_digits - max); + dtmf_detect_state->current_digits -= max; + } + buf[max] = '\0'; + return max; +} + +/* 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: + */ diff --git a/libs/freetdm/src/libteletone_generate.c b/libs/freetdm/src/libteletone_generate.c new file mode 100644 index 0000000000..f79c615a36 --- /dev/null +++ b/libs/freetdm/src/libteletone_generate.c @@ -0,0 +1,452 @@ +/* + * libteletone_generate.c -- Tone Generator + * + * Copyright (c) 2007, Anthony Minessale II + * 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 +#include "freetdm.h" + +#define SMAX 32767 +#define SMIN -32768 +#define normalize_to_16bit(n) if (n > SMAX) n = SMAX; else if (n < SMIN) n = SMIN; + +#ifdef _MSC_VER +#pragma warning(disable:4706) +#endif + +TELETONE_API_DATA int16_t TELETONE_SINES[SINE_TABLE_MAX] = { + 0x00c9, 0x025b, 0x03ed, 0x057f, 0x0711, 0x08a2, 0x0a33, 0x0bc4, + 0x0d54, 0x0ee4, 0x1073, 0x1201, 0x138f, 0x151c, 0x16a8, 0x1833, + 0x19be, 0x1b47, 0x1cd0, 0x1e57, 0x1fdd, 0x2162, 0x22e5, 0x2467, + 0x25e8, 0x2768, 0x28e5, 0x2a62, 0x2bdc, 0x2d55, 0x2ecc, 0x3042, + 0x31b5, 0x3327, 0x3497, 0x3604, 0x3770, 0x38d9, 0x3a40, 0x3ba5, + 0x3d08, 0x3e68, 0x3fc6, 0x4121, 0x427a, 0x43d1, 0x4524, 0x4675, + 0x47c4, 0x490f, 0x4a58, 0x4b9e, 0x4ce1, 0x4e21, 0x4f5e, 0x5098, + 0x51cf, 0x5303, 0x5433, 0x5560, 0x568a, 0x57b1, 0x58d4, 0x59f4, + 0x5b10, 0x5c29, 0x5d3e, 0x5e50, 0x5f5e, 0x6068, 0x616f, 0x6272, + 0x6371, 0x646c, 0x6564, 0x6657, 0x6747, 0x6832, 0x691a, 0x69fd, + 0x6add, 0x6bb8, 0x6c8f, 0x6d62, 0x6e31, 0x6efb, 0x6fc2, 0x7083, + 0x7141, 0x71fa, 0x72af, 0x735f, 0x740b, 0x74b3, 0x7556, 0x75f4, + 0x768e, 0x7723, 0x77b4, 0x7840, 0x78c8, 0x794a, 0x79c9, 0x7a42, + 0x7ab7, 0x7b27, 0x7b92, 0x7bf9, 0x7c5a, 0x7cb7, 0x7d0f, 0x7d63, + 0x7db1, 0x7dfb, 0x7e3f, 0x7e7f, 0x7eba, 0x7ef0, 0x7f22, 0x7f4e, + 0x7f75, 0x7f98, 0x7fb5, 0x7fce, 0x7fe2, 0x7ff1, 0x7ffa, 0x7fff +}; + + +TELETONE_API(int) teletone_set_tone(teletone_generation_session_t *ts, int index, ...) +{ + va_list ap; + int i = 0; + teletone_process_t x = 0; + + va_start(ap, index); + while (i < TELETONE_MAX_TONES && (x = va_arg(ap, teletone_process_t))) { + ts->TONES[index].freqs[i++] = x; + } + va_end(ap); + + return (i > TELETONE_MAX_TONES) ? -1 : 0; + +} + +TELETONE_API(int) teletone_set_map(teletone_tone_map_t *map, ...) +{ + va_list ap; + int i = 0; + teletone_process_t x = 0; + + va_start(ap, map); + while (i < TELETONE_MAX_TONES && (x = va_arg(ap, teletone_process_t))) { + map->freqs[i++] = x; + } + va_end(ap); + + return (i > TELETONE_MAX_TONES) ? -1 : 0; + +} + +TELETONE_API(int) teletone_init_session(teletone_generation_session_t *ts, int buflen, tone_handler handler, void *user_data) +{ + memset(ts, 0, sizeof(*ts)); + ts->rate = 8000; + ts->channels = 1; + ts->duration = 2000; + ts->wait = 500; + ts->tmp_duration = -1; + ts->tmp_wait = -1; + ts->handler = handler; + ts->user_data = user_data; + ts->volume = -7; + ts->decay_step = 0; + ts->decay_factor = 1; + if (buflen) { + if ((ts->buffer = ftdm_calloc(buflen, sizeof(teletone_audio_t))) == 0) { + return -1; + } + ts->datalen = buflen; + } else { + ts->dynamic = 1024; + } + /* Add Standard DTMF Tones */ + teletone_set_tone(ts, '1', 697.0, 1209.0, 0.0); + teletone_set_tone(ts, '2', 697.0, 1336.0, 0.0); + teletone_set_tone(ts, '3', 697.0, 1477.0, 0.0); + teletone_set_tone(ts, 'A', 697.0, 1633.0, 0.0); + teletone_set_tone(ts, '4', 770.0, 1209.0, 0.0); + teletone_set_tone(ts, '5', 770.0, 1336.0, 0.0); + teletone_set_tone(ts, '6', 770.0, 1477.0, 0.0); + teletone_set_tone(ts, 'B', 770.0, 1633.0, 0.0); + teletone_set_tone(ts, '7', 859.0, 1209.0, 0.0); + teletone_set_tone(ts, '8', 859.0, 1336.0, 0.0); + teletone_set_tone(ts, '9', 859.0, 1477.0, 0.0); + teletone_set_tone(ts, 'C', 859.0, 1633.0, 0.0); + teletone_set_tone(ts, '*', 941.0, 1209.0, 0.0); + teletone_set_tone(ts, '0', 941.0, 1336.0, 0.0); + teletone_set_tone(ts, '#', 941.0, 1477.0, 0.0); + teletone_set_tone(ts, 'D', 941.0, 1633.0, 0.0); + + return 0; +} + +TELETONE_API(int) teletone_destroy_session(teletone_generation_session_t *ts) +{ + if (ts->buffer) { + ftdm_safe_free(ts->buffer); + ts->buffer = NULL; + ts->samples = 0; + } + return 0; +} + +static int ensure_buffer(teletone_generation_session_t *ts, int need) +{ + need += ts->samples; + need *= sizeof(teletone_audio_t); + need *= ts->channels; + + if (need > ts->datalen) { + teletone_audio_t *tmp; + ts->datalen = need + ts->dynamic; + tmp = realloc(ts->buffer, ts->datalen); + if (!tmp) { + return -1; + } + ts->buffer = tmp; + } + + return 0; +} + +TELETONE_API(int) teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + /*teletone_process_t period = (1.0 / ts->rate) / ts->channels;*/ + int i, c; + int freqlen = 0; + teletone_dds_state_t tones[TELETONE_MAX_TONES+1]; + //int decay = 0; + int duration; + int wait = 0; + int32_t sample; + int32_t dc = 0; + float vol = ts->volume; + ts->samples = 0; + memset(tones, 0, sizeof(tones[0]) * TELETONE_MAX_TONES); + duration = (ts->tmp_duration > -1) ? ts->tmp_duration : ts->duration; + wait = (ts->tmp_wait > -1) ? ts->tmp_wait : ts->wait; + + if (map->freqs[0] > 0) { + for (freqlen = 0; freqlen < TELETONE_MAX_TONES && map->freqs[freqlen]; freqlen++) { + teletone_dds_state_set_tone(&tones[freqlen], map->freqs[freqlen], ts->rate, 0); + teletone_dds_state_set_tx_level(&tones[freqlen], vol); + } + + if (ts->channels > 1) { + duration *= ts->channels; + } + + if (ts->dynamic) { + if (ensure_buffer(ts, duration)) { + return -1; + } + } + + for (ts->samples = 0; ts->samples < ts->datalen && ts->samples < duration; ts->samples++) { + if (ts->decay_direction && ++dc >= ts->decay_step) { + float nvol = vol + ts->decay_direction * ts->decay_factor; + int j; + + if (nvol <= TELETONE_VOL_DB_MAX && nvol >= TELETONE_VOL_DB_MIN) { + vol = nvol; + for (j = 0; j < TELETONE_MAX_TONES && map->freqs[j]; j++) { + teletone_dds_state_set_tx_level(&tones[j], vol); + } + dc = 0; + } + } + + sample = 128; + + for (i = 0; i < freqlen; i++) { + int32_t s = teletone_dds_state_modulate_sample(&tones[i], 0); + sample += s; + } + sample /= freqlen; + ts->buffer[ts->samples] = (teletone_audio_t)sample; + + for (c = 1; c < ts->channels; c++) { + ts->buffer[ts->samples+1] = ts->buffer[ts->samples]; + ts->samples++; + } + + } + } + if (ts->dynamic) { + if (ensure_buffer(ts, wait)) { + return -1; + } + } + for (c = 0; c < ts->channels; c++) { + for (i = 0; i < wait && ts->samples < ts->datalen; i++) { + ts->buffer[ts->samples++] = 0; + } + } + + if (ts->debug && ts->debug_stream) { + if (map->freqs[0] <= 0) { + fprintf(ts->debug_stream, "wait %d (%dms)\n", wait, wait / (ts->rate / 1000)); + } else { + fprintf(ts->debug_stream, "Generate: ("); + + for (i = 0; i < TELETONE_MAX_TONES && map->freqs[i]; i++) { + fprintf(ts->debug_stream, "%s%0.2f", i == 0 ? "" : "+",map->freqs[i]); + } + + fprintf(ts->debug_stream, + ") [volume %0.2fdB; samples %d(%dms) x %d channel%s; wait %d(%dms); decay_factor %0.2fdB; decay_step %d(%dms); wrote %d bytes]\n", + ts->volume, + duration, + duration / (ts->rate / 1000), + ts->channels, + ts->channels == 1 ? "" : "s", + wait, + wait / (ts->rate / 1000), + ts->decay_factor, + ts->decay_step, + ts->decay_step / (ts->rate / 1000), + ts->samples * 2); + } + } + return ts->samples; +} + +TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cmd) +{ + char *data = NULL, *cur = NULL, *end = NULL; + int var = 0, LOOPING = 0; + + if (!cmd) { + return -1; + } + + do { + if (!(data = ftdm_strdup(cmd))) { + return -1; + } + + cur = data; + + while (*cur) { + var = 0; + if (*cur == ' ' || *cur == '\r' || *cur == '\n') { + cur++; + continue; + } + + if ((end = strchr(cur, ';')) != 0) { + *end++ = '\0'; + } + + if (*(cur + 1) == '=') { + var = 1; + switch(*cur) { + case 'c': + ts->channels = atoi(cur + 2); + break; + case 'r': + ts->rate = atoi(cur + 2); + break; + case 'd': + ts->duration = atoi(cur + 2) * (ts->rate / 1000); + break; + case 'v': + { + float vol = (float)atof(cur + 2); + if (vol <= TELETONE_VOL_DB_MAX && vol >= TELETONE_VOL_DB_MIN) { + ts->volume = vol; + } + } + break; + case '>': + ts->decay_step = atoi(cur + 2) * (ts->rate / 1000); + ts->decay_direction = -1; + break; + case '<': + ts->decay_step = atoi(cur + 2) * (ts->rate / 1000); + ts->decay_direction = 1; + break; + case '+': + ts->decay_factor = (float)atof(cur + 2); + break; + case 'w': + ts->wait = atoi(cur + 2) * (ts->rate / 1000); + break; + case 'l': + ts->loops = atoi(cur + 2); + break; + case 'L': + if (!LOOPING) { + ts->LOOPS = atoi(cur + 2); + } + LOOPING++; + break; + } + } else { + while (*cur) { + char *p = NULL, *e = NULL; + teletone_tone_map_t mymap, *mapp = NULL; + + if (*cur == ' ' || *cur == '\r' || *cur == '\n') { + cur++; + continue; + } + + ts->tmp_duration = -1; + ts->tmp_wait = -1; + + memset(&mymap, 0, sizeof(mymap)); + + if (*(cur + 1) == '(') { + p = cur + 2; + if (*cur) { + char *next; + int i = 0; + if ((e = strchr(p, ')')) != 0) { + *e++ = '\0'; + } + do { + if ((next = strchr(p, ',')) != 0) { + *next++ = '\0'; + } + if (i == 0) { + ts->tmp_duration = atoi(p) * (ts->rate / 1000); + i++; + } else if (i == 1) { + ts->tmp_wait = atoi(p) * (ts->rate / 1000); + i++; + } else { + mymap.freqs[i++ - 2] = atof(p); + } + p = next; + + } while (next && (i-2) < TELETONE_MAX_TONES); + if (i > 2 && *cur == '%') { + mapp = &mymap; + } else if ((i != 2 || *cur == '%')) { + if (ts->debug && ts->debug_stream) { + fprintf(ts->debug_stream, "Syntax Error!\n"); + } + goto bottom; + } + } + } + + if (*cur && !mapp) { + if (*cur > 0 && *cur < TELETONE_TONE_RANGE) { + mapp = &ts->TONES[(int)*cur]; + } else if (ts->debug && ts->debug_stream) { + fprintf(ts->debug_stream, "Map [%c] Out Of Range!\n", *cur); + } + } + + if (mapp) { + if (mapp->freqs[0]) { + if (ts->handler) { + do { + ts->handler(ts, mapp); + if (ts->loops > 0) { + ts->loops--; + } + } while (ts->loops); + } + } else if (ts->debug && ts->debug_stream) { + fprintf(ts->debug_stream, "Ignoring Empty Map [%c]!\n", *cur); + } + } + + if (e) { + cur = e; + } else { + cur++; + } + } + } + + if (end) { + cur = end; + } else if (*cur){ + cur++; + } + } + bottom: + ftdm_safe_free(data); + data = NULL; + if (ts->LOOPS > 0) { + ts->LOOPS--; + } + + } while (ts->LOOPS); + + return 0; +} + +/* 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: + */ diff --git a/libs/freetdm/src/m3ua/mstm3ua.c b/libs/freetdm/src/m3ua/mstm3ua.c new file mode 100644 index 0000000000..1d8179c58d --- /dev/null +++ b/libs/freetdm/src/m3ua/mstm3ua.c @@ -0,0 +1,62 @@ +/* WARNING WORK IN PROGRESS + * mstm3ua.c + * mstss7d port + * + * Created by Shane Burrell on 2/2/08. + * Copyright 2008 Shane Burrell. 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 "mstm3ua.h" + + + + + +int build_m3ua_hdr(unsigned char len,unsigned char *bytemsg) + +{ + + *bytemsg++ = M_VERSION_REL1; // 1 Verison + //bytemsg[1] = 0x00; // 2 RESERVED + //bytemsg[2] = M_CLASS_XFER; // 3 Msg Class + //SS7 BOX Kludge + *bytemsg++ = 0x01; // 2 RESERVED + *bytemsg++ = 0x00; // 2 RESERVED + + *bytemsg++ = M_TYPE_DATA ; // 4 Msg Type + + *bytemsg++ = len; // 5 Msg LENGTH 81 32bit field + *bytemsg++ = 0x00; // 6 + *bytemsg++ = 0x00; // 7 + *bytemsg++ = 0x00; // 8 + return(0); + +}; \ No newline at end of file diff --git a/libs/freetdm/src/m3ua/mstm3ua.h b/libs/freetdm/src/m3ua/mstm3ua.h new file mode 100644 index 0000000000..13527dac35 --- /dev/null +++ b/libs/freetdm/src/m3ua/mstm3ua.h @@ -0,0 +1,96 @@ +/* + * mstm3ua.h + * mstss7d + * + * Created by Shane Burrell on 3/2/08. + * Copyright 2008 Shane Burrell. 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. + */ +typedef unsigned long m3ua_ulong; +typedef unsigned short m3ua_ushort; +typedef unsigned char m3ua_uchar; + +typedef unsigned char u8; +typedef unsigned short u16; /* Note: multi-byte values are little-endian */ +typedef unsigned long u32; + + + + +#define M_TAG_NETWORK_APPEARANCE 1 +#define M_TAG_PROTOCOL_DATA 3 +#define M_TAG_INFO_STRING 4 +#define M_TAG_AFFECTED_DPC 5 +#define M_TAG_ROUTING_CONTEXT 6 +#define M_TAG_DIAGNOSTIC_INFORMATION 7 +#define M_TAG_HEARTBEAT_DATA 8 +#define M_TAG_UNAVAILABILITY_CAUSE 9 +#define M_TAG_REASON 10 +#define M_TAG_TRAFFIC_MODE_TYPE 11 +#define M_TAG_ERROR_CODE 12 +#define M_TAG_STATUS_TYPE 13 +#define M_TAG_CONGESTED_INDICATIONS 14 + +#define M_VERSION_REL1 1 + +#define M_CLASS_MGMT 0x00 +#define M_CLASS_XFER 0x01 +#define M_CLASS_SSNM 0x02 +#define M_CLASS_ASPSM 0x03 +#define M_CLASS_ASPTM 0x04 +#define M_CLASS_RKM 0x09 + +#define M_TYPE_ERR (0|M_CLASS_MGMT + +#define M_TYPE_NTFY (1|M_CLASS_XFER) +#define M_TYPE_DATA (1|M_CLASS_XFER) + +#define M_TYPE_DUNA (1|M_CLASS_SSNM) +#define M_TYPE_DAVA (2|M_CLASS_SSNM) +#define M_TYPE_DUAD (3|M_CLASS_SSNM) +#define M_TYPE_SCON (4|M_CLASS_SSNM) +#define M_TYPE_DUPU (5|M_CLASS_SSNM) + +#define M_TYPE_UP (1|M_CLASS_ASPSM) +#define M_TYPE_DOWN (2|M_CLASS_ASPSM) +#define M_TYPE_BEAT (3|M_CLASS_ASPSM) +#define M_TYPE_UP_ACK (4|M_CLASS_ASPSM) +#define M_TYPE_DOWN_ACK (5|M_CLASS_ASPSM) +#define M_TYPE_BEAT_ACK (6|M_CLASS_ASPSM) + +#define M_TYPE_ACTIVE (1|M_CLASS_ASPTM) +#define M_TYPE_INACTIVE (2|M_CLASS_ASPTM) +#define M_TYPE_ACTIVE_ACK (3|M_CLASS_ASPTM) +#define M_TYPE_INACTIVE_ACK (4|M_CLASS_ASPTM) + +#define M_CLASS_MASK 0xff00 +#define M_TYPE_MASK 0x00ff + diff --git a/libs/freetdm/src/m3ua_client.c b/libs/freetdm/src/m3ua_client.c new file mode 100644 index 0000000000..7608183896 --- /dev/null +++ b/libs/freetdm/src/m3ua_client.c @@ -0,0 +1,333 @@ +/* + * m3ua_client.c + * freetdm + * + * Created by Shane Burrell on 4/3/08. + * Copyright 2008 Shane Burrell. All rights reserved. + * + * + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * 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. + */ + +#if HAVE_NETDB_H +#include +#endif + +#include "freetdm.h" +#include + + +#ifndef HAVE_GETHOSTBYNAME_R +extern int gethostbyname_r (const char *__name, + struct hostent *__result_buf, + char *__buf, size_t __buflen, + struct hostent **__result, + int *__h_errnop); +#endif + +struct m3uac_map { + uint32_t event_id; + const char *name; +}; + +static struct m3uac_map m3uac_table[] = { + {M3UA_EVENT_CALL_START, "CALL_START"}, + {M3UA_EVENT_CALL_START_ACK, "CALL_START_ACK"}, + {M3UA_EVENT_CALL_START_NACK, "CALL_START_NACK"}, + {M3UA_EVENT_CALL_START_NACK_ACK, "CALL_START_NACK_ACK"}, + {M3UA_EVENT_CALL_ANSWERED, "CALL_ANSWERED"}, + {M3UA_EVENT_CALL_STOPPED, "CALL_STOPPED"}, + {M3UA_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"}, + {M3UA_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"}, + {M3UA_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"}, + {M3UA_EVENT_HEARTBEAT, "HEARTBEAT"}, + {M3UA_EVENT_INSERT_CHECK_LOOP, "LOOP START"}, + {M3UA_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"} +}; + + + +static int create_conn_socket(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) +{ + int rc; + struct hostent *result, *local_result; + char buf[512], local_buf[512]; + int err = 0; + + memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp)); + memset(&mcon->local_hp, 0, sizeof(mcon->local_hp)); + mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); + + ftdm_log(FTDM_LOG_DEBUG, "Creating L=%s:%d R=%s:%d\n", + local_ip,local_port,ip,port); + + if (mcon->socket >= 0) { + int flag; + + flag = 1; + gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + mcon->remote_addr.sin_family = mcon->remote_hp.h_addrtype; + memcpy((char *) &mcon->remote_addr.sin_addr.s_addr, mcon->remote_hp.h_addr_list[0], mcon->remote_hp.h_length); + mcon->remote_addr.sin_port = htons(port); + + mcon->local_addr.sin_family = mcon->local_hp.h_addrtype; + memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length); + mcon->local_addr.sin_port = htons(local_port); + + + setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY, (char *)&flag, sizeof(int)); + + rc=listen(mcon->socket,100); + if (rc) { + close(mcon->socket); + mcon->socket = -1; + + } + } + } + + ftdm_mutex_create(&mcon->mutex); + + return mcon->socket; +} + +int m3uac_connection_close(m3uac_connection_t *mcon) +{ + if (mcon->socket > -1) { + close(mcon->socket); + } + + ftdm_mutex_lock(mcon->mutex); + ftdm_mutex_unlock(mcon->mutex); + ftdm_mutex_destroy(&mcon->mutex); + memset(mcon, 0, sizeof(*mcon)); + mcon->socket = -1; + + return 0; +} + +int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) +{ + create_conn_socket(mcon, local_ip, local_port, ip, port); + return mcon->socket; +} + + +int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause) +{ + m3uac_event_t oevent; + int retry = 5; + + m3uac_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (cmd == SIGBOOST_EVENT_SYSTEM_RESTART) { + mcon->rxseq_reset = 1; + mcon->txseq = 0; + mcon->rxseq = 0; + mcon->txwindow = 0; + } + + if (id >= 0) { + oevent.call_setup_id = id; + } + + while (m3uac_connection_write(mcon, &oevent) <= 0) { + if (--retry <= 0) { + ftdm_log(FTDM_LOG_CRIT, "Failed to tx on M3UA socket: %s\n", strerror(errno)); + return -1; + } else { + ftdm_log(FTDM_LOG_WARNING, "Failed to tx on M3UA socket: %s :retry %i\n", strerror(errno), retry); + ftdm_sleep(1); + } + } + + return 0; +} + + + +m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration) +{ + unsigned int fromlen = sizeof(struct sockaddr_in); + int bytes = 0; + + bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, + (struct sockaddr *) &mcon->local_addr, &fromlen); + + if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) { + + if (mcon->rxseq_reset) { + if (mcon->event.event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { + ftdm_log(FTDM_LOG_DEBUG, "Rx sync ok\n"); + mcon->rxseq = mcon->event.fseqno; + return &mcon->event; + } + errno=EAGAIN; + ftdm_log(FTDM_LOG_DEBUG, "Waiting for rx sync...\n"); + return NULL; + } + + mcon->txwindow = mcon->txseq - mcon->event.bseqno; + mcon->rxseq++; + + if (mcon->rxseq != mcon->event.fseqno) { + ftdm_log(FTDM_LOG_CRIT, "Invalid Sequence Number Expect=%i Rx=%i\n", mcon->rxseq, mcon->event.fseqno); + return NULL; + } + + return &mcon->event; + } else { + if (iteration == 0) { + ftdm_log(FTDM_LOG_CRIT, "Invalid Event length from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); + return NULL; + } + } + + return NULL; +} + +m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration) +{ + unsigned int fromlen = sizeof(struct sockaddr_in); + int bytes = 0; + + bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, (struct sockaddr *) &mcon->local_addr, &fromlen); + + if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) { + return &mcon->event; + } else { + if (iteration == 0) { + ftdm_log(FTDM_LOG_CRIT, "Critical Error: PQ Invalid Event lenght from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); + return NULL; + } + } + + return NULL; +} + + +int m3uac_connection_write(m3uac_connection_t *mcon, ss7bc_event_t *event) +{ + int err; + + if (!event || mcon->socket < 0 || !mcon->mutex) { + ftdm_log(FTDM_LOG_DEBUG, "Critical Error: No Event Device\n"); + return -EINVAL; + } + + if (event->span > 16 || event->chan > 31) { + ftdm_log(FTDM_LOG_CRIT, "Critical Error: TX Cmd=%s Invalid Span=%i Chan=%i\n", m3uac_event_id_name(event->event_id), event->span,event->chan); + return -1; + } + + gettimeofday(&event->tv,NULL); + + ftdm_mutex_lock(mcon->mutex); + event->fseqno = mcon->txseq++; + event->bseqno = mcon->rxseq; + err = sendto(mcon->socket, event, sizeof(m3uac_event_t), 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); + ftdm_mutex_unlock(mcon->mutex); + + if (err != sizeof(m3uac_event_t)) { + err = -1; + } + + ftdm_log(FTDM_LOG_DEBUG, "TX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", + m3uac_event_id_name(event->event_id), + event->event_id, + event->span+1, + event->chan+1, + event->release_cause, + event->call_setup_id, + event->fseqno, + (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), + (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") + ); + + return err; +} + +void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id) +{ + memset(event, 0, sizeof(m3uac_event_t)); + event->event_id = M3UA_EVENT_CALL_START; + + if (calling) { + strncpy((char*)event->calling_number_digits, calling, sizeof(event->calling_number_digits)-1); + event->calling_number_digits_count = strlen(calling); + } + + if (called) { + strncpy((char*)event->called_number_digits, called, sizeof(event->called_number_digits)-1); + event->called_number_digits_count = strlen(called); + } + + event->call_setup_id = setup_id; + +} + +void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span) +{ + memset(event, 0, sizeof(ss7bc_event_t)); + event->event_id = event_id; + event->chan = chan; + event->span = span; +} + +const char *m3uac_event_id_name(uint32_t event_id) +{ + unsigned int x; + const char *ret = NULL; + + for (x = 0 ; x < sizeof(m3uac_table)/sizeof(struct m3uac_map); x++) { + if (m3uac_table[x].event_id == event_id) { + ret = m3uac_table[x].name; + break; + } + } + + return ret; +} + +/* 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: + */ + + diff --git a/libs/freetdm/src/m3ua_client.h b/libs/freetdm/src/m3ua_client.h new file mode 100644 index 0000000000..e451156a41 --- /dev/null +++ b/libs/freetdm/src/m3ua_client.h @@ -0,0 +1,164 @@ +/* + * m3ua_client.h + * freetdm + * + * Created by Shane Burrell on 4/3/08. + * Copyright 2008 Shane Burrell. All rights reserved. + * + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Fix this for portability +#include +//#include +#include +#include +#include +//#include +#include + +#define MAX_DIALED_DIGITS 31 +#define MAX_CALLING_NAME 31 + +/* Next two defines are used to create the range of values for call_setup_id + * in the t_sigboost structure. + * 0..((CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN) - 1) */ +#define CORE_MAX_SPANS 200 +#define CORE_MAX_CHAN_PER_SPAN 30 +#define MAX_PENDING_CALLS CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN +/* 0..(MAX_PENDING_CALLS-1) is range of call_setup_id below */ +#define SIZE_RDNIS 80 + +//#undef MSGWINDOW +#define MSGWINDOW + + +typedef struct +{ + uint32_t event_id; + uint32_t fseqno; +#ifdef MSGWINDOW + uint32_t bseqno; +#endif + uint16_t call_setup_id; + uint32_t trunk_group; + uint32_t span; + uint32_t chan; + uint8_t called_number_digits_count; + char called_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ + uint8_t calling_number_digits_count; /* it's an array */ + char calling_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ + uint8_t release_cause; + struct timeval tv; + /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ + uint8_t calling_number_screening_ind; + uint8_t calling_number_presentation; + char redirection_string [SIZE_RDNIS]; /* it's a null terminated string */ + +} t_m3ua; + +typedef t_m3ua m3uac_event_t; +typedef uint32_t m3uac_event_id_t; + + +typedef struct m3uac_ip_cfg +{ + char local_ip[25]; + int local_port; + char remote_ip[25]; + int remote_port; +}m3uac_ip_cfg_t; + +struct m3uac_connection { + ftdm_socket_t socket; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + m3uac_event_t event; + struct hostent remote_hp; + struct hostent local_hp; + unsigned int flags; + ftdm_mutex_t *mutex; + FILE *log; + unsigned int txseq; + unsigned int rxseq; + unsigned int txwindow; + unsigned int rxseq_reset; + m3uac_ip_cfg_t cfg; + uint32_t hb_elapsed; + int up; +}; + +typedef enum { + MSU_FLAG_EVENT = (1 << 0) +} m3uac_flag_t; + +typedef struct m3uac_connection m3uac_connection_t; + +static inline void sctp_no_nagle(int socket) +{ + //int flag = 1; + //setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int)); +} + +int m3uac_connection_close(m3uac_connection_t *mcon); +int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port); +m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration); +m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration); +int m3uac_connection_write(m3uac_connection_t *mcon, m3uac_event_t *event); +void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span); +void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id); +const char *m3uac_event_id_name(uint32_t event_id); +int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause); + + + + +/* 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: + */ diff --git a/libs/freetdm/src/priserver.c b/libs/freetdm/src/priserver.c new file mode 100644 index 0000000000..b67fa04483 --- /dev/null +++ b/libs/freetdm/src/priserver.c @@ -0,0 +1,328 @@ +/***************************************************************************** + * priserver.c Refactoring of pritest.c + * + * Author(s): Anthony Minessale II + * Nenad Corbic + * + * Copyright: (c) 2005 Anthony Minessale II + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * ============================================================================ + */ + +#include "freetdm.h" +#include +#include +#include +#include +#include +#include + +typedef struct { + int pid; + q931_call call; + void *pri; + int ready; +}call_info_t; + + +#define SANGOMA_MAX_CHAN_PER_SPAN 32 + +static call_info_t pidmap[SANGOMA_MAX_CHAN_PER_SPAN]; + +FIO_EVENT_CB_FUNCTION(my_ftdm_event_handler) +{ + if (event->e_type = FTDM_EVENT_DTMF) { + char *dtmf = event->data; + printf("DTMF %s\n", dtmf); + } +} + +/* Stupid runtime process to play a file to a b channel*/ +#define BYTES 320 +#define MAX_BYTES 1000 + +static int ready = 1; + +static void handle_SIGINT(int sig) +{ + if (sig) { + ready = 0; + } + + return; +} + +static void launch_channel(struct sangoma_pri *spri, int channo) +{ + pid_t pid; + int fd = 0, file = 0, inlen = 0, outlen = 0; + unsigned char inframe[MAX_BYTES], outframe[MAX_BYTES]; + fd_set readfds; + int mtu_mru=BYTES / 2; + int err; + ftdm_channel_t *chan; + ftdm_codec_t codec = FTDM_CODEC_SLIN; + unsigned ms = 20; + unsigned int lead = 50; + int ifd = -1; + ftdm_tone_type_t tt = FTDM_TONE_DTMF; + char dtmf[] = "1234567890"; + int loops = 0; + + pid = fork(); + + if (pid) { + pidmap[channo-1].pid = pid; + printf("-- Launching process %d to handle channel %d\n", pid, channo); + return; + } + + signal(SIGINT, handle_SIGINT); + + //ifd = open("/nfs/sounds/ptest.raw", O_WRONLY|O_CREAT|O_TRUNC, 777); + + memset(inframe, 0, MAX_BYTES); + memset(outframe, 0, MAX_BYTES); + + if (ftdm_channel_open(spri->span, channo, &chan) != FTDM_SUCCESS) { + printf("DEBUG cant open fd!\n"); + } + + + +#if 1 + if (ftdm_channel_command(chan, FTDM_COMMAND_SET_CODEC, &codec) != FTDM_SUCCESS) { + printf("Critical Error: Failed to set driver codec!\n"); + ftdm_channel_close(&chan); + exit(-1); + } +#endif + +#if 1 + if (ftdm_channel_command(chan, FTDM_COMMAND_ENABLE_DTMF_DETECT, &tt) != FTDM_SUCCESS) { + printf("Critical Error: Failed to set dtmf detect!\n"); + ftdm_channel_close(&chan); + exit(-1); + } + ftdm_channel_set_event_callback(chan, my_ftdm_event_handler); +#endif + + + if (ftdm_channel_command(chan, FTDM_COMMAND_SET_INTERVAL, &ms) != FTDM_SUCCESS) { + printf("Critical Error: Failed to set codec interval!\n"); + ftdm_channel_close(&chan); + exit(-1); + } + + file = open("sound.raw", O_RDONLY); + if (file < 0){ + printf("Critical Error: Failed to open sound file!\n"); + ftdm_channel_close(&chan); + exit(-1); + } + + + while(ready) { + ftdm_wait_flag_t flags = FTDM_READ; + ftdm_size_t len; + loops++; + + if (lead) { + lead--; + } + + if (!lead && loops == 300) { +#if 1 + if (ftdm_channel_command(chan, FTDM_COMMAND_SEND_DTMF, dtmf) != FTDM_SUCCESS) { + printf("Critical Error: Failed to send dtmf\n"); + ftdm_channel_close(&chan); + exit(-1); + } +#endif + + } + + if (ftdm_channel_wait(chan, &flags, 2000) != FTDM_SUCCESS) { + printf("wait FAIL! [%s]\n", chan->last_error); + break; + } + + if (flags & FTDM_READ) { + len = MAX_BYTES; + if (ftdm_channel_read(chan, inframe, &len) == FTDM_SUCCESS) { + //printf("READ: %d\n", len); + //write(ifd, inframe, len); + if(!lead && (outlen = read(file, outframe, len)) <= 0) { + break; + } + + } else { + printf("READ FAIL! %d [%s]\n", len, chan->last_error); + break; + } + if (lead) { + continue; + } + ftdm_channel_write(chan, outframe, sizeof(outframe), &len); + } else { + printf("BREAK"); + break; + } + } + + printf("loop done\n"); + + //sangoma_get_full_cfg(fd, &tdm_api); + close(file); + //close(ifd); + + pri_hangup(spri->pri, channo, 16); + if (ftdm_channel_close(&chan) != FTDM_SUCCESS) { + printf("Critical Error: Failed to close channel [%s]\n", chan->last_error); + } + + printf("Call Handler: Process Finished\n"); + exit(0); +} + + +/* Event Handlers */ + +static int on_info(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + printf( "number is: %s\n", event->ring.callednum); + if(strlen(event->ring.callednum) > 3) { + printf( "final number is: %s\n", event->ring.callednum); + pri_answer(spri->pri, event->ring.call, 0, 1); + } + return 0; +} + +static int on_hangup(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + //pri_hangup(spri->pri, event->hangup.call, event->hangup.cause); + printf("-- Hanging up channel %d\n", event->hangup.channel); + if(pidmap[event->hangup.channel-1].pid) { + pri_hangup(spri->pri, event->hangup.call, 16); + pri_destroycall(spri->pri, event->hangup.call); + kill(pidmap[event->hangup.channel-1].pid, SIGINT); + pidmap[event->hangup.channel-1].pid = 0; + } + return 0; +} + +static int on_ring(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + printf("-- Ring on channel %d (from %s to %s), answering...\n", event->ring.channel, event->ring.callingnum, event->ring.callednum); + pri_answer(spri->pri, event->ring.call, event->ring.channel, 1); + memcpy(&pidmap[event->ring.channel-1].call, event->ring.call, sizeof(q931_call)); + pidmap[event->ring.channel-1].pri=spri->pri; + pidmap[event->ring.channel-1].call = *event->ring.call; + launch_channel(spri, event->ring.channel); + return 0; +} + +static int on_restart(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + printf("-- Restarting channel %d\n", event->restart.channel); + return 0; +} + +static int on_anything(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + printf("%s: Caught Event %d (%s)\n", __FUNCTION__, event_type, sangoma_pri_event_str(event_type)); + return 0; +} + +/* Generic Reaper */ +static void chan_ended(int sig) +{ + int status; + int x; + struct rusage rusage; + pid_t pid; + pid = wait4(-1, &status, WNOHANG, &rusage); + + printf("-- PID %d ended\n", pid); + + for (x=0;x -1) { + fprintf(stderr, "--!! Unknown PID %d exited\n", pid); + signal(SIGCHLD, chan_ended); + return; + } +} + +/* Our Program */ +int main(int argc, char *argv[]) +{ + struct sangoma_pri spri; + int debug = 0; + if (argv[1]) { + debug = atoi(argv[1]); + } + + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + if (ftdm_global_init() != FTDM_SUCCESS) { + fprintf(stderr, "Error loading FreeTDM\n"); + exit(-1); + } + + printf("FreeTDM loaded\n"); + + + debug = PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE; + printf("WTF %d\n", debug); + + if (sangoma_init_pri(&spri, + 1, // span + 24, // dchan + SANGOMA_PRI_SWITCH_DMS100, + SANGOMA_PRI_CPE, + debug) < 0) { + return -1; + } + //spri.pri->debug = (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE); + + //pri_set_debug(&spri.pri, (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE)); + + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_ANY, on_anything); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_RING, on_ring); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_HANGUP, on_hangup); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_HANGUP_REQ, on_hangup); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_INFO_RECEIVED, on_info); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_RESTART, on_restart); + + signal(SIGCHLD, chan_ended); + sangoma_run_pri(&spri); + return 0; +} + +/* 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: + */ diff --git a/libs/freetdm/src/sangoma_pri.c b/libs/freetdm/src/sangoma_pri.c new file mode 100644 index 0000000000..402611e518 --- /dev/null +++ b/libs/freetdm/src/sangoma_pri.c @@ -0,0 +1,251 @@ +/***************************************************************************** + * sangoma_pri.c libpri Sangoma integration + * + * Author(s): Anthony Minessale II + * Nenad Corbic + * + * Copyright: (c) 2005 Anthony Minessale II + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * ============================================================================ + */ + +#include "freetdm.h" +#include +#ifndef HAVE_GETTIMEOFDAY + +#ifdef WIN32 +#include + +static __inline int gettimeofday(struct timeval *tp, void *nothing) +{ +#ifdef WITHOUT_MM_LIB + SYSTEMTIME st; + time_t tt; + struct tm tmtm; + /* mktime converts local to UTC */ + GetLocalTime (&st); + tmtm.tm_sec = st.wSecond; + tmtm.tm_min = st.wMinute; + tmtm.tm_hour = st.wHour; + tmtm.tm_mday = st.wDay; + tmtm.tm_mon = st.wMonth - 1; + tmtm.tm_year = st.wYear - 1900; tmtm.tm_isdst = -1; + tt = mktime (&tmtm); + tp->tv_sec = tt; + tp->tv_usec = st.wMilliseconds * 1000; +#else + /** + ** The earlier time calculations using GetLocalTime + ** had a time resolution of 10ms.The timeGetTime, part + ** of multimedia apis offer a better time resolution + ** of 1ms.Need to link against winmm.lib for this + **/ + unsigned long Ticks = 0; + unsigned long Sec =0; + unsigned long Usec = 0; + Ticks = timeGetTime(); + + Sec = Ticks/1000; + Usec = (Ticks - (Sec*1000))*1000; + tp->tv_sec = Sec; + tp->tv_usec = Usec; +#endif /* WITHOUT_MM_LIB */ + (void)nothing; + return 0; +} +#endif /* WIN32 */ +#endif /* HAVE_GETTIMEOFDAY */ + +static struct sangoma_pri_event_list SANGOMA_PRI_EVENT_LIST[] = { + {0, SANGOMA_PRI_EVENT_ANY, "ANY"}, + {1, SANGOMA_PRI_EVENT_DCHAN_UP, "DCHAN_UP"}, + {2, SANGOMA_PRI_EVENT_DCHAN_DOWN, "DCHAN_DOWN"}, + {3, SANGOMA_PRI_EVENT_RESTART, "RESTART"}, + {4, SANGOMA_PRI_EVENT_CONFIG_ERR, "CONFIG_ERR"}, + {5, SANGOMA_PRI_EVENT_RING, "RING"}, + {6, SANGOMA_PRI_EVENT_HANGUP, "HANGUP"}, + {7, SANGOMA_PRI_EVENT_RINGING, "RINGING"}, + {8, SANGOMA_PRI_EVENT_ANSWER, "ANSWER"}, + {9, SANGOMA_PRI_EVENT_HANGUP_ACK, "HANGUP_ACK"}, + {10, SANGOMA_PRI_EVENT_RESTART_ACK, "RESTART_ACK"}, + {11, SANGOMA_PRI_EVENT_FACNAME, "FACNAME"}, + {12, SANGOMA_PRI_EVENT_INFO_RECEIVED, "INFO_RECEIVED"}, + {13, SANGOMA_PRI_EVENT_PROCEEDING, "PROCEEDING"}, + {14, SANGOMA_PRI_EVENT_SETUP_ACK, "SETUP_ACK"}, + {15, SANGOMA_PRI_EVENT_HANGUP_REQ, "HANGUP_REQ"}, + {16, SANGOMA_PRI_EVENT_NOTIFY, "NOTIFY"}, + {17, SANGOMA_PRI_EVENT_PROGRESS, "PROGRESS"}, + {18, SANGOMA_PRI_EVENT_KEYPAD_DIGIT, "KEYPAD_DIGIT"} +}; + +#define LINE "--------------------------------------------------------------------------------" + +char *sangoma_pri_event_str(sangoma_pri_event_t event_id) +{ + return SANGOMA_PRI_EVENT_LIST[event_id].name; +} + +static int __pri_sangoma_read(struct pri *pri, void *buf, int buflen) +{ + struct sangoma_pri *spri = (struct sangoma_pri *) pri->userdata; + ftdm_size_t len = buflen; + int res; + char bb[4096] = ""; + + + if (ftdm_channel_read(spri->zdchan, buf, &len) != FTDM_SUCCESS) { + printf("D-READ FAIL! [%s]\n", spri->zdchan->last_error); + return 0; + } + res = (int)len; + memset(&((unsigned char*)buf)[res],0,2); + res+=2; + + //print_bits(buf, res-2, bb, sizeof(bb), 1, 0); + //ftdm_log(FTDM_LOG_DEBUG, "READ %d\n%s\n%s\n\n", res-2, LINE, bb); + + return res; +} + +static int __pri_sangoma_write(struct pri *pri, void *buf, int buflen) +{ + struct sangoma_pri *spri = (struct sangoma_pri *) pri->userdata; + int res; + ftdm_size_t len = buflen -2; + char bb[4096] = ""; + + if (ftdm_channel_write(spri->zdchan, buf, buflen, &len) != FTDM_SUCCESS) { + printf("D-WRITE FAIL! [%s]\n", spri->zdchan->last_error); + return 0; + } + + //print_bits(buf, (int)buflen-2, bb, sizeof(bb), 1, 0); + //ftdm_log(FTDM_LOG_DEBUG, "WRITE %d\n%s\n%s\n\n", (int)buflen-2, LINE, bb); + + return (int) buflen; +} + +int sangoma_init_pri(struct sangoma_pri *spri, int span, int dchan, int swtype, int node, int debug) +{ + int ret = -1; + ftdm_socket_t dfd = 0; + + memset(spri, 0, sizeof(struct sangoma_pri)); + + if (ftdm_channel_open(span, dchan, &spri->zdchan) != FTDM_SUCCESS) { + fprintf(stderr, "Unable to open DCHAN %d for span %d (%s)\n", dchan, span, strerror(errno)); + } else { + if ((spri->pri = pri_new_cb(spri->zdchan->sockfd, node, swtype, __pri_sangoma_read, __pri_sangoma_write, spri))){ + spri->span = span; + pri_set_debug(spri->pri, debug); + ret = 0; + } else { + fprintf(stderr, "Unable to create PRI\n"); + } + } + return ret; +} + + +int sangoma_one_loop(struct sangoma_pri *spri) +{ + fd_set rfds, efds; + struct timeval now = {0,0}, *next; + pri_event *event; + int sel; + + if (spri->on_loop) { + spri->on_loop(spri); + } + + FD_ZERO(&rfds); + FD_ZERO(&efds); + +#ifdef _MSC_VER + //Windows macro for FD_SET includes a warning C4127: conditional expression is constant +#pragma warning(push) +#pragma warning(disable:4127) +#endif + + FD_SET(spri->pri->fd, &rfds); + FD_SET(spri->pri->fd, &efds); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + if ((next = pri_schedule_next(spri->pri))) { + gettimeofday(&now, NULL); + now.tv_sec = next->tv_sec - now.tv_sec; + now.tv_usec = next->tv_usec - now.tv_usec; + if (now.tv_usec < 0) { + now.tv_usec += 1000000; + now.tv_sec -= 1; + } + if (now.tv_sec < 0) { + now.tv_sec = 0; + now.tv_usec = 0; + } + } + + sel = select(spri->pri->fd + 1, &rfds, NULL, &efds, next ? &now : NULL); + event = NULL; + + if (!sel) { + event = pri_schedule_run(spri->pri); + } else if (sel > 0) { + event = pri_check_event(spri->pri); + } + + if (event) { + event_handler handler; + /* 0 is catchall event handler */ + if ((handler = spri->eventmap[event->e] ? spri->eventmap[event->e] : spri->eventmap[0] ? spri->eventmap[0] : NULL)) { + handler(spri, event->e, event); + } else { + fprintf(stderr,"No event handler found for event %d.\n", event->e); + } + } + + return sel; +} + +int sangoma_run_pri(struct sangoma_pri *spri) +{ + int ret = 0; + + for (;;){ + ret=sangoma_one_loop(spri); + if (ret < 0){ + +#ifndef WIN32 //This needs to be adressed fror WIN32 still + if (errno == EINTR){ + /* Igonore an interrupted system call */ + continue; + } +#endif + printf("Error = %i\n",ret); + perror("Sangoma Run Pri: "); + break; + } + } + + return ret; + +} + +/* 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: + */ + diff --git a/libs/freetdm/src/sangoma_pri.h b/libs/freetdm/src/sangoma_pri.h new file mode 100644 index 0000000000..2a1b2a2cef --- /dev/null +++ b/libs/freetdm/src/sangoma_pri.h @@ -0,0 +1,100 @@ +/***************************************************************************** + * libsangoma.c AFT T1/E1: HDLC API Code Library + * + * Author(s): Anthony Minessale II + * Nenad Corbic + * + * Copyright: (c) 2005 Anthony Minessale II + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * ============================================================================ + */ + +#ifndef _SANGOMA_PRI_H +#define _SANGOMA_PRI_H +#include +#include + + +#define SANGOMA_MAX_CHAN_PER_SPAN 32 + +typedef enum { + SANGOMA_PRI_EVENT_ANY = 0, + SANGOMA_PRI_EVENT_DCHAN_UP = PRI_EVENT_DCHAN_UP, + SANGOMA_PRI_EVENT_DCHAN_DOWN = PRI_EVENT_DCHAN_DOWN, + SANGOMA_PRI_EVENT_RESTART = PRI_EVENT_RESTART, + SANGOMA_PRI_EVENT_CONFIG_ERR = PRI_EVENT_CONFIG_ERR, + SANGOMA_PRI_EVENT_RING = PRI_EVENT_RING, + SANGOMA_PRI_EVENT_HANGUP = PRI_EVENT_HANGUP, + SANGOMA_PRI_EVENT_RINGING = PRI_EVENT_RINGING, + SANGOMA_PRI_EVENT_ANSWER = PRI_EVENT_ANSWER, + SANGOMA_PRI_EVENT_HANGUP_ACK = PRI_EVENT_HANGUP_ACK, + SANGOMA_PRI_EVENT_RESTART_ACK = PRI_EVENT_RESTART_ACK, + SANGOMA_PRI_EVENT_FACNAME = PRI_EVENT_FACNAME, + SANGOMA_PRI_EVENT_INFO_RECEIVED = PRI_EVENT_INFO_RECEIVED, + SANGOMA_PRI_EVENT_PROCEEDING = PRI_EVENT_PROCEEDING, + SANGOMA_PRI_EVENT_SETUP_ACK = PRI_EVENT_SETUP_ACK, + SANGOMA_PRI_EVENT_HANGUP_REQ = PRI_EVENT_HANGUP_REQ, + SANGOMA_PRI_EVENT_NOTIFY = PRI_EVENT_NOTIFY, + SANGOMA_PRI_EVENT_PROGRESS = PRI_EVENT_PROGRESS, + SANGOMA_PRI_EVENT_KEYPAD_DIGIT = PRI_EVENT_KEYPAD_DIGIT +} sangoma_pri_event_t; + +typedef enum { + SANGOMA_PRI_NETWORK = PRI_NETWORK, + SANGOMA_PRI_CPE = PRI_CPE +} sangoma_pri_node_t; + +typedef enum { + SANGOMA_PRI_SWITCH_UNKNOWN = PRI_SWITCH_UNKNOWN, + SANGOMA_PRI_SWITCH_NI2 = PRI_SWITCH_NI2, + SANGOMA_PRI_SWITCH_DMS100 = PRI_SWITCH_DMS100, + SANGOMA_PRI_SWITCH_LUCENT5E = PRI_SWITCH_LUCENT5E, + SANGOMA_PRI_SWITCH_ATT4ESS = PRI_SWITCH_ATT4ESS, + SANGOMA_PRI_SWITCH_EUROISDN_E1 = PRI_SWITCH_EUROISDN_E1, + SANGOMA_PRI_SWITCH_EUROISDN_T1 = PRI_SWITCH_EUROISDN_T1, + SANGOMA_PRI_SWITCH_NI1 = PRI_SWITCH_NI1, + SANGOMA_PRI_SWITCH_GR303_EOC = PRI_SWITCH_GR303_EOC, + SANGOMA_PRI_SWITCH_GR303_TMC = PRI_SWITCH_GR303_TMC, + SANGOMA_PRI_SWITCH_QSIG = PRI_SWITCH_QSIG +} sangoma_pri_switch_t; + +typedef enum { + SANGOMA_PRI_READY = (1 << 0) +} sangoma_pri_flag_t; + +struct sangoma_pri; +typedef int (*event_handler)(struct sangoma_pri *, sangoma_pri_event_t, pri_event *); +typedef int (*loop_handler)(struct sangoma_pri *); +#define MAX_EVENT 18 + +struct sangoma_pri { + struct pri *pri; + int span; + int dchan; + unsigned int flags; + void *private_info; + event_handler eventmap[MAX_EVENT+1]; + loop_handler on_loop; + ftdm_channel_t *zdchan; +}; + +struct sangoma_pri_event_list { + int event_id; + int pri_event; + char *name; +}; + + + +#define SANGOMA_MAP_PRI_EVENT(spri, event, func) spri.eventmap[event] = func; + +char *sangoma_pri_event_str(sangoma_pri_event_t event_id); +int sangoma_one_loop(struct sangoma_pri *spri); +int sangoma_init_pri(struct sangoma_pri *spri, int span, int dchan, int swtype, int node, int debug); +int sangoma_run_pri(struct sangoma_pri *spri); + +#endif diff --git a/libs/freetdm/src/ss7/README b/libs/freetdm/src/ss7/README new file mode 100644 index 0000000000..0efe94c94a --- /dev/null +++ b/libs/freetdm/src/ss7/README @@ -0,0 +1,3 @@ +SS7 Coming soon + +-Shane Burrell- diff --git a/libs/freetdm/src/testanalog.c b/libs/freetdm/src/testanalog.c new file mode 100644 index 0000000000..52b79f16bc --- /dev/null +++ b/libs/freetdm/src/testanalog.c @@ -0,0 +1,133 @@ +#include "freetdm.h" + + +static void *test_call(ftdm_thread_t *me, void *obj) +{ + ftdm_channel_t *chan = (ftdm_channel_t *) obj; + uint8_t frame[1024]; + ftdm_size_t len; + char *number = strdup("5551212"); + + ftdm_sleep(10 * 1000); + + ftdm_log(FTDM_LOG_DEBUG, "answer call and start echo test\n"); + + ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_UP); + ftdm_channel_command(chan, FTDM_COMMAND_SEND_DTMF, number); + + while (chan->state == FTDM_CHANNEL_STATE_UP) { + ftdm_wait_flag_t flags = FTDM_READ; + + if (ftdm_channel_wait(chan, &flags, -1) == FTDM_FAIL) { + break; + } + len = sizeof(frame); + if (flags & FTDM_READ) { + if (ftdm_channel_read(chan, frame, &len) == FTDM_SUCCESS) { + //ftdm_log(FTDM_LOG_DEBUG, "WRITE %d\n", len); + ftdm_channel_write(chan, frame, sizeof(frame), &len); + } else { + break; + } + } + } + + if (chan->state == FTDM_CHANNEL_STATE_UP) { + ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_BUSY); + } + + ftdm_log(FTDM_LOG_DEBUG, "call over\n"); + free(number); + return NULL; +} + +static FIO_SIGNAL_CB_FUNCTION(on_signal) +{ + ftdm_log(FTDM_LOG_DEBUG, "got sig [%s]\n", ftdm_signal_event2str(sigmsg->event_id)); + + switch(sigmsg->event_id) { + case FTDM_SIGEVENT_START: + ftdm_set_state_locked(sigmsg->channel, FTDM_CHANNEL_STATE_RING); + ftdm_log(FTDM_LOG_DEBUG, "launching thread and indicating ring\n"); + ftdm_thread_create_detached(test_call, sigmsg->channel); + break; + default: + break; + } + + return FTDM_SUCCESS; +} + +static int R = 0; +#if 0 +static void handle_SIGINT(int sig) +{ + if (sig); + R = 0; + return; +} +#endif + +int main(int argc, char *argv[]) +{ + ftdm_span_t *span; + int span_id; + int digit_timeout = 2000; + int max_dialstr = 11; + + if (argc < 2) { + printf("usage %s \n", argv[0]); + exit(-1); + } + + span_id = atoi(argv[1]); + + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + + if (ftdm_global_init() != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error loading FreeTDM\n"); + exit(-1); + } + + ftdm_log(FTDM_LOG_DEBUG, "FreeTDM loaded\n"); + + if (ftdm_span_find(span_id, &span) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span\n"); + goto done; + } + + + if (ftdm_configure_span("analog", span, on_signal, + "tonemap", "us", + "digit_timeout", &digit_timeout, + "max_dialstr", &max_dialstr, + TAG_END + ) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span\n"); + goto done; + } + ftdm_span_start(span); + + R = 1; + + while(ftdm_running() && R) { + ftdm_sleep(1 * 1000); + } + +done: + + ftdm_global_destroy(); + return 0; +} + +/* 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: + */ + diff --git a/libs/freetdm/src/testapp.c b/libs/freetdm/src/testapp.c new file mode 100644 index 0000000000..0cb2822712 --- /dev/null +++ b/libs/freetdm/src/testapp.c @@ -0,0 +1,83 @@ +#include "freetdm.h" + +int main(int argc, char *argv[]) +{ + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + ftdm_channel_t *chan; + unsigned ms = 20; + ftdm_codec_t codec = FTDM_CODEC_SLIN; + unsigned runs = 1; + + + if (ftdm_global_init() != FTDM_SUCCESS) { + fprintf(stderr, "Error loading FreeTDM\n"); + exit(-1); + } + + printf("FreeTDM loaded\n"); + + top: + //if (ftdm_channel_open_any("wanpipe", 0, FTDM_TOP_DOWN, &chan) == FTDM_SUCCESS) { + if (ftdm_channel_open(1, 1, &chan) == FTDM_SUCCESS) { + int x = 0; + printf("opened channel %d:%d\n", chan->span_id, chan->chan_id); + +#if 1 + if (ftdm_channel_command(chan, FTDM_COMMAND_SET_INTERVAL, &ms) == FTDM_SUCCESS) { + ms = 0; + ftdm_channel_command(chan, FTDM_COMMAND_GET_INTERVAL, &ms); + printf("interval set to %u\n", ms); + } else { + printf("set interval failed [%s]\n", chan->last_error); + } +#endif + if (ftdm_channel_command(chan, FTDM_COMMAND_SET_CODEC, &codec) == FTDM_SUCCESS) { + codec = 1; + ftdm_channel_command(chan, FTDM_COMMAND_GET_CODEC, &codec); + printf("codec set to %u\n", codec); + } else { + printf("set codec failed [%s]\n", chan->last_error); + } + + for(x = 0; x < 25; x++) { + unsigned char buf[2048]; + ftdm_size_t len = sizeof(buf); + ftdm_wait_flag_t flags = FTDM_READ; + + if (ftdm_channel_wait(chan, &flags, -1) == FTDM_FAIL) { + printf("wait FAIL! %u [%s]\n", (unsigned)len, chan->last_error); + } + if (flags & FTDM_READ) { + if (ftdm_channel_read(chan, buf, &len) == FTDM_SUCCESS) { + printf("READ: %u\n", (unsigned)len); + } else { + printf("READ FAIL! %u [%s]\n", (unsigned)len, chan->last_error); + break; + } + } else { + printf("wait fail [%s]\n", chan->last_error); + } + } + ftdm_channel_close(&chan); + } else { + printf("open fail [%s]\n", chan->last_error); + } + + if(--runs) { + goto top; + } + + ftdm_global_destroy(); + return 0; +} + +/* 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: + */ diff --git a/libs/freetdm/src/testboost.c b/libs/freetdm/src/testboost.c new file mode 100644 index 0000000000..152cae35ec --- /dev/null +++ b/libs/freetdm/src/testboost.c @@ -0,0 +1,82 @@ +#include "freetdm.h" + +static FIO_SIGNAL_CB_FUNCTION(on_signal) +{ + return FTDM_FAIL; +} + +static int R = 0; +#if 0 +static void handle_SIGINT(int sig) +{ + if (sig); + R = 0; + return; +} +#endif +int main(int argc, char *argv[]) +{ + ftdm_conf_parameter_t parameters[20]; + ftdm_span_t *span; + int local_port, remote_port; + + local_port = remote_port = 53000; + + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); +#if 0 + if (argc < 2) { + printf("invalid arguments\n"); + exit(-1); + } +#endif + + if (ftdm_global_init() != FTDM_SUCCESS) { + fprintf(stderr, "Error loading FreeTDM\n"); + exit(-1); + } + if (ftdm_global_configuration() != FTDM_SUCCESS) { + fprintf(stderr, "Error configuring FreeTDM\n"); + exit(-1); + } + + printf("FreeTDM loaded\n"); + + if (ftdm_span_find_by_name("wp1", &span) != FTDM_SUCCESS) { + fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]); + goto done; + } + parameters[0].var = "sigmod"; + parameters[0].val = "sangoma_prid"; + parameters[1].var = "switchtype"; + parameters[1].val = "euroisdn"; + parameters[1].var = "signalling"; + parameters[1].val = "pri_cpe"; + parameters[2].var = NULL; + if (ftdm_configure_span_signaling("sangoma_boost", span, on_signal, parameters) == FTDM_SUCCESS) { + ftdm_span_start(span); + } else { + fprintf(stderr, "Error starting SS7_BOOST\n"); + goto done; + } + + while(ftdm_running() && R) { + ftdm_sleep(1 * 1000); + } + + done: + + ftdm_global_destroy(); + + return 0; +} + +/* 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: + */ diff --git a/libs/freetdm/src/testcid.c b/libs/freetdm/src/testcid.c new file mode 100644 index 0000000000..59086d003d --- /dev/null +++ b/libs/freetdm/src/testcid.c @@ -0,0 +1,108 @@ +#include "freetdm.h" +ftdm_status_t my_write_sample(int16_t *buf, ftdm_size_t buflen, void *user_data); + +struct helper { + int fd; + int wrote; +}; + +ftdm_status_t my_write_sample(int16_t *buf, ftdm_size_t buflen, void *user_data) +{ + struct helper *foo = (struct helper *) user_data; + size_t len; + len = write(foo->fd, buf, buflen * 2); + if (!len) return FTDM_FAIL; + foo->wrote += buflen * 2; + return FTDM_SUCCESS; +} + +int main(int argc, char *argv[]) +{ + struct ftdm_fsk_modulator fsk_trans; + ftdm_fsk_data_state_t fsk_data = {0}; + int fd = -1; + int16_t buf[160] = {0}; + ssize_t len = 0; + size_t type, mlen; + char *sp; + char str[128] = ""; + char fbuf[256]; + uint8_t databuf[1024] = ""; + struct helper foo = {0}; + // int x, bytes, start_bits = 180, stop_bits = 5, sbits = 300; + char time_str[9]; + struct tm tm; + time_t now; + + if (argc < 2) { + int x; + const char *url = "sip:cool@rad.com"; + + if ((fd = open("tone.raw", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + fprintf(stderr, "File Error! [%s]\n", strerror(errno)); + exit(-1); + } + + + time(&now); + localtime_r(&now, &tm); + strftime(time_str, sizeof(time_str), "%m%d%H%M", &tm); + + ftdm_fsk_data_init(&fsk_data, databuf, sizeof(databuf)); +#if 1 + + ftdm_fsk_data_add_mdmf(&fsk_data, MDMF_DATETIME, (uint8_t *)time_str, strlen(time_str)); + //ftdm_fsk_data_add_mdmf(&fsk_data, MDMF_DATETIME, "06091213", 8); + ftdm_fsk_data_add_mdmf(&fsk_data, MDMF_PHONE_NUM, (uint8_t *)"14149361212", 7); + ftdm_fsk_data_add_mdmf(&fsk_data, MDMF_PHONE_NAME, (uint8_t *)"Fred Smith", 10); + for(x = 0; x < 0; x++) + ftdm_fsk_data_add_mdmf(&fsk_data, MDMF_ALT_ROUTE, (uint8_t *)url, strlen(url)); +#else + ftdm_fsk_data_add_sdmf(&fsk_data, "06061234", "0"); + //ftdm_fsk_data_add_sdmf(&state, "06061234", "5551212"); +#endif + ftdm_fsk_data_add_checksum(&fsk_data); + + foo.fd = fd; + + + ftdm_fsk_modulator_init(&fsk_trans, FSK_BELL202, 8000, &fsk_data, -14, 180, 5, 300, my_write_sample, &foo); + ftdm_fsk_modulator_send_all((&fsk_trans)); + + printf("%u %d %d\n", (unsigned) fsk_data.dlen, foo.wrote, fsk_trans.est_bytes); + + if (fd > -1) { + close (fd); + } + + return 0; + } + + if (ftdm_fsk_demod_init(&fsk_data, 8000, (uint8_t *)fbuf, sizeof(fbuf))) { + printf("wtf\n"); + return 0; + } + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "cant open file %s\n", argv[1]); + exit (-1); + } + + while((len = read(fd, buf, sizeof(buf))) > 0) { + if (ftdm_fsk_demod_feed(&fsk_data, buf, len / 2) != FTDM_SUCCESS) { + break; + } + } + + while(ftdm_fsk_data_parse(&fsk_data, &type, &sp, &mlen) == FTDM_SUCCESS) { + ftdm_copy_string(str, sp, mlen+1); + *(str+mlen) = '\0'; + ftdm_clean_string(str); + printf("TYPE %u (%s) LEN %u VAL [%s]\n", (unsigned)type, ftdm_mdmf_type2str(type), (unsigned)mlen, str); + } + + ftdm_fsk_demod_destroy(&fsk_data); + + close(fd); + return 0; +} diff --git a/libs/freetdm/src/testisdn.c b/libs/freetdm/src/testisdn.c new file mode 100644 index 0000000000..c4b7295832 --- /dev/null +++ b/libs/freetdm/src/testisdn.c @@ -0,0 +1,74 @@ +#include "freetdm.h" +#include + + +static FIO_SIGNAL_CB_FUNCTION(on_signal) +{ + return FTDM_FAIL; +} + +static int R = 0; +static void handle_SIGINT(int sig) +{ + if (sig); + R = 0; + return; +} + +int main(int argc, char *argv[]) +{ + ftdm_span_t *span; + + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + + if (argc < 2) { + printf("umm no\n"); + exit(-1); + } + + if (ftdm_global_init() != FTDM_SUCCESS) { + fprintf(stderr, "Error loading FreeTDM\n"); + exit(-1); + } + + printf("FreeTDM loaded\n"); + + if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) { + fprintf(stderr, "Error finding FreeTDM span\n"); + goto done; + } + + if (ftdm_configure_span("isdn", span, on_signal, + "mode", "te", + "dialect", "national", + TAG_END) == FTDM_SUCCESS) { + ftdm_span_start(span); + } else { + fprintf(stderr, "Error starting ISDN D-Channel\n"); + goto done; + } + + signal(SIGINT, handle_SIGINT); + R = 1; + while(R) { + ftdm_sleep(1 * 1000); + } + + done: + + ftdm_global_destroy(); + + return 1; + +} + +/* 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: + */ diff --git a/libs/freetdm/src/testm3ua.c b/libs/freetdm/src/testm3ua.c new file mode 100644 index 0000000000..5848470e7a --- /dev/null +++ b/libs/freetdm/src/testm3ua.c @@ -0,0 +1,60 @@ +/* + * testm3ua.c + * freetdm + * + * Created by Shane Burrell on 4/8/08. + * Copyright 2008 __MyCompanyName__. All rights reserved. + * + */ + +#include "testm3ua.h" +#include "freetdm.h" +#include "ftdm_m3ua.h" + +static FIO_SIGNAL_CB_FUNCTION(on_signal) +{ + return FTDM_FAIL; +} + +int main(int argc, char *argv[]) +{ + ftdm_span_t *span; + //m3ua_data_t *data; + + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + + if (argc < 5) { + printf("more args needed\n"); + exit(-1); + } + + if (ftdm_global_init() != FTDM_SUCCESS) { + fprintf(stderr, "Error loading FreeTDM\n"); + exit(-1); + } + + printf("FreeTDM loaded\n"); + + if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) { + fprintf(stderr, "Error finding FreeTDM span\n"); + goto done; + } + + + if (ftdm_m3ua_configure_span(span) == FTDM_SUCCESS) { + //data = span->signal_data; + ftdm_m3ua_start(span); + } else { + fprintf(stderr, "Error starting M3UA\n"); + goto done; + } + + //while(ftdm_test_flag(data, FTDM_M3UA_RUNNING)) { + // ftdm_sleep(1 * 1000); + //} + + done: + + ftdm_global_destroy(); + +} diff --git a/libs/freetdm/src/testpri.c b/libs/freetdm/src/testpri.c new file mode 100644 index 0000000000..42afe2ffd3 --- /dev/null +++ b/libs/freetdm/src/testpri.c @@ -0,0 +1,162 @@ +#include "freetdm.h" +#include + +static int THREADS[4][31] = { {0} }; +static int R = 0; +static int T = 0; +static ftdm_mutex_t *mutex = NULL; + + +static void *channel_run(ftdm_thread_t *me, void *obj) +{ + ftdm_channel_t *ftdmchan = obj; + int fd = -1; + short buf[160]; + + ftdm_mutex_lock(mutex); + T++; + ftdm_mutex_unlock(mutex); + + ftdm_set_state_locked_wait(ftdmchan, FTDM_CHANNEL_STATE_UP); + + if ((fd = open("test.raw", O_RDONLY, 0)) < 0) { + goto end; + } + + while(R == 1 && THREADS[ftdmchan->span_id][ftdmchan->chan_id] == 1) { + ssize_t bytes = read(fd, buf, sizeof(buf)); + size_t bbytes; + + if (bytes <= 0) { + break; + } + + bbytes = (size_t) bytes; + + fio_slin2alaw(buf, sizeof(buf), &bbytes); + + if (ftdm_channel_write(ftdmchan, buf, sizeof(buf), &bbytes) != FTDM_SUCCESS) { + break; + } + } + + close(fd); + + end: + + ftdm_set_state_locked_wait(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + + THREADS[ftdmchan->span_id][ftdmchan->chan_id] = 0; + + ftdm_mutex_lock(mutex); + T = 0; + ftdm_mutex_unlock(mutex); + + return NULL; +} + +static FIO_SIGNAL_CB_FUNCTION(on_signal) +{ + ftdm_log(FTDM_LOG_DEBUG, "got sig %d:%d [%s]\n", sigmsg->channel->span_id, sigmsg->channel->chan_id, ftdm_signal_event2str(sigmsg->event_id)); + + switch(sigmsg->event_id) { + + case FTDM_SIGEVENT_STOP: + THREADS[sigmsg->channel->span_id][sigmsg->channel->chan_id] = -1; + break; + + case FTDM_SIGEVENT_START: + if (!THREADS[sigmsg->channel->span_id][sigmsg->channel->chan_id]) { + THREADS[sigmsg->channel->span_id][sigmsg->channel->chan_id] = 1; + ftdm_thread_create_detached(channel_run, sigmsg->channel); + } + + break; + default: + break; + } + + return FTDM_SUCCESS; +} + + +static void handle_SIGINT(int sig) +{ + if (sig); + + ftdm_mutex_lock(mutex); + R = 0; + ftdm_mutex_unlock(mutex); + + return; +} + +int main(int argc, char *argv[]) +{ + ftdm_span_t *span; + ftdm_mutex_create(&mutex); + + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + + if (argc < 2) { + printf("umm no\n"); + exit(-1); + } + + if (ftdm_global_init() != FTDM_SUCCESS) { + fprintf(stderr, "Error loading FreeTDM\n"); + exit(-1); + } + + printf("FreeTDM loaded\n"); + + if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) { + fprintf(stderr, "Error finding FreeTDM span\n"); + goto done; + } + + + + if (ftdm_configure_span( + "libpri", span, on_signal, + "node", "cpe", + "switch", "euroisdn", + "dp", "unknown", + "l1", "alaw", + "debug", NULL, + "opts", 0, + TAG_END) == FTDM_SUCCESS) { + + + ftdm_span_start(span); + } else { + fprintf(stderr, "Error starting ISDN D-Channel\n"); + goto done; + } + + signal(SIGINT, handle_SIGINT); + ftdm_mutex_lock(mutex); + R = 1; + ftdm_mutex_unlock(mutex); + while(R || T) { + ftdm_sleep(1 * 1000); + } + + done: + + ftdm_global_destroy(); + + return 1; + +} + +/* 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: + */ diff --git a/libs/freetdm/src/testr2.c b/libs/freetdm/src/testr2.c new file mode 100644 index 0000000000..5eb80dfe7a --- /dev/null +++ b/libs/freetdm/src/testr2.c @@ -0,0 +1,86 @@ +#include "freetdm.h" +#include + +static int R = 0; +static ftdm_mutex_t *mutex = NULL; + +static FIO_SIGNAL_CB_FUNCTION(on_r2_signal) +{ + ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", ftdm_signal_event2str(sigmsg->event_id), sigmsg->channel->physical_chan_id); + return FTDM_SUCCESS; +} + +static void handle_SIGINT(int sig) +{ + ftdm_mutex_lock(mutex); + R = 0; + ftdm_mutex_unlock(mutex); + return; +} + +int main(int argc, char *argv[]) +{ + ftdm_span_t *span; + ftdm_mutex_create(&mutex); + + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + + if (argc < 2) { + printf("umm no\n"); + exit(-1); + } + + if (ftdm_global_init() != FTDM_SUCCESS) { + fprintf(stderr, "Error loading FreeTDM\n"); + exit(-1); + } + + printf("FreeTDM loaded\n"); + + if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) { + fprintf(stderr, "Error finding FreeTDM span\n"); + goto done; + } + + + + if (ftdm_configure_span("r2", span, on_r2_signal, + "variant", "mx", + "max_ani", 10, + "max_dnis", 4, + "logging", "all", + TAG_END) == FTDM_SUCCESS) { + + + ftdm_span_start(span); + } else { + fprintf(stderr, "Error starting R2 span\n"); + goto done; + } + + signal(SIGINT, handle_SIGINT); + ftdm_mutex_lock(mutex); + R = 1; + ftdm_mutex_unlock(mutex); + while(R) { + ftdm_sleep(1 * 1000); + } + + done: + + ftdm_global_destroy(); + + return 1; + +} + +/* 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: + */ diff --git a/libs/freetdm/src/testsangomaboost.c b/libs/freetdm/src/testsangomaboost.c new file mode 100644 index 0000000000..8c888633f1 --- /dev/null +++ b/libs/freetdm/src/testsangomaboost.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2010, Sangoma Technologies + * Moises Silva + * 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. + */ + +/** + * Sample program for the boost signaling absraction. + * Usage: boostsample + * The span name must be a valid span defined in freetdm.conf + * compile this program linking to the freetdm library (ie -lfreetdm) + **/ + +#ifndef __linux__ +#define _CRT_SECURE_NO_WARNINGS 1 +#endif + +#include + +#include "freetdm.h" + + +/* arbitrary limit for max calls in this sample program */ +#define MAX_CALLS 255 + +/* some timers (in seconds) to fake responses in incoming calls */ +#define PROGRESS_TIMER 1 +#define ANSWER_TIMER 5 +#define HANGUP_TIMER 15 + +/* simple variable used to stop the application */ +static int app_running = 0; + +typedef void (*expired_function_t)(ftdm_channel_t *channel); +typedef struct dummy_timer_s { + int time; + ftdm_channel_t *channel; + expired_function_t expired; +} dummy_timer_t; + +/* dummy second resolution timers */ +static dummy_timer_t g_timers[MAX_CALLS]; + +/* mutex to protect the timers (both, the test thread and the signaling thread may modify them) */ +static ftdm_mutex_t *g_schedule_mutex; + +/* unique outgoing channel */ +static ftdm_channel_t *g_outgoing_channel = NULL; + +static void interrupt_requested(int signal) +{ + app_running = 0; +} + +static void schedule_timer(ftdm_channel_t *channel, int sec, expired_function_t expired) +{ + int i; + ftdm_mutex_lock(g_schedule_mutex); + for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { + /* check the timer slot is free to use */ + if (!g_timers[i].time) { + g_timers[i].time = sec; + g_timers[i].channel = channel; + g_timers[i].expired = expired; + ftdm_mutex_unlock(g_schedule_mutex); + return; + } + } + ftdm_log(FTDM_LOG_ERROR, "Failed to schedule timer\n"); + ftdm_mutex_unlock(g_schedule_mutex); +} + +static void run_timers(void) +{ + int i; + void *channel; + expired_function_t expired_func = NULL; + ftdm_mutex_lock(g_schedule_mutex); + for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { + /* if there's time left, decrement */ + if (g_timers[i].time) { + g_timers[i].time--; + } + + /* if time expired and we have an expired function, call it */ + if (!g_timers[i].time && g_timers[i].expired) { + expired_func = g_timers[i].expired; + channel = g_timers[i].channel; + memset(&g_timers[i], 0, sizeof(g_timers[i])); + expired_func(channel); + } + } + ftdm_mutex_unlock(g_schedule_mutex); +} + +static void release_timers(ftdm_channel_t *channel) +{ + int i; + ftdm_mutex_lock(g_schedule_mutex); + for (i = 0; i < sizeof(g_timers)/sizeof(g_timers[0]); i++) { + /* clear any timer belonging to the given channel */ + if (g_timers[i].channel == channel) { + memset(&g_timers[i], 0, sizeof(g_timers[i])); + } + } + ftdm_mutex_unlock(g_schedule_mutex); +} + +/* hangup the call */ +static void send_hangup(ftdm_channel_t *channel) +{ + ftdm_log(FTDM_LOG_NOTICE, "-- Requesting hangup in channel %d:%d\n", channel->span_id, channel->chan_id); + ftdm_set_state_locked(channel, FTDM_CHANNEL_STATE_HANGUP); +} + +/* send answer for an incoming call */ +static void send_answer(ftdm_channel_t *channel) +{ + /* we move the channel signaling state machine to UP (answered) */ + ftdm_log(FTDM_LOG_NOTICE, "-- Requesting answer in channel %d:%d\n", channel->span_id, channel->chan_id); + ftdm_set_state_locked(channel, FTDM_CHANNEL_STATE_UP); + schedule_timer(channel, HANGUP_TIMER, send_hangup); +} + +/* send progress for an incoming */ +static void send_progress(ftdm_channel_t *channel) +{ + /* we move the channel signaling state machine to UP (answered) */ + ftdm_log(FTDM_LOG_NOTICE, "-- Requesting progress\n", channel->span_id, channel->chan_id); + ftdm_set_state_locked(channel, FTDM_CHANNEL_STATE_PROGRESS); + schedule_timer(channel, ANSWER_TIMER, send_answer); +} + +/* This function will be called in an undetermined signaling thread, you must not do + * any blocking operations here or the signaling stack may delay other call event processing + * The arguments for this function are defined in FIO_SIGNAL_CB_FUNCTION prototype, I just + * name them here for your convenience: + * ftdm_sigmsg_t *sigmsg + * - The sigmsg structure contains the ftdm_channel structure that represents the channel where + * the event occurred and the event_id of the signaling event that just occurred. + * */ +static FIO_SIGNAL_CB_FUNCTION(on_signaling_event) +{ + switch (sigmsg->event_id) { + /* This event signals the start of an incoming call */ + case FTDM_SIGEVENT_START: + ftdm_log(FTDM_LOG_NOTICE, "Incoming call received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); + schedule_timer(sigmsg->channel, PROGRESS_TIMER, send_progress); + break; + /* This event signals progress on an outgoing call */ + case FTDM_SIGEVENT_PROGRESS_MEDIA: + ftdm_log(FTDM_LOG_NOTICE, "Progress message received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); + break; + /* This event signals answer in an outgoing call */ + case FTDM_SIGEVENT_UP: + ftdm_log(FTDM_LOG_NOTICE, "Answer received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); + /* now the channel is answered and we can use + * ftdm_channel_wait() to wait for input/output in a channel (equivalent to poll() or select()) + * ftdm_channel_read() to read available data in a channel + * ftdm_channel_write() to write to the channel */ + break; + /* This event signals hangup from the other end */ + case FTDM_SIGEVENT_STOP: + ftdm_log(FTDM_LOG_NOTICE, "Hangup received in channel %d:%d\n", sigmsg->span_id, sigmsg->chan_id); + if (g_outgoing_channel == sigmsg->channel) { + g_outgoing_channel = NULL; + } + /* release any timer for this channel */ + release_timers(sigmsg->channel); + /* acknowledge the hangup */ + ftdm_set_state_locked(sigmsg->channel, FTDM_CHANNEL_STATE_HANGUP); + break; + default: + ftdm_log(FTDM_LOG_WARNING, "Unhandled event %s in channel %d:%d\n", ftdm_signal_event2str(sigmsg->event_id), + sigmsg->span_id, sigmsg->chan_id); + break; + } + return FTDM_SUCCESS; +} + +static void place_call(const ftdm_span_t *span, const char *number) +{ + ftdm_channel_t *ftdmchan = NULL; + ftdm_caller_data_t caller_data = {{ 0 }}; + ftdm_status_t status = FTDM_FAIL; + + /* set destiny number */ + ftdm_set_string(caller_data.dnis.digits, number); + + /* set callerid */ + ftdm_set_string(caller_data.cid_name, "testsangomaboost"); + ftdm_set_string(caller_data.cid_num.digits, "1234"); + + /* request to search for an outgoing channel top down with the given caller data. + * it is also an option to use ftdm_channel_open_by_group to let freetdm hunt + * an available channel in a given group instead of per span + * */ + status = ftdm_channel_open_by_span(span->span_id, FTDM_TOP_DOWN, &caller_data, &ftdmchan); + if (status != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n"); + return; + } + + g_outgoing_channel = ftdmchan; + + /* set the caller data for the outgoing channel */ + memcpy(&ftdmchan->caller_data, &caller_data, sizeof(caller_data)); + + status = ftdm_channel_outgoing_call(ftdmchan); + if (status != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to originate call\n"); + return; + } + + /* this is required to initialize the outgoing channel */ + ftdm_channel_init(ftdmchan); +} + +int main(int argc, char *argv[]) +{ + ftdm_conf_parameter_t parameters[20]; + ftdm_span_t *span; + char *todial = NULL; + int32_t ticks = 0; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [number to dial if any]\n", argv[0]); + exit(-1); + } + + /* register a handler to shutdown things properly */ + if (signal(SIGINT, interrupt_requested) == SIG_ERR) { + fprintf(stderr, "Could not set the SIGINT signal handler: %s\n", strerror(errno)); + exit(-1); + } + + if (argc >= 3) { + todial = argv[2]; + if (!strlen(todial)) { + todial = NULL; + } + } + + /* clear any outstanding timers */ + memset(&g_timers, 0, sizeof(g_timers)); + + /* set the logging level to use */ + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + + /* Initialize the FTDM library */ + if (ftdm_global_init() != FTDM_SUCCESS) { + fprintf(stderr, "Error loading FreeTDM\n"); + exit(-1); + } + + /* create the schedule mutex */ + ftdm_mutex_create(&g_schedule_mutex); + + /* Load the FreeTDM configuration */ + if (ftdm_global_configuration() != FTDM_SUCCESS) { + fprintf(stderr, "Error configuring FreeTDM\n"); + exit(-1); + } + + /* At this point FreeTDM is ready to be used, the spans defined in freetdm.conf have the basic I/O board configuration + * but no telephony signaling configuration at all. */ + printf("FreeTDM loaded ...\n"); + + /* Retrieve a span by name (according to freetdm.conf) */ + if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) { + fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]); + goto done; + } + + /* prepare the configuration parameters that will be sent down to the signaling stack, the array of paramters must be terminated by an + * array element with a null .var member */ + + /* for sangoma_boost signaling (abstraction signaling used by Sangoma for PRI, BRI and SS7) the first parameter you must send + * is sigmod, which must be either sangoma_prid, if you have the PRI stack available, or sangoma_brid for the BRI stack */ + parameters[0].var = "sigmod"; + parameters[0].val = "sangoma_prid"; + + /* following parameters are signaling stack specific, this ones are for PRI */ + parameters[1].var = "switchtype"; + parameters[1].val = "national"; + + parameters[2].var = "signalling"; + parameters[2].val = "pri_cpe"; + + /* + * parameters[3].var = "nfas_primary"; + * parameters[3].val = "4"; //span number + * + * parameters[4].var = "nfas_secondary"; + * parameters[4].val = "2"; //span number + * + * parameters[5].var = "nfas_group"; + * parameters[5].val = "1"; + * */ + + /* the last parameter .var member must be NULL! */ + parameters[3].var = NULL; + + /* send the configuration values down to the stack */ + if (ftdm_configure_span_signaling("sangoma_boost", span, on_signaling_event, parameters) != FTDM_SUCCESS) { + fprintf(stderr, "Error configuring sangoma_boost signaling abstraction in span %s\n", span->name); + goto done; + } + + /* configuration succeeded, we can proceed now to start the span + * This step will launch at least 1 background (may be more, depending on the signaling stack used) + * to handle *ALL* signaling events for this span, your on_signaling_event callback will be called always + * in one of those infraestructure threads and you MUST NOT block in that handler to avoid delays and errors + * in the signaling processing for any call. + * */ + ftdm_span_start(span); + + app_running = 1; + + /* The application thread can go on and do anything else, like waiting for a shutdown signal */ + while(ftdm_running() && app_running) { + ftdm_sleep(1000); + run_timers(); + ticks++; + if (!(ticks % 10) && todial && !g_outgoing_channel) { + ftdm_log(FTDM_LOG_NOTICE, "Originating call to number %s\n", todial); + place_call(span, todial); + } + } + printf("Shutting down FreeTDM ...\n"); + done: + + ftdm_mutex_destroy(&g_schedule_mutex); + + /* whenever you're done, this function will shutdown the signaling threads in any span that was started */ + ftdm_global_destroy(); + + return 0; +} + +/* 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: + */ diff --git a/libs/freetdm/src/testtones.c b/libs/freetdm/src/testtones.c new file mode 100644 index 0000000000..c4cc327772 --- /dev/null +++ b/libs/freetdm/src/testtones.c @@ -0,0 +1,76 @@ +#include "freetdm.h" + +struct ttmp { + int fd; +}; + +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + struct ttmp *tmp = ts->user_data; + int wrote; + size_t len; + + wrote = teletone_mux_tones(ts, map); + len = write(tmp->fd, ts->buffer, wrote * 2); + + if (!len) return -1; + + return 0; +} + +#if 1 +int main(int argc, char *argv[]) +{ + teletone_generation_session_t ts; + struct ttmp tmp; + + if (argc < 3) { + fprintf(stderr, "Arg Error! \n"); + exit(-1); + } + + if ((tmp.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + fprintf(stderr, "File Error! [%s]\n", strerror(errno)); + exit(-1); + } + + teletone_init_session(&ts, 0, teletone_handler, &tmp); + ts.rate = 8000; + ts.debug = 1; + ts.debug_stream = stdout; + teletone_run(&ts, argv[2]); + close(tmp.fd); + + return 0; +} +#else +int32_t main(int argc, char *argv[]) +{ + int32_t j, i, fd = -1; + int32_t rate = 8000; + /* SIT tones and durations */ + float tones[] = { 913.8, 1370.6, 1776.7, 0 }; + int32_t durations[] = {274, 274, 380, 0}; + teletone_dds_state_t dds = {0}; + int16_t sample; + size_t len = 1; + + if (argc < 2 || (fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + fprintf(stderr, "File Error!\n", strerror(errno)); + exit(-1); + } + + for (j = 0; tones[j] && durations[j]; j++) { + + teletone_dds_state_set_tone(&dds, tones[j], rate, -50); + + for(i = 0; (i < durations[j] * rate / 1000) && len != 0; i++) { + sample = teletone_dds_modulate_sample(&dds) * 20; + len = write(fd, &sample, sizeof(sample)); + } + + } + + close(fd); +} +#endif diff --git a/libs/freetdm/src/uart.c b/libs/freetdm/src/uart.c new file mode 100644 index 0000000000..d042496a86 --- /dev/null +++ b/libs/freetdm/src/uart.c @@ -0,0 +1,125 @@ + +/* + * uart.c + * + * Copyright (c) 2005 Robert Krten. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This module contains a simple 8-bit UART, which performs a callback + * with the decoded byte value. + * + * 2005 06 11 R. Krten created +*/ + +#include +#include +#include +#include +#include + +#include "uart.h" + +/* + * dsp_uart_attr_init + * + * Initializes the attributes structure; this must be done before the + * attributes structure is used. +*/ + +void dsp_uart_attr_init (dsp_uart_attr_t *attr) +{ + memset (attr, 0, sizeof (*attr)); +} + +/* + * dsp_uart_attr_get_bytehandler + * dsp_uart_attr_set_bytehandler + * + * These functions get and set their respective elements from the + * attributes structure. If an error code is returned, it is just + * zero == ok, -1 == fail. +*/ + +bytehandler_func_t dsp_uart_attr_get_bytehandler(dsp_uart_attr_t *attr, void **bytehandler_arg) +{ + *bytehandler_arg = attr->bytehandler_arg; + return attr->bytehandler; +} + +void dsp_uart_attr_set_bytehandler(dsp_uart_attr_t *attr, bytehandler_func_t bytehandler, void *bytehandler_arg) +{ + attr->bytehandler = bytehandler; + attr->bytehandler_arg = bytehandler_arg; +} + +dsp_uart_handle_t *dsp_uart_create(dsp_uart_attr_t *attr) +{ + dsp_uart_handle_t *handle; + + handle = ftdm_malloc(sizeof (*handle)); + if (handle) { + memset(handle, 0, sizeof (*handle)); + + /* fill the attributes member */ + memcpy(&handle->attr, attr, sizeof (*attr)); + } + return handle; +} + +void dsp_uart_destroy(dsp_uart_handle_t **handle) +{ + if (*handle) { + ftdm_safe_free(*handle); + *handle = NULL; + } +} + + +void dsp_uart_bit_handler(void *x, int bit) +{ + dsp_uart_handle_t *handle = (dsp_uart_handle_t *) x; + + if (!handle->have_start) { + if (bit) { + return; /* waiting for start bit (0) */ + } + handle->have_start = 1; + handle->data = 0; + handle->nbits = 0; + return; + } + + handle->data >>= 1; + handle->data |= 0x80 * !!bit; + handle->nbits++; + + if (handle->nbits == 8) { + handle->attr.bytehandler(handle->attr.bytehandler_arg, handle->data); + handle->nbits = 0; + handle->data = 0; + handle->have_start = 0; + } +/* might consider handling errors in the future... */ +} + diff --git a/libs/openzap/.gitignore b/libs/openzap/.gitignore new file mode 100644 index 0000000000..d90d79d08d --- /dev/null +++ b/libs/openzap/.gitignore @@ -0,0 +1,13 @@ +*.o +*.lo +*.so +*.a +*.orig +*.rej +*.log + +Makefile +config.* +configure +libtool +aclocal.m4 diff --git a/libs/openzap/.update b/libs/openzap/.update new file mode 100644 index 0000000000..e9475b68ed --- /dev/null +++ b/libs/openzap/.update @@ -0,0 +1 @@ +Fri Oct 3 17:54:41 EDT 2008 diff --git a/libs/openzap/AUTHORS b/libs/openzap/AUTHORS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/openzap/ChangeLog b/libs/openzap/ChangeLog new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/openzap/Makefile.am b/libs/openzap/Makefile.am new file mode 100644 index 0000000000..3d8094fba2 --- /dev/null +++ b/libs/openzap/Makefile.am @@ -0,0 +1,272 @@ +# Copyright (c) 2007, Anthony Minessale II +# 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. + +PREFIX = $(prefix) +SRC = src + +moddir = @modinstdir@ +libdir = @libdir@ +library_includedir = $(PREFIX)/include + +INCS = -I$(OZ_SRCDIR)/$(SRC)/include -I$(OZ_SRCDIR)/$(SRC)/isdn/include +if HAVE_SCTP +INCS += -I$(OZ_SRCDIR)/$(SRC)/ozmod/ozmod_sangoma_boost +endif +MY_CFLAGS = $(INCS) $(ZAP_CFLAGS) -DZAP_CONFIG_DIR=\"@confdir@\" -DZAP_MOD_DIR=\"$(moddir)\" @COMP_VENDOR_CFLAGS@ @DEFS@ +COMPILE = $(CC) $(MY_CFLAGS) $(INCS) +LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(COMPILE) +LINK = $(LIBTOOL) --mode=link --tag=CC $(CC) $(MY_CFLAGS) $(LDFLAGS) -o $@ + + +# +# GNU pkgconfig file +# +EXTRA_DIST = openzap.pc.in + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = openzap.pc + + +# +# libopenzap +# +libopenzap_la_SOURCES = \ +$(SRC)/hashtable.c \ +$(SRC)/hashtable_itr.c \ +$(SRC)/zap_io.c \ +$(SRC)/zap_config.c \ +$(SRC)/zap_callerid.c \ +$(SRC)/fsk.c \ +$(SRC)/uart.c \ +$(SRC)/g711.c \ +$(SRC)/libteletone_detect.c \ +$(SRC)/libteletone_generate.c \ +$(SRC)/zap_buffer.c \ +$(SRC)/zap_threadmutex.c \ +$(SRC)/zap_dso.c \ +$(SRC)/zap_cpu_monitor.c + +library_include_HEADERS = \ +$(SRC)/include/fsk.h \ +$(SRC)/include/g711.h \ +$(SRC)/include/hashtable.h \ +$(SRC)/include/hashtable_itr.h \ +$(SRC)/include/hashtable_private.h \ +$(SRC)/include/libteletone_detect.h \ +$(SRC)/include/libteletone_generate.h \ +$(SRC)/include/libteletone.h \ +$(SRC)/include/openzap.h \ +$(SRC)/include/sangoma_tdm_api.h \ +$(SRC)/include/uart.h \ +$(SRC)/include/zap_buffer.h \ +$(SRC)/include/zap_config.h \ +$(SRC)/include/zap_threadmutex.h \ +$(SRC)/include/zap_dso.h \ +$(SRC)/include/zap_types.h \ +$(SRC)/include/zap_cpu_monitor.h + +lib_LTLIBRARIES = libopenzap.la +libopenzap_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +libopenzap_la_LDFLAGS = -version-info 1:0:0 $(AM_LDFLAGS) +libopenzap_la_LIBADD = $(LIBS) + +MYLIB = libopenzap.la + +core: libopenzap.la +core-install: install-libLTLIBRARIES + +# +# tools & test programs +# +noinst_PROGRAMS = testtones detect_tones detect_dtmf testisdn testpri testr2 testanalog testapp testcid +if HAVE_SCTP +noinst_PROGRAMS += testboost +endif + +testapp_SOURCES = $(SRC)/testapp.c +testapp_LDADD = libopenzap.la +testapp_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testcid_SOURCES = $(SRC)/testcid.c +testcid_LDADD = libopenzap.la +testcid_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testtones_SOURCES = $(SRC)/testtones.c +testtones_LDADD = libopenzap.la +testtones_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +detect_tones_SOURCES = $(SRC)/detect_tones.c +detect_tones_LDADD = libopenzap.la +detect_tones_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +detect_dtmf_SOURCES = $(SRC)/detect_dtmf.c +detect_dtmf_LDADD = libopenzap.la +detect_dtmf_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testisdn_SOURCES = $(SRC)/testisdn.c +testisdn_LDADD = libopenzap.la +testisdn_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testpri_SOURCES = $(SRC)/testpri.c +testpri_LDADD = libopenzap.la +testpri_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +testr2_SOURCES = $(SRC)/testr2.c +testr2_LDADD = libopenzap.la +testr2_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +if HAVE_SCTP +testboost_SOURCES = $(SRC)/testboost.c +testboost_LDADD = libopenzap.la +testboost_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +endif + +testanalog_SOURCES = $(SRC)/testanalog.c +testanalog_LDADD = libopenzap.la +testanalog_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) + +# +# ozmod modules +# +mod_LTLIBRARIES = ozmod_zt.la ozmod_skel.la ozmod_isdn.la ozmod_analog.la ozmod_analog_em.la + + +if HAVE_SCTP +mod_LTLIBRARIES += ozmod_sangoma_boost.la +endif + +if LIBSANGOMA +mod_LTLIBRARIES += ozmod_wanpipe.la +endif + +if LIBPRI +mod_LTLIBRARIES += ozmod_libpri.la +endif + +if OPENR2 +mod_LTLIBRARIES += ozmod_r2.la +endif + +ozmod_zt_la_SOURCES = $(SRC)/ozmod/ozmod_zt/ozmod_zt.c +ozmod_zt_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ozmod_zt_la_LDFLAGS = -module -avoid-version +ozmod_zt_la_LIBADD = $(MYLIB) + +ozmod_skel_la_SOURCES = $(SRC)/ozmod/ozmod_skel/ozmod_skel.c +ozmod_skel_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ozmod_skel_la_LDFLAGS = -module -avoid-version +ozmod_skel_la_LIBADD = $(MYLIB) + +if LIBSANGOMA +ozmod_wanpipe_la_SOURCES = $(SRC)/ozmod/ozmod_wanpipe/ozmod_wanpipe.c +ozmod_wanpipe_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) -D__LINUX__ -I/usr/include/wanpipe +ozmod_wanpipe_la_LDFLAGS = -module -avoid-version -lsangoma +ozmod_wanpipe_la_LIBADD = $(MYLIB) +endif + +ozmod_isdn_la_SOURCES = \ +$(SRC)/isdn/EuroISDNStateNT.c \ +$(SRC)/isdn/EuroISDNStateTE.c \ +$(SRC)/isdn/mfifo.c \ +$(SRC)/isdn/Q921.c \ +$(SRC)/isdn/Q931api.c \ +$(SRC)/isdn/Q931.c \ +$(SRC)/isdn/Q931ie.c \ +$(SRC)/isdn/Q931mes.c \ +$(SRC)/isdn/Q931StateNT.c \ +$(SRC)/isdn/Q931StateTE.c \ +$(SRC)/isdn/nationalmes.c \ +$(SRC)/isdn/nationalStateNT.c \ +$(SRC)/isdn/nationalStateTE.c \ +$(SRC)/isdn/DMSmes.c \ +$(SRC)/isdn/DMSStateNT.c \ +$(SRC)/isdn/DMSStateTE.c \ +$(SRC)/isdn/5ESSmes.c \ +$(SRC)/isdn/5ESSStateNT.c \ +$(SRC)/isdn/5ESSStateTE.c \ +$(SRC)/isdn/Q932mes.c \ +$(SRC)/ozmod/ozmod_isdn/ozmod_isdn.c + +ozmod_isdn_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) -D_GNU_SOURCE +ozmod_isdn_la_LDFLAGS = $(PCAP_LIB_FLAGS) -module -avoid-version +ozmod_isdn_la_LIBADD = $(MYLIB) + +ozmod_analog_la_SOURCES = $(SRC)/ozmod/ozmod_analog/ozmod_analog.c +ozmod_analog_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ozmod_analog_la_LDFLAGS = -module -avoid-version +ozmod_analog_la_LIBADD = $(MYLIB) + +ozmod_analog_em_la_SOURCES = $(SRC)/ozmod/ozmod_analog_em/ozmod_analog_em.c +ozmod_analog_em_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ozmod_analog_em_la_LDFLAGS = -module -avoid-version +ozmod_analog_em_la_LIBADD = $(MYLIB) + +if HAVE_SCTP +ozmod_sangoma_boost_la_SOURCES = $(SRC)/ozmod/ozmod_sangoma_boost/sangoma_boost_client.c $(SRC)/ozmod/ozmod_sangoma_boost/ozmod_sangoma_boost.c +ozmod_sangoma_boost_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ozmod_sangoma_boost_la_LDFLAGS = -module -avoid-version +ozmod_sangoma_boost_la_LIBADD = $(MYLIB) +endif + +if LIBPRI +ozmod_libpri_la_SOURCES = $(SRC)/ozmod/ozmod_libpri/ozmod_libpri.c $(SRC)/ozmod/ozmod_libpri/lpwrap_pri.c +ozmod_libpri_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ozmod_libpri_la_LDFLAGS = -module -avoid-version -lpri +ozmod_libpri_la_LIBADD = $(MYLIB) +endif + +if OPENR2 +ozmod_r2_la_SOURCES = $(SRC)/ozmod/ozmod_r2/ozmod_r2.c +ozmod_r2_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS) +ozmod_r2_la_LDFLAGS = -module -avoid-version -lopenr2 +ozmod_r2_la_LIBADD = $(MYLIB) +endif + + +dox doxygen: + cd docs && doxygen $(OZ_SRCDIR)/docs/Doxygen.conf + +mod_openzap/mod_openzap.$(DYNAMIC_LIB_EXTEN): $(MYLIB) mod_openzap/mod_openzap.c + cd mod_openzap && make + +mod_openzap: mod_openzap/mod_openzap.$(DYNAMIC_LIB_EXTEN) + +mod_openzap-install: mod_openzap + cd mod_openzap && make install + +mod_openzap-clean: + @if [ -f mod_openzap/mod_openzap.$(DYNAMIC_LIB_EXTEN) ] ; then cd mod_openzap && make clean ; fi + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(PREFIX) + $(mkinstalldirs) $(DESTDIR)@confdir@ + @[ -f "$(DESTDIR)@confdir@/openzap.conf" ] || ( cp conf/*.conf $(DESTDIR)@confdir@) + @echo OpenZAP Installed diff --git a/libs/openzap/NEWS b/libs/openzap/NEWS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/openzap/README b/libs/openzap/README new file mode 100644 index 0000000000..4ef720b10a --- /dev/null +++ b/libs/openzap/README @@ -0,0 +1,3 @@ +OPENZAP (WORK IN PROGRESS) + +*shrug* diff --git a/libs/openzap/acinclude.m4 b/libs/openzap/acinclude.m4 new file mode 100644 index 0000000000..908ddba855 --- /dev/null +++ b/libs/openzap/acinclude.m4 @@ -0,0 +1 @@ +m4_include([build/libpcap.m4]) diff --git a/libs/openzap/bootstrap b/libs/openzap/bootstrap new file mode 100755 index 0000000000..8ca4856832 --- /dev/null +++ b/libs/openzap/bootstrap @@ -0,0 +1,6 @@ +#!/bin/bash +autoheader +libtoolize --force --copy +aclocal +automake -f --copy --add-missing +autoconf diff --git a/libs/openzap/build/libpcap.m4 b/libs/openzap/build/libpcap.m4 new file mode 100644 index 0000000000..c20d345ca6 --- /dev/null +++ b/libs/openzap/build/libpcap.m4 @@ -0,0 +1,144 @@ +dnl libpcap.m4--PCAP libraries and includes +dnl Derrick Brashear +dnl from KTH krb and Arla +dnl $Id: libpcap.m4,v 1.4 2006/01/20 20:21:09 snsimon Exp $ + +AC_DEFUN([PCAP_INC_WHERE1], [ +ac_cv_found_pcap_inc=no +if test -f "$1/pcap.h" ; then + ac_cv_found_pcap_inc=yes +fi +]) + +AC_DEFUN([PCAP_INC_WHERE], [ + for i in $1; do + AC_MSG_CHECKING(for pcap header in $i) + PCAP_INC_WHERE1($i) + if test "$ac_cv_found_pcap_inc" = "yes"; then + ac_cv_pcap_where_inc=$i + AC_MSG_RESULT(found) + break + else + AC_MSG_RESULT(no found) + fi + done +]) + +AC_DEFUN([PCAP_LIB_WHERE1], [ +saved_LIBS=$LIBS +LIBS="$saved_LIBS -L$1 -lpcap" +AC_TRY_LINK(, +[pcap_lookupdev("");], +[ac_cv_found_pcap_lib=yes], +ac_cv_found_pcap_lib=no) +LIBS=$saved_LIBS +]) + +AC_DEFUN([TEST_LIBPATH], [ +changequote(<<, >>) +define(<>, translit(ac_cv_found_$2_lib, <<- *>>, <<__p>>)) +changequote([, ]) +if test "$AC_CV_FOUND" = "yes"; then + if test \! -r "$1/lib$2.a" -a \! -r "$1/lib$2.so" -a \! -r "$1/lib$2.sl" -a \! -r "$1/lib$2.dylib"; then + AC_CV_FOUND=no + fi +fi +]) + + +AC_DEFUN([PCAP_LIB_WHERE], [ + for i in $1; do + AC_MSG_CHECKING(for pcap library in $i) + PCAP_LIB_WHERE1($i) + TEST_LIBPATH($i, pcap) + if test "$ac_cv_found_pcap_lib" = "yes" ; then + ac_cv_pcap_where_lib=$i + AC_MSG_RESULT(found) + break + else + AC_MSG_RESULT(no found) + fi + done +]) + +AC_DEFUN([FIND_LIB_SUBDIR], +[dnl +AC_ARG_WITH([lib-subdir], AC_HELP_STRING([--with-lib-subdir=DIR],[Find libraries in DIR instead of lib])) +AC_CHECK_SIZEOF(long) +AC_CACHE_CHECK([what directory libraries are found in], [ac_cv_cmu_lib_subdir], +[test "X$with_lib_subdir" = "Xyes" && with_lib_subdir= +test "X$with_lib_subdir" = "Xno" && with_lib_subdir= + if test "X$with_lib_subdir" = "X" ; then + ac_cv_cmu_lib_subdir=lib + if test $ac_cv_sizeof_long -eq 4 ; then + test -d /usr/lib32 && ac_cv_cmu_lib_subdir=lib32 + test -r /usr/lib/libpcap.so && ac_cv_cmu_lib_subdir=lib + fi + if test $ac_cv_sizeof_long -eq 8 ; then + test -d /usr/lib64 && ac_cv_cmu_lib_subdir=lib64 + fi + else + ac_cv_cmu_lib_subdir=$with_lib_subdir + fi]) + AC_SUBST(LIB_SUBDIR, $ac_cv_cmu_lib_subdir) + ]) + + +AC_DEFUN([AX_LIB_PCAP], [ +AC_REQUIRE([FIND_LIB_SUBDIR]) +AC_ARG_WITH(pcap, + [ --with-pcap=PREFIX Compile with PCAP support], + [if test "X$with_pcap" = "X"; then + with_pcap=yes + fi]) +AC_ARG_WITH(pcap-lib, + [ --with-pcap-lib=dir use pcap libraries in dir], + [if test "$withval" = "yes" -o "$withval" = "no"; then + AC_MSG_ERROR([No argument for --with-pcap-lib]) + fi]) +AC_ARG_WITH(pcap-include, + [ --with-pcap-include=dir use pcap headers in dir], + [if test "$withval" = "yes" -o "$withval" = "no"; then + AC_MSG_ERROR([No argument for --with-pcap-include]) + fi]) + + if test "X$with_pcap" != "X"; then + if test "$with_pcap" != "yes"; then + ac_cv_pcap_where_lib=$with_pcap + ac_cv_pcap_where_inc=$with_pcap/include + fi + fi + + if test "X$with_pcap_lib" != "X"; then + ac_cv_pcap_where_lib=$with_pcap_lib + fi + if test "X$ac_cv_pcap_where_lib" = "X"; then + PCAP_LIB_WHERE(/usr/$LIB_SUBDIR /usr/local/$LIB_SUBDIR) + fi + + if test "X$with_pcap_include" != "X"; then + ac_cv_pcap_where_inc=$with_pcap_include + fi + if test "X$ac_cv_pcap_where_inc" = "X"; then + PCAP_INC_WHERE(/usr/ng/include /usr/include /usr/local/include) + fi + + AC_MSG_CHECKING(whether to include pcap) + if test "X$ac_cv_pcap_where_lib" != "X" -a "X$ac_cv_pcap_where_inc" != "X"; then + ac_cv_found_pcap=yes + AC_MSG_RESULT(yes) + PCAP_INC_DIR=$ac_cv_pcap_where_inc + PCAP_LIB_DIR=$ac_cv_pcap_where_lib + PCAP_INC_FLAGS="-I${PCAP_INC_DIR}" + PCAP_LIB_FLAGS="-L${PCAP_LIB_DIR} -lpcap" + AC_SUBST(PCAP_INC_DIR) + AC_SUBST(PCAP_LIB_DIR) + AC_SUBST(PCAP_INC_FLAGS) + AC_SUBST(PCAP_LIB_FLAGS) + AC_DEFINE([HAVE_LIBPCAP],[1],[libpcap]) + else + ac_cv_found_pcap=no + AC_MSG_RESULT(no) + fi + ]) + diff --git a/libs/openzap/conf/m3ua.conf b/libs/openzap/conf/m3ua.conf new file mode 100644 index 0000000000..e3eeed3a4a --- /dev/null +++ b/libs/openzap/conf/m3ua.conf @@ -0,0 +1,38 @@ +;M3UA SS7 Links Config +; +;ss7box-m3ua_mode => true +;local_sctp_ip => localhost +;local sctp_port => 30000 +;remote_sctp_ip => localhost +;remote_sctp_port => 30001 +;opc => 0-0-0 +;dpc => 0-0-0 + + +; AP Specific Stuff. This will likely move later. + +; CNAM Gateways +cnam1_dpc => 0-0-0 +cnam1_ssn => 253 +cnam2_dpc => 0-0-0 +cnam2_ssn => 253 +cnam3_dpc => 0-0-0 +cnam3_ssn => 253 + +;LNP Gateways +lnp1_dpc => 0-0-0 +lnp1_ssn => 253 +lnp2_dpc => 0-0-0 +lnp2_ssn => 253 +lnp3_dpc => 0-0-0 +lnp3_ssn => 253 + +;LNP Gateways +sms8001_dpc => 0-0-0 +sms8001_ssn => 253 +sms8002_dpc => 0-0-0 +sms8002_ssn => 253 +sms8003_dpc => 0-0-0 +sms8003_ssn => 253 + + diff --git a/libs/openzap/conf/openzap.conf b/libs/openzap/conf/openzap.conf new file mode 100644 index 0000000000..d8a2f4b792 --- /dev/null +++ b/libs/openzap/conf/openzap.conf @@ -0,0 +1,19 @@ +[span wanpipe] +name => OpenZAP +number => 1 +fxs-channel => 1:3-4 + +[span wanpipe] +fxo-channel => 1:1-2 + +[span zt] +name => OpenZAP +number => 2 +fxs-channel => 1 + +[span zt] +name => OpenZAP +number => 2 +fxo-channel => 3 + + diff --git a/libs/openzap/conf/openzap.conf.xml b/libs/openzap/conf/openzap.conf.xml new file mode 100644 index 0000000000..bfa2bc0391 --- /dev/null +++ b/libs/openzap/conf/openzap.conf.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/conf/pika.conf b/libs/openzap/conf/pika.conf new file mode 100644 index 0000000000..8cc8d9c11d --- /dev/null +++ b/libs/openzap/conf/pika.conf @@ -0,0 +1,41 @@ +; each category is a config profile +; to apply the profile append it to a channel def in +; openzap.conf with @ +; e.g. +; [span pika] +; name => pika +; number => pika +; fxs-channel => 1:0:1-12@default + +[default] +; region is na or eu +;region => na +;rx-gain => 0.00 +;rx-agc-enabled => false +;rx-agc-targetPower => -15.00 +;rx-agc-minGain => -6.00 +;rx-agc-maxGain => 18.00 +;rx-agc-attackRate => 170 +;rx-agc-decayRate => 750 +;rx-agc-speechThreshold => -36.00 +;rx-vad-enabled => false +;rx-vad-activationThreshold => -40.00 +;rx-vad-activationDebounceTime => 72 +;rx-vad-deactivationThreshold => -40.00 +;rx-vad-deactivationDebounceTime => 984 +;rx-vad-preSpeechBufferSize => 240 +;tx-gain => 0.00 +;tx-agc-enabled => true +;tx-agc-targetPower => -15.00 +;tx-agc-minGain => -6.00 +;tx-agc-maxGain => 18.00 +;tx-agc-attackRate => 170 +;tx-agc-decayRate => 750 +;tx-agc-speechThreshold => -36.00 +;ec-enabled => false +;ec-doubleTalkerThreshold => -6.00 +;ec-speechPresentThreshold => -40.00 +;ec-echoSuppressionThreshold => -18.00 +;ec-echoSuppressionEnabled => true +;ec-comfortNoiseEnabled => true +;ec-adaptationModeEnabled => true diff --git a/libs/openzap/conf/tones.conf b/libs/openzap/conf/tones.conf new file mode 100644 index 0000000000..36db1d4d91 --- /dev/null +++ b/libs/openzap/conf/tones.conf @@ -0,0 +1,63 @@ +[us] +generate-dial => v=-7;%(1000,0,350,440) +detect-dial => 350,440 + +generate-ring => v=-7;%(2000,4000,440,480) +detect-ring => 440,480 + +generate-busy => v=-7;%(500,500,480,620) +detect-busy => 480,620 + +generate-attn => v=0;%(100,100,1400,2060,2450,2600) +detect-attn => 1400,2060,2450,2600 + +generate-callwaiting-sas => v=0;%(300,0,440) +detect-callwaiting-sas => 440 + +generate-callwaiting-cas => v=0;%(80,0,2750,2130) +detect-callwaiting-cas => 2750,2130 + +detect-fail1 => 913.8 +detect-fail2 => 1370.6 +detect-fail3 => 1776.7 + + +[sg] +generate-dial => v=-7;%(1000,0,425) +detect-dial => 425 + +generate-ring => v=-7;%(2000,4000,425) +detect-ring => 425 + +generate-busy => v=-7;%(750,750,425) +detect-busy => 425 + +generate-attn => v=0;%(100,100,1400,2060,2450,2600) +detect-attn => 1400,2060,2450,2600 + +generate-callwaiting-sas => v=0;%(300,0,440) +detect-callwaiting-sas => 440 + +generate-callwaiting-cas => v=0;%(80,0,2750,2130) +detect-callwaiting-cas => 2750,2130 + +detect-fail1 => 913.8 +detect-fail2 => 1370.6 +detect-fail3 => 1776.7 + +[ru] +generate-dial => v=-7;%(1000,425) +detect-dial => 0 +generate-ring => v=-7;%(800,5000,425,0) +detect-ring => 425,0 +generate-busy => v=-7;%(350,350,425,0) +detect-busy => 425,0 +generate-attn => v=0;%(100,100,1400,2060,2450,2600) +detect-attn => 1400,2060,2450,2600 +generate-callwaiting-sas => v=0;%(300,0,440) +detect-callwaiting-sas => 440,480 +generate-callwaiting-cas => v=0;%(80,0,2750,2130) +detect-callwaiting-cas => 2750,2130 +detect-fail1 => 913.8 +detect-fail2 => 1370.6 +detect-fail3 => 1776.7 diff --git a/libs/openzap/conf/wanpipe.conf b/libs/openzap/conf/wanpipe.conf new file mode 100644 index 0000000000..ba609ac42e --- /dev/null +++ b/libs/openzap/conf/wanpipe.conf @@ -0,0 +1,4 @@ +[defaults] +codec_ms => 20 +wink_ms => 150 +flash_ms => 750 diff --git a/libs/openzap/conf/zt.conf b/libs/openzap/conf/zt.conf new file mode 100644 index 0000000000..a060a40315 --- /dev/null +++ b/libs/openzap/conf/zt.conf @@ -0,0 +1,8 @@ +[defaults] +codec_ms => 20 +wink_ms => 150 +flash_ms => 750 +echo_cancel_level => 64 +rxgain => 0.0 +txgain => 0.0 + diff --git a/libs/openzap/configure.ac b/libs/openzap/configure.ac new file mode 100644 index 0000000000..f1bba93f1e --- /dev/null +++ b/libs/openzap/configure.ac @@ -0,0 +1,181 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([openzap],[pre-alpha],[bugs@freeswitch.org]) +AC_CONFIG_SRCDIR([src/zap_io.c]) + +AC_CONFIG_AUX_DIR(build) +AM_INIT_AUTOMAKE(libopenzap,0.1) + +# Checks for programs. +AC_PROG_CC +AC_PROG_MAKE_SET +AM_PROG_CC_C_O + +AC_PREFIX_DEFAULT(/usr/local/openzap) +# AC_PREFIX_DEFAULT does not get expanded until too late so we need to do this to use prefix in this script +if test "x$prefix" = "xNONE" ; then + prefix='/usr/local/openzap' +fi + +# Absolute source/build directory +OZ_SRCDIR=`(cd $srcdir && pwd)` +oz_builddir=`pwd` +AC_SUBST(OZ_SRCDIR) +AC_SUBST(oz_builddir) + +if test "$sysconfdir" = "\${prefix}/etc" ; then + confdir="$prefix/conf" +else + confdir="$sysconfdir" +fi + +AC_SUBST(confdir) + +#override some default libtool behavior and invoke AC_PROG_LIBTOOL (see http://lists.gnu.org/archive/html/libtool/2007-03/msg00000.html) +m4_defun([_LT_AC_LANG_F77_CONFIG], [:]) +m4_defun([_LT_AC_LANG_GCJ_CONFIG], [:]) +m4_defun([_LT_AC_LANG_RC_CONFIG], [:]) +#AM_PROG_CC_C_O +AC_PROG_LIBTOOL +AC_PROG_INSTALL + +# Check for com;iler type +AC_DEFUN([AX_COMPILER_VENDOR], +[ +AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, + [ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown + # note: don't check for GCC first, since some other compilers define __GNUC__ + for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do + vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ +#if !($vencpp) + thisisanerror; +#endif +])], [ax_cv_]_AC_LANG_ABBREV[_compiler_vendor=`echo $ventest | cut -d: -f1`; break]) + done + ]) +]) +AC_ARG_ENABLE([enable_64], [AS_HELP_STRING([--enable-64], [Enable 64bit compilation])], [enable_64="$enableval"], [enable_64="no"]) + +AX_COMPILER_VENDOR + +case "${ax_cv_c_compiler_vendor}" in +gnu) + COMP_VENDOR_CFLAGS="-ffast-math -Wall -Werror -Wunused-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -O0" + ;; +sun) + COMP_VENDOR_CFLAGS="-xc99=all -mt -xCC -D__FUNCTION__=__func__ -xvpara" + if test "$enable_64" = "yes" ; then + COMP_VENDOR_CFLAGS="-m64 $COMP_VENDOR_CFLAGS" + fi + ;; +*) + COMP_VENDOR_CFLAGS="-std=c99 -Wall -Wunused-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes" + ;; +esac + + +#set SOLINK variable based on compiler and host +if test "x${ax_cv_c_compiler_vendor}" = "xsun" ; then + SOLINK="-Bdynamic -dy -G" +elif test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then + case "$host" in + *darwin*) + SOLINK="-dynamic -bundle -force-flat-namespace" + ;; + *-solaris2*) + SOLINK="-shared -Xlinker" + ;; + *) + SOLINK="-shared -Xlinker -x" + ;; + esac +else + AC_ERROR([Please update configure.in with SOLINK values for your compiler]) +fi + +# set DYNAMIC_LIB_EXTEN +# we should really be using libtool so we don't need to do this +case "$host" in + *cygwin* | *mingw*) + DYNAMIC_LIB_EXTEN="dll" + ;; + *) + DYNAMIC_LIB_EXTEN="so" + ;; +esac + +AC_SUBST(SOLINK) +AC_SUBST(DYNAMIC_LIB_EXTEN) + +AC_CHECK_LIB([dl], [dlopen]) +AC_CHECK_LIB([pthread], [pthread_create]) +AC_CHECK_LIB([m], [cos]) +AX_LIB_PCAP + +AC_CHECK_HEADERS([netinet/sctp.h netdb.h sys/select.h]) +AM_CONDITIONAL([HAVE_SCTP],[test "${ac_cv_header_netinet_sctp_h}" = "yes"]) + +AC_CHECK_FUNC([gethostbyname_r], + [], [AC_CHECK_LIB([nsl], [gethostbyname_r])] +) +if test "$ac_cv_func_gethostbyname_r" = "yes" -o "$ac_cv_lib_nsl_gethostbyname_r" = "yes" ; then + +AC_MSG_CHECKING([whether gethostbyname_r requires five arguments]) + +ac_cv_func_gethostbyname_r_five_args="no" + +AC_TRY_COMPILE([#include ], + [char *name; + struct hostent *he, *res; + char buffer[2048]; + int buflen = 2048; + (void)gethostbyname_r(name, he, buffer, buflen, &res)], + [ac_cv_func_gethostbyname_r_five_args="yes" + AC_DEFINE([HAVE_GETHOSTBYNAME_R_FIVE], [1], [gethostbyname_r has five arguments])] +) + + AC_MSG_RESULT([$ac_cv_func_gethostbyname_r_five_args]) + AC_DEFINE([HAVE_GETHOSTBYNAME_R],[1],[threadsafe gethostbyname]) +fi + +# Enable debugging +AC_ARG_ENABLE(debug, +[AC_HELP_STRING([--enable-debug],[build with debug information])],[enable_debug="$enableval"],[enable_debug="yes"]) + +if test "${enable_debug}" = "yes"; then + AC_DEFINE([DEBUG],[],[Enable extra debugging.]) + + if test "x${ax_cv_c_compiler_vendor}" = "xgnu" ; then + COMP_VENDOR_CFLAGS="$COMP_VENDOR_CFLAGS -g -ggdb" + fi +fi + +# Where to install the modules +AC_ARG_WITH([modinstdir], + [AS_HELP_STRING([--with-modinstdir=DIR], [Install modules into this location (default: $prefix/mod)])], [modinstdir="$withval"], [modinstdir="${prefix}/mod"]) + +AC_SUBST(modinstdir) + + +# libpri? +AC_ARG_WITH([libpri], + [AS_HELP_STRING([--with-libpri], [Install ozmod_libpri])], [enable_libpri="yes"], [enable_libpri="no"]) +AC_SUBST(enable_libpri) + +AC_CHECK_LIB([sangoma], [sangoma_span_chan_toif], [have_libsangoma="yes"]) +AM_CONDITIONAL([LIBSANGOMA],[test "${have_libsangoma}" = "yes"]) + +AM_CONDITIONAL([LIBPRI],[test "${enable_libpri}" = "yes"]) + +AC_CHECK_LIB([openr2], [openr2_context_set_io_type], [have_openr2="yes"]) +AM_CONDITIONAL([OPENR2],[test "${have_openr2}" = "yes"]) + +COMP_VENDOR_CFLAGS="$COMP_VENDOR_CFLAGS" +AC_SUBST(COMP_VENDOR_CFLAGS) +AC_CONFIG_FILES([Makefile + openzap.pc + mod_openzap/Makefile]) +AC_OUTPUT diff --git a/libs/openzap/configure.gnu b/libs/openzap/configure.gnu new file mode 100644 index 0000000000..002d5eab53 --- /dev/null +++ b/libs/openzap/configure.gnu @@ -0,0 +1,4 @@ +#! /bin/sh +srcpath=$(dirname $0 2>/dev/null ) || srcpath="." +$srcpath/configure "$@" --with-pic + diff --git a/libs/openzap/docs/Doxygen.conf b/libs/openzap/docs/Doxygen.conf new file mode 100644 index 0000000000..c4e47076fa --- /dev/null +++ b/libs/openzap/docs/Doxygen.conf @@ -0,0 +1,277 @@ +# Doxyfile 1.4.6 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = OpenZAP +PROJECT_NUMBER = +OUTPUT_DIRECTORY = . +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = YES +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +IGNORE_PREFIX = zap_ ZAP_ Q921 Q931 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ../src ../src/include \ + ../src/isdn ../src/isdn/include \ + ../mod_openzap ../ \ + ../src/ozmod \ + ../src/ozmod/ozmod_analog \ + ../src/ozmod/ozmod_analog_em \ + ../src/ozmod/ozmod_isdn \ + ../src/ozmod/ozmod_pika \ + ../src/ozmod/ozmod_skel \ + ../src/ozmod/ozmod_ss7_boost \ + ../src/ozmod/ozmod_wanpipe \ + ../src/ozmod/ozmod_zt + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = YES +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +USE_HTAGS = YES +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 1 +IGNORE_PREFIX = zap_ +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = freeswitch.chm +HHC_LOCATION = +GENERATE_CHI = YES +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = YES +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = NO +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = *.h +PREDEFINED = ZAP_DECLARE(x)=x \ + APR_DECLARE(x)=x \ + ZAP_MOD_DECLARE(x)=x \ + DoxyDefine(x)=x + +EXPAND_AS_DEFINED = NO +SKIP_FUNCTION_MACROS = NO +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = jpg +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/libs/openzap/mod_openzap/Makefile.in b/libs/openzap/mod_openzap/Makefile.in new file mode 100644 index 0000000000..85275d1d8d --- /dev/null +++ b/libs/openzap/mod_openzap/Makefile.in @@ -0,0 +1,23 @@ +OZ_CFLAGS=@CFLAGS@ @COMP_VENDOR_CFLAGS@ @DEFS@ + +BASE=../../.. +OZ_DIR=.. +VERBOSE=1 +OZLA=$(OZ_DIR)/libopenzap.la +LOCAL_CFLAGS=-I$(OZ_DIR)/src/include -I$(OZ_DIR)/src/isdn/include $(OZ_CFLAGS) +LOCAL_LDFLAGS=-L$(OZ_DIR) -lopenzap +include $(BASE)/build/modmake.rules + +local_depend: $(OZLA) + +$(OZLA): $(OZ_DIR)/.update + cd $(OZ_DIR) && $(MAKE) + +local_install: + cd $(OZ_DIR) && $(MAKE) install + [ -f $(DESTDIR)@confdir@/autoload_configs/openzap.conf.xml ] || cp -f $(OZ_DIR)/conf/openzap.conf.xml $(DESTDIR)@confdir@/autoload_configs + +local_clean: + cd $(OZ_DIR) && $(MAKE) clean + + diff --git a/libs/openzap/mod_openzap/mod_openzap.2005.vcproj b/libs/openzap/mod_openzap/mod_openzap.2005.vcproj new file mode 100644 index 0000000000..959115d3d8 --- /dev/null +++ b/libs/openzap/mod_openzap/mod_openzap.2005.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/mod_openzap/mod_openzap.2008.vcproj b/libs/openzap/mod_openzap/mod_openzap.2008.vcproj new file mode 100644 index 0000000000..bf5be95c21 --- /dev/null +++ b/libs/openzap/mod_openzap/mod_openzap.2008.vcproj @@ -0,0 +1,369 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/mod_openzap/mod_openzap.c b/libs/openzap/mod_openzap/mod_openzap.c new file mode 100644 index 0000000000..563972f5a4 --- /dev/null +++ b/libs/openzap/mod_openzap/mod_openzap.c @@ -0,0 +1,3221 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Moises Silva + * + * + * mod_openzap.c -- OPENZAP Endpoint Module + * + */ +#include +#include "openzap.h" + +#ifndef __FUNCTION__ +#define __FUNCTION__ __SWITCH_FUNC__ +#endif + +#define OPENZAP_VAR_PREFIX "openzap_" +#define OPENZAP_VAR_PREFIX_LEN 8 + +SWITCH_MODULE_LOAD_FUNCTION(mod_openzap_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_openzap_shutdown); +SWITCH_MODULE_DEFINITION(mod_openzap, mod_openzap_load, mod_openzap_shutdown, NULL); + +switch_endpoint_interface_t *openzap_endpoint_interface; + +static switch_memory_pool_t *module_pool = NULL; + +typedef enum { + ANALOG_OPTION_NONE = 0, + ANALOG_OPTION_3WAY = (1 << 0), + ANALOG_OPTION_CALL_SWAP = (1 << 1) +} analog_option_t; + +struct span_config { + zap_span_t *span; + char dialplan[80]; + char context[80]; + char dial_regex[256]; + char fail_dial_regex[256]; + char hold_music[256]; + char type[256]; + analog_option_t analog_options; +}; + +static struct span_config SPAN_CONFIG[ZAP_MAX_SPANS_INTERFACE] = {{0}}; + +typedef enum { + TFLAG_IO = (1 << 0), + TFLAG_DTMF = (1 << 1), + TFLAG_CODEC = (1 << 2), + TFLAG_BREAK = (1 << 3), + TFLAG_HOLD = (1 << 4), + TFLAG_DEAD = (1 << 5) +} TFLAGS; + +static struct { + int debug; + char *dialplan; + char *codec_string; + char *codec_order[SWITCH_MAX_CODECS]; + int codec_order_last; + char *codec_rates_string; + char *codec_rates[SWITCH_MAX_CODECS]; + int codec_rates_last; + unsigned int flags; + int fd; + int calls; + char hold_music[256]; + switch_mutex_t *mutex; + analog_option_t analog_options; +} globals; + +struct private_object { + unsigned int flags; + switch_codec_t read_codec; + switch_codec_t write_codec; + switch_frame_t read_frame; + unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_frame_t cng_frame; + unsigned char cng_databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_core_session_t *session; + switch_caller_profile_t *caller_profile; + unsigned int codec; + unsigned int codecs; + unsigned short samprate; + switch_mutex_t *mutex; + switch_mutex_t *flag_mutex; + zap_channel_t *zchan; + uint32_t wr_error; +}; + +typedef struct private_object private_t; + + +static switch_status_t channel_on_init(switch_core_session_t *session); +static switch_status_t channel_on_hangup(switch_core_session_t *session); +static switch_status_t channel_on_destroy(switch_core_session_t *session); +static switch_status_t channel_on_routing(switch_core_session_t *session); +static switch_status_t channel_on_exchange_media(switch_core_session_t *session); +static switch_status_t channel_on_soft_execute(switch_core_session_t *session); +static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool, + switch_originate_flag_t flags, switch_call_cause_t *cancel_cause); +static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id); +static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id); +static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig); +zap_status_t zap_channel_from_event(zap_sigmsg_t *sigmsg, switch_core_session_t **sp); +void dump_chan(zap_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream); +void dump_chan_xml(zap_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream); + +static void zap_set_npi(const char *npi_string, uint8_t *target) +{ + if (!strcasecmp(npi_string, "isdn") || !strcasecmp(npi_string, "e164")) { + *target = ZAP_NPI_ISDN; + } else if (!strcasecmp(npi_string, "data")) { + *target = ZAP_NPI_DATA; + } else if (!strcasecmp(npi_string, "telex")) { + *target = ZAP_NPI_TELEX; + } else if (!strcasecmp(npi_string, "national")) { + *target = ZAP_NPI_NATIONAL; + } else if (!strcasecmp(npi_string, "private")) { + *target = ZAP_NPI_PRIVATE; + } else if (!strcasecmp(npi_string, "reserved")) { + *target = ZAP_NPI_RESERVED; + } else if (!strcasecmp(npi_string, "unknown")) { + *target = ZAP_NPI_UNKNOWN; + } else { + zap_log(ZAP_LOG_WARNING, "Invalid NPI value (%s)\n", npi_string); + *target = ZAP_NPI_UNKNOWN; + } +} + +static void zap_set_ton(const char *ton_string, uint8_t *target) +{ + if (!strcasecmp(ton_string, "national")) { + *target = ZAP_TON_NATIONAL; + } else if (!strcasecmp(ton_string, "international")) { + *target = ZAP_TON_INTERNATIONAL; + } else if (!strcasecmp(ton_string, "local")) { + *target = ZAP_TON_SUBSCRIBER_NUMBER; + } else if (!strcasecmp(ton_string, "unknown")) { + *target = ZAP_TON_UNKNOWN; + } else { + zap_log(ZAP_LOG_WARNING, "Invalid TON value (%s)\n", ton_string); + *target = ZAP_TON_UNKNOWN; + } +} + +static switch_core_session_t *zap_channel_get_session(zap_channel_t *channel, int32_t id) +{ + switch_core_session_t *session = NULL; + + if (id > ZAP_MAX_TOKENS) { + return NULL; + } + + if (!zstr(channel->tokens[id])) { + if (!(session = switch_core_session_locate(channel->tokens[id]))) { + zap_channel_clear_token(channel, channel->tokens[id]); + } + } + + return session; +} + +static const char *zap_channel_get_uuid(zap_channel_t *channel, int32_t id) +{ + if (id > ZAP_MAX_TOKENS) { + return NULL; + } + + if (!zstr(channel->tokens[id])) { + return channel->tokens[id]; + } + return NULL; +} + +static void stop_hold(switch_core_session_t *session_a, const char *uuid) +{ + switch_core_session_t *session; + switch_channel_t *channel, *channel_a; + ; + + if (!uuid) { + return; + } + + if ((session = switch_core_session_locate(uuid))) { + channel = switch_core_session_get_channel(session); + + if (switch_channel_test_flag(channel, CF_HOLD)) { + channel_a = switch_core_session_get_channel(session_a); + switch_ivr_unhold(session); + switch_channel_clear_flag(channel_a, CF_SUSPEND); + switch_channel_clear_flag(channel_a, CF_HOLD); + } else { + switch_channel_stop_broadcast(channel); + switch_channel_wait_for_flag(channel, CF_BROADCAST, SWITCH_FALSE, 2000, NULL); + } + + switch_core_session_rwunlock(session); + } +} + +static void start_hold(zap_channel_t *zchan, switch_core_session_t *session_a, const char *uuid, const char *stream) +{ + switch_core_session_t *session; + switch_channel_t *channel, *channel_a; + + if (!uuid) { + return; + } + + + if ((session = switch_core_session_locate(uuid))) { + channel = switch_core_session_get_channel(session); + if (zstr(stream)) { + if (!strcasecmp(globals.hold_music, "indicate_hold")) { + stream = "indicate_hold"; + } + if (!strcasecmp(SPAN_CONFIG[zchan->span->span_id].hold_music, "indicate_hold")) { + stream = "indicate_hold"; + } + } + + if (zstr(stream)) { + stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE); + } + + if (zstr(stream)) { + stream = SPAN_CONFIG[zchan->span->span_id].hold_music; + } + + if (zstr(stream)) { + stream = globals.hold_music; + } + + + if (zstr(stream) && !(stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE))) { + stream = globals.hold_music; + } + + if (!zstr(stream)) { + if (!strcasecmp(stream, "indicate_hold")) { + channel_a = switch_core_session_get_channel(session_a); + switch_ivr_hold_uuid(uuid, NULL, 0); + switch_channel_set_flag(channel_a, CF_SUSPEND); + switch_channel_set_flag(channel_a, CF_HOLD); + } else { + switch_ivr_broadcast(switch_core_session_get_uuid(session), stream, SMF_ECHO_ALEG | SMF_LOOP); + } + } + + switch_core_session_rwunlock(session); + } +} + + +static void cycle_foreground(zap_channel_t *zchan, int flash, const char *bcast) { + uint32_t i = 0; + switch_core_session_t *session; + switch_channel_t *channel; + private_t *tech_pvt; + + + for (i = 0; i < zchan->token_count; i++) { + if ((session = zap_channel_get_session(zchan, i))) { + const char *buuid; + tech_pvt = switch_core_session_get_private(session); + channel = switch_core_session_get_channel(session); + buuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + + + if (zchan->token_count == 1 && flash) { + if (switch_test_flag(tech_pvt, TFLAG_HOLD)) { + stop_hold(session, buuid); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + } else { + start_hold(zchan, session, buuid, bcast); + switch_set_flag_locked(tech_pvt, TFLAG_HOLD); + } + } else if (i) { + start_hold(zchan, session, buuid, bcast); + switch_set_flag_locked(tech_pvt, TFLAG_HOLD); + } else { + stop_hold(session, buuid); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + if (!switch_channel_test_flag(channel, CF_ANSWERED)) { + switch_channel_mark_answered(channel); + } + } + switch_core_session_rwunlock(session); + } + } +} + + + + +static switch_status_t tech_init(private_t *tech_pvt, switch_core_session_t *session, zap_channel_t *zchan) +{ + const char *dname = NULL; + uint32_t interval = 0, srate = 8000; + zap_codec_t codec; + + tech_pvt->zchan = zchan; + tech_pvt->read_frame.data = tech_pvt->databuf; + tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf); + tech_pvt->cng_frame.data = tech_pvt->cng_databuf; + tech_pvt->cng_frame.buflen = sizeof(tech_pvt->cng_databuf); + tech_pvt->cng_frame.flags = SFF_CNG; + tech_pvt->cng_frame.codec = &tech_pvt->read_codec; + memset(tech_pvt->cng_frame.data, 255, tech_pvt->cng_frame.buflen); + switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + switch_core_session_set_private(session, tech_pvt); + tech_pvt->session = session; + + if (ZAP_SUCCESS != zap_channel_command(zchan, ZAP_COMMAND_GET_INTERVAL, &interval)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve channel interval.\n"); + return SWITCH_STATUS_GENERR; + } + + if (ZAP_SUCCESS != zap_channel_command(zchan, ZAP_COMMAND_GET_CODEC, &codec)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to retrieve channel codec.\n"); + return SWITCH_STATUS_GENERR; + } + + switch(codec) { + case ZAP_CODEC_ULAW: + { + dname = "PCMU"; + } + break; + case ZAP_CODEC_ALAW: + { + dname = "PCMA"; + } + break; + case ZAP_CODEC_SLIN: + { + dname = "L16"; + } + break; + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid codec value retrieved from channel, codec value: %d\n", codec); + return SWITCH_STATUS_GENERR; + } + } + + + if (switch_core_codec_init(&tech_pvt->read_codec, + dname, + NULL, + srate, + interval, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + return SWITCH_STATUS_GENERR; + } else { + if (switch_core_codec_init(&tech_pvt->write_codec, + dname, + NULL, + srate, + interval, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + switch_core_codec_destroy(&tech_pvt->read_codec); + return SWITCH_STATUS_GENERR; + } + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set codec %s %dms\n", dname, interval); + switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec); + switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec); + switch_set_flag_locked(tech_pvt, TFLAG_CODEC); + tech_pvt->read_frame.codec = &tech_pvt->read_codec; + switch_set_flag_locked(tech_pvt, TFLAG_IO); + + return SWITCH_STATUS_SUCCESS; + +} + +static switch_status_t channel_on_init(switch_core_session_t *session) +{ + switch_channel_t *channel; + private_t *tech_pvt = NULL; + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_SUCCESS; + } + + /* Move channel's state machine to ROUTING */ + switch_channel_set_state(channel, CS_ROUTING); + switch_mutex_lock(globals.mutex); + globals.calls++; + switch_mutex_unlock(globals.mutex); + + zap_channel_init(tech_pvt->zchan); + + //switch_channel_set_flag(channel, CF_ACCEPT_CNG); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_routing(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_execute(switch_core_session_t *session) +{ + + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel)); + + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_destroy(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + + if ((tech_pvt = switch_core_session_get_private(session))) { + + if (tech_pvt->read_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->read_codec); + } + + if (tech_pvt->write_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->write_codec); + } + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_hangup(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (!tech_pvt->zchan) { + goto end; + } + + zap_channel_clear_token(tech_pvt->zchan, switch_core_session_get_uuid(session)); + + switch (tech_pvt->zchan->type) { + case ZAP_CHAN_TYPE_FXO: + case ZAP_CHAN_TYPE_EM: + case ZAP_CHAN_TYPE_CAS: + { + zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); + } + break; + case ZAP_CHAN_TYPE_FXS: + { + if (tech_pvt->zchan->state != ZAP_CHANNEL_STATE_BUSY && tech_pvt->zchan->state != ZAP_CHANNEL_STATE_DOWN) { + if (tech_pvt->zchan->token_count) { + cycle_foreground(tech_pvt->zchan, 0, NULL); + } else { + zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } + } + break; + case ZAP_CHAN_TYPE_B: + { + if (tech_pvt->zchan->state != ZAP_CHANNEL_STATE_DOWN) { + if (tech_pvt->zchan->state != ZAP_CHANNEL_STATE_TERMINATING) { + tech_pvt->zchan->caller_data.hangup_cause = switch_channel_get_cause_q850(channel); + if (tech_pvt->zchan->caller_data.hangup_cause < 1 || tech_pvt->zchan->caller_data.hangup_cause > 127) { + tech_pvt->zchan->caller_data.hangup_cause = ZAP_CAUSE_DESTINATION_OUT_OF_ORDER; + } + } + zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } + break; + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unhandled channel type %d for channel %s\n", tech_pvt->zchan->type, + switch_channel_get_name(channel)); + } + break; + } + + end: + + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel)); + switch_mutex_lock(globals.mutex); + globals.calls--; + if (globals.calls < 0) { + globals.calls = 0; + } + switch_mutex_unlock(globals.mutex); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch (sig) { + case SWITCH_SIG_KILL: + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + break; + case SWITCH_SIG_BREAK: + switch_set_flag_locked(tech_pvt, TFLAG_BREAK); + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_exchange_media(switch_core_session_t *session) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CHANNEL EXCHANGE_MEDIA\n"); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_soft_execute(switch_core_session_t *session) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CHANNEL SOFT_EXECUTE\n"); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) +{ + private_t *tech_pvt = NULL; + char tmp[2] = ""; + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + tmp[0] = dtmf->digit; + zap_channel_command(tech_pvt->zchan, ZAP_COMMAND_SEND_DTMF, tmp); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + zap_size_t len; + zap_wait_flag_t wflags = ZAP_READ; + char dtmf[128] = ""; + zap_status_t status; + int total_to; + int chunk, do_break = 0; + + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + return SWITCH_STATUS_FALSE; + } + + /* Digium Cards sometimes timeout several times in a row here. + Yes, we support digium cards, ain't we nice....... + 6 double length intervals should compensate */ + chunk = tech_pvt->zchan->effective_interval * 2; + total_to = chunk * 6; + + top: + + if (switch_channel_test_flag(channel, CF_SUSPEND)) { + do_break = 1; + } + + if (switch_test_flag(tech_pvt, TFLAG_BREAK)) { + switch_clear_flag_locked(tech_pvt, TFLAG_BREAK); + do_break = 1; + } + + if (switch_test_flag(tech_pvt, TFLAG_HOLD) || do_break) { + switch_yield(tech_pvt->zchan->effective_interval * 1000); + tech_pvt->cng_frame.datalen = tech_pvt->zchan->packet_len; + tech_pvt->cng_frame.samples = tech_pvt->cng_frame.datalen; + tech_pvt->cng_frame.flags = SFF_CNG; + *frame = &tech_pvt->cng_frame; + if (tech_pvt->zchan->effective_codec == ZAP_CODEC_SLIN) { + tech_pvt->cng_frame.samples /= 2; + } + return SWITCH_STATUS_SUCCESS; + } + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + goto fail; + } + + wflags = ZAP_READ; + status = zap_channel_wait(tech_pvt->zchan, &wflags, chunk); + + if (status == ZAP_FAIL) { + goto fail; + } + + if (status == ZAP_TIMEOUT) { + if (!switch_test_flag(tech_pvt, TFLAG_HOLD)) { + total_to -= chunk; + if (total_to <= 0) { + goto fail; + } + } + + goto top; + } + + if (!(wflags & ZAP_READ)) { + goto fail; + } + + len = tech_pvt->read_frame.buflen; + if (zap_channel_read(tech_pvt->zchan, tech_pvt->read_frame.data, &len) != ZAP_SUCCESS) { + goto fail; + } + + *frame = &tech_pvt->read_frame; + tech_pvt->read_frame.datalen = (uint32_t)len; + tech_pvt->read_frame.samples = tech_pvt->read_frame.datalen; + + if (tech_pvt->zchan->effective_codec == ZAP_CODEC_SLIN) { + tech_pvt->read_frame.samples /= 2; + } + + while (zap_channel_dequeue_dtmf(tech_pvt->zchan, dtmf, sizeof(dtmf))) { + switch_dtmf_t _dtmf = { 0, SWITCH_DEFAULT_DTMF_DURATION }; + char *p; + for (p = dtmf; p && *p; p++) { + if (is_dtmf(*p)) { + _dtmf.digit = *p; + zap_log(ZAP_LOG_DEBUG, "queue DTMF [%c]\n", *p); + switch_channel_queue_dtmf(channel, &_dtmf); + } + } + } + return SWITCH_STATUS_SUCCESS; + + fail: + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + return SWITCH_STATUS_GENERR; + + +} + +static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) +{ + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; + zap_size_t len; + unsigned char data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; + zap_wait_flag_t wflags = ZAP_WRITE; + zap_status_t status; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (!tech_pvt->zchan) { + return SWITCH_STATUS_FALSE; + } + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + return SWITCH_STATUS_FALSE; + } + + if (switch_test_flag(tech_pvt, TFLAG_HOLD)) { + return SWITCH_STATUS_SUCCESS; + } + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + goto fail; + } + + if (switch_test_flag(frame, SFF_CNG)) { + frame->data = data; + frame->buflen = sizeof(data); + if ((frame->datalen = tech_pvt->write_codec.implementation->encoded_bytes_per_packet) > frame->buflen) { + goto fail; + } + memset(data, 255, frame->datalen); + } + + + wflags = ZAP_WRITE; + status = zap_channel_wait(tech_pvt->zchan, &wflags, tech_pvt->zchan->effective_interval * 10); + + if (!(wflags & ZAP_WRITE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Dropping frame! (write not ready)\n"); + return SWITCH_STATUS_SUCCESS; + } + + len = frame->datalen; + if (zap_channel_write(tech_pvt->zchan, frame->data, frame->buflen, &len) != ZAP_SUCCESS) { + if (++tech_pvt->wr_error > 10) { + goto fail; + } + } else { + tech_pvt->wr_error = 0; + } + + return SWITCH_STATUS_SUCCESS; + + fail: + + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + return SWITCH_STATUS_GENERR; + +} + +static switch_status_t channel_receive_message_cas(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_channel_t *channel; + private_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + zap_log(ZAP_LOG_DEBUG, "Got Freeswitch message in R2 channel %d [%d]\n", tech_pvt->zchan->physical_chan_id, + msg->message_id); + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_RINGING: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); + } else { + zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); + } + } + break; + case SWITCH_MESSAGE_INDICATE_PROGRESS: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_MEDIA); + } else { + zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); + zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); + } + } + break; + case SWITCH_MESSAGE_INDICATE_ANSWER: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_ANSWERED); + } else { + /* lets make the ozmod_r2 module life easier by moving thru each + * state waiting for completion, clumsy, but does the job + */ + if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS) { + zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); + } + if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS_MEDIA) { + zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); + } + zap_set_state_locked_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_UP); + } + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_message_b(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_channel_t *channel; + private_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + if (tech_pvt->zchan->state == ZAP_CHANNEL_STATE_TERMINATING) { + zap_mutex_unlock(tech_pvt->zchan->mutex); + return SWITCH_STATUS_SUCCESS; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_RINGING: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + zap_set_flag(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); + } else { + zap_set_state_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); + } + } + break; + case SWITCH_MESSAGE_INDICATE_PROGRESS: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + zap_set_flag(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); + zap_set_flag(tech_pvt->zchan, ZAP_CHANNEL_MEDIA); + } else { + /* Don't skip messages in the ISDN call setup + * TODO: make the isdn stack smart enough to handle that itself + * until then, this is here for safety... + */ + if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS) { + zap_set_state_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); + } + zap_set_state_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); + } + } + break; + case SWITCH_MESSAGE_INDICATE_ANSWER: + { + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + zap_set_flag(tech_pvt->zchan, ZAP_CHANNEL_ANSWERED); + } else { + /* Don't skip messages in the ISDN call setup + * TODO: make the isdn stack smart enough to handle that itself + * until then, this is here for safety... + */ + if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS) { + zap_set_state_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS); + } + if (tech_pvt->zchan->state < ZAP_CHANNEL_STATE_PROGRESS_MEDIA) { + zap_set_state_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); + } + zap_set_state_wait(tech_pvt->zchan, ZAP_CHANNEL_STATE_UP); + } + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_message_fxo(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_channel_t *channel; + private_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_PROGRESS: + case SWITCH_MESSAGE_INDICATE_ANSWER: + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_ANSWERED); + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_MEDIA); + } else { + zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_UP); + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_message_fxs(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + switch_channel_t *channel; + private_t *tech_pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_PROGRESS: + case SWITCH_MESSAGE_INDICATE_ANSWER: + if (!switch_channel_test_flag(channel, CF_OUTBOUND)) { + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_ANSWERED); + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_PROGRESS); + zap_set_flag_locked(tech_pvt->zchan, ZAP_CHANNEL_MEDIA); + zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_UP); + switch_channel_mark_answered(channel); + } + break; + case SWITCH_MESSAGE_INDICATE_RINGING: + if (!switch_channel_test_flag(channel, CF_OUTBOUND)) { + + if (!switch_channel_test_flag(channel, CF_ANSWERED) && + !switch_channel_test_flag(channel, CF_EARLY_MEDIA) && + !switch_channel_test_flag(channel, CF_RING_READY) + ) { + zap_set_state_locked(tech_pvt->zchan, ZAP_CHANNEL_STATE_RING); + switch_channel_mark_ring_ready(channel); + } + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) +{ + private_t *tech_pvt; + switch_status_t status; + switch_channel_t *channel; + const char *var; + zap_channel_t *zchan = NULL; + + tech_pvt = (private_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + if (!(zchan = tech_pvt->zchan)) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + return SWITCH_STATUS_FALSE; + } + + zap_mutex_lock(zchan->mutex); + + if (!tech_pvt->zchan) { + switch_channel_hangup(channel, SWITCH_CAUSE_LOSE_RACE); + status = SWITCH_STATUS_FALSE; + goto end; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_PROGRESS: + case SWITCH_MESSAGE_INDICATE_ANSWER: + if (!switch_channel_test_flag(channel, CF_OUTBOUND)) { + if ((var = switch_channel_get_variable(channel, "openzap_pre_buffer_size"))) { + int tmp = atoi(var); + if (tmp > -1) { + zap_channel_command(tech_pvt->zchan, ZAP_COMMAND_SET_PRE_BUFFER_SIZE, &tmp); + } + } + } + break; + case SWITCH_MESSAGE_INDICATE_UUID_CHANGE: + { + zap_channel_replace_token(tech_pvt->zchan, msg->string_array_arg[0], msg->string_array_arg[1]); + } + break; + default: + break; + } + + switch (tech_pvt->zchan->type) { + case ZAP_CHAN_TYPE_FXS: + case ZAP_CHAN_TYPE_EM: + status = channel_receive_message_fxs(session, msg); + break; + case ZAP_CHAN_TYPE_FXO: + status = channel_receive_message_fxo(session, msg); + break; + case ZAP_CHAN_TYPE_B: + status = channel_receive_message_b(session, msg); + break; + case ZAP_CHAN_TYPE_CAS: + status = channel_receive_message_cas(session, msg); + break; + default: + status = SWITCH_STATUS_FALSE; + break; + } + + end: + + zap_mutex_unlock(zchan->mutex); + + return status; + +} + +switch_state_handler_table_t openzap_state_handlers = { + /*.on_init */ channel_on_init, + /*.on_routing */ channel_on_routing, + /*.on_execute */ channel_on_execute, + /*.on_hangup */ channel_on_hangup, + /*.on_exchange_media */ channel_on_exchange_media, + /*.on_soft_execute */ channel_on_soft_execute, + /*.on_consume_media */ NULL, + /*.on_hibernate */ NULL, + /*.on_reset */ NULL, + /*.on_park*/ NULL, + /*.on_reporting*/ NULL, + /*.on_destroy*/ channel_on_destroy + +}; + +switch_io_routines_t openzap_io_routines = { + /*.outgoing_channel */ channel_outgoing_channel, + /*.read_frame */ channel_read_frame, + /*.write_frame */ channel_write_frame, + /*.kill_channel */ channel_kill_channel, + /*.send_dtmf */ channel_send_dtmf, + /*.receive_message*/ channel_receive_message +}; + +/* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines +that allocate memory or you will have 1 channel with memory allocated from another channel's pool! +*/ +static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, switch_memory_pool_t **pool, + switch_originate_flag_t flags, switch_call_cause_t *cancel_cause) +{ + + const char *dest = NULL; + char *data = NULL; + int span_id = -1, chan_id = 0; + zap_channel_t *zchan = NULL; + switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + char name[128]; + zap_status_t status; + int direction = ZAP_TOP_DOWN; + zap_caller_data_t caller_data = {{ 0 }}; + char *span_name = NULL; + switch_event_header_t *h; + char *argv[3]; + int argc = 0; + const char *var; + const char *dest_num = NULL, *callerid_num = NULL; + + if (!outbound_profile) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (zstr(outbound_profile->destination_number)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid dial string\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + + data = switch_core_strdup(outbound_profile->pool, outbound_profile->destination_number); + + if (!zstr(outbound_profile->destination_number)) { + dest_num = switch_sanitize_number(switch_core_strdup(outbound_profile->pool, outbound_profile->destination_number)); + } + + if (!zstr(outbound_profile->caller_id_number)) { + callerid_num = switch_sanitize_number(switch_core_strdup(outbound_profile->pool, outbound_profile->caller_id_number)); + } + + if (!zstr(callerid_num) && !strcmp(callerid_num, "0000000000")) { + callerid_num = NULL; + } + + if ((argc = switch_separate_string(data, '/', argv, (sizeof(argv) / sizeof(argv[0])))) < 2) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid dial string\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (switch_is_number(argv[0])) { + span_id = atoi(argv[0]); + } else { + span_name = argv[0]; + } + + if (*argv[1] == 'A') { + direction = ZAP_BOTTOM_UP; + } else if (*argv[1] == 'a') { + direction = ZAP_TOP_DOWN; + } else { + chan_id = atoi(argv[1]); + } + + if (!(dest = argv[2])) { + dest = ""; + } + + if (span_id == 0 && chan_id != 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Span 0 is used to pick the first available span, selecting a channel is not supported (and doesn't make sense)\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (span_id == -1 && !zstr(span_name)) { + zap_span_t *span; + zap_status_t zstatus = zap_span_find_by_name(span_name, &span); + if (zstatus == ZAP_SUCCESS && span) { + span_id = span->span_id; + } + } + + if (span_id == -1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing span\n"); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (chan_id < 0) { + direction = ZAP_BOTTOM_UP; + chan_id = 0; + } + + if (switch_test_flag(outbound_profile, SWITCH_CPF_SCREEN)) { + caller_data.screen = 1; + } + + if (switch_test_flag(outbound_profile, SWITCH_CPF_HIDE_NUMBER)) { + caller_data.pres = 1; + } + + if (!zstr(dest)) { + zap_set_string(caller_data.ani.digits, dest); + } + + if ((var = switch_event_get_header(var_event, "openzap_outbound_ton")) || (var = switch_core_get_variable("openzap_outbound_ton"))) { + if (!strcasecmp(var, "national")) { + caller_data.ani.type = ZAP_TON_NATIONAL; + } else if (!strcasecmp(var, "international")) { + caller_data.ani.type = ZAP_TON_INTERNATIONAL; + } else if (!strcasecmp(var, "local")) { + caller_data.ani.type = ZAP_TON_SUBSCRIBER_NUMBER; + } else if (!strcasecmp(var, "unknown")) { + caller_data.ani.type = ZAP_TON_UNKNOWN; + } + } else { + caller_data.ani.type = outbound_profile->destination_number_ton; + } + + caller_data.ani.plan = outbound_profile->destination_number_numplan; + + /* blindly copy data from outbound_profile. They will be overwritten + * by calling zap_caller_data if needed after */ + caller_data.cid_num.type = outbound_profile->caller_ton; + caller_data.cid_num.plan = outbound_profile->caller_numplan; + + caller_data.rdnis.type = outbound_profile->rdnis_ton; + caller_data.rdnis.plan = outbound_profile->rdnis_numplan; + +#if 0 + if (!zstr(outbound_profile->rdnis)) { + zap_set_string(caller_data.rdnis.digits, outbound_profile->rdnis); + } +#endif + + zap_set_string(caller_data.cid_name, outbound_profile->caller_id_name); + zap_set_string(caller_data.cid_num.digits, switch_str_nil(callerid_num)); + + if (chan_id) { + status = zap_channel_open(span_id, chan_id, &zchan); + } else { + status = zap_channel_open_any(span_id, direction, &caller_data, &zchan); + } + + if (status != ZAP_SUCCESS) { + if (caller_data.hangup_cause == SWITCH_CAUSE_NONE) { + caller_data.hangup_cause = SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; + } + return caller_data.hangup_cause; + } + + if ((var = switch_event_get_header(var_event, "openzap_pre_buffer_size"))) { + int tmp = atoi(var); + if (tmp > -1) { + zap_channel_command(zchan, ZAP_COMMAND_SET_PRE_BUFFER_SIZE, &tmp); + } + } + + zap_channel_clear_vars(zchan); + for (h = var_event->headers; h; h = h->next) { + if (!strncasecmp(h->name, OPENZAP_VAR_PREFIX, OPENZAP_VAR_PREFIX_LEN)) { + char *v = h->name + OPENZAP_VAR_PREFIX_LEN; + if (!zstr(v)) { + zap_channel_add_var(zchan, v, h->value); + } + } + } + + if ((*new_session = switch_core_session_request(openzap_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, pool)) != 0) { + private_t *tech_pvt; + switch_caller_profile_t *caller_profile; + switch_channel_t *channel = switch_core_session_get_channel(*new_session); + + switch_core_session_add_stream(*new_session, NULL); + if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) { + tech_init(tech_pvt, *new_session, zchan); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); + switch_core_session_destroy(new_session); + cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + goto fail; + } + + snprintf(name, sizeof(name), "OpenZAP/%u:%u/%s", zchan->span_id, zchan->chan_id, dest); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect outbound channel %s\n", name); + switch_channel_set_name(channel, name); + switch_channel_set_variable(channel, "openzap_span_name", zchan->span->name); + switch_channel_set_variable_printf(channel, "openzap_span_number", "%d", zchan->span_id); + switch_channel_set_variable_printf(channel, "openzap_chan_number", "%d", zchan->chan_id); + zap_channel_set_caller_data(zchan, &caller_data); + caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); + caller_profile->destination_number = switch_core_strdup(caller_profile->pool, switch_str_nil(dest_num)); + caller_profile->caller_id_number = switch_core_strdup(caller_profile->pool, switch_str_nil(callerid_num)); + switch_channel_set_caller_profile(channel, caller_profile); + tech_pvt->caller_profile = caller_profile; + + + switch_channel_set_flag(channel, CF_OUTBOUND); + switch_channel_set_state(channel, CS_INIT); + if (zap_channel_add_token(zchan, switch_core_session_get_uuid(*new_session), zchan->token_count) != ZAP_SUCCESS) { + switch_core_session_destroy(new_session); + cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + goto fail; + } + + + if (zap_channel_outgoing_call(zchan) != ZAP_SUCCESS) { + if (tech_pvt->read_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->read_codec); + } + + if (tech_pvt->write_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->write_codec); + } + switch_core_session_destroy(new_session); + cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + goto fail; + } + + zap_channel_init(zchan); + + return SWITCH_CAUSE_SUCCESS; + } + + fail: + + if (zchan) { + zap_channel_done(zchan); + } + + return cause; + +} + +zap_status_t zap_channel_from_event(zap_sigmsg_t *sigmsg, switch_core_session_t **sp) +{ + switch_core_session_t *session = NULL; + private_t *tech_pvt = NULL; + switch_channel_t *channel = NULL; + char name[128]; + + *sp = NULL; + + if (!(session = switch_core_session_request(openzap_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Initilization Error!\n"); + return ZAP_FAIL; + } + + switch_core_session_add_stream(session, NULL); + + tech_pvt = (private_t *) switch_core_session_alloc(session, sizeof(private_t)); + assert(tech_pvt != NULL); + channel = switch_core_session_get_channel(session); + if (tech_init(tech_pvt, session, sigmsg->channel) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Initilization Error!\n"); + switch_core_session_destroy(&session); + return ZAP_FAIL; + } + + *sigmsg->channel->caller_data.collected = '\0'; + + if (zstr(sigmsg->channel->caller_data.cid_name)) { + switch_set_string(sigmsg->channel->caller_data.cid_name, sigmsg->channel->chan_name); + } + + if (zstr(sigmsg->channel->caller_data.cid_num.digits)) { + if (!zstr(sigmsg->channel->caller_data.ani.digits)) { + switch_set_string(sigmsg->channel->caller_data.cid_num.digits, sigmsg->channel->caller_data.ani.digits); + } else { + switch_set_string(sigmsg->channel->caller_data.cid_num.digits, sigmsg->channel->chan_number); + } + } + + tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), + "OpenZAP", + SPAN_CONFIG[sigmsg->channel->span_id].dialplan, + sigmsg->channel->caller_data.cid_name, + sigmsg->channel->caller_data.cid_num.digits, + NULL, + sigmsg->channel->caller_data.ani.digits, + sigmsg->channel->caller_data.aniII, + sigmsg->channel->caller_data.rdnis.digits, + (char *) modname, + SPAN_CONFIG[sigmsg->channel->span_id].context, + sigmsg->channel->caller_data.dnis.digits); + + assert(tech_pvt->caller_profile != NULL); + + if (sigmsg->channel->caller_data.screen == 1 || sigmsg->channel->caller_data.screen == 3) { + switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_SCREEN); + } + + tech_pvt->caller_profile->caller_ton = sigmsg->channel->caller_data.cid_num.type; + tech_pvt->caller_profile->caller_numplan = sigmsg->channel->caller_data.cid_num.plan; + tech_pvt->caller_profile->ani_ton = sigmsg->channel->caller_data.ani.type; + tech_pvt->caller_profile->ani_numplan = sigmsg->channel->caller_data.ani.plan; + tech_pvt->caller_profile->destination_number_ton = sigmsg->channel->caller_data.dnis.type; + tech_pvt->caller_profile->destination_number_numplan = sigmsg->channel->caller_data.dnis.plan; + tech_pvt->caller_profile->rdnis_ton = sigmsg->channel->caller_data.rdnis.type; + tech_pvt->caller_profile->rdnis_numplan = sigmsg->channel->caller_data.rdnis.plan; + + if (sigmsg->channel->caller_data.pres) { + switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); + } + + snprintf(name, sizeof(name), "OpenZAP/%u:%u/%s", sigmsg->channel->span_id, sigmsg->channel->chan_id, tech_pvt->caller_profile->destination_number); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect inbound channel %s\n", name); + switch_channel_set_name(channel, name); + switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); + + switch_channel_set_variable(channel, "openzap_span_name", sigmsg->channel->span->name); + switch_channel_set_variable_printf(channel, "openzap_span_number", "%d", sigmsg->channel->span_id); + switch_channel_set_variable_printf(channel, "openzap_chan_number", "%d", sigmsg->channel->chan_id); + + switch_channel_set_state(channel, CS_INIT); + if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error spawning thread\n"); + switch_core_session_destroy(&session); + return ZAP_FAIL; + } + + if (zap_channel_add_token(sigmsg->channel, switch_core_session_get_uuid(session), 0) != ZAP_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error adding token\n"); + switch_core_session_destroy(&session); + return ZAP_FAIL; + } + *sp = session; + + return ZAP_SUCCESS; +} + +static ZIO_SIGNAL_CB_FUNCTION(on_common_signal) +{ + switch_event_t *event = NULL; + + switch (sigmsg->event_id) { + + case ZAP_SIGEVENT_ALARM_CLEAR: + case ZAP_SIGEVENT_ALARM_TRAP: + { + if (zap_channel_get_alarms(sigmsg->channel) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "failed to retrieve alarms\n"); + return ZAP_FAIL; + } + if (switch_event_create(&event, SWITCH_EVENT_TRAP) != SWITCH_STATUS_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "failed to create alarms events\n"); + return ZAP_FAIL; + } + if (sigmsg->event_id == ZAP_SIGEVENT_ALARM_CLEAR) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "zap-alarm-clear"); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "zap-alarm-trap"); + } + } + break; + default: + return ZAP_SUCCESS; + break; + } + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-name", "%s", sigmsg->channel->span->name); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-number", "%d", sigmsg->channel->span_id); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "chan-number", "%d", sigmsg->channel->chan_id); + + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_RECOVER)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "recover"); + } + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_LOOPBACK)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "loopback"); + } + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_YELLOW)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "yellow"); + } + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_RED)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "red"); + } + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_BLUE)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "blue"); + } + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_NOTOPEN)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "notopen"); + } + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_AIS)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "ais"); + } + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_RAI)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "rai"); + } + if (zap_test_alarm_flag(sigmsg->channel, ZAP_ALARM_GENERAL)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "general"); + } + switch_event_fire(&event); + + return ZAP_BREAK; +} + +static ZIO_SIGNAL_CB_FUNCTION(on_fxo_signal) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + zap_status_t status; + + zap_log(ZAP_LOG_DEBUG, "got FXO sig %d:%d [%s]\n", sigmsg->channel->span_id, sigmsg->channel->chan_id, zap_signal_event2str(sigmsg->event_id)); + + switch(sigmsg->event_id) { + + case ZAP_SIGEVENT_PROGRESS_MEDIA: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_pre_answered(channel); + switch_core_session_rwunlock(session); + } + } + break; + case ZAP_SIGEVENT_STOP: + { + private_t *tech_pvt = NULL; + while((session = zap_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + zap_channel_clear_token(sigmsg->channel, 0); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); + zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + } + break; + case ZAP_SIGEVENT_UP: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_answered(channel); + switch_core_session_rwunlock(session); + } + } + break; + case ZAP_SIGEVENT_START: + { + status = zap_channel_from_event(sigmsg, &session); + if (status != ZAP_SUCCESS) { + zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled msg type %d for channel %d:%d\n", + sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + + } + + return ZAP_SUCCESS; +} + +static ZIO_SIGNAL_CB_FUNCTION(on_fxs_signal) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + zap_status_t status = ZAP_SUCCESS; + + zap_log(ZAP_LOG_DEBUG, "got FXS sig [%s]\n", zap_signal_event2str(sigmsg->event_id)); + + switch(sigmsg->event_id) { + case ZAP_SIGEVENT_UP: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_answered(channel); + switch_core_session_rwunlock(session); + } + } + break; + case ZAP_SIGEVENT_PROGRESS: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_ring_ready(channel); + switch_core_session_rwunlock(session); + } + } + break; + case ZAP_SIGEVENT_START: + { + zap_clear_flag_locked(sigmsg->channel, ZAP_CHANNEL_HOLD); + status = zap_channel_from_event(sigmsg, &session); + if (status != ZAP_SUCCESS) { + zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_BUSY); + } + } + break; + case ZAP_SIGEVENT_STOP: + { + private_t *tech_pvt = NULL; + switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; + if (sigmsg->channel->token_count) { + switch_core_session_t *session_a, *session_b, *session_t = NULL; + switch_channel_t *channel_a = NULL, *channel_b = NULL; + int digits = !zstr(sigmsg->channel->caller_data.collected); + const char *br_a_uuid = NULL, *br_b_uuid = NULL; + private_t *tech_pvt = NULL; + + + if ((session_a = switch_core_session_locate(sigmsg->channel->tokens[0]))) { + channel_a = switch_core_session_get_channel(session_a); + br_a_uuid = switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE); + + tech_pvt = switch_core_session_get_private(session_a); + stop_hold(session_a, switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE)); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + } + + if ((session_b = switch_core_session_locate(sigmsg->channel->tokens[1]))) { + channel_b = switch_core_session_get_channel(session_b); + br_b_uuid = switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE); + + tech_pvt = switch_core_session_get_private(session_b); + stop_hold(session_a, switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE)); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + } + + if (channel_a && channel_b && !switch_channel_test_flag(channel_a, CF_OUTBOUND) && !switch_channel_test_flag(channel_b, CF_OUTBOUND)) { + cause = SWITCH_CAUSE_ATTENDED_TRANSFER; + if (br_a_uuid && br_b_uuid) { + switch_ivr_uuid_bridge(br_a_uuid, br_b_uuid); + } else if (br_a_uuid && digits) { + session_t = switch_core_session_locate(br_a_uuid); + } else if (br_b_uuid && digits) { + session_t = switch_core_session_locate(br_b_uuid); + } + } + + if (session_t) { + switch_ivr_session_transfer(session_t, sigmsg->channel->caller_data.collected, NULL, NULL); + switch_core_session_rwunlock(session_t); + } + + if (session_a) { + switch_core_session_rwunlock(session_a); + } + + if (session_b) { + switch_core_session_rwunlock(session_b); + } + + + } + + while((session = zap_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, cause); + zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + zap_channel_clear_token(sigmsg->channel, NULL); + + } + break; + + case ZAP_SIGEVENT_ADD_CALL: + { + cycle_foreground(sigmsg->channel, 1, NULL); + } + break; + case ZAP_SIGEVENT_FLASH: + { + + if (zap_test_flag(sigmsg->channel, ZAP_CHANNEL_HOLD) && sigmsg->channel->token_count == 1) { + switch_core_session_t *session; + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + const char *buuid; + switch_channel_t *channel; + private_t *tech_pvt; + + tech_pvt = switch_core_session_get_private(session); + channel = switch_core_session_get_channel(session); + buuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_UP); + stop_hold(session, buuid); + switch_clear_flag_locked(tech_pvt, TFLAG_HOLD); + switch_core_session_rwunlock(session); + } + } else if (sigmsg->channel->token_count == 2 && (SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_3WAY)) { + if (zap_test_flag(sigmsg->channel, ZAP_CHANNEL_3WAY)) { + zap_clear_flag(sigmsg->channel, ZAP_CHANNEL_3WAY); + if ((session = zap_channel_get_session(sigmsg->channel, 1))) { + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + cycle_foreground(sigmsg->channel, 1, NULL); + } else { + char *cmd; + cmd = switch_mprintf("three_way::%s", sigmsg->channel->tokens[0]); + zap_set_flag(sigmsg->channel, ZAP_CHANNEL_3WAY); + cycle_foreground(sigmsg->channel, 1, cmd); + free(cmd); + } + } else if ((SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_CALL_SWAP) + || (SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_3WAY) + ) { + cycle_foreground(sigmsg->channel, 1, NULL); + if (sigmsg->channel->token_count == 1) { + zap_set_flag_locked(sigmsg->channel, ZAP_CHANNEL_HOLD); + zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_DIALTONE); + } + } + + } + break; + + case ZAP_SIGEVENT_COLLECTED_DIGIT: + { + char *dtmf = sigmsg->raw_data; + char *regex = SPAN_CONFIG[sigmsg->channel->span->span_id].dial_regex; + char *fail_regex = SPAN_CONFIG[sigmsg->channel->span->span_id].fail_dial_regex; + + if (zstr(regex)) { + regex = NULL; + } + + if (zstr(fail_regex)) { + fail_regex = NULL; + } + + zap_log(ZAP_LOG_DEBUG, "got DTMF sig [%s]\n", dtmf); + switch_set_string(sigmsg->channel->caller_data.collected, dtmf); + + if ((regex || fail_regex) && !zstr(dtmf)) { + switch_regex_t *re = NULL; + int ovector[30]; + int match = 0; + + if (fail_regex) { + match = switch_regex_perform(dtmf, fail_regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? ZAP_SUCCESS : ZAP_BREAK; + switch_regex_safe_free(re); + } + + if (status == ZAP_SUCCESS && regex) { + match = switch_regex_perform(dtmf, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? ZAP_BREAK : ZAP_SUCCESS; + } + + switch_regex_safe_free(re); + } + + } + break; + + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled msg type %d for channel %d:%d\n", + sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + + } + + return status; +} + +static ZIO_SIGNAL_CB_FUNCTION(on_r2_signal) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + zap_status_t status = ZAP_SUCCESS; + + zap_log(ZAP_LOG_DEBUG, "Got R2 channel sig [%s] in channel %d\n", zap_signal_event2str(sigmsg->event_id), sigmsg->channel->physical_chan_id); + + if (on_common_signal(sigmsg) == ZAP_BREAK) { + return ZAP_SUCCESS; + } + + switch(sigmsg->event_id) { + /* on_call_disconnect from the R2 side */ + case ZAP_SIGEVENT_STOP: + { + private_t *tech_pvt = NULL; + while((session = zap_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); + zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + } + break; + + /* on_call_offered from the R2 side */ + case ZAP_SIGEVENT_START: + { + status = zap_channel_from_event(sigmsg, &session); + } + break; + + /* on DNIS received from the R2 forward side, return status == ZAP_BREAK to stop requesting DNIS */ + case ZAP_SIGEVENT_COLLECTED_DIGIT: + { + char *regex = SPAN_CONFIG[sigmsg->channel->span->span_id].dial_regex; + char *fail_regex = SPAN_CONFIG[sigmsg->channel->span->span_id].fail_dial_regex; + + if (zstr(regex)) { + regex = NULL; + } + + if (zstr(fail_regex)) { + fail_regex = NULL; + } + + zap_log(ZAP_LOG_DEBUG, "R2 DNIS so far [%s]\n", sigmsg->channel->caller_data.dnis.digits); + + if ((regex || fail_regex) && !zstr(sigmsg->channel->caller_data.dnis.digits)) { + switch_regex_t *re = NULL; + int ovector[30]; + int match = 0; + + if (fail_regex) { + match = switch_regex_perform(sigmsg->channel->caller_data.dnis.digits, fail_regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? ZAP_SUCCESS : ZAP_BREAK; + switch_regex_safe_free(re); + } + + if (status == ZAP_SUCCESS && regex) { + match = switch_regex_perform(sigmsg->channel->caller_data.dnis.digits, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? ZAP_BREAK : ZAP_SUCCESS; + } + + switch_regex_safe_free(re); + } + } + break; + + case ZAP_SIGEVENT_PROGRESS: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_ring_ready(channel); + switch_core_session_rwunlock(session); + } + } + break; + + case ZAP_SIGEVENT_UP: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + zap_tone_type_t tt = ZAP_TONE_DTMF; + channel = switch_core_session_get_channel(session); + switch_channel_mark_answered(channel); + if (zap_channel_command(sigmsg->channel, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Failed to enable DTMF detection in R2 channel %d:%d\n", sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + switch_core_session_rwunlock(session); + } + } + break; + + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled event %d from R2 for channel %d:%d\n", + sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + } + + return status; +} + +static ZIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + zap_log(ZAP_LOG_DEBUG, "got clear channel sig [%s]\n", zap_signal_event2str(sigmsg->event_id)); + + if (on_common_signal(sigmsg) == ZAP_BREAK) { + return ZAP_SUCCESS; + } + + switch(sigmsg->event_id) { + case ZAP_SIGEVENT_START: + { + zap_tone_type_t tt = ZAP_TONE_DTMF; + + if (zap_channel_command(sigmsg->channel, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "TONE ERROR\n"); + } + + return zap_channel_from_event(sigmsg, &session); + } + break; + case ZAP_SIGEVENT_STOP: + case ZAP_SIGEVENT_RESTART: + { + private_t *tech_pvt = NULL; + while((session = zap_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + switch_set_flag_locked(tech_pvt, TFLAG_DEAD); + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, sigmsg->channel->caller_data.hangup_cause); + zap_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); + switch_core_session_rwunlock(session); + } + } + break; + case ZAP_SIGEVENT_UP: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + zap_tone_type_t tt = ZAP_TONE_DTMF; + channel = switch_core_session_get_channel(session); + switch_channel_mark_answered(channel); + if (zap_channel_command(sigmsg->channel, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "TONE ERROR\n"); + } + switch_core_session_rwunlock(session); + } else { + const char *uuid = zap_channel_get_uuid(sigmsg->channel, 0); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Session for channel %d:%d not found [UUID: %s]\n", + sigmsg->channel->span_id, sigmsg->channel->chan_id, (uuid) ? uuid : "N/A"); + } + } + case ZAP_SIGEVENT_PROGRESS_MEDIA: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_pre_answered(channel); + switch_core_session_rwunlock(session); + } else { + const char *uuid = zap_channel_get_uuid(sigmsg->channel, 0); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Session for channel %d:%d not found [UUID: %s]\n", + sigmsg->channel->span_id, sigmsg->channel->chan_id, (uuid) ? uuid : "N/A"); + } + } + break; + case ZAP_SIGEVENT_PROGRESS: + { + if ((session = zap_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + switch_channel_mark_ring_ready(channel); + switch_core_session_rwunlock(session); + } else { + const char *uuid = zap_channel_get_uuid(sigmsg->channel, 0); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Session for channel %d:%d not found [UUID: %s]\n", + sigmsg->channel->span_id, sigmsg->channel->chan_id, (uuid) ? uuid : "N/A"); + } + } + break; + + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled msg type %d for channel %d:%d\n", + sigmsg->event_id, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + } + + return ZAP_SUCCESS; +} + + +static ZIO_SIGNAL_CB_FUNCTION(on_analog_signal) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + if (on_common_signal(sigmsg) == ZAP_BREAK) { + return ZAP_SUCCESS; + } + + switch (sigmsg->channel->type) { + case ZAP_CHAN_TYPE_FXO: + case ZAP_CHAN_TYPE_EM: + { + status = on_fxo_signal(sigmsg); + } + break; + case ZAP_CHAN_TYPE_FXS: + { + status = on_fxs_signal(sigmsg); + } + break; + default: + { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled analog channel type %d for channel %d:%d\n", + sigmsg->channel->type, sigmsg->channel->span_id, sigmsg->channel->chan_id); + } + break; + } + + return status; +} + +static void zap_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + char *data = NULL; + va_list ap; + + va_start(ap, fmt); + + if (switch_vasprintf(&data, fmt, ap) != -1) { + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, (char *)func, line, NULL, level, "%s", data); + free(data); + } + + va_end(ap); + +} + +static uint32_t enable_analog_option(const char *str, uint32_t current_options) +{ + if (!strcasecmp(str, "3-way")) { + current_options |= ANALOG_OPTION_3WAY; + current_options &= ~ANALOG_OPTION_CALL_SWAP; + } else if (!strcasecmp(str, "call-swap")) { + current_options |= ANALOG_OPTION_CALL_SWAP; + current_options &= ~ANALOG_OPTION_3WAY; + } + + return current_options; + +} + +static switch_status_t load_config(void) +{ + const char *cf = "openzap.conf"; + switch_xml_t cfg, xml, settings, param, spans, myspan; + + memset(&globals, 0, sizeof(globals)); + switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool); + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); + return SWITCH_STATUS_TERM; + } + + if ((settings = switch_xml_child(cfg, "settings"))) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "debug")) { + globals.debug = atoi(val); + } else if (!strcasecmp(var, "hold-music")) { + switch_set_string(globals.hold_music, val); + } else if (!strcasecmp(var, "enable-analog-option")) { + globals.analog_options = enable_analog_option(val, globals.analog_options); + } + } + } + + if ((spans = switch_xml_child(cfg, "analog_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + zap_status_t zstatus = ZAP_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + const char *tonegroup = NULL; + char *digit_timeout = NULL; + char *max_digits = NULL; + char *hotline = NULL; + char *dial_regex = NULL; + char *hold_music = NULL; + char *fail_dial_regex = NULL; + const char *enable_callerid = "true"; + + uint32_t span_id = 0, to = 0, max = 0; + zap_span_t *span = NULL; + analog_option_t analog_options = ANALOG_OPTION_NONE; + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { + digit_timeout = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "dial-regex")) { + dial_regex = val; + } else if (!strcasecmp(var, "enable-callerid")) { + enable_callerid = val; + } else if (!strcasecmp(var, "fail-dial-regex")) { + fail_dial_regex = val; + } else if (!strcasecmp(var, "hold-music")) { + hold_music = val; + } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { + max_digits = val; + } else if (!strcasecmp(var, "hotline")) { + hotline = val; + } else if (!strcasecmp(var, "enable-analog-option")) { + analog_options = enable_analog_option(val, analog_options); + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + + + if (!tonegroup) { + tonegroup = "us"; + } + + if (digit_timeout) { + to = atoi(digit_timeout); + } + + if (max_digits) { + max = atoi(max_digits); + } + + if (name) { + zstatus = zap_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = zap_span_find(span_id, &span); + } + + if (zstatus != ZAP_SUCCESS) { + zstatus = zap_span_find_by_name(id, &span); + } + } + + if (zstatus != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + if (zap_configure_span("analog", span, on_analog_signal, + "tonemap", tonegroup, + "digit_timeout", &to, + "max_dialstr", &max, + "hotline", hotline, + "enable_callerid", enable_callerid, + TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d\n", span_id); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_set_string(SPAN_CONFIG[span->span_id].context, context); + switch_set_string(SPAN_CONFIG[span->span_id].dialplan, dialplan); + SPAN_CONFIG[span->span_id].analog_options = analog_options | globals.analog_options; + + if (dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); + } + + if (fail_dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); + } + + if (hold_music) { + switch_set_string(SPAN_CONFIG[span->span_id].hold_music, hold_music); + } + switch_copy_string(SPAN_CONFIG[span->span_id].type, "analog", sizeof(SPAN_CONFIG[span->span_id].type)); + zap_span_start(span); + } + } + + if ((spans = switch_xml_child(cfg, "analog_em_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + zap_status_t zstatus = ZAP_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + const char *tonegroup = NULL; + char *digit_timeout = NULL; + char *max_digits = NULL; + char *dial_regex = NULL; + char *hold_music = NULL; + char *fail_dial_regex = NULL; + uint32_t span_id = 0, to = 0, max = 0; + zap_span_t *span = NULL; + analog_option_t analog_options = ANALOG_OPTION_NONE; + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { + digit_timeout = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "dial-regex")) { + dial_regex = val; + } else if (!strcasecmp(var, "fail-dial-regex")) { + fail_dial_regex = val; + } else if (!strcasecmp(var, "hold-music")) { + hold_music = val; + } else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) { + max_digits = val; + } else if (!strcasecmp(var, "enable-analog-option")) { + analog_options = enable_analog_option(val, analog_options); + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + + if (!tonegroup) { + tonegroup = "us"; + } + + if (digit_timeout) { + to = atoi(digit_timeout); + } + + if (max_digits) { + max = atoi(max_digits); + } + + + if (name) { + zstatus = zap_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = zap_span_find(span_id, &span); + } + + if (zstatus != ZAP_SUCCESS) { + zstatus = zap_span_find_by_name(id, &span); + } + } + + if (zstatus != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + + if (zap_configure_span("analog_em", span, on_analog_signal, + "tonemap", tonegroup, + "digit_timeout", &to, + "max_dialstr", &max, + TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d\n", span_id); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_set_string(SPAN_CONFIG[span->span_id].context, context); + switch_set_string(SPAN_CONFIG[span->span_id].dialplan, dialplan); + SPAN_CONFIG[span->span_id].analog_options = analog_options | globals.analog_options; + + if (dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); + } + + if (fail_dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); + } + + if (hold_music) { + switch_set_string(SPAN_CONFIG[span->span_id].hold_music, hold_music); + } + switch_copy_string(SPAN_CONFIG[span->span_id].type, "analog_em", sizeof(SPAN_CONFIG[span->span_id].type)); + zap_span_start(span); + } + } + + if ((spans = switch_xml_child(cfg, "pri_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + zap_status_t zstatus = ZAP_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + //Q921NetUser_t mode = Q931_TE; + //Q931Dialect_t dialect = Q931_Dialect_National; + char *mode = NULL; + char *dialect = NULL; + uint32_t span_id = 0; + zap_span_t *span = NULL; + const char *tonegroup = NULL; + char *digit_timeout = NULL; + const char *opts = "none"; + uint32_t to = 0; + int q921loglevel = -1; + int q931loglevel = -1; + + // quick debug + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ID: '%s', Name:'%s'\n",id,name); + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "mode")) { + mode = val; + } else if (!strcasecmp(var, "dialect")) { + dialect = val; + } else if (!strcasecmp(var, "q921loglevel")) { + if ((q921loglevel = switch_log_str2level(val)) == SWITCH_LOG_INVALID) { + q921loglevel = -1; + } + } else if (!strcasecmp(var, "q931loglevel")) { + if ((q931loglevel = switch_log_str2level(val)) == SWITCH_LOG_INVALID) { + q931loglevel = -1; + } + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "opts")) { + opts = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) { + digit_timeout = val; + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + if (name) { + zstatus = zap_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = zap_span_find(span_id, &span); + } + + if (zstatus != ZAP_SUCCESS) { + zstatus = zap_span_find_by_name(id, &span); + } + } + + if (digit_timeout) { + to = atoi(digit_timeout); + } + + if (zstatus != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + if (!tonegroup) { + tonegroup = "us"; + } + + if (zap_configure_span("isdn", span, on_clear_channel_signal, + "mode", mode, + "dialect", dialect, + "digit_timeout", &to, + "opts", opts, + "tonemap", tonegroup, + "q921loglevel", q921loglevel, + "q931loglevel", q931loglevel, + TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d mode: %s dialect: %s error: %s\n", span_id, mode, dialect, span->last_error); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); + switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); + switch_copy_string(SPAN_CONFIG[span->span_id].type, "isdn", sizeof(SPAN_CONFIG[span->span_id].type)); + + zap_span_start(span); + } + } + + + + if ((spans = switch_xml_child(cfg, "libpri_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + zap_status_t zstatus = ZAP_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + + const char *o_node = "cpe"; + const char *o_switch = "dms100"; + const char *o_dp = "unknown"; + const char *o_l1 = "ulaw"; + const char *o_debug = "none"; + const char* opts = "none"; + + uint32_t span_id = 0; + zap_span_t *span = NULL; + + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "node")) { + o_node = val; + } else if (!strcasecmp(var, "switch")) { + o_switch = val; + } else if (!strcasecmp(var, "dp")) { + o_dp = val; + } else if (!strcasecmp(var, "l1")) { + o_l1 = val; + } else if (!strcasecmp(var, "debug")) { + o_debug = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "opts")) { + opts = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + if (name) { + zstatus = zap_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = zap_span_find(span_id, &span); + } + + if (zstatus != ZAP_SUCCESS) { + zstatus = zap_span_find_by_name(id, &span); + } + } + + if (zstatus != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + + if (zap_configure_span("libpri", span, on_clear_channel_signal, + "node", o_node, + "switch", o_switch, + "dp", o_dp, + "l1", o_l1, + "debug", o_debug, + "opts", opts, + TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d node: %s switch: %s dp: %s l1: %s debug: %s error: %s\n", + span_id, switch_str_nil(o_node), switch_str_nil(o_switch), switch_str_nil(o_dp), switch_str_nil(o_l1), switch_str_nil(o_debug), + span->last_error); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); + switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); + switch_copy_string(SPAN_CONFIG[span->span_id].type, "isdn", sizeof(SPAN_CONFIG[span->span_id].type)); + + zap_span_start(span); + } + } + + if ((spans = switch_xml_child(cfg, "boost_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + zap_status_t zstatus = ZAP_FAIL; + const char *context = "default"; + const char *dialplan = "XML"; + const char *outbound_called_ton = "national"; + const char *outbound_called_npi = "isdn"; + const char *outbound_calling_ton = "national"; + const char *outbound_calling_npi = "isdn"; + const char *outbound_rdnis_ton = "national"; + const char *outbound_rdnis_npi = "isdn"; + uint32_t span_id = 0; + zap_span_t *span = NULL; + const char *tonegroup = NULL; + char *local_ip = NULL; + int local_port = 0; + char *remote_ip = NULL; + int remote_port = 0; + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "tonegroup")) { + tonegroup = val; + } else if (!strcasecmp(var, "local-ip")) { + local_ip = val; + } else if (!strcasecmp(var, "local-port")) { + local_port = atoi(val); + } else if (!strcasecmp(var, "remote-ip")) { + remote_ip = val; + } else if (!strcasecmp(var, "remote-port")) { + remote_port = atoi(val); + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "outbound-called-ton")) { + outbound_called_ton = val; + } else if (!strcasecmp(var, "outbound-called-npi")) { + outbound_called_npi = val; + } else if (!strcasecmp(var, "outbound-calling-ton")) { + outbound_calling_ton = val; + } else if (!strcasecmp(var, "outbound-calling-npi")) { + outbound_calling_npi = val; + } else if (!strcasecmp(var, "outbound-rdnis-ton")) { + outbound_rdnis_ton = val; + } else if (!strcasecmp(var, "outbound-rdnis-npi")) { + outbound_rdnis_npi = val; + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param\n"); + continue; + } + + if (!tonegroup) { + tonegroup = "us"; + } + + if (name) { + zstatus = zap_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = zap_span_find(span_id, &span); + } + + if (zstatus != ZAP_SUCCESS) { + zstatus = zap_span_find_by_name(id, &span); + } + } + + if (zstatus != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + zap_set_npi(outbound_called_npi, &span->default_caller_data.ani.plan); + zap_set_npi(outbound_calling_npi, &span->default_caller_data.cid_num.plan); + zap_set_npi(outbound_rdnis_npi, &span->default_caller_data.rdnis.plan); + + zap_set_ton(outbound_called_ton, &span->default_caller_data.ani.type); + zap_set_ton(outbound_calling_ton, &span->default_caller_data.cid_num.type); + zap_set_ton(outbound_rdnis_ton, &span->default_caller_data.rdnis.type); + + if (zap_configure_span("sangoma_boost", span, on_clear_channel_signal, + "local_ip", local_ip, + "local_port", &local_port, + "remote_ip", remote_ip, + "remote_port", &remote_port, + TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d error: %s\n", span_id, span->last_error); + continue; + } + + SPAN_CONFIG[span->span_id].span = span; + switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); + switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); + + zap_span_start(span); + switch_copy_string(SPAN_CONFIG[span->span_id].type, "Sangoma (boost)", sizeof(SPAN_CONFIG[span->span_id].type)); + } + } + + if ((spans = switch_xml_child(cfg, "r2_spans"))) { + for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { + char *id = (char *) switch_xml_attr(myspan, "id"); + char *name = (char *) switch_xml_attr(myspan, "name"); + zap_status_t zstatus = ZAP_FAIL; + + /* strings */ + const char *variant = "itu"; + const char *category = "national_subscriber"; + const char *logdir = "/usr/local/freeswitch/log/"; /* FIXME: get PREFIX variable */ + const char *logging = "notice,warning,error"; + const char *advanced_protocol_file = ""; + + /* booleans */ + int call_files = 0; + int get_ani_first = -1; + int immediate_accept = -1; + int double_answer = -1; + int skip_category = -1; + int forced_release = -1; + int charge_calls = -1; + + /* integers */ + int mfback_timeout = -1; + int metering_pulse_timeout = -1; + int allow_collect_calls = -1; + int max_ani = 10; + int max_dnis = 4; + + /* common non r2 stuff */ + const char *context = "default"; + const char *dialplan = "XML"; + char *dial_regex = NULL; + char *fail_dial_regex = NULL; + uint32_t span_id = 0; + zap_span_t *span = NULL; + + + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + /* string parameters */ + if (!strcasecmp(var, "variant")) { + variant = val; + } else if (!strcasecmp(var, "category")) { + category = val; + } else if (!strcasecmp(var, "logdir")) { + logdir = val; + } else if (!strcasecmp(var, "logging")) { + logging = val; + } else if (!strcasecmp(var, "advanced_protocol_file")) { + advanced_protocol_file = val; + + /* booleans */ + } else if (!strcasecmp(var, "allow_collect_calls")) { + allow_collect_calls = switch_true(val); + } else if (!strcasecmp(var, "immediate_accept")) { + immediate_accept = switch_true(val); + } else if (!strcasecmp(var, "double_answer")) { + double_answer = switch_true(val); + } else if (!strcasecmp(var, "skip_category")) { + skip_category = switch_true(var); + } else if (!strcasecmp(var, "forced_release")) { + forced_release = switch_true(val); + } else if (!strcasecmp(var, "charge_calls")) { + charge_calls = switch_true(val); + } else if (!strcasecmp(var, "get_ani_first")) { + get_ani_first = switch_true(val); + } else if (!strcasecmp(var, "call_files")) { + call_files = switch_true(val); + + /* integers */ + } else if (!strcasecmp(var, "mfback_timeout")) { + mfback_timeout = atoi(val); + } else if (!strcasecmp(var, "metering_pulse_timeout")) { + metering_pulse_timeout = atoi(val); + } else if (!strcasecmp(var, "max_ani")) { + max_ani = atoi(val); + } else if (!strcasecmp(var, "max_dnis")) { + max_dnis = atoi(val); + + /* common non r2 stuff */ + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "dial-regex")) { + dial_regex = val; + } else if (!strcasecmp(var, "fail-dial-regex")) { + fail_dial_regex = val; + } + } + + if (!id && !name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + continue; + } + + if (name) { + zstatus = zap_span_find_by_name(name, &span); + } else { + if (switch_is_number(id)) { + span_id = atoi(id); + zstatus = zap_span_find(span_id, &span); + } + + if (zstatus != ZAP_SUCCESS) { + zstatus = zap_span_find_by_name(id, &span); + } + } + + if (zstatus != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + continue; + } + + if (!span_id) { + span_id = span->span_id; + } + + if (zap_configure_span("r2", span, on_r2_signal, + "variant", variant, + "max_ani", max_ani, + "max_dnis", max_dnis, + "category", category, + "logdir", logdir, + "logging", logging, + "advanced_protocol_file", advanced_protocol_file, + "allow_collect_calls", allow_collect_calls, + "immediate_accept", immediate_accept, + "double_answer", double_answer, + "skip_category", skip_category, + "forced_release", forced_release, + "charge_calls", charge_calls, + "get_ani_first", get_ani_first, + "call_files", call_files, + "mfback_timeout", mfback_timeout, + "metering_pulse_timeout", metering_pulse_timeout, + TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error configuring R2 OpenZAP span %d, error: %s\n", + span_id, span->last_error); + continue; + } + + if (dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].dial_regex, dial_regex); + } + + if (fail_dial_regex) { + switch_set_string(SPAN_CONFIG[span->span_id].fail_dial_regex, fail_dial_regex); + } + + SPAN_CONFIG[span->span_id].span = span; + switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context)); + switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan)); + switch_copy_string(SPAN_CONFIG[span->span_id].type, "r2", sizeof(SPAN_CONFIG[span->span_id].type)); + + if (zap_span_start(span) == ZAP_FAIL) { + zap_log(ZAP_LOG_ERROR, "Error starting R2 OpenZAP span %d, error: %s\n", span_id, span->last_error); + continue; + } + } + } + + switch_xml_free(xml); + + return SWITCH_STATUS_SUCCESS; +} + + +void dump_chan(zap_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream) +{ + if (chan_id > span->chan_count) { + return; + } + + stream->write_function(stream, + "span_id: %u\n" + "chan_id: %u\n" + "physical_span_id: %u\n" + "physical_chan_id: %u\n" + "type: %s\n" + "state: %s\n" + "last_state: %s\n" + "txgain: %3.2f\n" + "rxgain: %3.2f\n" + "cid_date: %s\n" + "cid_name: %s\n" + "cid_num: %s\n" + "ani: %s\n" + "aniII: %s\n" + "dnis: %s\n" + "rdnis: %s\n" + "cause: %s\n\n", + span->channels[chan_id]->span_id, + span->channels[chan_id]->chan_id, + span->channels[chan_id]->physical_span_id, + span->channels[chan_id]->physical_chan_id, + zap_chan_type2str(span->channels[chan_id]->type), + zap_channel_state2str(span->channels[chan_id]->state), + zap_channel_state2str(span->channels[chan_id]->last_state), + span->channels[chan_id]->txgain, + span->channels[chan_id]->rxgain, + span->channels[chan_id]->caller_data.cid_date, + span->channels[chan_id]->caller_data.cid_name, + span->channels[chan_id]->caller_data.cid_num.digits, + span->channels[chan_id]->caller_data.ani.digits, + span->channels[chan_id]->caller_data.aniII, + span->channels[chan_id]->caller_data.dnis.digits, + span->channels[chan_id]->caller_data.rdnis.digits, + switch_channel_cause2str(span->channels[chan_id]->caller_data.hangup_cause) + ); +} + +void dump_chan_xml(zap_span_t *span, uint32_t chan_id, switch_stream_handle_t *stream) +{ + if (chan_id > span->chan_count) { + return; + } + + stream->write_function(stream, + " \n" + " %u\n" + " %u>\n" + " %u\n" + " %u\n" + " %s\n" + " %s\n" + " %s\n" + " %3.2f\n" + " %3.2f\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " \n", + span->channels[chan_id]->span_id, + span->channels[chan_id]->chan_id, + span->channels[chan_id]->physical_span_id, + span->channels[chan_id]->physical_chan_id, + zap_chan_type2str(span->channels[chan_id]->type), + zap_channel_state2str(span->channels[chan_id]->state), + zap_channel_state2str(span->channels[chan_id]->last_state), + span->channels[chan_id]->txgain, + span->channels[chan_id]->rxgain, + span->channels[chan_id]->caller_data.cid_date, + span->channels[chan_id]->caller_data.cid_name, + span->channels[chan_id]->caller_data.cid_num.digits, + span->channels[chan_id]->caller_data.ani.digits, + span->channels[chan_id]->caller_data.aniII, + span->channels[chan_id]->caller_data.dnis.digits, + span->channels[chan_id]->caller_data.rdnis.digits, + switch_channel_cause2str(span->channels[chan_id]->caller_data.hangup_cause) + ); +} + +#define OZ_SYNTAX "list || dump [] || q931_pcap on|off [pcapfilename without suffix] || gains []" +SWITCH_STANDARD_API(oz_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc) { + stream->write_function(stream, "%s", OZ_SYNTAX); + goto end; + } + + if (!strcasecmp(argv[0], "dump")) { + if (argc < 2) { + stream->write_function(stream, "-ERR Usage: oz dump []\n"); + goto end; + } else { + uint32_t chan_id = 0; + zap_span_t *span; + char *as = NULL; + + zap_span_find_by_name(argv[1], &span); + + if (argc > 2) { + if (argv[3] && !strcasecmp(argv[2], "as")) { + as = argv[3]; + } else { + chan_id = atoi(argv[2]); + } + } + + if (argv[4] && !strcasecmp(argv[3], "as")) { + as = argv[4]; + } + + if (!zstr(as) && !strcasecmp(as, "xml")) { + stream->write_function(stream, "\n"); + if (!span) { + stream->write_function(stream, "invalid span\n"); + } else { + if (chan_id) { + if(chan_id > span->chan_count) { + stream->write_function(stream, "invalid channel\n"); + } else { + dump_chan_xml(span, chan_id, stream); + } + } else { + uint32_t j; + for (j = 1; j <= span->chan_count; j++) { + dump_chan_xml(span, j, stream); + } + + } + } + stream->write_function(stream, "\n"); + } else { + if (!span) { + stream->write_function(stream, "-ERR invalid span\n"); + } else { + if (chan_id) { + if(chan_id > span->chan_count) { + stream->write_function(stream, "-ERR invalid channel\n"); + } else { + dump_chan(span, chan_id, stream); + } + } else { + uint32_t j; + + stream->write_function(stream, "+OK\n"); + for (j = 1; j <= span->chan_count; j++) { + dump_chan(span, j, stream); + } + + } + } + } + } + } else if (!strcasecmp(argv[0], "list")) { + int j; + for (j = 0 ; j < ZAP_MAX_SPANS_INTERFACE; j++) { + if (SPAN_CONFIG[j].span) { + const char *flags = "none"; + + if (SPAN_CONFIG[j].analog_options & ANALOG_OPTION_3WAY) { + flags = "3way"; + } else if (SPAN_CONFIG[j].analog_options & ANALOG_OPTION_CALL_SWAP) { + flags = "call swap"; + } + + stream->write_function(stream, + "+OK\n" + "span: %u (%s)\n" + "type: %s\n" + "chan_count: %u\n" + "dialplan: %s\n" + "context: %s\n" + "dial_regex: %s\n" + "fail_dial_regex: %s\n" + "hold_music: %s\n" + "analog_options %s\n", + j, + SPAN_CONFIG[j].span->name, + SPAN_CONFIG[j].type, + SPAN_CONFIG[j].span->chan_count, + SPAN_CONFIG[j].dialplan, + SPAN_CONFIG[j].context, + SPAN_CONFIG[j].dial_regex, + SPAN_CONFIG[j].fail_dial_regex, + SPAN_CONFIG[j].hold_music, + flags + ); + } + } + } else if (!strcasecmp(argv[0], "stop") || !strcasecmp(argv[0], "start")) { + char *span_name = argv[1]; + zap_span_t *span = NULL; + zap_status_t status; + + if (span_name) { + zap_span_find_by_name(span_name, &span); + } + + if (!span) { + stream->write_function(stream, "-ERR no span\n"); + goto end; + } + + if (!strcasecmp(argv[0], "stop")) { + status = zap_span_stop(span); + } else { + status = zap_span_start(span); + } + + stream->write_function(stream, status == ZAP_SUCCESS ? "+OK\n" : "-ERR failure\n"); + + goto end; + + /*Q931ToPcap enhancement*/ + } else if (!strcasecmp(argv[0], "q931_pcap")) { + int32_t span_id = 0; + zap_span_t *span; + const char *pcapfn = NULL; + char *tmp_path = NULL; + + if (argc < 3) { + stream->write_function(stream, "-ERR Usage: oz q931_pcap on|off [pcapfilename without suffix]\n"); + goto end; + } + span_id = atoi(argv[1]); + if (!(span_id && (span = SPAN_CONFIG[span_id].span))) { + stream->write_function(stream, "-ERR invalid span\n"); + goto end; + } + + /*Look for a given file name or use default file name*/ + if (argc > 3) { + if(argv[3]){ + pcapfn=argv[3]; + } + } + else { + pcapfn="q931"; + } + + /*Add log directory path to file name*/ + tmp_path=switch_mprintf("%s%s%s.pcap", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR, pcapfn); + + if(!strcasecmp(argv[2], "on")) { + if (zap_configure_span("isdn", span, on_clear_channel_signal, "q931topcap", 1, "pcapfilename", tmp_path, TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_WARNING, "Error couldn't (re-)enable Q931-To-Pcap!\n"); + goto end; + } else { + stream->write_function(stream, "+OK\n"); + } + } else if(!strcasecmp(argv[2], "off")) { + if (zap_configure_span("isdn", span, on_clear_channel_signal, "q931topcap", 0, TAG_END) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error couldn't enable Q931-To-Pcap!\n"); + goto end; + } else { + stream->write_function(stream, "+OK\n"); + } + } else { + stream->write_function(stream, "-ERR Usage: oz q931_pcap on|off [pcapfilename without suffix]\n"); + goto end; + } + + } else if (!strcasecmp(argv[0], "gains")) { + int i = 0; + float txgain = 0.0; + float rxgain = 0.0; + uint32_t chan_id = 0; + zap_span_t *span = NULL; + if (argc < 4) { + stream->write_function(stream, "-ERR Usage: oz gains []\n"); + goto end; + } + zap_span_find_by_name(argv[3], &span); + if (!span) { + stream->write_function(stream, "-ERR invalid span\n"); + goto end; + } + if (argc > 4) { + chan_id = atoi(argv[4]); + if (chan_id > span->chan_count) { + stream->write_function(stream, "-ERR invalid chan\n"); + goto end; + } + } + i = sscanf(argv[1], "%f", &rxgain); + i += sscanf(argv[2], "%f", &txgain); + if (i != 2) { + stream->write_function(stream, "-ERR invalid gains\n"); + goto end; + } + if (chan_id) { + zap_channel_command(span->channels[chan_id], ZAP_COMMAND_SET_RX_GAIN, &rxgain); + zap_channel_command(span->channels[chan_id], ZAP_COMMAND_SET_TX_GAIN, &txgain); + } else { + for (i = 1; i < (int)span->chan_count; i++) { + zap_channel_command(span->channels[i], ZAP_COMMAND_SET_RX_GAIN, &rxgain); + zap_channel_command(span->channels[i], ZAP_COMMAND_SET_TX_GAIN, &txgain); + } + } + stream->write_function(stream, "+OK gains set to Rx %f and Tx %f\n", rxgain, txgain); + } else { + char *rply = zap_api_execute(cmd, NULL); + + if (rply) { + stream->write_function(stream, "%s", rply); + free(rply); + } else { + stream->write_function(stream, "-ERR Usage: %s\n", OZ_SYNTAX); + } + } + /*Q931ToPcap enhancement done*/ + + end: + + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_STANDARD_APP(disable_ec_function) +{ + private_t *tech_pvt; + int x = 0; + + if (!switch_core_session_check_interface(session, openzap_endpoint_interface)) { + zap_log(ZAP_LOG_ERROR, "This application is only for OpenZAP channels.\n"); + return; + } + + tech_pvt = switch_core_session_get_private(session); + + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_LOSE_RACE); + return; + } + + zap_channel_command(tech_pvt->zchan, ZAP_COMMAND_DISABLE_ECHOCANCEL, &x); + zap_channel_command(tech_pvt->zchan, ZAP_COMMAND_DISABLE_ECHOTRAIN, &x); + zap_log(ZAP_LOG_INFO, "Echo Canceller Disabled\n"); +} + + +SWITCH_MODULE_LOAD_FUNCTION(mod_openzap_load) +{ + + switch_api_interface_t *commands_api_interface; + switch_application_interface_t *app_interface; + + module_pool = pool; + + zap_global_set_logger(zap_logger); + zap_cpu_monitor_disable(); + + if (zap_global_init() != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error loading OpenZAP\n"); + return SWITCH_STATUS_TERM; + } + + if (load_config() != SWITCH_STATUS_SUCCESS) { + zap_global_destroy(); + return SWITCH_STATUS_TERM; + } + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + openzap_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); + openzap_endpoint_interface->interface_name = "openzap"; + openzap_endpoint_interface->io_routines = &openzap_io_routines; + openzap_endpoint_interface->state_handler = &openzap_state_handlers; + + SWITCH_ADD_API(commands_api_interface, "oz", "OpenZAP commands", oz_function, OZ_SYNTAX); + + SWITCH_ADD_APP(app_interface, "disable_ec", "Disable Echo Canceller", "Disable Echo Canceller", disable_ec_function, "", SAF_NONE); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_openzap_shutdown) +{ + zap_global_destroy(); + + // this breaks pika but they are MIA so *shrug* + //return SWITCH_STATUS_NOUNLOAD; + return SWITCH_STATUS_SUCCESS; +} + +/* 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: + */ diff --git a/libs/openzap/msvc/openzap.2005.vcproj b/libs/openzap/msvc/openzap.2005.vcproj new file mode 100644 index 0000000000..59419ea8e9 --- /dev/null +++ b/libs/openzap/msvc/openzap.2005.vcproj @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/msvc/openzap.2008.vcproj b/libs/openzap/msvc/openzap.2008.vcproj new file mode 100644 index 0000000000..4a7531e3c4 --- /dev/null +++ b/libs/openzap/msvc/openzap.2008.vcproj @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/msvc/testanalog/testanalog.2005.vcproj b/libs/openzap/msvc/testanalog/testanalog.2005.vcproj new file mode 100644 index 0000000000..93553d2a5f --- /dev/null +++ b/libs/openzap/msvc/testanalog/testanalog.2005.vcproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/msvc/testanalog/testanalog.2008.vcproj b/libs/openzap/msvc/testanalog/testanalog.2008.vcproj new file mode 100644 index 0000000000..120eb5fd75 --- /dev/null +++ b/libs/openzap/msvc/testanalog/testanalog.2008.vcproj @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/msvc/testisdn/testisdn.2005.vcproj b/libs/openzap/msvc/testisdn/testisdn.2005.vcproj new file mode 100644 index 0000000000..133557166b --- /dev/null +++ b/libs/openzap/msvc/testisdn/testisdn.2005.vcproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/msvc/testisdn/testisdn.2008.vcproj b/libs/openzap/msvc/testisdn/testisdn.2008.vcproj new file mode 100644 index 0000000000..e350a45edf --- /dev/null +++ b/libs/openzap/msvc/testisdn/testisdn.2008.vcproj @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/openzap.2005.sln b/libs/openzap/openzap.2005.sln new file mode 100644 index 0000000000..c9f666b381 --- /dev/null +++ b/libs/openzap/openzap.2005.sln @@ -0,0 +1,82 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openzap", "msvc\openzap.2005.vcproj", "{93B8812C-3EC4-4F78-8970-FFBFC99E167D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testanalog", "msvc\testanalog\testanalog.2005.vcproj", "{BB833648-BAFF-4BE2-94DB-F8BB043C588C}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testisdn", "msvc\testisdn\testisdn.2005.vcproj", "{6DA6FD42-641D-4147-92F5-3BC4AAA6589B}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_openzap", "mod_openzap\mod_openzap.2005.vcproj", "{FE3540C5-3303-46E0-A69E-D92F775687F1}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_analog", "src\ozmod\ozmod_analog\ozmod_analog.2005.vcproj", "{37C94798-6E33-4B4F-8EE0-C72A7DC91157}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_analog_em", "src\ozmod\ozmod_analog_em\ozmod_analog_em.2005.vcproj", "{C539D7C8-26A8-4A94-B938-77672165C130}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_isdn", "src\ozmod\ozmod_isdn\ozmod_isdn.2005.vcproj", "{729344A5-D5E9-434D-8EE8-AF8C6C795D15}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_wanpipe", "src\ozmod\ozmod_wanpipe\ozmod_wanpipe.2005.vcproj", "{1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_pika", "src\ozmod\ozmod_pika\ozmod_pika.2005.vcproj", "{E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|Win32.ActiveCfg = Debug|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|Win32.Build.0 = Debug|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|Win32.ActiveCfg = Release|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|Win32.Build.0 = Release|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|Win32.ActiveCfg = Debug|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|Win32.Build.0 = Debug|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|Win32.ActiveCfg = Release|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|Win32.Build.0 = Release|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|Win32.Build.0 = Debug|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|Win32.ActiveCfg = Release|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|Win32.Build.0 = Release|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|Win32.ActiveCfg = Debug|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|Win32.Build.0 = Debug|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|Win32.ActiveCfg = Release|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|Win32.Build.0 = Release|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|Win32.ActiveCfg = Debug|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|Win32.Build.0 = Debug|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|Win32.ActiveCfg = Release|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|Win32.Build.0 = Release|Win32 + {C539D7C8-26A8-4A94-B938-77672165C130}.Debug|Win32.ActiveCfg = Debug|Win32 + {C539D7C8-26A8-4A94-B938-77672165C130}.Debug|Win32.Build.0 = Debug|Win32 + {C539D7C8-26A8-4A94-B938-77672165C130}.Release|Win32.ActiveCfg = Release|Win32 + {C539D7C8-26A8-4A94-B938-77672165C130}.Release|Win32.Build.0 = Release|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|Win32.ActiveCfg = Debug|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|Win32.Build.0 = Debug|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|Win32.ActiveCfg = Release|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|Win32.Build.0 = Release|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Debug|Win32.ActiveCfg = Debug|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Release|Win32.ActiveCfg = Release|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Debug|Win32.ActiveCfg = Debug|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Release|Win32.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/libs/openzap/openzap.2008.sln b/libs/openzap/openzap.2008.sln new file mode 100644 index 0000000000..7867bc5939 --- /dev/null +++ b/libs/openzap/openzap.2008.sln @@ -0,0 +1,119 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openzap", "msvc\openzap.2008.vcproj", "{93B8812C-3EC4-4F78-8970-FFBFC99E167D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testanalog", "msvc\testanalog\testanalog.2008.vcproj", "{BB833648-BAFF-4BE2-94DB-F8BB043C588C}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testisdn", "msvc\testisdn\testisdn.2008.vcproj", "{6DA6FD42-641D-4147-92F5-3BC4AAA6589B}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_openzap", "mod_openzap\mod_openzap.2008.vcproj", "{FE3540C5-3303-46E0-A69E-D92F775687F1}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_analog", "src\ozmod\ozmod_analog\ozmod_analog.2008.vcproj", "{37C94798-6E33-4B4F-8EE0-C72A7DC91157}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_analog_em", "src\ozmod\ozmod_analog_em\ozmod_analog_em.2008.vcproj", "{B3F49375-2834-4937-9D8C-4AC2EC911010}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_isdn", "src\ozmod\ozmod_isdn\ozmod_isdn.2008.vcproj", "{729344A5-D5E9-434D-8EE8-AF8C6C795D15}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_pika", "src\ozmod\ozmod_pika\ozmod_pika.2008.vcproj", "{E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}" + ProjectSection(ProjectDependencies) = postProject + {93B8812C-3EC4-4F78-8970-FFBFC99E167D} = {93B8812C-3EC4-4F78-8970-FFBFC99E167D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ozmod_wanpipe", "src\ozmod\ozmod_wanpipe\ozmod_wanpipe.2008.vcproj", "{1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|Win32.ActiveCfg = Debug|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|Win32.Build.0 = Debug|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|x64.ActiveCfg = Debug|x64 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Debug|x64.Build.0 = Debug|x64 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|Win32.ActiveCfg = Release|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|Win32.Build.0 = Release|Win32 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|x64.ActiveCfg = Release|x64 + {93B8812C-3EC4-4F78-8970-FFBFC99E167D}.Release|x64.Build.0 = Release|x64 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|Win32.ActiveCfg = Debug|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|Win32.Build.0 = Debug|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|x64.ActiveCfg = Debug|x64 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Debug|x64.Build.0 = Debug|x64 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|Win32.ActiveCfg = Release|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|Win32.Build.0 = Release|Win32 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|x64.ActiveCfg = Release|x64 + {BB833648-BAFF-4BE2-94DB-F8BB043C588C}.Release|x64.Build.0 = Release|x64 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|Win32.Build.0 = Debug|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|x64.ActiveCfg = Debug|x64 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Debug|x64.Build.0 = Debug|x64 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|Win32.ActiveCfg = Release|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|Win32.Build.0 = Release|Win32 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|x64.ActiveCfg = Release|x64 + {6DA6FD42-641D-4147-92F5-3BC4AAA6589B}.Release|x64.Build.0 = Release|x64 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|Win32.ActiveCfg = Debug|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|Win32.Build.0 = Debug|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|x64.ActiveCfg = Debug|x64 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Debug|x64.Build.0 = Debug|x64 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|Win32.ActiveCfg = Release|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|Win32.Build.0 = Release|Win32 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|x64.ActiveCfg = Release|x64 + {FE3540C5-3303-46E0-A69E-D92F775687F1}.Release|x64.Build.0 = Release|x64 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|Win32.ActiveCfg = Debug|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|Win32.Build.0 = Debug|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|x64.ActiveCfg = Debug|x64 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Debug|x64.Build.0 = Debug|x64 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|Win32.ActiveCfg = Release|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|Win32.Build.0 = Release|Win32 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|x64.ActiveCfg = Release|x64 + {37C94798-6E33-4B4F-8EE0-C72A7DC91157}.Release|x64.Build.0 = Release|x64 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Debug|Win32.ActiveCfg = Debug|Win32 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Debug|Win32.Build.0 = Debug|Win32 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Debug|x64.ActiveCfg = Debug|x64 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Debug|x64.Build.0 = Debug|x64 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Release|Win32.ActiveCfg = Release|Win32 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Release|Win32.Build.0 = Release|Win32 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Release|x64.ActiveCfg = Release|x64 + {B3F49375-2834-4937-9D8C-4AC2EC911010}.Release|x64.Build.0 = Release|x64 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|Win32.ActiveCfg = Debug|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|Win32.Build.0 = Debug|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|x64.ActiveCfg = Debug|x64 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Debug|x64.Build.0 = Debug|x64 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|Win32.ActiveCfg = Release|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|Win32.Build.0 = Release|Win32 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|x64.ActiveCfg = Release|x64 + {729344A5-D5E9-434D-8EE8-AF8C6C795D15}.Release|x64.Build.0 = Release|x64 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Debug|Win32.ActiveCfg = Debug|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Debug|x64.ActiveCfg = Debug|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Release|Win32.ActiveCfg = Release|Win32 + {E886B4D5-AB4F-4092-B8F4-3B06E1E462EF}.Release|x64.ActiveCfg = Release|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Debug|Win32.ActiveCfg = Debug|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Debug|x64.ActiveCfg = Debug|x64 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Release|Win32.ActiveCfg = Release|Win32 + {1A145EE9-BBD8-45E5-98CD-EB4BE99E1DCD}.Release|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/libs/openzap/openzap.pc.in b/libs/openzap/openzap.pc.in new file mode 100644 index 0000000000..082ed5c8f6 --- /dev/null +++ b/libs/openzap/openzap.pc.in @@ -0,0 +1,17 @@ +# +# OpenZAP pkg-config file +# +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: OpenZAP +Description: +Version: @PACKAGE_VERSION@ +URL: http://www.openzap.org/ +Requires: +Conflicts: +Libs: -L${libdir} -lopenzap +Libs.private: -lm +Cflags: -I${includedir} diff --git a/libs/openzap/patches/oz.diff b/libs/openzap/patches/oz.diff new file mode 100644 index 0000000000..2e9c558b7c --- /dev/null +++ b/libs/openzap/patches/oz.diff @@ -0,0 +1,134 @@ +Index: src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c +=================================================================== +--- src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c (revision 745) ++++ src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c (working copy) +@@ -98,19 +98,21 @@ + * so we can have one analong handler thread that will deal with all the idle analog channels for events + * the alternative would be for the driver to provide one socket for all of the oob events for all analog channels + */ +-static __inline__ int tdmv_api_wait_socket(sng_fd_t fd, int timeout, int *flags) ++static __inline__ int tdmv_api_wait_socket(zap_channel_t *zchan, int timeout, int *flags) + { + + #ifdef LIBSANGOMA_VERSION + int err; +- sangoma_wait_obj_t sangoma_wait_obj; ++ sangoma_wait_obj_t *sangoma_wait_obj = zchan->mod_data; + +- sangoma_init_wait_obj(&sangoma_wait_obj, fd, 1, 1, *flags, SANGOMA_WAIT_OBJ); ++ sangoma_init_wait_obj(sangoma_wait_obj, zchan->sockfd, 1, 1, 0, SANGOMA_WAIT_OBJ); + +- err=sangoma_socket_waitfor_many(&sangoma_wait_obj,1 , timeout); ++ err = sangoma_socket_waitfor_many(&sangoma_wait_obj, 1, timeout); ++ + if (err > 0) { +- *flags=sangoma_wait_obj.flags_out; ++ *flags = sangoma_wait_obj.flags_out; + } ++ + return err; + + #else +@@ -118,7 +120,7 @@ + int res; + + memset(&pfds[0], 0, sizeof(pfds[0])); +- pfds[0].fd = fd; ++ pfds[0].fd = zchan->sockfd; + pfds[0].events = *flags; + res = poll(pfds, 1, timeout); + *flags = 0; +@@ -200,6 +202,15 @@ + + if (sockfd != WP_INVALID_SOCKET && zap_span_add_channel(span, sockfd, type, &chan) == ZAP_SUCCESS) { + wanpipe_tdm_api_t tdm_api; ++#ifdef LIBSANGOMA_VERSION ++ sangoma_wait_obj_t *sangoma_wait_obj; ++ ++ sangoma_wait_obj = malloc(sizeof(*sangoma_wait_obj)); ++ memset(sangoma_wait_obj, 0, sizeof(*sangoma_wait_obj)); ++ sangoma_init_wait_obj(sangoma_wait_obj, sockfd, 1, 1, 0, SANGOMA_WAIT_OBJ); ++ chan->mod_data = sangoma_wait_obj; ++#endif ++ + memset(&tdm_api,0,sizeof(tdm_api)); + + chan->physical_span_id = spanno; +@@ -211,7 +222,7 @@ + + dtmf = "software"; + +- /* FIXME: Handle Error Conditino Check for return code */ ++ /* FIXME: Handle Error Condition Check for return code */ + err= sangoma_tdm_get_hw_coding(chan->sockfd, &tdm_api); + + if (tdm_api.wp_tdm_cmd.hw_tdm_coding) { +@@ -606,7 +617,7 @@ + inflags |= POLLPRI; + } + +- result = tdmv_api_wait_socket(zchan->sockfd, to, &inflags); ++ result = tdmv_api_wait_socket(zchan, to, &inflags); + + *flags = ZAP_NO_FLAGS; + +@@ -643,26 +654,30 @@ + ZIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event) + { + #ifdef LIBSANGOMA_VERSION +- sangoma_wait_obj_t pfds[ZAP_MAX_CHANNELS_SPAN]; ++ sangoma_wait_obj_t *pfds[ZAP_MAX_CHANNELS_SPAN] = { 0 }; + #else + struct pollfd pfds[ZAP_MAX_CHANNELS_SPAN]; + #endif + + uint32_t i, j = 0, k = 0, l = 0; +- int objects=0; + int r; + + for(i = 1; i <= span->chan_count; i++) { + zap_channel_t *zchan = span->channels[i]; + ++ + #ifdef LIBSANGOMA_VERSION +- sangoma_init_wait_obj(&pfds[j], zchan->sockfd , 1, 1, POLLPRI, SANGOMA_WAIT_OBJ); ++ if (!zchan->mod_data) { ++ continue; ++ } ++ pfds[j] = zchan->mod_data; ++ + #else + memset(&pfds[j], 0, sizeof(pfds[j])); + pfds[j].fd = span->channels[i]->sockfd; + pfds[j].events = POLLPRI; + #endif +- objects++; ++ + /* The driver probably should be able to do this wink/flash/ringing by itself this is sort of a hack to make it work! */ + + if (zap_test_flag(zchan, ZAP_CHANNEL_WINK) || zap_test_flag(zchan, ZAP_CHANNEL_FLASH)) { +@@ -703,7 +718,7 @@ + ms = l; + } + #ifdef LIBSANGOMA_VERSION +- r = sangoma_socket_waitfor_many(pfds,objects,ms); ++ r = sangoma_socket_waitfor_many(pfds, j, ms); + #else + r = poll(pfds, j, ms); + #endif +@@ -935,6 +950,15 @@ + */ + static ZIO_CHANNEL_DESTROY_FUNCTION(wanpipe_channel_destroy) + { ++ sangoma_wait_obj_t *sangoma_wait_obj; ++ ++ if (zchan->mod_data) { ++ sangoma_wait_obj = zchan->mod_data; ++ zchan->mod_data = NULL; ++ sangoma_release_wait_obj(sangoma_wait_obj); ++ free(sangoma_wait_obj); ++ } ++ + if (zchan->sockfd > -1) { + close(zchan->sockfd); + zchan->sockfd = WP_INVALID_SOCKET; diff --git a/libs/openzap/src/detect_dtmf.c b/libs/openzap/src/detect_dtmf.c new file mode 100644 index 0000000000..f73586adcb --- /dev/null +++ b/libs/openzap/src/detect_dtmf.c @@ -0,0 +1,33 @@ +//#include "openzap.h" +#include "libteletone_detect.h" + +int main(int argc, char *argv[]) +{ + int fd, b; + short sln[512] = {0}; + teletone_dtmf_detect_state_t dtmf_detect = {0}; + char digit_str[128] = ""; + + if (argc < 2) { + fprintf(stderr, "Arg Error!\n"); + exit(-1); + } + + teletone_dtmf_detect_init (&dtmf_detect, 8000); + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "File Error! [%s]\n", strerror(errno)); + exit(-1); + } + + while((b = read(fd, sln, 320)) > 0) { + teletone_dtmf_detect(&dtmf_detect, sln, b / 2); + teletone_dtmf_get(&dtmf_detect, digit_str, sizeof(digit_str)); + if (*digit_str) { + printf("digit: %s\n", digit_str); + } + } + close(fd); + return 0; +} + diff --git a/libs/openzap/src/detect_tones.c b/libs/openzap/src/detect_tones.c new file mode 100644 index 0000000000..580f6a9d17 --- /dev/null +++ b/libs/openzap/src/detect_tones.c @@ -0,0 +1,32 @@ +//#include "openzap.h" +#include "libteletone_detect.h" + +int main(int argc, char *argv[]) +{ + teletone_multi_tone_t mt = {0}; + teletone_tone_map_t map = {{0}}; + + int fd, b; + short sln[512] = {0}; + + if (argc < 2) { + fprintf(stderr, "Arg Error!\n"); + exit(-1); + } + + map.freqs[0] = atof("350"); + map.freqs[1] = atof("440"); + teletone_multi_tone_init(&mt, &map); + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "File Error! [%s]\n", strerror(errno)); + exit(-1); + } + + while((b = read(fd, sln, 320)) > 0) { + printf("TEST %d %d\n", b, teletone_multi_tone_detect(&mt, sln, b / 2)); + } + close(fd); + return 0; +} + diff --git a/libs/openzap/src/fsk.c b/libs/openzap/src/fsk.c new file mode 100644 index 0000000000..773b0deaae --- /dev/null +++ b/libs/openzap/src/fsk.c @@ -0,0 +1,351 @@ + +/* + * bell202.c + * + * Copyright (c) 2005 Robert Krten. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This module contains a Bell-202 1200-baud FSK decoder, suitable for + * use in a library. The general style of the library calls is modeled + * after the POSIX pthread_*() functions. + * + * 2005 03 20 R. Krten created +*/ + +#include +#include +#include +#include +#include + +#include "fsk.h" +#include "uart.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +fsk_modem_definition_t fsk_modem_definitions[] = +{ + { /* FSK_V23_FORWARD_MODE1 */ 1700, 1300, 600 }, + { /* FSK_V23_FORWARD_MODE2 */ 2100, 1300, 1200 }, + { /* FSK_V23_BACKWARD */ 450, 390, 75 }, + { /* FSK_BELL202 */ 2200, 1200, 1200 }, +}; + +/* + * dsp_fsk_attr_init + * + * Initializes the attributes structure; this must be done before the + * attributes structure is used. +*/ + +void dsp_fsk_attr_init (dsp_fsk_attr_t *attr) +{ + memset(attr, 0, sizeof(*attr)); +} + +/* + * dsp_fsk_attr_get_bithandler + * dsp_fsk_attr_set_bithandler + * dsp_fsk_attr_get_bytehandler + * dsp_fsk_attr_set_bytehandler + * dsp_fsk_attr_getsamplerate + * dsp_fsk_attr_setsamplerate + * + * These functions get and set their respective elements from the + * attributes structure. If an error code is returned, it is just + * zero == ok, -1 == fail. +*/ + +bithandler_func_t dsp_fsk_attr_get_bithandler(dsp_fsk_attr_t *attr, void **bithandler_arg) +{ + *bithandler_arg = attr->bithandler_arg; + return attr->bithandler; +} + +void dsp_fsk_attr_set_bithandler(dsp_fsk_attr_t *attr, bithandler_func_t bithandler, void *bithandler_arg) +{ + attr->bithandler = bithandler; + attr->bithandler_arg = bithandler_arg; +} + +bytehandler_func_t dsp_fsk_attr_get_bytehandler(dsp_fsk_attr_t *attr, void **bytehandler_arg) +{ + *bytehandler_arg = attr->bytehandler_arg; + return attr->bytehandler; +} + +void dsp_fsk_attr_set_bytehandler(dsp_fsk_attr_t *attr, bytehandler_func_t bytehandler, void *bytehandler_arg) +{ + attr->bytehandler = bytehandler; + attr->bytehandler_arg = bytehandler_arg; +} + +int dsp_fsk_attr_get_samplerate (dsp_fsk_attr_t *attr) +{ + return attr->sample_rate; +} + +int dsp_fsk_attr_set_samplerate (dsp_fsk_attr_t *attr, int samplerate) +{ + if (samplerate <= 0) { + return -1; + } + attr->sample_rate = samplerate; + return 0; +} + +/* + * dsp_fsk_create + * + * Creates a handle for subsequent use. The handle is created to contain + * a context data structure for use by the sample handler function. The + * function expects an initialized attributes structure, and returns the + * handle or a NULL if there were errors. + * + * Once created, the handle can be used until it is destroyed. +*/ + +dsp_fsk_handle_t *dsp_fsk_create(dsp_fsk_attr_t *attr) +{ + int i; + double phi_mark, phi_space; + dsp_fsk_handle_t *handle; + + handle = malloc(sizeof(*handle)); + if (!handle) { + return NULL; + } + + memset(handle, 0, sizeof(*handle)); + + /* fill the attributes member */ + memcpy(&handle->attr, attr, sizeof(*attr)); + + /* see if we can do downsampling. We only really need 6 samples to "match" */ + if (attr->sample_rate / fsk_modem_definitions[FSK_BELL202].freq_mark > 6) { + handle->downsampling_count = attr->sample_rate / fsk_modem_definitions[FSK_BELL202].freq_mark / 6; + } else { + handle->downsampling_count = 1; + } + handle->current_downsample = 1; + + /* calculate the correlate size (number of samples required for slowest wave) */ + handle->corrsize = attr->sample_rate / handle->downsampling_count / fsk_modem_definitions[FSK_BELL202].freq_mark; + + /* allocate the correlation sin/cos arrays and initialize */ + for (i = 0; i < 4; i++) { + handle->correlates[i] = malloc(sizeof(double) * handle->corrsize); + if (handle->correlates[i] == NULL) { + /* some failed, back out memory allocations */ + dsp_fsk_destroy(&handle); + return NULL; + } + } + + /* now initialize them */ + phi_mark = 2. * M_PI / ((double) attr->sample_rate / (double) handle->downsampling_count / (double) fsk_modem_definitions[FSK_BELL202].freq_mark); + phi_space = 2. * M_PI / ((double) attr->sample_rate / (double) handle->downsampling_count / (double) fsk_modem_definitions[FSK_BELL202].freq_space); + + for (i = 0; i < handle->corrsize; i++) { + handle->correlates[0][i] = sin(phi_mark * (double) i); + handle->correlates[1][i] = cos(phi_mark * (double) i); + handle->correlates[2][i] = sin(phi_space * (double) i); + handle->correlates[3][i] = cos(phi_space * (double) i); + } + + /* initialize the ring buffer */ + handle->buffer = malloc(sizeof(double) * handle->corrsize); + if (!handle->buffer) { /* failed; back out memory allocations */ + dsp_fsk_destroy(&handle); + return NULL; + } + memset(handle->buffer, 0, sizeof(double) * handle->corrsize); + handle->ringstart = 0; + + /* initalize intra-cell position */ + handle->cellpos = 0; + handle->celladj = fsk_modem_definitions[FSK_BELL202].baud_rate / (double) attr->sample_rate * (double) handle->downsampling_count; + + /* if they have provided a byte handler, add a UART to the processing chain */ + if (handle->attr.bytehandler) { + dsp_uart_attr_t uart_attr; + dsp_uart_handle_t *uart_handle; + + dsp_uart_attr_init(&uart_attr); + dsp_uart_attr_set_bytehandler(&uart_attr, handle->attr.bytehandler, handle->attr.bytehandler_arg); + uart_handle = dsp_uart_create(&uart_attr); + if (uart_handle == NULL) { + dsp_fsk_destroy(&handle); + return NULL; + } + handle->attr.bithandler = dsp_uart_bit_handler; + handle->attr.bithandler_arg = uart_handle; + } + + return handle; +} + +/* + * dsp_fsk_destroy + * + * Destroys a handle, releasing any associated memory. Sets handle pointer to NULL + * so A destroyed handle can not be used for anything after the destroy. +*/ + +void dsp_fsk_destroy(dsp_fsk_handle_t **handle) +{ + int i; + + /* if empty handle, just return */ + if (*handle == NULL) { + return; + } + + for (i = 0; i < 4; i++) { + if ((*handle)->correlates[i] != NULL) { + free((*handle)->correlates[i]); + (*handle)->correlates[i] = NULL; + } + } + + if ((*handle)->buffer != NULL) { + free((*handle)->buffer); + (*handle)->buffer = NULL; + } + + if ((*handle)->attr.bytehandler) { + dsp_uart_handle_t** dhandle = (void *)(&(*handle)->attr.bithandler_arg); + dsp_uart_destroy(dhandle); + } + + free(*handle); + *handle = NULL; +} + +/* + * dsp_fsk_sample + * + * This is the main processing entry point. The function accepts a normalized + * sample (i.e., one whose range is between -1 and +1). The function performs + * the Bell-202 FSK modem decode processing, and, if it detects a valid bit, + * will call the bithandler associated with the attributes structure. + * + * For the Bell-202 standard, a logical zero (space) is 2200 Hz, and a logical + * one (mark) is 1200 Hz. +*/ + +void +dsp_fsk_sample (dsp_fsk_handle_t *handle, double normalized_sample) +{ + double val; + double factors[4]; + int i, j; + + /* if we can avoid processing samples, do so */ + if (handle->downsampling_count != 1) { + if (handle->current_downsample < handle->downsampling_count) { + handle->current_downsample++; + return; /* throw this sample out */ + } + handle->current_downsample = 1; + } + + /* store sample in buffer */ + handle->buffer[handle->ringstart++] = normalized_sample; + if (handle->ringstart >= handle->corrsize) { + handle->ringstart = 0; + } + + /* do the correlation calculation */ + factors[0] = factors[1] = factors[2] = factors[3] = 0; /* clear out intermediate sums */ + j = handle->ringstart; + for (i = 0; i < handle->corrsize; i++) { + if (j >= handle->corrsize) { + j = 0; + } + val = handle->buffer[j]; + factors[0] += handle->correlates[0][i] * val; + factors[1] += handle->correlates[1][i] * val; + factors[2] += handle->correlates[2][i] * val; + factors[3] += handle->correlates[3][i] * val; + j++; + } + + /* store the bit (bit value is comparison of the two sets of correlate factors) */ + handle->previous_bit = handle->current_bit; + handle->current_bit = (factors[0] * factors[0] + factors[1] * factors[1] > factors[2] * factors[2] + factors[3] * factors[3]); + + /* if there's a transition, we can synchronize the cell position */ + if (handle->previous_bit != handle->current_bit) { + handle->cellpos = 0.5; /* adjust cell position to be in the middle of the cell */ + } + handle->cellpos += handle->celladj; /* walk the cell along */ + + if (handle->cellpos > 1.0) { + handle->cellpos -= 1.0; + + switch (handle->state) { + case FSK_STATE_DATA: + { + + (*handle->attr.bithandler) (handle->attr.bithandler_arg, handle->current_bit); + } + break; + case FSK_STATE_CHANSEIZE: + { + + if (handle->last_bit != handle->current_bit) { + handle->conscutive_state_bits++; + } else { + handle->conscutive_state_bits = 0; + } + + if (handle->conscutive_state_bits > 15) { + handle->state = FSK_STATE_CARRIERSIG; + handle->conscutive_state_bits = 0; + } + } + break; + case FSK_STATE_CARRIERSIG: + { + if (handle->current_bit) { + handle->conscutive_state_bits++; + } else { + handle->conscutive_state_bits = 0; + } + + if (handle->conscutive_state_bits > 15) { + handle->state = FSK_STATE_DATA; + handle->conscutive_state_bits = 0; + } + } + break; + } + + handle->last_bit = handle->current_bit; + } +} + diff --git a/libs/openzap/src/g711.c b/libs/openzap/src/g711.c new file mode 100644 index 0000000000..37511c3a9c --- /dev/null +++ b/libs/openzap/src/g711.c @@ -0,0 +1,104 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g711.c - A-law and u-law transcoding routines + * + * Written by Steve Underwood + * + * Copyright (C) 2006 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * $Id: g711.c,v 1.1 2006/06/07 15:46:39 steveu Exp $ + */ + +/*! \file */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#ifdef HAVE_TGMATH_H +#include +#endif +#endif + +#include "g711.h" + +/* Copied from the CCITT G.711 specification */ +static const uint8_t ulaw_to_alaw_table[256] = + { + 42, 43, 40, 41, 46, 47, 44, 45, 34, 35, 32, 33, 38, 39, 36, 37, + 58, 59, 56, 57, 62, 63, 60, 61, 50, 51, 48, 49, 54, 55, 52, 53, + 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 26, + 27, 24, 25, 30, 31, 28, 29, 18, 19, 16, 17, 22, 23, 20, 21, 106, + 104, 105, 110, 111, 108, 109, 98, 99, 96, 97, 102, 103, 100, 101, 122, 120, + 126, 127, 124, 125, 114, 115, 112, 113, 118, 119, 116, 117, 75, 73, 79, 77, + 66, 67, 64, 65, 70, 71, 68, 69, 90, 91, 88, 89, 94, 95, 92, 93, + 82, 82, 83, 83, 80, 80, 81, 81, 86, 86, 87, 87, 84, 84, 85, 85, + 170, 171, 168, 169, 174, 175, 172, 173, 162, 163, 160, 161, 166, 167, 164, 165, + 186, 187, 184, 185, 190, 191, 188, 189, 178, 179, 176, 177, 182, 183, 180, 181, + 138, 139, 136, 137, 142, 143, 140, 141, 130, 131, 128, 129, 134, 135, 132, 154, + 155, 152, 153, 158, 159, 156, 157, 146, 147, 144, 145, 150, 151, 148, 149, 234, + 232, 233, 238, 239, 236, 237, 226, 227, 224, 225, 230, 231, 228, 229, 250, 248, + 254, 255, 252, 253, 242, 243, 240, 241, 246, 247, 244, 245, 203, 201, 207, 205, + 194, 195, 192, 193, 198, 199, 196, 197, 218, 219, 216, 217, 222, 223, 220, 221, + 210, 210, 211, 211, 208, 208, 209, 209, 214, 214, 215, 215, 212, 212, 213, 213 + }; + +/* These transcoding tables are copied from the CCITT G.711 specification. To achieve + optimal results, do not change them. */ + +static const uint8_t alaw_to_ulaw_table[256] = + { + 42, 43, 40, 41, 46, 47, 44, 45, 34, 35, 32, 33, 38, 39, 36, 37, + 57, 58, 55, 56, 61, 62, 59, 60, 49, 50, 47, 48, 53, 54, 51, 52, + 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 5, + 26, 27, 24, 25, 30, 31, 28, 29, 18, 19, 16, 17, 22, 23, 20, 21, + 98, 99, 96, 97, 102, 103, 100, 101, 93, 93, 92, 92, 95, 95, 94, 94, + 116, 118, 112, 114, 124, 126, 120, 122, 106, 107, 104, 105, 110, 111, 108, 109, + 72, 73, 70, 71, 76, 77, 74, 75, 64, 65, 63, 63, 68, 69, 66, 67, + 86, 87, 84, 85, 90, 91, 88, 89, 79, 79, 78, 78, 82, 83, 80, 81, + 170, 171, 168, 169, 174, 175, 172, 173, 162, 163, 160, 161, 166, 167, 164, 165, + 185, 186, 183, 184, 189, 190, 187, 188, 177, 178, 175, 176, 181, 182, 179, 180, + 138, 139, 136, 137, 142, 143, 140, 141, 130, 131, 128, 129, 134, 135, 132, 133, + 154, 155, 152, 153, 158, 159, 156, 157, 146, 147, 144, 145, 150, 151, 148, 149, + 226, 227, 224, 225, 230, 231, 228, 229, 221, 221, 220, 220, 223, 223, 222, 222, + 244, 246, 240, 242, 252, 254, 248, 250, 234, 235, 232, 233, 238, 239, 236, 237, + 200, 201, 198, 199, 204, 205, 202, 203, 192, 193, 191, 191, 196, 197, 194, 195, + 214, 215, 212, 213, 218, 219, 216, 217, 207, 207, 206, 206, 210, 211, 208, 209 + }; + +uint8_t alaw_to_ulaw(uint8_t alaw) +{ + return alaw_to_ulaw_table[alaw]; +} +/*- End of function --------------------------------------------------------*/ + +uint8_t ulaw_to_alaw(uint8_t ulaw) +{ + return ulaw_to_alaw_table[ulaw]; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ + +/* 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: + */ + diff --git a/libs/openzap/src/hashtable.c b/libs/openzap/src/hashtable.c new file mode 100644 index 0000000000..eeb2aaa07a --- /dev/null +++ b/libs/openzap/src/hashtable.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2002, Christopher Clark + * 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 "hashtable.h" +#include "hashtable_private.h" +#include +#include +#include +#include + +/* + Credit for primes table: Aaron Krowne + http://br.endernet.org/~akrowne/ + http://planetmath.org/encyclopedia/GoodHashTablePrimes.html +*/ +static const unsigned int primes[] = { + 53, 97, 193, 389, + 769, 1543, 3079, 6151, + 12289, 24593, 49157, 98317, + 196613, 393241, 786433, 1572869, + 3145739, 6291469, 12582917, 25165843, + 50331653, 100663319, 201326611, 402653189, + 805306457, 1610612741 +}; +const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]); +const float max_load_factor = 0.65f; + +/*****************************************************************************/ +OZ_DECLARE(struct hashtable *) +create_hashtable(unsigned int minsize, + unsigned int (*hashf) (void*), + int (*eqf) (void*,void*)) +{ + struct hashtable *h; + unsigned int pindex, size = primes[0]; + /* Check requested hashtable isn't too large */ + if (minsize > (1u << 30)) return NULL; + /* Enforce size as prime */ + for (pindex=0; pindex < prime_table_length; pindex++) { + if (primes[pindex] > minsize) { size = primes[pindex]; break; } + } + h = (struct hashtable *)malloc(sizeof(struct hashtable)); + if (NULL == h) return NULL; /*oom*/ + h->table = (struct entry **)malloc(sizeof(struct entry*) * size); + if (NULL == h->table) { free(h); return NULL; } /*oom*/ + memset(h->table, 0, size * sizeof(struct entry *)); + h->tablelength = size; + h->primeindex = pindex; + h->entrycount = 0; + h->hashfn = hashf; + h->eqfn = eqf; + h->loadlimit = (unsigned int) ceil(size * max_load_factor); + return h; +} + +/*****************************************************************************/ +unsigned int +hash(struct hashtable *h, void *k) +{ + /* Aim to protect against poor hash functions by adding logic here + * - logic taken from java 1.4 hashtable source */ + unsigned int i = h->hashfn(k); + i += ~(i << 9); + i ^= ((i >> 14) | (i << 18)); /* >>> */ + i += (i << 4); + i ^= ((i >> 10) | (i << 22)); /* >>> */ + return i; +} + +/*****************************************************************************/ +static int +hashtable_expand(struct hashtable *h) +{ + /* Double the size of the table to accomodate more entries */ + struct entry **newtable; + struct entry *e; + struct entry **pE; + unsigned int newsize, i, index; + /* Check we're not hitting max capacity */ + if (h->primeindex == (prime_table_length - 1)) return 0; + newsize = primes[++(h->primeindex)]; + + newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); + if (NULL != newtable) + { + memset(newtable, 0, newsize * sizeof(struct entry *)); + /* This algorithm is not 'stable'. ie. it reverses the list + * when it transfers entries between the tables */ + for (i = 0; i < h->tablelength; i++) { + while (NULL != (e = h->table[i])) { + h->table[i] = e->next; + index = indexFor(newsize,e->h); + e->next = newtable[index]; + newtable[index] = e; + } + } + free(h->table); + h->table = newtable; + } + /* Plan B: realloc instead */ + else + { + newtable = (struct entry **) + realloc(h->table, newsize * sizeof(struct entry *)); + if (NULL == newtable) { (h->primeindex)--; return 0; } + h->table = newtable; + memset(newtable[h->tablelength], 0, newsize - h->tablelength); + for (i = 0; i < h->tablelength; i++) { + for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { + index = indexFor(newsize,e->h); + if (index == i) + { + pE = &(e->next); + } + else + { + *pE = e->next; + e->next = newtable[index]; + newtable[index] = e; + } + } + } + } + h->tablelength = newsize; + h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); + return -1; +} + +/*****************************************************************************/ +OZ_DECLARE(unsigned int) +hashtable_count(struct hashtable *h) +{ + return h->entrycount; +} + +/*****************************************************************************/ +OZ_DECLARE(int) +hashtable_insert(struct hashtable *h, void *k, void *v, hashtable_flag_t flags) +{ + /* This method allows duplicate keys - but they shouldn't be used */ + unsigned int index; + struct entry *e; + if (++(h->entrycount) > h->loadlimit) + { + /* Ignore the return value. If expand fails, we should + * still try cramming just this value into the existing table + * -- we may not have memory for a larger table, but one more + * element may be ok. Next time we insert, we'll try expanding again.*/ + hashtable_expand(h); + } + e = (struct entry *)malloc(sizeof(struct entry)); + if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ + e->h = hash(h,k); + index = indexFor(h->tablelength,e->h); + e->k = k; + e->v = v; + e->flags = flags; + e->next = h->table[index]; + h->table[index] = e; + return -1; +} + +/*****************************************************************************/ +OZ_DECLARE(void *) /* returns value associated with key */ +hashtable_search(struct hashtable *h, void *k) +{ + struct entry *e; + unsigned int hashvalue, index; + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hashvalue); + e = h->table[index]; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +OZ_DECLARE(void *) /* returns value associated with key */ +hashtable_remove(struct hashtable *h, void *k) +{ + /* TODO: consider compacting the table when the load factor drops enough, + * or provide a 'compact' method. */ + + struct entry *e; + struct entry **pE; + void *v; + unsigned int hashvalue, index; + + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hash(h,k)); + pE = &(h->table[index]); + e = *pE; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) + { + *pE = e->next; + h->entrycount--; + v = e->v; + if (e->flags & HASHTABLE_FLAG_FREE_KEY) { + freekey(e->k); + } + free(e); + return v; + } + pE = &(e->next); + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +/* destroy */ +OZ_DECLARE(void) +hashtable_destroy(struct hashtable *h) +{ + unsigned int i; + struct entry *e, *f; + struct entry **table = h->table; + + for (i = 0; i < h->tablelength; i++) + { + e = table[i]; + while (NULL != e) + { f = e; e = e->next; if (f->flags & HASHTABLE_FLAG_FREE_KEY) freekey(f->k); if (f->flags & HASHTABLE_FLAG_FREE_VALUE) free(f->v); free(f); } + } + + free(h->table); + free(h); +} + +OZ_DECLARE(struct hashtable_iterator *) hashtable_next(struct hashtable_iterator *i) +{ + + if (i->e) { + if ((i->e = i->e->next) != 0) { + return i; + } else { + i->pos++; + } + } + + while(i->pos < i->h->tablelength && !i->h->table[i->pos]) { + i->pos++; + } + + if (i->pos >= i->h->tablelength) { + return NULL; + } + + if ((i->e = i->h->table[i->pos]) != 0) { + return i; + } + + return NULL; +} + +OZ_DECLARE(struct hashtable_iterator *) hashtable_first(struct hashtable *h) +{ + h->iterator.pos = 0; + h->iterator.e = NULL; + h->iterator.h = h; + return hashtable_next(&h->iterator); +} + + + +OZ_DECLARE(void) hashtable_this(struct hashtable_iterator *i, const void **key, int *klen, void **val) +{ + if (i->e) { + if (key) { + *key = i->e->k; + } + if (klen) { + *klen = (int)strlen(i->e->k); + } + if (val) { + *val = i->e->v; + } + } else { + if (key) { + *key = NULL; + } + if (klen) { + *klen = 0; + } + if (val) { + *val = NULL; + } + } +} + + +/* 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: + */ + diff --git a/libs/openzap/src/hashtable_itr.c b/libs/openzap/src/hashtable_itr.c new file mode 100644 index 0000000000..8973b08ad0 --- /dev/null +++ b/libs/openzap/src/hashtable_itr.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2002, 2004, Christopher Clark + * 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 "hashtable.h" +#include "hashtable_private.h" +#include "hashtable_itr.h" +#include /* defines NULL */ + +/*****************************************************************************/ +/* hashtable_iterator - iterator constructor */ + +struct hashtable_itr * +hashtable_iterator(struct hashtable *h) +{ + unsigned int i, tablelength; + struct hashtable_itr *itr = (struct hashtable_itr *) + malloc(sizeof(struct hashtable_itr)); + if (NULL == itr) return NULL; + itr->h = h; + itr->e = NULL; + itr->parent = NULL; + tablelength = h->tablelength; + itr->index = tablelength; + if (0 == h->entrycount) return itr; + + for (i = 0; i < tablelength; i++) + { + if (NULL != h->table[i]) + { + itr->e = h->table[i]; + itr->index = i; + break; + } + } + return itr; +} + +/*****************************************************************************/ +/* advance - advance the iterator to the next element + * returns zero if advanced to end of table */ + +int +hashtable_iterator_advance(struct hashtable_itr *itr) +{ + unsigned int j,tablelength; + struct entry **table; + struct entry *next; + if (NULL == itr->e) return 0; /* stupidity check */ + + next = itr->e->next; + if (NULL != next) + { + itr->parent = itr->e; + itr->e = next; + return -1; + } + tablelength = itr->h->tablelength; + itr->parent = NULL; + if (tablelength <= (j = ++(itr->index))) + { + itr->e = NULL; + return 0; + } + table = itr->h->table; + while (NULL == (next = table[j])) + { + if (++j >= tablelength) + { + itr->index = tablelength; + itr->e = NULL; + return 0; + } + } + itr->index = j; + itr->e = next; + return -1; +} + +/*****************************************************************************/ +/* remove - remove the entry at the current iterator position + * and advance the iterator, if there is a successive + * element. + * If you want the value, read it before you remove: + * beware memory leaks if you don't. + * Returns zero if end of iteration. */ + +int +hashtable_iterator_remove(struct hashtable_itr *itr) +{ + struct entry *remember_e, *remember_parent; + int ret; + + /* Do the removal */ + if (NULL == (itr->parent)) + { + /* element is head of a chain */ + itr->h->table[itr->index] = itr->e->next; + } else { + /* element is mid-chain */ + itr->parent->next = itr->e->next; + } + /* itr->e is now outside the hashtable */ + remember_e = itr->e; + itr->h->entrycount--; + freekey(remember_e->k); + + /* Advance the iterator, correcting the parent */ + remember_parent = itr->parent; + ret = hashtable_iterator_advance(itr); + if (itr->parent == remember_e) { itr->parent = remember_parent; } + free(remember_e); + return ret; +} + +/*****************************************************************************/ +int /* returns zero if not found */ +hashtable_iterator_search(struct hashtable_itr *itr, + struct hashtable *h, void *k) +{ + struct entry *e, *parent; + unsigned int hashvalue, index; + + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hashvalue); + + e = h->table[index]; + parent = NULL; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) + { + itr->index = index; + itr->e = e; + itr->parent = parent; + itr->h = h; + return -1; + } + parent = e; + e = e->next; + } + return 0; +} + + +/* 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: + */ + diff --git a/libs/openzap/src/include/fsk.h b/libs/openzap/src/include/fsk.h new file mode 100644 index 0000000000..b098444d03 --- /dev/null +++ b/libs/openzap/src/include/fsk.h @@ -0,0 +1,113 @@ +/* + * bell202.h + * + * Copyright (c) 2005 Robert Krten. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This module contains the manifest constants and declarations for + * the Bell-202 1200 baud FSK modem. + * + * 2005 03 20 R. Krten created +*/ + +#ifndef __FSK_H__ +#define __FSK_H__ +#include "uart.h" + +typedef struct { + int freq_space; /* Frequency of the 0 bit */ + int freq_mark; /* Frequency of the 1 bit */ + int baud_rate; /* baud rate for the modem */ +} fsk_modem_definition_t; + +/* Must be kept in sync with fsk_modem_definitions array in fsk.c */ +/* V.23 definitions: http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-V.23 */ +typedef enum { + FSK_V23_FORWARD_MODE1 = 0, /* Maximum 600 bps for long haul */ + FSK_V23_FORWARD_MODE2, /* Standard 1200 bps V.23 */ + FSK_V23_BACKWARD, /* 75 bps return path for V.23 */ + FSK_BELL202 /* Bell 202 half-duplex 1200 bps */ +} fsk_modem_types_t; + +typedef enum { + FSK_STATE_CHANSEIZE = 0, + FSK_STATE_CARRIERSIG, + FSK_STATE_DATA +} fsk_state_t; + +typedef struct dsp_fsk_attr_s +{ + int sample_rate; /* sample rate in HZ */ + bithandler_func_t bithandler; /* bit handler */ + void *bithandler_arg; /* arbitrary ID passed to bithandler as first argument */ + bytehandler_func_t bytehandler; /* byte handler */ + void *bytehandler_arg; /* arbitrary ID passed to bytehandler as first argument */ +} dsp_fsk_attr_t; + +typedef struct +{ + fsk_state_t state; + dsp_fsk_attr_t attr; /* attributes structure */ + double *correlates[4]; /* one for each of sin/cos for mark/space */ + int corrsize; /* correlate size (also number of samples in ring buffer) */ + double *buffer; /* sample ring buffer */ + int ringstart; /* ring buffer start offset */ + double cellpos; /* bit cell position */ + double celladj; /* bit cell adjustment for each sample */ + int previous_bit; /* previous bit (for detecting a transition to sync-up cell position) */ + int current_bit; /* current bit */ + int last_bit; + int downsampling_count; /* number of samples to skip */ + int current_downsample; /* current skip count */ + int conscutive_state_bits; /* number of bits in a row that matches the pattern for the current state */ +} dsp_fsk_handle_t; + +/* + * Function prototypes + * + * General calling order is: + * a) create the attributes structure (dsp_fsk_attr_init) + * b) initialize fields in the attributes structure (dsp_fsk_attr_set_*) + * c) create a Bell-202 handle (dsp_fsk_create) + * d) feed samples through the handler (dsp_fsk_sample) +*/ + +void dsp_fsk_attr_init(dsp_fsk_attr_t *attributes); + +bithandler_func_t dsp_fsk_attr_get_bithandler(dsp_fsk_attr_t *attributes, void **bithandler_arg); +void dsp_fsk_attr_set_bithandler(dsp_fsk_attr_t *attributes, bithandler_func_t bithandler, void *bithandler_arg); +bytehandler_func_t dsp_fsk_attr_get_bytehandler(dsp_fsk_attr_t *attributes, void **bytehandler_arg); +void dsp_fsk_attr_set_bytehandler(dsp_fsk_attr_t *attributes, bytehandler_func_t bytehandler, void *bytehandler_arg); +int dsp_fsk_attr_get_samplerate(dsp_fsk_attr_t *attributes); +int dsp_fsk_attr_set_samplerate(dsp_fsk_attr_t *attributes, int samplerate); + +dsp_fsk_handle_t * dsp_fsk_create(dsp_fsk_attr_t *attributes); +void dsp_fsk_destroy(dsp_fsk_handle_t **handle); + +void dsp_fsk_sample(dsp_fsk_handle_t *handle, double normalized_sample); + +extern fsk_modem_definition_t fsk_modem_definitions[]; + +#endif + diff --git a/libs/openzap/src/include/g711.h b/libs/openzap/src/include/g711.h new file mode 100644 index 0000000000..6f4c1d2542 --- /dev/null +++ b/libs/openzap/src/include/g711.h @@ -0,0 +1,395 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * g711.h - In line A-law and u-law conversion routines + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * $Id: g711.h,v 1.1 2006/06/07 15:46:39 steveu Exp $ + */ + +/*! \file */ + +/*! \page g711_page A-law and mu-law handling + Lookup tables for A-law and u-law look attractive, until you consider the impact + on the CPU cache. If it causes a substantial area of your processor cache to get + hit too often, cache sloshing will severely slow things down. The main reason + these routines are slow in C, is the lack of direct access to the CPU's "find + the first 1" instruction. A little in-line assembler fixes that, and the + conversion routines can be faster than lookup tables, in most real world usage. + A "find the first 1" instruction is available on most modern CPUs, and is a + much underused feature. + + If an assembly language method of bit searching is not available, these routines + revert to a method that can be a little slow, so the cache thrashing might not + seem so bad :( + + Feel free to submit patches to add fast "find the first 1" support for your own + favourite processor. + + Look up tables are used for transcoding between A-law and u-law, since it is + difficult to achieve the precise transcoding procedure laid down in the G.711 + specification by other means. +*/ + +#if !defined(_G711_H_) +#define _G711_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif + typedef unsigned __int8 uint8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef unsigned __int16 uint16_t; +#else +#include +#endif + +#if defined(__i386__) + /*! \brief Find the bit position of the highest set bit in a word + \param bits The word to be searched + \return The bit number of the highest set bit, or -1 if the word is zero. */ + static __inline__ int top_bit(unsigned int bits) + { + int res; + + __asm__ __volatile__(" movl $-1,%%edx;\n" + " bsrl %%eax,%%edx;\n" + : "=d" (res) + : "a" (bits)); + return res; + } + /*- End of function --------------------------------------------------------*/ + + /*! \brief Find the bit position of the lowest set bit in a word + \param bits The word to be searched + \return The bit number of the lowest set bit, or -1 if the word is zero. */ + static __inline__ int bottom_bit(unsigned int bits) + { + int res; + + __asm__ __volatile__(" movl $-1,%%edx;\n" + " bsfl %%eax,%%edx;\n" + : "=d" (res) + : "a" (bits)); + return res; + } + /*- End of function --------------------------------------------------------*/ +#elif defined(__x86_64__) + static __inline__ int top_bit(unsigned int bits) + { + int res; + + __asm__ __volatile__(" movq $-1,%%rdx;\n" + " bsrq %%rax,%%rdx;\n" + : "=d" (res) + : "a" (bits)); + return res; + } + /*- End of function --------------------------------------------------------*/ + + static __inline__ int bottom_bit(unsigned int bits) + { + int res; + + __asm__ __volatile__(" movq $-1,%%rdx;\n" + " bsfq %%rax,%%rdx;\n" + : "=d" (res) + : "a" (bits)); + return res; + } + /*- End of function --------------------------------------------------------*/ +#else + static __inline__ int top_bit(unsigned int bits) + { + int i; + + if (bits == 0) + return -1; + i = 0; + if (bits & 0xFFFF0000) + { + bits &= 0xFFFF0000; + i += 16; + } + if (bits & 0xFF00FF00) + { + bits &= 0xFF00FF00; + i += 8; + } + if (bits & 0xF0F0F0F0) + { + bits &= 0xF0F0F0F0; + i += 4; + } + if (bits & 0xCCCCCCCC) + { + bits &= 0xCCCCCCCC; + i += 2; + } + if (bits & 0xAAAAAAAA) + { + bits &= 0xAAAAAAAA; + i += 1; + } + return i; + } + /*- End of function --------------------------------------------------------*/ + + static __inline__ int bottom_bit(unsigned int bits) + { + int i; + + if (bits == 0) + return -1; + i = 32; + if (bits & 0x0000FFFF) + { + bits &= 0x0000FFFF; + i -= 16; + } + if (bits & 0x00FF00FF) + { + bits &= 0x00FF00FF; + i -= 8; + } + if (bits & 0x0F0F0F0F) + { + bits &= 0x0F0F0F0F; + i -= 4; + } + if (bits & 0x33333333) + { + bits &= 0x33333333; + i -= 2; + } + if (bits & 0x55555555) + { + bits &= 0x55555555; + i -= 1; + } + return i; + } + /*- End of function --------------------------------------------------------*/ +#endif + + /* N.B. It is tempting to use look-up tables for A-law and u-law conversion. + * However, you should consider the cache footprint. + * + * A 64K byte table for linear to x-law and a 512 byte table for x-law to + * linear sound like peanuts these days, and shouldn't an array lookup be + * real fast? No! When the cache sloshes as badly as this one will, a tight + * calculation may be better. The messiest part is normally finding the + * segment, but a little inline assembly can fix that on an i386, x86_64 and + * many other modern processors. + */ + + /* + * Mu-law is basically as follows: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + + /*#define ULAW_ZEROTRAP*/ /* turn on the trap as per the MIL-STD */ +#define ULAW_BIAS 0x84 /* Bias for linear code. */ + + /*! \brief Encode a linear sample to u-law + \param linear The sample to encode. + \return The u-law value. + */ + static __inline__ uint8_t linear_to_ulaw(int linear) + { + uint8_t u_val; + int mask; + int seg; + + /* Get the sign and the magnitude of the value. */ + if (linear < 0) + { + linear = ULAW_BIAS - linear; + mask = 0x7F; + } + else + { + linear = ULAW_BIAS + linear; + mask = 0xFF; + } + + seg = top_bit(linear | 0xFF) - 7; + + /* + * Combine the sign, segment, quantization bits, + * and complement the code word. + */ + if (seg >= 8) + u_val = (uint8_t) (0x7F ^ mask); + else + u_val = (uint8_t) (((seg << 4) | ((linear >> (seg + 3)) & 0xF)) ^ mask); +#ifdef ULAW_ZEROTRAP + /* Optional ITU trap */ + if (u_val == 0) + u_val = 0x02; +#endif + return u_val; + } + /*- End of function --------------------------------------------------------*/ + + /*! \brief Decode an u-law sample to a linear value. + \param ulaw The u-law sample to decode. + \return The linear value. + */ + static __inline__ int16_t ulaw_to_linear(uint8_t ulaw) + { + int t; + + /* Complement to obtain normal u-law value. */ + ulaw = ~ulaw; + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = (((ulaw & 0x0F) << 3) + ULAW_BIAS) << (((int) ulaw & 0x70) >> 4); + return (int16_t) ((ulaw & 0x80) ? (ULAW_BIAS - t) : (t - ULAW_BIAS)); + } + /*- End of function --------------------------------------------------------*/ + + /* + * A-law is basically as follows: + * + * Linear Input Code Compressed Code + * ----------------- --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +#define ALAW_AMI_MASK 0x55 + + /*! \brief Encode a linear sample to A-law + \param linear The sample to encode. + \return The A-law value. + */ + static __inline__ uint8_t linear_to_alaw(int linear) + { + int mask; + int seg; + + if (linear >= 0) + { + /* Sign (bit 7) bit = 1 */ + mask = ALAW_AMI_MASK | 0x80; + } + else + { + /* Sign (bit 7) bit = 0 */ + mask = ALAW_AMI_MASK; + linear = -linear - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = top_bit(linear | 0xFF) - 7; + if (seg >= 8) + { + if (linear >= 0) + { + /* Out of range. Return maximum value. */ + return (uint8_t) (0x7F ^ mask); + } + /* We must be just a tiny step below zero */ + return (uint8_t) (0x00 ^ mask); + } + /* Combine the sign, segment, and quantization bits. */ + return (uint8_t) (((seg << 4) | ((linear >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask); + } + /*- End of function --------------------------------------------------------*/ + + /*! \brief Decode an A-law sample to a linear value. + \param alaw The A-law sample to decode. + \return The linear value. + */ + static __inline__ int16_t alaw_to_linear(uint8_t alaw) + { + int i; + int seg; + + alaw ^= ALAW_AMI_MASK; + i = ((alaw & 0x0F) << 4); + seg = (((int) alaw & 0x70) >> 4); + if (seg) + i = (i + 0x108) << (seg - 1); + else + i += 8; + return (int16_t) ((alaw & 0x80) ? i : -i); + } + /*- End of function --------------------------------------------------------*/ + + /*! \brief Transcode from A-law to u-law, using the procedure defined in G.711. + \param alaw The A-law sample to transcode. + \return The best matching u-law value. + */ + uint8_t alaw_to_ulaw(uint8_t alaw); + + /*! \brief Transcode from u-law to A-law, using the procedure defined in G.711. + \param alaw The u-law sample to transcode. + \return The best matching A-law value. + */ + uint8_t ulaw_to_alaw(uint8_t ulaw); + +#ifdef __cplusplus +} +#endif + +#endif +/*- End of file ------------------------------------------------------------*/ + +/* 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: + */ diff --git a/libs/openzap/src/include/hashtable.h b/libs/openzap/src/include/hashtable.h new file mode 100644 index 0000000000..0d185bbc5b --- /dev/null +++ b/libs/openzap/src/include/hashtable.h @@ -0,0 +1,228 @@ +/* Copyright (C) 2002 Christopher Clark */ + +#ifndef __HASHTABLE_CWC22_H__ +#define __HASHTABLE_CWC22_H__ +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#endif +#include "openzap.h" + +struct hashtable; +struct hashtable_iterator; + +/* Example of use: + * + * struct hashtable *h; + * struct some_key *k; + * struct some_value *v; + * + * static unsigned int hash_from_key_fn( void *k ); + * static int keys_equal_fn ( void *key1, void *key2 ); + * + * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); + * k = (struct some_key *) malloc(sizeof(struct some_key)); + * v = (struct some_value *) malloc(sizeof(struct some_value)); + * + * (initialise k and v to suitable values) + * + * if (! hashtable_insert(h,k,v) ) + * { exit(-1); } + * + * if (NULL == (found = hashtable_search(h,k) )) + * { printf("not found!"); } + * + * if (NULL == (found = hashtable_remove(h,k) )) + * { printf("Not found\n"); } + * + */ + +/* Macros may be used to define type-safe(r) hashtable access functions, with + * methods specialized to take known key and value types as parameters. + * + * Example: + * + * Insert this at the start of your file: + * + * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); + * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); + * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); + * + * This defines the functions 'insert_some', 'search_some' and 'remove_some'. + * These operate just like hashtable_insert etc., with the same parameters, + * but their function signatures have 'struct some_key *' rather than + * 'void *', and hence can generate compile time errors if your program is + * supplying incorrect data as a key (and similarly for value). + * + * Note that the hash and key equality functions passed to create_hashtable + * still take 'void *' parameters instead of 'some key *'. This shouldn't be + * a difficult issue as they're only defined and passed once, and the other + * functions will ensure that only valid keys are supplied to them. + * + * The cost for this checking is increased code size and runtime overhead + * - if performance is important, it may be worth switching back to the + * unsafe methods once your program has been debugged with the safe methods. + * This just requires switching to some simple alternative defines - eg: + * #define insert_some hashtable_insert + * + */ + +/***************************************************************************** + * create_hashtable + + * @name create_hashtable + * @param minsize minimum initial size of hashtable + * @param hashfunction function for hashing keys + * @param key_eq_fn function for determining key equality + * @return newly created hashtable or NULL on failure + */ + +OZ_DECLARE(struct hashtable *) +create_hashtable(unsigned int minsize, + unsigned int (*hashfunction) (void*), + int (*key_eq_fn) (void*,void*)); + +/***************************************************************************** + * hashtable_insert + + * @name hashtable_insert + * @param h the hashtable to insert into + * @param k the key - hashtable claims ownership and will free on removal + * @param v the value - does not claim ownership + * @return non-zero for successful insertion + * + * This function will cause the table to expand if the insertion would take + * the ratio of entries to table size over the maximum load factor. + * + * This function does not check for repeated insertions with a duplicate key. + * The value returned when using a duplicate key is undefined -- when + * the hashtable changes size, the order of retrieval of duplicate key + * entries is reversed. + * If in doubt, remove before insert. + */ + + +typedef enum { + HASHTABLE_FLAG_NONE = 0, + HASHTABLE_FLAG_FREE_KEY = (1 << 0), + HASHTABLE_FLAG_FREE_VALUE = (1 << 1) +} hashtable_flag_t; + +OZ_DECLARE(int) +hashtable_insert(struct hashtable *h, void *k, void *v, hashtable_flag_t flags); + +#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ + int fnname (struct hashtable *h, keytype *k, valuetype *v) \ + { \ + return hashtable_insert(h,k,v); \ + } + +/***************************************************************************** + * hashtable_search + + * @name hashtable_search + * @param h the hashtable to search + * @param k the key to search for - does not claim ownership + * @return the value associated with the key, or NULL if none found + */ + +OZ_DECLARE(void *) +hashtable_search(struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ + valuetype * fnname (struct hashtable *h, keytype *k) \ + { \ + return (valuetype *) (hashtable_search(h,k)); \ + } + +/***************************************************************************** + * hashtable_remove + + * @name hashtable_remove + * @param h the hashtable to remove the item from + * @param k the key to search for - does not claim ownership + * @return the value associated with the key, or NULL if none found + */ + +OZ_DECLARE(void *) /* returns value */ +hashtable_remove(struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ + valuetype * fnname (struct hashtable *h, keytype *k) \ + { \ + return (valuetype *) (hashtable_remove(h,k)); \ + } + + +/***************************************************************************** + * hashtable_count + + * @name hashtable_count + * @param h the hashtable + * @return the number of items stored in the hashtable + */ +OZ_DECLARE(unsigned int) +hashtable_count(struct hashtable *h); + + +/***************************************************************************** + * hashtable_destroy + + * @name hashtable_destroy + * @param h the hashtable + * @param free_values whether to call 'free' on the remaining values + */ + +OZ_DECLARE(void) +hashtable_destroy(struct hashtable *h); + +OZ_DECLARE(struct hashtable_iterator*) hashtable_first(struct hashtable *h); +OZ_DECLARE(struct hashtable_iterator*) hashtable_next(struct hashtable_iterator *i); +OZ_DECLARE(void) hashtable_this(struct hashtable_iterator *i, const void **key, int *klen, void **val); + +#endif /* __HASHTABLE_CWC22_H__ */ + +/* + * Copyright (c) 2002, Christopher Clark + * 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. + */ + +/* 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: + */ diff --git a/libs/openzap/src/include/hashtable_itr.h b/libs/openzap/src/include/hashtable_itr.h new file mode 100644 index 0000000000..6897ce949c --- /dev/null +++ b/libs/openzap/src/include/hashtable_itr.h @@ -0,0 +1,129 @@ +/* Copyright (C) 2002, 2004 Christopher Clark */ + +#ifndef __HASHTABLE_ITR_CWC22__ +#define __HASHTABLE_ITR_CWC22__ +#include "hashtable.h" +#include "hashtable_private.h" /* needed to enable inlining */ + +/*****************************************************************************/ +/* This struct is only concrete here to allow the inlining of two of the + * accessor functions. */ +struct hashtable_itr +{ + struct hashtable *h; + struct entry *e; + struct entry *parent; + unsigned int index; +}; + + +/*****************************************************************************/ +/* hashtable_iterator + */ + +struct hashtable_itr * +hashtable_iterator(struct hashtable *h); + +/*****************************************************************************/ +/* hashtable_iterator_key + * - return the value of the (key,value) pair at the current position */ +extern __inline__ void * +hashtable_iterator_key(struct hashtable_itr *i); + +extern __inline__ void * +hashtable_iterator_key(struct hashtable_itr *i) +{ + return i->e->k; +} + +/*****************************************************************************/ +/* value - return the value of the (key,value) pair at the current position */ + +extern __inline__ void * +hashtable_iterator_value(struct hashtable_itr *i); + +extern __inline__ void * +hashtable_iterator_value(struct hashtable_itr *i) +{ + return i->e->v; +} + +/*****************************************************************************/ +/* advance - advance the iterator to the next element + * returns zero if advanced to end of table */ + +int +hashtable_iterator_advance(struct hashtable_itr *itr); + +/*****************************************************************************/ +/* remove - remove current element and advance the iterator to the next element + * NB: if you need the value to free it, read it before + * removing. ie: beware memory leaks! + * returns zero if advanced to end of table */ + +int +hashtable_iterator_remove(struct hashtable_itr *itr); + +/*****************************************************************************/ +/* search - overwrite the supplied iterator, to point to the entry + * matching the supplied key. + h points to the hashtable to be searched. + * returns zero if not found. */ +int +hashtable_iterator_search(struct hashtable_itr *itr, + struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ + int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ + { \ + return (hashtable_iterator_search(i,h,k)); \ + } + + + +#endif /* __HASHTABLE_ITR_CWC22__*/ + +/* + * Copyright (c) 2002, 2004, Christopher Clark + * 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. + */ + +/* 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: + */ + diff --git a/libs/openzap/src/include/hashtable_private.h b/libs/openzap/src/include/hashtable_private.h new file mode 100644 index 0000000000..1bb10fd016 --- /dev/null +++ b/libs/openzap/src/include/hashtable_private.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2002, 2004 Christopher Clark */ + +#ifndef __HASHTABLE_PRIVATE_CWC22_H__ +#define __HASHTABLE_PRIVATE_CWC22_H__ + +#include "hashtable.h" + + +/*****************************************************************************/ + +struct entry +{ + void *k, *v; + unsigned int h; + hashtable_flag_t flags; + struct entry *next; +}; + +struct hashtable_iterator { + unsigned int pos; + struct entry *e; + struct hashtable *h; +}; + +struct hashtable { + unsigned int tablelength; + struct entry **table; + unsigned int entrycount; + unsigned int loadlimit; + unsigned int primeindex; + unsigned int (*hashfn) (void *k); + int (*eqfn) (void *k1, void *k2); + struct hashtable_iterator iterator; +}; + +/*****************************************************************************/ +unsigned int +hash(struct hashtable *h, void *k); + +/*****************************************************************************/ +/* indexFor */ +static __inline__ unsigned int +indexFor(unsigned int tablelength, unsigned int hashvalue) { + return (hashvalue % tablelength); +} + +/* Only works if tablelength == 2^N */ +/*static inline unsigned int + indexFor(unsigned int tablelength, unsigned int hashvalue) + { + return (hashvalue & (tablelength - 1u)); + } +*/ + +/*****************************************************************************/ +#define freekey(X) free(X) +/*define freekey(X) ; */ + + +/*****************************************************************************/ + +#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ + +/* + * Copyright (c) 2002, Christopher Clark + * 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. + */ +/* 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: + */ diff --git a/libs/openzap/src/include/libteletone.h b/libs/openzap/src/include/libteletone.h new file mode 100644 index 0000000000..6e806cec4f --- /dev/null +++ b/libs/openzap/src/include/libteletone.h @@ -0,0 +1,161 @@ +/* + * libteletone + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libteletone + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * libteletone.h -- Tone Generator/Detector + * + * + * + * Exception: + * The author hereby grants the use of this source code under the + * following license if and only if the source code is distributed + * as part of the openzap library. Any use or distribution of this + * source code outside the scope of the openzap library will nullify the + * following license and reinact the MPL 1.1 as stated above. + * + * Copyright (c) 2007, Anthony Minessale II + * 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 LIBTELETONE_H +#define LIBTELETONE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define TELETONE_MAX_DTMF_DIGITS 128 +#define TELETONE_MAX_TONES 18 +#define TELETONE_TONE_RANGE 127 + +typedef double teletone_process_t; + +/*! \file libteletone.h + \brief Top level include file + + This file should be included by applications using the library +*/ + +/*! \brief An abstraction to store a tone mapping */ +typedef struct { + /*! An array of tone frequencies */ + teletone_process_t freqs[TELETONE_MAX_TONES]; +} teletone_tone_map_t; + +#if !defined(M_PI) +/* C99 systems may not define M_PI */ +#define M_PI 3.14159265358979323846264338327 +#endif + +#ifdef _MSC_VER +typedef __int16 int16_t; +#endif + +#if (_MSC_VER >= 1400) // VC8+ +#define teletone_assert(expr) assert(expr);__analysis_assume( expr ) +#else +#define teletone_assert(expr) assert(expr) +#endif + +#ifdef _MSC_VER +#if defined(TT_DECLARE_STATIC) +#define TELETONE_API(type) type __stdcall +#define TELETONE_API_NONSTD(type) type __cdecl +#define TELETONE_API_DATA +#elif defined(TELETONE_EXPORTS) +#define TELETONE_API(type) __declspec(dllexport) type __stdcall +#define TELETONE_API_NONSTD(type) __declspec(dllexport) type __cdecl +#define TELETONE_API_DATA __declspec(dllexport) +#else +#define TELETONE_API(type) __declspec(dllimport) type __stdcall +#define TELETONE_API_NONSTD(type) __declspec(dllimport) type __cdecl +#define TELETONE_API_DATA __declspec(dllimport) +#endif +#else +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(HAVE_VISIBILITY) +#define TELETONE_API(type) __attribute__((visibility("default"))) type +#define TELETONE_API_NONSTD(type) __attribute__((visibility("default"))) type +#define TELETONE_API_DATA __attribute__((visibility("default"))) +#else +#define TELETONE_API(type) type +#define TELETONE_API_NONSTD(type) type +#define TELETONE_API_DATA +#endif +#endif + +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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: + */ diff --git a/libs/openzap/src/include/libteletone_detect.h b/libs/openzap/src/include/libteletone_detect.h new file mode 100644 index 0000000000..3d0a5130ec --- /dev/null +++ b/libs/openzap/src/include/libteletone_detect.h @@ -0,0 +1,240 @@ +/* + * libteletone + * Copyright (C) 2005/2006, Anthony Minessale II + * + * libteletone_detect.c Tone Detection Code + * + * Exception: + * The author hereby grants the use of this source code under the + * following license if and only if the source code is distributed + * as part of the openzap library. Any use or distribution of this + * source code outside the scope of the openzap library will nullify the + * following license and reinact the MPL 1.1 as stated above. + * + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + ********************************************************************************* + * + * Derived from tone_detect.h - General telephony tone detection, and specific + * detection of DTMF. + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * + */ + +#ifndef LIBTELETONE_DETECT_H +#define LIBTELETONE_DETECT_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + + /*! \file libteletone_detect.h + \brief Tone Detection Routines + + This module is responsible for tone detection specifics + */ + +#ifndef FALSE +#define FALSE 0 +#ifndef TRUE +#define TRUE (!FALSE) +#endif +#endif + + /* Basic DTMF specs: + * + * Minimum tone on = 40ms + * Minimum tone off = 50ms + * Maximum digit rate = 10 per second + * Normal twist <= 8dB accepted + * Reverse twist <= 4dB accepted + * S/N >= 15dB will detect OK + * Attenuation <= 26dB will detect OK + * Frequency tolerance +- 1.5% will detect, +-3.5% will reject + */ + +#define DTMF_THRESHOLD 8.0e7 +#define DTMF_NORMAL_TWIST 6.3 /* 8dB */ +#define DTMF_REVERSE_TWIST 2.5 /* 4dB */ +#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */ +#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */ +#define DTMF_2ND_HARMONIC_ROW 2.5 /* 4dB */ +#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */ +#define GRID_FACTOR 4 +#define BLOCK_LEN 102 +#define M_TWO_PI 2.0*M_PI + + /*! \brief A continer for the elements of a Goertzel Algorithm (The names are from his formula) */ + typedef struct { + float v2; + float v3; + double fac; + } teletone_goertzel_state_t; + + /*! \brief A container for a DTMF detection state.*/ + typedef struct { + int hit1; + int hit2; + int hit3; + int hit4; + int mhit; + + teletone_goertzel_state_t row_out[GRID_FACTOR]; + teletone_goertzel_state_t col_out[GRID_FACTOR]; + teletone_goertzel_state_t row_out2nd[GRID_FACTOR]; + teletone_goertzel_state_t col_out2nd[GRID_FACTOR]; + float energy; + + int current_sample; + char digits[TELETONE_MAX_DTMF_DIGITS + 1]; + int current_digits; + int detected_digits; + int lost_digits; + int digit_hits[16]; + } teletone_dtmf_detect_state_t; + + /*! \brief An abstraction to store the coefficient of a tone frequency */ + typedef struct { + float fac; + } teletone_detection_descriptor_t; + + /*! \brief A container for a single multi-tone detection + TELETONE_MAX_TONES dictates the maximum simultaneous tones that can be present + in a multi-tone representation. + */ + typedef struct { + int sample_rate; + + teletone_detection_descriptor_t tdd[TELETONE_MAX_TONES]; + teletone_goertzel_state_t gs[TELETONE_MAX_TONES]; + teletone_goertzel_state_t gs2[TELETONE_MAX_TONES]; + int tone_count; + + float energy; + int current_sample; + + int min_samples; + int total_samples; + + int positives; + int negatives; + int hits; + + int positive_factor; + int negative_factor; + int hit_factor; + + } teletone_multi_tone_t; + + + /*! + \brief Initilize a multi-frequency tone detector + \param mt the multi-frequency tone descriptor + \param map a representation of the multi-frequency tone + */ +TELETONE_API(void) teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *map); + + /*! + \brief Check a sample buffer for the presence of the mulit-frequency tone described by mt + \param mt the multi-frequency tone descriptor + \param sample_buffer an array aof 16 bit signed linear samples + \param samples the number of samples present in sample_buffer + \return true when the tone was detected or false when it is not + */ +TELETONE_API(int) teletone_multi_tone_detect (teletone_multi_tone_t *mt, + int16_t sample_buffer[], + int samples); + + /*! + \brief Initilize a DTMF detection state object + \param dtmf_detect_state the DTMF detection state to initilize + \param sample_rate the desired sample rate + */ +TELETONE_API(void) teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf_detect_state, int sample_rate); + + /*! + \brief Check a sample buffer for the presence of DTMF digits + \param dtmf_detect_state the detection state object to check + \param sample_buffer an array aof 16 bit signed linear samples + \param samples the number of samples present in sample_buffer + \return true when DTMF was detected or false when it is not + */ +TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, + int16_t sample_buffer[], + int samples); + /*! + \brief retrieve any collected digits into a string buffer + \param dtmf_detect_state the detection state object to check + \param buf the string buffer to write to + \param max the maximum length of buf + \return the number of characters written to buf + */ +TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, + char *buf, + int max); + + /*! + \brief Step through the Goertzel Algorithm for each sample in a buffer + \param goertzel_state the goertzel state to step the samples through + \param sample_buffer an array aof 16 bit signed linear samples + \param samples the number of samples present in sample_buffer + */ +TELETONE_API(void) teletone_goertzel_update(teletone_goertzel_state_t *goertzel_state, + int16_t sample_buffer[], + int samples); + + + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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: + */ diff --git a/libs/openzap/src/include/libteletone_generate.h b/libs/openzap/src/include/libteletone_generate.h new file mode 100644 index 0000000000..fce19e7255 --- /dev/null +++ b/libs/openzap/src/include/libteletone_generate.h @@ -0,0 +1,281 @@ +/* + * libteletone + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Copyright (c) 2007, Anthony Minessale II + * 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 LIBTELETONE_GENERATE_H +#define LIBTELETONE_GENERATE_H +#ifdef __cplusplus +extern "C" { +#ifdef _doh +} +#endif +#endif + +#include +#include + +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#ifndef __inline__ +#define __inline__ inline +#endif +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif + +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +#else +#include +#endif +#include +#include +#include +#include +#if !defined(powf) && !defined(_WIN64) +extern float powf (float, float); +#endif +#include +#include +#ifndef _MSC_VER +#include +#endif +#include +#include +#include + +#define TELETONE_VOL_DB_MAX 0 +#define TELETONE_VOL_DB_MIN -63 +#define MAX_PHASE_TONES 4 + +struct teletone_dds_state { + uint32_t phase_rate[MAX_PHASE_TONES]; + uint32_t scale_factor; + uint32_t phase_accumulator; + teletone_process_t tx_level; +}; +typedef struct teletone_dds_state teletone_dds_state_t; + +#define SINE_TABLE_MAX 128 +#define SINE_TABLE_LEN (SINE_TABLE_MAX - 1) +#define MAX_PHASE_ACCUMULATOR 0x10000 * 0x10000 +/* 3.14 == the max power on ulaw (alaw is 3.17) */ +/* 3.02 represents twice the power */ +#define DBM0_MAX_POWER (3.14f + 3.02f) + +TELETONE_API_DATA extern int16_t TELETONE_SINES[SINE_TABLE_MAX]; + +static __inline__ int32_t teletone_dds_phase_rate(teletone_process_t tone, uint32_t rate) +{ + return (int32_t) ((tone * MAX_PHASE_ACCUMULATOR) / rate); +} + +static __inline__ int16_t teletone_dds_state_modulate_sample(teletone_dds_state_t *dds, uint32_t pindex) +{ + int32_t bitmask = dds->phase_accumulator, sine_index = (bitmask >>= 23) & SINE_TABLE_LEN; + int16_t sample; + + if (pindex >= MAX_PHASE_TONES) { + pindex = 0; + } + + if (bitmask & SINE_TABLE_MAX) { + sine_index = SINE_TABLE_LEN - sine_index; + } + + sample = TELETONE_SINES[sine_index]; + + if (bitmask & (SINE_TABLE_MAX * 2)) { + sample *= -1; + } + + dds->phase_accumulator += dds->phase_rate[pindex]; + return (int16_t) (sample * dds->scale_factor >> 15); +} + +static __inline__ void teletone_dds_state_set_tx_level(teletone_dds_state_t *dds, float tx_level) +{ + dds->scale_factor = (int) (powf(10.0f, (tx_level - DBM0_MAX_POWER) / 20.0f) * (32767.0f * 1.414214f)); + dds->tx_level = tx_level; +} + +static __inline__ void teletone_dds_state_reset_accum(teletone_dds_state_t *dds) +{ + dds->phase_accumulator = 0; +} + +static __inline__ int teletone_dds_state_set_tone(teletone_dds_state_t *dds, teletone_process_t tone, uint32_t rate, uint32_t pindex) +{ + if (pindex < MAX_PHASE_TONES) { + dds->phase_rate[pindex] = teletone_dds_phase_rate(tone, rate); + return 0; + } + + return -1; +} + + + +/*! \file libteletone_generate.h + \brief Tone Generation Routines + + This module is responsible for tone generation specifics +*/ + +typedef int16_t teletone_audio_t; +struct teletone_generation_session; +typedef int (*tone_handler)(struct teletone_generation_session *ts, teletone_tone_map_t *map); + +/*! \brief An abstraction to store a tone generation session */ +struct teletone_generation_session { + /*! An array of tone mappings to character mappings */ + teletone_tone_map_t TONES[TELETONE_TONE_RANGE]; + /*! The number of channels the output audio should be in */ + int channels; + /*! The Rate in hz of the output audio */ + int rate; + /*! The duration (in samples) of the output audio */ + int duration; + /*! The duration of silence to append after the initial audio is generated */ + int wait; + /*! The duration (in samples) of the output audio (takes prescedence over actual duration value) */ + int tmp_duration; + /*! The duration of silence to append after the initial audio is generated (takes prescedence over actual wait value)*/ + int tmp_wait; + /*! Number of loops to repeat a single instruction*/ + int loops; + /*! Number of loops to repeat the entire set of instructions*/ + int LOOPS; + /*! Number to mutiply total samples by to determine when to begin ascent or decent e.g. 0=beginning 4=(last 25%) */ + float decay_factor; + /*! Direction to perform volume increase/decrease 1/-1*/ + int decay_direction; + /*! Number of samples between increase/decrease of volume */ + int decay_step; + /*! Volume factor of the tone */ + float volume; + /*! Debug on/off */ + int debug; + /*! FILE stream to write debug data to */ + FILE *debug_stream; + /*! Extra user data to attach to the session*/ + void *user_data; + /*! Buffer for storing sample data (dynamic) */ + teletone_audio_t *buffer; + /*! Size of the buffer */ + int datalen; + /*! In-Use size of the buffer */ + int samples; + /*! Callback function called during generation */ + int dynamic; + tone_handler handler; +}; + +typedef struct teletone_generation_session teletone_generation_session_t; + + +/*! + \brief Assign a set of tones to a tone_session indexed by a paticular index/character + \param ts the tone generation session + \param index the index to map the tone to + \param ... up to TELETONE_MAX_TONES frequencies terminated by 0.0 + \return 0 +*/ +TELETONE_API(int) teletone_set_tone(teletone_generation_session_t *ts, int index, ...); + +/*! + \brief Assign a set of tones to a single tone map + \param map the map to assign the tones to + \param ... up to TELETONE_MAX_TONES frequencies terminated by 0.0 + \return 0 +*/ +TELETONE_API(int) teletone_set_map(teletone_tone_map_t *map, ...); + +/*! + \brief Initilize a tone generation session + \param ts the tone generation session to initilize + \param buflen the size of the buffer(in samples) to dynamically allocate + \param handler a callback function to execute when a tone generation instruction is complete + \param user_data optional user data to send + \return 0 +*/ +TELETONE_API(int) teletone_init_session(teletone_generation_session_t *ts, int buflen, tone_handler handler, void *user_data); + +/*! + \brief Free the buffer allocated by a tone generation session + \param ts the tone generation session to destroy + \return 0 +*/ +TELETONE_API(int) teletone_destroy_session(teletone_generation_session_t *ts); + +/*! + \brief Execute a single tone generation instruction + \param ts the tone generation session to consult for parameters + \param map the tone mapping to use for the frequencies + \return 0 +*/ +TELETONE_API(int) teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *map); + +/*! + \brief Execute a tone generation script and call callbacks after each instruction + \param ts the tone generation session to execute on + \param cmd the script to execute + \return 0 +*/ +TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif + +/* 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: + */ diff --git a/libs/openzap/src/include/openzap.h b/libs/openzap/src/include/openzap.h new file mode 100644 index 0000000000..8fde5795d5 --- /dev/null +++ b/libs/openzap/src/include/openzap.h @@ -0,0 +1,797 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 OPENZAP_H +#define OPENZAP_H + + +#if !defined(_XOPEN_SOURCE) && !defined(__FreeBSD__) +#define _XOPEN_SOURCE 600 +#endif + +#ifndef HAVE_STRINGS_H +#define HAVE_STRINGS_H 1 +#endif +#ifndef HAVE_SYS_SOCKET_H +#define HAVE_SYS_SOCKET_H 1 +#endif + +#ifndef __WINDOWS__ +#if defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64) +#define __WINDOWS__ +#endif +#endif + +#ifdef _MSC_VER +#if defined(OZ_DECLARE_STATIC) +#define OZ_DECLARE(type) type __stdcall +#define OZ_DECLARE_NONSTD(type) type __cdecl +#define OZ_DECLARE_DATA +#elif defined(OPENZAP_EXPORTS) +#define OZ_DECLARE(type) __declspec(dllexport) type __stdcall +#define OZ_DECLARE_NONSTD(type) __declspec(dllexport) type __cdecl +#define OZ_DECLARE_DATA __declspec(dllexport) +#else +#define OZ_DECLARE(type) __declspec(dllimport) type __stdcall +#define OZ_DECLARE_NONSTD(type) __declspec(dllimport) type __cdecl +#define OZ_DECLARE_DATA __declspec(dllimport) +#endif +#define EX_DECLARE_DATA __declspec(dllexport) +#else +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(HAVE_VISIBILITY) +#define OZ_DECLARE(type) __attribute__((visibility("default"))) type +#define OZ_DECLARE_NONSTD(type) __attribute__((visibility("default"))) type +#define OZ_DECLARE_DATA __attribute__((visibility("default"))) +#else +#define OZ_DECLARE(type) type +#define OZ_DECLARE_NONSTD(type) type +#define OZ_DECLARE_DATA +#endif +#define EX_DECLARE_DATA +#endif + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#if (_MSC_VER >= 1400) /* VC8+ */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#ifndef strncasecmp +#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#undef HAVE_STRINGS_H +#undef HAVE_SYS_SOCKET_H +/* disable warning for zero length array in a struct */ +/* this will cause errors on c99 and ansi compliant compilers and will need to be fixed in the wanpipe header files */ +#pragma warning(disable:4706) +#pragma comment(lib, "Winmm") +#endif + +#define ZAP_THREAD_STACKSIZE 240 * 1024 +#define ZAP_ENUM_NAMES(_NAME, _STRINGS) static const char * _NAME [] = { _STRINGS , NULL }; +#define ZAP_STR2ENUM_P(_FUNC1, _FUNC2, _TYPE) OZ_DECLARE(_TYPE) _FUNC1 (const char *name); OZ_DECLARE(const char *) _FUNC2 (_TYPE type); +#define ZAP_STR2ENUM(_FUNC1, _FUNC2, _TYPE, _STRINGS, _MAX) \ + OZ_DECLARE(_TYPE) _FUNC1 (const char *name) \ + { \ + int i; \ + _TYPE t = _MAX ; \ + \ + for (i = 0; i < _MAX ; i++) { \ + if (!strcasecmp(name, _STRINGS[i])) { \ + t = (_TYPE) i; \ + break; \ + } \ + } \ + \ + return t; \ + } \ + OZ_DECLARE(const char *) _FUNC2 (_TYPE type) \ + { \ + if (type > _MAX) { \ + type = _MAX; \ + } \ + return _STRINGS[(int)type]; \ + } \ + +#define zap_true(expr) \ + (expr && ( !strcasecmp(expr, "yes") || \ + !strcasecmp(expr, "on") || \ + !strcasecmp(expr, "true") || \ + !strcasecmp(expr, "enabled") || \ + !strcasecmp(expr, "active") || \ + atoi(expr))) ? 1 : 0 + + +#include +#ifndef __WINDOWS__ +#include +#endif + +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#include +#include "zap_types.h" +#include "hashtable.h" +#include "zap_config.h" +#include "g711.h" +#include "libteletone.h" +#include "zap_buffer.h" +#include "zap_threadmutex.h" + +#ifdef __WINDOWS__ +#define zap_sleep(x) Sleep(x) +#else +#define zap_sleep(x) usleep(x * 1000) +#endif + +#ifdef NDEBUG +#undef assert +#define assert(_Expression) ((void)(_Expression)) +#endif + +#define ZAP_MAX_CHANNELS_PHYSICAL_SPAN 32 +#define ZAP_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN 32 +#define ZAP_MAX_CHANNELS_SPAN ZAP_MAX_CHANNELS_PHYSICAL_SPAN * ZAP_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN +#define ZAP_MAX_SPANS_INTERFACE 128 + +#define GOTO_STATUS(label,st) status = st; goto label ; + +#define zap_copy_string(x,y,z) strncpy(x, y, z - 1) +#define zap_set_string(x,y) strncpy(x, y, sizeof(x)-1) +#define zap_strlen_zero(s) (!s || *s == '\0') +#define zap_strlen_zero_buf(s) (*s == '\0') + + +#define zap_channel_test_feature(obj, flag) ((obj)->features & flag) +#define zap_channel_set_feature(obj, flag) (obj)->features |= (flag) +#define zap_channel_clear_feature(obj, flag) (obj)->features &= ~(flag) +#define zap_channel_set_member_locked(obj, _m, _v) zap_mutex_lock(obj->mutex); obj->_m = _v; zap_mutex_unlock(obj->mutex) + +/*! + \brief Test for the existance of a flag on an arbitary object + \command obj the object to test + \command flag the or'd list of flags to test + \return true value if the object has the flags defined +*/ +#define zap_test_flag(obj, flag) ((obj)->flags & flag) +#define zap_test_pflag(obj, flag) ((obj)->pflags & flag) +#define zap_test_sflag(obj, flag) ((obj)->sflags & flag) + + +#define zap_set_alarm_flag(obj, flag) (obj)->alarm_flags |= (flag) +#define zap_clear_alarm_flag(obj, flag) (obj)->alarm_flags &= ~(flag) +#define zap_test_alarm_flag(obj, flag) ((obj)->alarm_flags & flag) + +/*! + \brief Set a flag on an arbitrary object + \command obj the object to set the flags on + \command flag the or'd list of flags to set +*/ +#define zap_set_flag(obj, flag) (obj)->flags |= (flag) +#define zap_set_flag_locked(obj, flag) assert(obj->mutex != NULL); \ + zap_mutex_lock(obj->mutex); \ + (obj)->flags |= (flag); \ + zap_mutex_unlock(obj->mutex); + +#define zap_set_pflag(obj, flag) (obj)->pflags |= (flag) +#define zap_set_pflag_locked(obj, flag) assert(obj->mutex != NULL); \ + zap_mutex_lock(obj->mutex); \ + (obj)->pflags |= (flag); \ + zap_mutex_unlock(obj->mutex); + +#define zap_set_sflag(obj, flag) (obj)->sflags |= (flag) +#define zap_set_sflag_locked(obj, flag) assert(obj->mutex != NULL); \ + zap_mutex_lock(obj->mutex); \ + (obj)->sflags |= (flag); \ + zap_mutex_unlock(obj->mutex); + +/*! + \brief Clear a flag on an arbitrary object while locked + \command obj the object to test + \command flag the or'd list of flags to clear +*/ +#define zap_clear_flag(obj, flag) (obj)->flags &= ~(flag) + +#define zap_clear_flag_locked(obj, flag) assert(obj->mutex != NULL); zap_mutex_lock(obj->mutex); (obj)->flags &= ~(flag); zap_mutex_unlock(obj->mutex); + +#define zap_clear_pflag(obj, flag) (obj)->pflags &= ~(flag) + +#define zap_clear_pflag_locked(obj, flag) assert(obj->mutex != NULL); zap_mutex_lock(obj->mutex); (obj)->pflags &= ~(flag); zap_mutex_unlock(obj->mutex); + +#define zap_clear_sflag(obj, flag) (obj)->sflags &= ~(flag) + +#define zap_clear_sflag_locked(obj, flag) assert(obj->mutex != NULL); zap_mutex_lock(obj->mutex); (obj)->sflags &= ~(flag); zap_mutex_unlock(obj->mutex); + + +#define zap_set_state_locked(obj, s) if ( obj->state == s ) { \ + if (s != ZAP_CHANNEL_STATE_HANGUP) zap_log(ZAP_LOG_WARNING, "Why bother changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(obj->state), zap_channel_state2str(s)); \ + } else if (zap_test_flag(obj, ZAP_CHANNEL_READY)) { \ + int st = obj->state; \ + zap_channel_set_state(obj, s, 1); \ + if (obj->state == s) zap_log(ZAP_LOG_DEBUG, "Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(st), zap_channel_state2str(s)); \ + else if (!((obj->state == ZAP_CHANNEL_STATE_HANGUP && s == ZAP_CHANNEL_STATE_TERMINATING) || (obj->state == ZAP_CHANNEL_STATE_HANGUP_COMPLETE && s == ZAP_CHANNEL_STATE_HANGUP) || (obj->state == ZAP_CHANNEL_STATE_TERMINATING && s == ZAP_CHANNEL_STATE_HANGUP))) zap_log(ZAP_LOG_WARNING, "VETO Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(st), zap_channel_state2str(s)); \ + } + +#ifdef _MSC_VER +/* The while(0) below throws a conditional expression is constant warning */ +#pragma warning(disable:4127) +#endif + +#define zap_set_state_locked_wait(obj, s) \ + do { \ + int __safety = 100; \ + zap_set_state_locked(obj, s); \ + while(__safety-- && zap_test_flag(obj, ZAP_CHANNEL_STATE_CHANGE)) { \ + zap_sleep(10); \ + } \ + if(!__safety) { \ + zap_log(ZAP_LOG_CRIT, "State change not completed\n"); \ + } \ + } while(0); + +#define zap_wait_for_flag_cleared(obj, flag, time) \ + do { \ + int __safety = time; \ + while(__safety-- && zap_test_flag(obj, flag)) { \ + zap_mutex_unlock(obj->mutex); \ + zap_sleep(10); \ + zap_mutex_lock(obj->mutex); \ + } \ + if(!__safety) { \ + zap_log(ZAP_LOG_CRIT, "flag %d was never cleared\n", flag); \ + } \ + } while(0); + +#define zap_set_state_wait(obj, s) \ + do { \ + zap_channel_set_state(obj, s, 0); \ + zap_wait_for_flag_cleared(obj, ZAP_CHANNEL_STATE_CHANGE, 100); \ + } while(0); + + +typedef enum { + ZAP_STATE_CHANGE_FAIL, + ZAP_STATE_CHANGE_SUCCESS, + ZAP_STATE_CHANGE_SAME, +} zap_state_change_result_t; + +#define zap_set_state_r(obj, s, l, r) if ( obj->state == s ) { \ + if (s != ZAP_CHANNEL_STATE_HANGUP) zap_log(ZAP_LOG_WARNING, "Why bother changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(obj->state), zap_channel_state2str(s)); r = ZAP_STATE_CHANGE_SAME; \ + } else if (zap_test_flag(obj, ZAP_CHANNEL_READY)) { \ + int st = obj->state; \ + r = (zap_channel_set_state(obj, s, l) == ZAP_SUCCESS) ? ZAP_STATE_CHANGE_SUCCESS : ZAP_STATE_CHANGE_FAIL; \ + if (obj->state == s) {zap_log(ZAP_LOG_DEBUG, "Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(st), zap_channel_state2str(s));} \ + else { if (!((obj->state == ZAP_CHANNEL_STATE_HANGUP && s == ZAP_CHANNEL_STATE_TERMINATING) || (obj->state == ZAP_CHANNEL_STATE_HANGUP_COMPLETE && s == ZAP_CHANNEL_STATE_HANGUP) || (obj->state == ZAP_CHANNEL_STATE_TERMINATING && s == ZAP_CHANNEL_STATE_HANGUP))) zap_log(ZAP_LOG_WARNING, "VETO Changing state on %d:%d from %s to %s\n", obj->span_id, obj->chan_id, zap_channel_state2str(st), zap_channel_state2str(s)); } \ + } + + +#define zap_is_dtmf(key) ((key > 47 && key < 58) || (key > 64 && key < 69) || (key > 96 && key < 101) || key == 35 || key == 42 || key == 87 || key == 119) + +/*! + \brief Copy flags from one arbitrary object to another + \command dest the object to copy the flags to + \command src the object to copy the flags from + \command flags the flags to copy +*/ +#define zap_copy_flags(dest, src, flags) (dest)->flags &= ~(flags); (dest)->flags |= ((src)->flags & (flags)) + +/*! + \brief Free a pointer and set it to NULL unless it already is NULL + \command it the pointer +*/ +#define zap_safe_free(it) if (it) {free(it);it=NULL;} + +#define zap_socket_close(it) if (it > -1) { close(it); it = -1;} + + +struct zap_stream_handle { + zap_stream_handle_write_function_t write_function; + zap_stream_handle_raw_write_function_t raw_write_function; + void *data; + void *end; + zap_size_t data_size; + zap_size_t data_len; + zap_size_t alloc_len; + zap_size_t alloc_chunk; +}; + + +OZ_DECLARE_NONSTD(zap_status_t) zap_console_stream_raw_write(zap_stream_handle_t *handle, uint8_t *data, zap_size_t datalen); +OZ_DECLARE_NONSTD(zap_status_t) zap_console_stream_write(zap_stream_handle_t *handle, const char *fmt, ...); + +#define ZAP_CMD_CHUNK_LEN 1024 +#define ZAP_STANDARD_STREAM(s) memset(&s, 0, sizeof(s)); s.data = malloc(ZAP_CMD_CHUNK_LEN); \ + assert(s.data); \ + memset(s.data, 0, ZAP_CMD_CHUNK_LEN); \ + s.end = s.data; \ + s.data_size = ZAP_CMD_CHUNK_LEN; \ + s.write_function = zap_console_stream_write; \ + s.raw_write_function = zap_console_stream_raw_write; \ + s.alloc_len = ZAP_CMD_CHUNK_LEN; \ + s.alloc_chunk = ZAP_CMD_CHUNK_LEN + +struct zap_event { + zap_event_type_t e_type; + uint32_t enum_id; + zap_channel_t *channel; + void *data; +}; + +#define ZAP_TOKEN_STRLEN 128 +#define ZAP_MAX_TOKENS 10 + +static __inline__ char *zap_clean_string(char *s) +{ + char *p; + + for (p = s; p && *p; p++) { + uint8_t x = (uint8_t) *p; + if (x < 32 || x > 127) { + *p = ' '; + } + } + + return s; +} + +struct zap_bitstream { + uint8_t *data; + uint32_t datalen; + uint32_t byte_index; + uint8_t bit_index; + int8_t endian; + uint8_t top; + uint8_t bot; + uint8_t ss; + uint8_t ssv; +}; + +struct zap_fsk_data_state { + dsp_fsk_handle_t *fsk1200_handle; + uint8_t init; + uint8_t *buf; + size_t bufsize; + zap_size_t blen; + zap_size_t bpos; + zap_size_t dlen; + zap_size_t ppos; + int checksum; +}; + +struct zap_fsk_modulator { + teletone_dds_state_t dds; + zap_bitstream_t bs; + uint32_t carrier_bits_start; + uint32_t carrier_bits_stop; + uint32_t chan_sieze_bits; + uint32_t bit_factor; + uint32_t bit_accum; + uint32_t sample_counter; + int32_t samples_per_bit; + int32_t est_bytes; + fsk_modem_types_t modem_type; + zap_fsk_data_state_t *fsk_data; + zap_fsk_write_sample_t write_sample_callback; + void *user_data; + int16_t sample_buffer[64]; +}; + +/** + * Type Of Number (TON) + */ +typedef enum { + ZAP_TON_UNKNOWN = 0, + ZAP_TON_INTERNATIONAL, + ZAP_TON_NATIONAL, + ZAP_TON_NETWORK_SPECIFIC, + ZAP_TON_SUBSCRIBER_NUMBER, + ZAP_TON_ABBREVIATED_NUMBER, + ZAP_TON_RESERVED, + ZAP_TON_INVALID = 255 +} zap_ton_t; + +/** + * Numbering Plan Identification (NPI) + */ +typedef enum { + ZAP_NPI_UNKNOWN = 0, + ZAP_NPI_ISDN = 1, + ZAP_NPI_DATA = 3, + ZAP_NPI_TELEX = 4, + ZAP_NPI_NATIONAL = 8, + ZAP_NPI_PRIVATE = 9, + ZAP_NPI_RESERVED = 10, + ZAP_NPI_INVALID = 255 +} zap_npi_t; + +typedef struct { + char digits[25]; + uint8_t type; + uint8_t plan; +} zap_number_t; + +typedef enum { + ZAP_CALLER_STATE_DIALING, + ZAP_CALLER_STATE_SUCCESS, + ZAP_CALLER_STATE_FAIL +} zap_caller_state_t; + +struct zap_caller_data { + char cid_date[8]; + char cid_name[80]; + zap_number_t cid_num; + zap_number_t ani; + zap_number_t dnis; + zap_number_t rdnis; + char aniII[25]; + uint8_t screen; + uint8_t pres; + char collected[25]; + int CRV; + int hangup_cause; + uint8_t raw_data[1024]; + uint32_t raw_data_len; + uint32_t flags; + zap_caller_state_t call_state; + uint32_t chan_id; +}; + +typedef enum { + ZAP_TYPE_NONE, + ZAP_TYPE_SPAN = 0xFF, + ZAP_TYPE_CHANNEL +} zap_data_type_t; + +/* 2^8 table size, one for each byte value */ +#define ZAP_GAINS_TABLE_SIZE 256 +struct zap_channel { + zap_data_type_t data_type; + uint32_t span_id; + uint32_t chan_id; + uint32_t physical_span_id; + uint32_t physical_chan_id; + uint32_t rate; + uint32_t extra_id; + zap_chan_type_t type; + zap_socket_t sockfd; + zap_channel_flag_t flags; + uint32_t pflags; + uint32_t sflags; + zap_alarm_flag_t alarm_flags; + zap_channel_feature_t features; + zap_codec_t effective_codec; + zap_codec_t native_codec; + uint32_t effective_interval; + uint32_t native_interval; + uint32_t packet_len; + zap_channel_state_t state; + zap_channel_state_t last_state; + zap_channel_state_t init_state; + zap_mutex_t *mutex; + teletone_dtmf_detect_state_t dtmf_detect; + uint32_t buffer_delay; + zap_event_t event_header; + char last_error[256]; + zio_event_cb_t event_callback; + uint32_t skip_read_frames; + zap_buffer_t *dtmf_buffer; + zap_buffer_t *gen_dtmf_buffer; + zap_buffer_t *pre_buffer; + zap_buffer_t *digit_buffer; + zap_buffer_t *fsk_buffer; + zap_mutex_t *pre_buffer_mutex; + uint32_t dtmf_on; + uint32_t dtmf_off; + char *dtmf_hangup_buf; + teletone_generation_session_t tone_session; + zap_time_t last_event_time; + zap_time_t ring_time; + char tokens[ZAP_MAX_TOKENS+1][ZAP_TOKEN_STRLEN]; + uint8_t needed_tones[ZAP_TONEMAP_INVALID]; + uint8_t detected_tones[ZAP_TONEMAP_INVALID]; + zap_tonemap_t last_detected_tone; + uint32_t token_count; + char chan_name[128]; + char chan_number[32]; + zap_filehandle_t fds[2]; + zap_fsk_data_state_t fsk; + uint8_t fsk_buf[80]; + uint32_t ring_count; + void *mod_data; + void *call_data; + struct zap_caller_data caller_data; + struct zap_span *span; + struct zap_io_interface *zio; + zap_hash_t *variable_hash; + unsigned char rx_cas_bits; + uint32_t pre_buffer_size; + unsigned char rxgain_table[ZAP_GAINS_TABLE_SIZE]; + unsigned char txgain_table[ZAP_GAINS_TABLE_SIZE]; + float rxgain; + float txgain; +}; + + +struct zap_sigmsg { + zap_signal_event_t event_id; + uint32_t chan_id; + uint32_t span_id; + zap_channel_t *channel; + void *raw_data; + uint32_t raw_data_len; +}; + + +struct zap_span { + zap_data_type_t data_type; + char *name; + uint32_t span_id; + uint32_t chan_count; + zap_span_flag_t flags; + struct zap_io_interface *zio; + zio_event_cb_t event_callback; + zap_mutex_t *mutex; + zap_trunk_type_t trunk_type; + zap_analog_start_type_t start_type; + zap_signal_type_t signal_type; + void *signal_data; + zio_signal_cb_t signal_cb; + zap_event_t event_header; + char last_error[256]; + char tone_map[ZAP_TONEMAP_INVALID+1][ZAP_TONEMAP_LEN]; + teletone_tone_map_t tone_detect_map[ZAP_TONEMAP_INVALID+1]; + teletone_multi_tone_t tone_finder[ZAP_TONEMAP_INVALID+1]; + zap_channel_t *channels[ZAP_MAX_CHANNELS_SPAN+1]; + zio_channel_outgoing_call_t outgoing_call; + zio_channel_request_t channel_request; + zap_span_start_t start; + zap_span_stop_t stop; + void *mod_data; + char *type; + char *dtmf_hangup; + size_t dtmf_hangup_len; + int suggest_chan_id; + zap_state_map_t *state_map; + zap_caller_data_t default_caller_data; + struct zap_span *next; +}; + + +OZ_DECLARE_DATA extern zap_logger_t zap_log; + +struct zap_io_interface { + const char *name; + zio_configure_span_t configure_span; + zio_configure_t configure; + zio_open_t open; + zio_close_t close; + zio_channel_destroy_t channel_destroy; + zio_span_destroy_t span_destroy; + zio_get_alarms_t get_alarms; + zio_command_t command; + zio_wait_t wait; + zio_read_t read; + zio_write_t write; + zio_span_poll_event_t poll_event; + zio_span_next_event_t next_event; + zio_api_t api; +}; + + +OZ_DECLARE(zap_size_t) zap_fsk_modulator_generate_bit(zap_fsk_modulator_t *fsk_trans, int8_t bit, int16_t *buf, zap_size_t buflen); +OZ_DECLARE(int32_t) zap_fsk_modulator_generate_carrier_bits(zap_fsk_modulator_t *fsk_trans, uint32_t bits); +OZ_DECLARE(void) zap_fsk_modulator_generate_chan_sieze(zap_fsk_modulator_t *fsk_trans); +OZ_DECLARE(void) zap_fsk_modulator_send_data(zap_fsk_modulator_t *fsk_trans); +#define zap_fsk_modulator_send_all(_it) zap_fsk_modulator_generate_chan_sieze(_it); \ + zap_fsk_modulator_generate_carrier_bits(_it, _it->carrier_bits_start); \ + zap_fsk_modulator_send_data(_it); \ + zap_fsk_modulator_generate_carrier_bits(_it, _it->carrier_bits_stop) + +OZ_DECLARE(zap_status_t) zap_fsk_modulator_init(zap_fsk_modulator_t *fsk_trans, + fsk_modem_types_t modem_type, + uint32_t sample_rate, + zap_fsk_data_state_t *fsk_data, + float db_level, + uint32_t carrier_bits_start, + uint32_t carrier_bits_stop, + uint32_t chan_sieze_bits, + zap_fsk_write_sample_t write_sample_callback, + void *user_data); +OZ_DECLARE(int8_t) zap_bitstream_get_bit(zap_bitstream_t *bsp); +OZ_DECLARE(void) zap_bitstream_init(zap_bitstream_t *bsp, uint8_t *data, uint32_t datalen, zap_endian_t endian, uint8_t ss); +OZ_DECLARE(zap_status_t) zap_fsk_data_parse(zap_fsk_data_state_t *state, zap_size_t *type, char **data, zap_size_t *len); +OZ_DECLARE(zap_status_t) zap_fsk_demod_feed(zap_fsk_data_state_t *state, int16_t *data, size_t samples); +OZ_DECLARE(zap_status_t) zap_fsk_demod_destroy(zap_fsk_data_state_t *state); +OZ_DECLARE(int) zap_fsk_demod_init(zap_fsk_data_state_t *state, int rate, uint8_t *buf, size_t bufsize); +OZ_DECLARE(zap_status_t) zap_fsk_data_init(zap_fsk_data_state_t *state, uint8_t *data, uint32_t datalen); +OZ_DECLARE(zap_status_t) zap_fsk_data_add_mdmf(zap_fsk_data_state_t *state, zap_mdmf_type_t type, const uint8_t *data, uint32_t datalen); +OZ_DECLARE(zap_status_t) zap_fsk_data_add_checksum(zap_fsk_data_state_t *state); +OZ_DECLARE(zap_status_t) zap_fsk_data_add_sdmf(zap_fsk_data_state_t *state, const char *date, char *number); +OZ_DECLARE(zap_status_t) zap_channel_outgoing_call(zap_channel_t *zchan); +OZ_DECLARE(void) zap_channel_rotate_tokens(zap_channel_t *zchan); +OZ_DECLARE(void) zap_channel_clear_detected_tones(zap_channel_t *zchan); +OZ_DECLARE(void) zap_channel_clear_needed_tones(zap_channel_t *zchan); +OZ_DECLARE(zap_status_t) zap_channel_get_alarms(zap_channel_t *zchan); +OZ_DECLARE(zap_status_t) zap_channel_send_fsk_data(zap_channel_t *zchan, zap_fsk_data_state_t *fsk_data, float db_level); +OZ_DECLARE(zap_status_t) zap_channel_clear_token(zap_channel_t *zchan, const char *token); +OZ_DECLARE(void) zap_channel_replace_token(zap_channel_t *zchan, const char *old_token, const char *new_token); +OZ_DECLARE(zap_status_t) zap_channel_add_token(zap_channel_t *zchan, char *token, int end); +OZ_DECLARE(zap_status_t) zap_channel_set_state(zap_channel_t *zchan, zap_channel_state_t state, int lock); +OZ_DECLARE(zap_status_t) zap_span_load_tones(zap_span_t *span, const char *mapname); +OZ_DECLARE(zap_size_t) zap_channel_dequeue_dtmf(zap_channel_t *zchan, char *dtmf, zap_size_t len); +OZ_DECLARE(zap_status_t) zap_channel_queue_dtmf(zap_channel_t *zchan, const char *dtmf); +OZ_DECLARE(void) zap_channel_flush_dtmf(zap_channel_t *zchan); +OZ_DECLARE(zap_time_t) zap_current_time_in_ms(void); +OZ_DECLARE(zap_status_t) zap_span_poll_event(zap_span_t *span, uint32_t ms); +OZ_DECLARE(zap_status_t) zap_span_next_event(zap_span_t *span, zap_event_t **event); +OZ_DECLARE(zap_status_t) zap_span_find(uint32_t id, zap_span_t **span); +OZ_DECLARE(zap_status_t) zap_span_create(zap_io_interface_t *zio, zap_span_t **span, const char *name); +OZ_DECLARE(zap_status_t) zap_span_close_all(void); +OZ_DECLARE(zap_status_t) zap_span_add_channel(zap_span_t *span, zap_socket_t sockfd, zap_chan_type_t type, zap_channel_t **chan); +OZ_DECLARE(zap_status_t) zap_span_set_event_callback(zap_span_t *span, zio_event_cb_t event_callback); +OZ_DECLARE(zap_status_t) zap_channel_set_event_callback(zap_channel_t *zchan, zio_event_cb_t event_callback); +OZ_DECLARE(zap_status_t) zap_channel_open(uint32_t span_id, uint32_t chan_id, zap_channel_t **zchan); +OZ_DECLARE(zap_status_t) zap_channel_open_chan(zap_channel_t *zchan); +OZ_DECLARE(zap_status_t) zap_span_channel_use_count(zap_span_t *span, uint32_t *count); +OZ_DECLARE(zap_status_t) zap_channel_open_any(uint32_t span_id, zap_direction_t direction, zap_caller_data_t *caller_data, zap_channel_t **zchan); +OZ_DECLARE(zap_status_t) zap_channel_close(zap_channel_t **zchan); +OZ_DECLARE(zap_status_t) zap_channel_done(zap_channel_t *zchan); +OZ_DECLARE(zap_status_t) zap_channel_use(zap_channel_t *zchan); +OZ_DECLARE(zap_status_t) zap_channel_command(zap_channel_t *zchan, zap_command_t command, void *obj); +OZ_DECLARE(zap_status_t) zap_channel_wait(zap_channel_t *zchan, zap_wait_flag_t *flags, int32_t to); +OZ_DECLARE(zap_status_t) zap_channel_read(zap_channel_t *zchan, void *data, zap_size_t *datalen); +OZ_DECLARE(void) zap_generate_sln_silence(int16_t *data, uint32_t samples, uint32_t divisor); +OZ_DECLARE(zap_status_t) zap_channel_write(zap_channel_t *zchan, void *data, zap_size_t datasize, zap_size_t *datalen); +OZ_DECLARE(zap_status_t) zap_channel_add_var(zap_channel_t *zchan, const char *var_name, const char *value); +OZ_DECLARE(const char *) zap_channel_get_var(zap_channel_t *zchan, const char *var_name); +OZ_DECLARE(zap_status_t) zap_channel_clear_vars(zap_channel_t *zchan); +OZ_DECLARE(zap_status_t) zap_global_init(void); +OZ_DECLARE(zap_status_t) zap_global_destroy(void); +OZ_DECLARE(void) zap_global_set_logger(zap_logger_t logger); +OZ_DECLARE(void) zap_global_set_default_logger(int level); +OZ_DECLARE(uint32_t) zap_separate_string(char *buf, char delim, char **array, int arraylen); +OZ_DECLARE(void) print_bits(uint8_t *b, int bl, char *buf, int blen, int e, uint8_t ss); +OZ_DECLARE(void) print_hex_bytes(uint8_t *data, zap_size_t dlen, char *buf, zap_size_t blen); +OZ_DECLARE_NONSTD(int) zap_hash_equalkeys(void *k1, void *k2); +OZ_DECLARE_NONSTD(uint32_t) zap_hash_hashfromstring(void *ky); +OZ_DECLARE(uint32_t) zap_running(void); +OZ_DECLARE(zap_status_t) zap_channel_complete_state(zap_channel_t *zchan); +OZ_DECLARE(zap_status_t) zap_channel_init(zap_channel_t *zchan); +OZ_DECLARE(int) zap_load_modules(void); +OZ_DECLARE(zap_status_t) zap_unload_modules(void); +OZ_DECLARE(zap_status_t) zap_configure_span(const char *type, zap_span_t *span, zio_signal_cb_t sig_cb, ...); +OZ_DECLARE(zap_status_t) zap_span_start(zap_span_t *span); +OZ_DECLARE(zap_status_t) zap_span_stop(zap_span_t *span); +OZ_DECLARE(zap_status_t) zap_span_send_signal(zap_span_t *span, zap_sigmsg_t *sigmsg); +OZ_DECLARE(int) zap_load_module(const char *name); +OZ_DECLARE(int) zap_load_module_assume(const char *name); +OZ_DECLARE(zap_status_t) zap_span_find_by_name(const char *name, zap_span_t **span); +OZ_DECLARE(char *) zap_api_execute(const char *type, const char *cmd); +OZ_DECLARE(int) zap_vasprintf(char **ret, const char *fmt, va_list ap); +OZ_DECLARE(zap_status_t) zap_channel_set_caller_data(zap_channel_t *zchan, zap_caller_data_t *caller_data); +OZ_DECLARE(void) zap_cpu_monitor_disable(void); + +ZIO_CODEC_FUNCTION(zio_slin2ulaw); +ZIO_CODEC_FUNCTION(zio_ulaw2slin); +ZIO_CODEC_FUNCTION(zio_slin2alaw); +ZIO_CODEC_FUNCTION(zio_alaw2slin); +ZIO_CODEC_FUNCTION(zio_ulaw2alaw); +ZIO_CODEC_FUNCTION(zio_alaw2ulaw); + +#ifdef DEBUG_LOCKS +#define zap_mutex_lock(_x) printf("++++++lock %s:%d\n", __FILE__, __LINE__) && _zap_mutex_lock(_x) +#define zap_mutex_trylock(_x) printf("++++++try %s:%d\n", __FILE__, __LINE__) && _zap_mutex_trylock(_x) +#define zap_mutex_unlock(_x) printf("------unlock %s:%d\n", __FILE__, __LINE__) && _zap_mutex_unlock(_x) +#else +#define zap_mutex_lock(_x) _zap_mutex_lock(_x) +#define zap_mutex_trylock(_x) _zap_mutex_trylock(_x) +#define zap_mutex_unlock(_x) _zap_mutex_unlock(_x) +#endif + + +static __inline__ void zap_set_state_all(zap_span_t *span, zap_channel_state_t state) +{ + uint32_t j; + zap_mutex_lock(span->mutex); + for(j = 1; j <= span->chan_count; j++) { + zap_set_state_locked((span->channels[j]), state); + } + zap_mutex_unlock(span->mutex); +} + +static __inline__ int zap_check_state_all(zap_span_t *span, zap_channel_state_t state) +{ + uint32_t j; + for(j = 1; j <= span->chan_count; j++) { + if (span->channels[j]->state != state || zap_test_flag(span->channels[j], ZAP_CHANNEL_STATE_CHANGE)) { + return 0; + } + } + + return 1; +} + +static __inline__ void zap_set_flag_all(zap_span_t *span, uint32_t flag) +{ + uint32_t j; + zap_mutex_lock(span->mutex); + for(j = 1; j <= span->chan_count; j++) { + zap_set_flag_locked((span->channels[j]), flag); + } + zap_mutex_unlock(span->mutex); +} + +static __inline__ void zap_clear_flag_all(zap_span_t *span, uint32_t flag) +{ + uint32_t j; + zap_mutex_lock(span->mutex); + for(j = 1; j <= span->chan_count; j++) { + zap_clear_flag_locked((span->channels[j]), flag); + } + zap_mutex_unlock(span->mutex); +} + +#endif + +/* 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: + */ diff --git a/libs/openzap/src/include/sangoma_tdm_api.h b/libs/openzap/src/include/sangoma_tdm_api.h new file mode 100644 index 0000000000..062d4e219d --- /dev/null +++ b/libs/openzap/src/include/sangoma_tdm_api.h @@ -0,0 +1,321 @@ +/***************************************************************************** + * sangoma_tdm_api.h Sangoma TDM API Portability functions + * + * Author(s): Anthony Minessale II + * Nenad Corbic + * Michael Jerris + * David Rokhvarg + * + * Copyright: (c) 2006 Nenad Corbic + * Anthony Minessale II + * (c) 1984-2007 Sangoma Technologies Inc. + * + * ============================================================================ + */ + +#ifndef _SANGOMA_TDM_API_H +#define _SANGOMA_TDM_API_H + +/* This entire block of defines and includes from this line, through #define FNAME_LEN probably dont belong here */ +/* most of them probably belong in wanpipe_defines.h, then each header file listed included below properly included */ +/* in the header files that depend on them, leaving only the include for wanpipe_tdm_api.h left in this file or */ +/* possibly integrating the rest of this file diretly into wanpipe_tdm_api.h */ +#ifndef __WINDOWS__ +#if defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) +#define __WINDOWS__ +#endif /* defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32) */ +#endif /* ndef __WINDOWS__ */ + +#if defined(__WINDOWS__) +#if defined(_MSC_VER) +/* disable some warnings caused by wanpipe headers that will need to be fixed in those headers */ +#pragma warning(disable:4201 4214) + +/* sang_api.h(74) : warning C4201: nonstandard extension used : nameless struct/union */ + +/* wanpipe_defines.h(219) : warning C4214: nonstandard extension used : bit field types other than int */ +/* wanpipe_defines.h(220) : warning C4214: nonstandard extension used : bit field types other than int */ +/* this will break for any compilers that are strict ansi or strict c99 */ + +/* The following definition for that struct should resolve this warning and work for 32 and 64 bit */ +#if 0 +struct iphdr { + +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned ihl:4, + version:4; +#elif defined (__BIG_ENDIAN_BITFIELD) + unsigned version:4, + ihl:4; +#else +# error "unknown byteorder!" +#endif + unsigned tos:8; + unsigned tot_len:16; + unsigned id:16; + unsigned frag_off:16; + __u8 ttl; + __u8 protocol; + __u16 check; + __u32 saddr; + __u32 daddr; + /*The options start here. */ +}; +#endif /* #if 0 */ + +#define __inline__ __inline +#endif /* defined(_MSC_VER) */ +#include +/* do we like the name WP_INVALID_SOCKET or should it be changed? */ +#define WP_INVALID_SOCKET INVALID_HANDLE_VALUE +#else /* defined(__WINDOWS__) */ +#define WP_INVALID_SOCKET -1 +#include +#include +#include +#endif + +#include +#include +#include +#include +#ifdef __WINDOWS__ +#include +#include +#endif +#include + +#define FNAME_LEN 50 + + +#if defined(__WINDOWS__) +/* This might be broken on windows, as POLL_EVENT_TELEPHONY seems to be commented out in sang_api.h.. it should be added to POLLPRI */ +#define POLLPRI (POLL_EVENT_LINK_STATE | POLL_EVENT_LINK_CONNECT | POLL_EVENT_LINK_DISCONNECT) +#endif + +/* return -1 for error, 0 for timeout or 1 for success. *flags is set to the poll evetns POLLIN | POLLOUT | POLLPRI based on the result of the poll */ +/* on windows we actually have POLLPRI defined with several events, so we could theoretically poll */ +/* for specific events. Is there any way to do this on *nix as well? */ + +/* a cross platform way to poll on an actual pollset (span and/or list of spans) will probably also be needed for analog */ +/* so we can have one analong handler thread that will deal with all the idle analog channels for events */ +/* the alternative would be for the driver to provide one socket for all of the oob events for all analog channels */ +static __inline__ int tdmv_api_wait_socket(sng_fd_t fd, int timeout, int *flags) +{ +#if defined(__WINDOWS__) + DWORD ln; + API_POLL_STRUCT api_poll; + + memset(&api_poll, 0x00, sizeof(API_POLL_STRUCT)); + + api_poll.user_flags_bitmap = *flags; + api_poll.timeout = timeout; + + if (!DeviceIoControl( + fd, + IoctlApiPoll, + (LPVOID)NULL, + 0L, + (LPVOID)&api_poll, + sizeof(API_POLL_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL)) { + return -1; + } + + *flags = 0; + + switch(api_poll.operation_status) + { + case SANG_STATUS_RX_DATA_AVAILABLE: + break; + + case SANG_STATUS_RX_DATA_TIMEOUT: + return 0; + + default: + return -1; + } + + if (api_poll.poll_events_bitmap == 0){ + return -1; + } + + if (api_poll.poll_events_bitmap & POLL_EVENT_TIMEOUT) { + return 0; + } + + *flags = api_poll.poll_events_bitmap; + + return 1; +#else + struct pollfd pfds[1]; + int res; + + memset(&pfds[0], 0, sizeof(pfds[0])); + pfds[0].fd = fd; + pfds[0].events = *flags; + res = poll(pfds, 1, timeout); + *flags = 0; + + if (pfds[0].revents & POLLERR) { + res = -1; + } + + if (res > 0) { + *flags = pfds[0].revents; + } + + return res; +#endif +} + +/* on windows right now, there is no way to specify if we want to read events here or not, we allways get them here */ +/* we need some what to select if we are reading regular tdm msgs or events */ +/* need to either have 2 functions, 1 for events, 1 for regural read, or a flag on this function to choose */ +/* 2 functions preferred. Need implementation for the event function for both nix and windows that is threadsafe */ +static __inline__ int tdmv_api_readmsg_tdm(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen) +{ + /* What do we need to do here to avoid having to do all */ + /* the memcpy's on windows and still maintain api compat with nix */ + int rx_len=0; +#if defined(__WINDOWS__) + static RX_DATA_STRUCT rx_data; + api_header_t *pri; + wp_tdm_api_rx_hdr_t *tdm_api_rx_hdr; + wp_tdm_api_rx_hdr_t *user_buf = (wp_tdm_api_rx_hdr_t*)hdrbuf; + DWORD ln; + + if (hdrlen != sizeof(wp_tdm_api_rx_hdr_t)){ + return -1; + } + + if (!DeviceIoControl( + fd, + IoctlReadCommand, + (LPVOID)NULL, + 0L, + (LPVOID)&rx_data, + sizeof(RX_DATA_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + )){ + return -1; + } + + pri = &rx_data.api_header; + tdm_api_rx_hdr = (wp_tdm_api_rx_hdr_t*)rx_data.data; + + user_buf->wp_tdm_api_event_type = pri->operation_status; + + switch(pri->operation_status) + { + case SANG_STATUS_RX_DATA_AVAILABLE: + if (pri->data_length > datalen){ + break; + } + memcpy(databuf, rx_data.data, pri->data_length); + rx_len = pri->data_length; + break; + + default: + break; + } + +#else + struct msghdr msg; + struct iovec iov[2]; + + memset(&msg,0,sizeof(struct msghdr)); + + iov[0].iov_len=hdrlen; + iov[0].iov_base=hdrbuf; + + iov[1].iov_len=datalen; + iov[1].iov_base=databuf; + + msg.msg_iovlen=2; + msg.msg_iov=iov; + + rx_len = read(fd,&msg,datalen+hdrlen); + + if (rx_len <= sizeof(wp_tdm_api_rx_hdr_t)){ + return -EINVAL; + } + + rx_len-=sizeof(wp_tdm_api_rx_hdr_t); +#endif + return rx_len; +} + +static __inline__ int tdmv_api_writemsg_tdm(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen) +{ + /* What do we need to do here to avoid having to do all */ + /* the memcpy's on windows and still maintain api compat with nix */ + int bsent = 0; +#if defined(__WINDOWS__) + static TX_DATA_STRUCT local_tx_data; + api_header_t *pri; + DWORD ln; + + /* Are these really not needed or used??? What about for nix?? */ + (void)hdrbuf; + (void)hdrlen; + + pri = &local_tx_data.api_header; + + pri->data_length = datalen; + memcpy(local_tx_data.data, databuf, pri->data_length); + + if (!DeviceIoControl( + fd, + IoctlWriteCommand, + (LPVOID)&local_tx_data, + (ULONG)sizeof(TX_DATA_STRUCT), + (LPVOID)&local_tx_data, + sizeof(TX_DATA_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + )){ + return -1; + } + + if (local_tx_data.api_header.operation_status == SANG_STATUS_SUCCESS) { + bsent = datalen; + } +#else + struct msghdr msg; + struct iovec iov[2]; + + memset(&msg,0,sizeof(struct msghdr)); + + iov[0].iov_len = hdrlen; + iov[0].iov_base = hdrbuf; + + iov[1].iov_len = datalen; + iov[1].iov_base = databuf; + + msg.msg_iovlen = 2; + msg.msg_iov = iov; + + bsent = write(fd, &msg, datalen + hdrlen); + if (bsent > 0){ + bsent -= sizeof(wp_tdm_api_tx_hdr_t); + } +#endif + return bsent; +} + +#endif /* _SANGOMA_TDM_API_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: + */ + diff --git a/libs/openzap/src/include/uart.h b/libs/openzap/src/include/uart.h new file mode 100644 index 0000000000..89c3a13abc --- /dev/null +++ b/libs/openzap/src/include/uart.h @@ -0,0 +1,76 @@ +/* + * uart.h + * + * Copyright (c) 2005 Robert Krten. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This module contains the manifest constants and declarations for + * the UART module. + * + * 2005 06 19 R. Krten created +*/ + +#ifndef __UART_H__ +#define __UART_H__ + +typedef void (*bytehandler_func_t) (void *, int); +typedef void (*bithandler_func_t) (void *, int); + + +typedef struct dsp_uart_attr_s +{ + bytehandler_func_t bytehandler; /* byte handler */ + void *bytehandler_arg; /* arbitrary ID passed to bytehandler as first argument */ +} dsp_uart_attr_t; + +typedef struct +{ + dsp_uart_attr_t attr; + int have_start; /* wait for start bit to show up */ + int data; /* data buffer */ + int nbits; /* number of bits accumulated so far */ +} dsp_uart_handle_t; + +/* + * Function prototypes + * + * General calling order is: + * a) create the attributes structure (dsp_uart_attr_init) + * b) initialize fields in the attributes structure (dsp_uart_attr_set_*) + * c) create a Bell-202 handle (dsp_uart_create) + * d) feed bits through dsp_uart_bit_handler +*/ + +void dsp_uart_attr_init(dsp_uart_attr_t *attributes); + +bytehandler_func_t dsp_uart_attr_get_bytehandler(dsp_uart_attr_t *attributes, void **bytehandler_arg); +void dsp_uart_attr_set_bytehandler(dsp_uart_attr_t *attributes, bytehandler_func_t bytehandler, void *bytehandler_arg); + +dsp_uart_handle_t * dsp_uart_create(dsp_uart_attr_t *attributes); +void dsp_uart_destroy(dsp_uart_handle_t **handle); + +void dsp_uart_bit_handler(void *handle, int bit); + +#endif + diff --git a/libs/openzap/src/include/zap_buffer.h b/libs/openzap/src/include/zap_buffer.h new file mode 100644 index 0000000000..9a05420979 --- /dev/null +++ b/libs/openzap/src/include/zap_buffer.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 ZAP_BUFFER_H +#define ZAP_BUFFER_H + +#include "openzap.h" + +/** + * @defgroup zap_buffer Buffer Routines + * @ingroup buffer + * The purpose of this module is to make a plain buffering interface that can be used for read/write buffers + * throughout the application. + * @{ + */ +struct zap_buffer; +typedef struct zap_buffer zap_buffer_t; + +/*! \brief Allocate a new dynamic zap_buffer + * \param buffer returned pointer to the new buffer + * \param blocksize length to realloc by as data is added + * \param start_len ammount of memory to reserve initially + * \param max_len length the buffer is allowed to grow to + * \return status + */ +OZ_DECLARE(zap_status_t) zap_buffer_create(zap_buffer_t **buffer, zap_size_t blocksize, zap_size_t start_len, zap_size_t max_len); + +/*! \brief Get the length of a zap_buffer_t + * \param buffer any buffer of type zap_buffer_t + * \return int size of the buffer. + */ +OZ_DECLARE(zap_size_t) zap_buffer_len(zap_buffer_t *buffer); + +/*! \brief Get the freespace of a zap_buffer_t + * \param buffer any buffer of type zap_buffer_t + * \return int freespace in the buffer. + */ +OZ_DECLARE(zap_size_t) zap_buffer_freespace(zap_buffer_t *buffer); + +/*! \brief Get the in use amount of a zap_buffer_t + * \param buffer any buffer of type zap_buffer_t + * \return int ammount of buffer curently in use + */ +OZ_DECLARE(zap_size_t) zap_buffer_inuse(zap_buffer_t *buffer); + +/*! \brief Read data from a zap_buffer_t up to the ammount of datalen if it is available. Remove read data from buffer. + * \param buffer any buffer of type zap_buffer_t + * \param data pointer to the read data to be returned + * \param datalen amount of data to be returned + * \return int ammount of data actually read + */ +OZ_DECLARE(zap_size_t) zap_buffer_read(zap_buffer_t *buffer, void *data, zap_size_t datalen); + +/*! \brief Read data endlessly from a zap_buffer_t + * \param buffer any buffer of type zap_buffer_t + * \param data pointer to the read data to be returned + * \param datalen amount of data to be returned + * \return int ammount of data actually read + * \note Once you have read all the data from the buffer it will loop around. + */ +OZ_DECLARE(zap_size_t) zap_buffer_read_loop(zap_buffer_t *buffer, void *data, zap_size_t datalen); + +/*! \brief Assign a number of loops to read + * \param buffer any buffer of type zap_buffer_t + * \param loops the number of loops (-1 for infinite) + */ +OZ_DECLARE(void) zap_buffer_set_loops(zap_buffer_t *buffer, int32_t loops); + +/*! \brief Write data into a zap_buffer_t up to the length of datalen + * \param buffer any buffer of type zap_buffer_t + * \param data pointer to the data to be written + * \param datalen amount of data to be written + * \return int amount of buffer used after the write, or 0 if no space available + */ +OZ_DECLARE(zap_size_t) zap_buffer_write(zap_buffer_t *buffer, const void *data, zap_size_t datalen); + +/*! \brief Remove data from the buffer + * \param buffer any buffer of type zap_buffer_t + * \param datalen amount of data to be removed + * \return int size of buffer, or 0 if unable to toss that much data + */ +OZ_DECLARE(zap_size_t) zap_buffer_toss(zap_buffer_t *buffer, zap_size_t datalen); + +/*! \brief Remove all data from the buffer + * \param buffer any buffer of type zap_buffer_t + */ +OZ_DECLARE(void) zap_buffer_zero(zap_buffer_t *buffer); + +/*! \brief Destroy the buffer + * \param buffer buffer to destroy + * \note only neccessary on dynamic buffers (noop on pooled ones) + */ +OZ_DECLARE(void) zap_buffer_destroy(zap_buffer_t **buffer); + +/*! \brief Seek to offset from the beginning of the buffer + * \param buffer buffer to seek + * \param datalen offset in bytes + * \return new position + */ +OZ_DECLARE(zap_size_t) zap_buffer_seek(zap_buffer_t *buffer, zap_size_t datalen); + +/** @} */ + +OZ_DECLARE(zap_size_t) zap_buffer_zwrite(zap_buffer_t *buffer, const void *data, zap_size_t datalen); + +#endif +/* 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: + */ diff --git a/libs/openzap/src/include/zap_config.h b/libs/openzap/src/include/zap_config.h new file mode 100644 index 0000000000..f2f2e374bf --- /dev/null +++ b/libs/openzap/src/include/zap_config.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + */ + +/** + * @defgroup config Config File Parser + * @ingroup config + * This module implements a basic interface and file format parser + * + *
+ *
+ * EXAMPLE 
+ * 
+ * [category1]
+ * var1 => val1
+ * var2 => val2
+ * \# lines that begin with \# are comments
+ * \#var3 => val3
+ * 
+ * @{ + */ + +#ifndef ZAP_CONFIG_H +#define ZAP_CONFIG_H + +#include "openzap.h" +#define ZAP_URL_SEPARATOR "://" + + +#ifdef WIN32 +#define ZAP_PATH_SEPARATOR "\\" +#ifndef ZAP_CONFIG_DIR +#define ZAP_CONFIG_DIR "c:\\openzap" +#endif +#define zap_is_file_path(file) (*(file +1) == ':' || *file == '/' || strstr(file, SWITCH_URL_SEPARATOR)) +#else +#define ZAP_PATH_SEPARATOR "/" +#ifndef ZAP_CONFIG_DIR +#define ZAP_CONFIG_DIR "/etc/openzap" +#endif +#define zap_is_file_path(file) ((*file == '/') || strstr(file, SWITCH_URL_SEPARATOR)) +#endif + +typedef struct zap_config zap_config_t; + +/*! \brief A simple file handle representing an open configuration file **/ +struct zap_config { + /*! FILE stream buffer to the opened file */ + FILE *file; + /*! path to the file */ + char path[512]; + /*! current category */ + char category[256]; + /*! current section */ + char section[256]; + /*! buffer of current line being read */ + char buf[1024]; + /*! current line number in file */ + int lineno; + /*! current category number in file */ + int catno; + /*! current section number in file */ + int sectno; + + int lockto; +}; + +/*! + \brief Open a configuration file + \param cfg (zap_config_t *) config handle to use + \param file_path path to the file + \return 1 (true) on success 0 (false) on failure +*/ +int zap_config_open_file(zap_config_t * cfg, const char *file_path); + +/*! + \brief Close a previously opened configuration file + \param cfg (zap_config_t *) config handle to use +*/ +void zap_config_close_file(zap_config_t * cfg); + +/*! + \brief Retrieve next name/value pair from configuration file + \param cfg (zap_config_t *) config handle to use + \param var pointer to aim at the new variable name + \param val pointer to aim at the new value +*/ +int zap_config_next_pair(zap_config_t * cfg, char **var, char **val); + +/*! + \brief Retrieve the CAS bits from a configuration string value + \param strvalue pointer to the configuration string value (expected to be in format whatever:xxxx) + \param outbits pointer to aim at the CAS bits +*/ +OZ_DECLARE (int) zap_config_get_cas_bits(char *strvalue, unsigned char *outbits); + + +/** @} */ +#endif +/* 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: + */ diff --git a/libs/openzap/src/include/zap_cpu_monitor.h b/libs/openzap/src/include/zap_cpu_monitor.h new file mode 100644 index 0000000000..d16c7ad175 --- /dev/null +++ b/libs/openzap/src/include/zap_cpu_monitor.h @@ -0,0 +1,74 @@ +/* * Copyright (c) 2009, Sangoma Technologies + * Moises Silva + * 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. + * + * + * Contributors: + * David Yat Sin + * + */ + +/*! \brief opaque cpu stats structure */ +struct zap_cpu_monitor_stats; + +/*! + * \brief create a new cpu monitor + * \return profile timer structure previously created with new_profile_timer, NULL on error + */ +OZ_DECLARE(struct zap_cpu_monitor_stats*) zap_new_cpu_monitor(void); + +/*! + * \brief Deletes cpu_monitor + */ +OZ_DECLARE(void) zap_delete_cpu_monitor(struct zap_cpu_monitor_stats *p); + +/*! + * \brief provides the percentage of idle system time + * \param p cpu_stats structure previously created with zap_new_cpu_monitor + * \param pointer to store the percentage of idle time + * \return -1 on error 0 for success + */ +OZ_DECLARE(zap_status_t) zap_cpu_get_system_idle_time (struct zap_cpu_monitor_stats *p, double *idle_percentage); + + + + + +/* 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: + */ diff --git a/libs/openzap/src/include/zap_dso.h b/libs/openzap/src/include/zap_dso.h new file mode 100644 index 0000000000..bf51f92b18 --- /dev/null +++ b/libs/openzap/src/include/zap_dso.h @@ -0,0 +1,44 @@ +/* + * Cross Platform dso/dll load abstraction + * Copyright(C) 2008 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + + +#ifndef _ZAP_DSO_H +#define _ZAP_DSO_H + +typedef void (*zap_func_ptr_t) (void); +typedef void * zap_dso_lib_t; + +void zap_dso_destroy(zap_dso_lib_t *lib); +zap_dso_lib_t zap_dso_open(const char *path, char **err); +void *zap_dso_func_sym(zap_dso_lib_t lib, const char *sym, char **err); + + +#endif + +/* 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 + */ + diff --git a/libs/openzap/src/include/zap_m3ua.h b/libs/openzap/src/include/zap_m3ua.h new file mode 100644 index 0000000000..18de6db838 --- /dev/null +++ b/libs/openzap/src/include/zap_m3ua.h @@ -0,0 +1,128 @@ +/* + * zap_m3ua.h + * openzap + * + * Created by Shane Burrell on 4/3/08. + * Copyright 2008 Shane Burrell. All rights reserved. + * + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * + * 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 "m3ua_client.h" +#include "openzap.h" +enum e_sigboost_event_id_values +{ + SIGBOOST_EVENT_CALL_START = 0x80, /*128*/ + SIGBOOST_EVENT_CALL_START_ACK = 0x81, /*129*/ + SIGBOOST_EVENT_CALL_START_NACK = 0x82, /*130*/ + SIGBOOST_EVENT_CALL_START_NACK_ACK = 0x83, /*131*/ + SIGBOOST_EVENT_CALL_ANSWERED = 0x84, /*132*/ + SIGBOOST_EVENT_CALL_STOPPED = 0x85, /*133*/ + SIGBOOST_EVENT_CALL_STOPPED_ACK = 0x86, /*134*/ + SIGBOOST_EVENT_SYSTEM_RESTART = 0x87, /*135*/ + SIGBOOST_EVENT_SYSTEM_RESTART_ACK = 0x88, /*136*/ + /* Following IDs are ss7boost to sangoma_mgd only. */ + SIGBOOST_EVENT_HEARTBEAT = 0x89, /*137*/ + SIGBOOST_EVENT_INSERT_CHECK_LOOP = 0x8a, /*138*/ + SIGBOOST_EVENT_REMOVE_CHECK_LOOP = 0x8b, /*139*/ + SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE = 0x8c, /*140*/ +}; +enum e_sigboost_release_cause_values +{ + SIGBOOST_RELEASE_CAUSE_UNDEFINED = 0, + SIGBOOST_RELEASE_CAUSE_NORMAL = 16, + SIGBOOST_RELEASE_CAUSE_BUSY = 17, + /* probable elimination */ + //SIGBOOST_RELEASE_CAUSE_BUSY = 0x91, /* 145 */ + //SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST = 0x92, /* 146 */ + //SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET = 0x93, /* 147 */ + //SIGBOOST_RELEASE_CAUSE_NOANSWER = 0x94, /* 148 */ +}; + +enum e_sigboost_call_setup_ack_nack_cause_values +{ + SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 117, /* unused Q.850 value */ + SIGBOOST_CALL_SETUP_NACK_TEST_CKT_BUSY = 118, /* unused Q.850 value */ + SIGBOOST_CALL_SETUP_NACK_INVALID_NUMBER = 28, + /* probable elimination */ + //SIGBOOST_CALL_SETUP_RESERVED = 0x00, + //SIGBOOST_CALL_SETUP_CIRCUIT_RESET = 0x10, + //SIGBOOST_CALL_SETUP_NACK_CKT_START_TIMEOUT = 0x11, + //SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP = 0x17, +}; +typedef enum { + M3UA_SPAN_SIGNALING_M3UA, + M3UA_SPAN_SIGNALING_SS7BOX, + +} M3UA_TSpanSignaling; +#define M3UA_SPAN_STRINGS "M3UA", "SS7BOX" +ZAP_STR2ENUM_P(m3ua_str2span, m3ua_span2str, M3UA_TSpanSignaling) + + + +typedef enum { + ZAP_M3UA_RUNNING = (1 << 0) +} zap_m3uat_flag_t; + +/*typedef struct m3ua_data { + m3uac_connection_t mcon; + m3uac_connection_t pcon; + zio_signal_cb_t signal_cb; + uint32_t flags; +} m3ua_data_t; + +*/ +/*typedef struct mu3a_link { + ss7bc_connection_t mcon; + ss7bc_connection_t pcon; + zio_signal_cb_t signal_cb; + uint32_t flags; +} zap_m3ua_data_t; +*/ + +zap_status_t m3ua_init(zap_io_interface_t **zint); +zap_status_t m3ua_destroy(void); +zap_status_t m3ua_start(zap_span_t *span); + + + +/* 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: + */ + diff --git a/libs/openzap/src/include/zap_threadmutex.h b/libs/openzap/src/include/zap_threadmutex.h new file mode 100644 index 0000000000..bc043ca180 --- /dev/null +++ b/libs/openzap/src/include/zap_threadmutex.h @@ -0,0 +1,56 @@ +/* + * Cross Platform Thread/Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + + +#ifndef _ZAP_THREADMUTEX_H +#define _ZAP_THREADMUTEX_H + +#include "openzap.h" + +typedef struct zap_mutex zap_mutex_t; +typedef struct zap_thread zap_thread_t; +typedef void *(*zap_thread_function_t) (zap_thread_t *, void *); + +OZ_DECLARE(zap_status_t) zap_thread_create_detached(zap_thread_function_t func, void *data); +OZ_DECLARE(zap_status_t) zap_thread_create_detached_ex(zap_thread_function_t func, void *data, zap_size_t stack_size); +OZ_DECLARE(void) zap_thread_override_default_stacksize(zap_size_t size); +OZ_DECLARE(zap_status_t) zap_mutex_create(zap_mutex_t **mutex); +OZ_DECLARE(zap_status_t) zap_mutex_destroy(zap_mutex_t **mutex); +OZ_DECLARE(zap_status_t) _zap_mutex_lock(zap_mutex_t *mutex); +OZ_DECLARE(zap_status_t) _zap_mutex_trylock(zap_mutex_t *mutex); +OZ_DECLARE(zap_status_t) _zap_mutex_unlock(zap_mutex_t *mutex); + +OZ_DECLARE(zap_status_t) zap_interrupt_create(zap_interrupt_t **ininterrupt, zap_socket_t device); +OZ_DECLARE(zap_status_t) zap_interrupt_wait(zap_interrupt_t *interrupt, int ms); +OZ_DECLARE(zap_status_t) zap_interrupt_signal(zap_interrupt_t *interrupt); +OZ_DECLARE(zap_status_t) zap_interrupt_destroy(zap_interrupt_t **ininterrupt); +OZ_DECLARE(zap_status_t) zap_interrupt_multiple_wait(zap_interrupt_t *interrupts[], zap_size_t size, int ms); +#endif + +/* 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: + */ + diff --git a/libs/openzap/src/include/zap_types.h b/libs/openzap/src/include/zap_types.h new file mode 100644 index 0000000000..a97f98abd7 --- /dev/null +++ b/libs/openzap/src/include/zap_types.h @@ -0,0 +1,639 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 ZAP_TYPES_H +#define ZAP_TYPES_H +#include "fsk.h" + +#ifdef WIN32 +#define ZAP_INVALID_SOCKET INVALID_HANDLE_VALUE +#else +#define ZAP_INVALID_SOCKET -1 +#endif +#define zap_array_len(obj) sizeof(obj)/sizeof(obj[0]) + +#ifdef WIN32 +#include +typedef HANDLE zap_socket_t; +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +typedef intptr_t zap_ssize_t; +typedef int zap_filehandle_t; +#else +#include +#include +#include +#include +typedef int zap_socket_t; +typedef ssize_t zap_ssize_t; +typedef int zap_filehandle_t; +#endif + +#define TAG_END NULL + +typedef size_t zap_size_t; +struct zap_io_interface; +typedef struct zap_interrupt zap_interrupt_t; + +#define ZAP_COMMAND_OBJ_INT *((int *)obj) +#define ZAP_COMMAND_OBJ_CHAR_P (char *)obj +#define ZAP_COMMAND_OBJ_FLOAT *((float *)obj) +#define ZAP_FSK_MOD_FACTOR 0x10000 +#define ZAP_DEFAULT_DTMF_ON 250 +#define ZAP_DEFAULT_DTMF_OFF 50 + +#define ZAP_END -1 +#define ZAP_ANY_STATE -1 + +typedef uint64_t zap_time_t; + +typedef enum { + ZAP_ENDIAN_BIG = 1, + ZAP_ENDIAN_LITTLE = -1 +} zap_endian_t; + +typedef enum { + ZAP_CID_TYPE_SDMF = 0x04, + ZAP_CID_TYPE_MDMF = 0x80 +} zap_cid_type_t; + +typedef enum { + MDMF_DATETIME = 1, + MDMF_PHONE_NUM = 2, + MDMF_DDN = 3, + MDMF_NO_NUM = 4, + MDMF_PHONE_NAME = 7, + MDMF_NO_NAME = 8, + MDMF_ALT_ROUTE = 9, + MDMF_INVALID = 10 +} zap_mdmf_type_t; +#define MDMF_STRINGS "X", "DATETIME", "PHONE_NUM", "DDN", "NO_NUM", "X", "X", "PHONE_NAME", "NO_NAME", "ALT_ROUTE", "INVALID" +ZAP_STR2ENUM_P(zap_str2zap_mdmf_type, zap_mdmf_type2str, zap_mdmf_type_t) + +#define ZAP_TONEMAP_LEN 128 +typedef enum { + ZAP_TONEMAP_NONE, + ZAP_TONEMAP_DIAL, + ZAP_TONEMAP_RING, + ZAP_TONEMAP_BUSY, + ZAP_TONEMAP_FAIL1, + ZAP_TONEMAP_FAIL2, + ZAP_TONEMAP_FAIL3, + ZAP_TONEMAP_ATTN, + ZAP_TONEMAP_CALLWAITING_CAS, + ZAP_TONEMAP_CALLWAITING_SAS, + ZAP_TONEMAP_CALLWAITING_ACK, + ZAP_TONEMAP_INVALID +} zap_tonemap_t; +#define TONEMAP_STRINGS "NONE", "DIAL", "RING", "BUSY", "FAIL1", "FAIL2", "FAIL3", "ATTN", "CALLWAITING-CAS", "CALLWAITING-SAS", "CALLWAITING-ACK", "INVALID" +ZAP_STR2ENUM_P(zap_str2zap_tonemap, zap_tonemap2str, zap_tonemap_t) + +typedef enum { + ZAP_TRUNK_E1, + ZAP_TRUNK_T1, + ZAP_TRUNK_J1, + ZAP_TRUNK_BRI, + ZAP_TRUNK_BRI_PTMP, + ZAP_TRUNK_FXO, + ZAP_TRUNK_FXS, + ZAP_TRUNK_EM, + ZAP_TRUNK_NONE +} zap_trunk_type_t; +#define TRUNK_STRINGS "E1", "T1", "J1", "BRI", "BRI_PTMP", "FXO", "FXS", "EM", "NONE" +ZAP_STR2ENUM_P(zap_str2zap_trunk_type, zap_trunk_type2str, zap_trunk_type_t) + +typedef enum { + ZAP_ANALOG_START_KEWL, + ZAP_ANALOG_START_LOOP, + ZAP_ANALOG_START_GROUND, + ZAP_ANALOG_START_WINK, + ZAP_ANALOG_START_NA +} zap_analog_start_type_t; +#define START_TYPE_STRINGS "KEWL", "LOOP", "GROUND", "WINK", "NA" +ZAP_STR2ENUM_P(zap_str2zap_analog_start_type, zap_analog_start_type2str, zap_analog_start_type_t) + +typedef enum { + ZAP_OOB_DTMF, + ZAP_OOB_ONHOOK, + ZAP_OOB_OFFHOOK, + ZAP_OOB_WINK, + ZAP_OOB_FLASH, + ZAP_OOB_RING_START, + ZAP_OOB_RING_STOP, + ZAP_OOB_ALARM_TRAP, + ZAP_OOB_ALARM_CLEAR, + ZAP_OOB_NOOP, + ZAP_OOB_CAS_BITS_CHANGE, + ZAP_OOB_INVALID +} zap_oob_event_t; +#define OOB_STRINGS "DTMF", "ONHOOK", "OFFHOOK", "WINK", "FLASH", "RING_START", "RING_STOP", "ALARM_TRAP", "ALARM_CLEAR", "NOOP", "CAS_BITS_CHANGE", "INVALID" +ZAP_STR2ENUM_P(zap_str2zap_oob_event, zap_oob_event2str, zap_oob_event_t) + +typedef enum { + ZAP_ALARM_NONE = 0, + ZAP_ALARM_RECOVER = (1 << 0), + ZAP_ALARM_LOOPBACK = (1 << 2), + ZAP_ALARM_YELLOW = (1 << 3), + ZAP_ALARM_RED = (1 << 4), + ZAP_ALARM_BLUE = (1 << 5), + ZAP_ALARM_NOTOPEN = ( 1 << 6), + ZAP_ALARM_AIS = ( 1 << 7), + ZAP_ALARM_RAI = ( 1 << 8), + ZAP_ALARM_GENERAL = ( 1 << 30) +} zap_alarm_flag_t; + +typedef enum { + ZAP_SIGTYPE_NONE, + ZAP_SIGTYPE_ISDN, + ZAP_SIGTYPE_RBS, + ZAP_SIGTYPE_ANALOG, + ZAP_SIGTYPE_SANGOMABOOST, + ZAP_SIGTYPE_M3UA, + ZAP_SIGTYPE_R2 +} zap_signal_type_t; + +typedef enum { + ZAP_SIGEVENT_START, + ZAP_SIGEVENT_STOP, + ZAP_SIGEVENT_TRANSFER, + ZAP_SIGEVENT_ANSWER, + ZAP_SIGEVENT_UP, + ZAP_SIGEVENT_FLASH, + ZAP_SIGEVENT_PROGRESS, + ZAP_SIGEVENT_PROGRESS_MEDIA, + ZAP_SIGEVENT_NOTIFY, + ZAP_SIGEVENT_TONE_DETECTED, + ZAP_SIGEVENT_ALARM_TRAP, + ZAP_SIGEVENT_ALARM_CLEAR, + ZAP_SIGEVENT_MISC, + ZAP_SIGEVENT_COLLECTED_DIGIT, + ZAP_SIGEVENT_ADD_CALL, + ZAP_SIGEVENT_RESTART, + ZAP_SIGEVENT_INVALID +} zap_signal_event_t; +#define SIGNAL_STRINGS "START", "STOP", "TRANSFER", "ANSWER", "UP", "FLASH", "PROGRESS", \ + "PROGRESS_MEDIA", "NOTIFY", "TONE_DETECTED", "ALARM_TRAP", "ALARM_CLEAR", "MISC", "COLLECTED_DIGIT", "ADD_CALL", "RESTART", "INVALID" +ZAP_STR2ENUM_P(zap_str2zap_signal_event, zap_signal_event2str, zap_signal_event_t) + +typedef enum { + ZAP_EVENT_NONE, + ZAP_EVENT_DTMF, + ZAP_EVENT_OOB, + ZAP_EVENT_COUNT +} zap_event_type_t; + +typedef enum { + ZAP_TOP_DOWN, + ZAP_BOTTOM_UP +} zap_direction_t; + +typedef enum { + ZAP_SUCCESS, + ZAP_FAIL, + ZAP_MEMERR, + ZAP_TIMEOUT, + ZAP_NOTIMPL, + ZAP_CHECKSUM_ERROR, + ZAP_STATUS_COUNT, + ZAP_BREAK +} zap_status_t; + +typedef enum { + ZAP_NO_FLAGS = 0, + ZAP_READ = (1 << 0), + ZAP_WRITE = (1 << 1), + ZAP_EVENTS = (1 << 2) +} zap_wait_flag_t; + +typedef enum { + ZAP_CODEC_ULAW = 0, + ZAP_CODEC_ALAW = 8, + ZAP_CODEC_SLIN = 10, + ZAP_CODEC_NONE = (1 << 30) +} zap_codec_t; + +typedef enum { + ZAP_TONE_DTMF = (1 << 0) +} zap_tone_type_t; + +typedef enum { + ZAP_COMMAND_NOOP, + ZAP_COMMAND_SET_INTERVAL, + ZAP_COMMAND_GET_INTERVAL, + ZAP_COMMAND_SET_CODEC, + ZAP_COMMAND_GET_CODEC, + ZAP_COMMAND_SET_NATIVE_CODEC, + ZAP_COMMAND_GET_NATIVE_CODEC, + ZAP_COMMAND_ENABLE_DTMF_DETECT, + ZAP_COMMAND_DISABLE_DTMF_DETECT, + ZAP_COMMAND_SEND_DTMF, + ZAP_COMMAND_SET_DTMF_ON_PERIOD, + ZAP_COMMAND_GET_DTMF_ON_PERIOD, + ZAP_COMMAND_SET_DTMF_OFF_PERIOD, + ZAP_COMMAND_GET_DTMF_OFF_PERIOD, + ZAP_COMMAND_GENERATE_RING_ON, + ZAP_COMMAND_GENERATE_RING_OFF, + ZAP_COMMAND_OFFHOOK, + ZAP_COMMAND_ONHOOK, + ZAP_COMMAND_FLASH, + ZAP_COMMAND_WINK, + ZAP_COMMAND_ENABLE_PROGRESS_DETECT, + ZAP_COMMAND_DISABLE_PROGRESS_DETECT, + ZAP_COMMAND_TRACE_INPUT, + ZAP_COMMAND_TRACE_OUTPUT, + ZAP_COMMAND_ENABLE_CALLERID_DETECT, + ZAP_COMMAND_DISABLE_CALLERID_DETECT, + ZAP_COMMAND_ENABLE_ECHOCANCEL, + ZAP_COMMAND_DISABLE_ECHOCANCEL, + ZAP_COMMAND_ENABLE_ECHOTRAIN, + ZAP_COMMAND_DISABLE_ECHOTRAIN, + ZAP_COMMAND_SET_CAS_BITS, + ZAP_COMMAND_GET_CAS_BITS, + ZAP_COMMAND_SET_RX_GAIN, + ZAP_COMMAND_GET_RX_GAIN, + ZAP_COMMAND_SET_TX_GAIN, + ZAP_COMMAND_GET_TX_GAIN, + ZAP_COMMAND_FLUSH_TX_BUFFERS, + ZAP_COMMAND_FLUSH_RX_BUFFERS, + ZAP_COMMAND_FLUSH_BUFFERS, + ZAP_COMMAND_SET_PRE_BUFFER_SIZE, + ZAP_COMMAND_ENABLE_LOOP, + ZAP_COMMAND_DISABLE_LOOP, + ZAP_COMMAND_COUNT +} zap_command_t; + +typedef enum { + ZAP_SPAN_CONFIGURED = (1 << 0), + ZAP_SPAN_READY = (1 << 1), + ZAP_SPAN_STATE_CHANGE = (1 << 2), + ZAP_SPAN_SUSPENDED = (1 << 3), + ZAP_SPAN_IN_THREAD = (1 << 4), + ZAP_SPAN_STOP_THREAD = (1 << 5) +} zap_span_flag_t; + +typedef enum { + ZAP_CHAN_TYPE_B, + ZAP_CHAN_TYPE_DQ921, + ZAP_CHAN_TYPE_DQ931, + ZAP_CHAN_TYPE_FXS, + ZAP_CHAN_TYPE_FXO, + ZAP_CHAN_TYPE_EM, + ZAP_CHAN_TYPE_CAS, + ZAP_CHAN_TYPE_COUNT +} zap_chan_type_t; + +#define CHAN_TYPE_STRINGS "B", "DQ921", "DQ931", "FXS", "FXO", "EM", "CAS", "INVALID" +ZAP_STR2ENUM_P(zap_str2zap_chan_type, zap_chan_type2str, zap_chan_type_t) + +#define ZAP_IS_VOICE_CHANNEL(zap_chan) ((zap_chan)->type != ZAP_CHAN_TYPE_DQ921 && (zap_chan)->type != ZAP_CHAN_TYPE_DQ931) + +typedef enum { + ZAP_CHANNEL_FEATURE_DTMF_DETECT = (1 << 0), + ZAP_CHANNEL_FEATURE_DTMF_GENERATE = (1 << 1), + ZAP_CHANNEL_FEATURE_CODECS = (1 << 2), + ZAP_CHANNEL_FEATURE_INTERVAL = (1 << 3), + ZAP_CHANNEL_FEATURE_CALLERID = (1 << 4), + ZAP_CHANNEL_FEATURE_PROGRESS = (1 << 5) +} zap_channel_feature_t; + +typedef enum { + ZAP_CHANNEL_STATE_DOWN, + ZAP_CHANNEL_STATE_HOLD, + ZAP_CHANNEL_STATE_SUSPENDED, + ZAP_CHANNEL_STATE_DIALTONE, + ZAP_CHANNEL_STATE_COLLECT, + ZAP_CHANNEL_STATE_RING, + ZAP_CHANNEL_STATE_BUSY, + ZAP_CHANNEL_STATE_ATTN, + ZAP_CHANNEL_STATE_GENRING, + ZAP_CHANNEL_STATE_DIALING, + ZAP_CHANNEL_STATE_GET_CALLERID, + ZAP_CHANNEL_STATE_CALLWAITING, + ZAP_CHANNEL_STATE_RESTART, + ZAP_CHANNEL_STATE_PROGRESS, + ZAP_CHANNEL_STATE_PROGRESS_MEDIA, + ZAP_CHANNEL_STATE_UP, + ZAP_CHANNEL_STATE_IDLE, + ZAP_CHANNEL_STATE_TERMINATING, + ZAP_CHANNEL_STATE_CANCEL, + ZAP_CHANNEL_STATE_HANGUP, + ZAP_CHANNEL_STATE_HANGUP_COMPLETE, + ZAP_CHANNEL_STATE_IN_LOOP, + ZAP_CHANNEL_STATE_INVALID +} zap_channel_state_t; +#define CHANNEL_STATE_STRINGS "DOWN", "HOLD", "SUSPENDED", "DIALTONE", "COLLECT", \ + "RING", "BUSY", "ATTN", "GENRING", "DIALING", "GET_CALLERID", "CALLWAITING", \ + "RESTART", "PROGRESS", "PROGRESS_MEDIA", "UP", "IDLE", "TERMINATING", "CANCEL", "HANGUP", "HANGUP_COMPLETE", "INVALID" +ZAP_STR2ENUM_P(zap_str2zap_channel_state, zap_channel_state2str, zap_channel_state_t) + +typedef enum { + ZAP_CHANNEL_CONFIGURED = (1 << 0), + ZAP_CHANNEL_READY = (1 << 1), + ZAP_CHANNEL_OPEN = (1 << 2), + ZAP_CHANNEL_DTMF_DETECT = (1 << 3), + ZAP_CHANNEL_SUPRESS_DTMF = (1 << 4), + ZAP_CHANNEL_TRANSCODE = (1 << 5), + ZAP_CHANNEL_BUFFER = (1 << 6), + ZAP_CHANNEL_EVENT = (1 << 7), + ZAP_CHANNEL_INTHREAD = (1 << 8), + ZAP_CHANNEL_WINK = (1 << 9), + ZAP_CHANNEL_FLASH = (1 << 10), + ZAP_CHANNEL_STATE_CHANGE = (1 << 11), + ZAP_CHANNEL_HOLD = (1 << 12), + ZAP_CHANNEL_INUSE = (1 << 13), + ZAP_CHANNEL_OFFHOOK = (1 << 14), + ZAP_CHANNEL_RINGING = (1 << 15), + ZAP_CHANNEL_PROGRESS_DETECT = (1 << 16), + ZAP_CHANNEL_CALLERID_DETECT = (1 << 17), + ZAP_CHANNEL_OUTBOUND = (1 << 18), + ZAP_CHANNEL_SUSPENDED = (1 << 19), + ZAP_CHANNEL_3WAY = (1 << 20), + ZAP_CHANNEL_PROGRESS = (1 << 21), + ZAP_CHANNEL_MEDIA = (1 << 22), + ZAP_CHANNEL_ANSWERED = (1 << 23), + ZAP_CHANNEL_MUTE = (1 << 24), + ZAP_CHANNEL_USE_RX_GAIN = (1 << 25), + ZAP_CHANNEL_USE_TX_GAIN = (1 << 26), +} zap_channel_flag_t; + +typedef enum { + ZSM_NONE, + ZSM_UNACCEPTABLE, + ZSM_ACCEPTABLE +} zap_state_map_type_t; + +typedef enum { + ZSD_INBOUND, + ZSD_OUTBOUND, +} zap_state_direction_t; + +#define ZAP_MAP_NODE_SIZE 512 +#define ZAP_MAP_MAX ZAP_CHANNEL_STATE_INVALID+2 + +struct zap_state_map_node { + zap_state_direction_t direction; + zap_state_map_type_t type; + zap_channel_state_t check_states[ZAP_MAP_MAX]; + zap_channel_state_t states[ZAP_MAP_MAX]; +}; +typedef struct zap_state_map_node zap_state_map_node_t; + +struct zap_state_map { + zap_state_map_node_t nodes[ZAP_MAP_NODE_SIZE]; +}; +typedef struct zap_state_map zap_state_map_t; + +typedef struct zap_channel zap_channel_t; +typedef struct zap_event zap_event_t; +typedef struct zap_sigmsg zap_sigmsg_t; +typedef struct zap_span zap_span_t; +typedef struct zap_caller_data zap_caller_data_t; +typedef struct zap_io_interface zap_io_interface_t; + +struct zap_stream_handle; +typedef struct zap_stream_handle zap_stream_handle_t; + +typedef zap_status_t (*zap_stream_handle_raw_write_function_t) (zap_stream_handle_t *handle, uint8_t *data, zap_size_t datalen); +typedef zap_status_t (*zap_stream_handle_write_function_t) (zap_stream_handle_t *handle, const char *fmt, ...); + +#define ZIO_CHANNEL_REQUEST_ARGS (zap_span_t *span, uint32_t chan_id, zap_direction_t direction, zap_caller_data_t *caller_data, zap_channel_t **zchan) +#define ZIO_CHANNEL_OUTGOING_CALL_ARGS (zap_channel_t *zchan) +#define ZIO_SPAN_POLL_EVENT_ARGS (zap_span_t *span, uint32_t ms) +#define ZIO_SPAN_NEXT_EVENT_ARGS (zap_span_t *span, zap_event_t **event) +#define ZIO_SIGNAL_CB_ARGS (zap_sigmsg_t *sigmsg) +#define ZIO_EVENT_CB_ARGS (zap_channel_t *zchan, zap_event_t *event) +#define ZIO_CODEC_ARGS (void *data, zap_size_t max, zap_size_t *datalen) +#define ZIO_CONFIGURE_SPAN_ARGS (zap_span_t *span, const char *str, zap_chan_type_t type, char *name, char *number) +#define ZIO_CONFIGURE_ARGS (const char *category, const char *var, const char *val, int lineno) +#define ZIO_OPEN_ARGS (zap_channel_t *zchan) +#define ZIO_CLOSE_ARGS (zap_channel_t *zchan) +#define ZIO_CHANNEL_DESTROY_ARGS (zap_channel_t *zchan) +#define ZIO_SPAN_DESTROY_ARGS (zap_span_t *span) +#define ZIO_COMMAND_ARGS (zap_channel_t *zchan, zap_command_t command, void *obj) +#define ZIO_WAIT_ARGS (zap_channel_t *zchan, zap_wait_flag_t *flags, int32_t to) +#define ZIO_GET_ALARMS_ARGS (zap_channel_t *zchan) +#define ZIO_READ_ARGS (zap_channel_t *zchan, void *data, zap_size_t *datalen) +#define ZIO_WRITE_ARGS (zap_channel_t *zchan, void *data, zap_size_t *datalen) +#define ZIO_IO_LOAD_ARGS (zap_io_interface_t **zio) +#define ZIO_IO_UNLOAD_ARGS (void) +#define ZIO_SIG_LOAD_ARGS (void) +#define ZIO_SIG_CONFIGURE_ARGS (zap_span_t *span, zio_signal_cb_t sig_cb, va_list ap) +#define ZIO_SIG_UNLOAD_ARGS (void) +#define ZIO_API_ARGS (zap_stream_handle_t *stream, const char *data) + +typedef zap_status_t (*zio_channel_request_t) ZIO_CHANNEL_REQUEST_ARGS ; +typedef zap_status_t (*zio_channel_outgoing_call_t) ZIO_CHANNEL_OUTGOING_CALL_ARGS ; +typedef zap_status_t (*zio_span_poll_event_t) ZIO_SPAN_POLL_EVENT_ARGS ; +typedef zap_status_t (*zio_span_next_event_t) ZIO_SPAN_NEXT_EVENT_ARGS ; +typedef zap_status_t (*zio_signal_cb_t) ZIO_SIGNAL_CB_ARGS ; +typedef zap_status_t (*zio_event_cb_t) ZIO_EVENT_CB_ARGS ; +typedef zap_status_t (*zio_codec_t) ZIO_CODEC_ARGS ; +typedef zap_status_t (*zio_configure_span_t) ZIO_CONFIGURE_SPAN_ARGS ; +typedef zap_status_t (*zio_configure_t) ZIO_CONFIGURE_ARGS ; +typedef zap_status_t (*zio_open_t) ZIO_OPEN_ARGS ; +typedef zap_status_t (*zio_close_t) ZIO_CLOSE_ARGS ; +typedef zap_status_t (*zio_channel_destroy_t) ZIO_CHANNEL_DESTROY_ARGS ; +typedef zap_status_t (*zio_span_destroy_t) ZIO_SPAN_DESTROY_ARGS ; +typedef zap_status_t (*zio_get_alarms_t) ZIO_GET_ALARMS_ARGS ; +typedef zap_status_t (*zio_command_t) ZIO_COMMAND_ARGS ; +typedef zap_status_t (*zio_wait_t) ZIO_WAIT_ARGS ; +typedef zap_status_t (*zio_read_t) ZIO_READ_ARGS ; +typedef zap_status_t (*zio_write_t) ZIO_WRITE_ARGS ; +typedef zap_status_t (*zio_io_load_t) ZIO_IO_LOAD_ARGS ; +typedef zap_status_t (*zio_sig_load_t) ZIO_SIG_LOAD_ARGS ; +typedef zap_status_t (*zio_sig_configure_t) ZIO_SIG_CONFIGURE_ARGS ; +typedef zap_status_t (*zio_io_unload_t) ZIO_IO_UNLOAD_ARGS ; +typedef zap_status_t (*zio_sig_unload_t) ZIO_SIG_UNLOAD_ARGS ; +typedef zap_status_t (*zio_api_t) ZIO_API_ARGS ; + + +#define ZIO_CHANNEL_REQUEST_FUNCTION(name) zap_status_t name ZIO_CHANNEL_REQUEST_ARGS +#define ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(name) zap_status_t name ZIO_CHANNEL_OUTGOING_CALL_ARGS +#define ZIO_SPAN_POLL_EVENT_FUNCTION(name) zap_status_t name ZIO_SPAN_POLL_EVENT_ARGS +#define ZIO_SPAN_NEXT_EVENT_FUNCTION(name) zap_status_t name ZIO_SPAN_NEXT_EVENT_ARGS +#define ZIO_SIGNAL_CB_FUNCTION(name) zap_status_t name ZIO_SIGNAL_CB_ARGS +#define ZIO_EVENT_CB_FUNCTION(name) zap_status_t name ZIO_EVENT_CB_ARGS +#define ZIO_CODEC_FUNCTION(name) OZ_DECLARE_NONSTD(zap_status_t) name ZIO_CODEC_ARGS +#define ZIO_CONFIGURE_SPAN_FUNCTION(name) zap_status_t name ZIO_CONFIGURE_SPAN_ARGS +#define ZIO_CONFIGURE_FUNCTION(name) zap_status_t name ZIO_CONFIGURE_ARGS +#define ZIO_OPEN_FUNCTION(name) zap_status_t name ZIO_OPEN_ARGS +#define ZIO_CLOSE_FUNCTION(name) zap_status_t name ZIO_CLOSE_ARGS +#define ZIO_CHANNEL_DESTROY_FUNCTION(name) zap_status_t name ZIO_CHANNEL_DESTROY_ARGS +#define ZIO_SPAN_DESTROY_FUNCTION(name) zap_status_t name ZIO_SPAN_DESTROY_ARGS +#define ZIO_GET_ALARMS_FUNCTION(name) zap_status_t name ZIO_GET_ALARMS_ARGS +#define ZIO_COMMAND_FUNCTION(name) zap_status_t name ZIO_COMMAND_ARGS +#define ZIO_WAIT_FUNCTION(name) zap_status_t name ZIO_WAIT_ARGS +#define ZIO_READ_FUNCTION(name) zap_status_t name ZIO_READ_ARGS +#define ZIO_WRITE_FUNCTION(name) zap_status_t name ZIO_WRITE_ARGS +#define ZIO_IO_LOAD_FUNCTION(name) zap_status_t name ZIO_IO_LOAD_ARGS +#define ZIO_SIG_LOAD_FUNCTION(name) zap_status_t name ZIO_SIG_LOAD_ARGS +#define ZIO_SIG_CONFIGURE_FUNCTION(name) zap_status_t name ZIO_SIG_CONFIGURE_ARGS +#define ZIO_IO_UNLOAD_FUNCTION(name) zap_status_t name ZIO_IO_UNLOAD_ARGS +#define ZIO_SIG_UNLOAD_FUNCTION(name) zap_status_t name ZIO_SIG_UNLOAD_ARGS +#define ZIO_API_FUNCTION(name) zap_status_t name ZIO_API_ARGS + +#include "zap_dso.h" + + + +typedef struct { + char name[256]; + zio_io_load_t io_load; + zio_io_unload_t io_unload; + zio_sig_load_t sig_load; + zio_sig_configure_t sig_configure; + zio_sig_unload_t sig_unload; + zap_dso_lib_t lib; + char path[256]; +} zap_module_t; + +#ifndef __FUNCTION__ +#define __FUNCTION__ (const char *)__func__ +#endif + +#define ZAP_PRE __FILE__, __FUNCTION__, __LINE__ +#define ZAP_LOG_LEVEL_DEBUG 7 +#define ZAP_LOG_LEVEL_INFO 6 +#define ZAP_LOG_LEVEL_NOTICE 5 +#define ZAP_LOG_LEVEL_WARNING 4 +#define ZAP_LOG_LEVEL_ERROR 3 +#define ZAP_LOG_LEVEL_CRIT 2 +#define ZAP_LOG_LEVEL_ALERT 1 +#define ZAP_LOG_LEVEL_EMERG 0 + +#define ZAP_LOG_DEBUG ZAP_PRE, ZAP_LOG_LEVEL_DEBUG +#define ZAP_LOG_INFO ZAP_PRE, ZAP_LOG_LEVEL_INFO +#define ZAP_LOG_NOTICE ZAP_PRE, ZAP_LOG_LEVEL_NOTICE +#define ZAP_LOG_WARNING ZAP_PRE, ZAP_LOG_LEVEL_WARNING +#define ZAP_LOG_ERROR ZAP_PRE, ZAP_LOG_LEVEL_ERROR +#define ZAP_LOG_CRIT ZAP_PRE, ZAP_LOG_LEVEL_CRIT +#define ZAP_LOG_ALERT ZAP_PRE, ZAP_LOG_LEVEL_ALERT +#define ZAP_LOG_EMERG ZAP_PRE, ZAP_LOG_LEVEL_EMERG + +typedef struct zap_fsk_data_state zap_fsk_data_state_t; +typedef int (*zap_fsk_data_decoder_t)(zap_fsk_data_state_t *state); +typedef zap_status_t (*zap_fsk_write_sample_t)(int16_t *buf, zap_size_t buflen, void *user_data); +typedef void (*zap_logger_t)(const char *file, const char *func, int line, int level, const char *fmt, ...); +typedef struct hashtable zap_hash_t; +typedef struct hashtable_iterator zap_hash_iterator_t; +typedef struct key zap_hash_key_t; +typedef struct value zap_hash_val_t; +typedef struct zap_bitstream zap_bitstream_t; +typedef struct zap_fsk_modulator zap_fsk_modulator_t; +typedef zap_status_t (*zap_span_start_t)(zap_span_t *span); +typedef zap_status_t (*zap_span_stop_t)(zap_span_t *span); + +typedef enum { + ZAP_CAUSE_NONE = 0, + ZAP_CAUSE_UNALLOCATED = 1, + ZAP_CAUSE_NO_ROUTE_TRANSIT_NET = 2, + ZAP_CAUSE_NO_ROUTE_DESTINATION = 3, + ZAP_CAUSE_CHANNEL_UNACCEPTABLE = 6, + ZAP_CAUSE_CALL_AWARDED_DELIVERED = 7, + ZAP_CAUSE_NORMAL_CLEARING = 16, + ZAP_CAUSE_USER_BUSY = 17, + ZAP_CAUSE_NO_USER_RESPONSE = 18, + ZAP_CAUSE_NO_ANSWER = 19, + ZAP_CAUSE_SUBSCRIBER_ABSENT = 20, + ZAP_CAUSE_CALL_REJECTED = 21, + ZAP_CAUSE_NUMBER_CHANGED = 22, + ZAP_CAUSE_REDIRECTION_TO_NEW_DESTINATION = 23, + ZAP_CAUSE_EXCHANGE_ROUTING_ERROR = 25, + ZAP_CAUSE_DESTINATION_OUT_OF_ORDER = 27, + ZAP_CAUSE_INVALID_NUMBER_FORMAT = 28, + ZAP_CAUSE_FACILITY_REJECTED = 29, + ZAP_CAUSE_RESPONSE_TO_STATUS_ENQUIRY = 30, + ZAP_CAUSE_NORMAL_UNSPECIFIED = 31, + ZAP_CAUSE_NORMAL_CIRCUIT_CONGESTION = 34, + ZAP_CAUSE_NETWORK_OUT_OF_ORDER = 38, + ZAP_CAUSE_NORMAL_TEMPORARY_FAILURE = 41, + ZAP_CAUSE_SWITCH_CONGESTION = 42, + ZAP_CAUSE_ACCESS_INFO_DISCARDED = 43, + ZAP_CAUSE_REQUESTED_CHAN_UNAVAIL = 44, + ZAP_CAUSE_PRE_EMPTED = 45, + ZAP_CAUSE_FACILITY_NOT_SUBSCRIBED = 50, + ZAP_CAUSE_OUTGOING_CALL_BARRED = 52, + ZAP_CAUSE_INCOMING_CALL_BARRED = 54, + ZAP_CAUSE_BEARERCAPABILITY_NOTAUTH = 57, + ZAP_CAUSE_BEARERCAPABILITY_NOTAVAIL = 58, + ZAP_CAUSE_SERVICE_UNAVAILABLE = 63, + ZAP_CAUSE_BEARERCAPABILITY_NOTIMPL = 65, + ZAP_CAUSE_CHAN_NOT_IMPLEMENTED = 66, + ZAP_CAUSE_FACILITY_NOT_IMPLEMENTED = 69, + ZAP_CAUSE_SERVICE_NOT_IMPLEMENTED = 79, + ZAP_CAUSE_INVALID_CALL_REFERENCE = 81, + ZAP_CAUSE_INCOMPATIBLE_DESTINATION = 88, + ZAP_CAUSE_INVALID_MSG_UNSPECIFIED = 95, + ZAP_CAUSE_MANDATORY_IE_MISSING = 96, + ZAP_CAUSE_MESSAGE_TYPE_NONEXIST = 97, + ZAP_CAUSE_WRONG_MESSAGE = 98, + ZAP_CAUSE_IE_NONEXIST = 99, + ZAP_CAUSE_INVALID_IE_CONTENTS = 100, + ZAP_CAUSE_WRONG_CALL_STATE = 101, + ZAP_CAUSE_RECOVERY_ON_TIMER_EXPIRE = 102, + ZAP_CAUSE_MANDATORY_IE_LENGTH_ERROR = 103, + ZAP_CAUSE_PROTOCOL_ERROR = 111, + ZAP_CAUSE_INTERWORKING = 127, + ZAP_CAUSE_SUCCESS = 142, + ZAP_CAUSE_ORIGINATOR_CANCEL = 487, + ZAP_CAUSE_CRASH = 500, + ZAP_CAUSE_SYSTEM_SHUTDOWN = 501, + ZAP_CAUSE_LOSE_RACE = 502, + ZAP_CAUSE_MANAGER_REQUEST = 503, + ZAP_CAUSE_BLIND_TRANSFER = 600, + ZAP_CAUSE_ATTENDED_TRANSFER = 601, + ZAP_CAUSE_ALLOTTED_TIMEOUT = 602, + ZAP_CAUSE_USER_CHALLENGE = 603, + ZAP_CAUSE_MEDIA_TIMEOUT = 604 +} zap_call_cause_t; + + +#endif + +/* 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: + */ + diff --git a/libs/openzap/src/isdn/5ESSStateNT.c b/libs/openzap/src/isdn/5ESSStateNT.c new file mode 100644 index 0000000000..1eb9415422 --- /dev/null +++ b/libs/openzap/src/isdn/5ESSStateNT.c @@ -0,0 +1,132 @@ +/***************************************************************************** + + FileName: 5ESSStateNT.c + + Contents: AT&T 5ESS ISDN State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + Copyright (c) 2007, Michael S. Collins, All rights reserved. + email:mcollins@fcnetwork.com + + 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 Case Labs, Ltd nor the names of its 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 "5ESS.h" + +/***************************************************************************** + Function: ATT5ESSCreateNT + + Description: Will create the AT&T 5ESS ISDN NT as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void ATT5ESSCreateNT(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingNT, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingNT, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectNT, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckNT, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressNT, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupNT, Q931Umes_Setup, Q931Pmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckNT, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeNT, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckNT, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectNT, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendNT, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckNT, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectNT, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationNT, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectNT, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseNT, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteNT, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartNT, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckNT, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlNT, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationNT, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyNT, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusNT, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryNT, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentNT, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityNT, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldNT, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckNT, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectNT, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterNT, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveNT, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckNT, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectNT, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* TODO define state table here */ +} diff --git a/libs/openzap/src/isdn/5ESSStateTE.c b/libs/openzap/src/isdn/5ESSStateTE.c new file mode 100644 index 0000000000..e2ededd9ac --- /dev/null +++ b/libs/openzap/src/isdn/5ESSStateTE.c @@ -0,0 +1,291 @@ +/***************************************************************************** + + FileName: 5ESSStateTE.c + + Contents: AT&T 5ESS ISDN State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + This reference implementation uses a process per message, + meaning that each message must check call states. This + is easier for dialect maintenance as each message proc + can be replaced individually. A new TE variant only + need to copy the Q931CreateTE and replace those procs or + need to override. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + Copyright (c) 2007, Michael S. Collins, All rights reserved. + email:mcollins@fcnetwork.com + + 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 Case Labs, Ltd nor the names of its 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 "5ESS.h" +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: ATT5ESSCreateTE + + Description: Will create the AT&T 5ESS TE as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void ATT5ESSCreateTE(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingTE, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingTE, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, ATT5ESSProc0x07TE, ATT5ESSUmes_0x07, ATT5ESSPmes_0x07); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, ATT5ESSProc0x0fTE, ATT5ESSUmes_0x0f, ATT5ESSPmes_0x0f); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressTE, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupTE, ATT5ESSUmes_Setup, ATT5ESSPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckTE, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeTE, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckTE, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectTE, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendTE, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckTE, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectTE, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationTE, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectTE, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseTE, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteTE, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartTE, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckTE, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlTE, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationTE, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyTE, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusTE, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryTE, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentTE, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityTE, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldTE, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckTE, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectTE, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterTE, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveTE, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckTE, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectTE, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANGE_STATUS, i, Q931Pie_ChangeStatus, Q931Uie_ChangeStatus); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* State 0 Idle */ + Q931AddStateEntry(i, Q931_U0, Q931mes_RESUME, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_STATUS, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE_COMPLETE, 4); + + /* State 1 Call Initiating */ + Q931AddStateEntry(i, Q931_U1, Q931mes_DISCONNECT, 2); + Q931AddStateEntry(i, Q931_U1, Q931mes_SETUP_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_RELEASE_COMPLETE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CONNECT, 4); + + /* State 2 Overlap Sending */ + Q931AddStateEntry(i, Q931_U2, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U2, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_RELEASE, 2); + + /* State 3 Outgoing Call Proceeding */ + Q931AddStateEntry(i, Q931_U3, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_RELEASE, 2); + + /* State 4 Call Delivered */ + Q931AddStateEntry(i, Q931_U4, Q931mes_CONNECT, 4); + + /* State 6 Call Precent */ + Q931AddStateEntry(i, Q931_U6, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CALL_PROCEEDING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE_COMPLETE, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U6, Q931mes_DISCONNECT, 4); + + /* State 7 Call Received */ + Q931AddStateEntry(i, Q931_U7, Q931mes_CONNECT, 2); + + /* State 8 Connect request */ + Q931AddStateEntry(i, Q931_U8, Q931mes_CONNECT_ACKNOWLEDGE, 4); + + /* State 9 Incoming Call Proceeding */ + Q931AddStateEntry(i, Q931_U9, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_PROGRESS, 2); + + /* State 10 Active */ + Q931AddStateEntry(i, Q931_U10, Q931mes_SUSPEND, 2); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 4); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 2); + + /* State 11 Disconnect Request */ + Q931AddStateEntry(i, Q931_U11, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_NOTIFY, 4); + + /* State 12 Disconnect Ind */ + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 2); + + /* State 15 Suspend Request */ + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_REJECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_RELEASE, 4); + +/* TODO + Q931AddStateEntry(i, Q931_U17, + Q931AddStateEntry(i, Q931_U19, + Q931AddStateEntry(i, Q931_U25, +*/ +} + +/***************************************************************************** + + Function: ATT5ESSProc0x0fTE + +*****************************************************************************/ +L3INT ATT5ESSProc0x0fTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->ProtDisc == 8) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + if (iFrom == 4) { + /* TODO Add proc here*/ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom ==2) { + /* TODO Add proc here*/ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + + if (pMes->ProtDisc == 3 && pTrunk->autoServiceAck) { + printf("autoServiceAck is on, responding to Service Req from network...\n"); + Q931AckService(pTrunk, buf); + } + } + return ret; + +} + +/***************************************************************************** + + Function: ATT5ESSProc0x07TE + +*****************************************************************************/ +L3INT ATT5ESSProc0x07TE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->ProtDisc == 8) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + if (iFrom == 4) { + /* TODO Add proc here*/ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here*/ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; + +} diff --git a/libs/openzap/src/isdn/5ESSmes.c b/libs/openzap/src/isdn/5ESSmes.c new file mode 100644 index 0000000000..7cdd21e7cc --- /dev/null +++ b/libs/openzap/src/isdn/5ESSmes.c @@ -0,0 +1,361 @@ +/***************************************************************************** + + FileName: 5ESSmes.c + + Contents: Pack/Unpack functions. These functions will unpack a 5ESS ISDN + message from the bit packed original format into structs + that contains variables sized by the user. It will also pack + the struct back into a Q.931 message as required. + + See 5ESS.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + Copyright (c) 2007, Michael S. Collins, All rights reserved. + email:mcollins@fcnetwork.com + + 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 Case Labs, Ltd nor the names of its 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 "5ESS.h" + +/***************************************************************************** + + Function: ATT5ESSUmes_Setup + +*****************************************************************************/ +L3INT ATT5ESSUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT ir = 0; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + L3UCHAR last_codeset = 0, codeset = 0; + L3UCHAR shift_nolock = 1; + + while (IOff < Size) { + + if (shift_nolock) { + codeset = last_codeset; + } + + if ((IBuf[IOff] & 0xF0) == Q931ie_SHIFT) { + shift_nolock = (IBuf[IOff] & 0x08); + if (shift_nolock) { + last_codeset = codeset; + } + codeset = ((IBuf[IOff] & 0x07)); + IOff++; + } + + if (codeset == 0) { + switch (IBuf[IOff]) + { + case Q931ie_SENDING_COMPLETE: + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_NETWORK_SPECIFIC_FACILITIES: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_SIGNAL: + case Q931ie_CALLING_PARTY_NUMBER: + case Q931ie_CALLING_PARTY_SUBADDRESS: + case Q931ie_CALLED_PARTY_NUMBER: + case Q931ie_CALLED_PARTY_SUBADDRESS: + case Q931ie_TRANSIT_NETWORK_SELECTION: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_FACILITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_REPEAT_INDICATOR: + if (ir < 2) { + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + ir++; + } else { + return Q931E_ILLEGAL_IE; + } + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else if (codeset == 6) { + switch (IBuf[IOff]) + { + case Q931ie_GENERIC_DIGITS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else if (codeset == 7) { + switch (IBuf[IOff]) + { + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else { + return Q931E_ILLEGAL_IE; + } + } + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: ATT5ESSPmes_Setup + + Decription: Pack a Q931mes_Generic into a real Q.931 message. The user will + set up a SETUP message and issue this to the stack where it + is processed by Q931ProcSetup that processes and validates + it before it actually sends it out. This function is called + to compute the real Q.931 message. + + Parameters: IBuf[IN] Ptr to un-packed struct + ISize[IN] Size of input buffer (unpacked message). + OBuf[OUT] Ptr to packed 'octet' wise message. + OSize[OUT] Size of packed message. + + Called By: Q931ProcSetup + +*****************************************************************************/ +L3INT ATT5ESSPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3INT rc = Q931E_NO_ERROR; + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)(pMes->SendComplete & 0x00ff); + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->RepeatInd)) { + OBuf[Octet++] = (L3UCHAR)(pMes->RepeatInd & 0x00ff); + } + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet))!=0) + return rc; + } else { + rc = Q931E_BEARERCAP; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Network specific facilities */ + if (Q931IsIEPresent(pMes->NetFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_NETWORK_SPECIFIC_FACILITIES](pTrunk, Q931GetIEPtr(pMes->NetFac,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Calling Party Number */ + if (Q931IsIEPresent(pMes->CallingNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CallingNum,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Calling Party Subaddress */ + if (Q931IsIEPresent(pMes->CallingSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CallingSub,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Called party subaddress */ + if (Q931IsIEPresent(pMes->CalledSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CalledSub,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Transit network selection */ + if (Q931IsIEPresent(pMes->TransNetSel)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_TRANSIT_NETWORK_SELECTION](pTrunk, Q931GetIEPtr(pMes->TransNetSel,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->LLRepeatInd)) { + rc = Q931E_UNKNOWN_IE;/* TODO */ + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet))!=0) + return rc; + } + + *OSize = Octet; + return rc; +} + + +/***************************************************************************** + + Function: ATT5ESSUmes_0x0f + +*****************************************************************************/ +L3INT ATT5ESSUmes_0x0f(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + if (mes->ProtDisc == 8) { + return Q931Umes_ConnectAck(pTrunk, IBuf, mes, IOff, Size); + } + + if (mes->ProtDisc == 3) { + return Q931Umes_Service(pTrunk, IBuf, mes, IOff, Size); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: ATT5ESSPmes_0x0f + +*****************************************************************************/ +L3INT ATT5ESSPmes_0x0f(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *mes = (Q931mes_Generic *)IBuf; + + if (mes->ProtDisc == 8) { + return Q931Pmes_ConnectAck(pTrunk, IBuf, ISize, OBuf, OSize); + } + + if (mes->ProtDisc == 3) { + return Q931Pmes_Service(pTrunk, IBuf, ISize, OBuf, OSize); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: ATT5ESSUmes_0x07 + +*****************************************************************************/ +L3INT ATT5ESSUmes_0x07(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + if (mes->ProtDisc == 8) { + return Q931Umes_Connect(pTrunk, IBuf, mes, IOff, Size); + } + + if (mes->ProtDisc == 3) { + return Q931Umes_ServiceAck(pTrunk, IBuf, mes, IOff, Size); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: ATT5ESSPmes_0x07 + +*****************************************************************************/ +L3INT ATT5ESSPmes_0x07(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *mes = (Q931mes_Generic *)IBuf; + + if (mes->ProtDisc == 8) { + return Q931Pmes_Connect(pTrunk, IBuf, ISize, OBuf, OSize); + } + + if (mes->ProtDisc == 3) { + return Q931Pmes_ServiceAck(pTrunk, IBuf, ISize, OBuf, OSize); + } + + return Q931E_UNKNOWN_MESSAGE; +} diff --git a/libs/openzap/src/isdn/DMSStateNT.c b/libs/openzap/src/isdn/DMSStateNT.c new file mode 100644 index 0000000000..e8814ba8ef --- /dev/null +++ b/libs/openzap/src/isdn/DMSStateNT.c @@ -0,0 +1,126 @@ +/***************************************************************************** + + FileName: DMSStateNT.c + + Contents: DMS-100 ISDN State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "DMS.h" + +/***************************************************************************** + Function: DMSCreateNT + + Description: Will create the National ISDN NT as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void DMSCreateNT(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingNT, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingNT, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectNT, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckNT, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressNT, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupNT, DMSUmes_Setup, DMSPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckNT, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeNT, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckNT, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectNT, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendNT, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckNT, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectNT, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationNT, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectNT, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseNT, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteNT, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartNT, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckNT, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlNT, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationNT, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyNT, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusNT, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryNT, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentNT, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityNT, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldNT, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckNT, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectNT, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterNT, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveNT, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckNT, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectNT, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* TODO define state table here */ +} diff --git a/libs/openzap/src/isdn/DMSStateTE.c b/libs/openzap/src/isdn/DMSStateTE.c new file mode 100644 index 0000000000..cb740d3c2f --- /dev/null +++ b/libs/openzap/src/isdn/DMSStateTE.c @@ -0,0 +1,284 @@ +/***************************************************************************** + + FileName: DMSStateTE.c + + Contents: DMS-100 ISDN State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + This reference implementation uses a process per message, + meaning that each message must check call states. This + is easier for dialect maintenance as each message proc + can be replaced individually. A new TE variant only + need to copy the Q931CreateTE and replace those procs or + need to override. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "DMS.h" +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: DMSCreateTE + + Description: Will create the National TE as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void DMSCreateTE(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingTE, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingTE, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, DMSProc0x07TE, DMSUmes_0x07, DMSPmes_0x07); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, DMSProc0x0fTE, DMSUmes_0x0f, DMSPmes_0x0f); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressTE, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupTE, DMSUmes_Setup, DMSPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckTE, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeTE, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckTE, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectTE, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendTE, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckTE, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectTE, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationTE, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectTE, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseTE, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteTE, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartTE, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckTE, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlTE, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationTE, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyTE, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusTE, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryTE, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentTE, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityTE, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldTE, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckTE, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectTE, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterTE, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveTE, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckTE, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectTE, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANGE_STATUS, i, Q931Pie_ChangeStatus, Q931Uie_ChangeStatus); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* State 0 Idle */ + Q931AddStateEntry(i, Q931_U0, Q931mes_RESUME, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_STATUS, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE_COMPLETE, 4); + + /* State 1 Call Initiating */ + Q931AddStateEntry(i, Q931_U1, Q931mes_DISCONNECT, 2); + Q931AddStateEntry(i, Q931_U1, Q931mes_SETUP_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_RELEASE_COMPLETE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CONNECT, 4); + + /* State 2 Overlap Sending */ + Q931AddStateEntry(i, Q931_U2, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U2, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_RELEASE, 2); + + /* State 3 Outgoing Call Proceeding */ + Q931AddStateEntry(i, Q931_U3, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_RELEASE, 2); + + /* State 4 Call Delivered */ + Q931AddStateEntry(i, Q931_U4, Q931mes_CONNECT, 4); + + /* State 6 Call Precent */ + Q931AddStateEntry(i, Q931_U6, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CALL_PROCEEDING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE_COMPLETE, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U6, Q931mes_DISCONNECT, 4); + + /* State 7 Call Received */ + Q931AddStateEntry(i, Q931_U7, Q931mes_CONNECT, 2); + + /* State 8 Connect request */ + Q931AddStateEntry(i, Q931_U8, Q931mes_CONNECT_ACKNOWLEDGE, 4); + + /* State 9 Incoming Call Proceeding */ + Q931AddStateEntry(i, Q931_U9, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_PROGRESS, 2); + + /* State 10 Active */ + Q931AddStateEntry(i, Q931_U10, Q931mes_SUSPEND, 2); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 4); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 2); + + /* State 11 Disconnect Request */ + Q931AddStateEntry(i, Q931_U11, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_NOTIFY, 4); + + /* State 12 Disconnect Ind */ + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 2); + + /* State 15 Suspend Request */ + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_REJECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_RELEASE, 4); + +/* TODO + Q931AddStateEntry(i, Q931_U17, + Q931AddStateEntry(i, Q931_U19, + Q931AddStateEntry(i, Q931_U25, +*/ +} + +/***************************************************************************** + + Function: DMSProc0x0fTE + +*****************************************************************************/ +L3INT DMSProc0x0fTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->ProtDisc == 8) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + if (iFrom == 4) { + /* TODO Add proc here*/ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here*/ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + + if (pMes->ProtDisc == 3 && pTrunk->autoServiceAck) { + Q931AckService(pTrunk, buf); + } + } + return ret; + +} + +/***************************************************************************** + + Function: DMSProc0x07TE + +*****************************************************************************/ +L3INT DMSProc0x07TE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->ProtDisc == 8) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + if (iFrom == 4) { + /* TODO Add proc here*/ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here*/ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; + +} diff --git a/libs/openzap/src/isdn/DMSmes.c b/libs/openzap/src/isdn/DMSmes.c new file mode 100644 index 0000000000..3ce82f2ded --- /dev/null +++ b/libs/openzap/src/isdn/DMSmes.c @@ -0,0 +1,344 @@ +/***************************************************************************** + + FileName: DMSmes.c + + Contents: Pack/Unpack functions. These functions will unpack a DMS-100 ISDN + message from the bit packed original format into structs + that contains variables sized by the user. It will also pack + the struct back into a Q.931 message as required. + + See national.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "DMS.h" + +/***************************************************************************** + + Function: DMSUmes_Setup + +*****************************************************************************/ +L3INT DMSUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT ir = 0; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + L3UCHAR last_codeset = 0, codeset = 0; + L3UCHAR shift_lock = 1; + + while (IOff < Size) { + if (!shift_lock) { + codeset = last_codeset; + } + + if ((IBuf[IOff] & 0xF0) == Q931ie_SHIFT ) { + shift_lock = (IBuf[IOff] & 0x08); + if (shift_lock) { + last_codeset = codeset; + } + codeset = ((IBuf[IOff] & 0x07)); + IOff++; + } + + if (codeset == 0) { + switch (IBuf[IOff]) { + case Q931ie_SENDING_COMPLETE: + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_NETWORK_SPECIFIC_FACILITIES: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_SIGNAL: + case Q931ie_CALLING_PARTY_NUMBER: + case Q931ie_CALLING_PARTY_SUBADDRESS: + case Q931ie_CALLED_PARTY_NUMBER: + case Q931ie_CALLED_PARTY_SUBADDRESS: + case Q931ie_TRANSIT_NETWORK_SELECTION: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_REPEAT_INDICATOR: + if (ir < 2) { + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + ir++; + } else { + return Q931E_ILLEGAL_IE; + } + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else if (codeset == 6) { + switch (IBuf[IOff]) { + case Q931ie_GENERIC_DIGITS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + + } else { + return Q931E_ILLEGAL_IE; + } + } + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: DMSPmes_Setup + + Decription: Pack a Q931mes_Generic into a real Q.931 message. The user will + set up a SETUP message and issue this to the stack where it + is processed by Q931ProcSetup that processes and validates + it before it actually sends it out. This function is called + to compute the real Q.931 message. + + Parameters: IBuf[IN] Ptr to un-packed struct + ISize[IN] Size of input buffer (unpacked message). + OBuf[OUT] Ptr to packed 'octet' wise message. + OSize[OUT] Size of packed message. + + Called By: Q931ProcSetup + +*****************************************************************************/ +L3INT DMSPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3INT rc = Q931E_NO_ERROR; + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)(pMes->SendComplete & 0x00ff); + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->RepeatInd)) { + OBuf[Octet++] = (L3UCHAR)(pMes->RepeatInd & 0x00ff); + } + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } else { + rc = Q931E_BEARERCAP; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Network spesific facilities */ + if (Q931IsIEPresent(pMes->NetFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_NETWORK_SPECIFIC_FACILITIES](pTrunk, Q931GetIEPtr(pMes->NetFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Number */ + if (Q931IsIEPresent(pMes->CallingNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CallingNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Subaddress */ + if (Q931IsIEPresent(pMes->CallingSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CallingSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + + /* Called party subaddress */ + if (Q931IsIEPresent(pMes->CalledSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CalledSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Transit network selection */ + if (Q931IsIEPresent(pMes->TransNetSel)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_TRANSIT_NETWORK_SELECTION](pTrunk, Q931GetIEPtr(pMes->TransNetSel,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->LLRepeatInd)) { + rc = Q931E_UNKNOWN_IE;/* TODO */ + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + + +/***************************************************************************** + + Function: DMSUmes_0x0f + +*****************************************************************************/ +L3INT DMSUmes_0x0f(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + if (mes->ProtDisc == 8) { + return Q931Umes_ConnectAck(pTrunk, IBuf, mes, IOff, Size); + } + + if (mes->ProtDisc == 3) { + return Q931Umes_Service(pTrunk, IBuf, mes, IOff, Size); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: DMSPmes_0x0f + +*****************************************************************************/ +L3INT DMSPmes_0x0f(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *mes = (Q931mes_Generic *)IBuf; + + if (mes->ProtDisc == 8) { + return Q931Pmes_ConnectAck(pTrunk, IBuf, ISize, OBuf, OSize); + } + + if (mes->ProtDisc == 3) { + return Q931Pmes_Service(pTrunk, IBuf, ISize, OBuf, OSize); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: DMSUmes_0x07 + +*****************************************************************************/ +L3INT DMSUmes_0x07(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + if (mes->ProtDisc == 8) { + return Q931Umes_Connect(pTrunk, IBuf, mes, IOff, Size); + } + + if (mes->ProtDisc == 3) { + return Q931Umes_ServiceAck(pTrunk, IBuf, mes, IOff, Size); + } + + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: DMSPmes_0x07 + +*****************************************************************************/ +L3INT DMSPmes_0x07(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *mes = (Q931mes_Generic *)IBuf; + + if (mes->ProtDisc == 8) { + return Q931Pmes_Connect(pTrunk, IBuf, ISize, OBuf, OSize); + } + + if (mes->ProtDisc == 3) { + return Q931Pmes_ServiceAck(pTrunk, IBuf, ISize, OBuf, OSize); + } + + return Q931E_UNKNOWN_MESSAGE; +} diff --git a/libs/openzap/src/isdn/EuroISDNStateNT.c b/libs/openzap/src/isdn/EuroISDNStateNT.c new file mode 100644 index 0000000000..6b26c91e04 --- /dev/null +++ b/libs/openzap/src/isdn/EuroISDNStateNT.c @@ -0,0 +1,44 @@ +/***************************************************************************** + + FileName: EuroISDNStateNT.c + + Contents: EuroISDN State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" diff --git a/libs/openzap/src/isdn/EuroISDNStateTE.c b/libs/openzap/src/isdn/EuroISDNStateTE.c new file mode 100644 index 0000000000..ef6b39e8e5 --- /dev/null +++ b/libs/openzap/src/isdn/EuroISDNStateTE.c @@ -0,0 +1,58 @@ +/***************************************************************************** + + FileName: EuroISDNStateTE.c + + Contents: EuroISDN State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +/* + EuroISDN is a sub-set of Q.931. Q.931 is very generic as it embrase a lot, + while EuroISDN is more exact and make decitions on some of the + 'implementation options' in the original standard. EuroISDN will + however run smoothly under the generic space, so these functions are more + for show +*/ +#if 0 +static void EuroISDNCreateTE(L3UCHAR i) +{ + Q931CreateTE(i); +} +#endif diff --git a/libs/openzap/src/isdn/Q921.c b/libs/openzap/src/isdn/Q921.c new file mode 100644 index 0000000000..0496e09f5c --- /dev/null +++ b/libs/openzap/src/isdn/Q921.c @@ -0,0 +1,3517 @@ +/***************************************************************************** + + FileName: q921.c + + Description: Contains the implementation of a Q.921 protocol + + Created: 27.dec.2000/JVB + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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. + +*****************************************************************************/ + +/**************************************************************************** + * Changes: + * + * - June-August 2008: Stefan Knoblich : + * Add PTMP TEI management (NT + TE mode) + * Add timers + * Add retransmit counters + * Add logging + * Various cleanups + * Queues, retransmission of I frames + * PTMP NT mode + * + * + * TODO: + * + * - Cleanup queueing, test retransmission + * + * - Q921Start() /-Stop() TEI acquire + release + * (move everything related into these functions) + * + * - Q.921 '97 Appendix I (and maybe III, IV) + * + * - More complete Appendix II + * + * - Test PTP mode + * + * - PTMP NT mode (in progress) + * + * - NT mode TEI management: (ab)use T202 for TEI Check Request retransmission + * + * - General cleanup (move all non-public declarations into private header file) + * + * - Statistics, per-Frame type debug message filter + * + ****************************************************************************/ + +#include +#include +#include +#include + +#include "Q921.h" +#include "Q921priv.h" +#include "mfifo.h" + +#ifdef WIN32 +#pragma warning(disable:4100 4244) +#endif + +/****************************************************************************************************** + * Actual code below this line + ******************************************************************************************************/ + + +/** + * Q921StateNames + * \brief Static array of state name / value mappings + */ +static struct Q921StateName { + Q921State_t value; + const char *name; +} Q921StateNames[10] = { + { Q921_STATE_STOPPED, "Stopped" }, + { Q921_STATE_TEI_UNASSIGNED, "TEI Unassigned" }, + { Q921_STATE_TEI_AWAITING, "TEI Awaiting Assignment" }, + { Q921_STATE_TEI_ESTABLISH, "TEI Awaiting Establishment" }, + { Q921_STATE_TEI_ASSIGNED, "TEI Assigned" }, + { Q921_STATE_AWAITING_ESTABLISHMENT, "Awaiting Establishment" }, + { Q921_STATE_AWAITING_RELEASE, "Awaiting Release" }, + { Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, "Multiple Frame Mode Established" }, + { Q921_STATE_TIMER_RECOVERY, "Timer Recovery" }, + { 0, 0 } +}; + +/** + * Q921State2Name + * \brief Convert state value to name + * \param[in] state the state value + * \return the state name or "Unknown" + * + * \author Stefan Knoblich + */ +static const char *Q921State2Name(Q921State_t state) +{ + struct Q921StateName *p = Q921StateNames; + + while(p->name) { + if(p->value == state) + return p->name; + p++; + } + + return "Unknown"; +} + + +/** + * Q921SendEnquiry + */ +static int Q921SendEnquiry(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* send enquiry: begin */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + + Q921SendRNR(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 1); + } + else { + Q921SendRR(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 1); + } + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* "Start" T200 */ + Q921T200TimerReset(trunk, tei); + + /* send enquiry: end */ + return 1; +} + +/** + * Q921SendEnquiryResponse + */ +static int Q921SendEnquiryResponse(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* send enquiry: begin */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + + Q921SendRNR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + } + else { + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + /* send enquiry: end */ + return 1; +} + +/** + * Q921ResetExceptionConditions + * \brief Reset Q.921 Exception conditions procedure + * \param trunk Q.921 data structure + * \param tei TEI + * \todo Do something + */ +static void Q921ResetExceptionConditions(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* Clear peer receiver busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* Clear reject exception */ + Q921_CLEAR_FLAG(link, Q921_FLAG_REJECT); + + /* Clear own receiver busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_RECV_BUSY); + + /* Clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + return; +} + +/** + * Q921EstablishDataLink + * \brief Q.921 Establish data link procedure + * \param trunk Q.921 data structure + * \param tei TEI + * \return always 1 (success) + */ +static int Q921EstablishDataLink(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* reset exception conditions */ + Q921ResetExceptionConditions(trunk, tei); + + /* RC = 0 */ + link->N200 = 0; + + /* Send SABME */ + Q921SendSABME(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 1); + + /* Restart T200, stop T203 */ + Q921T200TimerReset(trunk, tei); + Q921T203TimerStop(trunk, tei); + + return 1; +} + +/** + * Q921NrErrorRecovery + * \brief NR(R) Error recovery procedure + * \param trunk Q.921 data structure + * \param tei TEI + * \return always 1 (success) + */ +static int Q921NrErrorRecovery(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* MDL Error indication (J) */ + + /* Establish datalink */ + Q921EstablishDataLink(trunk, tei); + + /* Clear L3 initiated */ + Q921_CLEAR_FLAG(link, Q921_FLAG_L3_INITIATED); + + return 1; +} + + +/** + * Q921InvokeRetransmission + * \brief I Frame retransmission procedure + * \param trunk Q.921 data structure + * \param tei TEI + * \param nr N(R) for retransmission + * \return always 1 (success) + */ +static int Q921InvokeRetransmission(L2TRUNK trunk, L2UCHAR tei, L2UCHAR nr) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + L2UCHAR *mes; + L2INT qpos, qnum, size = 0; + + qnum = MFIFOGetMesCount(link->IFrameResendQueue); + qpos = qnum - 1; + + /* + * slightly different than what is shown in the spec + * (Q.921 '97 Annex B, Figure B.9, page 104) + * + * what the above mentioned figure probably means is: + * "as long as V(S) != N(R), move the pointer marking + * the first frame to start resending at to the previous + * frame" + * + * if we actually implemented it as shown in the figure, we'd be + * resending frames in the wrong order (moving backwards in time) + * meaning we'd have to add an incoming queue to reorder the frames + * + */ + /* + * TODO: There's a "traditional" off-by-one error hidden in the original + * mfifo implementation + it's late, i'm tired and being lazy, + * so i'll probably have added another one :P + * + * wow, the first while loop sucks and can be removed + */ + while(link->vs != nr && qpos > 0) { /* ???? */ + /* V(S) = V(S) - 1 */ + Q921_DEC_COUNTER(link->vs); /* huh? backwards? */ + + /* next frame in queue (backtrack along I queue) ??? */ + qpos--; + } + + /* + * being lazy and trying to avoid mod 128 math this way... + */ + if(link->vs != nr && !qpos) { + /* fatal, we don't have enough history to resend all missing frames */ + /* TODO: how to handle this? */ + } + + /* + * resend frames in correct order (oldest missing frame first, + * contrary to what the spec figure shows) + */ + while(qpos < qnum) { + /* Grab frame's buffer ptr and size from queue */ + mes = MFIFOGetMesPtrOffset(link->IFrameResendQueue, &size, qpos); + if(mes) { + /* requeue frame (TODO: check queue full condition) */ + MFIFOWriteMes(link->IFrameQueue, mes, size); + + /* set I frame queued */ + } + + qpos++; + } + + return 1; +} + + +static int Q921AcknowledgePending(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + if(Q921_CHECK_FLAG(link, Q921_FLAG_ACK_PENDING)) { + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* send RR */ + Q921SendRR(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 0); + + return 1; + } + break; + + default: + break; + } + + return 0; +} + +/***************************************************************************** + + Function: Q921_InitTrunk + + Decription: Initialize a Q.921 trunk so it is ready for use. This + function MUST be called before you call any other functions. + +*****************************************************************************/ +int Q921_InitTrunk(L2TRUNK trunk, + L2UCHAR sapi, + L2UCHAR tei, + Q921NetUser_t NetUser, + Q921NetType_t NetType, + L2INT hsize, + Q921Tx21CB_t cb21, + Q921Tx23CB_t cb23, + void *priv21, + void *priv23) +{ + int numlinks = 0; + + trunk->sapi = sapi; + trunk->tei = tei; + trunk->NetUser = NetUser; + trunk->NetType = NetType; + trunk->Q921Tx21Proc = cb21; + trunk->Q921Tx23Proc = cb23; + trunk->PrivateData21 = priv21; + trunk->PrivateData23 = priv23; + trunk->Q921HeaderSpace = hsize; + + numlinks = Q921_IS_PTMP_NT(trunk) ? Q921_TEI_MAX : 1; + + if (trunk->initialized != INITIALIZED_MAGIC) { + MFIFOCreate(trunk->HDLCInQueue, Q921MAXHDLCSPACE, 10); + + /* + * Allocate space for per-link context(s) + */ + trunk->context = malloc(numlinks * sizeof(struct Q921_Link)); + if(!trunk->context) + return -1; + + trunk->initialized = INITIALIZED_MAGIC; + } + + /* timeout default values */ + trunk->T200Timeout = 1000; /* 1 second */ + trunk->T203Timeout = 10000; /* 10 seconds */ + trunk->T202Timeout = 2000; /* 2 seconds */ + trunk->T201Timeout = 200000; /* 200 seconds */ + trunk->TM01Timeout = 10000; /* 10 seconds */ + + /* octet / retransmit counter default limits */ + trunk->N200Limit = 3; /* 3 retransmits */ + trunk->N201Limit = 260; /* 260 octets */ + trunk->N202Limit = 3; /* 3 retransmits */ + trunk->k = 7; /* 7 outstanding ACKs */ + + /* reset counters, timers, etc. */ + trunk->T202 = 0; + trunk->N202 = 0; + + /* Reset per-link contexts */ + memset(trunk->context, 0, numlinks * sizeof(struct Q921_Link)); + + /* clear tei map */ + memset(trunk->tei_map, 0, Q921_TEI_MAX + 1); + + if(Q921_IS_PTMP(trunk)) { + /* + * We're either the Network side (NT, TEI = 0) + * or user-side equipment (TE) which will get it's TEI via + * dynamic assignment + */ + trunk->tei = 0; + } + + return 0; +} + + +/** + * Q921Tx21Proc + * \brief Submit frame to layer 1 (for sending) + * \param[in] trunk Pointer to trunk struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + */ +static int Q921Tx21Proc(L2TRUNK trunk, L2UCHAR *Msg, L2INT size) +{ + Q921LogMesg(trunk, Q921_LOG_DEBUG, 0, Msg, size, "Sending frame"); + + return trunk->Q921Tx21Proc(trunk->PrivateData21, Msg, size); +} + + +/** + * Q921Tx23Proc + * \brief Submit frame to layer 3 + * \param[in] trunk Pointer to trunk struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + */ +static int Q921Tx23Proc(L2TRUNK trunk, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR *Msg, L2INT size) +{ + return trunk->Q921Tx23Proc(trunk->PrivateData23, ind, tei, Msg, size); +} + + +/** + * Q921LogProc + * \brief Used for logging, converts to string and submits to higher level log function via callback + * \param[in] trunk Pointer to trunk struct + * \param[in] level Q921 Loglevel + * \param[in] fmt format of logmessage + * \return >= 0 on success, < 0 on error + * + * \author Stefan Knoblich + */ +static int Q921Log(L2TRUNK trunk, Q921LogLevel_t level, const char *fmt, ...) +{ + char buf[Q921_LOGBUFSIZE]; + L2INT len; + va_list ap; + + if(!trunk->Q921LogProc) + return 0; + + if(trunk->loglevel < level) + return 0; + + va_start(ap, fmt); + + len = vsnprintf(buf, sizeof(buf)-1, fmt, ap); + if(len <= 0) { + /* TODO: error handling */ + return -1; + } + if(len >= sizeof(buf)) + len = sizeof(buf) - 1; + + buf[len] = '\0'; + + va_end(ap); + + return trunk->Q921LogProc(trunk->PrivateDataLog, level, buf, len); +} + + +static int print_hex(char *buf, int bsize, const unsigned char *in, const int len) +{ + static const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + int offset = 0; + int pos = 0; + int nr = 0; + + buf[pos++] = '['; + bsize -= 3; + + while((bsize - pos) > 0 && offset < len) { + buf[pos++] = hex[(in[offset] & 0xF0) >> 4]; + buf[pos++] = hex[(in[offset++] & 0x0F)]; + + if(++nr == 32 && offset < len && (bsize - pos) > 3) { + nr = 0; + buf[pos++] = ']'; + buf[pos++] = '\n'; + buf[pos++] = '['; + } + else if(offset < len) { + buf[pos++] = ' '; + } + } + + buf[pos++] = ']'; + buf[pos++] = '\n'; + buf[pos] = '\0'; + + return pos; +} + +#define APPEND_MSG(buf, off, lef, fmt, ...) \ + len = snprintf(buf + off, lef, fmt, ##__VA_ARGS__); \ + if(len > 0) { \ + off += len; \ + lef -= len; \ + } else { \ + goto out; \ + } + +/** + * Q921LogProcMesg + * \brief Used for logging, converts to string and submits to higher level log function via callback + * \param[in] trunk Pointer to trunk struct + * \param[in] level Q921 Loglevel + * \param[in] received direction of the message (received = 1, sending = 0) + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \param[in] fmt format of logmessage + * \return >= 0 on success, < 0 on error + * + * \author Stefan Knoblich + */ +static int Q921LogMesg(L2TRUNK trunk, Q921LogLevel_t level, L2UCHAR received, L2UCHAR *mes, L2INT size, const char *fmt, ...) +{ + char buf[Q921_LOGBUFSIZE]; + size_t len, left; + va_list ap; + + if(!trunk->Q921LogProc) + return 0; + + if(trunk->loglevel < level) + return 0; + + if(!mes) + return 0; + + memset(buf, 0, sizeof(buf)); + + left = sizeof(buf) - 1; + + va_start(ap, fmt); + + len = vsnprintf(buf, left, fmt, ap); + if(len > 0) + left -= len; + else { + /* TODO: error handling */ + return -1; + } + + va_end(ap); + + if(trunk->loglevel == Q921_LOG_DEBUG) { + char pbuf[1024]; + size_t pleft, poffset; + L2UCHAR sapi, tei, cr; + L2UCHAR *pmes = mes + trunk->Q921HeaderSpace; + struct Q921_Link *link; + + memset(pbuf, 0, sizeof(pbuf)); + + pleft = sizeof(pbuf); + poffset = 0; + + /* + * Decode packet + */ + sapi = (pmes[0] & 0xfc) >> 2; + cr = (pmes[0] & 0x02) >> 1; + tei = (pmes[1] & 0xfe) >> 1; + link = Q921_LINK_CONTEXT(trunk, tei); + + /* make cr actually useful */ + cr = (received) ? Q921_IS_COMMAND(trunk, cr) : Q921_IS_RESPONSE(trunk, cr); + + /* filter */ + if((pmes[2] & 0x01) == 0x00) { + ; + } + else if((pmes[2] & 0x03) == 0x01) { + ; //return 0; + } + else if((pmes[2] & 0x03) == 0x03) { + ; + } + + APPEND_MSG(pbuf, poffset, pleft, "\n----------------- Q.921 Packet [%s%s] ---------------\n", received ? "Incoming" : "Outgoing", + (tei == link->tei || tei == Q921_TEI_BCAST) ? "" : ", Ignored" ); + + /* common header */ + APPEND_MSG(pbuf, poffset, pleft, " SAPI: %u, TEI: %u, C/R: %s (%d)\n\n", sapi, tei, (cr) ? "Command" : "Response", (mes[0] & 0x02) >> 1 ); + + /* + * message specific + */ + if((pmes[2] & 0x01) == 0x00) { + /* + * I frame + */ + L2UCHAR pf = pmes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = pmes[3] >> 1; /* receive sequence number */ + L2UCHAR ns = pmes[2] >> 1; /* send sequence number */ + + APPEND_MSG(pbuf, poffset, pleft, " Type: I Frame\n P/F: %d, N(S): %d, N(R): %d [V(A): %d, V(R): %d, V(S): %d]\n", pf, ns, nr, + link->va, link->vr, link->vs); + + /* Dump content of I Frames for foreign TEIs */ + if(tei != link->tei) { + APPEND_MSG(pbuf, poffset, pleft, " CONTENT:\n"); + + len = print_hex(pbuf + poffset, (int)pleft, &pmes[4], size - (trunk->Q921HeaderSpace + 4)); + poffset += len; + pleft -= len; + } + } + else if((pmes[2] & 0x03) == 0x01) { + /* + * S frame + */ + L2UCHAR sv = (pmes[2] & 0x0c) >> 2; /* supervisory format id */ + L2UCHAR pf = pmes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = pmes[3] >> 1; /* receive sequence number */ + const char *type; + + switch(sv) { + case 0x00: /* RR : Receive Ready */ + type = "RR (Receive Ready)"; + break; + + case 0x02: /* RNR : Receive Not Ready */ + type = "RNR (Receiver Not Ready)"; + break; + + case 0x04: /* REJ : Reject */ + type = "REJ (Reject)"; + break; + + default: /* Invalid / Unknown */ + type = "Unknown"; + break; + } + + APPEND_MSG(pbuf, poffset, pleft, " Type: S Frame, SV: %s\n P/F: %d, N(R): %d [V(A): %d, V(R): %d, V(S): %d]\n", type, pf, nr, + link->va, link->vr, link->vs); + } + else if((pmes[2] & 0x03) == 0x03) { + /* + * U frame + */ + L2UCHAR m = (pmes[2] & 0xe0) >> 3 | (pmes[2] & 0x0c) >> 2; /* modifier function id */ + L2UCHAR pf = (pmes[2] & 0x10) >> 4; /* poll / final flag */ + const char *type; + + switch(m) { + case 0x00: + type = "UI (Unnumbered Information)"; + break; + + case 0x03: + type = "DM (Disconnected Mode)"; + break; + + case 0x08: + type = "DISC (Disconnect)"; + break; + + case 0x0c: + type = "UA (Unnumbered Acknowledgement)"; + break; + + case 0x0f: + type = "SABME"; + break; + + case 0x11: + type = "FRMR (Frame Reject)"; + break; + + case 0x17: + type = "XID (Exchange Identification)"; + break; + + default: + type = "Unknown"; + } + + + APPEND_MSG(pbuf, poffset, pleft, " Type: U Frame (%s)\n P/F: %d\n", type, pf); + + if(m == 0x00) { + switch(pmes[3]) { + case Q921_LAYER_ENT_ID_TEI: + type = "TEI Mgmt"; + break; + + case Q921_LAYER_ENT_ID_Q931: + type = "Q.931"; + break; + + default: + type = "Unknown"; + } + + if(pmes[3] == Q921_LAYER_ENT_ID_TEI) { + const char *command = ""; + + switch(pmes[6]) { + case Q921_TEI_ID_REQUEST: + command = "Request"; + break; + case Q921_TEI_ID_VERIFY: + command = "Verify"; + break; + case Q921_TEI_ID_CHECKREQ: + command = "Check req"; + break; + case Q921_TEI_ID_CHECKRESP: + command = "Check resp"; + break; + case Q921_TEI_ID_REMOVE: + command = "Remove"; + break; + case Q921_TEI_ID_ASSIGNED: + command = "Assign"; + break; + case Q921_TEI_ID_DENIED: + command = "Denied"; + break; + } + APPEND_MSG(pbuf, poffset, pleft, " ENT ID: %d (%s), COMMAND: %d (%s), RI: %#x, AI: %d\n", + pmes[3], type, pmes[6], command, (int)((pmes[4] << 8) | pmes[5]), pmes[7] >> 1); + } + else { + APPEND_MSG(pbuf, poffset, pleft, " ENT ID: %d (%s), MESSAGE CONTENT:\n", pmes[3], type); + + len = print_hex(pbuf + poffset, (int)pleft, &pmes[3], size - (trunk->Q921HeaderSpace + 3)); + poffset += len; + pleft -= len; + } + } + } + else { + /* + * Unknown + */ + strncat(pbuf + poffset, " -- unknown frame type --\n", pleft); + + len = (sizeof(pbuf) - poffset) - strlen(pbuf + poffset); + if(len > 0) { + poffset += len; + pleft -= len; + } else + goto out; + } + + APPEND_MSG(pbuf, poffset, pleft, "\n Q.921 state: \"%s\" (%d) [flags: %c%c%c%c]\n", Q921State2Name(link->state), link->state, + Q921_CHECK_FLAG(link, Q921_FLAG_ACK_PENDING) ? 'A' : '-', + Q921_CHECK_FLAG(link, Q921_FLAG_REJECT) ? 'R' : '-', + Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY) ? 'P' : '-', + Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY) ? 'B' : '-'); + + strncat(pbuf + poffset, "----------------------------------------------\n\n", pleft); + + len = (sizeof(pbuf) - poffset) - strlen(pbuf + poffset); + if(len > 0) { + poffset += len; + pleft -= len; + } else + goto out; + + + /* concat buffers together */ + len = strlen(pbuf); + if(len <= left) + strncat(buf, pbuf, left); + else + strncat(buf, "-- packet truncated --\n", left); + } + +out: + buf[sizeof(buf) - 1] = '\0'; + + return trunk->Q921LogProc(trunk->PrivateDataLog, level, buf, (int)strlen(buf)); +} + +/***************************************************************************** + + Function: Q921TimeTick + + Description: Called periodically from an external source to allow the + stack to process and maintain it's own timers. + + Return Value: none + +*****************************************************************************/ +static L2ULONG (*Q921GetTimeProc) (void) = NULL; /* callback for func reading time in ms */ +static L2ULONG tLast = {0}; + +static L2ULONG Q921GetTime(void) +{ + L2ULONG tNow = 0; + + if(Q921GetTimeProc) + { + tNow = Q921GetTimeProc(); + if(tNow < tLast) /* wrapped */ + { + /* TODO */ + } + tLast = tNow; + } + return tNow; +} + +/* + * T200 handling (per-TEI in PTMP NT mode, tei=0 otherwise) + */ +static void Q921T200TimerStart(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + if (!link->T200) { + link->T200 = Q921GetTime() + trunk->T200Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T200 (timeout: %d msecs) started for TEI %d\n", trunk->T200Timeout, tei); + } +} + +static void Q921T200TimerStop(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T200 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "T200 stopped for TEI %d\n", tei); +} + +static void Q921T200TimerReset(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T200 = Q921GetTime() + trunk->T200Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T200 (timeout: %d msecs) restarted for TEI %d\n", trunk->T200Timeout, tei); +} + +/* + * T203 handling (per-TEI in PTMP NT mode, tei=0 otherwise) + */ +static void Q921T203TimerStart(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + if (!link->T203) { + link->T203 = Q921GetTime() + trunk->T203Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T203 (timeout: %d msecs) started for TEI %d\n", trunk->T203Timeout, tei); + } +} + +static void Q921T203TimerStop(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T203 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "T203 stopped for TEI %d\n", tei); +} + +static void Q921T203TimerReset(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T203 = Q921GetTime() + trunk->T203Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T203 (timeout: %d msecs) restarted for TEI %d\n", trunk->T203Timeout, tei); +} + +/* + * T202 handling (TEI message timeout, TE mode only) + */ +static void Q921T202TimerStart(L2TRUNK trunk) +{ + if (!trunk->T202) { + trunk->T202 = Q921GetTime() + trunk->T202Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T202 (timeout: %d msecs) started\n", trunk->T202Timeout); + } +} + +static void Q921T202TimerStop(L2TRUNK trunk) +{ + trunk->T202 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "T202 stopped\n"); +} + +static void Q921T202TimerReset(L2TRUNK trunk) +{ + trunk->T202 = Q921GetTime() + trunk->T202Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T202 (timeout: %d msecs) restarted\n", trunk->T202Timeout); +} + +/* + * T201 handling (TEI management (NT side), per-TEI) + */ +static void Q921T201TimerStart(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + if (!link->T201) { + link->T201 = Q921GetTime() + trunk->T201Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T201 (timeout: %d msecs) started for TEI %d\n", trunk->T201Timeout, tei); + } +} + +static void Q921T201TimerStop(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T201 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "T201 stopped for TEI %d\n", tei); +} + +#ifdef __UNUSED_FOR_NOW__ +static void Q921T201TimerReset(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->T201 = Q921GetTime() + trunk->T201Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "T201 (timeout: %d msecs) restarted for TEI %d\n", trunk->T201Timeout, tei); +} +#endif + +/* + * TM01 handling (Datalink inactivity shutdown timer) + */ +static void Q921TM01TimerStart(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + if (!link->TM01) { + link->TM01 = Q921GetTime() + trunk->TM01Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "TM01 (timeout: %d msecs) started for TEI %d\n", trunk->TM01Timeout, tei); + } +} + +#ifdef __UNUSED_FOR_NOW__ +static void Q921TM01TimerStop(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->TM01 = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "TM01 stopped for TEI %d\n", tei); +} +#endif + +static void Q921TM01TimerReset(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + link->TM01 = Q921GetTime() + trunk->TM01Timeout; + + Q921Log(trunk, Q921_LOG_DEBUG, "TM01 (timeout: %d msecs) restarted for TEI %d\n", trunk->TM01Timeout, tei); +} + +/* + * Expiry callbacks + */ +static void Q921T200TimerExpire(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + struct Q921_Link *trlink = Q921_TRUNK_CONTEXT(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "T200 expired for TEI %d (trunk TEI %d)\n", tei, trlink->tei); + + /* Stop timer first */ + Q921T200TimerStop(trunk, tei); + + switch(link->state) { + case Q921_STATE_AWAITING_ESTABLISHMENT: + if(link->N200 >= trunk->N200Limit) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* MDL-Error indication (G) */ + Q921Log(trunk, Q921_LOG_ERROR, "Failed to establish Q.921 link in %d retries\n", link->N200); + + /* DL-Release indication */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE, tei, NULL, 0); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + } else { + /* Increment retry counter */ + link->N200++; + + /* Send SABME */ + Q921SendSABME(trunk, + trunk->sapi, + Q921_COMMAND(trunk), + tei, + 1); + + /* Start T200 */ + Q921T200TimerStart(trunk, tei); + } + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + link->N200 = 0; + + if(!Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY)) { + /* get last transmitted I frame */ + + /* V(S) = V(S) - 1 */ + Q921_DEC_COUNTER(link->vs); + + /* retransmit I frame */ + + /* V(S) = V(S) + 1 (done by Q921SendI() ) */ + //Q921_INC_COUNTER(link->vs); + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* Start T200 */ + Q921T200TimerStart(trunk, tei); + } else { + /* transmit enquiry */ + Q921SendEnquiry(trunk, tei); + } + + /* increment counter */ + link->N200++; + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TIMER_RECOVERY, tei); + break; + + case Q921_STATE_TIMER_RECOVERY: + if(link->N200 == trunk->N200Limit) { + /* MDL Error indication (I) */ + + /* Establish data link */ + Q921EstablishDataLink(trunk, tei); + + /* Clear L3 initiated */ + Q921_CLEAR_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } else { + if(link->vs == link->va) { + /* transmit enquiry */ + Q921SendEnquiry(trunk, tei); + + } else if(!Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY)) { + /* get last transmitted frame */ + + /* V(S) = V(S) - 1 */ + Q921_DEC_COUNTER(link->vs); + + /* retrans frame */ + + /* V(S) = V(S) + 1 (done by Q921SendI() ) */ + //Q921_INC_COUNTER(link->vs); + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* Start T200 */ + Q921T200TimerStart(trunk, tei); + } + + /* increment counter */ + link->N200++; + + /* no state change */ + } + break; + + default: + break; + } +} + +static void Q921T203TimerExpire(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + struct Q921_Link *trlink = Q921_TRUNK_CONTEXT(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "T203 expired for TEI %d (trunk TEI %d)\n", tei, trlink->tei); + + /* Stop Timer first */ + Q921T203TimerStop(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* Send Enquiry */ + Q921SendEnquiry(trunk, tei); + + /* RC = 0 */ + link->N200 = 0; + + /* no state change */ + break; + + default: + break; + } +} + +static void Q921T202TimerExpire(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + + Q921T202TimerReset(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "T202 expired for Q.921 trunk with TEI %d\n", link->tei); + + /* todo: implement resend counter */ + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: /* Tei identity verify timeout */ + Q921TeiSendVerifyRequest(trunk); + break; + + default: /* Tei assignment request timeout (TODO: refine) */ + + if(trunk->N202 >= trunk->N202Limit) { + /* Too many retransmits, reset counter, stop timer and handle case (TODO) */ + trunk->N202 = 0; + + Q921T202TimerStop(trunk); + + return; + } + Q921TeiSendAssignRequest(trunk); + + trunk->N202++; + } +} + +static void Q921T201TimerExpire(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + struct Q921_Link *trlink = Q921_TRUNK_CONTEXT(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "T201 expired for TEI %d (trunk TEI: %d)\n", tei, trlink->tei); + + Q921T201TimerStop(trunk, tei); + + /* NOTE: abusing N202 for this */ + if(link->N202 < trunk->N202Limit) { + /* send check request */ + Q921TeiSendCheckRequest(trunk, tei); + + /* increment counter */ + link->N202++; + } else { + /* put context in STOPPED state */ + Q921ChangeState(trunk, Q921_STATE_STOPPED, tei); + + /* NOTE: should we clear the link too? */ + memset(link, 0, sizeof(struct Q921_Link)); + + /* mark TEI free */ + trunk->tei_map[tei] = 0; + } +} + +#ifdef __UNUSED_FOR_NOW__ +static void Q921TM01TimerExpire(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + struct Q921_Link *trlink = Q921_TRUNK_CONTEXT(trunk); + + Q921Log(trunk, Q921_LOG_DEBUG, "TM01 expired for TEI %d (trunk TEI: %d)\n", tei, trlink->tei); + + /* Restart TM01 */ + Q921TM01TimerReset(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: +/* + * NT-only, needs more support from L3 + */ +#if 0 + /* No activity, shutdown link */ + Q921SendDISC(trunk, trunk->sapi, Q921_COMMAND(trunk), tei, 1); + + /* clear I queue */ + MFIFOClear(link->IFrameQueue); + + /* change state */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_RELEASE, tei); +#endif + break; + + default: + break; + } +} +#endif + +/* + * Timer Tick function + */ +void Q921TimerTick(L2TRUNK trunk) +{ + struct Q921_Link *link; + L2ULONG tNow = Q921GetTime(); + int numlinks = Q921_IS_PTMP_NT(trunk) ? Q921_TEI_MAX : 1; + int x; + + for(x = 0; x <= numlinks; x++) { + link = Q921_LINK_CONTEXT(trunk, x); + + /* TODO: check if TEI is assigned and skip check if not (speedup!) */ + if(link->state == Q921_STATE_STOPPED) + continue; + + if (link->T200 && tNow > link->T200) { + Q921T200TimerExpire(trunk, link->tei); + } + if (link->T203 && tNow > link->T203) { + Q921T203TimerExpire(trunk, link->tei); + } + + if(Q921_IS_PTMP_NT(trunk) && link->tei) { + if (link->T201 && tNow > link->T201) { + Q921T201TimerExpire(trunk, link->tei); + } + } + + if(!Q921_IS_PTMP_NT(trunk)) { + if (trunk->T202 && tNow > trunk->T202) { + Q921T202TimerExpire(trunk); + } + } + + /* Send enqueued I frame, if available */ + Q921SendQueuedIFrame(trunk, link->tei); + + /* Send ack if pending */ + Q921AcknowledgePending(trunk, link->tei); + } + +} + +void Q921SetGetTimeCB(L2ULONG (*callback)(void)) +{ + Q921GetTimeProc = callback; +} + +/***************************************************************************** + + Function: Q921QueueHDLCFrame + + Description: Called to receive and queue an incoming HDLC frame. Will + queue this in Q921HDLCInQueue. The called must either call + Q921Rx12 directly afterwards or signal Q921Rx12 to be called + later. Q921Rx12 will read from the same queue and process + the frame. + + This function assumes that the message contains header + space. This is removed for internal Q921 processing, but + must be keept for I frames. + + Parameters: trunk trunk # + b ptr to frame; + size size of frame in bytes + +*****************************************************************************/ +int Q921QueueHDLCFrame(L2TRUNK trunk, L2UCHAR *b, L2INT size) +{ + return MFIFOWriteMes(trunk->HDLCInQueue, b, size); +} + +/** + * Q921EnqueueI + * \brief Put I frame into transmit queue + * + */ +static int Q921EnqueueI(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, Tei); + + /* I frame header */ + mes[trunk->Q921HeaderSpace+0] = ((Sapi << 2) & 0xfc) | ((cr << 1) & 0x02); + mes[trunk->Q921HeaderSpace+1] = (Tei << 1) | 0x01; + mes[trunk->Q921HeaderSpace+2] = 0x00; + mes[trunk->Q921HeaderSpace+3] = (pf & 0x01); + + Q921Log(trunk, Q921_LOG_DEBUG, "Enqueueing I frame for TEI %d [%d]\n", link->tei, Tei); + + /* transmit queue, (TODO: check for full condition!) */ + MFIFOWriteMes(link->IFrameQueue, mes, size); + + /* try to send queued frame */ + Q921SendQueuedIFrame(trunk, link->tei); + + return 1; +} + +/** + * Q921SendQueuedIFrame + * \brief Try to transmit queued I frame (if available) + */ +static int Q921SendQueuedIFrame(L2TRUNK trunk, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + L2INT size = 0; + L2UCHAR *mes; + + if(MFIFOGetMesCount(link->IFrameQueue) == 0) { + return 0; + } + + /* Link ready? */ + if(link->state != Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + return 0; + } + + /* peer receiver busy? */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY)) { + return 0; + } + + /* V(S) = V(A) + k? */ + if(link->vs == ((link->va + trunk->k) % 128)) { + Q921Log(trunk, Q921_LOG_WARNING, "Maximum number (%d) of outstanding I frames reached for TEI %d\n", trunk->k, tei); + return 0; + } + + mes = MFIFOGetMesPtr(link->IFrameQueue, &size); + if(mes) { + /* Fill in + update counter values */ + mes[trunk->Q921HeaderSpace+2] = link->vs << 1; + mes[trunk->Q921HeaderSpace+3] |= link->vr << 1; + + if(MFIFOGetMesCount(link->IFrameQueue) == 0) { + /* clear I frame queued */ + } + + /* Send I frame */ + Q921Tx21Proc(trunk, mes, size); + + /* V(S) = V(S) + 1 */ + Q921_INC_COUNTER(link->vs); + + /* clear acknowledge pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + + /* T200 running? */ + if(!link->T200) { + /* Stop T203, Start T200 */ + Q921T200TimerStart(trunk, tei); + Q921T203TimerStop(trunk, tei); + } + + /* put frame into resend queue */ + MFIFOWriteMesOverwrite(link->IFrameResendQueue, mes, size); + + /* dequeue frame */ + MFIFOKillNext(link->IFrameQueue); + + /* Restart TM01 */ + if(Q921_IS_NT(trunk)) { + Q921TM01TimerReset(trunk, tei); + } + + /* no state change */ + return 1; + } + + return 0; +} + +/** + * Q921SendS + * \brief Prepare and send S frame + */ +static int Q921SendS(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR sv, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, Tei); + + if(!Q921_IS_READY(link)) { + /* don't even bother trying */ + Q921Log(trunk, Q921_LOG_DEBUG, "Link not ready, discarding S frame for TEI %d\n", Tei); + return 0; + } + + /* S frame header */ + mes[trunk->Q921HeaderSpace+0] = ((Sapi << 2) & 0xfc) | ((cr << 1) & 0x02); + mes[trunk->Q921HeaderSpace+1] = (Tei << 1) | 0x01; + mes[trunk->Q921HeaderSpace+2] = ((sv << 2) & 0x0c) | 0x01; + mes[trunk->Q921HeaderSpace+3] = (link->vr << 1) | (pf & 0x01); + + return Q921Tx21Proc(trunk, mes, size); +} + + +/** + * Q921SendU + * \brief Prepare and send U frame + */ +static int Q921SendU(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR m, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, Tei); + + /* U frame header */ + mes[trunk->Q921HeaderSpace+0] = ((Sapi << 2) & 0xfc) | ((cr << 1) & 0x02); + mes[trunk->Q921HeaderSpace+1] = (Tei << 1) | 0x01; + mes[trunk->Q921HeaderSpace+2] = ((m << 3) & 0xe0) | ((pf << 4) & 0x10) | ((m << 2) & 0x0c) | 0x03; + + /* link not ready? enqueue non-TEI-mgmt UI (DL-UNIT DATA) frames */ + if(m == 0x00 && Sapi != Q921_SAPI_TEI && link->state < Q921_STATE_TEI_ASSIGNED) { + + /* write frame to queue */ + MFIFOWriteMes(link->UIFrameQueue, mes, size); + + Q921Log(trunk, Q921_LOG_DEBUG, "Link not ready, UI Frame of size %d bytes queued for TEI %d\n", size, Tei); + return 1; + } + + return Q921Tx21Proc(trunk, mes, size); +} + +/** + * TODO: NT mode handling? Need a way to get Link context from Q.931 + */ +int Q921Rx32(L2TRUNK trunk, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR * Mes, L2INT Size) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); /* TODO: need real link tei for NT mode */ + L2INT res = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "Got frame from Q.931, type: %d, tei: %d, size: %d\n", ind, tei, Size); + + switch(ind) { + case Q921_DL_ESTABLISH: + /* + * Hmm... + */ + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + if(!Q921_IS_NT(trunk)) { + /* establish data link */ + Q921EstablishDataLink(trunk, link->tei); + + /* Set layer 3 initiated */ + Q921_SET_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, link->tei); + } + break; + + case Q921_STATE_AWAITING_ESTABLISHMENT: + if(!Q921_IS_NT(trunk)) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* Set layer 3 initiated */ + Q921_SET_FLAG(link, Q921_FLAG_L3_INITIATED); + } + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + if(!Q921_IS_NT(trunk)) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* establish data link */ + Q921EstablishDataLink(trunk, link->tei); + + /* Set layer 3 initiated */ + Q921_SET_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, link->tei); + } + break; + + default: + break; + } + break; + + case Q921_DL_RELEASE: + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + /* send DL-RELEASE confirm */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE, tei, NULL, 0); + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + if(!Q921_IS_NT(trunk)) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* RC = 0 */ + link->N200 = 0; + + /* send DISC command */ + Q921SendDISC(trunk, trunk->sapi, Q921_COMMAND(trunk), link->tei, 1); + + /* Stop T203, restart T200 */ + if(link->state == Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + Q921T203TimerStop(trunk, link->tei); + } + Q921T200TimerReset(trunk, link->tei); + + /* change state */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_RELEASE, link->tei); + } + break; + + default: + break; + } + break; + + case Q921_DL_DATA: /* DL-DATA request */ + res = Q921EnqueueI(trunk, + trunk->sapi, + Q921_COMMAND(trunk), + link->tei, + 0, + Mes, + Size); + + if(link->state < Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + /* Treat as implicit DL-ESTABLISH request */ + + /* establish data link */ + Q921EstablishDataLink(trunk, link->tei); + + /* Set layer 3 initiated */ + Q921_SET_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, link->tei); + } + break; + + case Q921_DL_UNIT_DATA: /* DL-UNIT DATA request */ + res = Q921SendUN(trunk, + trunk->sapi, + Q921_COMMAND(trunk), + Q921_TEI_BCAST, + 0, + Mes, + Size); + /* NOTE: Let the other side initiate link establishment */ + break; + + default: + break; + } + + return res; +} +/***************************************************************************** + + Function: Q921SendRR + + Description: Compose and send Receive Ready. + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P/F fiels octet 5 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ + +static int Q921SendRR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x00, mes, trunk->Q921HeaderSpace+4); +} + +/***************************************************************************** + + Function: Q921SendRNR + + Description: Compose and send Receive Nor Ready + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P/F fiels octet 5 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendRNR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x01, mes, trunk->Q921HeaderSpace+4); +} + +/***************************************************************************** + + Function: Q921SendREJ + + Description: Compose and Send Reject. + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P/F fiels octet 5 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendREJ(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x03, mes, trunk->Q921HeaderSpace+4); +} + +/***************************************************************************** + + Function: Q921SendSABME + + Description: Compose and send SABME + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P fiels octet 4 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendSABME(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x0f, mes, trunk->Q921HeaderSpace+3); +} + + +/** + * Q921Start + * \brief Start trunk + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success; <= 0 on error + */ +int Q921Start(L2TRUNK trunk) +{ + int x, numlinks = Q921_IS_PTMP_NT(trunk) ? Q921_TEI_MAX : 1; + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + + if(trunk->initialized != INITIALIZED_MAGIC) + return 0; + + memset(trunk->context, 0, numlinks * sizeof(struct Q921_Link)); + + /* Common init part */ + for(x = 0; x <= numlinks; x++) { + link = Q921_LINK_CONTEXT(trunk, x); + + link->state = Q921_STATE_TEI_UNASSIGNED; + link->tei = 0; + + /* Initialize per-TEI I + UI queues */ + MFIFOCreate(link->UIFrameQueue, Q921MAXHDLCSPACE, 10); + MFIFOCreate(link->IFrameQueue, Q921MAXHDLCSPACE, 10); + MFIFOCreate(link->IFrameResendQueue, Q921MAXHDLCSPACE, 10); + } + + if(Q921_IS_PTMP_TE(trunk)) { + link->state = Q921_STATE_TEI_UNASSIGNED; + link->tei = 0; + } + else if(Q921_IS_PTMP_NT(trunk)) { + link = Q921_TRUNK_CONTEXT(trunk); + + link->state = Q921_STATE_TEI_ASSIGNED; + link->tei = trunk->tei; + + /* clear tei map */ + memset(trunk->tei_map, 0, Q921_TEI_MAX + 1); + } + else { + link->state = Q921_STATE_TEI_ASSIGNED; + link->tei = trunk->tei; + } + + Q921Log(trunk, Q921_LOG_DEBUG, "Starting trunk %p (sapi: %d, tei: %d, mode: %s %s)\n", + trunk, + trunk->sapi, + link->tei, + Q921_IS_PTMP(trunk) ? "PTMP" : "PTP", + Q921_IS_TE(trunk) ? "TE" : "NT"); + + if(Q921_IS_PTP(trunk)) { + Q921Log(trunk, Q921_LOG_DEBUG, "Sending SABME\n"); + + return Q921SendSABME(trunk, + trunk->sapi, + Q921_COMMAND(trunk), + link->tei, + 1); + + } else if(Q921_IS_PTMP_NT(trunk)) { + + Q921Log(trunk, Q921_LOG_DEBUG, "Revoking all TEIs\n"); + + return Q921TeiSendRemoveRequest(trunk, Q921_TEI_BCAST); /* Revoke all TEIs in use */ + } else { + + Q921Log(trunk, Q921_LOG_DEBUG, "Requesting TEI\n"); + + return Q921TeiSendAssignRequest(trunk); + } +} + + +/** + * Q921Stop + * \brief Stop trunk + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +int Q921Stop(L2TRUNK trunk) +{ + struct Q921_Link *link; + int x, numlinks; + + if(!trunk) + return -1; + + link = Q921_TRUNK_CONTEXT(trunk); + numlinks = Q921_IS_PTMP_NT(trunk) ? Q921_TEI_MAX : 1; + + if(Q921_IS_STOPPED(link)) + return 0; + + /* Release TEI */ + if(Q921_IS_PTMP_TE(trunk)) { + /* send verify request */ + Q921TeiSendVerifyRequest(trunk); + + /* drop TEI */ + link->tei = 0; + } + + /* Stop timers, stop link, flush queues */ + for(x = 0; x <= numlinks; x++) { + Q921T200TimerStop(trunk, x); + Q921T203TimerStop(trunk, x); + Q921T201TimerStop(trunk, x); + + /* Change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_STOPPED, x); + + /* Flush per-tei I/UI queues */ + MFIFOClear(link->UIFrameQueue); + MFIFOClear(link->IFrameQueue); + MFIFOClear(link->IFrameResendQueue); + } + Q921T202TimerStop(trunk); + + /* Flush HDLC queue */ + MFIFOClear(trunk->HDLCInQueue); + + return 0; +} + + +/***************************************************************************** + + Function: Q921SendDM + + Description: Compose and Send DM (Disconnected Mode) + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf F fiels octet 4 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendDM(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x03, mes, trunk->Q921HeaderSpace+3); +} + +/***************************************************************************** + + Function: Q921SendDISC + + Description: Compose and Send Disconnect + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf P fiels octet 4 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendDISC(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x08, mes, trunk->Q921HeaderSpace+3); +} + +/***************************************************************************** + + Function: Q921SendUA + + Description: Compose and Send UA + + Parameters: trunk trunk # + Sapi Sapi + cr C/R field. + Tei Tei. + pf F fiels octet 4 + + Return Value: 0 if failed, 1 if Send. + +*****************************************************************************/ +static int Q921SendUA(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf) +{ + L2UCHAR mes[25]; + + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x0c, mes, trunk->Q921HeaderSpace+3); +} + +static int Q921SendUN(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf, L2UCHAR *mes, L2INT size) +{ + return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x00, mes, size+trunk->Q921HeaderSpace+3); +} + + +/** + * Q921ProcSABME + * \brief Handle incoming SABME + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcSABME(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + /* send UA */ + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), /* or command? */ + tei, pf); + + /* clear counters */ + link->vr=0; + link->vs=0; + link->va=0; + + /* TODO: send DL-Establish indication to Q.931 */ + Q921Tx23Proc(trunk, Q921_DL_ESTABLISH, tei, NULL, 0); + + /* start T203 */ + Q921T203TimerStart(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + break; + + case Q921_STATE_AWAITING_ESTABLISHMENT: + /* send UA */ + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* no state change */ + break; + + case Q921_STATE_AWAITING_RELEASE: + /* send DM */ + Q921SendDM(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* no state change */ + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + /* send UA */ + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* clear exception conditions */ + Q921ResetExceptionConditions(trunk, tei); + + /* send MDL-Error indication */ + + /* V(S) == V(A) ? */ + if(link->vs != link->va) { + /* clear I queue */ + MFIFOClear(link->IFrameQueue); + + /* DL-Establish indication */ + Q921Tx23Proc(trunk, Q921_DL_ESTABLISH, tei, NULL, 0); + } + + /* clear counters */ + link->vr=0; + link->vs=0; + link->va=0; + + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + /* state change only if in TIMER_RECOVERY state */ + if(link->state == Q921_STATE_TIMER_RECOVERY) + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + break; + + default: + break; + } + + return 1; +} + + +/** + * Q921ProcDM + * \brief Handle incoming DM + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcDM(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + if(!pf) { + /* to next state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_AWAITING_ESTABLISHMENT: + case Q921_STATE_AWAITING_RELEASE: + if(pf) { + if(link->state == Q921_STATE_AWAITING_ESTABLISHMENT) { + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + } + + /* Send DL-Release indication to Q.931 */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE, tei, NULL, 0); + + /* Stop T200 */ + Q921T200TimerStop(trunk, tei); + + /* Change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + } + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + if(pf) { + /* MDL-Error indication (B) */ + + /* no state change */ + } else { + /* MDL-Error indication (E) */ + + /* establish data link */ + Q921EstablishDataLink(trunk, tei); + + /* clear L3 initiated */ + Q921_CLEAR_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state (no action?) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + if(pf) { + /* MDL Error indication (B) */ + } else { + /* MDL Error indication (E) */ + } + + /* establish data link */ + Q921EstablishDataLink(trunk, tei); + + /* clear layer 3 initiated */ + Q921_CLEAR_FLAG(link, Q921_FLAG_L3_INITIATED); + + /* change state */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + break; + + default: + break; + } + + return 1; +} + +/** + * Q921ProcUA + * \brief Handle incoming UA + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcUA(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + case Q921_STATE_TIMER_RECOVERY: + /* MDL Error indication (C, D) */ + Q921Log(trunk, Q921_LOG_ERROR, "Received UA frame in invalid state\n"); + break; + + case Q921_STATE_AWAITING_ESTABLISHMENT: + if(pf) { + /* TODO: other fancy stuff (see docs) */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_L3_INITIATED)) { /* layer3 initiated */ + link->vr = 0; + + /* DL-Establish confirm */ + Q921Tx23Proc(trunk, Q921_DL_ESTABLISH_CONFIRM, tei, NULL, 0); + + } else if(link->vs != link->va) { + + /* discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* DL-Establish indication */ + Q921Tx23Proc(trunk, Q921_DL_ESTABLISH, tei, NULL, 0); + } + + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + link->vs = 0; + link->va = 0; + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + } else { + /* MDL Error indication (C, D) */ + Q921Log(trunk, Q921_LOG_ERROR, "Received UA frame is not a response to a request\n"); + + /* no state change */ + } + break; + + case Q921_STATE_AWAITING_RELEASE: + if(pf) { + /* DL Release confirm */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE_CONFIRM, tei, NULL, 0); + + /* Stop T200 */ + Q921T200TimerStop(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + } else { + /* MDL Error indication (D) */ + Q921Log(trunk, Q921_LOG_ERROR, "Received UA frame is not a response to a request\n"); + + /* no state change */ + } + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* MDL Error indication (C, D) */ + Q921Log(trunk, Q921_LOG_ERROR, "Received UA frame in invalid state\n"); + + /* no state change */ + break; + + default: + break; + } + + return 1; +} + + +/** + * Q921ProcDISC + * \brief Handle incoming DISC + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcDISC(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_TEI_ASSIGNED: + case Q921_STATE_AWAITING_ESTABLISHMENT: + /* Send DM */ + Q921SendDM(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* no state change */ + break; + + case Q921_STATE_AWAITING_RELEASE: + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* no state change */ + break; + + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + /* Discard I queue */ + MFIFOClear(link->IFrameQueue); + + /* send UA */ + Q921SendUA(trunk, + trunk->sapi, + Q921_RESPONSE(trunk), + tei, pf); + + /* DL Release indication */ + Q921Tx23Proc(trunk, Q921_DL_RELEASE, tei, NULL, 0); + + /* Stop T200 */ + Q921T200TimerStop(trunk, tei); + + if(link->state == Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + /* Stop T203 */ + Q921T203TimerStop(trunk, tei); + } + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + break; + + default: + Q921Log(trunk, Q921_LOG_ERROR, "Invalid DISC received in state \"%s\" (%d)", Q921State2Name(link->state), link->state); + break; + } + + return 1; +} + + +/** + * Q921ProcRR + * \brief Handle incoming RR + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcRR(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR cr = (mes[0] & 0x02) >> 1; + L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = (mes[3] >> 1); +// L2UCHAR sapi = (mes[0] & 0xfc) >> 2; + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* clear receiver peer busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + if (Q921_IS_COMMAND(trunk, cr)) { /* if this is a command */ + if(pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + } else { + if(pf) { + /* MDL Error indication */ + } + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + if(nr == link->vs) { + /* V(A) = N(R) */ + link->va = nr; + + /* Stop T200, restart T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerReset(trunk, tei); + + } else if(nr == link->va) { + + /* do nothing */ + + } else { + /* V(A) = N(R) */ + link->va = nr; + + /* Restart T200 */ + Q921T200TimerReset(trunk, tei); + } + /* no state change */ + + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + /* clear receiver peer busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command + P? */ + if(Q921_IS_COMMAND(trunk, cr) && pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + /* V(A) = N(R) */ + link->va = nr; + + if(!Q921_IS_COMMAND(trunk, cr) && pf) { + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + /* Invoke retransmission */ + Q921InvokeRetransmission(trunk, tei, nr); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + } + /* no state change otherwise */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + default: + break; + } + return 1; +} + + +/** + * Q921ProcREJ + * \brief Handle incoming REJ + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcREJ(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR cr = (mes[0] & 0x02) >> 1; + L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = (mes[3] >> 1); +// L2UCHAR sapi = (mes[0] & 0xfc) >> 2; + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* clear receiver peer busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command? */ + if(Q921_IS_COMMAND(trunk, cr)) { + if(pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + } else { + if(pf) { + /* MDL Error indication (A) */ + } + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + /* V(A) = N(R) */ + link->va = nr; + + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + /* Invoke retransmission of frame >N(R) (?) */ + Q921InvokeRetransmission(trunk, tei, nr); + + /* no state change */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + /* clear receiver peer busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command + P ? */ + if(Q921_IS_COMMAND(trunk, cr) && pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + /* V(A) = N(R) */ + link->va = nr; + + if(!Q921_IS_COMMAND(trunk, cr) && pf) { + /* Stop T200, start T203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerStart(trunk, tei); + + /* Invoke retransmission */ + Q921InvokeRetransmission(trunk, tei, nr); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + } + /* no state change otherwise */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + default: + break; + } + + return 1; +} + + +/** + * Q921ProcRNR + * \brief Handle incoming RNR + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success, <= 0 on error + */ +static int Q921ProcRNR(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR cr = (mes[0] & 0x02) >> 1; + L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = (mes[3] >> 1); +// L2UCHAR sapi = (mes[0] & 0xfc) >> 2; + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* set peer receiver busy */ + Q921_SET_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command? */ + if(Q921_IS_COMMAND(trunk, cr)) { + if(pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + } else { + if(pf) { + /* MDL Error indication (A) */ + } + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + /* V(A) = N(R) */ + link->va = nr; + + /* Stop T203, restart T200 */ + Q921T200TimerReset(trunk, tei); + Q921T203TimerStop(trunk, tei); + + /* no state change */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + /* set peer receiver busy */ + Q921_SET_FLAG(link, Q921_FLAG_PEER_RECV_BUSY); + + /* command + P? */ + if(Q921_IS_COMMAND(trunk, cr) && pf) { + /* Enquiry response */ + Q921SendEnquiryResponse(trunk, tei); + } + + /* */ + if(link->va <= nr && nr <= link->vs) { + + /* V(A) = N(R) */ + link->va = nr; + + if(!Q921_IS_COMMAND(trunk, cr) && pf) { + /* Restart T200 */ + Q921T200TimerReset(trunk, tei); + + /* Invoke retransmission */ + Q921InvokeRetransmission(trunk, tei, nr); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, tei); + } + /* no state change otherwise */ + } else { + /* N(R) Error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + default: + break; + } + + return 1; +} + +#if 0 +static int Q921SetReceiverBusy(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + if(!Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + /* set own receiver busy */ + Q921_SET_FLAG(link, Q921_FLAG_RECV_BUSY); + + /* send RR response */ + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), link->tei, 0); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + if(!Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + /* set own receiver busy */ + Q921_SET_FLAG(link, Q921_FLAG_RECV_BUSY); + + /* send RNR response */ + Q921SendRNR(trunk, trunk->sapi, Q921_RESPONSE(trunk), link->tei, 0); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + break; + + default: + break; + } + + return 0; +} + +static int Q921ClearReceiverBusy(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + case Q921_STATE_TIMER_RECOVERY: + if(Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + /* clear own receiver busy */ + Q921_CLEAR_FLAG(link, Q921_FLAG_RECV_BUSY); + + /* send RNR response */ + Q921SendRNR(trunk, trunk->sapi, Q921_RESPONSE(trunk), link->tei, 0); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + break; + + default: + break; + } + + return 0; +} +#endif + +static int Q921ProcIFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + /* common fields: get sapi, tei and cr */ +// L2UCHAR sapi = (mes[0] & 0xfc) >> 2; +// L2UCHAR cr = (mes[0] & 0x02) >> 1; + L2UCHAR tei = (mes[1] & 0xfe) >> 1; + L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + L2UCHAR nr = mes[3] >> 1; /* receive sequence number */ + L2UCHAR ns = mes[2] >> 1; /* send sequence number */ + L2UCHAR discard = 0; + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* Ignore I frames in earlier states */ + if(link->state < Q921_STATE_MULTIPLE_FRAME_ESTABLISHED) { + Q921Log(trunk, Q921_LOG_NOTICE, "I frame in invalid state ignored\n"); + return 0; + } + + /* Receiver busy? */ + if(Q921_CHECK_FLAG(link, Q921_FLAG_RECV_BUSY)) { + /* discard information */ + discard = 1; + + if(pf) { + /* send RNR Response */ + Q921SendRNR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + + /* Clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + } + else { + if(ns != link->vr) { + /* discard information */ + discard = 1; + + if(Q921_CHECK_FLAG(link, Q921_FLAG_REJECT) && pf) { + + /* Send RR response */ + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + else if(!Q921_CHECK_FLAG(link, Q921_FLAG_REJECT)){ + + /* set reject exception */ + Q921_SET_FLAG(link, Q921_FLAG_REJECT); + + /* Send REJ response */ + Q921SendREJ(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, pf); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + } + else { + /* V(R) = V(R) + 1 */ + Q921_INC_COUNTER(link->vr); + + /* clear reject exception */ + Q921_CLEAR_FLAG(link, Q921_FLAG_REJECT); + + /* DL-Data indication */ + Q921Tx23Proc(trunk, Q921_DL_DATA, tei, mes, size); + + if(pf) { + /* Send RR response */ + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 1); + + /* clear ack pending */ + Q921_CLEAR_FLAG(link, Q921_FLAG_ACK_PENDING); + } + else if(!Q921_CHECK_FLAG(link, Q921_FLAG_ACK_PENDING)) { + /* ack pending */ + + /* Send RR response */ + Q921SendRR(trunk, trunk->sapi, Q921_RESPONSE(trunk), tei, 0); + + /* set ack pending*/ + Q921_SET_FLAG(link, Q921_FLAG_ACK_PENDING); + } + } + } + + + switch(link->state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + if(link->va <= nr && nr <= link->vs) { + if(Q921_CHECK_FLAG(link, Q921_FLAG_PEER_RECV_BUSY)) { + link->va = nr; + } + else if(nr == link->vs) { + /* V(A) = N(R) */ + link->va = nr; + + /* stop t200, restart t203 */ + Q921T200TimerStop(trunk, tei); + Q921T203TimerReset(trunk, tei); + } + else if(nr != link->va) { + /* V(A) = N(R) */ + link->va = nr; + + /* restart T200 */ + Q921T200TimerReset(trunk, tei); + } + + /* Restart TM01 */ + if(Q921_IS_NT(trunk)) { + Q921TM01TimerReset(trunk, tei); + } + } + else { + /* N(R) error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + case Q921_STATE_TIMER_RECOVERY: + if(link->va <= nr && nr <= link->vs) { + /* V(A) = N(R) */ + link->va = nr; + + /* Restart TM01 */ + if(Q921_IS_NT(trunk)) { + Q921TM01TimerReset(trunk, tei); + } + } + else { + /* N(R) error recovery */ + Q921NrErrorRecovery(trunk, tei); + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_AWAITING_ESTABLISHMENT, tei); + } + break; + + default: + break; + } + + return 0; +} + + +static int Q921ProcSFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR sv = (mes[2] & 0x0c) >> 2; /* supervisory format id */ + //L2UCHAR pf = mes[3] & 0x01; /* poll / final flag */ + //L2UCHAR nr = mes[3] >> 1; /* receive sequence number */ + L2INT res = -1; + + switch(sv) { + case 0x00: /* RR : Receive Ready */ + res = Q921ProcRR(trunk, mes, size); + break; + + case 0x02: /* RNR : Receive Not Ready */ + res = Q921ProcRNR(trunk, mes, size); + break; + + case 0x04: /* REJ : Reject */ + res = Q921ProcREJ(trunk, mes, size); + break; + + default: /* Invalid / Unknown */ + Q921Log(trunk, Q921_LOG_ERROR, "Invalid S frame type %d\n", sv); + break; + } + + return res; +} + + + +static int Q921ProcUFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR m = (mes[2] & 0xe0) >> 3 | (mes[2] & 0x0c) >> 2; /* modifier function id */ +// L2UCHAR pf = (mes[2] & 0x10) >> 4; /* poll / final flag */ + L2INT res = -1; + + switch(m) { + case 0x00: /* UN : Unnumbered Information */ + if(mes[3] == Q921_LAYER_ENT_ID_TEI) + { + if(!Q921_IS_PTMP(trunk)) { + /* wtf? nice try */ + return res; + } + + switch(mes[6]) { + case Q921_TEI_ID_REQUEST: /* (TE ->) NT */ + res = Q921TeiProcAssignRequest(trunk, mes, size); + break; + + case Q921_TEI_ID_ASSIGNED: /* (NT ->) TE */ + case Q921_TEI_ID_DENIED: + res = Q921TeiProcAssignResponse(trunk, mes, size); + break; + + case Q921_TEI_ID_CHECKREQ: /* (NT ->) TE */ + res = Q921TeiProcCheckRequest(trunk, mes, size); + break; + + case Q921_TEI_ID_CHECKRESP: /* (TE ->) NT */ + res = Q921TeiProcCheckResponse(trunk, mes, size); + break; + + case Q921_TEI_ID_REMOVE: /* (NT ->) TE */ + res = Q921TeiProcRemoveRequest(trunk, mes, size); + break; + + case Q921_TEI_ID_VERIFY: /* (TE ->) NT */ + res = Q921TeiProcVerifyRequest(trunk, mes, size); + break; + + default: /* Invalid / Unknown */ + Q921Log(trunk, Q921_LOG_ERROR, "Invalid UN message from TEI management/endpoint\n"); + break; + } + } + else if(mes[3] == Q921_LAYER_ENT_ID_Q931) { + + Q921Log(trunk, Q921_LOG_DEBUG, "UI Frame for Layer 3 received\n"); + + res = Q921Tx23Proc(trunk, Q921_DL_UNIT_DATA, 0, mes, size); + } + break; + + case 0x03: /* DM : Disconnect Mode */ + res = Q921ProcDM(trunk, mes, size); + break; + + case 0x08: /* DISC : Disconnect */ + res = Q921ProcDISC(trunk, mes, size); + break; + + case 0x0c: /* UA : Unnumbered Acknowledgement */ + res = Q921ProcUA(trunk, mes, size); + break; + + case 0x0f: /* SABME : Set Asynchronous Balanced Mode Extend */ + res = Q921ProcSABME(trunk, mes, size); + break; + + case 0x11: /* FRMR : Frame Reject */ + case 0x17: /* XID : Exchange Identification */ + res = 0; + break; + + default: /* Unknown / Invalid */ + Q921Log(trunk, Q921_LOG_ERROR, "Invalid U frame type: %d\n", m); + break; + } + + return res; +} + + +/***************************************************************************** + + Function: Q921Rx12 + + Description: Called to process a message frame from layer 1. Will + identify the message and call the proper 'processor' for + layer 2 messages and forward I frames to the layer 3 entity. + + Q921Rx12 will check the input fifo for a message, and if a + message exist process one message before it exits. The caller + must either call Q921Rx12 polling or keep track on # + messages in the queue. + + Parameters: trunk trunk #. + + Return Value: # messages processed (always 1 or 0). + +*****************************************************************************/ +int Q921Rx12(L2TRUNK trunk) +{ + L2INT size; /* receive size & Q921 frame size*/ + L2UCHAR *smes = MFIFOGetMesPtr(trunk->HDLCInQueue, &size); + + if(smes) + { + struct Q921_Link *link; + L2UCHAR sapi, tei; + L2UCHAR *mes; + L2INT rs; + + rs = size - trunk->Q921HeaderSpace; + mes = &smes[trunk->Q921HeaderSpace]; + + Q921LogMesg(trunk, Q921_LOG_DEBUG, 1, mes, rs, "New packet received (%d bytes)", rs); + + /* common fields: get sapi, tei and cr */ + sapi = (mes[0] & 0xfc) >> 2; + tei = (mes[1] & 0xfe) >> 1; + link = Q921_LINK_CONTEXT(trunk, tei); + + if(Q921_IS_PTMP_TE(trunk) && ( + (link->state >= Q921_STATE_TEI_ASSIGNED && tei != link->tei && tei != Q921_TEI_BCAST) || /* Assigned TEI: Only BCAST and directed */ + (link->state == Q921_STATE_TEI_UNASSIGNED && tei != Q921_TEI_BCAST))) /* No assigned TEI: Only BCAST */ + { + /* Ignore Messages with foreign TEIs */ + goto out; + } + + if((mes[2] & 0x01) == 0x00) { /* I frame */ + Q921ProcIFrame(trunk, mes, rs); + } + else if((mes[2] & 0x03) == 0x01) { /* S frame */ + Q921ProcSFrame(trunk, mes, rs); + } + else if((mes[2] & 0x03) == 0x03) { /* U frame */ + Q921ProcUFrame(trunk, mes, rs); + } + else { + Q921Log(trunk, Q921_LOG_ERROR, "Invalid frame type: %d\n", (int)(mes[2] & 0x03)); + /* TODO: send FRMR or REJ */ + } + +out: + MFIFOKillNext(trunk->HDLCInQueue); + + return 1; + } + + return 0; +} + +/* + * Misc + */ +/** + * Q921SetLogCB + * \brief Set logging callback + * \param[in] trunk pointer to Q921 data struct + * \param[in] func pointer to logging callback function + * \param[in] priv pointer to private data + * + * \author Stefan Knoblich + */ +void Q921SetLogCB(L2TRUNK trunk, Q921LogCB_t func, void *priv) +{ + if(!trunk) + return; + + trunk->Q921LogProc = func; + trunk->PrivateDataLog = priv; +} + +/** + * Q921SetLogLevel + * \brief Set loglevel of Q.921 logging functions + * \param[in] trunk pointer to Q921 data struct + * \param[in] level new loglevel + * + * \author Stefan Knoblich + */ +void Q921SetLogLevel(L2TRUNK trunk, Q921LogLevel_t level) +{ + if(!trunk) + return; + + if (level < Q921_LOG_NONE) { + level = Q921_LOG_NONE; + } else if (level > Q921_LOG_DEBUG) { + level = Q921_LOG_DEBUG; + } + + trunk->loglevel = level; +} + + +/** + * Q921ChangeState + * \brief Change state, invoke neccessary actions + * \param[in] trunk pointer to Q921 data struct + * \param[in] state state to change to + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921ChangeState(L2TRUNK trunk, Q921State_t state, L2UCHAR tei) +{ + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + Q921State_t oldstate = link->state; + int res = 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "Changing state from \"%s\" (%d) to \"%s\" (%d) for TEI %d\n", + Q921State2Name(oldstate), oldstate, + Q921State2Name(state), state, + tei); + + /* + * generic actions (depending on the target state only) + */ + switch(state) { + case Q921_STATE_MULTIPLE_FRAME_ESTABLISHED: + /* Start TM01 */ + if(Q921_IS_NT(trunk)) { + Q921TM01TimerStart(trunk, tei); + } + break; + + default: + break; + } + + /* + * actions that depend on type of the old -> new state transition + */ + switch(oldstate) { + case Q921_STATE_STOPPED: + + switch(state) { + case Q921_STATE_TEI_UNASSIGNED: + if(Q921_IS_PTMP_TE(trunk)) { + res = Q921TeiSendAssignRequest(trunk); + } + break; + + case Q921_STATE_TEI_ASSIGNED: + if(Q921_IS_PTMP_NT(trunk)) { + res = Q921TeiSendRemoveRequest(trunk, Q921_TEI_BCAST); + } + break; + + default: + break; + } + break; + + default: + break; + } + + link->state = state; + + Q921Log(trunk, Q921_LOG_DEBUG, "Q921ChangeState() returns %d, new state is \"%s\" (%d) for TEI %d\n", res, Q921State2Name(state), state, tei); + + return res; +} + +/* + * TEI Management functions + * \note All TEI-mgmt UN frames are sent with cr = command! + */ +static int Q921TeiSend(L2TRUNK trunk, L2UCHAR type, L2USHORT ri, L2UCHAR ai) +{ + L2UCHAR mes[10]; + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + + mes[offset++] = Q921_LAYER_ENT_ID_TEI; /* layer management entity identifier */ + mes[offset++] = (ri & 0xff00) >> 8; /* reference number upper part */ + mes[offset++] = ri & 0xff; /* reference number lower part */ + mes[offset++] = type; /* message type: Identity Request */ + mes[offset++] = ai << 1 | 0x01; /* action indicator: TEI */ + + return Q921SendU(trunk, Q921_SAPI_TEI, Q921_COMMAND(trunk), Q921_TEI_BCAST, 0, 0x00, mes, offset); +} + + +/** + * Q921TeiSendAssignRequest + * \brief Ask for new TEI (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success, <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendAssignRequest(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2INT res; + + if (!Q921_IS_PTMP_TE(trunk)) /* only ptmp te mode*/ + return 0; + +#ifndef WIN32 + link->ri = (L2USHORT)(random() % 0xffff); +#else + link->ri = (L2USHORT)(rand() % 0xffff); //todo +#endif + + /* send TEI assign request */ + res = Q921TeiSend(trunk, Q921_TEI_ID_REQUEST, link->ri, Q921_TEI_BCAST); + + /* start T202 */ + Q921T202TimerStart(trunk); + + return res; +} + + +/** + * Q921TeiProcessAssignResponse + * \brief Process assign response (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcAssignResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2USHORT ri = 0; + + if (!Q921_IS_PTMP_TE(trunk)) /* PTMP TE only */ + return 0; + + ri = (mes[offset + 1] << 8) | mes[offset + 2]; + + if(ri != link->ri) { + /* hmmm ..., not our response i guess */ + return 0; + } + + switch(mes[offset + 3]) { + case Q921_TEI_ID_ASSIGNED: + /* Yay, use the new TEI and change state to assigned */ + link->tei = mes[offset + 4] >> 1; + + Q921Log(trunk, Q921_LOG_DEBUG, "Assigned TEI %d, setting state to TEI_ASSIGNED\n", link->tei); + + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, link->tei); + break; + + case Q921_TEI_ID_DENIED: + /* oops, what to do now? */ + if ((mes[offset + 4] >> 1) == Q921_TEI_BCAST) { + /* No more free TEIs? this is bad */ + + //Q921TeiSendVerifyRequest(trunk, Q921_TEI_BCAST); /* TODO: does this work ?? */ + } else { + /* other reason, this is fatal, shutdown link */ + } + + Q921Log(trunk, Q921_LOG_DEBUG, "TEI assignment has been denied, reason: %s\n", + ((mes[offset +4] >> 1) == Q921_TEI_BCAST) ? "No free TEIs available" : "Unknown"); + + Q921ChangeState(trunk, Q921_STATE_TEI_UNASSIGNED, link->tei); + break; + + default: + return 0; + } + + /* stop T202 */ + Q921T202TimerStop(trunk); + + return 1; +} + + +/** + * Q921TeiSendVerifyRequest + * \brief Verify TEI (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendVerifyRequest(L2TRUNK trunk) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2INT res; + + if (!Q921_IS_PTMP_TE(trunk)) /* only ptmp te mode*/ + return 0; + + /* Request running? */ + if (trunk->T202) + return 0; + + /* Send TEI verify request */ + res = Q921TeiSend(trunk, Q921_TEI_ID_VERIFY, link->ri, link->tei); + + /* start T202 */ + Q921T202TimerStart(trunk); + + return res; +} + + +/** + * Q921TeiProcCheckRequest + * \brief Process Check Request (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcCheckRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2UCHAR tei = (mes[offset + 4] >> 1); /* action indicator => tei */ + L2INT res = 0; + + if (!Q921_IS_PTMP_TE(trunk)) /* ptmp te mode only */ + return 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "Received TEI Check request for TEI %d\n", tei); + + if (tei == Q921_TEI_BCAST || tei == link->tei) { + /* + * Broadcast TEI check or for our assigned TEI + */ + + /* send TEI check reponse */ + res = Q921TeiSend(trunk, Q921_TEI_ID_CHECKRESP, link->ri, link->tei); + + Q921T202TimerStop(trunk); + } + + return res; +} + + +/** + * Q921TeiProcRemoveRequest + * \brief Process remove Request (TE mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcRemoveRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link = Q921_TRUNK_CONTEXT(trunk); + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2UCHAR tei = (mes[offset + 4] >> 1); /* action indicator => tei */ + L2INT res = 0; + + if (!Q921_IS_PTMP_TE(trunk)) /* ptmp te mode only */ + return 0; + + Q921Log(trunk, Q921_LOG_DEBUG, "Received TEI Remove request for TEI %d\n", tei); + + if (tei == Q921_TEI_BCAST || tei == link->tei) { + /* + * Broadcast TEI remove or for our assigned TEI + */ + + /* reset tei */ + link->tei = 0; + + /* change state (no action) */ + Q921ChangeState(trunk, Q921_STATE_TEI_UNASSIGNED, link->tei); + + /* TODO: hmm, request new one ? */ + res = Q921TeiSendAssignRequest(trunk); + } + return res; +} + + +/** + * Q921TeiProcAssignRequest + * \brief Process assign request from peer (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcAssignRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2USHORT ri = 0; + L2UCHAR tei = 0; + + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + ri = (mes[offset + 1] << 8) | mes[offset + 2]; + tei = mes[offset + 4] >> 1; + + if(tei == Q921_TEI_BCAST) { + int x; + + /* dynamically allocate TEI */ + for(x = Q921_TEI_DYN_MIN, tei = 0; x <= Q921_TEI_MAX; x++) { + if(!trunk->tei_map[x]) { + tei = x; + break; + } + } + } + else if(!(tei > 0 && tei < Q921_TEI_DYN_MIN)) { + /* reject TEIs that are not in the static area */ + Q921TeiSendDenyResponse(trunk, 0, ri); + + return 0; + } + + if(!tei) { + /* no free TEI found */ + Q921TeiSendDenyResponse(trunk, Q921_TEI_BCAST, ri); + } + else { + struct Q921_Link *link = Q921_LINK_CONTEXT(trunk, tei); + + /* mark used */ + trunk->tei_map[tei] = 1; + + /* assign tei */ + link->tei = tei; + + /* put context in TEI ASSIGNED state */ + Q921ChangeState(trunk, Q921_STATE_TEI_ASSIGNED, tei); + + /* send assign response */ + Q921TeiSendAssignedResponse(trunk, tei, ri); + + /* Start T201 */ + Q921T201TimerStart(trunk, tei); + } + return 0; +} + +/** + * Q921TeiSendCheckRequest + * \brief Send check request (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] tei TEI to check + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendCheckRequest(L2TRUNK trunk, L2UCHAR tei) +{ + L2INT res = 0; + + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + /* send TEI check request */ + res = Q921TeiSend(trunk, Q921_TEI_ID_CHECKREQ, 0, tei); + + /* (Re-)Start T201 timer */ + Q921T201TimerStart(trunk, tei); + + return res; +} + +/** + * Q921TeiProcCheckResponse + * \brief Process Check Response (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcCheckResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + struct Q921_Link *link; + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2USHORT ri = 0; + L2UCHAR tei = 0; + + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT mode only */ + return 0; + + ri = (mes[offset + 1] << 8) | mes[offset + 2]; + tei = mes[offset + 4] >> 1; + + /* restart T201 */ + Q921T201TimerStop(trunk, tei); + + /* reset counter */ + link = Q921_LINK_CONTEXT(trunk, tei); + link->N202 = 0; + + if(!(tei > 0 && tei < Q921_TEI_MAX) || !trunk->tei_map[tei]) { + /* TODO: Should we send a DISC first? */ + + /* TEI not assigned? Invalid TEI? */ + Q921TeiSendRemoveRequest(trunk, tei); + + /* change state */ + Q921ChangeState(trunk, Q921_STATE_STOPPED, tei); + + /* clear */ + memset(link, 0, sizeof(struct Q921_Link)); + } else { + /* Start T201 */ + Q921T201TimerStart(trunk, tei); + } + + return 0; +} + + +/** + * Q921TeiProcVerifyRequest + * \brief Process Verify Request (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] mes pointer to message buffer + * \param[in] size size of message + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiProcVerifyRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size) +{ + L2UCHAR resp[25]; + L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk); + L2UCHAR tei = 0; + + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT mode only */ + return 0; + + tei = mes[offset + 4] >> 1; + + /* todo: handle response... verify assigned TEI */ + resp[offset + 0] = 0; + + return 0; +} + +/** + * Q921TeiSendDenyResponse + * \brief Send Deny Response (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendDenyResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri) +{ + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + return Q921TeiSend(trunk, Q921_TEI_ID_DENIED, ri, tei); +} + + +/** + * Q921TeiSendAssignedResponse + * \brief Send Assigned Response (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] tei TEI to assign + * \param[in] ri RI of request + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendAssignedResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri) +{ + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + return Q921TeiSend(trunk, Q921_TEI_ID_ASSIGNED, ri, tei); +} + +/** + * Q921TeiSendRemoveRequest + * \brief Send Remove Request (NT mode only) + * \param[in] trunk pointer to Q921 data struct + * \param[in] tei TEI to remove + * \return > 0 on success; <= 0 on error + * + * \author Stefan Knoblich + */ +static int Q921TeiSendRemoveRequest(L2TRUNK trunk, L2UCHAR tei) +{ + if (!Q921_IS_PTMP_NT(trunk)) /* PTMP NT only */ + return 0; + + return Q921TeiSend(trunk, Q921_TEI_ID_REMOVE, 0, tei); +} diff --git a/libs/openzap/src/isdn/Q931.c b/libs/openzap/src/isdn/Q931.c new file mode 100644 index 0000000000..b245c144d8 --- /dev/null +++ b/libs/openzap/src/isdn/Q931.c @@ -0,0 +1,888 @@ +/***************************************************************************** + + FileName: Q931.c + + Contents: Implementation of Q.931 stack main interface functions. + See q931.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q921.h" +#include "Q931.h" +#include "national.h" +#include "DMS.h" +#include "5ESS.h" + + +/***************************************************************************** + + Dialect function pointers tables. + + The following function pointer arrays define pack/unpack functions and + processing furnctions for the different Q.931 based dialects. + + The arrays are initialized with pointers to dummy functions and later + overrided with pointers to actual functions as new dialects are added. + + The initial Q.931 will as an example define 2 dialects as it treats User + and Network mode as separate ISDN dialects. + + The API messages Q931AddProc, Q931AddMes, Q931AddIE are used to initialize + these table entries during system inititialization of a stack. + +*****************************************************************************/ +q931proc_func_t *Q931Proc[Q931MAXDLCT][Q931MAXMES]; + +q931umes_func_t *Q931Umes[Q931MAXDLCT][Q931MAXMES]; +q931pmes_func_t *Q931Pmes[Q931MAXDLCT][Q931MAXMES]; + +q931uie_func_t *Q931Uie[Q931MAXDLCT][Q931MAXIE]; +q931pie_func_t *Q931Pie[Q931MAXDLCT][Q931MAXIE]; + +q931timeout_func_t *Q931Timeout[Q931MAXDLCT][Q931MAXTIMER]; +q931timer_t Q931Timer[Q931MAXDLCT][Q931MAXTIMER]; + +void (*Q931CreateDialectCB[Q931MAXDLCT])(L3UCHAR iDialect) = { NULL, NULL }; + +Q931State Q931st[Q931MAXSTATE]; + +/***************************************************************************** + + Core system tables and variables. + +*****************************************************************************/ + +L3INT Q931L4HeaderSpace = {0}; /* header space to be ignoder/inserted */ + /* at head of each message. */ + +L3INT Q931L2HeaderSpace = {4}; /* Q921 header space, sapi, tei etc */ + +/***************************************************************************** + + Main interface callback functions. + +*****************************************************************************/ + +Q931ErrorCB_t Q931ErrorProc; /* callback for error messages. */ +L3ULONG (*Q931GetTimeProc) (void) = NULL; /* callback for func reading time in ms */ + +/***************************************************************************** + + Function: Q931SetL4HeaderSpace + + Description: Set the # of bytes to be inserted/ignored at the head of + each message. Q931 will issue a message with space for header + and the user will use this to fill in whatever header info + is required to support the architecture used. + +*****************************************************************************/ +void Q931SetL4HeaderSpace(L3INT space) +{ + Q931L4HeaderSpace = space; +} + +/***************************************************************************** + + Function: Q931SetL2HeaderSpace + + Description: Set the # of bytes to be inserted/ignored at the head of + each message. Q931 will issue a message with space for header + and the user will use this to fill in whatever header info + is required to support the architecture used. + +*****************************************************************************/ +void Q931SetL2HeaderSpace(L3INT space) +{ + Q931L2HeaderSpace = space; +} + +/***************************************************************************** + + Function: Q931ProcDummy + + Description: Dummy function for message processing. + +*****************************************************************************/ +L3INT Q931ProcDummy(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b,L3INT c) +{ + return Q931E_INTERNAL; +} + +/***************************************************************************** + + Function: Q931UmesDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931UmesDummy(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size) +{ + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: Q931UieDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931UieDummy(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + return Q931E_UNKNOWN_IE; +} + +/***************************************************************************** + + Function: Q931PmesDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931PmesDummy(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + return Q931E_UNKNOWN_MESSAGE; +} + +/***************************************************************************** + + Function: Q931PieDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931PieDummy(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + return Q931E_UNKNOWN_IE; +} + +/***************************************************************************** + + Function: Q931TxDummy + + Description: Dummy function for message processing + +*****************************************************************************/ +L3INT Q931TxDummy(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT n) +{ + return Q931E_MISSING_CB; +} + +/***************************************************************************** + + Function: Q931ErrorDummy + + Description: Dummy function for error processing + +*****************************************************************************/ +L3INT Q931ErrorDummy(void *priv, L3INT a, L3INT b, L3INT c) +{ + return 0; +} + +/***************************************************************************** + + Function: Q931Initialize + + Description: This function Initialize the stack. + + Will set up the trunk array, channel + arrays and initialize Q931 function arrays before it finally + set up EuroISDN processing with User as dialect 0 and + Network as dialect 1. + + Note: Initialization of other stacks should be inserted after + the initialization of EuroISDN. + +*****************************************************************************/ +void Q931Initialize() +{ + L3INT x,y; + + /* Secure the callbacks to default procs */ + Q931ErrorProc = Q931ErrorDummy; + + /* The user will only add the message handlers and IE handlers he need, + * so we need to initialize every single entry to a default function + * that will throw an appropriate error if they are ever called. + */ + for (x = 0; x < Q931MAXDLCT; x++) { + for (y = 0; y < Q931MAXMES; y++) { + Q931Proc[x][y] = Q931ProcDummy; + Q931Umes[x][y] = Q931UmesDummy; + Q931Pmes[x][y] = Q931PmesDummy; + } + for (y = 0; y < Q931MAXIE; y++) { + Q931Pie[x][y] = Q931PieDummy; + Q931Uie[x][y] = Q931UieDummy; + } + for (y = 0; y < Q931MAXTIMER; y++) { + Q931Timeout[x][y] = Q931TimeoutDummy; + Q931Timer[x][y] = 0; + } + } + + if (Q931CreateDialectCB[Q931_Dialect_Q931 + Q931_TE] == NULL) + Q931AddDialect(Q931_Dialect_Q931 + Q931_TE, Q931CreateTE); + + if (Q931CreateDialectCB[Q931_Dialect_Q931 + Q931_NT] == NULL) + Q931AddDialect(Q931_Dialect_Q931 + Q931_NT, Q931CreateNT); + + if (Q931CreateDialectCB[Q931_Dialect_National + Q931_TE] == NULL) + Q931AddDialect(Q931_Dialect_National + Q931_TE, nationalCreateTE); + + if (Q931CreateDialectCB[Q931_Dialect_National + Q931_NT] == NULL) + Q931AddDialect(Q931_Dialect_National + Q931_NT, nationalCreateNT); + + if (Q931CreateDialectCB[Q931_Dialect_DMS + Q931_TE] == NULL) + Q931AddDialect(Q931_Dialect_DMS + Q931_TE, DMSCreateTE); + + if (Q931CreateDialectCB[Q931_Dialect_DMS + Q931_NT] == NULL) + Q931AddDialect(Q931_Dialect_DMS + Q931_NT, DMSCreateNT); + + if (Q931CreateDialectCB[Q931_Dialect_5ESS + Q931_TE] == NULL) + Q931AddDialect(Q931_Dialect_5ESS + Q931_TE, ATT5ESSCreateTE); + + if (Q931CreateDialectCB[Q931_Dialect_5ESS + Q931_NT] == NULL) + Q931AddDialect(Q931_Dialect_5ESS + Q931_NT, ATT5ESSCreateNT); + + /* The last step we do is to call the callbacks to create the dialects */ + for (x = 0; x < Q931MAXDLCT; x++) { + if (Q931CreateDialectCB[x] != NULL) { + Q931CreateDialectCB[x]((L3UCHAR)x); + } + } +} + +/** + * Q931TimerTick + * \brief Periodically called to update and check for expired timers + * \param pTrunk Q.931 trunk + */ +void Q931TimerTick(Q931_TrunkInfo_t *pTrunk) +{ + struct Q931_Call *call = NULL; + L3ULONG now = 0; + L3INT x; + + /* TODO: Loop through all active calls, check timers and call timout procs + * if timers are expired. + * Implement a function array so each dialect can deal with their own + * timeouts. + */ + now = Q931GetTime(); + + for (x = 0; x < Q931MAXCALLPERTRUNK; x++) { + call = &pTrunk->call[x]; + + if (!call->InUse || !call->Timer || !call->TimerID) + continue; + + if (call->Timer <= now) { + /* Stop Timer */ + Q931StopTimer(pTrunk, x, call->TimerID); + + /* Invoke dialect timeout callback */ + Q931Timeout[pTrunk->Dialect][call->TimerID](pTrunk, x); + } + } +} + +/***************************************************************************** + + Function: Q931Rx23 + + Description: Receive message from layer 2 (LAPD). Receiving a message + is always done in 2 steps. First the message must be + interpreted and translated to a static struct. Secondly + the message is processed and responded to. + + The Q.931 message contains a static header that is + interpreted in this function. The rest is interpreted + in a sub function according to mestype. + + Parameters: pTrunk [IN] Ptr to trunk info. + buf [IN] Ptr to buffer containing message. + Size [IN] Size of message. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931Rx23(Q931_TrunkInfo_t *pTrunk, L3INT ind, L3UCHAR tei, L3UCHAR * buf, L3INT Size) +{ + L3UCHAR *Mes = NULL; + L3INT RetCode = Q931E_NO_ERROR; + Q931mes_Generic *m = (Q931mes_Generic *) pTrunk->L3Buf; + L3INT ISize; + L3INT IOff = 0; + L3INT L2HSize = Q931L2HeaderSpace; + + switch (ind) { + case Q921_DL_UNIT_DATA: /* DL-UNITDATA indication (UI frame, 3 byte header) */ + L2HSize = 3; + + case Q921_DL_DATA: /* DL-DATA indication (I frame, 4 byte header) */ + /* Reset our decode buffer */ + memset(pTrunk->L3Buf, 0, sizeof(pTrunk->L3Buf)); + + /* L2 Header Offset */ + Mes = &buf[L2HSize]; + + /* Protocol Discriminator */ + m->ProtDisc = Mes[IOff++]; + + /* CRV */ + m->CRVFlag = (Mes[IOff + 1] >> 7) & 0x01; + m->CRV = Q931Uie_CRV(pTrunk, Mes, m->buf, &IOff, &ISize); + + /* Message Type */ + m->MesType = Mes[IOff++]; + + /* Store tei */ + m->Tei = tei; + + /* d'oh a little ugly but this saves us from: + * a) doing Q.921 work in the lower levels (extracting the TEI ourselves) + * b) adding a tei parameter to _all_ Proc functions + */ + if (tei) { + L3INT callIndex = 0; + + /* Find the call using CRV */ + RetCode = Q931FindCRV(pTrunk, m->CRV, &callIndex); + if (RetCode == Q931E_NO_ERROR && !pTrunk->call[callIndex].Tei) { + pTrunk->call[callIndex].Tei = tei; + } + } + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Received message from Q.921 (ind %d, tei %d, size %d)\nMesType: %d, CRVFlag %d (%s), CRV %d (Dialect: %d)\n", ind, m->Tei, Size, + m->MesType, m->CRVFlag, m->CRVFlag ? "Terminator" : "Originator", m->CRV, pTrunk->Dialect); + + RetCode = Q931Umes[pTrunk->Dialect][m->MesType](pTrunk, Mes, (Q931mes_Generic *)pTrunk->L3Buf, IOff, Size - L2HSize); + if (RetCode >= Q931E_NO_ERROR) { + RetCode = Q931Proc[pTrunk->Dialect][m->MesType](pTrunk, pTrunk->L3Buf, 2); + } + break; + + default: + break; + } + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Tx34 + + Description: Called from the stack to send a message to layer 4. + + Parameters: Mes[IN] Ptr to message buffer. + Size[IN] Message size in bytes. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931Tx34(Q931_TrunkInfo_t *pTrunk, L3UCHAR * Mes, L3INT Size) +{ + Q931Log(pTrunk, Q931_LOG_DEBUG, "Sending message to Layer4 (size: %d)\n", Size); + + if (pTrunk->Q931Tx34CBProc) { + return pTrunk->Q931Tx34CBProc(pTrunk->PrivateData34, Mes, Size); + } + return Q931E_MISSING_CB; +} + +/***************************************************************************** + + Function: Q931Rx43 + + Description: Receive message from Layer 4 (application). + + Parameters: pTrunk[IN] Trunk #. + buf[IN] Message Pointer. + Size[IN] Message size in bytes. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931Rx43(Q931_TrunkInfo_t *pTrunk,L3UCHAR * buf, L3INT Size) +{ + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + L3INT RetCode = Q931E_NO_ERROR; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Receiving message from Layer4 (size: %d, type: %d)\n", Size, ptr->MesType); + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Q931Rx43 return code: %d\n", RetCode); + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Tx32 + + Description: Called from the stack to send a message to L2. The input is + always a non-packed message so it will first make a proper + call to create a packed message before it transmits that + message to layer 2. + + Parameters: pTrunk[IN] Trunk # + buf[IN] Ptr to message buffer. + Size[IN] Message size in bytes. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931Tx32Data(Q931_TrunkInfo_t *pTrunk, L3UCHAR bcast, L3UCHAR * Mes, L3INT Size) +{ + Q931mes_Generic *ptr = (Q931mes_Generic*)Mes; + L3INT RetCode = Q931E_NO_ERROR; + L3INT iDialect = pTrunk->Dialect; + L3INT Offset = bcast ? (Q931L2HeaderSpace - 1) : Q931L2HeaderSpace; + L3INT OSize; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Sending message to Q.921 (size: %d)\n", Size); + + memset(pTrunk->L2Buf, 0, sizeof(pTrunk->L2Buf)); + + /* Call pack function through table. */ + RetCode = Q931Pmes[iDialect][ptr->MesType](pTrunk, (Q931mes_Generic *)Mes, Size, &pTrunk->L2Buf[Offset], &OSize); + if (RetCode >= Q931E_NO_ERROR) { + L3INT callIndex; + L3UCHAR tei = 0; + + if (ptr->CRV) { + /* Find the call using CRV */ + RetCode = Q931FindCRV(pTrunk, ptr->CRV, &callIndex); + if (RetCode != Q931E_NO_ERROR) + return RetCode; + + tei = pTrunk->call[callIndex].Tei; + } + + if (pTrunk->Q931Tx32CBProc) { + RetCode = pTrunk->Q931Tx32CBProc(pTrunk->PrivateData32, bcast ? Q921_DL_UNIT_DATA : Q921_DL_DATA, tei, pTrunk->L2Buf, OSize + Offset); + } else { + RetCode = Q931E_MISSING_CB; + } + } + + return RetCode; +} + + +/***************************************************************************** + + Function: Q931SetError + + Description: Called from the stack to indicate an error. + + Parameters: ErrID ID of ie or message causing error. + ErrPar1 Error parameter 1 + ErrPar2 Error parameter 2. + + +*****************************************************************************/ +void Q931SetError(Q931_TrunkInfo_t *pTrunk,L3INT ErrID, L3INT ErrPar1, L3INT ErrPar2) +{ + if (pTrunk->Q931ErrorCBProc) { + pTrunk->Q931ErrorCBProc(pTrunk->PrivateData34, ErrID, ErrPar1, ErrPar2); + } else { + Q931ErrorProc(pTrunk->PrivateData34, ErrID, ErrPar1, ErrPar2); + } +} + +void Q931SetDefaultErrorCB(Q931ErrorCB_t Q931ErrorPar) +{ + Q931ErrorProc = Q931ErrorPar; +} + +/***************************************************************************** + + Function: Q931CreateCRV + + Description: Create a CRV entry and return it's index. The function will + locate a free entry in the call tables allocate it and + allocate a unique CRV value attached to it. + + Parameters: pTrunk [IN] Trunk number + callindex [OUT] return call table index. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. +****************************************************************************/ +L3INT Q931CreateCRV(Q931_TrunkInfo_t *pTrunk, L3INT * callIndex) +{ + L3INT CRV = Q931GetUniqueCRV(pTrunk); + + return Q931AllocateCRV(pTrunk, CRV, callIndex); +} + + +L3INT Q931ReleaseCRV(Q931_TrunkInfo_t *pTrunk, L3INT CRV) +{ + int callIndex; + + if ((Q931FindCRV(pTrunk, CRV, &callIndex)) == Q931E_NO_ERROR) { + pTrunk->call[callIndex].InUse = 0; + return Q931E_NO_ERROR; + } + + return Q931E_INVALID_CRV; +} + +/***************************************************************************** + + Function: Q931AllocateCRV + + Description: Allocate a call table entry and assigns the given CRV value + to it. + + Parameters: pTrunk [IN] Trunk number + iCRV [IN] Call Reference Value. + callindex [OUT] return call table index. + + Return Value: Error Code. 0 = No Error, < 0 :error, > 0 : Warning + see q931errors.h for details. + +*****************************************************************************/ +L3INT Q931AllocateCRV(Q931_TrunkInfo_t *pTrunk, L3INT iCRV, L3INT * callIndex) +{ + L3INT x; + for (x = 0; x < Q931MAXCALLPERTRUNK; x++) { + if (!pTrunk->call[x].InUse) { + pTrunk->call[x].CRV = iCRV; + pTrunk->call[x].BChan = 255; + pTrunk->call[x].State = 0; /* null state - idle */ + pTrunk->call[x].TimerID = 0; /* no timer running */ + pTrunk->call[x].Timer = 0; + pTrunk->call[x].InUse = 1; /* mark as used */ + *callIndex = x; + return Q931E_NO_ERROR; + } + } + return Q931E_TOMANYCALLS; +} + +/***************************************************************************** + + Function: Q931GetCallState + + Description: Look up CRV and return current call state. A non existing + CRV is the same as state zero (0). + + Parameters: pTrunk [IN] Trunk number. + iCRV [IN] CRV + + Return Value: Call State. + +*****************************************************************************/ +L3INT Q931GetCallState(Q931_TrunkInfo_t *pTrunk, L3INT iCRV) +{ + L3INT x; + for (x = 0; x < Q931MAXCALLPERTRUNK; x++) { + if (pTrunk->call[x].InUse) { + if (pTrunk->call[x].CRV == iCRV) { + return pTrunk->call[x].State; + } + } + } + return 0; /* assume state zero for non existing CRV's */ +} + +/** + * Q931StartTimer + * \brief Start a timer + * \param pTrunk Q.931 trunk + * \param callindex Index of the call + * \param iTimerID ID of timer + * \return always 0 + */ +L3INT Q931StartTimer(Q931_TrunkInfo_t *pTrunk, L3INT callIndex, L3USHORT iTimerID) +{ +#if 0 + L3ULONG duration = Q931Timer[pTrunk->Dialect][iTimerID]; + + if (duration) { + pTrunk->call[callIndex].Timer = Q931GetTime() + duration; + pTrunk->call[callIndex].TimerID = iTimerID; + } +#endif + return 0; +} + +/** + * Q931StopTimer + * \brief Stop a timer + * \param pTrunk Q.931 trunk + * \param callindex Index of the call + * \param iTimerID ID of timer + * \return always 0 + */ +L3INT Q931StopTimer(Q931_TrunkInfo_t *pTrunk, L3INT callindex, L3USHORT iTimerID) +{ + if (pTrunk->call[callindex].TimerID == iTimerID) + pTrunk->call[callindex].TimerID = 0; + + return 0; +} + +L3INT Q931SetState(Q931_TrunkInfo_t *pTrunk, L3INT callIndex, L3INT iState) +{ + pTrunk->call[callIndex].State = iState; + + return 0; +} + +L3ULONG Q931GetTime() +{ + L3ULONG tNow = 0; + static L3ULONG tLast = 0; + + if (Q931GetTimeProc != NULL) { + tNow = Q931GetTimeProc(); + if (tNow < tLast) { /* wrapped */ + /* TODO */ + } + tLast = tNow; + } + return tNow; +} + +void Q931SetGetTimeCB(L3ULONG (*callback)(void)) +{ + Q931GetTimeProc = callback; +} + +L3INT Q931FindCRV(Q931_TrunkInfo_t *pTrunk, L3INT crv, L3INT *callindex) +{ + L3INT x; + for (x = 0; x < Q931MAXCALLPERTRUNK; x++) { + if (pTrunk->call[x].InUse) { + if (pTrunk->call[x].CRV == crv) { + *callindex = x; + return Q931E_NO_ERROR; + } + } + } + return Q931E_INVALID_CRV; +} + + +void Q931AddDialect(L3UCHAR i, void (*callback)(L3UCHAR iD )) +{ + if (i < Q931MAXDLCT) { + Q931CreateDialectCB[i] = callback; + } +} + +/***************************************************************************** + Function: Q931AddStateEntry + + Description: Find an empty entry in the dialects state table and add this + entry. +*****************************************************************************/ +void Q931AddStateEntry(L3UCHAR iD, L3INT iState, L3INT iMes, L3UCHAR cDir) +{ + int x; + for (x = 0; x < Q931MAXSTATE; x++) { + if (Q931st[x].Message == 0) { + Q931st[x].State = iState; + Q931st[x].Message = iMes; + Q931st[x].Direction = cDir; + /* TODO Sort table and use bsearch */ + return; + } + } +} + +/***************************************************************************** + Function: Q931IsEventLegal + + Description: Check state table for matching criteria to indicate if this + Message is legal in this state or not. + + Note: Someone write a bsearch or invent something smart here + please - sequential is ok for now. +*****************************************************************************/ +L3BOOL Q931IsEventLegal(L3UCHAR iD, L3INT iState, L3INT iMes, L3UCHAR cDir) +{ + int x; + /* TODO Sort table and use bsearch */ + for (x = 0; x < Q931MAXSTATE; x++) { + if (Q931st[x].State == iState && Q931st[x].Message == iMes && + Q931st[x].Direction == cDir) { + return L3TRUE; + } + } + return L3FALSE; +} + +/***************************************************************************** + Function: q931_error_to_name() + + Description: Check state table for matching criteria to indicate if this + Message is legal in this state or not. + + Note: Someone write a bsearch or invent something smart here + please - sequential is ok for now. +*****************************************************************************/ +static const char *q931_error_names[] = { + "Q931E_NO_ERROR", /* 0 */ + + "Q931E_UNKNOWN_MESSAGE", /* -3001 */ + "Q931E_ILLEGAL_IE", /* -3002 */ + "Q931E_UNKNOWN_IE", /* -3003 */ + "Q931E_BEARERCAP", /* -3004 */ + "Q931E_HLCOMP", /* -3005 */ + "Q931E_LLCOMP", /* -3006 */ + "Q931E_INTERNAL", /* -3007 */ + "Q931E_MISSING_CB", /* -3008 */ + "Q931E_UNEXPECTED_MESSAGE", /* -3009 */ + "Q931E_ILLEGAL_MESSAGE", /* -3010 */ + "Q931E_TOMANYCALLS", /* -3011 */ + "Q931E_INVALID_CRV", /* -3012 */ + "Q931E_CALLID", /* -3013 */ + "Q931E_CALLSTATE", /* -3014 */ + "Q931E_CALLEDSUB", /* -3015 */ + "Q931E_CALLEDNUM", /* -3016 */ + "Q931E_CALLINGNUM", /* -3017 */ + "Q931E_CALLINGSUB", /* -3018 */ + "Q931E_CAUSE", /* -3019 */ + "Q931E_CHANID", /* -3020 */ + "Q931E_DATETIME", /* -3021 */ + "Q931E_DISPLAY", /* -3022 */ + "Q931E_KEYPADFAC", /* -3023 */ + "Q931E_NETFAC", /* -3024 */ + "Q931E_NOTIFIND", /* -3025 */ + "Q931E_PROGIND", /* -3026 */ + "Q931E_RESTARTIND", /* -3027 */ + "Q931E_SEGMENT", /* -3028 */ + "Q931E_SIGNAL", /* -3029 */ + "Q931E_GENERIC_DIGITS" /* -3030 */ + +}; + +#define Q931_MAX_ERROR 30 + +const char *q931_error_to_name(q931_error_t error) +{ + int index = 0; + if ((int)error < 0) { + index = (((int)error * -1) -3000); + } + if (index < 0 || index > Q931_MAX_ERROR) { + return ""; + } + return q931_error_names[index]; +} +/* + * Logging + */ +#include + +L3INT Q931Log(Q931_TrunkInfo_t *trunk, Q931LogLevel_t level, const char *fmt, ...) +{ + char buf[Q931_LOGBUFSIZE]; + L3INT len; + va_list ap; + + if (!trunk->Q931LogCBProc) + return 0; + + if (trunk->loglevel < level) + return 0; + + va_start(ap, fmt); + + len = vsnprintf(buf, sizeof(buf)-1, fmt, ap); + if (len <= 0) { + /* TODO: error handling */ + return -1; + } + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + + buf[len] = '\0'; + + va_end(ap); + + return trunk->Q931LogCBProc(trunk->PrivateDataLog, level, buf, len); +} + +/** + * Q921SetLogCB + * \brief Set Logging callback function and private data + */ +void Q931SetLogCB(Q931_TrunkInfo_t *trunk, Q931LogCB_t func, void *priv) +{ + trunk->Q931LogCBProc = func; + trunk->PrivateDataLog = priv; +} + +/** + * Q921SetLogLevel + * \brief Set Loglevel + */ +void Q931SetLogLevel(Q931_TrunkInfo_t *trunk, Q931LogLevel_t level) +{ + if(!trunk) + return; + + if (level < Q931_LOG_NONE) { + level = Q931_LOG_NONE; + } else if (level > Q931_LOG_DEBUG) { + level = Q931_LOG_DEBUG; + } + + trunk->loglevel = level; +} + +/** + * Q931TimeoutDummy + * \brief Dummy handler for timeouts + * \param pTrunk Q.931 trunk + * \param callIndex Index of call + */ +L3INT Q931TimeoutDummy(Q931_TrunkInfo_t *pTrunk, L3INT callIndex) +{ + Q931Log(pTrunk, Q931_LOG_DEBUG, "Timer %d of call %d (CRV: %d) timed out\n", pTrunk->call[callIndex].TimerID, callIndex, pTrunk->call[callIndex].CRV); + + return 0; +} diff --git a/libs/openzap/src/isdn/Q931StateNT.c b/libs/openzap/src/isdn/Q931StateNT.c new file mode 100644 index 0000000000..8d05fb6d03 --- /dev/null +++ b/libs/openzap/src/isdn/Q931StateNT.c @@ -0,0 +1,1218 @@ +/***************************************************************************** + + FileName: q931StateNT.c + + Contents: Q.931 State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: Q931CreateNT + + Description: Will create the Q931 NT as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void Q931CreateNT(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingNT, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingNT, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectNT, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckNT, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressNT, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupNT, Q931Umes_Setup, Q931Pmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckNT, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeNT, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckNT, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectNT, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendNT, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckNT, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectNT, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationNT, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectNT, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseNT, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteNT, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartNT, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckNT, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlNT, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationNT, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyNT, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusNT, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryNT, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentNT, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityNT, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldNT, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckNT, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectNT, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterNT, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveNT, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckNT, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectNT, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_REDIRECTING_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message + * procs can when search this to find out if the message/state + * combination is legale. If not, the proc for unexpected message apply. + */ + + /* TODO define state table here */ + + /* Timer default values */ + Q931SetTimerDefault(i, Q931_TIMER_T301, 180000); /* T301: 180s */ + Q931SetTimerDefault(i, Q931_TIMER_T302, 15000); /* T302: 15s */ + Q931SetTimerDefault(i, Q931_TIMER_T303, 4000); /* T303: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T304, 20000); /* T304: 20s */ + Q931SetTimerDefault(i, Q931_TIMER_T305, 30000); /* T305: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T306, 30000); /* T306: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T307, 180000); /* T307: 180s */ + Q931SetTimerDefault(i, Q931_TIMER_T308, 4000); /* T308: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T309, 60000); /* T309: 60s */ + Q931SetTimerDefault(i, Q931_TIMER_T310, 10000); /* T310: 10s */ + Q931SetTimerDefault(i, Q931_TIMER_T312, 12000); /* T312: 12s */ + Q931SetTimerDefault(i, Q931_TIMER_T314, 4000); /* T314: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T316, 120000); /* T316: 120s */ + Q931SetTimerDefault(i, Q931_TIMER_T317, 90000); /* T317: 90s */ + Q931SetTimerDefault(i, Q931_TIMER_T320, 30000); /* T320: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T321, 30000); /* T321: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T322, 4000); /* T322: 4s */ +} + +/***************************************************************************** + + Function: Q931ProcAlertingNT + +*****************************************************************************/ +L3INT Q931ProcAlertingNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* Reset 4 sec timer. */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcCallProceedingNT + +*****************************************************************************/ +L3INT Q931ProcCallProceedingNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcConnectNT + +*****************************************************************************/ +L3INT Q931ProcConnectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcConnectAckNT + +*****************************************************************************/ +L3INT Q931ProcConnectAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcProgressNT + +*****************************************************************************/ +L3INT Q931ProcProgressNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSetupNT + + Description: Process a SETUP message. + + *****************************************************************************/ +L3INT Q931ProcSetupNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT rc = 0; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Reject SETUP on existing calls */ + if (Q931GetCallState(pTrunk, pMes->CRV) != Q931_U0) { + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 81); + return Q931E_UNEXPECTED_MESSAGE; + } + + /* outgoing call */ + if (iFrom == 4) { + ret = Q931CreateCRV(pTrunk, &callIndex); + if (ret) + return ret; + + pMes->CRV = pTrunk->call[callIndex].CRV; + + /* + * Outgoing SETUP message will be broadcasted in PTMP mode + */ + ret = Q931Tx32Data(pTrunk, Q931_IS_PTP(pTrunk) ? 0 : 1, buf, pMes->Size); + if (ret) + return ret; + + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + Q931SetState(pTrunk, callIndex, Q931_U1); + } + /* incoming call */ + else { + /* Locate free CRV entry and store info */ + ret = Q931AllocateCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) { + /* Not possible to allocate CRV entry, so must reject call */ + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 42); + return ret; + } + + /* store TEI in call */ + pTrunk->call[callIndex].Tei = pMes->Tei; + + /* Send setup indication to user */ + ret = Q931Tx34(pTrunk, (L3UCHAR*)pMes, pMes->Size); + if (ret != Q931E_NO_ERROR) { + return ret; + } else { + /* Must be full queue, meaning we can't process the call */ + /* so we must disconnect */ + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 81); + return ret; + } +#if 0 + /* TODO: Unreachable code??? */ + /* Set state U6 */ + Q931SetState(pTrunk, callIndex, Q931_U6); + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); +#endif + } + + return rc; +} + +/***************************************************************************** + + Function: Q931ProcSetupAckNT + + Description: Used to acknowedge a SETUP. Usually the first initial + response recevide back used to buy some time. + + Note that ChanID (B Channel Assignment) might come here from + NT side. + +*****************************************************************************/ +L3INT Q931ProcSetupAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeNT + +*****************************************************************************/ +L3INT Q931ProcResumeNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeAckNT + +*****************************************************************************/ +L3INT Q931ProcResumeAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeRejectNT + +*****************************************************************************/ +L3INT Q931ProcResumeRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendNT + +*****************************************************************************/ +L3INT Q931ProcSuspendNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendAckNT + +*****************************************************************************/ +L3INT Q931ProcSuspendAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendRejectNT + +*****************************************************************************/ +L3INT Q931ProcSuspendRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcInformationNT + +*****************************************************************************/ +L3INT Q931ProcUserInformationNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcDisconnectNT + +*****************************************************************************/ +L3INT Q931ProcDisconnectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcReleaseNT + +*****************************************************************************/ +L3INT Q931ProcReleaseNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcReleaseCompleteNT + +*****************************************************************************/ +L3INT Q931ProcReleaseCompleteNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRestartNT + +*****************************************************************************/ +L3INT Q931ProcRestartNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRestartAckNT + +*****************************************************************************/ +L3INT Q931ProcRestartAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcCongestionControlNT + +*****************************************************************************/ +L3INT Q931ProcCongestionControlNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcInformationNT + +*****************************************************************************/ +L3INT Q931ProcInformationNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcNotifyNT + +*****************************************************************************/ +L3INT Q931ProcNotifyNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcStatusNT + +*****************************************************************************/ +L3INT Q931ProcStatusNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcStatusEnquiryNT + +*****************************************************************************/ +L3INT Q931ProcStatusEnquiryNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSegmentNT + +*****************************************************************************/ +L3INT Q931ProcSegmentNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/****************************************************************************/ +/******************* Q.932 - Supplementary Services *************************/ +/****************************************************************************/ + +/***************************************************************************** + + Function: Q932ProcFacilityNT + +*****************************************************************************/ +L3INT Q932ProcFacilityNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcHoldNT + +*****************************************************************************/ +L3INT Q932ProcHoldNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcHoldAckNT + +*****************************************************************************/ +L3INT Q932ProcHoldAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcHoldRejectNT + +*****************************************************************************/ +L3INT Q932ProcHoldRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRegisterTE + +*****************************************************************************/ +L3INT Q932ProcRegisterNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveNT + +*****************************************************************************/ +L3INT Q932ProcRetrieveNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRetrieveAckNT + +*****************************************************************************/ +L3INT Q932ProcRetrieveAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRetrieveRejectNT + +*****************************************************************************/ +L3INT Q932ProcRetrieveRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here*/ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} diff --git a/libs/openzap/src/isdn/Q931StateTE.c b/libs/openzap/src/isdn/Q931StateTE.c new file mode 100644 index 0000000000..562dd0b458 --- /dev/null +++ b/libs/openzap/src/isdn/Q931StateTE.c @@ -0,0 +1,1310 @@ +/***************************************************************************** + + FileName: q931StateTE.c + + Contents: Q.931 State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + This reference implementation uses a process per message, + meaning that each message must check call states. This + is easier for dialect maintenance as each message proc + can be replaced individually. A new TE variant only + need to copy the Q931CreateTE and replace those procs or + need to override. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: Q931CreateTE + + Description: Will create the Q931 TE as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void Q931CreateTE(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingTE, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingTE, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectTE, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckTE, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressTE, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupTE, Q931Umes_Setup, Q931Pmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckTE, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeTE, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckTE, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectTE, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendTE, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckTE, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectTE, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationTE, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectTE, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseTE, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteTE, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartTE, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckTE, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlTE, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationTE, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyTE, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusTE, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryTE, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentTE, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityTE, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldTE, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckTE, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectTE, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterTE, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveTE, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckTE, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectTE, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_REDIRECTING_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message + * procs can when search this to find out if the message/state + * combination is legale. If not, the proc for unexpected message apply. + */ + + /* State 0 Idle */ + Q931AddStateEntry(i, Q931_U0, Q931mes_RESUME, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_STATUS, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE_COMPLETE, 4); + + /* State 1 Call Initiating */ + Q931AddStateEntry(i, Q931_U1, Q931mes_DISCONNECT, 2); + Q931AddStateEntry(i, Q931_U1, Q931mes_SETUP_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_RELEASE_COMPLETE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CONNECT, 4); + + /* State 2 Overlap Sending */ + Q931AddStateEntry(i, Q931_U2, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U2, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_RELEASE, 2); + + /* State 3 Outgoing Call Proceeding */ + Q931AddStateEntry(i, Q931_U3, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_RELEASE, 2); + + /* State 4 Call Delivered */ + Q931AddStateEntry(i, Q931_U4, Q931mes_CONNECT, 4); + + /* State 6 Call Precent */ + Q931AddStateEntry(i, Q931_U6, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CALL_PROCEEDING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE_COMPLETE, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U6, Q931mes_DISCONNECT, 4); + + /* State 7 Call Received */ + Q931AddStateEntry(i, Q931_U7, Q931mes_CONNECT, 2); + + /* State 8 Connect request */ + Q931AddStateEntry(i, Q931_U8, Q931mes_CONNECT_ACKNOWLEDGE, 4); + + /* State 9 Incoming Call Proceeding */ + Q931AddStateEntry(i, Q931_U9, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_PROGRESS, 2); + + /* State 10 Active */ + Q931AddStateEntry(i, Q931_U10, Q931mes_SUSPEND, 2); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 4); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 2); + + /* State 11 Disconnect Request */ + Q931AddStateEntry(i, Q931_U11, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_NOTIFY, 4); + + /* State 12 Disconnect Ind */ + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 2); + + /* State 15 Suspend Request */ + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_REJECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_RELEASE, 4); + +/* TODO + Q931AddStateEntry(i, Q931_U17, + Q931AddStateEntry(i, Q931_U19, + Q931AddStateEntry(i, Q931_U25, +*/ + + /* Timer default values */ + Q931SetTimerDefault(i, Q931_TIMER_T301, 180000); /* T301: 180s */ + Q931SetTimerDefault(i, Q931_TIMER_T302, 15000); /* T302: 15s */ + Q931SetTimerDefault(i, Q931_TIMER_T303, 4000); /* T303: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T304, 30000); /* T304: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T305, 30000); /* T305: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T308, 4000); /* T308: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T309, 60000); /* T309: 60s */ + Q931SetTimerDefault(i, Q931_TIMER_T310, 60000); /* T310: 60s */ + Q931SetTimerDefault(i, Q931_TIMER_T313, 4000); /* T313: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T314, 4000); /* T314: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T316, 120000); /* T316: 120s */ + Q931SetTimerDefault(i, Q931_TIMER_T317, 90000); /* T317: 90s */ + Q931SetTimerDefault(i, Q931_TIMER_T318, 4000); /* T318: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T319, 4000); /* T319: 4s */ + Q931SetTimerDefault(i, Q931_TIMER_T321, 30000); /* T321: 30s */ + Q931SetTimerDefault(i, Q931_TIMER_T322, 4000); /* T322: 4s */ +} + +/***************************************************************************** + + Function: Q931ProcAlertingTE + +*****************************************************************************/ +L3INT Q931ProcAlertingTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* Reset 4 sec timer. */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcCallProceedingTE + +*****************************************************************************/ +L3INT Q931ProcCallProceedingTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcConnectTE + +*****************************************************************************/ +L3INT Q931ProcConnectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + if (pTrunk->autoConnectAck) { + Q931AckConnect(pTrunk, buf); + } + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcConnectAckTE + +*****************************************************************************/ +L3INT Q931ProcConnectAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcProgressTE + +*****************************************************************************/ +L3INT Q931ProcProgressTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSetupTE + +*****************************************************************************/ +L3INT Q931ProcSetupTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT rc = 0; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Reject SETUP on existing calls */ + if (Q931GetCallState(pTrunk, pMes->CRV) != Q931_U0) { + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 81); + return Q931E_UNEXPECTED_MESSAGE; + } + + /* outgoing call */ + if (iFrom == 4) { + ret = Q931CreateCRV(pTrunk, &callIndex); + if (ret) + return ret; + pMes->CRV = pTrunk->call[callIndex].CRV; + + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + if (ret) + return ret; + + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + + /* TODO: Add this back when we get the state stuff more filled out */ + /*Q931SetState(pTrunk, callIndex, Q931_U1);*/ + } + /* incoming call */ + else { + /* Locate free CRV entry and store info */ + ret = Q931AllocateCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) { + /* Not possible to allocate CRV entry, so must reject call */ + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 42); + return ret; + } + + /* Send setup indication to user */ + ret = Q931Tx34(pTrunk, (L3UCHAR*)pMes, pMes->Size); + if (ret != Q931E_NO_ERROR) { + if (pTrunk->autoSetupAck) { + Q931AckSetup(pTrunk, buf); + } + return ret; + } else { + /* Must be full queue, meaning we can't process the call */ + /* so we must disconnect */ + Q931Disconnect(pTrunk, iFrom, pMes->CRV, 81); + return ret; + } +#if 0 + /* TODO: Unreachable code??? */ + /* Set state U6 */ + Q931SetState(pTrunk, callIndex, Q931_U6); + + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); +#endif + } + return rc; +} + +/***************************************************************************** + + Function: Q931ProcSetupAckTE + + Description: Used to acknowedge a SETUP. Usually the first initial + response recevide back used to buy some time. L4 sending this + should only be passed on. L2 sending this means that we set + a new timer (and pass it to L4). + + Note that ChanID (B Channel Assignment) might come here from + NT side. + +*****************************************************************************/ +L3INT Q931ProcSetupAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeTE + +*****************************************************************************/ +L3INT Q931ProcResumeTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic * pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (Q931GetCallState(pTrunk, pMes->CRV) == Q931_U0 && iFrom ==4) { + /* Call reference selection */ + ret = Q931CreateCRV(pTrunk, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + pMes->CRV = pTrunk->call[callIndex].CRV; + + /* Send RESUME to network */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + if (ret != Q931E_NO_ERROR) + return ret; + + /* Start timer T318 */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T318); + + /* set state U17 */ + Q931SetState(pTrunk, callIndex, Q931_U17); + } else { + return Q931E_ILLEGAL_MESSAGE; + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeAckTE + +*****************************************************************************/ +L3INT Q931ProcResumeAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcResumeRejectTE + +*****************************************************************************/ +L3INT Q931ProcResumeRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendTE + +*****************************************************************************/ +L3INT Q931ProcSuspendTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendAckTE + +*****************************************************************************/ +L3INT Q931ProcSuspendAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSuspendRejectTE + +*****************************************************************************/ +L3INT Q931ProcSuspendRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcInformationTE + +*****************************************************************************/ +L3INT Q931ProcUserInformationTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcDisconnectTE + +*****************************************************************************/ +L3INT Q931ProcDisconnectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Processing DISCONNECT message from %s for CRV: %d (%#hx)\n", + iFrom == 4 ? "Local" : "Remote", pMes->CRV, pMes->CRV); + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcReleaseTE + +*****************************************************************************/ +L3INT Q931ProcReleaseTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT state = Q931GetCallState(pTrunk, pMes->CRV); + L3INT ret = Q931E_NO_ERROR; + + if (iFrom == 4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (state == Q931_U0 && iFrom == 2) { + Q931Tx34(pTrunk, buf, pMes->Size); + ret = Q931ReleaseComplete(pTrunk, buf); + } else { + ret = Q931ProcUnexpectedMessage(pTrunk, buf, iFrom); + } + if (pMes->CRV && iFrom == 2) { + /* Find the call using CRV */ + if ((Q931FindCRV(pTrunk, pMes->CRV, &callIndex)) != Q931E_NO_ERROR) + return ret; + pTrunk->call[callIndex].InUse = 0; + } + + return ret; +} + +/***************************************************************************** + + Function: Q931ProcReleaseCompleteTE + +*****************************************************************************/ +L3INT Q931ProcReleaseCompleteTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } else { + if (pMes->CRV) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + pTrunk->call[callIndex].InUse = 0; + + /* TODO: experimental, send RELEASE_COMPLETE message */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRestartTE + +*****************************************************************************/ +L3INT Q931ProcRestartTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->CRV) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + + /* TODO chack against state table for illegal or unexpected message here */ + + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + + if (pTrunk->autoRestartAck) { + Q931AckRestart(pTrunk, buf); + } + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRestartAckTE + +*****************************************************************************/ +L3INT Q931ProcRestartAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + if (pMes->CRV) { + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + } + + /* TODO chack against state table for illegal or unexpected message here */ + + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcCongestionControlTE + +*****************************************************************************/ +L3INT Q931ProcCongestionControlTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcInformationTE + +*****************************************************************************/ +L3INT Q931ProcInformationTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcNotifyTE + +*****************************************************************************/ +L3INT Q931ProcNotifyTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcStatusTE + +*****************************************************************************/ +L3INT Q931ProcStatusTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcStatusEnquiryTE + +*****************************************************************************/ +L3INT Q931ProcStatusEnquiryTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcSegmentTE + +*****************************************************************************/ +L3INT Q931ProcSegmentTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/****************************************************************************/ +/******************* Q.932 - Supplementary Services *************************/ +/****************************************************************************/ + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcFacilityTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcHoldTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcHoldAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcHoldRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRegisterTE + +*****************************************************************************/ +L3INT Q932ProcRegisterTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q932ProcRetrieveTE + +*****************************************************************************/ +L3INT Q932ProcRetrieveTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRetrieveAckTE + +*****************************************************************************/ +L3INT Q932ProcRetrieveAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} + +/***************************************************************************** + + Function: Q931ProcRetrieveRejectTE + +*****************************************************************************/ +L3INT Q932ProcRetrieveRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom) +{ + Q931mes_Header *pMes = (Q931mes_Header *)&buf[Q931L4HeaderSpace]; + L3INT callIndex; + L3INT ret = Q931E_NO_ERROR; + + /* Find the call using CRV */ + ret = Q931FindCRV(pTrunk, pMes->CRV, &callIndex); + if (ret != Q931E_NO_ERROR) + return ret; + + /* TODO chack against state table for illegal or unexpected message here */ + + /* TODO - Set correct timer here */ + Q931StartTimer(pTrunk, callIndex, Q931_TIMER_T303); + if (iFrom ==4) { + /* TODO Add proc here */ + ret = Q931Tx32Data(pTrunk, 0, buf, pMes->Size); + } + else if (iFrom == 2) { + /* TODO Add proc here */ + ret = Q931Tx34(pTrunk, buf, pMes->Size); + } + return ret; +} diff --git a/libs/openzap/src/isdn/Q931api.c b/libs/openzap/src/isdn/Q931api.c new file mode 100644 index 0000000000..ed0d13836b --- /dev/null +++ b/libs/openzap/src/isdn/Q931api.c @@ -0,0 +1,598 @@ +/***************************************************************************** + + FileName: Q931api.c + + Contents: api (Application Programming Interface) functions. + See q931.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" +#include "memory.h" + +extern L3INT Q931L4HeaderSpace; + +/* +L3INT Q931CreateMesIndex(L3INT mc) +{ + if(mc < 0 || mc > 127 ) + return Q931E_INTERNAL; + + if(Q931MesCount >127) + return Q931E_INTERNAL; + + Q931MesIndex[mc] = Q931MesCount ++; + + return Q931E_NO_ERROR; +} +*/ +/* +L3INT Q931CreateIEIndex(L3INT iec) +{ + if(iec < 0 || iec > 127 ) + return Q931E_INTERNAL; + + if(Q931IECount > 127) + return Q931E_INTERNAL; + + Q931IEIndex[iec] = Q931IECount ++; + + return Q931E_NO_ERROR; +} +*/ + +L3INT Q931Api_InitTrunk(Q931_TrunkInfo_t *pTrunk, + Q931Dialect_t Dialect, + Q931NetUser_t NetUser, + Q931_TrunkType_t TrunkType, + Q931Tx34CB_t Q931Tx34CBProc, + Q931Tx32CB_t Q931Tx32CBProc, + Q931ErrorCB_t Q931ErrorCBProc, + void *PrivateData32, + void *PrivateData34) +{ + int y, dchannel, maxchans, has_sync = 0; + + switch(TrunkType) + { + case Q931_TrType_E1: + dchannel = 16; + maxchans = 31; + has_sync = 1; + break; + + case Q931_TrType_T1: + case Q931_TrType_J1: + dchannel = 24; + maxchans = 24; + break; + + case Q931_TrType_BRI: + case Q931_TrType_BRI_PTMP: + dchannel = 3; + maxchans = 3; + break; + + default: + return 0; + } + + pTrunk->Q931Tx34CBProc = Q931Tx34CBProc; + pTrunk->Q931Tx32CBProc = Q931Tx32CBProc; + pTrunk->Q931ErrorCBProc = Q931ErrorCBProc; + pTrunk->PrivateData32 = PrivateData32; + pTrunk->PrivateData34 = PrivateData34; + + pTrunk->LastCRV = 0; + pTrunk->Dialect = Dialect + NetUser; + pTrunk->Enabled = 0; + pTrunk->TrunkType = TrunkType; + pTrunk->NetUser = NetUser; + pTrunk->TrunkState = 0; + pTrunk->autoRestartAck = 0; + for(y=0; y < Q931MAXCHPERTRUNK; y++) + { + pTrunk->ch[y].Available = 1; + + if(has_sync && y == 0) + { + pTrunk->ch[y].ChanType = Q931_ChType_Sync; + } + else if(y == dchannel) + { + pTrunk->ch[y].ChanType = Q931_ChType_D; + } + else if(y > maxchans) + { + pTrunk->ch[y].ChanType = Q931_ChType_NotUsed; + } + else + { + pTrunk->ch[y].ChanType = Q931_ChType_B; + } + } + + for(y=0; y < Q931MAXCALLPERTRUNK; y++) + { + pTrunk->call[y].InUse = 0; + + } + return 1; +} + +void Q931SetMesProc(L3UCHAR mes, L3UCHAR dialect, q931proc_func_t *Q931ProcFunc, q931umes_func_t *Q931UmesFunc, q931pmes_func_t *Q931PmesFunc) +{ + if(Q931ProcFunc != NULL) + Q931Proc[dialect][mes] = Q931ProcFunc; + if(Q931UmesFunc != NULL) + Q931Umes[dialect][mes] = Q931UmesFunc; + if(Q931PmesFunc != NULL) + Q931Pmes[dialect][mes] = Q931PmesFunc; +} + +void Q931SetIEProc(L3UCHAR iec, L3UCHAR dialect, q931pie_func_t *Q931PieProc, q931uie_func_t *Q931UieProc) +{ + if(Q931PieProc != NULL) + Q931Pie[dialect][iec] = Q931PieProc; + if(Q931UieProc != NULL) + Q931Uie[dialect][iec] = Q931UieProc; +} + +void Q931SetTimeoutProc(L3UCHAR dialect, L3UCHAR timer, q931timeout_func_t *Q931TimeoutProc) +{ + if(Q931Timeout != NULL) + Q931Timeout[dialect][timer] = Q931TimeoutProc; +} + +void Q931SetTimerDefault(L3UCHAR dialect, L3UCHAR timer, q931timer_t timeout) +{ + Q931Timer[dialect][timer] = timeout; +} + +L3INT Q931GetMesSize(Q931mes_Generic *pMes) +{ + + L3UCHAR *p = &pMes->buf[0]; + L3INT Size = (L3INT)(p - (L3UCHAR *)pMes); + return Size; +} + +/***************************************************************************** + + Function: q931AppendIE + + Description: Append IE to the message. + + Parameters: pm Ptr to message. + pi Ptr to information element + + Return Value ie setting + +*****************************************************************************/ + +ie Q931AppendIE( L3UCHAR *pm, L3UCHAR *pi) +{ + ie IE = 0; + Q931mes_Generic * pMes= (Q931mes_Generic *)pm; + Q931ie_BearerCap * pIE= (Q931ie_BearerCap *)pi; + L3INT iISize = pIE->Size; + + L3UCHAR *pBuf = &pMes->buf[0]; + L3INT Off = (L3INT)(pMes->Size - (pBuf - pm)); + IE = (ie)(Off | 0x8000); + + memcpy(&pm[pMes->Size], pi, iISize); + + pMes->Size += iISize; + + return IE; +} + +/***************************************************************************** +*****************************************************************************/ +static L3INT crv={1}; + +L3INT Q931GetUniqueCRV(Q931_TrunkInfo_t *pTrunk) +{ + L3INT max = (Q931_IS_BRI(pTrunk)) ? Q931_BRI_MAX_CRV : Q931_PRI_MAX_CRV; + + crv++; + crv = (crv <= max) ? crv : 1; + + return crv; +} + +L3INT Q931InitMesGeneric(Q931mes_Generic *pMes) +{ + memset(pMes, 0, sizeof(*pMes)); + pMes->ProtDisc = 0x08; + pMes->Size = Q931GetMesSize(pMes); + + return 0; +} + +L3INT Q931InitMesResume(Q931mes_Generic * pMes) +{ + pMes->ProtDisc = 0x08; + pMes->CRV = 0; /* CRV to be allocated, might be receive*/ + pMes->MesType = Q931mes_RESUME; + + pMes->Size = Q931GetMesSize(pMes); + pMes->CallID = 0; /* Channel Identification */ + return 0; +} + +L3INT Q931InitMesRestartAck(Q931mes_Generic * pMes) +{ + pMes->ProtDisc = 0x08; + pMes->CRV = 0; /* CRV to be allocated, might be receive*/ + pMes->MesType = Q931mes_RESTART_ACKNOWLEDGE; + + pMes->Size = Q931GetMesSize(pMes); + pMes->ChanID = 0; /* Channel Identification */ + pMes->Display = 0; + pMes->RestartInd = 0; + pMes->RestartWin = 0; + return 0; +} + +L3INT Q931InitIEBearerCap(Q931ie_BearerCap *pIE) +{ + pIE->IEId = Q931ie_BEARER_CAPABILITY; + pIE->Size = sizeof(Q931ie_BearerCap); + pIE->CodStand = 0; + pIE->ITC = 0; + pIE->TransMode = 0; + pIE->ITR = 0x10; + pIE->RateMul = 0; + + pIE->Layer1Ident = 0; + pIE->UIL1Prot = 0; /* User Information Layer 1 Protocol */ + pIE->SyncAsync = 0; /* Sync/Async */ + pIE->Negot = 0; + pIE->UserRate = 0; + pIE->InterRate = 0; /* Intermediate Rate */ + pIE->NIConTx = 0; + pIE->NIConRx = 0; + pIE->FlowCtlTx = 0; /* Flow control on Tx */ + pIE->FlowCtlRx = 0; /* Flow control on Rx */ + pIE->HDR = 0; + pIE->MultiFrame = 0; /* Multi frame support */ + pIE->Mode = 0; + pIE->LLInegot = 0; + pIE->Assignor = 0; /* Assignor/assignee */ + pIE->InBandNeg = 0; /* In-band/out-band negot. */ + pIE->NumStopBits = 0; /* Number of stop bits */ + pIE->NumDataBits = 0; /* Number of data bits. */ + pIE->Parity = 0; + pIE->DuplexMode = 0; + pIE->ModemType = 0; + pIE->Layer2Ident = 0; + pIE->UIL2Prot = 0; /* User Information Layer 2 Protocol */ + pIE->Layer3Ident = 0; + pIE->UIL3Prot = 0; /* User Information Layer 3 Protocol */ + pIE->AL3Info1 = 0; + pIE->AL3Info2 = 0; + + return 0; +} + +L3INT Q931InitIEChanID(Q931ie_ChanID *pIE) +{ + pIE->IEId = Q931ie_CHANNEL_IDENTIFICATION; + pIE->Size = sizeof(Q931ie_ChanID); + pIE->IntIDPresent = 0; /* Int. id. present */ + pIE->IntType = 0; /* Int. type */ + pIE->PrefExcl = 0; /* Pref./Excl. */ + pIE->DChanInd = 0; /* D-channel ind. */ + pIE->InfoChanSel = 0; /* Info. channel selection */ + pIE->InterfaceID = 0; /* Interface identifier */ + pIE->CodStand = 0; /* Code standard */ + pIE->NumMap = 0; /* Number/Map */ + pIE->ChanMapType = 0; /* Channel type/Map element type */ + pIE->ChanSlot = 0; /* Channel number/Slot map */ + + return 0; +} + +L3INT Q931InitIEProgInd(Q931ie_ProgInd * pIE) +{ + pIE->IEId = Q931ie_PROGRESS_INDICATOR; + pIE->Size = sizeof(Q931ie_ProgInd); + pIE->CodStand = 0; /* Coding standard */ + pIE->Location = 0; /* Location */ + pIE->ProgDesc = 0; /* Progress description */ + + return 0; +} + +L3INT Q931InitIENetFac(Q931ie_NetFac * pIE) +{ + pIE->IEId = Q931ie_NETWORK_SPECIFIC_FACILITIES; + pIE->Size = sizeof(Q931ie_NetFac); + pIE->LenNetID = 0; /* Length of network facilities id. */ + pIE->TypeNetID = 0; /* Type of network identification */ + pIE->NetIDPlan = 0; /* Network identification plan. */ + pIE->NetFac = 0; /* Network specific facility spec. */ + pIE->NetID[0] = 0; + return 0; +} + +L3INT Q931InitIEDisplay(Q931ie_Display * pIE) +{ + pIE->IEId = Q931ie_DISPLAY; + pIE->Size = sizeof(Q931ie_Display); + pIE->Display[0] = 0; + return 0; +} + +L3INT Q931InitIEDateTime(Q931ie_DateTime * pIE) +{ + pIE->IEId = Q931ie_DATETIME; + pIE->Size = sizeof(Q931ie_DateTime); + pIE->Year = 0; /* Year */ + pIE->Month = 0; /* Month */ + pIE->Day = 0; /* Day */ + pIE->Hour = 0; /* Hour */ + pIE->Minute = 0; /* Minute */ + pIE->Second = 0; /* Second */ + + return 0; +} + +L3INT Q931InitIEKeypadFac(Q931ie_KeypadFac * pIE) +{ + pIE->IEId = Q931ie_KEYPAD_FACILITY; + pIE->Size = sizeof(Q931ie_KeypadFac); + pIE->KeypadFac[0] = 0; + return 0; +} + +L3INT Q931InitIESignal(Q931ie_Signal * pIE) +{ + pIE->IEId = Q931ie_SIGNAL; + pIE->Size = sizeof(Q931ie_Signal); + pIE->Signal = 0; + return 0; +} + +L3INT Q931InitIECallingNum(Q931ie_CallingNum * pIE) +{ + pIE->IEId = Q931ie_CALLING_PARTY_NUMBER; + pIE->Size = sizeof(Q931ie_CallingNum); + pIE->TypNum = 0; /* Type of number */ + pIE->NumPlanID = 0; /* Numbering plan identification */ + pIE->PresInd = 0; /* Presentation indicator */ + pIE->ScreenInd = 0; /* Screening indicator */ + pIE->Digit[0] = 0; /* Number digits (IA5) */ + + return 0; +} + +L3INT Q931InitIECallingSub(Q931ie_CallingSub * pIE) +{ + pIE->IEId = Q931ie_CALLING_PARTY_SUBADDRESS; + pIE->Size = sizeof(Q931ie_CallingSub); + pIE->TypNum = 0; /* Type of subaddress */ + pIE->OddEvenInd = 0; /* Odd/Even indicator */ + pIE->Digit[0] = 0; /* Digits */ + + return 0; +} + +L3INT Q931InitIECalledNum(Q931ie_CalledNum * pIE) +{ + pIE->IEId = Q931ie_CALLED_PARTY_NUMBER; + pIE->Size = sizeof(Q931ie_CalledNum); + pIE->TypNum = 0; /* Type of Number */ + pIE->NumPlanID = 0; /* Numbering plan identification */ + pIE->Digit[0] = 0; /* Digit (IA5) */ + + return 0; +} + +L3INT Q931InitIECalledSub(Q931ie_CalledSub * pIE) +{ + pIE->IEId = Q931ie_CALLED_PARTY_SUBADDRESS; + pIE->Size = sizeof(Q931ie_CalledSub); + pIE->TypNum = 0; /* Type of subaddress */ + pIE->OddEvenInd = 0; /* Odd/Even indicator */ + pIE->Digit[0] = 0; /* Digits */ + + return 0; +} + +L3INT Q931InitIETransNetSel(Q931ie_TransNetSel * pIE) +{ + pIE->IEId = Q931ie_TRANSIT_NETWORK_SELECTION; + pIE->Size = sizeof(Q931ie_TransNetSel); + pIE->Type = 0; /* Type of network identifier */ + pIE->NetIDPlan = 0; /* Network idetification plan */ + pIE->NetID[0] = 0; /* Network identification(IA5) */ + + return 0; +} + +L3INT Q931InitIELLComp(Q931ie_LLComp * pIE) +{ + pIE->IEId = Q931ie_LOW_LAYER_COMPATIBILITY; + pIE->Size = sizeof(Q931ie_LLComp); + + pIE->CodStand = 0; /* Coding standard */ + pIE->ITransCap = 0; /* Information transfer capability */ + pIE->NegotInd = 0; /* Negot indic. */ + pIE->TransMode = 0; /* Transfer Mode */ + pIE->InfoRate = 0; /* Information transfer rate */ + pIE->RateMul = 0; /* Rate multiplier */ + pIE->Layer1Ident = 0; /* Layer 1 ident. */ + pIE->UIL1Prot = 0; /* User information layer 1 protocol */ + pIE->SyncAsync = 0; /* Synch/asynch */ + pIE->Negot = 0; /* Negot */ + pIE->UserRate = 0; /* User rate */ + pIE->InterRate = 0; /* Intermediate rate */ + pIE->NIConTx = 0; /* NIC on Tx */ + pIE->NIConRx = 0; /* NIC on Rx */ + pIE->FlowCtlTx = 0; /* Flow control on Tx */ + pIE->FlowCtlRx = 0; /* Flow control on Rx */ + pIE->HDR = 0; /* Hdr/no hdr */ + pIE->MultiFrame = 0; /* Multiframe */ + pIE->ModeL1 = 0; /* Mode L1 */ + pIE->NegotLLI = 0; /* Negot. LLI */ + pIE->Assignor = 0; /* Assignor/Assignor ee */ + pIE->InBandNeg = 0; /* In-band negot. */ + pIE->NumStopBits = 0; /* Number of stop bits */ + pIE->NumDataBits = 0; /* Number of data bits */ + pIE->Parity = 0; /* Parity */ + pIE->DuplexMode = 0; /* Duplex Mode */ + pIE->ModemType = 0; /* Modem type */ + pIE->Layer2Ident = 0; /* Layer 2 ident. */ + pIE->UIL2Prot = 0; /* User information layer 2 protocol */ + pIE->ModeL2 = 0; /* ModeL2 */ + pIE->Q933use = 0; /* Q.9333 use */ + pIE->UsrSpcL2Prot = 0; /* User specified layer 2 protocol info */ + pIE->WindowSize = 0; /* Window size (k) */ + pIE->Layer3Ident = 0; /* Layer 3 ident */ + pIE->OptL3Info = 0; /* Optional layer 3 protocol info. */ + pIE->ModeL3 = 0; /* Mode of operation */ +#if 0 + pIE->ModeX25op = 0; /* Mode of operation X.25 */ +#endif + pIE->DefPackSize = 0; /* Default packet size */ + pIE->PackWinSize = 0; /* Packet window size */ + pIE->AddL3Info = 0; /* Additional Layer 3 protocol info */ + + return 0; +} + +L3INT Q931InitIEHLComp(Q931ie_HLComp * pIE) +{ + pIE->IEId = Q931ie_HIGH_LAYER_COMPATIBILITY; + pIE->Size = sizeof(Q931ie_HLComp); + + return 0; +} + +L3INT Q931ProcUnknownMessage(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom) +{ + /* TODO: Unhandled paramaters */ + (void)pTrunk; + (void)b; + (void)iFrom; + + return 0; +} + +L3INT Q931ProcUnexpectedMessage(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom) +{ + /* TODO: Unhandled paramaters */ + (void)pTrunk; + (void)b; + (void)iFrom; + + return 0; +} + +L3INT Q931Disconnect(Q931_TrunkInfo_t *pTrunk, L3INT iTo, L3INT iCRV, L3INT iCause) +{ + /* TODO: Unhandled paramaters */ + (void)pTrunk; + (void)iTo; + (void)iCRV; + (void)iCause; + + return 0; +} + +L3INT Q931ReleaseComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_RELEASE_COMPLETE; + ptr->CRVFlag = !(ptr->CRVFlag); + return Q931Tx32Data(pTrunk,0,buf,ptr->Size); +} + +L3INT Q931AckRestart(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + L3INT RetCode; + + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_RESTART_ACKNOWLEDGE; + //if (ptr->CRV) { + ptr->CRVFlag = !(ptr->CRVFlag); + //} + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + return RetCode; +} + +L3INT Q931AckSetup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + L3INT RetCode; + + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_SETUP_ACKNOWLEDGE; + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + return RetCode; +} + +L3INT Q931AckConnect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + L3INT RetCode; + + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_CONNECT_ACKNOWLEDGE; + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + return RetCode; +} + +L3INT Q931AckService(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf) +{ + L3INT RetCode; + + Q931mes_Header *ptr = (Q931mes_Header*)&buf[Q931L4HeaderSpace]; + ptr->MesType = Q931mes_SERVICE_ACKNOWLEDGE; + if (ptr->CRV) { + ptr->CRVFlag = !(ptr->CRVFlag); + } + + RetCode = Q931Proc[pTrunk->Dialect][ptr->MesType](pTrunk, buf, 4); + + return RetCode; +} + +Q931_ENUM_NAMES(DIALECT_TYPE_NAMES, DIALECT_STRINGS) +Q931_STR2ENUM(q931_str2Q931Dialect_type, q931_Q931Dialect_type2str, Q931Dialect_t, DIALECT_TYPE_NAMES, Q931_Dialect_Count) diff --git a/libs/openzap/src/isdn/Q931ie.c b/libs/openzap/src/isdn/Q931ie.c new file mode 100644 index 0000000000..62312ee654 --- /dev/null +++ b/libs/openzap/src/isdn/Q931ie.c @@ -0,0 +1,3074 @@ +/***************************************************************************** + + FileName: Q931ie.c + + Contents: Information Element Pack/Unpack functions. + + These functions will pack out a Q931 message from the bit + packed original format into structs that are easier to process + and pack the same structs back into bit fields when sending + messages out. + + The messages contains a short for each possible IE. The MSB + bit flags the precense of an IE, while the remaining bits + are the offset into a buffer to find the actual IE. + + Each IE are supported by 3 functions: + + Q931Pie_XXX Pack struct into Q.931 IE + Q931Uie_XXX Unpack Q.931 IE into struct + Q931InitIEXXX Initialize IE (see Q931api.c). + + Dialect Note: This file will only contain standard DSS1 IE. Other IE as + used in QSIG, NI2, Q.932 etc are located in separate files. + + See q931.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#if (_MSC_VER >= 1400) /* VC8+ */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#ifndef strncasecmp +#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#endif + +/***************************************************************************** + + Macro: Q931MoreIE + + Description: Local helper macro detecting if there is more IE space left + based on the 3 standard parameters Octet, Off and IESpace. + This can be used to test if the IE is completed to avoid + that the header of the next IE is interpreted as a part of + the current IE. + +*****************************************************************************/ +#define Q931MoreIE() (Octet + Off - 2 < IESize) + +#define Q931IESizeTest(x) {\ + if (Octet + Off - 2 != IESize) {\ + Q931SetError(pTrunk, x, Octet, Off);\ + return x;\ + }\ +} + +/***************************************************************************** + + Function: Q931ReadExt + + Description: Many of the octets in the standard have an MSB 'ext.1'. This + means that the octet usually is the latest octet, but that a + futhure standard may extend the octet. A stack must be able + to handle such extensions by skipping the extension octets. + + This function will increase the offset counter with 1 for + each octet with an MSB of zero. This will allow the stack to + skip extensions wihout knowing anything about them. + + Parameters: IBuf ptr to octet array. + Off Starting offset counter + + Return Value: New offset value. + +*****************************************************************************/ + +L3INT Q931ReadExt(L3UCHAR * IBuf, L3INT Off) +{ + L3INT c = 0; + while ((IBuf[c] & 0x80) == 0) { + c++; + } + return Off + c; +} + +/***************************************************************************** + + Function: Q931Uie_BearerCap + + Description: Unpack a bearer capability ie. + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ + +L3INT Q931Uie_BearerCap(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_BearerCap *pie = (Q931ie_BearerCap*)OBuf; + ie *pIE = &pMsg->BearerCap; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->CodStand = ieGetOctet((IBuf[Octet] & 0x60) >> 5); + pie->ITC = ieGetOctet(IBuf[Octet] & 0x1f); + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + /* Octet 4 */ + pie->TransMode = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->ITR = ieGetOctet(IBuf[Octet + Off] & 0x1f); + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Octet++; + + /* Octet 4.1. Rate multiplier is only present if ITR = Multirate */ + if (pie->ITR == 0x18) { + pie->RateMul = ieGetOctet(IBuf[Octet + Off] & 0x7f); + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Off ++; + } + + /* Octet 5 */ + if ((IBuf[Octet + Off] & 0x60) == 0x20 && Q931MoreIE()) { + pie->Layer1Ident = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->UIL1Prot = ieGetOctet(IBuf[Octet + Off] & 0x1f); + Octet++; + + /* Octet 5a. The octet may be present if ITC is unrestrictd digital info + * and UIL1Prot is either V.110, I.460 and X.30 or V.120. It may also + * be present if ITC = 3.1 kHz audio and UIL1Prot is G.711. + * Bit 8 of Octet 5 = 0 indicates that 5a is present. + */ + + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (((pie->ITC == 0x08) && (pie->UIL1Prot == 0x01 || pie->UIL1Prot == 0x08)) + || ((pie->ITC == 0x10) && (pie->UIL1Prot == 0x02 || pie->UIL1Prot == 0x03))) { + pie->SyncAsync = ieGetOctet((IBuf[Octet + Off] & 0x40) >> 6); + pie->Negot = ieGetOctet((IBuf[Octet + Off] & 0x20) >> 5); + pie->UserRate = ieGetOctet(IBuf[Octet + Off] & 0x1f); + Off ++; + } + else { + /* We have detected bit 8 = 0, but no setting that require the */ + /* additional octets ??? */ + Q931SetError(pTrunk, Q931E_BEARERCAP, 5,Off); + return Q931E_BEARERCAP; + } + + /* Octet 5b. Two different structures used. */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL1Prot == 0x01) { /* ITU V.110, I.460 and X.30 */ + pie->InterRate = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->NIConTx = ieGetOctet((IBuf[Octet + Off] & 0x10) >> 4); + pie->NIConRx = ieGetOctet((IBuf[Octet + Off] & 0x08) >> 3); + pie->FlowCtlTx = ieGetOctet((IBuf[Octet + Off] & 0x04) >> 2); + pie->FlowCtlRx = ieGetOctet((IBuf[Octet + Off] & 0x20) >> 1); + Off++; + } + else if (pie->UIL1Prot == 0x08) { /* ITU V.120 */ + pie->HDR = ieGetOctet((IBuf[Octet + Off] & 0x40) >> 6); + pie->MultiFrame = ieGetOctet((IBuf[Octet + Off] & 0x20) >> 5); + pie->Mode = ieGetOctet((IBuf[Octet + Off] & 0x10) >> 4); + pie->LLInegot = ieGetOctet((IBuf[Octet + Off] & 0x08) >> 3); + pie->Assignor = ieGetOctet((IBuf[Octet + Off] & 0x04) >> 2); + pie->InBandNeg = ieGetOctet((IBuf[Octet + Off] & 0x02) >> 1); + Off++; + } + else { + Q931SetError(pTrunk,Q931E_BEARERCAP, 5,Off); + return Q931E_BEARERCAP; + } + + /* Octet 5c */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->NumStopBits = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->NumDataBits = ieGetOctet((IBuf[Octet + Off] & 0x18) >> 3); + pie->Parity = ieGetOctet(IBuf[Octet + Off] & 0x07); + Off++; + + /* Octet 5d */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->DuplexMode = ieGetOctet((IBuf[Octet + Off] & 0x40) >> 6); + pie->ModemType = ieGetOctet(IBuf[Octet + Off] & 0x3f); + Off ++; + } + } + } + } + } + + /* Octet 6 */ + if ((IBuf[Octet + Off] & 0x60) == 0x40 && Q931MoreIE()) { + pie->Layer2Ident = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->UIL2Prot = ieGetOctet(IBuf[Octet + Off] & 0x1f); + + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Octet ++; + } + + /* Octet 7 */ + if ((IBuf[Octet + Off] & 0x60) == 0x60 && Q931MoreIE()) { + pie->Layer3Ident = ieGetOctet((IBuf[Octet + Off] & 0x60) >> 5); + pie->UIL3Prot = ieGetOctet(IBuf[Octet + Off] & 0x1f); + Octet++; + + /* Octet 7a */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL3Prot == 0x0c) { + pie->AL3Info1 = ieGetOctet(IBuf[Octet + Off] & 0x0f); + Off++; + + /* Octet 7b */ + if (IsQ931Ext(IBuf[Octet + Off])) { + pie->AL3Info2 = ieGetOctet(IBuf[Octet + Off] & 0x0f); + Off++; + } + } + else { + Q931SetError(pTrunk,Q931E_BEARERCAP, 7, Off); + return Q931E_BEARERCAP; + + } + } + } + + Q931IESizeTest(Q931E_BEARERCAP); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_BearerCap); + pie->Size = sizeof(Q931ie_BearerCap); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_BearerCap + + Description: Packing a Q.931 Bearer Capability element from a generic + struct into a packed octet structure in accordance with the + standard. + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ + +L3INT Q931Pie_BearerCap(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_BearerCap *pIE = (Q931ie_BearerCap*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; /* remember current offset */ + L3INT li; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Encoding Bearer Capability IE\n"); + + OBuf[(*Octet)++] = Q931ie_BEARER_CAPABILITY ; + li = (*Octet)++; /* remember length position */ + + /* Octet 3 - Coding standard / Information transfer capability */ + OBuf[(*Octet)++] = 0x80 | ((pIE->CodStand << 5) & 0x60) | (pIE->ITC & 0x1f); + + /* Octet 4 - Transfer mode / Information transfer rate */ + OBuf[(*Octet)++] = 0x80 | ((pIE->TransMode << 5) & 0x60) | (pIE->ITR & 0x1f); + + if (pIE->ITR == 0x18) { + /* Octet 4.1 - Rate Multiplier */ + OBuf[(*Octet)++] = 0x80 | (pIE->RateMul & 0x7f); + } + + /* Octet 5 - Layer 1 Ident / User information layer 1 protocol */ + if (pIE->Layer1Ident == 0x01) { + if (((pIE->ITC == 0x08) && (pIE->UIL1Prot == 0x01 || pIE->UIL1Prot == 0x08)) || + ((pIE->ITC == 0x10) && (pIE->UIL1Prot == 0x02 || pIE->UIL1Prot == 0x03))) { + OBuf[(*Octet)++] = 0x00 | ((pIE->Layer1Ident << 5) & 0x60) | (pIE->UIL1Prot & 0x15); + + /* Octet 5a - SyncAsync/Negot/UserRate */ + OBuf[(*Octet)++] = 0x00 | ((pIE->SyncAsync << 6) & 0x40) | ((pIE->Negot << 5) & 0x20) | (pIE->UserRate & 0x1f); + + /* Octet 5b - one of two types */ + if (pIE->UIL1Prot == 0x01) { /* ITU V.110, I.460 and X.30 */ + /* Octet 5b - Intermed rate/ Nic on Tx/Nix on Rx/FlowCtlTx/FlowCtlRx */ + OBuf[(*Octet)++] = 0x00 + | ((pIE->InterRate << 6) & 0x60) + | ((pIE->NIConTx << 4) & 0x10) + | ((pIE->NIConRx << 3) & 0x08) + | ((pIE->FlowCtlTx << 2) & 0x04) + | ((pIE->FlowCtlRx << 1) & 0x02); + } + else if (pIE->UIL1Prot == 0x08) { /* ITU V.120 */ + /* Octet 5b - HDR/Multiframe/Mode/LLINegot/Assignor/Inbandneg*/ + OBuf[(*Octet)++] = 0x00 + | ((pIE->InterRate << 6) & 0x60) + | ((pIE->MultiFrame << 5) & 0x20) + | ((pIE->Mode << 4) & 0x10) + | ((pIE->LLInegot << 3) & 0x08) + | ((pIE->Assignor << 2) & 0x04) + | ((pIE->InBandNeg << 1) & 0x02); + } + + /* Octet 5c - NumStopBits/NumStartBits/Parity */ + OBuf[(*Octet)++] = 0x00 + | ((pIE->NumStopBits << 5) & 0x60) + | ((pIE->NumDataBits << 3) & 0x18) + | (pIE->Parity & 0x07); + + /* Octet 5d - Duplex Mode/Modem Type */ + OBuf[(*Octet)++] = 0x80 | ((pIE->DuplexMode << 6) & 0x40) | (pIE->ModemType & 0x3f); + } + else { + OBuf[(*Octet)++] = 0x80 | ((pIE->Layer1Ident << 5) & 0x60) | (pIE->UIL1Prot & 0x1f); + } + } + + /* Octet 6 - Layer2Ident/User information layer 2 prtocol */ + if (pIE->Layer2Ident == 0x02) { + OBuf[(*Octet)++] = 0x80 | ((pIE->Layer2Ident << 5) & 0x60) | (pIE->UIL2Prot & 0x1f); + } + + /* Octet 7 - Layer 3 Ident/ User information layer 3 protocol */ + if (pIE->Layer3Ident == 0x03) { + if (pIE->UIL3Prot == 0x0c) { + OBuf[(*Octet)++] = 0x00 | ((pIE->Layer3Ident << 5) & 0x60) | (pIE->UIL3Prot & 0x1f); + + /* Octet 7a - Additional information layer 3 msb */ + OBuf[(*Octet)++] = 0x00 | (pIE->AL3Info1 & 0x0f); + + /* Octet 7b - Additional information layer 3 lsb */ + OBuf[(*Octet)++] = 0x80 | (pIE->AL3Info2 & 0x0f); + } + else { + OBuf[(*Octet)++] = 0x80 | ((pIE->Layer3Ident << 5) & 0x60) | (pIE->UIL3Prot & 0x1f); + } + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CallID + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CallID(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CallID *pie = (Q931ie_CallID*)OBuf; + ie *pIE = &pMsg->CallID; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x = 0; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + do { + pie->CallId[x] = IBuf[Octet + Off] & 0x7f; + Off++; + x++; + } while (Q931MoreIE()); + + Q931IESizeTest(Q931E_CALLID); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallID) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CallID) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CallID + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ + +L3INT Q931Pie_CallID(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CallID *pIE = (Q931ie_CallID*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet;/* remember current offset */ + L3INT li; + L3INT sCI = pIE->Size - sizeof(Q931ie_CallID) + 1; + L3INT x; + + OBuf[(*Octet)++] = Q931ie_CALL_IDENTITY ; + li = (*Octet)++; /* remember length position */ + + for (x = 0; x < sCI; x++) { + OBuf[(*Octet)++] = pIE->CallId[x]; + } + + OBuf[(*Octet) - 1] |= 0x80; /* set complete flag at last octet*/ + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CallState + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CallState(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CallState *pie = (Q931ie_CallState*)OBuf; + ie *pIE = &pMsg->CallState; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->CodStand = (IBuf[Octet + Off] >> 6) & 0x03; + pie->CallState = IBuf[Octet + Off] & 0x3f; + Octet++; + + Q931IESizeTest(Q931E_CALLSTATE); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallState); + pie->Size = sizeof(Q931ie_CallState); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CallState + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CallState(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CallState *pIE = (Q931ie_CallState*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; /* remember current offset */ + L3INT li; + + OBuf[(*Octet)++] = Q931ie_CALL_STATE; + li = (*Octet)++; /* remember length position */ + + OBuf[(*Octet)++] = (pIE->CodStand << 6) | (pIE->CallState & 0x3f); + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CalledSub + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CalledSub(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CalledSub *pie = (Q931ie_CalledSub*)OBuf; + ie *pIE = &pMsg->CalledSub; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->TypNum = (IBuf[Octet + Off] >> 4) & 0x07; + pie->OddEvenInd = (IBuf[Octet + Off] >> 3) & 0x01; + Octet++; + + /* Octet 4 */ + x = 0; + do { + pie->Digit[x] = IBuf[Octet + Off] & 0x7f; + Off++; + x++; + } while (Q931MoreIE() && x < 20); + + Q931IESizeTest(Q931E_CALLEDSUB); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CalledSub) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CalledSub) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CalledSub + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CalledSub(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CalledSub *pIE = (Q931ie_CalledSub*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT sN = pIE->Size - sizeof(Q931ie_CalledSub) + 1; + L3INT x; + + /* Octet 1 */ + OBuf[(*Octet)++] = Q931ie_CALLED_PARTY_SUBADDRESS; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TypNum << 4) | (pIE->OddEvenInd << 3); + + /* Octet 4 */ + for (x = 0; xDigit[x]; + } + + OBuf[(*Octet) - 1] |= 0x80; /* Terminate bit */ + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CalledNum + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CalledNum(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CalledNum *pie = (Q931ie_CalledNum*)OBuf; + ie *pIE = &pMsg->CalledNum; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; /* # digits in this case */ + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->TypNum = (IBuf[Octet + Off] >> 4) & 0x07; + pie->NumPlanID = IBuf[Octet + Off] & 0x0f; + Octet++; + + /* Octet 4*/ + x = 0; + do { + pie->Digit[x] = IBuf[Octet + Off] & 0x7f; + Off++; + x++; + } while ((IBuf[Octet + Off]&0x80) == 0 && Q931MoreIE()); + + pie->Digit[x] = '\0'; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CalledNum) + x; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CalledNum) + x); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CalledNum + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CalledNum(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CalledNum *pIE = (Q931ie_CalledNum*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT sN = pIE->Size - sizeof(Q931ie_CalledNum); + L3INT x; + + /* Octet 1 */ + OBuf[(*Octet)++] = Q931ie_CALLED_PARTY_NUMBER; + + /* Octet 2 */ + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TypNum << 4) | (pIE->NumPlanID); + + /* Octet 4 */ + for (x = 0; xDigit[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CallingNum + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CallingNum(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CallingNum *pie = (Q931ie_CallingNum*)OBuf; + ie *pIE = &pMsg->CallingNum; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->TypNum = (IBuf[Octet + Off] >> 4) & 0x07; + pie->NumPlanID = IBuf[Octet + Off] & 0x0f; + + /* Octet 3a */ + if ((IBuf[Octet + Off] & 0x80) == 0) { + Off++; + pie->PresInd = (IBuf[Octet + Off] >> 5) & 0x03; + pie->ScreenInd = IBuf[Octet + Off] & 0x03; + } + Octet++; + + /* Octet 4 */ + x = 0; + while (Q931MoreIE()) { + pie->Digit[x++] = IBuf[Octet + Off] & 0x7f; + + if ((IBuf[Octet + Off] & 0x80) != 0) { + break; + } + Off++; + } + pie->Digit[x] = '\0'; + + Q931IESizeTest(Q931E_CALLINGNUM); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallingNum) + x; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CallingNum) + x); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CallingNum + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CallingNum(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CallingNum *pIE = (Q931ie_CallingNum*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT sN = pIE->Size - sizeof(Q931ie_CallingNum); + L3INT x; + + /* Octet 1 */ + OBuf[(*Octet)++] = Q931ie_CALLING_PARTY_NUMBER; + + /* Octet 2 */ + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x00 | (pIE->TypNum << 4) | (pIE->NumPlanID); + + /* Octet 4 */ + OBuf[(*Octet)++] = 0x80; + + /* Octet 5 */ + for (x = 0; xDigit[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CallingSub + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CallingSub(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CallingSub *pie = (Q931ie_CallingSub*)OBuf; + ie *pIE = &pMsg->CallingSub; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->TypNum = (IBuf[Octet + Off] >> 4) & 0x07; + pie->OddEvenInd = (IBuf[Octet + Off] >> 3) & 0x01; + Octet++; + + /* Octet 4*/ + x = 0; + do { + pie->Digit[x] = IBuf[Octet + Off] & 0x7f; + Off++; + x++; + } while (Q931MoreIE() && x < 20); + + Q931IESizeTest(Q931E_CALLINGSUB); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallingSub) + x -1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CallingSub) + x -1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CallingSub + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CallingSub(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CallingSub *pIE = (Q931ie_CallingSub*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT sN = pIE->Size - sizeof(Q931ie_CallingSub) + 1; + L3INT x; + + /* Octet 1 */ + OBuf[(*Octet)++] = Q931ie_CALLING_PARTY_SUBADDRESS; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TypNum << 4) | (pIE->OddEvenInd << 3); + + /* Octet 4 */ + for (x = 0; xDigit[x]; + } + + OBuf[(*Octet) - 1] |= 0x80; /* Terminate bit */ + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_Cause + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_Cause(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_Cause *pie = (Q931ie_Cause*)OBuf; + ie *pIE = &pMsg->Cause; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2*/ + IESize = IBuf[Octet++]; + + /* Octet 3*/ + pie->CodStand = (IBuf[Octet + Off]>>5) & 0x03; + pie->Location = IBuf[Octet + Off] & 0x0f; + + /* Octet 3a */ + if ((IBuf[Octet + Off] & 0x80) == 0) { + Off++; + pie->Recom = IBuf[Octet + Off] & 0x7f; + } + Octet++; + + /* Octet 4 */ + pie->Value = IBuf[Octet + Off] & 0x7f; + Octet++; + + /* Consume optional Diagnostic bytes */ + while (Q931MoreIE()) { + Off++; + }; + + Q931IESizeTest(Q931E_CAUSE); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_Cause); + pie->Size = sizeof(Q931ie_Cause); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_Cause + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_Cause(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_Cause *pIE = (Q931ie_Cause*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_CAUSE; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->CodStand<<5) | pIE->Location; + + /* Octet 3a - currently not supported in send */ + + /* Octet 4 */ + OBuf[(*Octet)++] = 0x80 | pIE->Value; + + /* Octet 5 - diagnostics not supported in send */ + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_CongLevel + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_CongLevel(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_CongLevel *pie = (Q931ie_CongLevel*)OBuf; + ie *pIE = &pMsg->CongestionLevel; + L3INT Off = 0; + L3INT Octet = 0; + + *pIE = 0; + + pie->IEId = IBuf[Octet] & 0xf0; + pie->CongLevel = IBuf[Octet] & 0x0f; + Octet ++; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CongLevel); + pie->Size = sizeof(Q931ie_CongLevel); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_CongLevel + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_CongLevel(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_CongLevel *pIE = (Q931ie_CongLevel*)IBuf; + L3INT rc = 0; + /* L3INT Beg = *Octet; */ + + OBuf[(*Octet)++] = Q931ie_CONGESTION_LEVEL | pIE->CongLevel; + + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_ChanID + + Parameters: IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Uie_ChanID(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_ChanID *pie = (Q931ie_ChanID*)OBuf; + ie *pIE = &pMsg->ChanID; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; +//18 04 e1 80 83 01 + *pIE = 0; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Decoding ChanID IE\n"); + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->IntIDPresent = (IBuf[Octet] >> 6) & 0x01; + pie->IntType = (IBuf[Octet] >> 5) & 0x01; + pie->PrefExcl = (IBuf[Octet] >> 3) & 0x01; + pie->DChanInd = (IBuf[Octet] >> 2) & 0x01; + pie->InfoChanSel = IBuf[Octet] & 0x03; + + Off = Q931ReadExt(&IBuf[Octet++], Off); + + /* Octet 3.1 */ + if (pie->IntIDPresent) { + pie->InterfaceID = IBuf[Octet + Off] & 0x7f; + + /* Temp fix. Interface id can be extended using the extension bit */ + /* this will read the octets, but do nothing with them. this is done */ + /* because the usage of this field is a little unclear */ + /* 30.jan.2001/JVB */ + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Off++; + } + + if ((Octet + Off - 2) != IESize) { + /* Octet 3.2 */ + if (pie->IntType == 1) { /* PRI etc */ + pie->CodStand = (IBuf[Octet + Off] >> 5) & 0x03; + pie->NumMap = (IBuf[Octet + Off] >> 4) & 0x01; + pie->ChanMapType = IBuf[Octet + Off] & 0x0f; + Off++; + + /* Octet 3.3 */ + /* Temp fix. Assume B channel. H channels not supported */ + pie->ChanSlot = IBuf[Octet + Off] & 0x7f; + + /* Some dialects don't follow the extension coding properly for this, but this should be safe for all */ + if ((Octet + Off - 1) != IESize) { + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + } + Off++; + } + } + + Q931IESizeTest(Q931E_CHANID); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_ChanID); + pie->Size = sizeof(Q931ie_ChanID); + + if (pTrunk->loglevel == Q931_LOG_DEBUG) { + const char *iface; + char tmp[100] = ""; + + if (!pie->IntType) { + switch (pie->InfoChanSel) { + case 0x0: + iface = "None"; + break; + case 0x1: + iface = "B1"; + break; + case 0x2: + iface = "B2"; + break; + default: + iface = "Any Channel"; + } + + snprintf(tmp, sizeof(tmp)-1, "InfoChanSel: %d (%s)", pie->InfoChanSel, iface); + } + + Q931Log(pTrunk, Q931_LOG_DEBUG, + "\n-------------------------- Q.931 Channel ID ------------------------\n" + " Pref/Excl: %s, Interface Type: %s\n" + " %s\n" + "--------------------------------------------------------------------\n\n", + ((pie->PrefExcl) ? "Preferred" : "Exclusive"), + ((pie->IntType) ? "PRI/Other" : "BRI"), + tmp); + } + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_ChanID + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_ChanID(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_ChanID *pIE = (Q931ie_ChanID*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; /* remember current offset */ + L3INT li; + + OBuf[(*Octet)++] = Q931ie_CHANNEL_IDENTIFICATION; + li = (*Octet)++; /* remember length position */ + + /* Octet 3 flags & BRI chan # */ + OBuf[(*Octet)++] = 0x80 + | ((pIE->IntIDPresent << 6) & 0x40) + | ((pIE->IntType << 5) & 0x20) + | ((pIE->PrefExcl << 3) & 0x08) + | (pIE->InfoChanSel & 0x03); + + /* Octet 3.1 - Interface Identifier */ + if (pIE->IntIDPresent) { + OBuf[(*Octet)++] = 0x80 | (pIE->InterfaceID & 0x7f); + } + + /* Octet 3.2 & 3.3 - PRI */ + if (pIE->IntType) { + OBuf[(*Octet)++] = 0x80 + | ((pIE->CodStand << 5) & 0x60) + | ((pIE->NumMap << 4) & 0x10) + | (pIE->ChanMapType & 0x0f); /* TODO: support all possible channel map types */ + + /* Octet 3.3 Channel number */ + switch (pIE->ChanMapType) { + case 0x6: /* Slot map: H0 Channel Units */ /* unsupported, Octets 3.3.1 - 3.3.3 */ + return Q931E_CHANID; + + case 0x8: /* Slot map: H11 Channel Units */ + case 0x9: /* Slot map: H12 Channel Units */ + default: /* Channel number */ + OBuf[(*Octet)++] = 0x80 | (pIE->ChanSlot & 0x7f); + break; + } + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + + +/***************************************************************************** + + Function: Q931Uie_CRV + + Description: Reading CRV. + + The CRV is currently returned in the return value that + Q921Rx23 will assign to the CRV field in the unpacked + message. CRV is basically 2 bytes etc, but the spec allows + the use of longer CRV values. + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: CRV + +*****************************************************************************/ +L3USHORT Q931Uie_CRV(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff) +{ + L3USHORT CRV = 0; + L3INT Octet = *IOff; + L3INT l = IBuf[Octet++]; + + if (l == 1) { /* One octet CRV */ + CRV = IBuf[Octet++] & 0x7F; + } + else if (l == 2) { /* two octet CRV */ + CRV = (IBuf[Octet++] & 0x7f) << 8; + CRV |= IBuf[Octet++]; + } + else { + /* Long CRV is not used, so we skip this */ + /* TODO: is it right to set to 0 here? */ + CRV = 0; + Octet += l; + } + + *IOff = Octet; + return CRV; +} + +/***************************************************************************** + + Function: Q931Uie_DateTime + + Parameters: pTrunk [IN] Ptr to trunk information. + pIE [OUT] ptr to Information Element id. + IBuf [IN] ptr to a packed ie. + OBuf [OUT] ptr to buffer for Unpacked ie. + IOff [IN\OUT] Input buffer offset + OOff [IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_DateTime(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_DateTime * pie = (Q931ie_DateTime*)OBuf; + ie *pIE = &pMsg->DateTime; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize = 0; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 - Year */ + pie->Year = IBuf[Octet++]; + + /* Octet 4 - Month */ + pie->Month = IBuf[Octet++]; + + /* Octet 5 - Day */ + pie->Day = IBuf[Octet++]; + + /******************************************************************* + The remaining part of the IE are optioinal, but only the length + can now tell us wherever these fields are present or not + (always remember: IESize does not include ID and Size octet) + ********************************************************************/ + pie->Format = 0; + + /* Octet 6 - Hour (optional)*/ + if (IESize >= 4) { + pie->Format = 1; + pie->Hour = IBuf[Octet++]; + + /* Octet 7 - Minute (optional)*/ + if (IESize >= 5) { + pie->Format = 2; + pie->Minute = IBuf[Octet++]; + + /* Octet 8 - Second (optional)*/ + if (IESize >= 6) { + pie->Format = 3; + pie->Second = IBuf[Octet++]; + } + } + } + + Q931IESizeTest(Q931E_DATETIME); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_DateTime); + pie->Size = sizeof(Q931ie_DateTime); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_DateTime + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_DateTime(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_DateTime *pIE = (Q931ie_DateTime*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_DATETIME; + li = (*Octet)++; + + OBuf[(*Octet)++] = pIE->Year; + OBuf[(*Octet)++] = pIE->Month; + OBuf[(*Octet)++] = pIE->Day; + if (pIE->Format >= 1) { + OBuf[(*Octet)++] = pIE->Hour; + + if (pIE->Format >= 2) { + OBuf[(*Octet)++] = pIE->Minute; + + if (pIE->Format >= 3) { + OBuf[(*Octet)++] = pIE->Second; + } + } + } + + OBuf[li] = (L3UCHAR)((*Octet)-Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_Display + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_Display(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_Display *pie = (Q931ie_Display*)OBuf; + ie *pIE = &pMsg->Display; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + L3INT x; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + IESize = IBuf[Octet++]; + + for (x = 0; xDisplay[x] = IBuf[Octet + Off] & 0x7f; + Off++; + } + + Q931IESizeTest(Q931E_DISPLAY); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_Display) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_Display) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_Display + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_Display(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_Display *pIE = (Q931ie_Display*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT DSize; + L3INT x; + + OBuf[(*Octet)++] = Q931ie_DISPLAY; + li = (*Octet)++; + + DSize = pIE->Size - sizeof(Q931ie_Display); + + for (x = 0; x< DSize; x++) { + + OBuf[(*Octet)++] = pIE->Display[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_HLComp + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_HLComp(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_HLComp * pie = (Q931ie_HLComp*)OBuf; + ie *pIE = &pMsg->HLComp; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet */ + IESize = IBuf[Octet++]; + + /* Octet 3*/ + pie->CodStand = (IBuf[Octet + Off] >>5) & 0x03; + pie->Interpret = (IBuf[Octet + Off] >>2) & 0x07; + pie->PresMeth = IBuf[Octet + Off] & 0x03; + Octet++; + + /* Octet 4 */ + pie->HLCharID = IBuf[Octet + Off] & 0x7f; + Octet++; + + /* Octet 4a*/ + if ((IBuf[Octet + Off - 1] & 0x80) == 0 && Q931MoreIE()) { + if (pie->HLCharID == 0x5e || pie->HLCharID == 0x5f) { + pie->EHLCharID = IBuf[Octet + Off] & 0x7f; + Off++; + } + else if ( pie->HLCharID >= 0xc3 && pie->HLCharID <= 0xcf) { + pie->EVideoTlfCharID = IBuf[Octet + Off] & 0x7f; + Off++; + } + else { + /* error Octet 4a indicated, but invalid value in Octet 4. */ + Q931SetError(pTrunk,Q931E_HLCOMP, 4, Off); + return Q931E_HLCOMP; + } + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + } + + Q931IESizeTest(Q931E_HLCOMP); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_HLComp); + pie->Size = sizeof(Q931ie_HLComp); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_HLComp + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_HLComp(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_HLComp *pIE = (Q931ie_HLComp*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_HIGH_LAYER_COMPATIBILITY; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | ((pIE->CodStand << 5) & 0x60) | ((pIE->Interpret << 2) & 0x1c) | (pIE->PresMeth & 0x03); + + /* Octet 4 */ + OBuf[(*Octet)++] = pIE->HLCharID; + + /* Octet 4a */ + if (pIE->HLCharID == 0x5e || pIE->HLCharID == 0x5f) { + OBuf[(*Octet)++] = 0x80 | (pIE->EHLCharID & 0x7f); + } + else if ( pIE->HLCharID >= 0xc3 && pIE->HLCharID <= 0xcf) { + OBuf[(*Octet)++] = 0x80 | (pIE->EVideoTlfCharID & 0x7f); + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_KeypadFac + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_KeypadFac(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_KeypadFac *pie = (Q931ie_KeypadFac*)OBuf; + ie *pIE = &pMsg->KeypadFac; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + L3INT x; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + IESize = IBuf[Octet++]; + + for (x = 0; xKeypadFac[x] = IBuf[Octet + Off] & 0x7f; + Off++; + } + + Q931IESizeTest(Q931E_KEYPADFAC); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_KeypadFac) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_KeypadFac) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_KeypadFac + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_KeypadFac(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_KeypadFac *pIE = (Q931ie_KeypadFac*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + L3INT DSize; + L3INT x; + + OBuf[(*Octet)++] = Q931ie_KEYPAD_FACILITY; + li = (*Octet)++; + + DSize = pIE->Size - sizeof(Q931ie_KeypadFac) + 1; + + for (x = 0; x< DSize; x++) { + OBuf[(*Octet)++] = pIE->KeypadFac[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_LLComp + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_LLComp(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_LLComp *pie = (Q931ie_LLComp*)OBuf; + ie *pIE = &pMsg->LLComp; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->CodStand = (IBuf[Octet + Off] >> 5) & 0x03; + pie->ITransCap = IBuf[Octet + Off] & 0x1f; + Octet++; + + /* Octet 3a*/ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->NegotInd = (IBuf[Octet + Off] >> 6) & 0x01; + Off++; + } + + /* Octet 4 */ + pie->TransMode = (IBuf[Octet + Off] >> 5) & 0x03; + pie->InfoRate = IBuf[Octet + Off] & 0x1f; + + Octet++; + + /* Octet 4.1 */ + if (pie->InfoRate == 0x14) { /* Mutirate */ + pie->RateMul = IBuf[Octet + Off] & 0x7f; + Off++; + } + + /* Octet 5 - Layer 1 Ident */ + if ((IBuf[Octet + Off] & 0x60) == 0x20) { /* Layer 1 Ident ? */ + pie->Layer1Ident = (IBuf[Octet + Off] >> 5) & 0x03; + pie->UIL1Prot = IBuf[Octet + Off] & 0x1f; + Octet++; + + /* Octet 5a */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->SyncAsync = (IBuf[Octet + Off] >> 6) & 0x01; + pie->Negot = (IBuf[Octet + Off] >> 5) & 0x01; + pie->UserRate = IBuf[Octet + Off] & 0x1f; + Off++; + + /* Octet 5b - 2 options */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL1Prot == 0x01) { /* V.110, I.460 and X.30*/ + pie->InterRate = (IBuf[Octet + Off] >> 5) & 0x03; + pie->NIConTx = (IBuf[Octet + Off] >> 4) & 0x01; + pie->NIConRx = (IBuf[Octet + Off] >> 3) & 0x01; + pie->FlowCtlTx = (IBuf[Octet + Off] >> 2) & 0x01; + pie->FlowCtlRx = (IBuf[Octet + Off] >> 1) & 0x01; + Off++; + } + else if (pie->UIL1Prot == 0x80) { /* V.120 */ + pie->HDR = (IBuf[Octet + Off] >> 6) & 0x01; + pie->MultiFrame = (IBuf[Octet + Off] >> 5) & 0x01; + pie->ModeL1 = (IBuf[Octet + Off] >> 4) & 0x01; + pie->NegotLLI = (IBuf[Octet + Off] >> 3) & 0x01; + pie->Assignor = (IBuf[Octet + Off] >> 2) & 0x01; + pie->InBandNeg = (IBuf[Octet + Off] >> 1) & 0x01; + Off++; + } + else if (pie->UIL1Prot == 0x07) { /* non standard */ + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Off++; + } + else { + Q931SetError(pTrunk,Q931E_LLCOMP, 5,2); + return Q931E_LLCOMP; + } + + /* Octet 5c */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->NumStopBits = (IBuf[Octet + Off] >> 5) & 0x03; + pie->NumDataBits = (IBuf[Octet + Off] >> 3) & 0x03; + pie->Parity = IBuf[Octet + Off] & 0x07; + Off++; + + /* Octet 5d */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->DuplexMode = (IBuf[Octet + Off] >> 6) & 0x01; + pie->ModemType = IBuf[Octet + Off] & 0x3f; + Off = Q931ReadExt(&IBuf[Octet + Off], Off); + Off++; + } + } + } + } + } + + /* Octet 6 - Layer 2 Ident */ + if ((IBuf[Octet + Off] & 0x60) == 0x40) { /* Layer 1 Ident ? */ + pie->Layer2Ident = (IBuf[Octet + Off] >>5) & 0x03; + pie->UIL2Prot = IBuf[Octet + Off] & 0x1f; + Octet++; + + /* Octet 6a */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL2Prot == 0x10) { /* 2nd 6a */ + pie->UsrSpcL2Prot = IBuf[Octet + Off] & 0x7f; + Off++; + } + else { /* assume 1st 6a */ + pie->ModeL2 = (IBuf[Octet + Off] >> 5) & 0x03; + pie->Q933use = IBuf[Octet + Off] & 0x03; + Off++; + } + /* Octet 6b */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->WindowSize = IBuf[Octet + Off] & 0x7f; + Off++; + } + } + } + + /* Octet 7 - layer 3 Ident */ + if ((IBuf[Octet + Off] & 0x60) == 0x60) { /* Layer 3 Ident ? */ + pie->Layer3Ident = (IBuf[Octet + Off] >> 5) & 0x03; + pie->UIL3Prot = IBuf[Octet + Off] & 0x1f; + Octet++; + + /* Octet 7a */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + if (pie->UIL3Prot == 0x0b) { + /* Octet 7a + 7b AddL3Info */ + pie->AddL3Info = ((IBuf[Octet + Off] << 4) & 0xf0) + | (IBuf[Octet + Off + 1] & 0x0f); + Off += 2; + } + else { + if (pie->UIL3Prot == 0x1f) { + pie->ModeL3 = (IBuf[Octet + Off] >> 5) & 0x03; + Off++; + } + else { + pie->OptL3Info = IBuf[Octet + Off] & 0x7f; + Off++; + } + + /* Octet 7b*/ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->DefPackSize = IBuf[Octet + Off] & 0x0f; + Off++; + + /* Octet 7c */ + if (IsQ931Ext(IBuf[Octet + Off - 1])) { + pie->PackWinSize= IBuf[Octet + Off] & 0x7f; + } + } + } + } + } + + Q931IESizeTest(Q931E_LLCOMP); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_LLComp); + pie->Size = sizeof(Q931ie_LLComp); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_LLComp + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_LLComp(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_LLComp *pIE = (Q931ie_LLComp*)IBuf; + L3INT rc = 0; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_LOW_LAYER_COMPATIBILITY; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = (pIE->CodStand << 6) | pIE->ITransCap; + + /* Octet 3a */ + OBuf[(*Octet)++] = 0x80 | (pIE->NegotInd << 6); + + /* Octet 4 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TransMode << 5) | pIE->InfoRate; + + /* Octet 4.1 */ + if (pIE->InfoRate == 0x18) { + OBuf[(*Octet)++] = 0x80 | pIE->RateMul; + } + + /* Octet 5 */ + if (pIE->Layer1Ident == 0x01) { + OBuf[(*Octet)++] = (pIE->Layer1Ident << 5) | pIE->UIL1Prot; + + /* Octet 5a */ + if ((pIE->ITransCap == 0x08 && (pIE->UIL1Prot == 0x01 || pIE->UIL1Prot == 0x08)) + || (pIE->ITransCap == 0x10 && (pIE->UIL1Prot == 0x02 || pIE->UIL1Prot == 0x03))) { + OBuf[(*Octet)++] = (pIE->SyncAsync<<6) | (pIE->Negot<<5) | pIE->UserRate; + + /* Octet 5b*/ + if (pIE->UIL1Prot == 0x01) { + OBuf[(*Octet)++] = (pIE->InterRate << 5) + | (pIE->NIConTx << 4) + | (pIE->NIConTx << 3) + | (pIE->FlowCtlTx << 2) + | (pIE->FlowCtlRx << 1); + } + else if (pIE->UIL1Prot == 0x08) { + OBuf[(*Octet)++] = (pIE->HDR << 6) + | (pIE->MultiFrame << 5) + | (pIE->ModeL1 << 4) + | (pIE->NegotLLI << 3) + | (pIE->Assignor << 2) + | (pIE->InBandNeg << 1); + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + + /* How to detect wherever 5c and 5d is to present is not clear + * but they have been inculded as 'standard' + * Octet 5c + */ + if (pIE->UIL1Prot == 0x01 || pIE->UIL1Prot == 0x08) { + OBuf[(*Octet)++] = (pIE->NumStopBits << 5) | (pIE->NumDataBits << 3) | pIE->Parity ; + + /* Octet 5d */ + OBuf[(*Octet)++] = 0x80 | (pIE->DuplexMode << 6) | pIE->ModemType; + } + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + } + + /* Octet 6 */ + if (pIE->Layer2Ident == 0x02) { + OBuf[(*Octet)++] = (pIE->Layer2Ident << 5) | pIE->UIL2Prot; + + /* Octet 6a*/ + if (pIE->UIL2Prot == 0x02 /* Q.921/I.441 */ + || pIE->UIL2Prot == 0x06 /* X.25 link layer */ + || pIE->UIL2Prot == 0x07 /* X.25 multilink */ + || pIE->UIL2Prot == 0x09 /* HDLC ARM */ + || pIE->UIL2Prot == 0x0a /* HDLC NRM */ + || pIE->UIL2Prot == 0x0b /* HDLC ABM */ + || pIE->UIL2Prot == 0x0d /* X.75 SLP */ + || pIE->UIL2Prot == 0x0e /* Q.922 */ + || pIE->UIL2Prot == 0x11) { /* ISO/ECE 7776 DTE-DCE */ + OBuf[(*Octet)++] = (pIE->ModeL2 << 5) | pIE->Q933use; + + /* Octet 6b */ + OBuf[(*Octet)++] = 0x80 | pIE->WindowSize; + } + else if (pIE->UIL2Prot == 0x10) { /* User Specific */ + OBuf[(*Octet)++] = 0x80 | pIE->UsrSpcL2Prot; + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + } + + /* Octet 7 */ + if (pIE->Layer3Ident == 0x03) { + OBuf[(*Octet)++] = (pIE->Layer3Ident << 5) | pIE->UIL3Prot; + + /* Octet 7a - 3 different ones */ + if (pIE->UIL3Prot == 0x10) { + OBuf[(*Octet++)] = 0x80 | pIE->OptL3Info; + } + else if (pIE->UIL3Prot == 0x06 + || pIE->UIL3Prot == 0x07 + || pIE->UIL3Prot == 0x08) { + OBuf[(*Octet)++] = pIE->ModeL3 << 5; + + /* Octet 7b note 7 */ + OBuf[(*Octet)++] = pIE->DefPackSize; + + /* Octet 7c note 7 */ + OBuf[(*Octet)++] = 0x80 | pIE->PackWinSize; + } + else if (pIE->UIL3Prot == 0x0b) { + OBuf[(*Octet)++] = (pIE->AddL3Info >> 4) & 0x0f; + OBuf[(*Octet)++] = 0x80 | (pIE->AddL3Info & 0x0f); + } + else { + OBuf[(*Octet) - 1] |= 0x80; + } + } + else { + Q931SetError(pTrunk,Q931E_LLCOMP, 7,0); + rc = Q931E_LLCOMP; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_NetFac + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_NetFac(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_NetFac *pie = (Q931ie_NetFac*)OBuf; + ie *pIE = &pMsg->NetFac; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + pie->LenNetID = IBuf[Octet + Off]; /* full octet is used */ + Octet++; + + if (pie->LenNetID > 0) { + /* Octet 3.1 */ + pie->TypeNetID = (IBuf[Octet + Off] >> 4) & 0x0f; + pie->NetIDPlan = IBuf[Octet + Off] & 0x0f; + Off = Q931ReadExt(&IBuf[Octet], Off); + Off++; + + /* Octet 3.2*/ + for (x = 0; x < pie->LenNetID; x++) { + pie->NetID[x] = IBuf[Octet + Off] & 0x7f; + Off++; + } + } + + /* Octet 4*/ + pie->NetFac = IBuf[Octet + Off]; /* Full Octet is used */ + Octet++; + + Q931IESizeTest(Q931E_NETFAC); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_NetFac) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_NetFac) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_NetFac + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_NetFac(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_NetFac *pIE = (Q931ie_NetFac*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + L3INT x; + + OBuf[(*Octet)++] = Q931ie_NETWORK_SPECIFIC_FACILITIES; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = pIE->LenNetID; + + if (pIE->LenNetID > 0) { + /* Octet 3.1 */ + OBuf[(*Octet)++] = 0x80 | (pIE->TypeNetID << 4) | pIE->NetIDPlan; + + /* Octet 3.2 */ + for (x = 0; x LenNetID; x++) { + OBuf[(*Octet)++] = pIE->NetID[x]; + } + } + + /* Octet 4 */ + OBuf[(*Octet)++] = pIE->NetFac; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_NotifInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_NotifInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_NotifInd *pie = (Q931ie_NotifInd*)OBuf; + ie *pIE = &pMsg->NotifInd; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2*/ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Notification = IBuf[Octet + Off] & 0x7f; + + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + Q931IESizeTest(Q931E_NOTIFIND); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_NotifInd); + pie->Size = sizeof(Q931ie_NotifInd); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_NotifInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_NotifInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_NotifInd *pIE = (Q931ie_NotifInd*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_NOTIFICATION_INDICATOR; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = pIE->Notification; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_ProgInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_ProgInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_ProgInd *pie = (Q931ie_ProgInd*)OBuf; + ie *pIE = &pMsg->ProgInd; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->CodStand = (IBuf[Octet + Off] >> 5) & 0x03; + pie->Location = IBuf[Octet + Off] & 0x0f; + + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + /* Octet 4 */ + pie->ProgDesc = IBuf[Octet + Off] & 0x7f; + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + Q931IESizeTest(Q931E_PROGIND); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_ProgInd); + pie->Size = sizeof(Q931ie_ProgInd); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_ProgInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset L3INTo OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_ProgInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_ProgInd *pIE = (Q931ie_ProgInd*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_PROGRESS_INDICATOR; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->CodStand << 5) | pIE->Location; + + /* Octet 4 */ + OBuf[(*Octet)++] = 0x80 | pIE->ProgDesc; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_RepeatInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_RepeatInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_RepeatInd *pie = (Q931ie_RepeatInd*)OBuf; + ie *pIE = &pMsg->RepeatInd; + L3INT Off = 0; + L3INT Octet = 0; + + *pIE = 0; + + pie->IEId = IBuf[Octet] & 0xf0; + pie->RepeatInd = IBuf[Octet] & 0x0f; + Octet ++; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_RepeatInd); + pie->Size = sizeof(Q931ie_RepeatInd); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_RepeatInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_RepeatInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_RepeatInd *pIE = (Q931ie_RepeatInd*)IBuf; + L3INT rc = 0; + /* L3INT Beg = *Octet; */ + + OBuf[(*Octet)++] = Q931ie_REPEAT_INDICATOR | pIE->RepeatInd; + + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_RevChargeInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_RevChargeInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + ie iE; + /* ie *pIE = &pMsg->RevChargeInd; */ + Q931SetIE(iE, *OOff); + + return iE; +} + +/***************************************************************************** + + Function: Q931Pie_RevChargeInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_RevChargeInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Uie_RestartInd + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_RestartInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_RestartInd *pie = (Q931ie_RestartInd*)OBuf; + ie *pIE = &pMsg->RestartInd; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Class = IBuf[Octet + Off] & 0x07; + pie->Spare = IBuf[Octet + Off] & 0x78; + + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + Q931IESizeTest(Q931E_RESTARTIND); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_RestartInd); + pie->Size = sizeof(Q931ie_RestartInd); + + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_RestartInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_RestartInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_RestartInd *pIE = (Q931ie_RestartInd*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_RESTART_INDICATOR; + li = (*Octet)++; + + /* Octet 3*/ + OBuf[(*Octet)++] = 0x80 | pIE->Class ; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_Segment + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_Segment(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_Segment *pie = (Q931ie_Segment*)OBuf; + ie *pIE = &pMsg->Segment; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + Octet++; + + /* Octet 2*/ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->FSI = (IBuf[Octet + Off] & 0x80) >> 7; + pie->NumSegRem = IBuf[Octet + Off] & 0x7f; + Octet++; + + /* Octet 4 */ + pie->SegType = IBuf[Octet + Off] & 0x7f; + Octet++; + + Q931IESizeTest(Q931E_SEGMENT); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_Segment); + pie->Size = sizeof(Q931ie_Segment); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_Segment + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_Segment(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_Segment *pIE = (Q931ie_Segment*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_SEGMENTED_MESSAGE; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = (pIE->FSI << 7) | pIE->NumSegRem; + + /* Octet 4 */ + OBuf[(*Octet)++] = pIE->SegType; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_SendComplete + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_SendComplete(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_SendComplete *pie = (Q931ie_SendComplete*)OBuf; + ie *pIE = &pMsg->SendComplete; + L3INT Off = 0; + L3INT Octet = 0; + + *pIE = 0; + Octet++; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_SendComplete); + pie->Size = sizeof(Q931ie_SendComplete); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_ProgInd + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_SendComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + /* Q931ie_SendComplete * pIE = (Q931ie_SendComplete*)IBuf; */ + L3INT rc = Q931E_NO_ERROR; + /* L3INT Beg = *Octet; */ + + OBuf[(*Octet)++] = 0x80 | (L3UCHAR)Q931ie_SENDING_COMPLETE; + + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_Signal + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_Signal(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_Signal *pie = (Q931ie_Signal*)OBuf; + ie *pIE = &pMsg->Signal; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Signal = IBuf[Octet + Off]; + Octet++; + + Q931IESizeTest(Q931E_SIGNAL); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_Signal); + pie->Size = sizeof(Q931ie_Signal); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_Signal + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_Signal(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_Signal *pIE = (Q931ie_Signal*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_SIGNAL; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = pIE->Signal; + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_TransNetSel + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_TransNetSel(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_TransNetSel *pie = (Q931ie_TransNetSel*)OBuf; + ie *pIE = &pMsg->TransNetSel; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x = 0; + L3INT l; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + l = IBuf[Octet++] - 3; + + /* Octet 3 */ + pie->Type = (IBuf[Octet + Off] >> 4) & 0x07; + + Off = Q931ReadExt(&IBuf[Octet], Off); + Octet++; + + for (x = 0; x < l; x++) { + pie->NetID[x] = IBuf[Octet + Off] & 0x7f; + Off++; + } + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_TransNetSel) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_TransNetSel) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_TransNetSel + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_TransNetSel(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_TransNetSel *pIE = (Q931ie_TransNetSel*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + L3INT x; + L3INT l; + + OBuf[(*Octet)++] = Q931ie_TRANSIT_NETWORK_SELECTION; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | (pIE->Type << 4) | pIE->NetIDPlan; + + /* Octet 4 */ + l = pIE->Size - sizeof(Q931ie_TransNetSel) + 1; + for (x = 0; x < l; x++) { + OBuf[(*Octet)++] = pIE->NetID[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_UserUser + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_UserUser(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_UserUser *pie = (Q931ie_UserUser*)OBuf; + ie *pIE = &pMsg->UserUser; + L3INT Off = 0; + L3INT Octet = 0; + L3INT l; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + l = IBuf[Octet++] - 1; + + /* Octet 3 */ + pie->ProtDisc = IBuf[Octet++]; + + for (Off = 0; Off < l; Off++) { + pie->User[Off] = IBuf[Octet + Off]; + } + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_UserUser) + Off - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_UserUser) + Off - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_UserUser + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_UserUser(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_UserUser *pIE = (Q931ie_UserUser*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + L3INT x; + L3INT l; + + OBuf[(*Octet)++] = Q931ie_USER_USER; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = pIE->ProtDisc; + + /* Octet 4 */ + l = pIE->Size - sizeof(Q931ie_UserUser) + 1; + for (x = 0; x < l; x++) { + OBuf[(*Octet)++] = pIE->User[x]; + } + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + +/***************************************************************************** + + Function: Q931Uie_GenericDigits + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_GenericDigits(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_GenericDigits *pie = (Q931ie_GenericDigits*)OBuf; + ie *pIE = &pMsg->GenericDigits; + L3INT Off = 0; + L3INT Octet = 0; + L3INT x; + L3INT IESize; + + *pIE = 0; + + /* Octet 1 */ + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Type = (IBuf[Octet]) & 0x1F; + pie->Encoding = (IBuf[Octet] >> 5) & 0x07; + Octet++; + + /* Octet 4*/ + if (pie->Encoding == 0) { /* BCD Even */ + x = 0; + do { + pie->Digit[x++] = IBuf[Octet + Off] & 0x0f; + pie->Digit[x++] = (IBuf[Octet + Off] >> 4) & 0x0f; + Off++; + } while (Q931MoreIE()); + } else if (pie->Encoding == 1) { /* BCD Odd */ + x = 0; + do { + pie->Digit[x++] = IBuf[Octet + Off] & 0x0f; + if (Q931MoreIE()) { + pie->Digit[x] = (IBuf[Octet + Off] >> 4) & 0x0f; + } + x++; + Off++; + } while (Q931MoreIE()); + } else if (pie->Encoding == 2) { /* IA5 */ + x = 0; + do { + pie->Digit[x++] = IBuf[Octet + Off] & 0x7f; + Off++; + } while (Q931MoreIE()); + } else { + /* Binary encoding type unkown */ + Q931SetError(pTrunk, Q931E_GENERIC_DIGITS, Octet, Off); + return Q931E_GENERIC_DIGITS; + } + + Q931IESizeTest(Q931E_GENERIC_DIGITS); + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_CallingSub) + x - 1; + pie->Size = (L3UCHAR)(sizeof(Q931ie_CallingSub) + x - 1); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_GenericDigits + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ + +L3INT Q931Pie_GenericDigits(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + OBuf[(*Octet)++] = (Q931ie_GENERIC_DIGITS & 0xFF); + OBuf[(*Octet)++] = 0; + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Uie_ChangeStatus + + Parameters: pIE[OUT] ptr to Information Element id. + IBuf[IN] ptr to a packed ie. + OBuf[OUT] ptr to buffer for Unpacked ie. + IOff[IN\OUT] Input buffer offset + OOff[IN\OUT] Output buffer offset + + Ibuf and OBuf points directly to buffers. The IOff and OOff + must be updated, but are otherwise not used in the ie unpack. + + Return Value: Error Message + +*****************************************************************************/ +L3INT Q931Uie_ChangeStatus(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff) +{ + Q931ie_ChangeStatus *pie = (Q931ie_ChangeStatus*)OBuf; + ie *pIE = &pMsg->ChangeStatus; + L3INT Off = 0; + L3INT Octet = 0; + L3INT IESize; + + *pIE = 0; + + pie->IEId = IBuf[Octet++]; + + /* Octet 2 */ + IESize = IBuf[Octet++]; + + /* Octet 3 */ + pie->Preference = (IBuf[Octet + Off] >> 6) & 0x01; + pie->Spare = IBuf[Octet + Off] & 0x38; + pie->NewStatus = IBuf[Octet + Off] & 0x07; + Octet++; + + Q931SetIE(*pIE, *OOff); + + *IOff = (*IOff) + Octet + Off; + *OOff = (*OOff) + sizeof(Q931ie_ChangeStatus); + pie->Size = sizeof(Q931ie_ChangeStatus); + + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pie_ChangeStatus + + Parameters: IBuf[IN] Ptr to struct. + OBuf[OUT] Ptr tp packed output buffer. + Octet[IN/OUT] Offset into OBuf. + + Return Value: Error code, 0 = OK + +*****************************************************************************/ +L3INT Q931Pie_ChangeStatus(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + Q931ie_ChangeStatus *pIE = (Q931ie_ChangeStatus*)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Beg = *Octet; + L3INT li; + + OBuf[(*Octet)++] = Q931ie_CHANGE_STATUS; + li = (*Octet)++; + + /* Octet 3 */ + OBuf[(*Octet)++] = 0x80 | pIE->NewStatus | ((pIE->Preference & 0x01) << 6); + + OBuf[li] = (L3UCHAR)((*Octet) - Beg) - 2; + return rc; +} + + + +L3INT Q931Uie_Generic(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff) +{ + L3INT Octet = 0; + L3UCHAR id = 0; + + /* id */ + id = IBuf[Octet++]; + + /* Length */ + Octet += IBuf[Octet]; + Octet++; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Discarding IE %#hhx with length %d\n", id, Octet - 2); + + *IOff += Octet; + return Q931E_NO_ERROR; +} + +L3INT Q931Pie_Generic(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet) +{ + /* do nothing */ + return Q931E_NO_ERROR; +} diff --git a/libs/openzap/src/isdn/Q931mes.c b/libs/openzap/src/isdn/Q931mes.c new file mode 100644 index 0000000000..8e34a170e1 --- /dev/null +++ b/libs/openzap/src/isdn/Q931mes.c @@ -0,0 +1,1870 @@ +/***************************************************************************** + + FileName: Q931mes.c + + Contents: Pack/Unpack functions. These functions will unpack a Q931 + message from the bit packed original format into structs + that contains variables sized by the user. It will also pack + the struct back into a Q.931 message as required. + + See q931.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +/** + * Q931MesgHeader + * \brief Create Q.931 Message header + */ +L3INT Q931MesgHeader(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *mes, L3UCHAR *OBuf, L3INT Size, L3INT *IOff) +{ + L3INT Octet = *IOff; + + Q931Log(pTrunk, Q931_LOG_DEBUG, "Creating Q.931 Message Header:\n ProtDisc %d (%#x), CRV %d (%#x), CRVflag: %d (%#x), MesType: %d (%#x)\n", + mes->ProtDisc, mes->ProtDisc, mes->CRV, mes->CRV, mes->CRVFlag, mes->CRVFlag, mes->MesType, mes->MesType); + + OBuf[Octet++] = mes->ProtDisc; /* Protocol discriminator */ + if (!Q931_IS_BRI(pTrunk)) { + OBuf[Octet++] = 2; /* length is 2 octets */ + OBuf[Octet++] = (L3UCHAR)((mes->CRV >> 8) & 0x7f) | ((mes->CRVFlag << 7) & 0x80); /* msb */ + OBuf[Octet++] = (L3UCHAR) (mes->CRV & 0xff); /* lsb */ + } else { + OBuf[Octet++] = 1; /* length is 1 octet */ + OBuf[Octet++] = (L3UCHAR) (mes->CRV & 0x7f) | ((mes->CRVFlag << 7) & 0x80); /* CRV & flag */ + } + OBuf[Octet++] = mes->MesType; /* message header */ + + *IOff = Octet; + return 0; +} + + +/***************************************************************************** + + Function: Q931Umes_Alerting + +*****************************************************************************/ + +L3INT Q931Umes_Alerting(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Alerting + +*****************************************************************************/ +L3INT Q931Pmes_Alerting(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_CallProceeding + +*****************************************************************************/ +L3INT Q931Umes_CallProceeding(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_CallProceeding + +*****************************************************************************/ +L3INT Q931Pmes_CallProceeding(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_CongestionControl + +*****************************************************************************/ +L3INT Q931Umes_CongestionControl(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(mes); + NoWarning(IBuf); + + return RetCode; +} + + +/***************************************************************************** + + Function: Q931Pmes_CongestionControl + +*****************************************************************************/ +L3INT Q931Pmes_CongestionControl(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + *OSize = 0; + return RetCode; +} + +/***************************************************************************** + + Function: Q931Umes_Connect + +*****************************************************************************/ +L3INT Q931Umes_Connect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_SIGNAL: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_CONNECTED_NUMBER: /* not actually used, seen while testing BRI PTMP TE */ + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + + default: + Q931Log(pTrunk, Q931_LOG_ERROR, "Illegal IE %#hhx in Connect Message\n", IBuf[IOff]); + + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Connect + +*****************************************************************************/ +L3INT Q931Pmes_Connect(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ConnectAck + +*****************************************************************************/ +L3INT Q931Umes_ConnectAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + + +/***************************************************************************** + + Function: Q931Pmes_ConnectAck + +*****************************************************************************/ +L3INT Q931Pmes_ConnectAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Disconnect + +*****************************************************************************/ +L3INT Q931Umes_Disconnect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + case Q931ie_FACILITY: + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Disconnect + +*****************************************************************************/ +L3INT Q931Pmes_Disconnect(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Information + +*****************************************************************************/ +L3INT Q931Umes_Information(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_SENDING_COMPLETE: + case Q931ie_DISPLAY: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_CALLED_PARTY_NUMBER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Information + +*****************************************************************************/ +L3INT Q931Pmes_Information(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)(pMes->SendComplete & 0x00ff); + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Notify + +*****************************************************************************/ +L3INT Q931Umes_Notify(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_NOTIFICATION_INDICATOR: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Notify + +*****************************************************************************/ +L3INT Q931Pmes_Notify(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Progress + +*****************************************************************************/ +L3INT Q931Umes_Progress(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CAUSE: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Progress + +*****************************************************************************/ +L3INT Q931Pmes_Progress(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Release + +*****************************************************************************/ +L3INT Q931Umes_Release(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Release + +*****************************************************************************/ +L3INT Q931Pmes_Release(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ReleaseComplete + +*****************************************************************************/ +L3INT Q931Umes_ReleaseComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + case Q931ie_USER_USER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_ReleaseComplete + +*****************************************************************************/ +L3INT Q931Pmes_ReleaseComplete(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Restart + +*****************************************************************************/ +L3INT Q931Umes_Restart(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_DISPLAY: + case Q931ie_RESTART_INDICATOR: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Restart + +*****************************************************************************/ +L3INT Q931Pmes_Restart(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* ChanID */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* RestartInd */ + if (Q931IsIEPresent(pMes->RestartInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_RESTART_INDICATOR](pTrunk, Q931GetIEPtr(pMes->RestartInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_RestartAck + +*****************************************************************************/ +L3INT Q931Umes_RestartAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size) +{ + Q931mes_Generic *mes = (Q931mes_Generic*)OBuf; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_DISPLAY: + case Q931ie_RESTART_INDICATOR: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_RestartAck + +*****************************************************************************/ +L3INT Q931Pmes_RestartAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* ChanID */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* RestartInd */ + if (Q931IsIEPresent(pMes->RestartInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_RESTART_INDICATOR](pTrunk, Q931GetIEPtr(pMes->RestartInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Resume + +*****************************************************************************/ +L3INT Q931Umes_Resume(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CALL_IDENTITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Resume + +*****************************************************************************/ +L3INT Q931Pmes_Resume(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Call Identity */ + if (Q931IsIEPresent(pMes->CallID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALL_IDENTITY](pTrunk, Q931GetIEPtr(pMes->CallID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ResumeAck + +*****************************************************************************/ +L3INT Q931Umes_ResumeAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + + +/***************************************************************************** + + Function: Q931Pmes_ResumeAck + +*****************************************************************************/ +L3INT Q931Pmes_ResumeAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ResumeReject + +*****************************************************************************/ +L3INT Q931Umes_ResumeReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + + +/***************************************************************************** + + Function: Q931Pmes_ResumeReject + +*****************************************************************************/ +L3INT Q931Pmes_ResumeReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +L3INT Q931Umes_Segment(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT OOff) +{ + L3INT i = IOff; + + return IOff - i; +} + +L3INT Q931Pmes_Segment(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + *OSize = 0; + return RetCode; +} + +/***************************************************************************** + + Function: Q931Umes_Setup + +*****************************************************************************/ +L3INT Q931Umes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT ir = 0; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_SENDING_COMPLETE: + IOff++; + break; + + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_NETWORK_SPECIFIC_FACILITIES: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_SIGNAL: + case Q931ie_CALLING_PARTY_NUMBER: + case Q931ie_CALLING_PARTY_SUBADDRESS: + case Q931ie_CALLED_PARTY_NUMBER: + case Q931ie_CALLED_PARTY_SUBADDRESS: + case Q931ie_TRANSIT_NETWORK_SELECTION: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_FACILITY: + case Q931ie_USER_USER: + case Q931ie_REDIRECTING_NUMBER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + + case Q931ie_REPEAT_INDICATOR: + if (ir < 2) { + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + ir++; + } else { + return Q931E_ILLEGAL_IE; + } + break; + + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Setup + + Decription: Pack a Q931mes_Generic into a real Q.931 message. The user will + set up a SETUP message and issue this to the stack where it + is processed by Q931ProcSetup that processes and validates + it before it actually sends it out. This function is called + to compute the real Q.931 message. + + Parameters: IBuf[IN] Ptr to un-packed struct + ISize[IN] Size of input buffer (unpacked message). + OBuf[OUT] Ptr to packed 'octet' wise message. + OSize[OUT] Size of packed message. + + Called By: Q931ProcSetup + +*****************************************************************************/ +L3INT Q931Pmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)Q931ie_SENDING_COMPLETE & 0xff; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->RepeatInd)) { + OBuf[Octet++] = (L3UCHAR)Q931ie_REPEAT_INDICATOR & 0xff; + } + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } else { + rc = Q931E_BEARERCAP; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Network spesific facilities */ + if (Q931IsIEPresent(pMes->NetFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_NETWORK_SPECIFIC_FACILITIES](pTrunk, Q931GetIEPtr(pMes->NetFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Number */ + if (Q931IsIEPresent(pMes->CallingNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CallingNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Subaddress */ + if (Q931IsIEPresent(pMes->CallingSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CallingSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called party subaddress */ + if (Q931IsIEPresent(pMes->CalledSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CalledSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Transit network selection */ + if (Q931IsIEPresent(pMes->TransNetSel)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_TRANSIT_NETWORK_SELECTION](pTrunk, Q931GetIEPtr(pMes->TransNetSel,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->LLRepeatInd)) { + rc = Q931E_UNKNOWN_IE;/* TODO */ + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_SetupAck + +*****************************************************************************/ +L3INT Q931Umes_SetupAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_DISPLAY: + case Q931ie_SIGNAL: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_SetupAck + +*****************************************************************************/ +L3INT Q931Pmes_SetupAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Status + +*****************************************************************************/ +L3INT Q931Umes_Status(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_CALL_STATE: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + + +/***************************************************************************** + + Function: Q931Pmes_Status + +*****************************************************************************/ +L3INT Q931Pmes_Status(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Call State */ + if (Q931IsIEPresent(pMes->CallState)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALL_STATE](pTrunk, Q931GetIEPtr(pMes->CallState,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_StatusEnquiry + +*****************************************************************************/ +L3INT Q931Umes_StatusEnquiry(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_StatusEnquiry + +*****************************************************************************/ +L3INT Q931Pmes_StatusEnquiry(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_Suspend + +*****************************************************************************/ +L3INT Q931Umes_Suspend(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CALL_IDENTITY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Suspend + +*****************************************************************************/ +L3INT Q931Pmes_Suspend(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Call Identity */ + if (Q931IsIEPresent(pMes->CallID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALL_IDENTITY](pTrunk, Q931GetIEPtr(pMes->CallID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_SuspendAck + +*****************************************************************************/ +L3INT Q931Umes_SuspendAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_SuspendAck + +*****************************************************************************/ +L3INT Q931Pmes_SuspendAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_SuspendReject + +*****************************************************************************/ +L3INT Q931Umes_SuspendReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CAUSE: + case Q931ie_DISPLAY: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_SuspendReject + +*****************************************************************************/ +L3INT Q931Pmes_SuspendReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Cause */ + if (Q931IsIEPresent(pMes->Cause)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CAUSE](pTrunk, Q931GetIEPtr(pMes->Cause,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_UserInformation + +*****************************************************************************/ +L3INT Q931Umes_UserInformation(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT I, L3INT O) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(mes); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Pmes_UserInformation + +*****************************************************************************/ +L3INT Q931Pmes_UserInformation(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + *OSize = 0; + + return RetCode; +} + +/***************************************************************************** + + Function: Q931Umes_Service + +*****************************************************************************/ +L3INT Q931Umes_Service(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_CHANGE_STATUS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Service + +*****************************************************************************/ +L3INT Q931Pmes_Service(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Display */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + if (Q931IsIEPresent(pMes->ChangeStatus)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANGE_STATUS](pTrunk, Q931GetIEPtr(pMes->ChangeStatus,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} + +/***************************************************************************** + + Function: Q931Umes_ServiceAck + +*****************************************************************************/ +L3INT Q931Umes_ServiceAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + + while (IOff < Size) { + switch (IBuf[IOff]) { + case Q931ie_CHANNEL_IDENTIFICATION: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_CHANGE_STATUS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_ServiceAck + +*****************************************************************************/ +L3INT Q931Pmes_ServiceAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT rc = Q931E_NO_ERROR; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + if (Q931IsIEPresent(pMes->ChangeStatus)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANGE_STATUS](pTrunk, Q931GetIEPtr(pMes->ChangeStatus,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} diff --git a/libs/openzap/src/isdn/Q932mes.c b/libs/openzap/src/isdn/Q932mes.c new file mode 100644 index 0000000000..574499a241 --- /dev/null +++ b/libs/openzap/src/isdn/Q932mes.c @@ -0,0 +1,286 @@ +/***************************************************************************** + + FileName: Q932mes.c + + Contents: Q.932 Message Encoders/Decoders + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "Q931.h" + +/***************************************************************************** + + Function: Q932Umes_Facility + +*****************************************************************************/ + +L3INT Q932Umes_Facility(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Facility + +*****************************************************************************/ +L3INT Q932Pmes_Facility(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_Hold + +*****************************************************************************/ + +L3INT Q932Umes_Hold(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Hold + +*****************************************************************************/ +L3INT Q932Pmes_Hold(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_HoldAck + +*****************************************************************************/ + +L3INT Q932Umes_HoldAck(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_HoldAck + +*****************************************************************************/ +L3INT Q932Pmes_HoldAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_HoldReject + +*****************************************************************************/ + +L3INT Q932Umes_HoldReject(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_HoldReject + +*****************************************************************************/ +L3INT Q932Pmes_HoldReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_Register + +*****************************************************************************/ + +L3INT Q932Umes_Register(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Register + +*****************************************************************************/ +L3INT Q932Pmes_Register(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_Retrieve + +*****************************************************************************/ + +L3INT Q932Umes_Retrieve(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_Retrieve + +*****************************************************************************/ +L3INT Q932Pmes_Retrieve(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_RetrieveAck + +*****************************************************************************/ + +L3INT Q932Umes_RetrieveAck(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_RetrieveAck + +*****************************************************************************/ +L3INT Q932Pmes_RetrieveAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} + +/***************************************************************************** + + Function: Q932Umes_RetrieveReject + +*****************************************************************************/ + +L3INT Q932Umes_RetrieveReject(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT OOff = 0; + + /* TODO */ + + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: Q931Pmes_RetrieveReject + +*****************************************************************************/ +L3INT Q932Pmes_RetrieveReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3BOOL RetCode = L3FALSE; + + NoWarning(OBuf); + NoWarning(IBuf); + + return RetCode; +} diff --git a/libs/openzap/src/isdn/include/5ESS.h b/libs/openzap/src/isdn/include/5ESS.h new file mode 100644 index 0000000000..dd364ed537 --- /dev/null +++ b/libs/openzap/src/isdn/include/5ESS.h @@ -0,0 +1,103 @@ +/****************************************************************************** + + FileName: 5ESS.h + + Contents: Header and definition for the AT&T 5ESS ISDN dialect. The + header contains the following parts: + + - Definition of codes + - Definition of information elements (5ESSie_). + - Definition of messages (5ESSmes_). + - Function prototypes. + + Description: The AT&T 5ESS ISDN dialect here covers ???? + + Related Files: 5ESS.h AT&T 5ESS ISDN Definitions + 5ESSie.c AT&T 5ESS ISDN IE encoders/coders (not extant yet) + See Q931ie.c for IE encoders/coders + 5ESSStateTE.c AT&T 5ESS ISDN TE State Engine + 5ESSStateNT.c AT&T 5ESS ISDN NT State Engine + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + Copyright (c) 2007, Michael S. Collins, All rights reserved. + email:mcollins@fcnetwork.com + + 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 Case Labs, Ltd nor the names of its 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 _5ESS_NL +#define _5ESS_NL + +#include "Q931.h" + +/***************************************************************************** + + Q.931 Message codes + Only 5ESS specific message and ie types + here the rest are inherited from Q931.h + +*****************************************************************************/ + + +/***************************************************************************** + + Q.931 Message Pack/Unpack functions. Implemented in 5ESSmes.c + Note: Because C variables may not begin with numeric digit, all identifiers + are prefixed with "ATT5ESS" instead of a bare "5ESS" + +*****************************************************************************/ +L3INT ATT5ESSUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT ATT5ESSPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT ATT5ESSUmes_SetupAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT ATT5ESSPmes_SetupAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +L3INT ATT5ESSUmes_0x07(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT ATT5ESSPmes_0x07(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT ATT5ESSUmes_0x0f(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT ATT5ESSPmes_0x0f(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + + + +/***************************************************************************** + + Q.931 Process Function Prototyping. Implemented in 5ESSStateTE.c + +*****************************************************************************/ +L3INT ATT5ESSProc0x0fTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom); +L3INT ATT5ESSProc0x07TE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom); + + +void ATT5ESSCreateTE(L3UCHAR i); +void ATT5ESSCreateNT(L3UCHAR i); + +#endif /* _5ESS_NL */ diff --git a/libs/openzap/src/isdn/include/DMS.h b/libs/openzap/src/isdn/include/DMS.h new file mode 100644 index 0000000000..605a61aa6e --- /dev/null +++ b/libs/openzap/src/isdn/include/DMS.h @@ -0,0 +1,91 @@ +/****************************************************************************** + + FileName: national.h + + Contents: Header and definition for the National ISDN dialect. The + header contents the following parts: + - Definition of codes + - Definition of information elements (nationalie_). + - Definition of messages (nationalmes_). + - Function prototypes. + + Description: The National ISDN dialect here covers ???? + + Related Files: national.h National ISDN Definitions + nationalie.c National ISDN IE encoders/coders + nationalStateTE.c National ISDN TE State Engine + nationalStateNT.c National ISDN NT State Engine + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 _DMS_NL +#define _DMS_NL + +#include "Q931.h" + +/***************************************************************************** + + Q.931 Message codes + Only National specific message and ie types + here the rest are inherited from national.h + +*****************************************************************************/ + + +/***************************************************************************** + + Q.931 Message Pack/Unpack functions. Implemented in nationalmes.c + +*****************************************************************************/ +L3INT DMSUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT DMSPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT DMSUmes_0x07(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT DMSPmes_0x07(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT DMSUmes_0x0f(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT DMSPmes_0x0f(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +/***************************************************************************** + + Q.931 Process Function Prototyping. Implemented in nationalStateTE.c + +*****************************************************************************/ + +L3INT DMSProc0x0fTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom); +L3INT DMSProc0x07TE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * buf, L3INT iFrom); + +void DMSCreateTE(L3UCHAR i); +void DMSCreateNT(L3UCHAR i); + +#endif /* _DMS_NL */ diff --git a/libs/openzap/src/isdn/include/Q921.h b/libs/openzap/src/isdn/include/Q921.h new file mode 100644 index 0000000000..cb21a157ea --- /dev/null +++ b/libs/openzap/src/isdn/include/Q921.h @@ -0,0 +1,227 @@ +/***************************************************************************** + + FileName: q921.h + + Description: Contains headers of a Q.921 protocol. + + Note: This header file is the only include file that should be + acessed by users of the Q.921 stack. + + Interface: The Q.921 stack contains 2 layers. + + - One interface layer. + - One driver layer. + + The interface layer contains the interface functions required + for a layer 2 stack to be able to send and receive messages. + + The driver layer will simply feed bytes into the ship as + required and queue messages received out from the ship. + + Q921TimeTick The Q.921 like any other blackbox + modules contains no thread by it's own + and must therefore be called regularly + by an external 'thread' to do maintenance + etc. + + Q921Rx32 Receive message from layer 3. Called by + the layer 3 stack to send a message. + + + NOTE: The following are not yet implemented + + OnQ921Error Function called every if an error is + detected. + + OnQ921Log Function called if logging is active. + + + Maintenance/Configuration interface + Logging + DL_ message passing to layer 3 + Timers + Api commands to tell 921 to stop and start for a trunk + + Created: 27.dec.2000/JVB + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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. + +*****************************************************************************/ + +/**************************************************************************** + * Changes: + * + * - June,July 2008: Stefan Knoblich : + * Add PTMP TEI management + * Add timers + * Add retransmit counters + * Add logging + * Various cleanups + * + ****************************************************************************/ + +#ifndef _Q921 +#define _Q921 + +#define Q921MAXHDLCSPACE 3000 +#define L2UCHAR unsigned char /* Min 8 bit */ +#define L2USHORT unsigned short /* 16 bit */ +#define L2INT int /* Min 16 bit signed */ +#define L2ULONG unsigned long /* Min 32 bit */ +#define L2TRUNK Q921Data_t * + +#define Q921_TEI_BCAST 127 +#define Q921_TEI_MAX Q921_TEI_BCAST + +#define Q921_TEI_DYN_MIN 64 +#define Q921_TEI_DYN_MAX 126 + + +typedef enum /* Network/User Mode */ +{ + Q921_TE=0, /* 0 : User Mode */ + Q921_NT=1 /* 1 : Network Mode */ +} Q921NetUser_t; + +typedef enum /* Type of connection */ +{ + Q921_PTP=0, /* 0 : Point-To-Point */ + Q921_PTMP=1 /* 1 : Point-To-Multipoint */ +} Q921NetType_t; + +typedef enum +{ + Q921_LOG_NONE = -1, + Q921_LOG_EMERG = 0, + Q921_LOG_ALERT, + Q921_LOG_CRIT, + Q921_LOG_ERROR, + Q921_LOG_WARNING, + Q921_LOG_NOTICE, + Q921_LOG_INFO, + Q921_LOG_DEBUG +} Q921LogLevel_t; + + +/* + * Messages for L2 <-> L3 communication + */ +typedef enum { + Q921_DL_ESTABLISH = 0, + Q921_DL_ESTABLISH_CONFIRM, + Q921_DL_RELEASE, + Q921_DL_RELEASE_CONFIRM, + Q921_DL_DATA, + Q921_DL_UNIT_DATA +} Q921DLMsg_t; + +typedef int (*Q921Tx21CB_t) (void *, L2UCHAR *, L2INT); +typedef int (*Q921Tx23CB_t) (void *, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR *, L2INT); +typedef int (*Q921LogCB_t) (void *, Q921LogLevel_t, char *, L2INT); + +struct Q921_Link; + +typedef struct Q921Data +{ + L2INT initialized; + + L2UCHAR sapi; /*!< User assigned SAPI */ + L2UCHAR tei; /*!< User assigned TEI value */ + + L2INT Q921HeaderSpace; + Q921NetUser_t NetUser; + Q921NetType_t NetType; + + struct Q921_Link *context; /*!< per-TEI / link context space */ + + /* timers */ + L2ULONG T202; /*!< PTMP TE mode TEI retransmit timer */ + L2ULONG T200Timeout; + L2ULONG T201Timeout; + L2ULONG T202Timeout; + L2ULONG T203Timeout; + + L2ULONG TM01Timeout; + + /* counters */ + L2ULONG N200Limit; /*!< max retransmit */ + + L2ULONG N202; /*!< PTMP TE mode retransmit counter */ + L2ULONG N202Limit; /*!< PTMP TE mode max retransmit */ + + L2ULONG N201Limit; /*!< max number of octets */ + L2ULONG k; /*!< max number of unacknowledged I frames */ + + /* callbacks and callback data pointers */ + Q921Tx21CB_t Q921Tx21Proc; + Q921Tx23CB_t Q921Tx23Proc; + void *PrivateData21; + void *PrivateData23; + + /* logging */ + Q921LogLevel_t loglevel; /*!< trunk loglevel */ + Q921LogCB_t Q921LogProc; /*!< log callback procedure */ + void *PrivateDataLog; /*!< private data pointer for log proc */ + + /* tei mgmt */ + L2UCHAR tei_map[Q921_TEI_MAX]; /*!< */ + + L2UCHAR HDLCInQueue[Q921MAXHDLCSPACE]; /*!< HDLC input queue */ +} Q921Data_t; + +/* + * Public functions + */ +int Q921_InitTrunk(L2TRUNK trunk, + L2UCHAR sapi, + L2UCHAR tei, + Q921NetUser_t NetUser, + Q921NetType_t NetType, + L2INT hsize, + Q921Tx21CB_t cb21, + Q921Tx23CB_t cb23, + void *priv21, + void *priv23); +int Q921Start(L2TRUNK trunk); +int Q921Stop(L2TRUNK trunk); + +void Q921SetLogCB(L2TRUNK trunk, Q921LogCB_t func, void *priv); +void Q921SetLogLevel(L2TRUNK trunk, Q921LogLevel_t level); + +int Q921Rx12(L2TRUNK trunk); +int Q921Rx32(L2TRUNK trunk, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR * Mes, L2INT Size); + +int Q921QueueHDLCFrame(L2TRUNK trunk, L2UCHAR *b, L2INT size); + +void Q921SetGetTimeCB(L2ULONG (*callback)(void)); +void Q921TimerTick(L2TRUNK trunk); + +#endif diff --git a/libs/openzap/src/isdn/include/Q921priv.h b/libs/openzap/src/isdn/include/Q921priv.h new file mode 100644 index 0000000000..d2d838c399 --- /dev/null +++ b/libs/openzap/src/isdn/include/Q921priv.h @@ -0,0 +1,321 @@ +/***************************************************************************** + + FileName: Q921priv.h + + Description: Private declarations + + Created: 08.Aug.2008/STKN + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2008, Stefan Knoblich, axsentis GmbH. All rights reserved. + email:s.knoblich@axsentis.de + + 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 Case Labs, Ltd nor the names of its 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 _Q921_PRIV_H_ +#define _Q921_PRIV_H_ + +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#if (_MSC_VER >= 1400) /* VC8+ */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif +#endif +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#ifndef strncasecmp +#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#endif + +typedef enum /* Q.921 States */ +{ + Q921_STATE_STOPPED = 0, /* Trunk stopped */ + Q921_STATE_TEI_UNASSIGNED = 1, /* TEI unassigned */ + Q921_STATE_TEI_AWAITING, /* Assign awaiting TEI */ + Q921_STATE_TEI_ESTABLISH, /* Establish awaiting TEI */ + Q921_STATE_TEI_ASSIGNED, /* TEI assigned */ + Q921_STATE_AWAITING_ESTABLISHMENT, /* Awaiting establishment */ + Q921_STATE_AWAITING_RELEASE, /* Awaiting release */ + Q921_STATE_MULTIPLE_FRAME_ESTABLISHED, /* Multiple frame established */ + Q921_STATE_TIMER_RECOVERY /* Timer recovery */ +} Q921State_t; + +/* + * Flags + */ +enum Q921_Flags { + Q921_FLAG_L3_INITIATED = (1 << 0), + + Q921_FLAG_UI_FRAME_QUEUED = (1 << 1), + Q921_FLAG_I_FRAME_QUEUED = (1 << 2), + + Q921_FLAG_ACK_PENDING = (1 << 3), + Q921_FLAG_REJECT = (1 << 4), + + Q921_FLAG_RECV_BUSY = (1 << 5), + Q921_FLAG_PEER_RECV_BUSY = (1 << 6) +}; + +#define Q921_SET_FLAG(x, f) ((x)->flags |= f) +#define Q921_CHECK_FLAG(x, f) ((x)->flags & f) +#define Q921_CLEAR_FLAG(x, f) ((x)->flags &= ~f) + + +/* + * dynamic TEI handling + */ +#define Q921_SAPI_TEI 63 /* SAPI for all TEI Messages */ +#define Q921_LAYER_ENT_ID_TEI 0x0f /* UN Layer Management Entity ID for TEI Mgmt */ +#define Q921_LAYER_ENT_ID_Q931 0x08 /* Q.931 Layer Management Entity ID */ + + +typedef enum { + Q921_TEI_ID_REQUEST = 1, + Q921_TEI_ID_ASSIGNED, + Q921_TEI_ID_DENIED, + Q921_TEI_ID_CHECKREQ, + Q921_TEI_ID_CHECKRESP, + Q921_TEI_ID_REMOVE, + Q921_TEI_ID_VERIFY +} Q921TeiMessageType_t; + + +/** + * Per-Datalink context + */ +struct Q921_Link { + L2UCHAR tei; /*!< This endpoint's TEI */ + + L2UCHAR va; + L2UCHAR vs; + L2UCHAR vr; + + L2INT flags; + Q921State_t state; + + L2ULONG N202; /*!< PTMP TE mode retransmit counter */ + L2ULONG N200; /*!< retransmit counter (per-TEI in PTMP NT mode) */ + + L2ULONG TM01; /*!< Datalink inactivity disconnect timer */ + + L2ULONG T200; + L2ULONG T201; /*!< PTMP NT mode timer */ + L2ULONG T203; + + L2USHORT ri; /*!< random id for TEI request mgmt */ + + /* I + UI Frame queue */ + L2UCHAR UIFrameQueue[Q921MAXHDLCSPACE]; + L2UCHAR IFrameQueue[Q921MAXHDLCSPACE]; + L2UCHAR IFrameResendQueue[Q921MAXHDLCSPACE]; +}; + + +#define Q921_LINK_CONTEXT(tr, tei) \ + (Q921_IS_PTMP_NT(tr) && tei != Q921_TEI_BCAST) ? ((struct Q921_Link *)&(tr)->context[tei]) : (tr)->context + +#define Q921_TRUNK_CONTEXT(tr) \ + (tr)->context + +#define Q921_LOGBUFSIZE 2000 +#define INITIALIZED_MAGIC 42 + +/* + * Helper macros + */ +#define Q921_INC_COUNTER(x) (x = (x + 1) % 128) +#define Q921_DEC_COUNTER(x) (x = (x) ? (x - 1) : 127) + +#define Q921_UFRAME_HEADER_SIZE 3 +#define Q921_UFRAME_DATA_OFFSET(tr) ((tr)->Q921HeaderSpace + Q921_UFRAME_HEADER_SIZE) + +#define Q921_SFRAME_HEADER_SIZE 4 +#define Q921_SFRAME_DATA_OFFSET(tr) ((tr)->Q921HeaderSpace + Q921_SFRAME_HEADER_SIZE) + +#define Q921_IFRAME_HEADER_SIZE 4 +#define Q921_IFRAME_DATA_OFFSET(tr) ((tr)->Q921HeaderSpace + Q921_IFRAME_HEADER_SIZE) + +#define Q921_IS_TE(x) ((x)->NetUser == Q921_TE) +#define Q921_IS_NT(x) ((x)->NetUser == Q921_NT) + +#define Q921_IS_STOPPED(tr) ((tr)->state == Q921_STATE_STOPPED) + +/* TODO: rework this one */ +#define Q921_IS_READY(tr) ((tr)->state >= Q921_STATE_TEI_ASSIGNED) + +#define Q921_IS_PTMP(x) ((x)->NetType == Q921_PTMP) +#define Q921_IS_PTMP_TE(x) ((x)->NetType == Q921_PTMP && (x)->NetUser == Q921_TE) +#define Q921_IS_PTMP_NT(x) ((x)->NetType == Q921_PTMP && (x)->NetUser == Q921_NT) + +#define Q921_IS_PTP(x) ((x)->NetType == Q921_PTP) +#define Q921_IS_PTP_TE(x) ((x)->NetType == Q921_PTP && (x)->NetUser == Q921_TE) +#define Q921_IS_PTP_NT(x) ((x)->NetType == Q921_PTP && (x)->NetUser == Q921_NT) + +/* Make life a little easier */ +#define Q921_COMMAND(x) ((x)->NetUser == Q921_TE ? 0 : 1) +#define Q921_RESPONSE(x) ((x)->NetUser == Q921_TE ? 1 : 0) + +#define Q921_IS_COMMAND(tr, x) ((x) == (Q921_IS_TE(tr) ? 1 : 0)) +#define Q921_IS_RESPONSE(tr, x) ((x) == (Q921_IS_TE(tr) ? 0 : 1)) + + +/******************************************************************************* + * Private functions + *******************************************************************************/ + +/* + * L1 / L2 Interface + */ +static int Q921Tx21Proc(L2TRUNK trunk, L2UCHAR *Msg, L2INT size); +static int Q921Tx23Proc(L2TRUNK trunk, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR *Msg, L2INT size); + + +/* + * Timers + */ +static L2ULONG Q921GetTime(void); + +static void Q921T200TimerStart(L2TRUNK trunk, L2UCHAR tei); +static void Q921T200TimerStop(L2TRUNK trunk, L2UCHAR tei); +static void Q921T200TimerReset(L2TRUNK trunk, L2UCHAR tei); +static void Q921T200TimerExpire(L2TRUNK trunk, L2UCHAR tei); + +static void Q921T201TimerStart(L2TRUNK trunk, L2UCHAR tei); +static void Q921T201TimerStop(L2TRUNK trunk, L2UCHAR tei); +/* static void Q921T201TimerReset(L2TRUNK trunk, L2UCHAR tei); - Unused for now */ +static void Q921T201TimerExpire(L2TRUNK trunk, L2UCHAR tei); + +static void Q921T202TimerStart(L2TRUNK trunk); +static void Q921T202TimerStop(L2TRUNK trunk); +static void Q921T202TimerReset(L2TRUNK trunk); +static void Q921T202TimerExpire(L2TRUNK trunk); + +static void Q921T203TimerStart(L2TRUNK trunk, L2UCHAR tei); +static void Q921T203TimerStop(L2TRUNK trunk, L2UCHAR tei); +static void Q921T203TimerReset(L2TRUNK trunk, L2UCHAR tei); +static void Q921T203TimerExpire(L2TRUNK trunk, L2UCHAR tei); + +static void Q921TM01TimerStart(L2TRUNK trunk, L2UCHAR tei); +/* static void Q921TM01TimerStop(L2TRUNK trunk, L2UCHAR tei); - Unused for now */ +static void Q921TM01TimerReset(L2TRUNK trunk, L2UCHAR tei); +/* static void Q921TM01TimerExpire(L2TRUNK trunk, L2UCHAR tei); - Unused for now */ + +/* + * Frame encoding + */ +static int Q921SendS(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR sv, L2UCHAR *mes, L2INT size); +static int Q921SendU(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR m, L2UCHAR *mes, L2INT size); + +static int Q921SendRNR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendREJ(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendSABME(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendDM(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendDISC(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendUA(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); +static int Q921SendUN(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf, L2UCHAR *mes, L2INT size); +static int Q921SendRR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf); + +/* + * Frame decoding + */ +static int Q921ProcIFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcSFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcUFrame(L2TRUNK trunk, L2UCHAR *mes, L2INT size); + +static int Q921ProcSABME(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcDM(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcUA(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcDISC(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcRR(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcRNR(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921ProcREJ(L2TRUNK trunk, L2UCHAR *mes, L2INT size); + + +/* + * (Common) procedures defined in the Q.921 SDL + */ +static int Q921SendEnquiry(L2TRUNK trunk, L2UCHAR tei); +static int Q921SendEnquiryResponse(L2TRUNK trunk, L2UCHAR tei); +static void Q921ResetExceptionConditions(L2TRUNK trunk, L2UCHAR tei); +static int Q921EstablishDataLink(L2TRUNK trunk, L2UCHAR tei); +static int Q921NrErrorRecovery(L2TRUNK trunk, L2UCHAR tei); +static int Q921InvokeRetransmission(L2TRUNK trunk, L2UCHAR tei, L2UCHAR nr); +static int Q921AcknowledgePending(L2TRUNK trunk, L2UCHAR tei); +/* +static int Q921SetReceiverBusy(L2TRUNK trunk); +static int Q921ClearReceiverBusy(L2TRUNK trunk); +*/ + +/* + * Queueing + */ +static int Q921SendQueuedIFrame(L2TRUNK trunk, L2UCHAR tei); +static int Q921EnqueueI(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR *mes, L2INT size); + +/* + * TEI management + */ +static int Q921TeiSendAssignRequest(L2TRUNK trunk); +static int Q921TeiProcAssignResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiSendVerifyRequest(L2TRUNK trunk); +static int Q921TeiProcCheckRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiProcRemoveRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiProcAssignRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiProcCheckResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiProcVerifyRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size); +static int Q921TeiSendRemoveRequest(L2TRUNK trunk, L2UCHAR tei); +static int Q921TeiSendDenyResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri); +static int Q921TeiSendAssignedResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri); +static int Q921TeiSendCheckRequest(L2TRUNK trunk, L2UCHAR tei); + +/* + * Logging + */ +static int Q921Log(L2TRUNK trunk, Q921LogLevel_t level, const char *fmt, ...); +static int Q921LogMesg(L2TRUNK trunk, Q921LogLevel_t level, L2UCHAR received, L2UCHAR *mes, L2INT size, const char *fmt, ...); + +/* + * State handling + */ +static int Q921ChangeState(L2TRUNK trunk, Q921State_t state, L2UCHAR tei); + +#endif diff --git a/libs/openzap/src/isdn/include/Q931.h b/libs/openzap/src/isdn/include/Q931.h new file mode 100644 index 0000000000..0dbafd9f11 --- /dev/null +++ b/libs/openzap/src/isdn/include/Q931.h @@ -0,0 +1,1175 @@ +/****************************************************************************** + + FileName: Q931.h + + Contents: Header and definition for the ITU-T Q.931 stack. The + header contents the following parts: + + - Definition of codes + - Definition of information elements (Q931ie_). + - Definition of messages (Q931mes_). + - Definitian of variables (var_). + - Function prototypes. + + Description: The Q.931 stack provided here covers ITU-T Q.931 w/Q.932 + supplementary services for both PRI, BRI and variants. + The stack is generic and designed to deal with variants as + needed. + + The stack uses the following interface functions: + + - Q931Initialize Initialize the Q.931 stack. + - Q931Rx23 Receive a message from layer 2 + - Q931Tx32 Send a message to layer 2 + - Q931Rx43 Receive a message from layer 4 or above. + - Q931Tx34 Send a message to layer 4 or above. + - Q931TimeTick Periodical timer processing. + - Q931ErrorProc Callback for stack error message. + + The protocol is a module with no external dependencies and + can easely be ported to any operating system like Windows, + Linux, rtos and others. + + Related Files: Q931.h Q.931 Definitions + Q931.c Q.931 Interface Functions. + Q931api.c Low level L4 API functions. + + Q932.h Q.932 Suplementary Services + Q932mes.c Q.932 encoders/coders + + Q931mes.c Q.931 Message encoders/coders + Q931ie.c Q.931 IE encoders/coders + Q931StateTE.c Generic Q.931 TE State Engine + Q931StateNT.c Generic Q.931 NT State Engine + + Design Note 1: For each variant please add separate files starting with + the variant short-name as follows: + + .h Spesific headers needed. + mes.c Message encoders/decores. + ie.c IE encoders/decoders. + StateTE.c TE side state engine. + StateNT.c NT side state engine. + + Design Note 2: The stack is deliberatly made non-threading. Use 1 + thread per Trunk, but lock access from the timertick + and rx, tx functions. And make sure the callbacks only + dump messages to a queue, no time-consuming processing + inside stack processing. + + All stack processing is async 'fire and forget', meaning + that there are not, and should not be any time-consuming + processing within the stack-time. The best way to thread + a stack is to use one single thread that signal 5 queues. + + - Incoming L2 queue. + - Incoming L4 queue. + - Outgoing L2 queue. + - Outgoing L4 queue. + - Error/Trace queue. + + Design Note 3: DSP optimization. The L3 (Rx23) can be called directly + from a HDLC receiver without usage of queues for optimized + processing. But keep in mind that Q.931 calls Tx34 or Tx32 + as part of receiving a message from Layer 2. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 _Q931_NL +#define _Q931_NL + +/* uncomment the #define below to add x.25 support to the Q.931 */ +/* #define Q931_X25_SUPPORT */ + +#include "stdio.h" + +#ifdef _MSC_VER +#pragma warning(disable:4100) +#ifndef strcasecmp +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#endif +#endif +#include + + +/***************************************************************************** + + Enum helper macros + +*****************************************************************************/ +#define Q931_ENUM_NAMES(_NAME, _STRINGS) static const char * _NAME [] = { _STRINGS , NULL }; +#define Q931_STR2ENUM_P(_FUNC1, _FUNC2, _TYPE) _TYPE _FUNC1 (const char *name); const char * _FUNC2 (_TYPE type); +#define Q931_STR2ENUM(_FUNC1, _FUNC2, _TYPE, _STRINGS, _MAX) \ + _TYPE _FUNC1 (const char *name) \ + { \ + int i; \ + _TYPE t = _MAX ; \ + \ + for (i = 0; i < _MAX ; i++) { \ + if (!strcasecmp(name, _STRINGS[i])) { \ + t = (_TYPE) i; \ + break; \ + } \ + } \ + \ + return t; \ + } \ + const char * _FUNC2 (_TYPE type) \ + { \ + if (type > _MAX) { \ + type = _MAX; \ + } \ + return _STRINGS[(int)type]; \ + } \ + +/***************************************************************************** + + Error Codes + +*****************************************************************************/ +typedef enum { + Q931E_NO_ERROR = 0, + + Q931E_UNKNOWN_MESSAGE = -3001, + Q931E_ILLEGAL_IE = -3002, + Q931E_UNKNOWN_IE = -3003, + Q931E_BEARERCAP = -3004, + Q931E_HLCOMP = -3005, + Q931E_LLCOMP = -3006, + Q931E_INTERNAL = -3007, + Q931E_MISSING_CB = -3008, + Q931E_UNEXPECTED_MESSAGE = -3009, + Q931E_ILLEGAL_MESSAGE = -3010, + Q931E_TOMANYCALLS = -3011, + Q931E_INVALID_CRV = -3012, + Q931E_CALLID = -3013, + Q931E_CALLSTATE = -3014, + Q931E_CALLEDSUB = -3015, + Q931E_CALLEDNUM = -3016, + Q931E_CALLINGNUM = -3017, + Q931E_CALLINGSUB = -3018, + Q931E_CAUSE = -3019, + Q931E_CHANID = -3020, + Q931E_DATETIME = -3021, + Q931E_DISPLAY = -3022, + Q931E_KEYPADFAC = -3023, + Q931E_NETFAC = -3024, + Q931E_NOTIFIND = -3025, + Q931E_PROGIND = -3026, + Q931E_RESTARTIND = -3027, + Q931E_SEGMENT = -3028, + Q931E_SIGNAL = -3029, + Q931E_GENERIC_DIGITS = -3030 +} q931_error_t; + +/* The q931_error_t enum should be kept in sync with the q931_error_names array in Q931.c */ + +const char *q931_error_to_name(q931_error_t error); + +/***************************************************************************** + + Some speed optimization can be achieved by changing all variables to the + word size of your processor. A 32 bit processor has to do a lot of extra + work to read a packed 8 bit integer. Changing all fields to 32 bit integer + will result in usage of some extra space, but will speed up the stack. + + The stack has been designed to allow L3UCHAR etc. to be any size of 8 bit + or larger. + +*****************************************************************************/ + +#define L3UCHAR unsigned char /* Min 8 bit */ +#define L3USHORT unsigned short /* Min 16 bit unsigned */ +#define L3UINT unsigned int /* Min 16 bit unsigned */ +#define L3INT int /* Min 16 bit signed */ +#define L3ULONG unsigned long /* Min 32 bit */ +#define L3BOOL char /* Min 1 bit, valuse 0 & 1 only */ + +#define L3TRUE 1 +#define L3FALSE 0 + +/***************************************************************************** + + Global defines. + +*****************************************************************************/ + +typedef L3USHORT ie; /* Special data type to hold a dynamic */ + /* or optional information element as */ + /* part of a message struct. MSB = 1 */ + /* indicate that the ie is present, the */ + /* last 15 bits is an offset ( or the */ + /* value for single octet ) to the */ + /* struct holding the ie. Offset = 0 */ + /* is buf[1] etc. */ + /* ie == 0xffff indicates error */ + +/***************************************************************************** + + MAXTRUNKS sets how many physical trunks this system might have. This + number should be keept at a minimum since it will use global space. + + It is recommended that you leave MAXCHPERTRUNK as is + +*****************************************************************************/ + +#define Q931_LOGBUFSIZE 1024 /* size of logging buffer */ + +#define Q931L4BUF 1000 /* size of message buffer */ + +#define Q931L2BUF 300 /* size of message buffer */ + +#define Q931MAXTRUNKS 4 /* Total number of trunks that will be */ + /* processed by this instance of the */ + /* stack */ + +#define Q931MAXCHPERTRUNK 32 /* Number of channels per trunk. The */ + /* stack uses a static set of 32 */ + /* channels regardless if it is E1, T1 */ + /* or BRI that actually is used. */ + +#define Q931MAXCALLPERTRUNK (Q931MAXCHPERTRUNK * 2) + /* Number of max active CRV per trunk. */ + /* Q.931 can have more calls than there */ + /* are channels. */ + + +#define Q931_IS_BRI(x) ((x)->TrunkType == Q931_TrType_BRI || (x)->TrunkType == Q931_TrType_BRI_PTMP) +#define Q931_IS_PRI(x) (!Q931_IS_BRI(x)) + +#define Q931_IS_PTP(x) ((x)->TrunkType != Q931_TrType_BRI_PTMP) +#define Q931_IS_PTMP(X) ((x)->TrunkType == Q931_TrType_BRI_PTMP) + +#define Q931_BRI_MAX_CRV 127 +#define Q931_PRI_MAX_CRV 32767 + +/***************************************************************************** + + The following defines control the dialect switch tables and should only be + changed when a new dialect needs to be inserted into the stack. + + This stack uses an array of functions to know which function to call as + it receives a SETUP message etc. A new dialect can when choose to use + the proc etc. for standard Q.931 or insert a modified proc. + + This technique has also been used to distinguish between user and network + mode to make the code as easy to read and maintainable as possible. + + A message and IE index have been used to save space. These indexes allowes + the message or IE code to be used directly and will give back a new index + into the table. + +*****************************************************************************/ + +/* WARNING! Initialize Q931CreateDialectCB[] will NULL when increasing the */ +/* Q931MAXDLCT value to avoid Q931Initialize from crashing if one entry is */ +/* not used. */ +#define Q931MAXDLCT 8 /* Max dialects included in this */ + /* compile. User and Network count as */ + /* one dialect each. */ + +#define Q931MAXMES 128 /* Number of messages */ +#define Q931MAXIE 255 /* Number of IE */ +#define Q931MAXUSEDIE 50 /* Maximum number of ie types per Dialect */ +#define Q931MAXCODESETS 7 /* Maximum number of codests (by spec, 0-7) */ +#define Q931MAXSTATE 100 /* Size of state tables */ +#define Q931MAXTIMER 25 /* Maximum number of timers */ + +/***************************************************************************** + + Call States for ITU-T Q.931 TE (User Mode) + +*****************************************************************************/ + +#define Q931_U0 0 +#define Q931_U1 1 +#define Q931_U2 2 +#define Q931_U3 3 +#define Q931_U4 4 +#define Q931_U6 6 +#define Q931_U7 7 +#define Q931_U8 8 +#define Q931_U9 9 +#define Q931_U10 10 +#define Q931_U11 11 +#define Q931_U12 12 +#define Q931_U15 15 +#define Q931_U17 17 +#define Q931_U19 19 +#define Q931_U25 25 + +/***************************************************************************** + + Call States for ITU-T Q.931 NT (Network Mode) + +*****************************************************************************/ +#define Q931_N0 (0x0100 | 0) +#define Q931_N1 (0x0100 | 1) +#define Q931_N2 (0x0100 | 2) +#define Q931_N3 (0x0100 | 3) +#define Q931_N4 (0x0100 | 4) +#define Q931_N6 (0x0100 | 6) +#define Q931_N7 (0x0100 | 7) +#define Q931_N8 (0x0100 | 8) +#define Q931_N9 (0x0100 | 9) +#define Q931_N10 (0x0100 | 11) +#define Q931_N11 (0x0100 | 11) +#define Q931_N12 (0x0100 | 12) +#define Q931_N15 (0x0100 | 15) +#define Q931_N17 (0x0100 | 17) +#define Q931_N19 (0x0100 | 19) +#define Q931_N22 (0x0100 | 22) +#define Q931_N25 (0x0100 | 25) + +/***************************************************************************** + + Q.931 Message codes + +*****************************************************************************/ + +#define Q931mes_ALERTING 0x01 /* 0000 0001 */ +#define Q931mes_CALL_PROCEEDING 0x02 /* 0000 0010 */ +#define Q931mes_CONNECT 0x07 /* 0000 0111 */ +#define Q931mes_CONNECT_ACKNOWLEDGE 0x0f /* 0000 1111 */ +#define Q931mes_PROGRESS 0x03 /* 0000 0011 */ +#define Q931mes_SETUP 0x05 /* 0000 0101 */ +#define Q931mes_SETUP_ACKNOWLEDGE 0x0d /* 0000 1101 */ +#define Q931mes_RESUME 0x26 /* 0010 0110 */ +#define Q931mes_RESUME_ACKNOWLEDGE 0x2e /* 0010 1110 */ +#define Q931mes_RESUME_REJECT 0x22 /* 0010 0010 */ +#define Q931mes_SUSPEND 0x25 /* 0010 0101 */ +#define Q931mes_SUSPEND_ACKNOWLEDGE 0x2d /* 0010 1101 */ +#define Q931mes_SUSPEND_REJECT 0x21 /* 0010 0001 */ +#define Q931mes_USER_INFORMATION 0x20 /* 0010 0000 */ +#define Q931mes_DISCONNECT 0x45 /* 0100 0101 */ +#define Q931mes_RELEASE 0x4d /* 0100 1101 */ +#define Q931mes_RELEASE_COMPLETE 0x5a /* 0101 1010 */ +#define Q931mes_RESTART 0x46 /* 0100 0110 */ +#define Q931mes_RESTART_ACKNOWLEDGE 0x4e /* 0100 1110 */ +#define Q931mes_CONGESTION_CONTROL 0x79 /* 0111 1001 */ +#define Q931mes_INFORMATION 0x7b /* 0111 1011 */ +#define Q931mes_NOTIFY 0x6e /* 0110 1110 */ +#define Q931mes_STATUS 0x7d /* 0111 1101 */ +#define Q931mes_STATUS_ENQUIRY 0x75 /* 0111 0101 */ +#define Q931mes_SEGMENT 0x60 /* 0110 0000 */ + +#define Q931mes_SERVICE 0x0f /* 0000 1111 */ +#define Q931mes_SERVICE_ACKNOWLEDGE 0x07 /* 0000 0111 */ + + +/** + * Generic Q.931 Timers + */ +enum { + Q931_TIMER_T300 = 1, /* */ + Q931_TIMER_T301, + Q931_TIMER_T302, + Q931_TIMER_T303, + Q931_TIMER_T304, + Q931_TIMER_T305, + Q931_TIMER_T306, + Q931_TIMER_T307, + Q931_TIMER_T308, + Q931_TIMER_T309, + Q931_TIMER_T310, + Q931_TIMER_T311, + Q931_TIMER_T312, + Q931_TIMER_T313, + Q931_TIMER_T314, + Q931_TIMER_T315, + Q931_TIMER_T316, + Q931_TIMER_T317, + Q931_TIMER_T318, + Q931_TIMER_T319, + Q931_TIMER_T320, + Q931_TIMER_T321, + Q931_TIMER_T322, +}; + +/** + * Q.931 ToN + */ +enum { + Q931_TON_UNKNOWN = 0x00, + Q931_TON_INTERNATIONAL = 0x01, + Q931_TON_NATIONAL = 0x02, + Q931_TON_NETWORK_SPECIFIC = 0x03, + Q931_TON_SUBSCRIBER_NUMBER = 0x04, + Q931_TON_ABBREVIATED_NUMBER = 0x06, + Q931_TON_RESERVED = 0x07 +}; + +/** + * Q.931 Numbering Plan + */ +enum { + Q931_NUMPLAN_UNKNOWN = 0x00, + Q931_NUMPLAN_E164 = 0x01, + Q931_NUMPLAN_X121 = 0x03, + Q931_NUMPLAN_F69 = 0x04, + Q931_NUMPLAN_NATIONAL = 0x08, + Q931_NUMPLAN_PRIVATE = 0x09, + Q931_NUMPLAN_RESERVED = 0x0e +}; + +/** + * Q.931 Presentation Indicator + */ +enum { + Q931_PRES_ALLOWED = 0x00, + Q931_PRES_RESTRICTED = 0x01, + Q931_PRES_NOT_AVAILABLE = 0x02, + Q931_PRES_RESERVED = 0x03 +}; + +/** + * Q.931 Screening Indicator + */ +enum { + Q931_SCREEN_USER_NOT_SCREENED = 0x00, + Q931_SCREEN_USER_VERIFIED_PASSED = 0x01, + Q931_SCREEN_USER_VERIFIED_FAILED = 0x02, + Q931_SCREEN_NETWORK = 0x03 +}; + +/** + * Q.931 Coding Standard + */ +enum { + Q931_CODING_ITU = 0x00, + Q931_CODING_ISO = 0x01, + Q931_CODING_NATIONAL = 0x02, + Q931_CODING_NETWORK = 0x03 +}; + +/** + * Q.931 High layer characteristik id + */ +enum { + Q931_HLCHAR_TELEPHONY = 0x01, + Q931_HLCHAR_FAX_G23 = 0x04, + Q931_HLCHAR_FAX_G4 = 0x21, + Q931_HLCHAR_FAX_G4II = 0x24, + Q931_HLCHAR_T102 = 0x32, + Q931_HLCHAR_T101 = 0x33, + Q931_HLCHAR_F60 = 0x35, + Q931_HLCHAR_X400 = 0x38, + Q931_HLCHAR_X200 = 0x41 +}; + +/** + * Q.931 User information layer 1 protocol + */ +enum { + Q931_UIL1P_V110 = 0x01, + Q931_UIL1P_I460 = 0x01, + Q931_UIL1P_X30 = 0x01, + + Q931_UIL1P_G711U = 0x02, + Q931_UIL1P_G711A = 0x03, + Q931_UIL1P_G721 = 0x04, + + Q931_UIL1P_H221 = 0x05, + Q931_UIL1P_H242 = 0x05, + + Q931_UIL1P_H223 = 0x06, + Q931_UIL1P_H245 = 0x06, + + Q931_UIL1P_RATE_ADAP = 0x07, + + Q931_UIL1P_V120 = 0x08, + Q931_UIL1P_X31 = 0x09 +}; + +/** + * Q.931 Information Transfer Capability + */ +enum { + Q931_ITC_SPEECH = 0x00, + Q931_ITC_UNRESTRICTED = 0x08, + Q931_ITC_RESTRICTED = 0x09, + Q931_ITC_3K1_AUDIO = 0x10, + Q931_ITC_UNRESTRICTED_TONES = 0x11, + Q931_ITC_VIDEO = 0x18 +}; + +/** + * Q.931 Information transfer rate + */ +enum { + Q931_ITR_PACKET = 0x00, + Q931_ITR_64K = 0x10, + Q931_ITR_128K = 0x11, + Q931_ITR_384K = 0x13, + Q931_ITR_1536K = 0x15, + Q931_ITR_1920K = 0x17, + Q931_ITR_MULTI = 0x18 +}; + +/***************************************************************************** + + Struct: Q931mes_Header + + Description: Used to read the header & message code. + +*****************************************************************************/ +typedef struct { + L3UINT Size; /* Size of message in bytes */ + L3UCHAR ProtDisc; /* Protocol Discriminator */ + L3UCHAR MesType; /* Message type */ + L3UCHAR CRVFlag; /* Call reference value flag */ + L3INT CRV; /* Call reference value */ + +} Q931mes_Header; + +/***************************************************************************** + + Struct: Q931mes_Generic + + Description: Generic header containing all IE's. This is not used, but is + provided in case a proprietary variant needs it. + +*****************************************************************************/ +typedef struct { + L3UINT Size; /* Size of message in bytes */ + L3UCHAR ProtDisc; /* Protocol Discriminator */ + L3UCHAR MesType; /* Message type */ + L3UCHAR CRVFlag; /* Call reference value flag */ + L3INT CRV; /* Call reference value */ + + /* WARNING: don't touch anything above this line (TODO: use Q931mes_Header directly to make sure it's the same) */ + + L3UCHAR Tei; /* TEI */ + + ie Shift; + ie MoreData; + ie SendComplete; + ie CongestionLevel; + ie RepeatInd; + + ie Segment; /* Segmented message */ + ie BearerCap; /* Bearer Capability */ + ie Cause; /* Cause */ + ie CallState; /* Call State */ + ie CallID; /* Call Identity */ + ie ChanID; /* Channel Identification */ + ie ChangeStatus; /* Change Staus */ + ie ProgInd; /* Progress Indicator */ + ie NetFac; /* Network Spesific Facilities */ + ie NotifInd; /* Notification Indicator */ + ie Display; /* Display */ + ie DateTime; /* Date/Time */ + ie KeypadFac; /* Keypad Facility */ + ie Signal; /* Signal */ + ie InfoRate; /* Information rate */ + ie EndEndTxDelay; /* End to End Transmit Delay */ + ie TransDelSelInd; /* Transmit Delay Sel. and Ind. */ + ie PackParam; /* Packed Layer Binary parameters */ + ie PackWinSize; /* Packet Layer Window Size */ + ie PackSize; /* Packed Size */ + ie ClosedUserGrp; /* Closed User Group */ + ie RevChargeInd; /* Reverse Charging Indicator */ + ie CalledNum; /* Called Party Number */ + ie CalledSub; /* Called Party subaddress */ + ie CallingNum; /* Calling Party Number */ + ie CallingSub; /* Calling Party Subaddress */ + ie RedirNum; /* Redirection Number */ + ie TransNetSel; /* Transmit Network Selection */ + ie LLRepeatInd; /* Repeat Indicator 2 LLComp */ + ie RestartWin; /* Restart Window */ + ie RestartInd; /* Restart Indicator */ + ie LLComp; /* Low Layer Compatibility */ + ie HLComp; /* High Layer Compatibility */ + ie UserUser; /* User-user */ + ie Escape; /* Escape for extension */ + ie Switchhook; + ie FeatAct; + ie FeatInd; + ie GenericDigits; + + L3UCHAR buf[1]; /* Buffer for IE's */ + +} Q931mes_Generic; + + +/***************************************************************************** + + Struct: Q931_TrunkInfo + + Description: TrunkInfo is the struct entry used to store Q.931 related + information and state for E1/T1/J1 trunks and associated + channels in the system. + + The user should store this information outside this stack + and needs to feed the interface functions with a pointer to + the TrunkInfo entry. + +*****************************************************************************/ +typedef struct Q931_TrunkInfo Q931_TrunkInfo_t; + +typedef enum { + Q931_LOG_NONE = -1, + Q931_LOG_EMERG, + Q931_LOG_ALERT, + Q931_LOG_CRIT, + Q931_LOG_ERROR, + Q931_LOG_WARNING, + Q931_LOG_NOTICE, + Q931_LOG_INFO, + Q931_LOG_DEBUG +} Q931LogLevel_t; + +typedef L3INT (*Q931Tx34CB_t) (void *,L3UCHAR *, L3INT); +typedef L3INT (*Q931Tx32CB_t) (void *, L3INT, L3UCHAR, L3UCHAR *, L3INT); +typedef L3INT (*Q931ErrorCB_t) (void *,L3INT,L3INT,L3INT); +typedef L3INT (*Q931LogCB_t) (void *, Q931LogLevel_t, char *, L3INT); + +typedef enum { /* Network/User Mode. */ + Q931_TE=0, /* 0 : User Mode */ + Q931_NT=1 /* 1 : Network Mode */ +} Q931NetUser_t; + +typedef enum { /* Dialect enum */ + Q931_Dialect_Q931 = 0, + Q931_Dialect_National = 2, + Q931_Dialect_DMS = 4, + Q931_Dialect_5ESS = 6, /* Coming soon to a PRI stack near you! */ + + Q931_Dialect_Count +} Q931Dialect_t; +#define DIALECT_STRINGS "q931", "", "national", "", "dms", "", "5ess", "" +Q931_STR2ENUM_P(q931_str2Q931Dialect_type, q931_Q931Dialect_type2str, Q931Dialect_t) + +typedef enum { /* Trunk Line Type. */ + Q931_TrType_E1 = 0, /* 0 : E1 Trunk */ + Q931_TrType_T1 = 1, /* 1 : T1 Trunk */ + Q931_TrType_J1 = 2, /* 2 : J1 Trunk */ + Q931_TrType_BRI = 3, /* 3 : BRI Trunk */ + Q931_TrType_BRI_PTMP = 4 /* 4 : BRI PTMP Trunk */ +} Q931_TrunkType_t; + +typedef enum { /* Trunk State */ + Q931_TrState_NoAlignment=0, /* Trunk not aligned */ + Q931_TrState_Aligning=1, /* Aligning in progress */ + Q931_TrState_Aligned=2 /* Trunk Aligned */ +} Q931_TrunkState_t; + +typedef enum { + Q931_ChType_NotUsed=0, /* Unused Channel */ + Q931_ChType_B=1, /* B Channel (Voice) */ + Q931_ChType_D=2, /* D Channel (Signalling) */ + Q931_ChType_Sync=3 /* Sync Channel */ +} Q931_ChanType_t; + +struct Q931_Call +{ + L3UCHAR InUse; /* Indicate if entry is in use. */ + /* 0 = Not in Use */ + /* 1 = Active Call. */ + + L3UCHAR Tei; /* Associated TEI */ + + L3UCHAR BChan; /* Associated B Channel. */ + /* 0 - 31 valid B chan */ + /* 255 = Not allocated */ + + L3INT CRV; /* Associated CRV. */ + + L3UINT State; /* Call State. */ + /* 0 is Idle, but other values are */ + /* defined per dialect. */ + /* Default usage is 1-99 for TE and */ + /* 101 - 199 for NT. */ + + L3ULONG Timer; /* Timer in ms. The TimeTick will check */ + /* if this has exceeded the timeout, and */ + /* if so call the timers timeout proc. */ + + L3USHORT TimerID; /* Timer Identification/State */ + /* actual values defined by dialect */ + /* 0 : No timer running */ + /* ITU-T Q.931:301 - 322 Timer running */ +}; + +struct Q931_TrunkInfo +{ + Q931NetUser_t NetUser; /* Network/User Mode. */ + Q931_TrunkType_t TrunkType; /* Trunk Line Type. */ + Q931Dialect_t Dialect; /* Q.931 Based dialect index. */ + + Q931Tx34CB_t Q931Tx34CBProc; + Q931Tx32CB_t Q931Tx32CBProc; + Q931ErrorCB_t Q931ErrorCBProc; + Q931LogCB_t Q931LogCBProc; + void *PrivateData32; + void *PrivateData34; + void *PrivateDataLog; + + Q931LogLevel_t loglevel; + + L3UCHAR Enabled; /* Enabled/Disabled */ + /* 0 = Disabled */ + /* 1 = Enabled */ + + Q931_TrunkState_t TrunkState; + + L3INT LastCRV; /* Last used crv for the trunk. */ + + L3UCHAR L3Buf[Q931L4BUF]; /* message buffer for messages to be */ + /* send from Q.931 L4. */ + + L3UCHAR L2Buf[Q931L2BUF]; /* buffer for messages send to L2. */ + + /* The auto flags below switch on/off automatic Ack messages. SETUP ACK */ + /* as an example can be sent by the stack in response to SETUP to buy */ + /* time in processing on L4. Setting this to true will cause the stack */ + /* to automatically send this. */ + + L3BOOL autoSetupAck; /* Indicate if the stack should send */ + /* SETUP ACK or not. 0=No, 1 = Yes. */ + + L3BOOL autoConnectAck; /* Indicate if the stack should send */ + /* CONNECT ACT or not. 0=No, 1=Yes. */ + + L3BOOL autoRestartAck; /* Indicate if the stack should send */ + /* RESTART ACK or not. 0=No, 1=Yes. */ + + L3BOOL autoServiceAck; /* Indicate if the stack should send */ + /* SERVICE ACK or not. 0=No, 1=Yes. */ + + /* channel array holding info per channel. Usually defined to 32 */ + /* channels to fit an E1 since T1/J1 and BRI will fit inside a E1. */ + struct _charray + { + Q931_ChanType_t ChanType; /* Unused, B, D, Sync */ + + L3UCHAR Available; /* Channel Available Flag */ + /* 0 : Avaiabled */ + /* 1 : Used */ + + L3INT CRV; /* Associated CRV */ + + } ch[Q931MAXCHPERTRUNK]; + + /* Active Call information indentified by CRV. See Q931AllocateCRV for */ + /* initialization of call table. */ + struct Q931_Call call[Q931MAXCALLPERTRUNK]; +}; + +/***************************************************************************** + + Struct: Q931State + + Description: Define a Q931 State, legal events and next state for each + event. Used to simplify the state engine logic. Each state + engine defines its own state table and the logic need only + call a helper function to check if the message is legal + at this stage. + +*****************************************************************************/ +typedef struct +{ + L3INT State; + L3INT Message; + L3UCHAR Direction; +} Q931State; + +/***************************************************************************** + + Proc table external references. + + The proc tables are defined in Q931.c and initialized in Q931Initialize. + +*****************************************************************************/ +typedef L3INT (q931proc_func_t) (Q931_TrunkInfo_t *pTrunk, L3UCHAR *, L3INT); + +typedef L3INT (q931umes_func_t) (Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +typedef L3INT (q931pmes_func_t) (Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +typedef L3INT (q931uie_func_t) (Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff); +typedef L3INT (q931pie_func_t) (Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); + +typedef L3INT (q931timeout_func_t) (Q931_TrunkInfo_t *pTrunk, L3INT callIndex); +typedef L3ULONG q931timer_t; + +extern q931proc_func_t *Q931Proc[Q931MAXDLCT][Q931MAXMES]; + +extern q931umes_func_t *Q931Umes[Q931MAXDLCT][Q931MAXMES]; +extern q931pmes_func_t *Q931Pmes[Q931MAXDLCT][Q931MAXMES]; + +extern q931uie_func_t *Q931Uie[Q931MAXDLCT][Q931MAXIE]; +extern q931pie_func_t *Q931Pie[Q931MAXDLCT][Q931MAXIE]; + +extern q931timeout_func_t *Q931Timeout[Q931MAXDLCT][Q931MAXTIMER]; +extern q931timer_t Q931Timer[Q931MAXDLCT][Q931MAXTIMER]; + + +/***************************************************************************** + + Macro: GetIETotoSize + + Syntax: L3INT GetIETotSize(InfoElem ie); + + Description: Compute the total size in bytes of an info element including + size of 'header'. + +*****************************************************************************/ +#define Q931GetIETotSize(ie) (((ie.InfoID & 0x80) != 0) ? 1 : ie.LenIE) + 2) + +/***************************************************************************** + + Macro: IsIEPresent + + Syntax: BOOL IsIEPresent(ie InfoElement); + + Description: Return TRUE if the Information Element is included. + +*****************************************************************************/ +#define Q931IsIEPresent(x) ((x & 0x8000) != 0) + +/***************************************************************************** + + Macro: GetIEOffset and GetIEValue + + Syntax: L3INT GetIEOffset(ie InfoElement) + L3INT GetIEValue(ie InfoElement) + + Description: Returns the offset (or the value )to the Information Element. + + Note: GetIEValue assumes that the 15 lsb bit is the value of a + single octet information element. This macro can not be used + on a variable information element. + +*****************************************************************************/ +#define Q931GetIEOffset(x) (x & 0x7fff) +#define Q931GetIEValue(x) (x & 0x7fff) + +/***************************************************************************** + + Macro: Q931GetIEPtr + + Syntax: void * Q931GetIEPtr(ie InfoElement, L3UCHAR * Buf); + + Description: Compute a Ptr to the information element. + +*****************************************************************************/ +#define Q931GetIEPtr(ie,buf) ((void *)&buf[Q931GetIEOffset(ie)]) + +/***************************************************************************** + + Macro: SetIE + + Syntax: void SetIE(ie InfoElement, L3INT Offset); + + Description: Set an information element. + +*****************************************************************************/ +#define Q931SetIE(x,o) { x = (ie)(o) | 0x8000; } + +/***************************************************************************** + + Macro: IsQ931Ext + + Syntax BOOL IsQ931Ext(L3UCHAR c) + + Description: Return true Check if the msb (bit 8) is 0. This indicate + that the octet is extended. + +*****************************************************************************/ +#define IsQ931Ext(x) ((x & 0x80) == 0) + +/***************************************************************************** + + Macro: ieGetOctet + + Syntax: unsigned L3UCHAR ieGetOctet(L3INT e) + + Description: Macro to fetch one byte from an integer. Mostly used to + avoid warnings. + +*****************************************************************************/ +#define ieGetOctet(x) ((L3UCHAR)(x)) + +/***************************************************************************** + + Macro: NoWarning + + Syntax: void NoWarning(x) + + Description: Macro to suppress unreferenced formal parameter warnings + + Used during creation of the stack since the stack is + developed for Warning Level 4 and this creates a lot of + warning for the initial empty functions. + +*****************************************************************************/ +#define NoWarning(x) (x = x) + +/***************************************************************************** + + External references. See Q931.c for details. + +*****************************************************************************/ + +#include "Q931ie.h" + +#include "Q932.h" + +/***************************************************************************** + + Q.931 Message Pack/Unpack functions. Implemented in Q931mes.c + +*****************************************************************************/ +L3INT Q931Pmes_Alerting(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_CallProceeding(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Connect(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ConnectAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Progress(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_SetupAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Resume(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ResumeAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ResumeReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Suspend(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_SuspendAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_SuspendReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_UserInformation(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Disconnect(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Release(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ReleaseComplete(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Restart(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_RestartAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_CongestionControl(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Information(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Notify(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Segment(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Status(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_StatusEnquiry(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_Service(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931Pmes_ServiceAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + + +L3INT Q931Umes_Alerting(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_CallProceeding(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Connect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_ConnectAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Progress(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_SetupAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Resume(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_ResumeAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_ResumeReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Suspend(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_SuspendAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_SuspendReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_UserInformation(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Disconnect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Release(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_ReleaseComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Restart(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_RestartAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_CongestionControl(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Information(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Notify(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Segment(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Status(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_StatusEnquiry(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT I, L3INT O); +L3INT Q931Umes_Service(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); +L3INT Q931Umes_ServiceAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size); + + +/***************************************************************************** + + Q.931 Process Function Prototyping. Implemented in Q931StateTE.c + +*****************************************************************************/ +L3INT Q931ProcAlertingTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcCallProceedingTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcConnectTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcConnectAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcProgressTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSetupTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSetupAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeRejectTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendRejectTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcUserInformationTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcDisconnectTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcReleaseTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcReleaseCompleteTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcRestartTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcRestartAckTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcCongestionControlTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcInformationTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcNotifyTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcStatusTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcStatusEnquiryTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSegmentTE(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); + +L3INT Q931ProcAlertingNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcCallProceedingNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcConnectNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcConnectAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcProgressNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSetupNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSetupAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcResumeRejectNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSuspendRejectNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcUserInformationNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcDisconnectNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcReleaseNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcReleaseCompleteNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcRestartNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcRestartAckNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcCongestionControlNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcInformationNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcNotifyNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcStatusNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcStatusEnquiryNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcSegmentNT(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); + +L3INT Q931ProcUnknownMessage(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); +L3INT Q931ProcUnexpectedMessage(Q931_TrunkInfo_t *pTrunk,L3UCHAR * b, L3INT iFrom); + +/***************************************************************************** + + Interface Function Prototypes. Implemented in Q931.c + +*****************************************************************************/ +void Q931TimerTick(Q931_TrunkInfo_t *pTrunk); +L3INT Q931Rx23(Q931_TrunkInfo_t *pTrunk, L3INT ind, L3UCHAR tei, L3UCHAR * Mes, L3INT Size); +L3INT Q931Tx32Data(Q931_TrunkInfo_t *pTrunk, L3UCHAR bcast, L3UCHAR * Mes, L3INT Size); +L3INT Q931Rx43(Q931_TrunkInfo_t *pTrunk, L3UCHAR * Mes, L3INT Size); +L3INT Q931Tx34(Q931_TrunkInfo_t *pTrunk, L3UCHAR * Mes, L3INT Size); +void Q931SetError(Q931_TrunkInfo_t *pTrunk,L3INT ErrID, L3INT ErrPar1, L3INT ErrPar2); + +void Q931SetDefaultErrorCB(Q931ErrorCB_t Q931ErrorPar); + +void Q931CreateTE(L3UCHAR i); +void Q931CreateNT(L3UCHAR i); +void Q931SetMesCreateCB(L3INT (*callback)(void)); +void Q931SetDialectCreateCB(L3INT (*callback)(L3INT)); +void Q931SetHeaderSpace(L3INT space); + +void Q931SetMesProc(L3UCHAR mes, L3UCHAR dialect, q931proc_func_t *Q931ProcFunc, q931umes_func_t *Q931UmesFunc, q931pmes_func_t *Q931PmesFunc); +void Q931SetIEProc(L3UCHAR iec, L3UCHAR dialect, q931pie_func_t *Q931PieProc, q931uie_func_t *Q931UieProc); +void Q931SetTimeoutProc(L3UCHAR dialect, L3UCHAR timer, q931timeout_func_t *Q931TimeoutProc); +void Q931SetTimerDefault(L3UCHAR dialect, L3UCHAR timer, q931timer_t timeout); + +void Q931Initialize(void); +void Q931AddDialect(L3UCHAR iDialect, void (*Q931CreateDialectCB)(L3UCHAR iDialect)); +L3INT Q931InitMesSetup(Q931mes_Generic *p); +L3INT Q931InitMesRestartAck(Q931mes_Generic * pMes); +L3INT Q931InitMesGeneric(Q931mes_Generic *pMes); + +L3INT Q931CreateCRV(Q931_TrunkInfo_t *pTrunk, L3INT * callIndex); +L3INT Q931ReleaseCRV(Q931_TrunkInfo_t *pTrunk, L3INT CRV); +L3INT Q931AllocateCRV(Q931_TrunkInfo_t *pTrunk, L3INT iCRV, L3INT * callIndex); +L3INT Q931FindCRV(Q931_TrunkInfo_t *pTrunk, L3INT crv, L3INT *callindex); +L3INT Q931GetCallState(Q931_TrunkInfo_t *pTrunk, L3INT iCRV); +L3INT Q931StartTimer(Q931_TrunkInfo_t *pTrunk, L3INT callIndex, L3USHORT iTimer); +L3INT Q931StopTimer(Q931_TrunkInfo_t *pTrunk, L3INT callindex, L3USHORT iTimer); +L3INT Q931SetState(Q931_TrunkInfo_t *pTrunk, L3INT callIndex, L3INT iState); +L3ULONG Q931GetTime(void); +void Q931SetGetTimeCB(L3ULONG (*callback)(void)); +void Q931AddStateEntry(L3UCHAR iD, L3INT iState, L3INT iMes, L3UCHAR cDir); +L3BOOL Q931IsEventLegal(L3UCHAR iD, L3INT iState, L3INT iMes, L3UCHAR cDir); + +/***************************************************************************** + + Q.931 Low Level API Function Prototyping. Implemented in Q931API.c + +*****************************************************************************/ +ie Q931AppendIE(L3UCHAR *pm, L3UCHAR *pi); +L3INT Q931GetUniqueCRV(Q931_TrunkInfo_t *pTrunk); + +L3INT Q931InitIEBearerCap(Q931ie_BearerCap *p); +L3INT Q931InitIEChanID(Q931ie_ChanID *p); +L3INT Q931InitIEProgInd(Q931ie_ProgInd *p); +L3INT Q931InitIENetFac(Q931ie_NetFac * pIE); +L3INT Q931InitIEDisplay(Q931ie_Display * pIE); +L3INT Q931InitIEDateTime(Q931ie_DateTime * pIE); +L3INT Q931InitIEKeypadFac(Q931ie_KeypadFac * pIE); +L3INT Q931InitIESignal(Q931ie_Signal * pIE); +L3INT Q931InitIECallingNum(Q931ie_CallingNum * pIE); +L3INT Q931InitIECallingSub(Q931ie_CallingSub * pIE); +L3INT Q931InitIECalledNum(Q931ie_CalledNum * pIE); +L3INT Q931InitIECalledSub(Q931ie_CalledSub * pIE); +L3INT Q931InitIETransNetSel(Q931ie_TransNetSel * pIE); +L3INT Q931InitIELLComp(Q931ie_LLComp * pIE); +L3INT Q931InitIEHLComp(Q931ie_HLComp * pIE); + +L3INT Q931Disconnect(Q931_TrunkInfo_t *pTrunk, L3INT iTo, L3INT iCRV, L3INT iCause); +L3INT Q931ReleaseComplete(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); +L3INT Q931AckRestart(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); +L3INT Q931AckConnect(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); +L3INT Q931AckSetup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); +L3INT Q931AckService(Q931_TrunkInfo_t *pTrunk, L3UCHAR *buf); + +L3INT Q931Api_InitTrunk(Q931_TrunkInfo_t *pTrunk, + Q931Dialect_t Dialect, + Q931NetUser_t NetUser, + Q931_TrunkType_t TrunkType, + Q931Tx34CB_t Q931Tx34CBProc, + Q931Tx32CB_t Q931Tx32CBProc, + Q931ErrorCB_t Q931ErrorCBProc, + void *PrivateData32, + void *PrivateData34); + +L3INT Q931GetMesSize(Q931mes_Generic *pMes); +L3INT Q931InitMesResume(Q931mes_Generic * pMes); + +L3INT Q931Log(Q931_TrunkInfo_t *trunk, Q931LogLevel_t level, const char *fmt, ...); +void Q931SetLogCB(Q931_TrunkInfo_t *trunk, Q931LogCB_t func, void *priv); +void Q931SetLogLevel(Q931_TrunkInfo_t *trunk, Q931LogLevel_t level); + +void Q931SetL4HeaderSpace(L3INT space); +void Q931SetL2HeaderSpace(L3INT space); +L3INT Q931ProcDummy(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b,L3INT c); +L3INT Q931UmesDummy(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT Q931UieDummy(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff); +L3INT Q931PmesDummy(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q931PieDummy(Q931_TrunkInfo_t *pTrunk,L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); +L3INT Q931TxDummy(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT n); +L3INT Q931ErrorDummy(void *priv, L3INT a, L3INT b, L3INT c); +L3INT Q931TimeoutDummy(Q931_TrunkInfo_t *pTrunk, L3INT callIndex); + +L3INT Q931MesgHeader(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *mes, L3UCHAR *OBuf, L3INT Size, L3INT *IOff); + +#endif /* _Q931_NL */ diff --git a/libs/openzap/src/isdn/include/Q931ie.h b/libs/openzap/src/isdn/include/Q931ie.h new file mode 100644 index 0000000000..65dba4e261 --- /dev/null +++ b/libs/openzap/src/isdn/include/Q931ie.h @@ -0,0 +1,1205 @@ +/****************************************************************************** + + FileName: Q931ie.h + + Contents: Header and definition for the ITU-T Q.931 ie + structures and functions + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 _Q931IE_NL +#define _Q931IE_NL + +/* Codesets */ + +typedef enum { + + Q931_CODESET_0 = ( 0 ), + Q931_CODESET_1 = ( 1 << 8 ), + Q931_CODESET_2 = ( 2 << 8 ), + Q931_CODESET_3 = ( 3 << 8 ), + Q931_CODESET_4 = ( 4 << 8 ), + Q931_CODESET_5 = ( 5 << 8 ), + Q931_CODESET_6 = ( 6 << 8 ), + Q931_CODESET_7 = ( 7 << 8 ) + +} q931_codeset_t; + +/* Single octet information elements */ +#define Q931ie_SHIFT 0x90 /* 1001 ---- */ +#define Q931ie_MORE_DATA 0xa0 /* 1010 ---- */ +#define Q931ie_SENDING_COMPLETE 0xa1 /* 1010 0001 */ +#define Q931ie_CONGESTION_LEVEL 0xb0 /* 1011 ---- */ +#define Q931ie_REPEAT_INDICATOR 0xd0 /* 1101 ---- */ + +/* Variable Length Information Elements */ +#define Q931ie_SEGMENTED_MESSAGE 0x00 /* 0000 0000 */ +#define Q931ie_CHANGE_STATUS 0x01 /* 0000 0001 */ +#define Q931ie_BEARER_CAPABILITY 0x04 /* 0000 0100 */ +#define Q931ie_CAUSE 0x08 /* 0000 1000 */ +#define Q931ie_CALL_IDENTITY 0x10 /* 0001 0000 */ +#define Q931ie_CALL_STATE 0x14 /* 0001 0100 */ +#define Q931ie_CHANNEL_IDENTIFICATION 0x18 /* 0001 1000 */ +#define Q931ie_PROGRESS_INDICATOR 0x1e /* 0001 1110 */ +#define Q931ie_NETWORK_SPECIFIC_FACILITIES 0x20 /* 0010 0000 */ +#define Q931ie_NOTIFICATION_INDICATOR 0x27 /* 0010 0111 */ +#define Q931ie_DISPLAY 0x28 /* 0010 1000 */ +#define Q931ie_DATETIME 0x29 /* 0010 1001 */ +#define Q931ie_KEYPAD_FACILITY 0x2c /* 0010 1100 */ +#define Q931ie_SIGNAL 0x34 /* 0011 0100 */ +#define Q931ie_SWITCHOOK 0x36 /* 0011 0110 */ +#define Q931ie_FEATURE_ACTIVATION 0x38 /* 0011 1000 */ +#define Q931ie_FEATURE_INDICATION 0x39 /* 0011 1001 */ +#define Q931ie_INFORMATION_RATE 0x40 /* 0100 0000 */ +#define Q931ie_END_TO_END_TRANSIT_DELAY 0x42 /* 0100 0010 */ +#define Q931ie_TRANSIT_DELAY_SELECTION_AND_IND 0x43 /* 0100 0011 */ +#define Q931ie_PACKED_LAYER_BIMARY_PARAMETERS 0x44 /* 0100 0100 */ +#define Q931ie_PACKED_LAYER_WINDOW_SIZE 0x45 /* 0100 0101 */ +#define Q931ie_PACKED_SIZE 0x46 /* 0100 0110 */ +#define Q931ie_CALLING_PARTY_NUMBER 0x6c /* 0110 1100 */ +#define Q931ie_CALLING_PARTY_SUBADDRESS 0x6d /* 0110 1101 */ +#define Q931ie_CALLED_PARTY_NUMBER 0x70 /* 0111 0000 */ +#define Q931ie_CALLED_PARTY_SUBADDRESS 0x71 /* 0111 0001 */ +#define Q931ie_REDIRECTING_NUMBER 0x74 /* 0111 0100 */ +#define Q931ie_TRANSIT_NETWORK_SELECTION 0x78 /* 0111 1000 */ +#define Q931ie_RESTART_INDICATOR 0x79 /* 0111 1001 */ +#define Q931ie_LOW_LAYER_COMPATIBILITY 0x7c /* 0111 1100 */ +#define Q931ie_HIGH_LAYER_COMPATIBILITY 0x7d /* 0111 1101 */ +#define Q931ie_USER_USER 0x7e /* 0111 1110 */ +#define Q931ie_ESCAPE_FOR_EX 0x7f /* 0111 1111 */ + +/* Variable Length Codeset 6 Information Elements */ +#define Q931ie_GENERIC_DIGITS 0x37 /* 0011 0111 */ + +/* Variable Length Information Element to shut up BRI testing */ +#define Q931ie_CONNECTED_NUMBER 0x4c /* 0100 1101 */ +#define Q931ie_FACILITY 0x1c /* 0001 1101 */ + + +/***************************************************************************** + + Struct: Q931ie_BearerCap + + Description: Bearer Capability Information Element. + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00000100 for Bearer Capability */ + L3UCHAR Size; /* Length of Information Element */ + + L3UCHAR CodStand; /* Coding Standard. */ + /* 00 - ITU-T */ + /* 01 - ISO/IEC */ + /* 10 - National standard */ + /* 11 - Network side spesific */ + + L3UCHAR ITC; /* Information Transfer Capability */ + /* 00000 - Speech */ + /* 01000 - Unrestricted digital info */ + /* 01001 - Restricted digital info */ + /* 10000 - 3.1 kHz audio */ + /* 10001 - Unrestricted with tones */ + /* 11000 - Video */ + + L3UCHAR TransMode; /* Transfer Mode. */ + /* 00 - Circuit mode */ + /* 10 - Packet mode */ + + L3UCHAR ITR; /* Information Transfer Rate. */ + /* 00000 - Packed mode */ + /* 10000 - 64 kbit/s */ + /* 10001 - 2 x 64 kbit/s */ + /* 10011 - 384 kbit/s */ + /* 10101 - 1536 kbit/s */ + /* 10111 - 1920 kbit/s */ + /* 11000 - Multirat (64 kbit/s base) */ + + L3UCHAR RateMul; /* Rate Multiplier */ + + L3UCHAR Layer1Ident; /* Layer 1 Ident. */ + + L3UCHAR UIL1Prot; /* User Information Layer 1 Protocol */ + /* 00001 : ITU-T V.110, I.460 and X.30 */ + /* 00010 : G.711 my-law */ + /* 00011 : G.711 A-law */ + /* 00100 : G.721 */ + /* 00101 : H.221 and H.242 */ + /* 00110 : H.223 and H.245 */ + /* 00111 : Non ITU-T Standard */ + /* 01000 : ITU-T V.120 */ + /* 01001 : ITU-T X.31 HDLC flag stuff. */ + + L3UCHAR SyncAsync; /* Sync/Async */ + /* 0 : Syncronous data */ + /* 1 : Asyncronous data */ + + L3UCHAR Negot; /* Negotiation */ + /* 0 : In-band negotiation not possib. */ + /* 1 : In-band negotiation possible */ + + L3UCHAR UserRate; /* User rate */ + /* 00000 : I.460, V.110, X,30 */ + /* 00001 : 0.6 kbit/s x.1 */ + /* 00010 : 1.2 kbit/s */ + /* 00011 : 2.4 kbit/s */ + /* 00100 : 3.6 kbit/s */ + /* 00101 : 4.8 kbit/s */ + /* 00110 : 7.2 kbit/s */ + /* 00111 : 8 kbit/s I.460 */ + /* 01000 : 9.6 kbit/s */ + /* 01001 : 14.4 kbit/s */ + /* 01010 : 16 kbit/s */ + /* 01011 : 19.2 kbit/s */ + /* 01100 : 32 kbit/s */ + /* 01101 : 38.4 kbit/s */ + /* 01110 : 48 kbit/s */ + /* 01111 : 56 kbit/s */ + /* 10000 : 57.6 kbit/s */ + /* 10010 : 28.8 kbit/s */ + /* 10100 : 24 kbit/s */ + /* 10101 : 0.1345 kbit/s */ + /* 10110 : 0.100 kbit/s */ + /* 10111 : 0.075/1.2 kbit/s */ + /* 11000 : 1.2/0.075/kbit/s */ + /* 11001 : 0.050 kbit/s */ + /* 11010 : 0.075 kbit/s */ + /* 11011 : 0.110 kbit/s */ + /* 11100 : 0.150 kbit/s */ + /* 11101 : 0.200 kbit/s */ + /* 11110 : 0.300 kbit/s */ + /* 11111 : 12 kbit/s */ + + L3UCHAR InterRate; /* Intermediate Rate */ + /* 00 : Not used */ + /* 01 : 8 kbit/s */ + /* 10 : 16 kbit/s */ + /* 11 : 32 kbit/s */ + + L3UCHAR NIConTx; /* Network Indepentend Clock on transmit*/ + /* 0 : Not required to send data clc */ + /* 1 : Send data w/NIC clc */ + + L3UCHAR NIConRx; /* NIC on Rx */ + /* 0 : Cannot accept indep. clc */ + /* 1 : data with indep. clc accepted */ + + L3UCHAR FlowCtlTx; /* Flow control on Tx */ + /* 0 : Send Flow ctrl not required */ + /* 1 : Send flow ctrl required */ + + L3UCHAR FlowCtlRx; /* Flow control on Rx */ + /* 0 : cannot use receive flow ctrl */ + /* 1 : Receive flow ctrl accepted */ + L3UCHAR HDR; /* HDR/No HDR */ + L3UCHAR MultiFrame; /* Multi frame support */ + /* 0 : multiframe not supported */ + /* 1 : multiframe supported */ + + L3UCHAR Mode; /* Mode of operation */ + /* 0 : bit transparent mode of operat. */ + /* 1 : protocol sesitive mode of op. */ + + L3UCHAR LLInegot; /* Logical link id negotiation (oct. 5b)*/ + /* 0 : default LLI=256 only */ + /* 1 : Full protocol negotiation */ + + L3UCHAR Assignor; /* Assignor/assignee */ + /* 0 : Default Asignee */ + /* 1 : Assignor only */ + + L3UCHAR InBandNeg; /* In-band/out-band negot. */ + /* 0 : negot done w/ USER INFO mes */ + /* 1 : negot done in-band w/link zero */ + + L3UCHAR NumStopBits; /* Number of stop bits */ + /* 00 : Not used */ + /* 01 : 1 bit */ + /* 10 : 1.5 bits */ + /* 11 : 2 bits */ + + L3UCHAR NumDataBits; /* Number of data bits. */ + /* 00 : not used */ + /* 01 : 5 bits */ + /* 10 : 7 bits */ + /* 11 : 8 bits */ + + L3UCHAR Parity; /* Parity Information */ + /* 000 : Odd */ + /* 010 : Even */ + /* 011 : None */ + /* 100 : Forced to 0 */ + /* 101 : Forced to 1 */ + + L3UCHAR DuplexMode; /* Mode duplex */ + /* 0 : Half duplex */ + /* 1 : Full duplex */ + + L3UCHAR ModemType; /* Modem type, see Q.931 p 64 */ + + L3UCHAR Layer2Ident; /* Layer 2 Ident */ + + L3UCHAR UIL2Prot; /* User Information Layer 2 Protocol */ + /* 00010 : Q.921/I.441 */ + /* 00110 : X.25 */ + /* 01100 : LAN logical link */ + + L3UCHAR Layer3Ident; /* Layer 3 ident. */ + + L3UCHAR UIL3Prot; /* User Information Layer 3 Protocol */ + /* 00010 : Q.931 */ + /* 00110 : X.25 */ + /* 01011 : ISO/IEC TR 9577 */ + + L3UCHAR AL3Info1; /* additional layer 3 info 1 */ + + L3UCHAR AL3Info2; /* additional layer 3 info 2 */ + +} Q931ie_BearerCap; + +/***************************************************************************** + + Struct: Q931ie_CallID + + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00010000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CallId[1]; /* Call identity */ + +} Q931ie_CallID; + +/***************************************************************************** + + Struct: Q931ie_CallState + + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00010100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding Standard */ + L3UCHAR CallState; /* Call State Value */ + +} Q931ie_CallState; + +/***************************************************************************** + + Struct: Q931ie_Cause + + Description: Cause IE as described in Q.850 + +*****************************************************************************/ +typedef struct { + + L3UCHAR IEId; /* 00010100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding Standard */ + L3UCHAR Location; /* Location */ + L3UCHAR Recom; /* Recommendation */ + L3UCHAR Value; /* Cause Value */ + L3UCHAR Diag[1]; /* Optional Diagnostics Field */ + +} Q931ie_Cause; + +/***************************************************************************** + + Struct: Q931ie_CalledNum + +*****************************************************************************/ +typedef struct { + + L3UCHAR IEId; /* 01110000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypNum; /* Type of Number */ + L3UCHAR NumPlanID; /* Numbering plan identification */ + L3UCHAR Digit[1]; /* Digit (IA5) */ + +} Q931ie_CalledNum; + +/***************************************************************************** + + Struct: Q931ie_CalledSub + + Description: Called party subaddress + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01110001 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypNum; /* Type of subaddress */ + L3UCHAR OddEvenInd; /* Odd/Even indicator */ + L3UCHAR Digit[1]; /* digits */ + +} Q931ie_CalledSub; + +/***************************************************************************** + + Struct: Q931ie_CallingNum + + Description: Calling party number + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01101100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypNum; /* Type of number */ + L3UCHAR NumPlanID; /* Numbering plan identification */ + L3UCHAR PresInd; /* Presentation indicator */ + L3UCHAR ScreenInd; /* Screening indicator */ + L3UCHAR Digit[1]; /* Number digits (IA5) */ + +} Q931ie_CallingNum; + +/***************************************************************************** + + Struct: Q931ie_CallingSub + + Description: Calling party subaddress + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01101101 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypNum; /* Type of subaddress */ + L3UCHAR OddEvenInd; /* Odd/Even indicator */ + L3UCHAR Digit[1]; /* digits */ + +} Q931ie_CallingSub; + +/***************************************************************************** + + Struct: Q931ie_ChanID + + Description: Channel identification + + Channel Identificationis one of the IE elements that differ + between BRI and PRI. IntType = 1 = BRI and ChanSlot is used + for channel number, while InfoChanSel is used for BRI. + + ChanID is one of the most important IE as it is passed + either though SETUP or CALL PROCEEDING to select the channel + to be used. + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00011000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR IntIDPresent; /* Int. id. present */ + L3UCHAR IntType; /* Interface Type */ + /* 0 : Basic Interface (BRI) */ + /* 1 : Other interfaces, PRI etc. */ + + L3UCHAR PrefExcl; /* Pref./Excl. */ + /* 0 : Indicated channel is preffered */ + /* 1 : Exclusive, no other accepted */ + + L3UCHAR DChanInd; /* D-channel ind. */ + /* 0 : chan is NOT D chan. */ + /* 1 : chan is D chan */ + + L3UCHAR InfoChanSel; /* Info. channel selection */ + /* 00 : No channel */ + /* 01 : B1 channel */ + /* 10 : B2 channel */ + /* 11 : Any channel */ + + L3UCHAR InterfaceID; /* Interface identifier */ + + L3UCHAR CodStand; /* Code standard */ + /* 00 : ITU-T standardization coding */ + /* 01 : ISO/IEC Standard */ + /* 10 : National Standard */ + /* 11 : Standard def. by network. */ + + L3UCHAR NumMap; /* Number/Map */ + /* 0 : chan is in following octet */ + /* 1 : chan is indicated by slot map */ + + L3UCHAR ChanMapType; /* Channel type/Map element type */ + /* 0011 : B Channel units */ + /* 0110 : H0 channel units */ + /* 1000 : H11 channel units */ + /* 1001 : H12 channel units */ + + L3UCHAR ChanSlot; /* Channel number */ + +} Q931ie_ChanID; + +/***************************************************************************** + + Struct: Q931ie_DateTime + + Description: Date/time + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00101001 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Year; /* Year */ + L3UCHAR Month; /* Month */ + L3UCHAR Day; /* Day */ + L3UCHAR Hour; /* Hour */ + L3UCHAR Minute; /* Minute */ + L3UCHAR Second; /* Second */ + L3UCHAR Format; /* Indicate presense of Hour, Min & sec */ + /* 0 : Only Date */ + /* 1 : Hour present */ + /* 2 : Hour and Minute present */ + /* 3 : Hour, Minute and Second present */ +} Q931ie_DateTime; + +/***************************************************************************** + + Struct: Q931ie_Display + + Description: Display + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00101000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Display[1]; /* Display information (IA5) */ + +} Q931ie_Display; + +/***************************************************************************** + + Struct: Q931ie_HLComp + + Description: High layer compatibility + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01111101 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding standard */ + L3UCHAR Interpret; /* Interpretation */ + L3UCHAR PresMeth; /* Presentation methor of prot. profile */ + L3UCHAR HLCharID; /* High layer characteristics id. */ + L3UCHAR EHLCharID; /* Extended high layer character. id. */ + L3UCHAR EVideoTlfCharID; /* Ext. videotelephony char. id. */ + +} Q931ie_HLComp; + +/***************************************************************************** + + Struct: Q931ie_KeypadFac + + Description: Keypad facility + +*****************************************************************************/ + +typedef struct { + L3UCHAR IEId; /* 00101100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR KeypadFac[1]; /* dynamic buffer */ + +} Q931ie_KeypadFac; + +/***************************************************************************** + + Struct: Q931ie_LLComp + + Description: Low layer compatibility + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01111100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding standard */ + /* 00 - ITU-T */ + /* 01 - ISO/IEC */ + /* 10 - National standard */ + /* 11 - Network side spesific */ + + L3UCHAR ITransCap; /* Information transfer capability */ + /* 00000 - Speech */ + /* 01000 - Unrestricted digital info */ + /* 01001 - Restricted digital info */ + /* 10000 - 3.1 kHz audio */ + /* 10001 - Unrestricted with tones */ + /* 11000 - Video */ + + L3UCHAR NegotInd; /* Negot indic. */ + /* 0 : Out-band neg. not possib. */ + /* 1 : Out-band neg. possible */ + + L3UCHAR TransMode; /* Transfer Mode */ + /* 00 : Circuit Mode */ + /* 10 : Packed Mode */ + + L3UCHAR InfoRate; /* Information transfer rate */ + /* 00000 - Packed mode */ + /* 10000 - 64 kbit/s */ + /* 10001 - 2 x 64 kbit/s */ + /* 10011 - 384 kbit/s */ + /* 10101 - 1536 kbit/s */ + /* 10111 - 1920 kbit/s */ + /* 11000 - Multirat (64 kbit/s base) */ + + L3UCHAR RateMul; /* Rate multiplier */ + L3UCHAR Layer1Ident; /* Layer 1 ident. */ + L3UCHAR UIL1Prot; /* User information layer 1 protocol */ + /* 00001 : ITU-T V.110, I.460 and X.30 */ + /* 00010 : G.711 my-law */ + /* 00011 : G.711 A-law */ + /* 00100 : G.721 */ + /* 00101 : H.221 and H.242 */ + /* 00110 : H.223 and H.245 */ + /* 00111 : Non ITU-T Standard */ + /* 01000 : ITU-T V.120 */ + /* 01001 : ITU-T X.31 HDLC flag stuff. */ + + L3UCHAR SyncAsync; /* Synch/asynch */ + /* 0 : Syncronous data */ + /* 1 : Asyncronous data */ + + L3UCHAR Negot; /* Negot */ + /* 0 : In-band negotiation not possib. */ + /* 1 : In-band negotiation possible */ + + L3UCHAR UserRate; /* User rate */ + /* 00000 : I.460, V.110, X,30 */ + /* 00001 : 0.6 kbit/s x.1 */ + /* 00010 : 1.2 kbit/s */ + /* 00011 : 2.4 kbit/s */ + /* 00100 : 3.6 kbit/s */ + /* 00101 : 4.8 kbit/s */ + /* 00110 : 7.2 kbit/s */ + /* 00111 : 8 kbit/s I.460 */ + /* 01000 : 9.6 kbit/s */ + /* 01001 : 14.4 kbit/s */ + /* 01010 : 16 kbit/s */ + /* 01011 : 19.2 kbit/s */ + /* 01100 : 32 kbit/s */ + /* 01101 : 38.4 kbit/s */ + /* 01110 : 48 kbit/s */ + /* 01111 : 56 kbit/s */ + /* 10000 : 57.6 kbit/s */ + /* 10010 : 28.8 kbit/s */ + /* 10100 : 24 kbit/s */ + /* 10101 : 0.1345 kbit/s */ + /* 10110 : 0.100 kbit/s */ + /* 10111 : 0.075/1.2 kbit/s */ + /* 11000 : 1.2/0.075/kbit/s */ + /* 11001 : 0.050 kbit/s */ + /* 11010 : 0.075 kbit/s */ + /* 11011 : 0.110 kbit/s */ + /* 11100 : 0.150 kbit/s */ + /* 11101 : 0.200 kbit/s */ + /* 11110 : 0.300 kbit/s */ + /* 11111 : 12 kbit/s */ + + L3UCHAR InterRate; /* Intermediate rate */ + /* 00 : Not used */ + /* 01 : 8 kbit/s */ + /* 10 : 16 kbit/s */ + /* 11 : 32 kbit/s */ + + L3UCHAR NIConTx; /* Network Indepentend Clock on transmit*/ + /* 0 : Not required to send data clc */ + /* 1 : Send data w/NIC clc */ + + L3UCHAR NIConRx; /* NIC on Rx */ + /* 0 : Cannot accept indep. clc */ + /* 1 : data with indep. clc accepted */ + + L3UCHAR FlowCtlTx; /* Flow control on Tx */ + /* 0 : Send Flow ctrl not required */ + /* 1 : Send flow ctrl required */ + + L3UCHAR FlowCtlRx; /* Flow control on Rx */ + /* 0 : cannot use receive flow ctrl */ + /* 1 : Receive flow ctrl accepted */ + L3UCHAR HDR; /* HDR/No HDR */ + L3UCHAR MultiFrame; /* Multi frame support */ + /* 0 : multiframe not supported */ + /* 1 : multiframe supported */ + + L3UCHAR ModeL1; /* Mode L1 */ + /* 0 : bit transparent mode of operat. */ + /* 1 : protocol sesitive mode of op. */ + + L3UCHAR NegotLLI; /* Negot. LLI */ + /* 0 : default LLI=256 only */ + /* 1 : Full protocol negotiation */ + + L3UCHAR Assignor; /* Assignor/Assignor ee */ + /* 0 : Default Asignee */ + /* 1 : Assignor only */ + + L3UCHAR InBandNeg; /* In-band negot. */ + /* 0 : negot done w/ USER INFO mes */ + /* 1 : negot done in-band w/link zero */ + + L3UCHAR NumStopBits; /* Number of stop bits */ + /* 00 : Not used */ + /* 01 : 1 bit */ + /* 10 : 1.5 bits */ + /* 11 : 2 bits */ + + L3UCHAR NumDataBits; /* Number of data bits. */ + /* 00 : not used */ + /* 01 : 5 bits */ + /* 10 : 7 bits */ + /* 11 : 8 bits */ + + L3UCHAR Parity; /* Parity Information */ + /* 000 : Odd */ + /* 010 : Even */ + /* 011 : None */ + /* 100 : Forced to 0 */ + /* 101 : Forced to 1 */ + + L3UCHAR DuplexMode; /* Mode duplex */ + /* 0 : Half duplex */ + /* 1 : Full duplex */ + + L3UCHAR ModemType; /* Modem type, see Q.931 p 89 */ + + L3UCHAR Layer2Ident; /* Layer 2 ident. */ + + L3UCHAR UIL2Prot; /* User information layer 2 protocol */ + /* 00001 : Basic mode ISO 1745 */ + /* 00010 : Q.921/I.441 */ + /* 00110 : X.25 single link */ + /* 00111 : X.25 multilink */ + /* 01000 : Extended LAPB T.71 */ + /* 01001 : HDLC ARM */ + /* 01010 : HDLC NRM */ + /* 01011 : HDLC ABM */ + /* 01100 : LAN logical link */ + /* 01101 : X.75 SLP */ + /* 01110 : Q.922 */ + /* 01111 : Q.922 core aspect */ + /* 10000 : User specified */ + /* 10001 : ISO/IEC 7776 DTE-DCE */ + + L3UCHAR ModeL2; /* Mode */ + /* 01 : Normal Mode of operation */ + /* 10 : Extended mode of operation */ + + L3UCHAR Q933use; /* Q.9333 use */ + + L3UCHAR UsrSpcL2Prot; /* User specified layer 2 protocol info */ + + L3UCHAR WindowSize; /* Window size (k) */ + + L3UCHAR Layer3Ident; /* Layer 3 ident */ + + L3UCHAR UIL3Prot; /* User Information Layer 3 protocol */ + /* 00010 : Q.931 */ + /* 00110 : X.25 */ + /* 00111 : 8208 */ + /* 01000 : X.233 ... */ + /* 01001 : 6473 */ + /* 01010 : T.70 */ + /* 01011 : ISO/IEC TR 9577 */ + /* 10000 : User specified */ + L3UCHAR OptL3Info; /* Optional Leyer 3 info */ + + L3UCHAR ModeL3; /* Mode of operation */ + /* 01 : Normal packed seq. numbering */ + /* 10 : Extended packed seq. numbering */ + + L3UCHAR DefPackSize; /* Default packet size */ + + L3UCHAR PackWinSize; /* Packet window size */ + + L3UCHAR AddL3Info; /* Additional Layer 3 protocol info */ + +} Q931ie_LLComp; + +/***************************************************************************** + + Struct: Q931ie_NetFac; + + Description: Network-specific facilities + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00100000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR LenNetID; /* Length of network facilities id. */ + L3UCHAR TypeNetID; /* Type of network identification */ + L3UCHAR NetIDPlan; /* Network identification plan. */ + L3UCHAR NetFac; /* Network specific facility spec. */ + L3UCHAR NetID[1]; /* Network id. (IA5) */ + +} Q931ie_NetFac; + +/***************************************************************************** + + Struct: Q931ie_NotifInd; + + Description: Notification Indicator + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00100000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Notification; /* Notification descriptor */ + +} Q931ie_NotifInd; + +/***************************************************************************** + + Struct: Q931ie_ProgInd + + Description: Progress indicator + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00011110 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CodStand; /* Coding standard */ + L3UCHAR Location; /* Location */ + L3UCHAR ProgDesc; /* Progress description */ + +} Q931ie_ProgInd; + +/***************************************************************************** + + Struct; Q931ie_Segment + + Description: Segmented message + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00000000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR FSI; /* First segment indicator */ + L3UCHAR NumSegRem; /* Number of segments remaining */ + L3UCHAR SegType; /* Segment message type */ + +} Q931ie_Segment; + + +typedef struct { + + L3UCHAR IEId; /* 00000000 */ + L3UCHAR Size; /* Length of Information Element */ + +} Q931ie_SendComplete; + +/***************************************************************************** + + Struct: Q931ie_Signal + + Description: Signal + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00000000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Signal; /* Signal value */ + /* 00000000 Dial tone on */ + /* 00000001 Ring back tone on */ + /* 00000010 Intercept tone on */ + /* 00000011 Network congestion on */ + /* 00000100 Busy tone on */ + /* 00000101 Confirm tone on */ + /* 00000110 Answer tone on */ + /* 00000111 Call waiting tone */ + /* 00001000 Off-hook warning tone */ + /* 00001001 Pre-emption tone on */ + /* 00111111 Tones off */ + /* 01000000 Alerting on - pattern 0 */ + /* 01000001 Alerting on - pattern 1 */ + /* 01000010 Alerting on - pattern 2 */ + /* 01000011 Alerting on - pattern 3 */ + /* 01000100 Alerting on - pattern 4 */ + /* 01000101 Alerting on - pattern 5 */ + /* 01000110 Alerting on - pattern 6 */ + /* 01000111 Alerting on - pattern 7 */ + /* 01001111 Alerting off */ +} Q931ie_Signal; + +/***************************************************************************** + + Struct: Q931ie_TransDelSelInd + + description: Transit delay selection and indication + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 00000000 */ + L3UCHAR Size; /* Length of Information Element */ + L3ULONG TxDSIValue; /* Trans. delay sel. & ind. value */ + +} Q931ie_TransDelSelInd; +#endif + +/***************************************************************************** + + Struct: Q931ie_TransNetSel + + Description: Transit network selection + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01111000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Type; /* Type of network identifier */ + L3UCHAR NetIDPlan; /* Network idetification plan */ + L3UCHAR NetID[1]; /* Network identification(IA5) */ + +} Q931ie_TransNetSel; + +/***************************************************************************** + + Struct: Q931ie_UserUser + + Description: User-user + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 01111110 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR ProtDisc; /* Protocol discriminator */ + L3UCHAR User[1]; /* User information */ + +} Q931ie_UserUser; + +/***************************************************************************** + + Struct: Q931ie_ClosedUserGrp + + Description: Closed user group + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000111 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CUGInd; /* CUG indication */ + L3UCHAR CUG[1]; /* CUG index code (IA5) */ + +} Q931ie_ClosedUserGrp; +#endif + +/***************************************************************************** + + Struct: Q931ie_CongLevel + + Description: Congestion Level + +*****************************************************************************/ +typedef struct { + + L3UCHAR IEId; /* 01000111 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR CongLevel; /* Conguestion Level */ + +} Q931ie_CongLevel; + +/***************************************************************************** + + Struct: Q931ie_EndEndTxDelay + + Description: End to end transit delay + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000010 */ + L3UCHAR Size; /* Length of Information Element */ + L3ULONG CumTxDelay; /* Cumulative transit delay value */ + L3ULONG ReqTxDelay; /* Requested end to end transit delay */ + L3ULONG MaxTxDelay; /* Maximum transit delay */ + +} Q931ie_EndEndTxDelay; +#endif + +/***************************************************************************** + + Struct: Q931ie_InfoRate + + Description: Information Rate + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01100000 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR InInfoRate; /* Incoming information rate */ + L3UCHAR OutInfoRate; /* Outgoing information rate */ + L3UCHAR MinInInfoRate; /* Minimum incoming information rate */ + L3UCHAR MinOutInfoRate; /* Minimum outgoing information rate */ + +} Q931ie_InfoRate; +#endif + +/***************************************************************************** + + Struct: Q931ie_PackParam + + Description: Packed layer binary parameters + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR FastSel; /* Fast selected */ + L3UCHAR ExpData; /* Exp. data */ + L3UCHAR DelConf; /* Delivery conf */ + L3UCHAR Modulus; /* Modulus */ + +} Q931ie_PackParam; +#endif + +/***************************************************************************** + + Struct: Q931ie_PackWinSize + + Description: Packed window size + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000101 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR ForwardValue; /* Forward value */ + L3UCHAR BackwardValue; /* Backward value */ + +} Q931ie_PackWinSize; +#endif + +/***************************************************************************** + + Struct: Q931ie_PackSize + + Description: Packet size + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01000110 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR ForwardValue; /* Forward value */ + L3UCHAR BackwardValue; /* Backward value */ + +} Q931ie_PackSize; +#endif + +/***************************************************************************** + + Struct: Q931ie_RedirNum + + Description: Redirecting number + +*****************************************************************************/ +#ifdef Q931_X25_SUPPORT +typedef struct { + + L3UCHAR IEId; /* 01110100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR TypeNum; /* Type of number */ + L3UCHAR NumPlanID; /* Number plan identification */ + L3UCHAR PresInd; /* Presentation indicator */ + L3UCHAR ScreenInd; /* Screening indicator */ + L3UCHAR Reason; /* Reason for redirection */ + L3UCHAR Digit[1]; /* Number digits (IA5) */ + +} Q931ie_RedirNum; +#endif + +typedef struct { + + L3UCHAR IEId; /* 01110100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR RepeatInd; /* 0010 Prioritized list for selecting */ + /* one possible. */ +} Q931ie_RepeatInd; + +typedef struct { + + L3UCHAR IEId; /* 01110100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Spare; /* Spare */ + L3UCHAR Class; /* Class */ + /* 000 Indicate channels */ + /* 110 Single interface */ + /* 111 All interfaces */ +} Q931ie_RestartInd; + +typedef struct { + + L3UCHAR IEId; /* 01110100 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Preference; /* Preference 0 = reserved, 1 = channel */ + L3UCHAR Spare; /* Spare */ + L3UCHAR NewStatus; /* NewStatus */ + /* 000 In service */ + /* 001 Maintenance */ + /* 010 Out of service */ +} Q931ie_ChangeStatus; + +/***************************************************************************** + + Struct: Q931ie_GenericDigits + + +*****************************************************************************/ + +typedef struct { + + L3UCHAR IEId; /* 00110111 */ + L3UCHAR Size; /* Length of Information Element */ + L3UCHAR Type; /* Type of number */ + L3UCHAR Encoding; /* Encoding of number */ + L3UCHAR Digit[1]; /* Number digits (IA5) */ + +} Q931ie_GenericDigits; + + +/***************************************************************************** + + Q.931 Information Element Pack/Unpack functions. Implemented in Q931ie.c + +*****************************************************************************/ +q931pie_func_t Q931Pie_ChangeStatus; +q931pie_func_t Q931Pie_BearerCap; +q931pie_func_t Q931Pie_ChanID; +q931pie_func_t Q931Pie_ProgInd; +q931pie_func_t Q931Pie_Display; +q931pie_func_t Q931Pie_Signal; +q931pie_func_t Q931Pie_HLComp; +q931pie_func_t Q931Pie_Segment; +q931pie_func_t Q931Pie_DateTime; +q931pie_func_t Q931Pie_Cause; +q931pie_func_t Q931Pie_SendComplete; +q931pie_func_t Q931Pie_KeypadFac; +q931pie_func_t Q931Pie_NotifInd; +q931pie_func_t Q931Pie_CallID; +q931pie_func_t Q931Pie_RepeatInd; +q931pie_func_t Q931Pie_NetFac; +q931pie_func_t Q931Pie_CallingNum; +q931pie_func_t Q931Pie_CallingSub; +q931pie_func_t Q931Pie_CalledNum; +q931pie_func_t Q931Pie_CalledSub; +q931pie_func_t Q931Pie_CalledNum; +q931pie_func_t Q931Pie_TransNetSel; +q931pie_func_t Q931Pie_LLComp; +q931pie_func_t Q931Pie_CallState; +q931pie_func_t Q931Pie_RestartInd; +q931pie_func_t Q931Pie_UserUser; + +q931pie_func_t Q931Pie_GenericDigits; + +L3USHORT Q931Uie_CRV(Q931_TrunkInfo_t *pTrunk,L3UCHAR * IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff); + +q931uie_func_t Q931Uie_ChangeStatus; +q931uie_func_t Q931Uie_BearerCap; +q931uie_func_t Q931Uie_ChanID; +q931uie_func_t Q931Uie_ProgInd; +q931uie_func_t Q931Uie_Display; +q931uie_func_t Q931Uie_Signal; +q931uie_func_t Q931Uie_HLComp; +q931uie_func_t Q931Uie_Segment; +q931uie_func_t Q931Uie_DateTime; +q931uie_func_t Q931Uie_Cause; +q931uie_func_t Q931Uie_SendComplete; +q931uie_func_t Q931Uie_KeypadFac; +q931uie_func_t Q931Uie_NotifInd; +q931uie_func_t Q931Uie_CallID; +q931uie_func_t Q931Uie_RepeatInd; +q931uie_func_t Q931Uie_NetFac; +q931uie_func_t Q931Uie_CallingNum; +q931uie_func_t Q931Uie_CallingSub; +q931uie_func_t Q931Uie_CalledNum; +q931uie_func_t Q931Uie_CalledSub; +q931uie_func_t Q931Uie_TransNetSel; +q931uie_func_t Q931Uie_LLComp; +q931uie_func_t Q931Uie_CallState; +q931uie_func_t Q931Uie_RestartInd; +q931uie_func_t Q931Uie_UserUser; + +q931uie_func_t Q931Uie_GenericDigits; + + +L3INT Q931ReadExt(L3UCHAR * IBuf, L3INT Off); +L3INT Q931Uie_CongLevel(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff); +L3INT Q931Pie_CongLevel(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); +L3INT Q931Uie_RevChargeInd(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR * IBuf, L3UCHAR * OBuf, L3INT *IOff, L3INT *OOff); +L3INT Q931Pie_RevChargeInd(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); + +L3INT Q931Uie_Generic(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *pMsg, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *IOff, L3INT *OOff); +L3INT Q931Pie_Generic(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, L3UCHAR *OBuf, L3INT *Octet); + +#endif /* _Q931IE_NL */ diff --git a/libs/openzap/src/isdn/include/Q932.h b/libs/openzap/src/isdn/include/Q932.h new file mode 100644 index 0000000000..9b69a34a02 --- /dev/null +++ b/libs/openzap/src/isdn/include/Q932.h @@ -0,0 +1,95 @@ +/***************************************************************************** + + FileName: Q932.h + + Contents: Header w/structs for Q932 Suplementary Services. + + NB: Do NOT include this header directly, include Q931.h + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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. +*****************************************************************************/ + +/***************************************************************************** + Q.932 Additional Message codes +*****************************************************************************/ + +#define Q932mes_HOLD 0x24 /* 0010 0100 */ +#define Q932mes_HOLD_ACKNOWLEDGE 0x28 /* 0010 1000 */ +#define Q932mes_HOLD_REJECT 0x30 /* 0011 0000 */ +#define Q932mes_RETRIEVE 0x31 /* 0011 0001 */ +#define Q932mes_RETRIEVE_ACKNOWLEDGE 0x33 /* 0011 0011 */ +#define Q932mes_RETRIEVE_REJECT 0x37 /* 0011 0111 */ +#define Q932mes_FACILITY 0x62 /* 0110 0010 */ +#define Q932mes_REGISTER 0x64 /* 0110 0100 */ + +/***************************************************************************** + Q.932 Additional EI Codes +*****************************************************************************/ +#define Q932ie_FACILITY 0x1c /* 0001 1100 */ + +/***************************************************************************** + Function Prototypes. +*****************************************************************************/ +L3INT Q932ProcFacilityTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRegisterTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveAckTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveRejectTE(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); + +L3INT Q932ProcFacilityNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcHoldRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRegisterNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveAckNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); +L3INT Q932ProcRetrieveRejectNT(Q931_TrunkInfo_t *pTrunk, L3UCHAR * b, L3INT iFrom); + +L3INT Q932Pmes_Facility(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_Hold(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_HoldAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_HoldReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_Register(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_Retrieve(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_RetrieveAck(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); +L3INT Q932Pmes_RetrieveReject(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +L3INT Q932Umes_Facility(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_Hold(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_HoldAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_HoldReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_Register(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_Retrieve(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_RetrieveAck(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); +L3INT Q932Umes_RetrieveReject(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic * OBuf, L3INT I, L3INT O); diff --git a/libs/openzap/src/isdn/include/mfifo.h b/libs/openzap/src/isdn/include/mfifo.h new file mode 100644 index 0000000000..4c1d850d81 --- /dev/null +++ b/libs/openzap/src/isdn/include/mfifo.h @@ -0,0 +1,85 @@ +/***************************************************************************** + + Filename: mfifo.h + + Contents: header for MFIFO + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 _MFIFO +#define _MFIFO + +/***************************************************************************** + + Struct: MINDEX + + Description: Message Index used to index a dynamic size Message FIFO. + +*****************************************************************************/ +typedef struct _mindex { + int offset; /* offset to message in buf */ + int size; /* size of message in bytes */ +} MINDEX; + +/***************************************************************************** + + Struct: MFIFO + + Description: Message FIFO. Provides a dynamic sized message based FIFO + queue. + +*****************************************************************************/ +typedef struct { + int first; /* first out */ + int last; /* last in + 1 */ + int bsize; /* buffer size */ + unsigned char *buf; /* ptr to start of buffer */ + int ixsize; /* index size */ + MINDEX ix[1]; /* message index */ +} MFIFO; + +/***************************************************************************** + Function prototypes. +*****************************************************************************/ +int MFIFOCreate(unsigned char *buf, int size, int index); +void MFIFOClear(unsigned char * buf); +int MFIFOGetLBOffset(unsigned char *buf); +int MFIFOGetFBOffset(unsigned char *buf); +void MFIFOWriteIX(unsigned char *buf, unsigned char *mes, int size, int ix, int off); +int MFIFOWriteMes(unsigned char *buf, unsigned char *mes, int size); +unsigned char * MFIFOGetMesPtr(unsigned char *buf, int *size); +void MFIFOKillNext(unsigned char *buf); + +unsigned char * MFIFOGetMesPtrOffset(unsigned char *buf, int *size, const int pos); +int MFIFOGetMesCount(unsigned char *buf); +int MFIFOWriteMesOverwrite(unsigned char *buf, unsigned char *mes, int size); + +#endif diff --git a/libs/openzap/src/isdn/include/national.h b/libs/openzap/src/isdn/include/national.h new file mode 100644 index 0000000000..cff26aa4a4 --- /dev/null +++ b/libs/openzap/src/isdn/include/national.h @@ -0,0 +1,86 @@ +/****************************************************************************** + + FileName: national.h + + Contents: Header and definition for the National ISDN dialect. The + header contents the following parts: + - Definition of codes + - Definition of information elements (nationalie_). + - Definition of messages (nationalmes_). + - Function prototypes. + + Description: The National ISDN dialect here covers ???? + + Related Files: national.h National ISDN Definitions + nationalie.c National ISDN IE encoders/coders + nationalStateTE.c National ISDN TE State Engine + nationalStateNT.c National ISDN NT State Engine + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 _national_NATIONAL_NL +#define _national_NATIONAL_NL + +#include "Q931.h" + +/***************************************************************************** + + Q.931 Message codes + Only National specific message and ie types + here the rest are inherited from national.h + +*****************************************************************************/ + + +/***************************************************************************** + + Q.931 Message Pack/Unpack functions. Implemented in nationalmes.c + +*****************************************************************************/ +L3INT nationalUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *OBuf, L3INT IOff, L3INT Size); +L3INT nationalPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize); + +#include "DMS.h" + +/***************************************************************************** + + Q.931 Process Function Prototyping. Implemented in nationalStateTE.c + +*****************************************************************************/ + +void nationalCreateTE(L3UCHAR i); +void nationalCreateNT(L3UCHAR i); + +#endif /* _national_NATIONAL_NL */ diff --git a/libs/openzap/src/isdn/mfifo.c b/libs/openzap/src/isdn/mfifo.c new file mode 100644 index 0000000000..28528f9b64 --- /dev/null +++ b/libs/openzap/src/isdn/mfifo.c @@ -0,0 +1,399 @@ +/***************************************************************************** + + Filename: mfifo.c + + Description: mfifo is a message orriented fifo system with support of + both message and byte per byte retriaval of messages. + + The fifo has been designed with two usages in mind: + + - Queueing of frames for hdlc and feeding out byte per byte + with the possibility of re-sending of frames etc. + + - fifo for messages of dynamic size. + + The fifo is allocated on top of any buffer and creates an + index of message in the queue. The user can write/read + messages or write messages and read the message one byte + at the time. + + Interface: + MFIFOCreate Create/reset/initialize fifo. + MFIFOClear Clear FIFO. + MFIFOWriteMes Write message into fifo + * MFIFOReadMes Read message from fifo. + MFIFOGetMesPtr Get ptr to next message. + MFIFOKillNext Kill next message. + + * currently not implemented. + + Note: The message will always be saved continuously. If there is not + sufficient space at the end of the buffer, the fifo will skip + the last bytes and save the message at the top of the buffer. + + This is required to allow direct ptr access to messages + stored in the queue. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + 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 Case Labs, Ltd nor the names of its 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 "mfifo.h" +#include +#include + +/***************************************************************************** + + Function: MFIFOCreate + + Description: Creates a fifo on top of an existing buffer. + + Parameters: buf ptr to buffer. + size size of buffer in bytes. + index size of index entries (max no messages). + + Return value: 0 if failure, 1 if ok. + +*****************************************************************************/ +int MFIFOCreate(unsigned char *buf, int size, int index) +{ + MFIFO *mf = (MFIFO *)buf; + + mf->first = mf->last = 0; + mf->ixsize = index; + mf->buf = &buf[sizeof(MFIFO) + (sizeof(MINDEX) * index)]; + + if (mf->buf > &buf[size]) + return 0; + + mf->bsize = size - sizeof(MFIFO) - (sizeof(MINDEX) * index); + + return 1; +} + +/***************************************************************************** + + Function: MFIFOClear + + Description: Clear the FIFO + + Paremeters: buf ptr to fifo + + Return Value: none + +*****************************************************************************/ +void MFIFOClear(unsigned char * buf) +{ + MFIFO *mf = (MFIFO *)buf; + + mf->first = mf->last = 0; +} + +/***************************************************************************** + + Function: MFIFOGetLBOffset + + Description: Helper function caclulating offset to the 'first out' byte. + + Paremeters: buf ptr to fifo + + Return Value: offset. + +*****************************************************************************/ +int MFIFOGetLBOffset(unsigned char *buf) +{ + MFIFO *mf = (MFIFO *)buf; + + if (mf->last != mf->first) + return mf->ix[mf->last].offset; + + return 0; +} + +/***************************************************************************** + + Function: MFIFOGetFBOffset + + Description: Helper function calculating the offset to the 'first in' + byte in the buffer. This is the position the next byte + entering the fifo will occupy. + + Paremeters: buf ptr to fifo + + Return Value: offset + +*****************************************************************************/ +int MFIFOGetFBOffset(unsigned char *buf) +{ + MFIFO *mf = (MFIFO *)buf; + int x; + + if (mf->last == mf->first) + return 0; + + x = mf->first - 1; + + if (x < 0) + x = mf->ixsize - 1; + + return mf->ix[x].offset + mf->ix[x].size; +} + +/***************************************************************************** + + Function: MFIFOWriteIX + + Description: Helper function writing a calculated entry. The function + will perform a memcpy to move the message and set the index + values as well as increase the 'first in' index. + + Paremeters: buf ptr to fifo + mes ptr to message + size size of message in bytes. + ix index to index entry. + off offset to position to receive the message + + Return Value: none + +*****************************************************************************/ +void MFIFOWriteIX(unsigned char *buf, unsigned char *mes, int size, int ix, int off) +{ + MFIFO *mf = (MFIFO *)buf; + int x; + + memcpy(&mf->buf[off], mes, size); + mf->ix[ix].offset = off; + mf->ix[ix].size = size; + + x = mf->first + 1; + + if (x >= mf->ixsize) + x = 0; + + mf->first = x; +} + +/***************************************************************************** + + Function: MFIFOWriteMes + + Description: + + Paremeters: + + Return Value: + +*****************************************************************************/ +int MFIFOWriteMes(unsigned char *buf, unsigned char *mes, int size) +{ + MFIFO *mf = (MFIFO *)buf; + int of, ol, x; + + x = mf->first + 1; + + if (x >= mf->ixsize) + x = 0; + + if (x == mf->last) + return 0; /* full queue */ + + of = MFIFOGetFBOffset(buf); + ol = MFIFOGetLBOffset(buf); + if (mf->last == mf->first) { /* empty queue */ + mf->first = mf->last = 0; /* optimize */ + + MFIFOWriteIX(buf, mes, size, mf->first, 0); + return 1; + } + else if (of > ol) { + if (mf->bsize - of >= size) { + MFIFOWriteIX(buf, mes, size, mf->first, of); + return 1; + } + else if (ol > size) { + MFIFOWriteIX(buf, mes, size, mf->first, ol); + return 1; + } + } + else if (ol - of > size) { + MFIFOWriteIX(buf, mes, size, mf->first, of); + return 1; + } + + return 0; +} + +/***************************************************************************** + + Function: MFIFOGetMesPtr + + Description: + + Paremeters: + + Return Value: + +*****************************************************************************/ +unsigned char * MFIFOGetMesPtr(unsigned char *buf, int *size) +{ + MFIFO *mf = (MFIFO *)buf; + + if (mf->first == mf->last) { + return NULL; + } + + *size = mf->ix[mf->last].size; + return &mf->buf[mf->ix[mf->last].offset]; +} + +/***************************************************************************** + + Function: MFIFOKillNext + + Description: + + Paremeters: + + Return Value: + +*****************************************************************************/ +void MFIFOKillNext(unsigned char *buf) +{ + MFIFO *mf = (MFIFO *)buf; + int x; + + if (mf->first != mf->last) { + x = mf->last + 1; + if (x >= mf->ixsize) { + x = 0; + } + + mf->last = x; + } +} + + +/* + * Queue-style accessor functions + */ + +/** + * MFIFOGetMesPtrOffset + * \brief Get pointer to and size of message at position x + */ +unsigned char * MFIFOGetMesPtrOffset(unsigned char *buf, int *size, const int pos) +{ + MFIFO *mf = (MFIFO *)buf; + int x; + + if (mf->first == mf->last) { + return NULL; + } + + if (pos < 0 || pos >= mf->ixsize) { + return NULL; + } + + x = pos - mf->last; + if (x < 0) { + x += (mf->ixsize - 1); + } + + *size = mf->ix[x].size; + return &mf->buf[mf->ix[x].offset]; +} + + +/** + * MFIFOGetMesCount + * \brief How many messages are currently in the buffer? + */ +int MFIFOGetMesCount(unsigned char *buf) +{ + MFIFO *mf = (MFIFO *)buf; + + if (mf->first == mf->last) { + return 0; + } + else if (mf->first > mf->last) { + return mf->first - mf->last; + } + else { + return (mf->ixsize - mf->last) + mf->first; + } +} + +/** + * MFIFOWriteMesOverwrite + * \brief Same as MFIFOWriteMes but old frames will be overwritten if the fifo is full + */ +int MFIFOWriteMesOverwrite(unsigned char *buf, unsigned char *mes, int size) +{ + MFIFO *mf = (MFIFO *)buf; + int of, ol, x; + + x = mf->first + 1; + + if (x >= mf->ixsize) + x = 0; + + if (x == mf->last) { + /* advance last pointer */ + mf->last++; + + if (mf->last >= mf->ixsize) + mf->last = 0; + } + + of = MFIFOGetFBOffset(buf); + ol = MFIFOGetLBOffset(buf); + + if (mf->last == mf->first) { /* empty queue */ + mf->first = mf->last = 0; /* optimize */ + + MFIFOWriteIX(buf, mes, size, mf->first, 0); + return 1; + } + else if (of > ol) { + if (mf->bsize - of >= size) { + MFIFOWriteIX(buf, mes, size, mf->first, of); + return 1; + } + else if (ol > size) { + MFIFOWriteIX(buf, mes, size, mf->first, ol); + return 1; + } + } + else if (ol - of > size) { + MFIFOWriteIX(buf, mes, size, mf->first, of); + return 1; + } + return 0; +} diff --git a/libs/openzap/src/isdn/nationalStateNT.c b/libs/openzap/src/isdn/nationalStateNT.c new file mode 100644 index 0000000000..b150999650 --- /dev/null +++ b/libs/openzap/src/isdn/nationalStateNT.c @@ -0,0 +1,130 @@ +/***************************************************************************** + + FileName: nationalStateNT.c + + Contents: National ISDN State Engine for NT (Network Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "national.h" + +/***************************************************************************** + Function: nationalCreateNT + + Description: Will create the National ISDN NT as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void nationalCreateNT(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingNT, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingNT, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, Q931ProcConnectNT, Q931Umes_Connect, Q931Pmes_Connect); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, Q931ProcConnectAckNT, Q931Umes_ConnectAck, Q931Pmes_ConnectAck); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressNT, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupNT, nationalUmes_Setup, nationalPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckNT, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeNT, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckNT, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectNT, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendNT, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckNT, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectNT, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationNT, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectNT, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseNT, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteNT, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartNT, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckNT, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlNT, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationNT, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyNT, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusNT, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryNT, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentNT, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityNT, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldNT, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckNT, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectNT, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterNT, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveNT, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckNT, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectNT, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_REDIRECTING_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message */ + /* procs can when search this to find out if the message/state */ + /* combination is legale. If not, the proc for unexpected message apply.*/ + + /* TODO define state table here */ +} diff --git a/libs/openzap/src/isdn/nationalStateTE.c b/libs/openzap/src/isdn/nationalStateTE.c new file mode 100644 index 0000000000..46731d0910 --- /dev/null +++ b/libs/openzap/src/isdn/nationalStateTE.c @@ -0,0 +1,217 @@ +/***************************************************************************** + + FileName: nationalStateTE.c + + Contents: National ISDN State Engine for TE (User Mode). + + The controlling state engine for Q.931 is the state engine + on the NT side. The state engine on the TE side is a slave + of this. The TE side maintain it's own states as described in + ITU-T Q931, but will in raise conditions be overridden by + the NT side. + + This reference implementation uses a process per message, + meaning that each message must check call states. This + is easier for dialect maintenance as each message proc + can be replaced individually. A new TE variant only + need to copy the Q931CreateTE and replace those procs or + need to override. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "national.h" +extern L3INT Q931L4HeaderSpace; + +/***************************************************************************** + Function: nationalCreateTE + + Description: Will create the National TE as a Dialect in the stack. The first + bulk set up the message handlers, the second bulk the IE + encoders/coders, and the last bulk set up the state table. + + Parameters: i Dialect index +*****************************************************************************/ +void nationalCreateTE(L3UCHAR i) +{ + Q931SetMesProc(Q931mes_ALERTING, i, Q931ProcAlertingTE, Q931Umes_Alerting, Q931Pmes_Alerting); + Q931SetMesProc(Q931mes_CALL_PROCEEDING, i, Q931ProcCallProceedingTE, Q931Umes_CallProceeding, Q931Pmes_CallProceeding); + Q931SetMesProc(Q931mes_CONNECT, i, DMSProc0x07TE, DMSUmes_0x07, DMSPmes_0x07); + Q931SetMesProc(Q931mes_CONNECT_ACKNOWLEDGE, i, DMSProc0x0fTE, DMSUmes_0x0f, DMSPmes_0x0f); + Q931SetMesProc(Q931mes_PROGRESS, i, Q931ProcProgressTE, Q931Umes_Progress, Q931Pmes_Progress); + Q931SetMesProc(Q931mes_SETUP, i, Q931ProcSetupTE, nationalUmes_Setup, nationalPmes_Setup); + Q931SetMesProc(Q931mes_SETUP_ACKNOWLEDGE, i, Q931ProcSetupAckTE, Q931Umes_SetupAck, Q931Pmes_SetupAck); + Q931SetMesProc(Q931mes_RESUME, i, Q931ProcResumeTE, Q931Umes_Resume, Q931Pmes_Resume); + Q931SetMesProc(Q931mes_RESUME_ACKNOWLEDGE, i, Q931ProcResumeAckTE, Q931Umes_ResumeAck, Q931Pmes_ResumeAck); + Q931SetMesProc(Q931mes_RESUME_REJECT, i, Q931ProcResumeRejectTE, Q931Umes_ResumeReject, Q931Pmes_ResumeReject); + Q931SetMesProc(Q931mes_SUSPEND, i, Q931ProcSuspendTE, Q931Umes_Suspend, Q931Pmes_Suspend); + Q931SetMesProc(Q931mes_SUSPEND_ACKNOWLEDGE, i, Q931ProcSuspendAckTE, Q931Umes_SuspendAck, Q931Pmes_SuspendAck); + Q931SetMesProc(Q931mes_SUSPEND_REJECT, i, Q931ProcSuspendRejectTE, Q931Umes_SuspendReject, Q931Pmes_SuspendReject); + Q931SetMesProc(Q931mes_USER_INFORMATION, i, Q931ProcUserInformationTE, Q931Umes_UserInformation, Q931Pmes_UserInformation); + Q931SetMesProc(Q931mes_DISCONNECT, i, Q931ProcDisconnectTE, Q931Umes_Disconnect, Q931Pmes_Disconnect); + Q931SetMesProc(Q931mes_RELEASE, i, Q931ProcReleaseTE, Q931Umes_Release, Q931Pmes_Release); + Q931SetMesProc(Q931mes_RELEASE_COMPLETE, i, Q931ProcReleaseCompleteTE, Q931Umes_ReleaseComplete, Q931Pmes_ReleaseComplete); + Q931SetMesProc(Q931mes_RESTART, i, Q931ProcRestartTE, Q931Umes_Restart, Q931Pmes_Restart); + Q931SetMesProc(Q931mes_RESTART_ACKNOWLEDGE, i, Q931ProcRestartAckTE, Q931Umes_RestartAck, Q931Pmes_RestartAck); + Q931SetMesProc(Q931mes_CONGESTION_CONTROL, i, Q931ProcCongestionControlTE, Q931Umes_CongestionControl, Q931Pmes_CongestionControl); + Q931SetMesProc(Q931mes_INFORMATION, i, Q931ProcInformationTE, Q931Umes_Information, Q931Pmes_Information); + Q931SetMesProc(Q931mes_NOTIFY, i, Q931ProcNotifyTE, Q931Umes_Notify, Q931Pmes_Notify); + Q931SetMesProc(Q931mes_STATUS, i, Q931ProcStatusTE, Q931Umes_Status, Q931Pmes_Status); + Q931SetMesProc(Q931mes_STATUS_ENQUIRY, i, Q931ProcStatusEnquiryTE, Q931Umes_StatusEnquiry, Q931Pmes_StatusEnquiry); + Q931SetMesProc(Q931mes_SEGMENT, i, Q931ProcSegmentTE, Q931Umes_Segment, Q931Pmes_Segment); + + Q931SetMesProc(Q932mes_FACILITY, i, Q932ProcFacilityTE, Q932Umes_Facility, Q932Pmes_Facility); + Q931SetMesProc(Q932mes_HOLD, i, Q932ProcHoldTE, Q932Umes_Hold, Q932Pmes_Hold); + Q931SetMesProc(Q932mes_HOLD_ACKNOWLEDGE, i, Q932ProcHoldAckTE, Q932Umes_HoldAck, Q932Pmes_HoldAck); + Q931SetMesProc(Q932mes_HOLD_REJECT, i, Q932ProcHoldRejectTE, Q932Umes_HoldReject, Q932Pmes_HoldReject); + Q931SetMesProc(Q932mes_REGISTER, i, Q932ProcRegisterTE, Q932Umes_Register, Q932Pmes_Register); + Q931SetMesProc(Q932mes_RETRIEVE, i, Q932ProcRetrieveTE, Q932Umes_Retrieve, Q932Pmes_Retrieve); + Q931SetMesProc(Q932mes_RETRIEVE_ACKNOWLEDGE, i, Q932ProcRetrieveAckTE, Q932Umes_RetrieveAck, Q932Pmes_RetrieveAck); + Q931SetMesProc(Q932mes_RETRIEVE_REJECT, i, Q932ProcRetrieveRejectTE, Q932Umes_RetrieveReject, Q932Pmes_RetrieveReject); + + /* Set up the IE encoder/decoder handle table.*/ + Q931SetIEProc(Q931ie_SEGMENTED_MESSAGE, i, Q931Pie_Segment, Q931Uie_Segment); + Q931SetIEProc(Q931ie_BEARER_CAPABILITY, i, Q931Pie_BearerCap, Q931Uie_BearerCap); + Q931SetIEProc(Q931ie_CAUSE, i, Q931Pie_Cause, Q931Uie_Cause); + Q931SetIEProc(Q931ie_CALL_IDENTITY, i, Q931Pie_CallID, Q931Uie_CallID); + Q931SetIEProc(Q931ie_CALL_STATE, i, Q931Pie_CallState, Q931Uie_CallState); + Q931SetIEProc(Q931ie_CHANGE_STATUS, i, Q931Pie_ChangeStatus, Q931Uie_ChangeStatus); + Q931SetIEProc(Q931ie_CHANNEL_IDENTIFICATION, i, Q931Pie_ChanID, Q931Uie_ChanID); + Q931SetIEProc(Q931ie_PROGRESS_INDICATOR, i, Q931Pie_ProgInd, Q931Uie_ProgInd); + Q931SetIEProc(Q931ie_NETWORK_SPECIFIC_FACILITIES, i, Q931Pie_NetFac, Q931Uie_NetFac); + Q931SetIEProc(Q931ie_NOTIFICATION_INDICATOR, i, Q931Pie_NotifInd, Q931Uie_NotifInd); + Q931SetIEProc(Q931ie_DISPLAY, i, Q931Pie_Display, Q931Uie_Display); + Q931SetIEProc(Q931ie_DATETIME, i, Q931Pie_DateTime, Q931Uie_DateTime); + Q931SetIEProc(Q931ie_KEYPAD_FACILITY, i, Q931Pie_KeypadFac, Q931Uie_KeypadFac); + Q931SetIEProc(Q931ie_SIGNAL, i, Q931Pie_Signal, Q931Uie_Signal); + Q931SetIEProc(Q931ie_TRANSIT_DELAY_SELECTION_AND_IND, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_CALLING_PARTY_NUMBER, i, Q931Pie_CallingNum, Q931Uie_CallingNum); + Q931SetIEProc(Q931ie_CALLING_PARTY_SUBADDRESS, i, Q931Pie_CallingSub, Q931Uie_CallingSub); + Q931SetIEProc(Q931ie_CALLED_PARTY_NUMBER, i, Q931Pie_CalledNum, Q931Uie_CalledNum); + Q931SetIEProc(Q931ie_CALLED_PARTY_SUBADDRESS, i, Q931Pie_CalledSub, Q931Uie_CalledSub); + Q931SetIEProc(Q931ie_TRANSIT_NETWORK_SELECTION, i, Q931Pie_TransNetSel, Q931Uie_TransNetSel); + Q931SetIEProc(Q931ie_RESTART_INDICATOR, i, Q931Pie_RestartInd, Q931Uie_RestartInd); + Q931SetIEProc(Q931ie_LOW_LAYER_COMPATIBILITY, i, Q931Pie_LLComp, Q931Uie_LLComp); + Q931SetIEProc(Q931ie_HIGH_LAYER_COMPATIBILITY, i, Q931Pie_HLComp, Q931Uie_HLComp); + Q931SetIEProc(Q931ie_USER_USER, i, Q931Pie_UserUser, Q931Uie_UserUser); + Q931SetIEProc(Q931ie_GENERIC_DIGITS, i, Q931Pie_GenericDigits, Q931Uie_GenericDigits); + + Q931SetIEProc(Q931ie_CONNECTED_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_FACILITY, i, Q931Pie_Generic, Q931Uie_Generic); + Q931SetIEProc(Q931ie_REDIRECTING_NUMBER, i, Q931Pie_Generic, Q931Uie_Generic); + + /* The following define a state machine. The point is that the Message + * procs can when search this to find out if the message/state + * combination is legale. If not, the proc for unexpected message apply. + */ + + /* State 0 Idle */ + Q931AddStateEntry(i, Q931_U0, Q931mes_RESUME, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_SETUP, 2); + Q931AddStateEntry(i, Q931_U0, Q931mes_STATUS, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U0, Q931mes_RELEASE_COMPLETE, 4); + + /* State 1 Call Initiating */ + Q931AddStateEntry(i, Q931_U1, Q931mes_DISCONNECT, 2); + Q931AddStateEntry(i, Q931_U1, Q931mes_SETUP_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_RELEASE_COMPLETE, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U1, Q931mes_CONNECT, 4); + + /* State 2 Overlap Sending */ + Q931AddStateEntry(i, Q931_U2, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U2, Q931mes_CALL_PROCEEDING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U2, Q931mes_RELEASE, 2); + + /* State 3 Outgoing Call Proceeding */ + Q931AddStateEntry(i, Q931_U3, Q931mes_PROGRESS, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_ALERTING, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_CONNECT, 4); + Q931AddStateEntry(i, Q931_U3, Q931mes_RELEASE, 2); + + /* State 4 Call Delivered */ + Q931AddStateEntry(i, Q931_U4, Q931mes_CONNECT, 4); + + /* State 6 Call Precent */ + Q931AddStateEntry(i, Q931_U6, Q931mes_INFORMATION, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CALL_PROCEEDING, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE_COMPLETE, 2); + Q931AddStateEntry(i, Q931_U6, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U6, Q931mes_DISCONNECT, 4); + + /* State 7 Call Received */ + Q931AddStateEntry(i, Q931_U7, Q931mes_CONNECT, 2); + + /* State 8 Connect request */ + Q931AddStateEntry(i, Q931_U8, Q931mes_CONNECT_ACKNOWLEDGE, 4); + + /* State 9 Incoming Call Proceeding */ + Q931AddStateEntry(i, Q931_U9, Q931mes_CONNECT, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_ALERTING, 2); + Q931AddStateEntry(i, Q931_U9, Q931mes_PROGRESS, 2); + + /* State 10 Active */ + Q931AddStateEntry(i, Q931_U10, Q931mes_SUSPEND, 2); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 4); + Q931AddStateEntry(i, Q931_U10, Q931mes_NOTIFY, 2); + + /* State 11 Disconnect Request */ + Q931AddStateEntry(i, Q931_U11, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U11, Q931mes_NOTIFY, 4); + + /* State 12 Disconnect Ind */ + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 4); + Q931AddStateEntry(i, Q931_U12, Q931mes_RELEASE, 2); + + /* State 15 Suspend Request */ + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_ACKNOWLEDGE, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_SUSPEND_REJECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_DISCONNECT, 4); + Q931AddStateEntry(i, Q931_U15, Q931mes_RELEASE, 4); + +/* TODO + Q931AddStateEntry(i, Q931_U17, + Q931AddStateEntry(i, Q931_U19, + Q931AddStateEntry(i, Q931_U25, +*/ +} diff --git a/libs/openzap/src/isdn/nationalmes.c b/libs/openzap/src/isdn/nationalmes.c new file mode 100644 index 0000000000..6d8350fbcd --- /dev/null +++ b/libs/openzap/src/isdn/nationalmes.c @@ -0,0 +1,269 @@ +/***************************************************************************** + + FileName: nationalmes.c + + Contents: Pack/Unpack functions. These functions will unpack a National ISDN + message from the bit packed original format into structs + that contains variables sized by the user. It will also pack + the struct back into a Q.931 message as required. + + See national.h for description. + + License/Copyright: + + Copyright (c) 2007, Jan Vidar Berger, Case Labs, Ltd. All rights reserved. + email:janvb@caselaboratories.com + + Copyright (c) 2007, Michael Jerris. All rights reserved. + email:mike@jerris.com + + 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 Case Labs, Ltd nor the names of its 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 "national.h" + +/***************************************************************************** + + Function: nationalUmes_Setup + +*****************************************************************************/ +L3INT nationalUmes_Setup(Q931_TrunkInfo_t *pTrunk, L3UCHAR *IBuf, Q931mes_Generic *mes, L3INT IOff, L3INT Size) +{ + L3INT ir = 0; + L3INT OOff = 0; + L3INT rc = Q931E_NO_ERROR; + L3UCHAR last_codeset = 0, codeset = 0; + L3UCHAR shift_lock = 1; + + while (IOff < Size) { + if (!shift_lock) { + codeset = last_codeset; + } + + if ((IBuf[IOff] & 0xF0) == Q931ie_SHIFT ) { + shift_lock = (IBuf[IOff] & 0x08); + if (shift_lock) { + last_codeset = codeset; + } + codeset = ((IBuf[IOff] & 0x07)); + IOff++; + } + + if (codeset == 0) { + switch (IBuf[IOff]) { + case Q931ie_SENDING_COMPLETE: + case Q931ie_BEARER_CAPABILITY: + case Q931ie_CHANNEL_IDENTIFICATION: + case Q931ie_PROGRESS_INDICATOR: + case Q931ie_NETWORK_SPECIFIC_FACILITIES: + case Q931ie_DISPLAY: + case Q931ie_DATETIME: + case Q931ie_KEYPAD_FACILITY: + case Q931ie_SIGNAL: + case Q931ie_CALLING_PARTY_NUMBER: + case Q931ie_CALLING_PARTY_SUBADDRESS: + case Q931ie_CALLED_PARTY_NUMBER: + case Q931ie_CALLED_PARTY_SUBADDRESS: + case Q931ie_TRANSIT_NETWORK_SELECTION: + case Q931ie_LOW_LAYER_COMPATIBILITY: + case Q931ie_HIGH_LAYER_COMPATIBILITY: + case Q931ie_FACILITY: + case Q931ie_REDIRECTING_NUMBER: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + case Q931ie_REPEAT_INDICATOR: + if (ir < 2) { + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + ir++; + } else { + return Q931E_ILLEGAL_IE; + } + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + } else if (codeset == 6) { + switch (IBuf[IOff]) { + case Q931ie_GENERIC_DIGITS: + rc = Q931Uie[pTrunk->Dialect][IBuf[IOff]](pTrunk, mes, &IBuf[IOff], &mes->buf[OOff], &IOff, &OOff); + if (rc != Q931E_NO_ERROR) + return rc; + break; + default: + return Q931E_ILLEGAL_IE; + break; + } + + } else { + return Q931E_ILLEGAL_IE; + } + } + mes->Size = sizeof(Q931mes_Generic) - 1 + OOff; + return Q931E_NO_ERROR; +} + +/***************************************************************************** + + Function: nationalPmes_Setup + + Decription: Pack a Q931mes_Generic into a real Q.931 message. The user will + set up a SETUP message and issue this to the stack where it + is processed by Q931ProcSetup that processes and validates + it before it actually sends it out. This function is called + to compute the real Q.931 message. + + Parameters: IBuf[IN] Ptr to un-packed struct + ISize[IN] Size of input buffer (unpacked message). + OBuf[OUT] Ptr to packed 'octet' wise message. + OSize[OUT] Size of packed message. + + Called By: Q931ProcSetup + +*****************************************************************************/ +L3INT nationalPmes_Setup(Q931_TrunkInfo_t *pTrunk, Q931mes_Generic *IBuf, L3INT ISize, L3UCHAR *OBuf, L3INT *OSize) +{ + L3INT rc = Q931E_NO_ERROR; + Q931mes_Generic *pMes = (Q931mes_Generic *)IBuf; + L3INT Octet = 0; + + /* Q931 Message Header */ + Q931MesgHeader(pTrunk, pMes, OBuf, *OSize, &Octet); + + /* Sending Complete */ + if (Q931IsIEPresent(pMes->SendComplete)) { + OBuf[Octet++] = (L3UCHAR)(pMes->SendComplete & 0x00ff); + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->RepeatInd)) { + OBuf[Octet++] = (L3UCHAR)(pMes->RepeatInd & 0x00ff); + } + + /* Bearer capability */ + if (Q931IsIEPresent(pMes->BearerCap)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_BEARER_CAPABILITY](pTrunk, Q931GetIEPtr(pMes->BearerCap,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + else { + rc = Q931E_BEARERCAP; + } + + /* Channel Identification */ + if (Q931IsIEPresent(pMes->ChanID)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CHANNEL_IDENTIFICATION](pTrunk, Q931GetIEPtr(pMes->ChanID,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Progress indicator */ + if (Q931IsIEPresent(pMes->ProgInd)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_PROGRESS_INDICATOR](pTrunk, Q931GetIEPtr(pMes->ProgInd,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Network spesific facilities */ + if (Q931IsIEPresent(pMes->NetFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_NETWORK_SPECIFIC_FACILITIES](pTrunk, Q931GetIEPtr(pMes->NetFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Display */ + if (Q931IsIEPresent(pMes->Display)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DISPLAY](pTrunk, Q931GetIEPtr(pMes->Display,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Date/Time */ + if (Q931IsIEPresent(pMes->DateTime)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_DATETIME](pTrunk, Q931GetIEPtr(pMes->DateTime,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Keypad Facility */ + if (Q931IsIEPresent(pMes->KeypadFac)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_KEYPAD_FACILITY](pTrunk, Q931GetIEPtr(pMes->KeypadFac,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Signal */ + if (Q931IsIEPresent(pMes->Signal)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_SIGNAL](pTrunk, Q931GetIEPtr(pMes->Signal,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Number */ + if (Q931IsIEPresent(pMes->CallingNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CallingNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Calling Party Subaddress */ + if (Q931IsIEPresent(pMes->CallingSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLING_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CallingSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called Party number */ + if (Q931IsIEPresent(pMes->CalledNum)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_NUMBER](pTrunk, Q931GetIEPtr(pMes->CalledNum,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Called party subaddress */ + if (Q931IsIEPresent(pMes->CalledSub)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_CALLED_PARTY_SUBADDRESS](pTrunk, Q931GetIEPtr(pMes->CalledSub,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Transit network selection */ + if (Q931IsIEPresent(pMes->TransNetSel)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_TRANSIT_NETWORK_SELECTION](pTrunk, Q931GetIEPtr(pMes->TransNetSel,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* Repeat Indicator */ + if (Q931IsIEPresent(pMes->LLRepeatInd)) { + rc = Q931E_UNKNOWN_IE;/* TODO */ + } + + /* Low Layer Compatibility */ + if (Q931IsIEPresent(pMes->LLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_LOW_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->LLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + /* High Layer Compatibility */ + if (Q931IsIEPresent(pMes->HLComp)) { + if ((rc = Q931Pie[pTrunk->Dialect][Q931ie_HIGH_LAYER_COMPATIBILITY](pTrunk, Q931GetIEPtr(pMes->HLComp,pMes->buf), OBuf, &Octet)) != 0) + return rc; + } + + *OSize = Octet; + return rc; +} diff --git a/libs/openzap/src/libteletone_detect.c b/libs/openzap/src/libteletone_detect.c new file mode 100644 index 0000000000..61fef90149 --- /dev/null +++ b/libs/openzap/src/libteletone_detect.c @@ -0,0 +1,427 @@ +/* + * libteletone + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Much less efficient expansion interface was added to allow for the detection of + * a single arbitrary tone combination which may also exceed 2 simultaneous tones. + * (controlled by compile time constant TELETONE_MAX_TONES) + * + * Copyright (C) 2006 Anthony Minessale II + * + * libteletone_detect.c Tone Detection Code + * + * Copyright (c) 2007, Anthony Minessale II + * 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. + * + ********************************************************************************* + * + * Derived from tone_detect.c - General telephony tone detection, and specific + * detection of DTMF. + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * + * + */ + +#include + +#ifndef _MSC_VER +#include +#endif +#include +#include +#include +#include + + +static teletone_detection_descriptor_t dtmf_detect_row[GRID_FACTOR]; +static teletone_detection_descriptor_t dtmf_detect_col[GRID_FACTOR]; +static teletone_detection_descriptor_t dtmf_detect_row_2nd[GRID_FACTOR]; +static teletone_detection_descriptor_t dtmf_detect_col_2nd[GRID_FACTOR]; + +static float dtmf_row[] = {697.0f, 770.0f, 852.0f, 941.0f}; +static float dtmf_col[] = {1209.0f, 1336.0f, 1477.0f, 1633.0f}; + +static char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; + +static void goertzel_init(teletone_goertzel_state_t *goertzel_state, teletone_detection_descriptor_t *tdesc) { + goertzel_state->v2 = goertzel_state->v3 = 0.0; + goertzel_state->fac = tdesc->fac; +} + +TELETONE_API(void) teletone_goertzel_update(teletone_goertzel_state_t *goertzel_state, + int16_t sample_buffer[], + int samples) +{ + int i; + float v1; + + for (i = 0; i < samples; i++) { + v1 = goertzel_state->v2; + goertzel_state->v2 = goertzel_state->v3; + goertzel_state->v3 = (float)(goertzel_state->fac*goertzel_state->v2 - v1 + sample_buffer[i]); + } +} +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +#define teletone_goertzel_result(gs) (double)(((gs)->v3 * (gs)->v3 + (gs)->v2 * (gs)->v2 - (gs)->v2 * (gs)->v3 * (gs)->fac)) + +TELETONE_API(void) teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf_detect_state, int sample_rate) +{ + int i; + float theta; + + dtmf_detect_state->hit1 = dtmf_detect_state->hit2 = 0; + + for (i = 0; i < GRID_FACTOR; i++) { + theta = (float)(M_TWO_PI*(dtmf_row[i]/(float)sample_rate)); + dtmf_detect_row[i].fac = (float)(2.0*cos(theta)); + + theta = (float)(M_TWO_PI*(dtmf_col[i]/(float)sample_rate)); + dtmf_detect_col[i].fac = (float)(2.0*cos(theta)); + + theta = (float)(M_TWO_PI*(dtmf_row[i]*2.0/(float)sample_rate)); + dtmf_detect_row_2nd[i].fac = (float)(2.0*cos(theta)); + + theta = (float)(M_TWO_PI*(dtmf_col[i]*2.0/(float)sample_rate)); + dtmf_detect_col_2nd[i].fac = (float)(2.0*cos(theta)); + + goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]); + + dtmf_detect_state->energy = 0.0; + } + dtmf_detect_state->current_sample = 0; + dtmf_detect_state->detected_digits = 0; + dtmf_detect_state->lost_digits = 0; + dtmf_detect_state->digits[0] = '\0'; + dtmf_detect_state->mhit = 0; +} + +TELETONE_API(void) teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *map) +{ + float theta = 0; + int x = 0; + + if (!mt->sample_rate) { + mt->sample_rate = 8000; + } + + if (!mt->min_samples) { + mt->min_samples = 102; + } + + mt->min_samples *= (mt->sample_rate / 8000); + + if (!mt->positive_factor) { + mt->positive_factor = 2; + } + + if(!mt->negative_factor) { + mt->negative_factor = 10; + } + + if (!mt->hit_factor) { + mt->hit_factor = 2; + } + + for(x = 0; x < TELETONE_MAX_TONES; x++) { + if ((int) map->freqs[x] == 0) { + break; + } + mt->tone_count++; + theta = (float)(M_TWO_PI*(map->freqs[x]/(float)mt->sample_rate)); + mt->tdd[x].fac = (float)(2.0 * cos(theta)); + goertzel_init (&mt->gs[x], &mt->tdd[x]); + goertzel_init (&mt->gs2[x], &mt->tdd[x]); + } + +} + +TELETONE_API(int) teletone_multi_tone_detect (teletone_multi_tone_t *mt, + int16_t sample_buffer[], + int samples) +{ + int sample, limit = 0, j, x = 0; + float v1, famp; + float eng_sum = 0, eng_all[TELETONE_MAX_TONES] = {0.0}; + int gtest = 0, see_hit = 0; + + for (sample = 0; sample >= 0 && sample < samples; sample = limit) { + mt->total_samples++; + + if ((samples - sample) >= (mt->min_samples - mt->current_sample)) { + limit = sample + (mt->min_samples - mt->current_sample); + } else { + limit = samples; + } + if (limit < 0 || limit > samples) { + limit = samples; + } + + for (j = sample; j < limit; j++) { + famp = sample_buffer[j]; + + mt->energy += famp*famp; + + for(x = 0; x < TELETONE_MAX_TONES && x < mt->tone_count; x++) { + v1 = mt->gs[x].v2; + mt->gs[x].v2 = mt->gs[x].v3; + mt->gs[x].v3 = (float)(mt->gs[x].fac * mt->gs[x].v2 - v1 + famp); + + v1 = mt->gs2[x].v2; + mt->gs2[x].v2 = mt->gs2[x].v3; + mt->gs2[x].v3 = (float)(mt->gs2[x].fac*mt->gs2[x].v2 - v1 + famp); + } + } + + mt->current_sample += (limit - sample); + if (mt->current_sample < mt->min_samples) { + continue; + } + + eng_sum = 0; + for(x = 0; x < TELETONE_MAX_TONES && x < mt->tone_count; x++) { + eng_all[x] = (float)(teletone_goertzel_result (&mt->gs[x])); + eng_sum += eng_all[x]; + } + + gtest = 0; + for(x = 0; x < TELETONE_MAX_TONES && x < mt->tone_count; x++) { + gtest += teletone_goertzel_result (&mt->gs2[x]) < eng_all[x] ? 1 : 0; + } + + if ((gtest >= 2 || gtest == mt->tone_count) && eng_sum > 42.0 * mt->energy) { + if(mt->negatives) { + mt->negatives--; + } + mt->positives++; + + if(mt->positives >= mt->positive_factor) { + mt->hits++; + } + if (mt->hits >= mt->hit_factor) { + see_hit++; + mt->positives = mt->negatives = mt->hits = 0; + } + } else { + mt->negatives++; + if(mt->positives) { + mt->positives--; + } + if(mt->negatives > mt->negative_factor) { + mt->positives = mt->hits = 0; + } + } + + /* Reinitialise the detector for the next block */ + for(x = 0; x < TELETONE_MAX_TONES && x < mt->tone_count; x++) { + goertzel_init (&mt->gs[x], &mt->tdd[x]); + goertzel_init (&mt->gs2[x], &mt->tdd[x]); + } + + mt->energy = 0.0; + mt->current_sample = 0; + } + + return see_hit; +} + + +TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, + int16_t sample_buffer[], + int samples) +{ + float row_energy[GRID_FACTOR]; + float col_energy[GRID_FACTOR]; + float famp; + float v1; + int i; + int j; + int sample; + int best_row; + int best_col; + char hit; + int limit; + + hit = 0; + for (sample = 0; sample < samples; sample = limit) { + /* BLOCK_LEN is optimised to meet the DTMF specs. */ + if ((samples - sample) >= (BLOCK_LEN - dtmf_detect_state->current_sample)) { + limit = sample + (BLOCK_LEN - dtmf_detect_state->current_sample); + } else { + limit = samples; + } + + for (j = sample; j < limit; j++) { + int x = 0; + famp = sample_buffer[j]; + + dtmf_detect_state->energy += famp*famp; + + for(x = 0; x < GRID_FACTOR; x++) { + v1 = dtmf_detect_state->row_out[x].v2; + dtmf_detect_state->row_out[x].v2 = dtmf_detect_state->row_out[x].v3; + dtmf_detect_state->row_out[x].v3 = (float)(dtmf_detect_state->row_out[x].fac*dtmf_detect_state->row_out[x].v2 - v1 + famp); + + v1 = dtmf_detect_state->col_out[x].v2; + dtmf_detect_state->col_out[x].v2 = dtmf_detect_state->col_out[x].v3; + dtmf_detect_state->col_out[x].v3 = (float)(dtmf_detect_state->col_out[x].fac*dtmf_detect_state->col_out[x].v2 - v1 + famp); + + v1 = dtmf_detect_state->col_out2nd[x].v2; + dtmf_detect_state->col_out2nd[x].v2 = dtmf_detect_state->col_out2nd[x].v3; + dtmf_detect_state->col_out2nd[x].v3 = (float)(dtmf_detect_state->col_out2nd[x].fac*dtmf_detect_state->col_out2nd[x].v2 - v1 + famp); + + v1 = dtmf_detect_state->row_out2nd[x].v2; + dtmf_detect_state->row_out2nd[x].v2 = dtmf_detect_state->row_out2nd[x].v3; + dtmf_detect_state->row_out2nd[x].v3 = (float)(dtmf_detect_state->row_out2nd[x].fac*dtmf_detect_state->row_out2nd[x].v2 - v1 + famp); + } + + } + + dtmf_detect_state->current_sample += (limit - sample); + if (dtmf_detect_state->current_sample < BLOCK_LEN) { + continue; + } + /* We are at the end of a DTMF detection block */ + /* Find the peak row and the peak column */ + row_energy[0] = teletone_goertzel_result (&dtmf_detect_state->row_out[0]); + col_energy[0] = teletone_goertzel_result (&dtmf_detect_state->col_out[0]); + + for (best_row = best_col = 0, i = 1; i < GRID_FACTOR; i++) { + row_energy[i] = teletone_goertzel_result (&dtmf_detect_state->row_out[i]); + if (row_energy[i] > row_energy[best_row]) { + best_row = i; + } + col_energy[i] = teletone_goertzel_result (&dtmf_detect_state->col_out[i]); + if (col_energy[i] > col_energy[best_col]) { + best_col = i; + } + } + hit = 0; + /* Basic signal level test and the twist test */ + if (row_energy[best_row] >= DTMF_THRESHOLD && + col_energy[best_col] >= DTMF_THRESHOLD && + col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST && + col_energy[best_col]*DTMF_NORMAL_TWIST > row_energy[best_row]) { + /* Relative peak test */ + for (i = 0; i < GRID_FACTOR; i++) { + if ((i != best_col && col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) || + (i != best_row && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) { + break; + } + } + /* ... and second harmonic test */ + if (i >= GRID_FACTOR && (row_energy[best_row] + col_energy[best_col]) > 42.0*dtmf_detect_state->energy && + teletone_goertzel_result (&dtmf_detect_state->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col] && + teletone_goertzel_result (&dtmf_detect_state->row_out2nd[best_row])*DTMF_2ND_HARMONIC_ROW < row_energy[best_row]) { + hit = dtmf_positions[(best_row << 2) + best_col]; + /* Look for two successive similar results */ + /* The logic in the next test is: + We need two successive identical clean detects, with + something different preceeding it. This can work with + back to back differing digits. More importantly, it + can work with nasty phones that give a very wobbly start + to a digit. */ + if (hit == dtmf_detect_state->hit3 && dtmf_detect_state->hit3 != dtmf_detect_state->hit2) { + dtmf_detect_state->mhit = hit; + dtmf_detect_state->digit_hits[(best_row << 2) + best_col]++; + dtmf_detect_state->detected_digits++; + if (dtmf_detect_state->current_digits < TELETONE_MAX_DTMF_DIGITS) { + dtmf_detect_state->digits[dtmf_detect_state->current_digits++] = hit; + dtmf_detect_state->digits[dtmf_detect_state->current_digits] = '\0'; + } + else + { + dtmf_detect_state->lost_digits++; + } + } + } + } + dtmf_detect_state->hit1 = dtmf_detect_state->hit2; + dtmf_detect_state->hit2 = dtmf_detect_state->hit3; + dtmf_detect_state->hit3 = hit; + /* Reinitialise the detector for the next block */ + for (i = 0; i < GRID_FACTOR; i++) { + goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]); + } + dtmf_detect_state->energy = 0.0; + dtmf_detect_state->current_sample = 0; + } + if ((!dtmf_detect_state->mhit) || (dtmf_detect_state->mhit != hit)) { + dtmf_detect_state->mhit = 0; + return(0); + } + return (hit); +} + + +TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, + char *buf, + int max) +{ + teletone_assert(dtmf_detect_state->current_digits <= TELETONE_MAX_DTMF_DIGITS); + + if (max > dtmf_detect_state->current_digits) { + max = dtmf_detect_state->current_digits; + } + if (max > 0) { + memcpy (buf, dtmf_detect_state->digits, max); + memmove (dtmf_detect_state->digits, dtmf_detect_state->digits + max, dtmf_detect_state->current_digits - max); + dtmf_detect_state->current_digits -= max; + } + buf[max] = '\0'; + return max; +} + +/* 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: + */ diff --git a/libs/openzap/src/libteletone_generate.c b/libs/openzap/src/libteletone_generate.c new file mode 100644 index 0000000000..1d5d696af2 --- /dev/null +++ b/libs/openzap/src/libteletone_generate.c @@ -0,0 +1,464 @@ +/* + * libteletone_generate.c -- Tone Generator + * + * Copyright (c) 2007, Anthony Minessale II + * 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 + +#define SMAX 32767 +#define SMIN -32768 +#define normalize_to_16bit(n) if (n > SMAX) n = SMAX; else if (n < SMIN) n = SMIN; + +#ifdef _MSC_VER +#pragma warning(disable:4706) +#endif + +TELETONE_API_DATA int16_t TELETONE_SINES[SINE_TABLE_MAX] = { + 0x00c9, 0x025b, 0x03ed, 0x057f, 0x0711, 0x08a2, 0x0a33, 0x0bc4, + 0x0d54, 0x0ee4, 0x1073, 0x1201, 0x138f, 0x151c, 0x16a8, 0x1833, + 0x19be, 0x1b47, 0x1cd0, 0x1e57, 0x1fdd, 0x2162, 0x22e5, 0x2467, + 0x25e8, 0x2768, 0x28e5, 0x2a62, 0x2bdc, 0x2d55, 0x2ecc, 0x3042, + 0x31b5, 0x3327, 0x3497, 0x3604, 0x3770, 0x38d9, 0x3a40, 0x3ba5, + 0x3d08, 0x3e68, 0x3fc6, 0x4121, 0x427a, 0x43d1, 0x4524, 0x4675, + 0x47c4, 0x490f, 0x4a58, 0x4b9e, 0x4ce1, 0x4e21, 0x4f5e, 0x5098, + 0x51cf, 0x5303, 0x5433, 0x5560, 0x568a, 0x57b1, 0x58d4, 0x59f4, + 0x5b10, 0x5c29, 0x5d3e, 0x5e50, 0x5f5e, 0x6068, 0x616f, 0x6272, + 0x6371, 0x646c, 0x6564, 0x6657, 0x6747, 0x6832, 0x691a, 0x69fd, + 0x6add, 0x6bb8, 0x6c8f, 0x6d62, 0x6e31, 0x6efb, 0x6fc2, 0x7083, + 0x7141, 0x71fa, 0x72af, 0x735f, 0x740b, 0x74b3, 0x7556, 0x75f4, + 0x768e, 0x7723, 0x77b4, 0x7840, 0x78c8, 0x794a, 0x79c9, 0x7a42, + 0x7ab7, 0x7b27, 0x7b92, 0x7bf9, 0x7c5a, 0x7cb7, 0x7d0f, 0x7d63, + 0x7db1, 0x7dfb, 0x7e3f, 0x7e7f, 0x7eba, 0x7ef0, 0x7f22, 0x7f4e, + 0x7f75, 0x7f98, 0x7fb5, 0x7fce, 0x7fe2, 0x7ff1, 0x7ffa, 0x7fff +}; + + +TELETONE_API(int) teletone_set_tone(teletone_generation_session_t *ts, int index, ...) +{ + va_list ap; + int i = 0; + teletone_process_t x = 0; + + va_start(ap, index); + while (i < TELETONE_MAX_TONES && (x = va_arg(ap, teletone_process_t))) { + ts->TONES[index].freqs[i++] = x; + } + va_end(ap); + + return (i > TELETONE_MAX_TONES) ? -1 : 0; + +} + +TELETONE_API(int) teletone_set_map(teletone_tone_map_t *map, ...) +{ + va_list ap; + int i = 0; + teletone_process_t x = 0; + + va_start(ap, map); + while (i < TELETONE_MAX_TONES && (x = va_arg(ap, teletone_process_t))) { + map->freqs[i++] = x; + } + va_end(ap); + + return (i > TELETONE_MAX_TONES) ? -1 : 0; + +} + +TELETONE_API(int) teletone_init_session(teletone_generation_session_t *ts, int buflen, tone_handler handler, void *user_data) +{ + memset(ts, 0, sizeof(*ts)); + ts->rate = 8000; + ts->channels = 1; + ts->duration = 2000; + ts->wait = 500; + ts->tmp_duration = -1; + ts->tmp_wait = -1; + ts->handler = handler; + ts->user_data = user_data; + ts->volume = -7; + ts->decay_step = 0; + ts->decay_factor = 1; + if (buflen) { + if ((ts->buffer = calloc(buflen, sizeof(teletone_audio_t))) == 0) { + return -1; + } + ts->datalen = buflen; + } else { + ts->dynamic = 1024; + } + /* Add Standard DTMF Tones */ + teletone_set_tone(ts, '1', 697.0, 1209.0, 0.0); + teletone_set_tone(ts, '2', 697.0, 1336.0, 0.0); + teletone_set_tone(ts, '3', 697.0, 1477.0, 0.0); + teletone_set_tone(ts, 'A', 697.0, 1633.0, 0.0); + teletone_set_tone(ts, '4', 770.0, 1209.0, 0.0); + teletone_set_tone(ts, '5', 770.0, 1336.0, 0.0); + teletone_set_tone(ts, '6', 770.0, 1477.0, 0.0); + teletone_set_tone(ts, 'B', 770.0, 1633.0, 0.0); + teletone_set_tone(ts, '7', 859.0, 1209.0, 0.0); + teletone_set_tone(ts, '8', 859.0, 1336.0, 0.0); + teletone_set_tone(ts, '9', 859.0, 1477.0, 0.0); + teletone_set_tone(ts, 'C', 859.0, 1633.0, 0.0); + teletone_set_tone(ts, '*', 941.0, 1209.0, 0.0); + teletone_set_tone(ts, '0', 941.0, 1336.0, 0.0); + teletone_set_tone(ts, '#', 941.0, 1477.0, 0.0); + teletone_set_tone(ts, 'D', 941.0, 1633.0, 0.0); + + return 0; +} + +TELETONE_API(int) teletone_destroy_session(teletone_generation_session_t *ts) +{ + if (ts->buffer) { + free(ts->buffer); + ts->buffer = NULL; + ts->samples = 0; + } + return 0; +} + +static int ensure_buffer(teletone_generation_session_t *ts, int need) +{ + need += ts->samples; + need *= sizeof(teletone_audio_t); + need *= ts->channels; + + if (need > ts->datalen) { + teletone_audio_t *tmp; + ts->datalen = need + ts->dynamic; + tmp = realloc(ts->buffer, ts->datalen); + if (!tmp) { + return -1; + } + ts->buffer = tmp; + } + + return 0; +} + +TELETONE_API(int) teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + /*teletone_process_t period = (1.0 / ts->rate) / ts->channels;*/ + int i, c; + int freqlen = 0; + teletone_dds_state_t tones[TELETONE_MAX_TONES+1]; + //int decay = 0; + int duration; + int wait = 0; + int32_t sample; + int32_t dc = 0; + float vol = ts->volume; + ts->samples = 0; + memset(tones, 0, sizeof(tones[0]) * TELETONE_MAX_TONES); + duration = (ts->tmp_duration > -1) ? ts->tmp_duration : ts->duration; + wait = (ts->tmp_wait > -1) ? ts->tmp_wait : ts->wait; + + if (map->freqs[0] > 0) { + for (freqlen = 0; freqlen < TELETONE_MAX_TONES && map->freqs[freqlen]; freqlen++) { + teletone_dds_state_set_tone(&tones[freqlen], map->freqs[freqlen], ts->rate, 0); + teletone_dds_state_set_tx_level(&tones[freqlen], vol); + } + + if (ts->channels > 1) { + duration *= ts->channels; + } + + if (ts->dynamic) { + if (ensure_buffer(ts, duration)) { + return -1; + } + } + + for (ts->samples = 0; ts->samples < ts->datalen && ts->samples < duration; ts->samples++) { + if (ts->decay_direction && ++dc >= ts->decay_step) { + float nvol = vol + ts->decay_direction * ts->decay_factor; + int j; + + if (nvol <= TELETONE_VOL_DB_MAX && nvol >= TELETONE_VOL_DB_MIN) { + vol = nvol; + for (j = 0; j < TELETONE_MAX_TONES && map->freqs[j]; j++) { + teletone_dds_state_set_tx_level(&tones[j], vol); + } + dc = 0; + } + } + + sample = 128; + + for (i = 0; i < freqlen; i++) { + int32_t s = teletone_dds_state_modulate_sample(&tones[i], 0); + sample += s; + } + sample /= freqlen; + ts->buffer[ts->samples] = (teletone_audio_t)sample; + + for (c = 1; c < ts->channels; c++) { + ts->buffer[ts->samples+1] = ts->buffer[ts->samples]; + ts->samples++; + } + + } + } + if (ts->dynamic) { + if (ensure_buffer(ts, wait)) { + return -1; + } + } + for (c = 0; c < ts->channels; c++) { + for (i = 0; i < wait && ts->samples < ts->datalen; i++) { + ts->buffer[ts->samples++] = 0; + } + } + + if (ts->debug && ts->debug_stream) { + if (map->freqs[0] <= 0) { + fprintf(ts->debug_stream, "wait %d (%dms)\n", wait, wait / (ts->rate / 1000)); + } else { + fprintf(ts->debug_stream, "Generate: ("); + + for (i = 0; i < TELETONE_MAX_TONES && map->freqs[i]; i++) { + fprintf(ts->debug_stream, "%s%0.2f", i == 0 ? "" : "+",map->freqs[i]); + } + + fprintf(ts->debug_stream, + ") [volume %0.2fdB; samples %d(%dms) x %d channel%s; wait %d(%dms); decay_factor %0.2fdB; decay_step %d(%dms); wrote %d bytes]\n", + ts->volume, + duration, + duration / (ts->rate / 1000), + ts->channels, + ts->channels == 1 ? "" : "s", + wait, + wait / (ts->rate / 1000), + ts->decay_factor, + ts->decay_step, + ts->decay_step / (ts->rate / 1000), + ts->samples * 2); + } + } + return ts->samples; +} + +/* don't ask */ +static char *my_strdup (const char *s) +{ + size_t len = strlen (s) + 1; + void *new = malloc (len); + + if (new == NULL) { + return NULL; + } + + return (char *) memcpy (new, s, len); +} + +TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cmd) +{ + char *data = NULL, *cur = NULL, *end = NULL; + int var = 0, LOOPING = 0; + + if (!cmd) { + return -1; + } + + do { + if (!(data = my_strdup(cmd))) { + return -1; + } + + cur = data; + + while (*cur) { + var = 0; + if (*cur == ' ' || *cur == '\r' || *cur == '\n') { + cur++; + continue; + } + + if ((end = strchr(cur, ';')) != 0) { + *end++ = '\0'; + } + + if (*(cur + 1) == '=') { + var = 1; + switch(*cur) { + case 'c': + ts->channels = atoi(cur + 2); + break; + case 'r': + ts->rate = atoi(cur + 2); + break; + case 'd': + ts->duration = atoi(cur + 2) * (ts->rate / 1000); + break; + case 'v': + { + float vol = (float)atof(cur + 2); + if (vol <= TELETONE_VOL_DB_MAX && vol >= TELETONE_VOL_DB_MIN) { + ts->volume = vol; + } + } + break; + case '>': + ts->decay_step = atoi(cur + 2) * (ts->rate / 1000); + ts->decay_direction = -1; + break; + case '<': + ts->decay_step = atoi(cur + 2) * (ts->rate / 1000); + ts->decay_direction = 1; + break; + case '+': + ts->decay_factor = (float)atof(cur + 2); + break; + case 'w': + ts->wait = atoi(cur + 2) * (ts->rate / 1000); + break; + case 'l': + ts->loops = atoi(cur + 2); + break; + case 'L': + if (!LOOPING) { + ts->LOOPS = atoi(cur + 2); + } + LOOPING++; + break; + } + } else { + while (*cur) { + char *p = NULL, *e = NULL; + teletone_tone_map_t mymap, *mapp = NULL; + + if (*cur == ' ' || *cur == '\r' || *cur == '\n') { + cur++; + continue; + } + + ts->tmp_duration = -1; + ts->tmp_wait = -1; + + memset(&mymap, 0, sizeof(mymap)); + + if (*(cur + 1) == '(') { + p = cur + 2; + if (*cur) { + char *next; + int i = 0; + if ((e = strchr(p, ')')) != 0) { + *e++ = '\0'; + } + do { + if ((next = strchr(p, ',')) != 0) { + *next++ = '\0'; + } + if (i == 0) { + ts->tmp_duration = atoi(p) * (ts->rate / 1000); + i++; + } else if (i == 1) { + ts->tmp_wait = atoi(p) * (ts->rate / 1000); + i++; + } else { + mymap.freqs[i++ - 2] = atof(p); + } + p = next; + + } while (next && (i-2) < TELETONE_MAX_TONES); + if (i > 2 && *cur == '%') { + mapp = &mymap; + } else if ((i != 2 || *cur == '%')) { + if (ts->debug && ts->debug_stream) { + fprintf(ts->debug_stream, "Syntax Error!\n"); + } + goto bottom; + } + } + } + + if (*cur && !mapp) { + if (*cur > 0 && *cur < TELETONE_TONE_RANGE) { + mapp = &ts->TONES[(int)*cur]; + } else if (ts->debug && ts->debug_stream) { + fprintf(ts->debug_stream, "Map [%c] Out Of Range!\n", *cur); + } + } + + if (mapp) { + if (mapp->freqs[0]) { + if (ts->handler) { + do { + ts->handler(ts, mapp); + if (ts->loops > 0) { + ts->loops--; + } + } while (ts->loops); + } + } else if (ts->debug && ts->debug_stream) { + fprintf(ts->debug_stream, "Ignoring Empty Map [%c]!\n", *cur); + } + } + + if (e) { + cur = e; + } else { + cur++; + } + } + } + + if (end) { + cur = end; + } else if (*cur){ + cur++; + } + } + bottom: + free(data); + data = NULL; + if (ts->LOOPS > 0) { + ts->LOOPS--; + } + + } while (ts->LOOPS); + + return 0; +} + +/* 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: + */ diff --git a/libs/openzap/src/m3ua/mstm3ua.c b/libs/openzap/src/m3ua/mstm3ua.c new file mode 100644 index 0000000000..1d8179c58d --- /dev/null +++ b/libs/openzap/src/m3ua/mstm3ua.c @@ -0,0 +1,62 @@ +/* WARNING WORK IN PROGRESS + * mstm3ua.c + * mstss7d port + * + * Created by Shane Burrell on 2/2/08. + * Copyright 2008 Shane Burrell. 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 "mstm3ua.h" + + + + + +int build_m3ua_hdr(unsigned char len,unsigned char *bytemsg) + +{ + + *bytemsg++ = M_VERSION_REL1; // 1 Verison + //bytemsg[1] = 0x00; // 2 RESERVED + //bytemsg[2] = M_CLASS_XFER; // 3 Msg Class + //SS7 BOX Kludge + *bytemsg++ = 0x01; // 2 RESERVED + *bytemsg++ = 0x00; // 2 RESERVED + + *bytemsg++ = M_TYPE_DATA ; // 4 Msg Type + + *bytemsg++ = len; // 5 Msg LENGTH 81 32bit field + *bytemsg++ = 0x00; // 6 + *bytemsg++ = 0x00; // 7 + *bytemsg++ = 0x00; // 8 + return(0); + +}; \ No newline at end of file diff --git a/libs/openzap/src/m3ua/mstm3ua.h b/libs/openzap/src/m3ua/mstm3ua.h new file mode 100644 index 0000000000..13527dac35 --- /dev/null +++ b/libs/openzap/src/m3ua/mstm3ua.h @@ -0,0 +1,96 @@ +/* + * mstm3ua.h + * mstss7d + * + * Created by Shane Burrell on 3/2/08. + * Copyright 2008 Shane Burrell. 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. + */ +typedef unsigned long m3ua_ulong; +typedef unsigned short m3ua_ushort; +typedef unsigned char m3ua_uchar; + +typedef unsigned char u8; +typedef unsigned short u16; /* Note: multi-byte values are little-endian */ +typedef unsigned long u32; + + + + +#define M_TAG_NETWORK_APPEARANCE 1 +#define M_TAG_PROTOCOL_DATA 3 +#define M_TAG_INFO_STRING 4 +#define M_TAG_AFFECTED_DPC 5 +#define M_TAG_ROUTING_CONTEXT 6 +#define M_TAG_DIAGNOSTIC_INFORMATION 7 +#define M_TAG_HEARTBEAT_DATA 8 +#define M_TAG_UNAVAILABILITY_CAUSE 9 +#define M_TAG_REASON 10 +#define M_TAG_TRAFFIC_MODE_TYPE 11 +#define M_TAG_ERROR_CODE 12 +#define M_TAG_STATUS_TYPE 13 +#define M_TAG_CONGESTED_INDICATIONS 14 + +#define M_VERSION_REL1 1 + +#define M_CLASS_MGMT 0x00 +#define M_CLASS_XFER 0x01 +#define M_CLASS_SSNM 0x02 +#define M_CLASS_ASPSM 0x03 +#define M_CLASS_ASPTM 0x04 +#define M_CLASS_RKM 0x09 + +#define M_TYPE_ERR (0|M_CLASS_MGMT + +#define M_TYPE_NTFY (1|M_CLASS_XFER) +#define M_TYPE_DATA (1|M_CLASS_XFER) + +#define M_TYPE_DUNA (1|M_CLASS_SSNM) +#define M_TYPE_DAVA (2|M_CLASS_SSNM) +#define M_TYPE_DUAD (3|M_CLASS_SSNM) +#define M_TYPE_SCON (4|M_CLASS_SSNM) +#define M_TYPE_DUPU (5|M_CLASS_SSNM) + +#define M_TYPE_UP (1|M_CLASS_ASPSM) +#define M_TYPE_DOWN (2|M_CLASS_ASPSM) +#define M_TYPE_BEAT (3|M_CLASS_ASPSM) +#define M_TYPE_UP_ACK (4|M_CLASS_ASPSM) +#define M_TYPE_DOWN_ACK (5|M_CLASS_ASPSM) +#define M_TYPE_BEAT_ACK (6|M_CLASS_ASPSM) + +#define M_TYPE_ACTIVE (1|M_CLASS_ASPTM) +#define M_TYPE_INACTIVE (2|M_CLASS_ASPTM) +#define M_TYPE_ACTIVE_ACK (3|M_CLASS_ASPTM) +#define M_TYPE_INACTIVE_ACK (4|M_CLASS_ASPTM) + +#define M_CLASS_MASK 0xff00 +#define M_TYPE_MASK 0x00ff + diff --git a/libs/openzap/src/m3ua_client.c b/libs/openzap/src/m3ua_client.c new file mode 100644 index 0000000000..ba33dd4317 --- /dev/null +++ b/libs/openzap/src/m3ua_client.c @@ -0,0 +1,333 @@ +/* + * m3ua_client.c + * openzap + * + * Created by Shane Burrell on 4/3/08. + * Copyright 2008 Shane Burrell. All rights reserved. + * + * + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * 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. + */ + +#if HAVE_NETDB_H +#include +#endif + +#include "openzap.h" +#include + + +#ifndef HAVE_GETHOSTBYNAME_R +extern int gethostbyname_r (const char *__name, + struct hostent *__result_buf, + char *__buf, size_t __buflen, + struct hostent **__result, + int *__h_errnop); +#endif + +struct m3uac_map { + uint32_t event_id; + const char *name; +}; + +static struct m3uac_map m3uac_table[] = { + {M3UA_EVENT_CALL_START, "CALL_START"}, + {M3UA_EVENT_CALL_START_ACK, "CALL_START_ACK"}, + {M3UA_EVENT_CALL_START_NACK, "CALL_START_NACK"}, + {M3UA_EVENT_CALL_START_NACK_ACK, "CALL_START_NACK_ACK"}, + {M3UA_EVENT_CALL_ANSWERED, "CALL_ANSWERED"}, + {M3UA_EVENT_CALL_STOPPED, "CALL_STOPPED"}, + {M3UA_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"}, + {M3UA_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"}, + {M3UA_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"}, + {M3UA_EVENT_HEARTBEAT, "HEARTBEAT"}, + {M3UA_EVENT_INSERT_CHECK_LOOP, "LOOP START"}, + {M3UA_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"} +}; + + + +static int create_conn_socket(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) +{ + int rc; + struct hostent *result, *local_result; + char buf[512], local_buf[512]; + int err = 0; + + memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp)); + memset(&mcon->local_hp, 0, sizeof(mcon->local_hp)); + mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); + + zap_log(ZAP_LOG_DEBUG, "Creating L=%s:%d R=%s:%d\n", + local_ip,local_port,ip,port); + + if (mcon->socket >= 0) { + int flag; + + flag = 1; + gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + mcon->remote_addr.sin_family = mcon->remote_hp.h_addrtype; + memcpy((char *) &mcon->remote_addr.sin_addr.s_addr, mcon->remote_hp.h_addr_list[0], mcon->remote_hp.h_length); + mcon->remote_addr.sin_port = htons(port); + + mcon->local_addr.sin_family = mcon->local_hp.h_addrtype; + memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length); + mcon->local_addr.sin_port = htons(local_port); + + + setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY, (char *)&flag, sizeof(int)); + + rc=listen(mcon->socket,100); + if (rc) { + close(mcon->socket); + mcon->socket = -1; + + } + } + } + + zap_mutex_create(&mcon->mutex); + + return mcon->socket; +} + +int m3uac_connection_close(m3uac_connection_t *mcon) +{ + if (mcon->socket > -1) { + close(mcon->socket); + } + + zap_mutex_lock(mcon->mutex); + zap_mutex_unlock(mcon->mutex); + zap_mutex_destroy(&mcon->mutex); + memset(mcon, 0, sizeof(*mcon)); + mcon->socket = -1; + + return 0; +} + +int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) +{ + create_conn_socket(mcon, local_ip, local_port, ip, port); + return mcon->socket; +} + + +int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause) +{ + m3uac_event_t oevent; + int retry = 5; + + m3uac_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (cmd == SIGBOOST_EVENT_SYSTEM_RESTART) { + mcon->rxseq_reset = 1; + mcon->txseq = 0; + mcon->rxseq = 0; + mcon->txwindow = 0; + } + + if (id >= 0) { + oevent.call_setup_id = id; + } + + while (m3uac_connection_write(mcon, &oevent) <= 0) { + if (--retry <= 0) { + zap_log(ZAP_LOG_CRIT, "Failed to tx on M3UA socket: %s\n", strerror(errno)); + return -1; + } else { + zap_log(ZAP_LOG_WARNING, "Failed to tx on M3UA socket: %s :retry %i\n", strerror(errno), retry); + zap_sleep(1); + } + } + + return 0; +} + + + +m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration) +{ + unsigned int fromlen = sizeof(struct sockaddr_in); + int bytes = 0; + + bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, + (struct sockaddr *) &mcon->local_addr, &fromlen); + + if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) { + + if (mcon->rxseq_reset) { + if (mcon->event.event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { + zap_log(ZAP_LOG_DEBUG, "Rx sync ok\n"); + mcon->rxseq = mcon->event.fseqno; + return &mcon->event; + } + errno=EAGAIN; + zap_log(ZAP_LOG_DEBUG, "Waiting for rx sync...\n"); + return NULL; + } + + mcon->txwindow = mcon->txseq - mcon->event.bseqno; + mcon->rxseq++; + + if (mcon->rxseq != mcon->event.fseqno) { + zap_log(ZAP_LOG_CRIT, "Invalid Sequence Number Expect=%i Rx=%i\n", mcon->rxseq, mcon->event.fseqno); + return NULL; + } + + return &mcon->event; + } else { + if (iteration == 0) { + zap_log(ZAP_LOG_CRIT, "Invalid Event length from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); + return NULL; + } + } + + return NULL; +} + +m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration) +{ + unsigned int fromlen = sizeof(struct sockaddr_in); + int bytes = 0; + + bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, (struct sockaddr *) &mcon->local_addr, &fromlen); + + if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) { + return &mcon->event; + } else { + if (iteration == 0) { + zap_log(ZAP_LOG_CRIT, "Critical Error: PQ Invalid Event lenght from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); + return NULL; + } + } + + return NULL; +} + + +int m3uac_connection_write(m3uac_connection_t *mcon, ss7bc_event_t *event) +{ + int err; + + if (!event || mcon->socket < 0 || !mcon->mutex) { + zap_log(ZAP_LOG_DEBUG, "Critical Error: No Event Device\n"); + return -EINVAL; + } + + if (event->span > 16 || event->chan > 31) { + zap_log(ZAP_LOG_CRIT, "Critical Error: TX Cmd=%s Invalid Span=%i Chan=%i\n", m3uac_event_id_name(event->event_id), event->span,event->chan); + return -1; + } + + gettimeofday(&event->tv,NULL); + + zap_mutex_lock(mcon->mutex); + event->fseqno = mcon->txseq++; + event->bseqno = mcon->rxseq; + err = sendto(mcon->socket, event, sizeof(m3uac_event_t), 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); + zap_mutex_unlock(mcon->mutex); + + if (err != sizeof(m3uac_event_t)) { + err = -1; + } + + zap_log(ZAP_LOG_DEBUG, "TX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", + m3uac_event_id_name(event->event_id), + event->event_id, + event->span+1, + event->chan+1, + event->release_cause, + event->call_setup_id, + event->fseqno, + (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), + (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") + ); + + return err; +} + +void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id) +{ + memset(event, 0, sizeof(m3uac_event_t)); + event->event_id = M3UA_EVENT_CALL_START; + + if (calling) { + strncpy((char*)event->calling_number_digits, calling, sizeof(event->calling_number_digits)-1); + event->calling_number_digits_count = strlen(calling); + } + + if (called) { + strncpy((char*)event->called_number_digits, called, sizeof(event->called_number_digits)-1); + event->called_number_digits_count = strlen(called); + } + + event->call_setup_id = setup_id; + +} + +void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span) +{ + memset(event, 0, sizeof(ss7bc_event_t)); + event->event_id = event_id; + event->chan = chan; + event->span = span; +} + +const char *m3uac_event_id_name(uint32_t event_id) +{ + unsigned int x; + const char *ret = NULL; + + for (x = 0 ; x < sizeof(m3uac_table)/sizeof(struct m3uac_map); x++) { + if (m3uac_table[x].event_id == event_id) { + ret = m3uac_table[x].name; + break; + } + } + + return ret; +} + +/* 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: + */ + + diff --git a/libs/openzap/src/m3ua_client.h b/libs/openzap/src/m3ua_client.h new file mode 100644 index 0000000000..b80309017c --- /dev/null +++ b/libs/openzap/src/m3ua_client.h @@ -0,0 +1,164 @@ +/* + * m3ua_client.h + * openzap + * + * Created by Shane Burrell on 4/3/08. + * Copyright 2008 Shane Burrell. All rights reserved. + * + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Fix this for portability +#include +//#include +#include +#include +#include +//#include +#include + +#define MAX_DIALED_DIGITS 31 +#define MAX_CALLING_NAME 31 + +/* Next two defines are used to create the range of values for call_setup_id + * in the t_sigboost structure. + * 0..((CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN) - 1) */ +#define CORE_MAX_SPANS 200 +#define CORE_MAX_CHAN_PER_SPAN 30 +#define MAX_PENDING_CALLS CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN +/* 0..(MAX_PENDING_CALLS-1) is range of call_setup_id below */ +#define SIZE_RDNIS 80 + +//#undef MSGWINDOW +#define MSGWINDOW + + +typedef struct +{ + uint32_t event_id; + uint32_t fseqno; +#ifdef MSGWINDOW + uint32_t bseqno; +#endif + uint16_t call_setup_id; + uint32_t trunk_group; + uint32_t span; + uint32_t chan; + uint8_t called_number_digits_count; + char called_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ + uint8_t calling_number_digits_count; /* it's an array */ + char calling_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ + uint8_t release_cause; + struct timeval tv; + /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ + uint8_t calling_number_screening_ind; + uint8_t calling_number_presentation; + char redirection_string [SIZE_RDNIS]; /* it's a null terminated string */ + +} t_m3ua; + +typedef t_m3ua m3uac_event_t; +typedef uint32_t m3uac_event_id_t; + + +typedef struct m3uac_ip_cfg +{ + char local_ip[25]; + int local_port; + char remote_ip[25]; + int remote_port; +}m3uac_ip_cfg_t; + +struct m3uac_connection { + zap_socket_t socket; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + m3uac_event_t event; + struct hostent remote_hp; + struct hostent local_hp; + unsigned int flags; + zap_mutex_t *mutex; + FILE *log; + unsigned int txseq; + unsigned int rxseq; + unsigned int txwindow; + unsigned int rxseq_reset; + m3uac_ip_cfg_t cfg; + uint32_t hb_elapsed; + int up; +}; + +typedef enum { + MSU_FLAG_EVENT = (1 << 0) +} m3uac_flag_t; + +typedef struct m3uac_connection m3uac_connection_t; + +static inline void sctp_no_nagle(int socket) +{ + //int flag = 1; + //setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int)); +} + +int m3uac_connection_close(m3uac_connection_t *mcon); +int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port); +m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration); +m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration); +int m3uac_connection_write(m3uac_connection_t *mcon, m3uac_event_t *event); +void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span); +void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id); +const char *m3uac_event_id_name(uint32_t event_id); +int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause); + + + + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.2005.vcproj b/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.2005.vcproj new file mode 100644 index 0000000000..964393805c --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.2005.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.2008.vcproj b/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.2008.vcproj new file mode 100644 index 0000000000..da21d1f48f --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.2008.vcproj @@ -0,0 +1,353 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.c b/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.c new file mode 100644 index 0000000000..943badb1dd --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_analog/ozmod_analog.c @@ -0,0 +1,1012 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "openzap.h" +#include "zap_analog.h" + +#ifndef localtime_r +struct tm * localtime_r(const time_t *clock, struct tm *result); +#endif + +static void *zap_analog_channel_run(zap_thread_t *me, void *obj); + +/** + * \brief Starts an FXO channel thread (outgoing call) + * \param zchan Channel to initiate call on + * \return Success or failure + * + * Initialises state, starts tone progress detection and runs the channel in a new a thread. + */ +static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_fxo_outgoing_call) +{ + if (!zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_channel_clear_needed_tones(zchan); + zap_channel_clear_detected_tones(zchan); + + zap_channel_command(zchan, ZAP_COMMAND_OFFHOOK, NULL); + zap_channel_command(zchan, ZAP_COMMAND_ENABLE_PROGRESS_DETECT, NULL); + zchan->needed_tones[ZAP_TONEMAP_DIAL] = 1; + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DIALING); + zap_thread_create_detached(zap_analog_channel_run, zchan); + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +/** + * \brief Starts an FXS channel thread (outgoing call) + * \param zchan Channel to initiate call on + * \return Success or failure + * + * Indicates call waiting if channel is already in use, otherwise runs the channel in a new thread. + */ +static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_fxs_outgoing_call) +{ + + if (zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_CALLWAITING); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_GENRING); + zap_thread_create_detached(zap_analog_channel_run, zchan); + } + + return ZAP_SUCCESS; +} + +/** + * \brief Starts an analog span thread (monitor) + * \param span Span to monitor + * \return Success or failure + */ +static zap_status_t zap_analog_start(zap_span_t *span) +{ + zap_analog_data_t *analog_data = span->signal_data; + zap_set_flag(analog_data, ZAP_ANALOG_RUNNING); + return zap_thread_create_detached(zap_analog_run, span); +} + +/** + * \brief Initialises an analog span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static ZIO_SIG_CONFIGURE_FUNCTION(zap_analog_configure_span) +//zap_status_t zap_analog_configure_span(zap_span_t *span, char *tonemap, uint32_t digit_timeout, uint32_t max_dialstr, zio_signal_cb_t sig_cb) +{ + zap_analog_data_t *analog_data; + const char *tonemap = "us"; + const char *hotline = ""; + uint32_t digit_timeout = 10; + uint32_t max_dialstr = MAX_DTMF; + const char *var, *val; + int *intval; + uint32_t flags = ZAP_ANALOG_CALLERID; + + assert(sig_cb != NULL); + + if (span->signal_type) { + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling."); + return ZAP_FAIL; + } + + analog_data = malloc(sizeof(*analog_data)); + assert(analog_data != NULL); + memset(analog_data, 0, sizeof(*analog_data)); + + while ((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "tonemap")) { + if (!(val = va_arg(ap, char *))) { + break; + } + tonemap = val; + } else if (!strcasecmp(var, "digit_timeout")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + digit_timeout = *intval; + } else if (!strcasecmp(var, "enable_callerid")) { + if (!(val = va_arg(ap, char *))) { + break; + } + + if (zap_true(val)) { + flags |= ZAP_ANALOG_CALLERID; + } else { + flags &= ~ZAP_ANALOG_CALLERID; + } + } else if (!strcasecmp(var, "max_dialstr")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + max_dialstr = *intval; + } else if (!strcasecmp(var, "hotline")) { + if (!(val = va_arg(ap, char *))) { + break; + } + hotline = val; + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return ZAP_FAIL; + } + } + + + if (digit_timeout < 2000 || digit_timeout > 10000) { + digit_timeout = 2000; + } + + if ((max_dialstr < 1 && !strlen(hotline)) || max_dialstr > MAX_DTMF) { + max_dialstr = MAX_DTMF; + } + + span->start = zap_analog_start; + analog_data->flags = flags; + analog_data->digit_timeout = digit_timeout; + analog_data->max_dialstr = max_dialstr; + span->signal_cb = sig_cb; + strncpy(analog_data->hotline, hotline, sizeof(analog_data->hotline)); + span->signal_type = ZAP_SIGTYPE_ANALOG; + span->signal_data = analog_data; + span->outgoing_call = span->trunk_type == ZAP_TRUNK_FXS ? analog_fxs_outgoing_call : analog_fxo_outgoing_call; + zap_span_load_tones(span, tonemap); + + return ZAP_SUCCESS; + +} + +/** + * \brief Retrieves tone generation output to be sent + * \param ts Teletone generator + * \param map Tone map + * \return -1 on error, 0 on success + */ +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + zap_buffer_t *dt_buffer = ts->user_data; + int wrote; + + if (!dt_buffer) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + zap_buffer_write(dt_buffer, ts->buffer, wrote * 2); + return 0; +} + +/** + * \brief Sends caller id on an analog channel (FSK coded) + * \param zchan Channel to send caller id on + */ +static void send_caller_id(zap_channel_t *zchan) +{ + zap_fsk_data_state_t fsk_data; + uint8_t databuf[1024] = ""; + char time_str[9]; + struct tm tm; + time_t now; + zap_mdmf_type_t mt = MDMF_INVALID; + + time(&now); +#ifdef WIN32 + _tzset(); + _localtime64_s(&tm, &now); +#else + localtime_r(&now, &tm); +#endif + strftime(time_str, sizeof(time_str), "%m%d%H%M", &tm); + + zap_fsk_data_init(&fsk_data, databuf, sizeof(databuf)); + zap_fsk_data_add_mdmf(&fsk_data, MDMF_DATETIME, (uint8_t *) time_str, 8); + + if (zap_strlen_zero(zchan->caller_data.cid_num.digits)) { + mt = MDMF_NO_NUM; + zap_set_string(zchan->caller_data.cid_num.digits, "O"); + } else if (!strcasecmp(zchan->caller_data.cid_num.digits, "P") || !strcasecmp(zchan->caller_data.cid_num.digits, "O")) { + mt = MDMF_NO_NUM; + } else { + mt = MDMF_PHONE_NUM; + } + zap_fsk_data_add_mdmf(&fsk_data, mt, (uint8_t *) zchan->caller_data.cid_num.digits, (uint8_t)strlen(zchan->caller_data.cid_num.digits)); + + if (zap_strlen_zero(zchan->caller_data.cid_name)) { + mt = MDMF_NO_NAME; + zap_set_string(zchan->caller_data.cid_name, "O"); + } else if (!strcasecmp(zchan->caller_data.cid_name, "P") || !strcasecmp(zchan->caller_data.cid_name, "O")) { + mt = MDMF_NO_NAME; + } else { + mt = MDMF_PHONE_NAME; + } + zap_fsk_data_add_mdmf(&fsk_data, mt, (uint8_t *) zchan->caller_data.cid_name, (uint8_t)strlen(zchan->caller_data.cid_name)); + + zap_fsk_data_add_checksum(&fsk_data); + zap_channel_send_fsk_data(zchan, &fsk_data, -14); +} + +/** + * \brief Main thread function for analog channel (outgoing call) + * \param me Current thread + * \param obj Channel to run in this thread + */ +static void *zap_analog_channel_run(zap_thread_t *me, void *obj) +{ + zap_channel_t *zchan = (zap_channel_t *) obj; + zap_buffer_t *dt_buffer = NULL; + teletone_generation_session_t ts; + uint8_t frame[1024]; + zap_size_t len, rlen; + zap_tone_type_t tt = ZAP_TONE_DTMF; + char dtmf[MAX_DTMF+1] = ""; + zap_size_t dtmf_offset = 0; + zap_analog_data_t *analog_data = zchan->span->signal_data; + zap_channel_t *closed_chan; + uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = 30000; + zap_sigmsg_t sig; + zap_status_t status; + + zap_log(ZAP_LOG_DEBUG, "ANALOG CHANNEL thread starting.\n"); + + ts.buffer = NULL; + + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "OPEN ERROR [%s]\n", zchan->last_error); + goto done; + } + + if (zap_buffer_create(&dt_buffer, 1024, 3192, 0) != ZAP_SUCCESS) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "memory error!"); + zap_log(ZAP_LOG_ERROR, "MEM ERROR\n"); + goto done; + } + + if (zap_channel_command(zchan, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "error initilizing tone detector!"); + zap_log(ZAP_LOG_ERROR, "TONE ERROR\n"); + goto done; + } + + zap_set_flag_locked(zchan, ZAP_CHANNEL_INTHREAD); + teletone_init_session(&ts, 0, teletone_handler, dt_buffer); + ts.rate = 8000; +#if 0 + ts.debug = 1; + ts.debug_stream = stdout; +#endif + zap_channel_command(zchan, ZAP_COMMAND_GET_INTERVAL, &interval); + zap_buffer_set_loops(dt_buffer, -1); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + + assert(interval != 0); + + while (zap_running() && zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_wait_flag_t flags = ZAP_READ; + zap_size_t dlen = 0; + + len = sizeof(frame); + + elapsed += interval; + state_counter += interval; + + if (!zap_test_flag(zchan, ZAP_CHANNEL_STATE_CHANGE)) { + switch(zchan->state) { + case ZAP_CHANNEL_STATE_GET_CALLERID: + { + if (state_counter > 5000 || !zap_test_flag(zchan, ZAP_CHANNEL_CALLERID_DETECT)) { + zap_channel_command(zchan, ZAP_COMMAND_DISABLE_CALLERID_DETECT, NULL); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_IDLE); + } + } + break; + case ZAP_CHANNEL_STATE_DIALING: + { + if (state_counter > dial_timeout) { + if (zchan->needed_tones[ZAP_TONEMAP_DIAL]) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + } + } + } + break; + case ZAP_CHANNEL_STATE_GENRING: + { + if (state_counter > 60000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } else if (!zchan->fsk_buffer || !zap_buffer_inuse(zchan->fsk_buffer)) { + zap_sleep(interval); + continue; + } + } + break; + case ZAP_CHANNEL_STATE_DIALTONE: + { + if (!zap_test_flag(zchan, ZAP_CHANNEL_HOLD) && state_counter > 10000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } + } + break; + case ZAP_CHANNEL_STATE_BUSY: + { + if (state_counter > 20000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_ATTN); + } + } + break; + case ZAP_CHANNEL_STATE_ATTN: + { + if (state_counter > 20000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + case ZAP_CHANNEL_STATE_HANGUP: + { + if (state_counter > 500) { + if (zap_test_flag(zchan, ZAP_CHANNEL_RINGING)) { + zap_channel_command(zchan, ZAP_COMMAND_GENERATE_RING_OFF, NULL); + } + + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && + (zchan->last_state == ZAP_CHANNEL_STATE_RING || zchan->last_state == ZAP_CHANNEL_STATE_DIALTONE + || zchan->last_state >= ZAP_CHANNEL_STATE_IDLE)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CLEARING; + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + } + break; + case ZAP_CHANNEL_STATE_CALLWAITING: + { + int done = 0; + + if (zchan->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK] == 1) { + send_caller_id(zchan); + zchan->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK]++; + } else if (state_counter > 600 && !zchan->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK]) { + send_caller_id(zchan); + zchan->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK]++; + } else if (state_counter > 1000 && !zchan->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK]) { + done = 1; + } else if (state_counter > 10000) { + if (zchan->fsk_buffer) { + zap_buffer_zero(zchan->fsk_buffer); + } else { + zap_buffer_create(&zchan->fsk_buffer, 128, 128, 0); + } + + ts.user_data = zchan->fsk_buffer; + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_CALLWAITING_SAS]); + ts.user_data = dt_buffer; + done = 1; + } + + if (done) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE); + zap_clear_flag_locked(zchan->span, ZAP_SPAN_STATE_CHANGE); + zchan->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK] = 0; + } + } + case ZAP_CHANNEL_STATE_UP: + case ZAP_CHANNEL_STATE_IDLE: + { + zap_sleep(interval); + continue; + } + break; + case ZAP_CHANNEL_STATE_DOWN: + { + goto done; + } + break; + default: + break; + } + } else { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE); + zap_clear_flag_locked(zchan->span, ZAP_SPAN_STATE_CHANGE); + zap_channel_complete_state(zchan); + indicate = 0; + state_counter = 0; + + zap_log(ZAP_LOG_DEBUG, "Executing state handler on %d:%d for %s\n", + zchan->span_id, zchan->chan_id, + zap_channel_state2str(zchan->state)); + switch(zchan->state) { + case ZAP_CHANNEL_STATE_UP: + { + zap_channel_use(zchan); + zap_channel_clear_needed_tones(zchan); + zap_channel_flush_dtmf(zchan); + + if (zchan->type == ZAP_CHAN_TYPE_FXO && !zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK)) { + zap_channel_command(zchan, ZAP_COMMAND_OFFHOOK, NULL); + } + + if (zchan->fsk_buffer && zap_buffer_inuse(zchan->fsk_buffer)) { + zap_log(ZAP_LOG_DEBUG, "Cancel FSK transmit due to early answer.\n"); + zap_buffer_zero(zchan->fsk_buffer); + } + + if (zchan->type == ZAP_CHAN_TYPE_FXS && zap_test_flag(zchan, ZAP_CHANNEL_RINGING)) { + zap_channel_command(zchan, ZAP_COMMAND_GENERATE_RING_OFF, NULL); + } + + if (zchan->token_count == 1) { + zap_clear_flag(zchan, ZAP_CHANNEL_HOLD); + } + + if (zap_test_flag(zchan, ZAP_CHANNEL_HOLD)) { + zap_clear_flag(zchan, ZAP_CHANNEL_HOLD); + sig.event_id = ZAP_SIGEVENT_ADD_CALL; + } else { + sig.event_id = ZAP_SIGEVENT_UP; + } + + zap_span_send_signal(zchan->span, &sig); + continue; + } + break; + case ZAP_CHANNEL_STATE_DIALING: + { + zap_channel_use(zchan); + } + break; + case ZAP_CHANNEL_STATE_IDLE: + { + zap_channel_use(zchan); + sig.event_id = ZAP_SIGEVENT_START; + + if (zchan->type == ZAP_CHAN_TYPE_FXO) { + zap_set_string(zchan->caller_data.dnis.digits, zchan->chan_number); + } else { + zap_set_string(zchan->caller_data.dnis.digits, dtmf); + } + + zap_span_send_signal(zchan->span, &sig); + continue; + } + break; + case ZAP_CHANNEL_STATE_DOWN: + { + sig.event_id = ZAP_SIGEVENT_STOP; + zap_span_send_signal(zchan->span, &sig); + goto done; + } + break; + case ZAP_CHANNEL_STATE_DIALTONE: + { + memset(&zchan->caller_data, 0, sizeof(zchan->caller_data)); + *dtmf = '\0'; + dtmf_offset = 0; + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_DIAL]); + indicate = 1; + } + break; + case ZAP_CHANNEL_STATE_CALLWAITING: + { + zchan->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK] = 0; + if (zchan->fsk_buffer) { + zap_buffer_zero(zchan->fsk_buffer); + } else { + zap_buffer_create(&zchan->fsk_buffer, 128, 128, 0); + } + + ts.user_data = zchan->fsk_buffer; + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_CALLWAITING_SAS]); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_CALLWAITING_CAS]); + ts.user_data = dt_buffer; + } + break; + case ZAP_CHANNEL_STATE_GENRING: + { + zap_sigmsg_t sig; + + send_caller_id(zchan); + zap_channel_command(zchan, ZAP_COMMAND_GENERATE_RING_ON, NULL); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + sig.event_id = ZAP_SIGEVENT_PROGRESS; + zap_span_send_signal(zchan->span, &sig); + + } + break; + case ZAP_CHANNEL_STATE_GET_CALLERID: + { + memset(&zchan->caller_data, 0, sizeof(zchan->caller_data)); + zap_channel_command(zchan, ZAP_COMMAND_ENABLE_CALLERID_DETECT, NULL); + continue; + } + break; + case ZAP_CHANNEL_STATE_RING: + { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_RING]); + indicate = 1; + + } + break; + case ZAP_CHANNEL_STATE_BUSY: + { + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CIRCUIT_CONGESTION; + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_BUSY]); + indicate = 1; + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + case ZAP_CHANNEL_STATE_ATTN: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_ATTN]); + indicate = 1; + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + default: + break; + } + } + + + if (zchan->state == ZAP_CHANNEL_STATE_DIALTONE || zchan->state == ZAP_CHANNEL_STATE_COLLECT) { + if ((dlen = zap_channel_dequeue_dtmf(zchan, dtmf + dtmf_offset, sizeof(dtmf) - strlen(dtmf)))) { + + if (zchan->state == ZAP_CHANNEL_STATE_DIALTONE) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_COLLECT); + collecting = 1; + } + dtmf_offset = strlen(dtmf); + last_digit = elapsed; + sig.event_id = ZAP_SIGEVENT_COLLECTED_DIGIT; + sig.raw_data = dtmf; + if (zap_span_send_signal(zchan->span, &sig) == ZAP_BREAK) { + collecting = 0; + } + } + else if(!analog_data->max_dialstr) + { + last_digit = elapsed; + collecting = 0; + strcpy(dtmf, analog_data->hotline); + } + } + + + if (last_digit && (!collecting || ((elapsed - last_digit > analog_data->digit_timeout) || strlen(dtmf) >= analog_data->max_dialstr))) { + zap_log(ZAP_LOG_DEBUG, "Number obtained [%s]\n", dtmf); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_IDLE); + last_digit = 0; + collecting = 0; + } + + if (zap_channel_wait(zchan, &flags, interval * 2) != ZAP_SUCCESS) { + continue; + } + + if (!(flags & ZAP_READ)) { + continue; + } + + if (zap_channel_read(zchan, frame, &len) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "READ ERROR [%s]\n", zchan->last_error); + goto done; + } + + if (zchan->type == ZAP_CHAN_TYPE_FXO && zchan->detected_tones[0]) { + zap_sigmsg_t sig; + int i; + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + sig.event_id = ZAP_SIGEVENT_TONE_DETECTED; + + for (i = 1; i < ZAP_TONEMAP_INVALID; i++) { + if (zchan->detected_tones[i]) { + zap_log(ZAP_LOG_DEBUG, "Detected tone %s on %d:%d\n", zap_tonemap2str(i), zchan->span_id, zchan->chan_id); + sig.raw_data = &i; + zap_span_send_signal(zchan->span, &sig); + } + } + + if (zchan->detected_tones[ZAP_TONEMAP_BUSY] || + zchan->detected_tones[ZAP_TONEMAP_FAIL1] || + zchan->detected_tones[ZAP_TONEMAP_FAIL2] || + zchan->detected_tones[ZAP_TONEMAP_FAIL3] || + zchan->detected_tones[ZAP_TONEMAP_ATTN] + ) { + zap_log(ZAP_LOG_ERROR, "Failure indication detected!\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else if (zchan->detected_tones[ZAP_TONEMAP_DIAL]) { + if (zap_strlen_zero(zchan->caller_data.ani.digits)) { + zap_log(ZAP_LOG_ERROR, "No Digits to send!\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + if (zap_channel_command(zchan, ZAP_COMMAND_SEND_DTMF, zchan->caller_data.ani.digits) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Send Digits Failed [%s]\n", zchan->last_error); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + state_counter = 0; + zchan->needed_tones[ZAP_TONEMAP_RING] = 1; + zchan->needed_tones[ZAP_TONEMAP_BUSY] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL1] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL2] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL3] = 1; + dial_timeout = ((zchan->dtmf_on + zchan->dtmf_off) * strlen(zchan->caller_data.ani.digits)) + 2000; + } + } + } else if (zchan->detected_tones[ZAP_TONEMAP_RING]) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + } + + zap_channel_clear_detected_tones(zchan); + } + + if ((zchan->dtmf_buffer && zap_buffer_inuse(zchan->dtmf_buffer)) || (zchan->fsk_buffer && zap_buffer_inuse(zchan->fsk_buffer))) { + //rlen = len; + //memset(frame, 0, len); + //zap_channel_write(zchan, frame, sizeof(frame), &rlen); + continue; + } + + if (!indicate) { + continue; + } + + if (zchan->type == ZAP_CHAN_TYPE_FXO && !zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK)) { + zap_channel_command(zchan, ZAP_COMMAND_OFFHOOK, NULL); + } + + if (zchan->effective_codec != ZAP_CODEC_SLIN) { + len *= 2; + } + + rlen = zap_buffer_read_loop(dt_buffer, frame, len); + + if (zchan->effective_codec != ZAP_CODEC_SLIN) { + zio_codec_t codec_func = NULL; + + if (zchan->native_codec == ZAP_CODEC_ULAW) { + codec_func = zio_slin2ulaw; + } else if (zchan->native_codec == ZAP_CODEC_ALAW) { + codec_func = zio_slin2alaw; + } + + if (codec_func) { + status = codec_func(frame, sizeof(frame), &rlen); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "codec error!"); + goto done; + } + } + + zap_channel_write(zchan, frame, sizeof(frame), &rlen); + } + + done: + + + if (zchan->type == ZAP_CHAN_TYPE_FXO && zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK)) { + zap_channel_command(zchan, ZAP_COMMAND_ONHOOK, NULL); + } + + if (zchan->type == ZAP_CHAN_TYPE_FXS && zap_test_flag(zchan, ZAP_CHANNEL_RINGING)) { + zap_channel_command(zchan, ZAP_COMMAND_GENERATE_RING_OFF, NULL); + } + + + closed_chan = zchan; + zap_channel_close(&zchan); + + zap_channel_command(closed_chan, ZAP_COMMAND_SET_NATIVE_CODEC, NULL); + + if (ts.buffer) { + teletone_destroy_session(&ts); + } + + if (dt_buffer) { + zap_buffer_destroy(&dt_buffer); + } + + if (closed_chan->state != ZAP_CHANNEL_STATE_DOWN) { + zap_set_state_locked(closed_chan, ZAP_CHANNEL_STATE_DOWN); + } + + zap_log(ZAP_LOG_DEBUG, "ANALOG CHANNEL %d:%d thread ended.\n", closed_chan->span_id, closed_chan->chan_id); + zap_clear_flag(closed_chan, ZAP_CHANNEL_INTHREAD); + + return NULL; +} + +/** + * \brief Processes openzap event + * \param span Span on which the event was fired + * \param event Event to be treated + * \return Success or failure + */ +static __inline__ zap_status_t process_event(zap_span_t *span, zap_event_t *event) +{ + zap_sigmsg_t sig; + zap_analog_data_t *analog_data = event->channel->span->signal_data; + int locked = 0; + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = event->channel->chan_id; + sig.span_id = event->channel->span_id; + sig.channel = event->channel; + + + zap_log(ZAP_LOG_DEBUG, "EVENT [%s][%d:%d] STATE [%s]\n", + zap_oob_event2str(event->enum_id), event->channel->span_id, event->channel->chan_id, zap_channel_state2str(event->channel->state)); + + zap_mutex_lock(event->channel->mutex); + locked++; + + switch(event->enum_id) { + case ZAP_OOB_RING_START: + { + if (event->channel->type != ZAP_CHAN_TYPE_FXO) { + zap_log(ZAP_LOG_ERROR, "Cannot get a RING_START event on a non-fxo channel, please check your config.\n"); + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + goto end; + } + if (!event->channel->ring_count && (event->channel->state == ZAP_CHANNEL_STATE_DOWN && !zap_test_flag(event->channel, ZAP_CHANNEL_INTHREAD))) { + if (zap_test_flag(analog_data, ZAP_ANALOG_CALLERID)) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_GET_CALLERID); + } else { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_IDLE); + } + event->channel->ring_count = 1; + zap_mutex_unlock(event->channel->mutex); + locked = 0; + zap_thread_create_detached(zap_analog_channel_run, event->channel); + } else { + event->channel->ring_count++; + } + } + break; + case ZAP_OOB_ONHOOK: + { + if (zap_test_flag(event->channel, ZAP_CHANNEL_RINGING)) { + zap_channel_command(event->channel, ZAP_COMMAND_GENERATE_RING_OFF, NULL); + } + + if (event->channel->state != ZAP_CHANNEL_STATE_DOWN) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + } + + } + break; + case ZAP_OOB_FLASH: + { + if (event->channel->state == ZAP_CHANNEL_STATE_CALLWAITING) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_UP); + zap_clear_flag_locked(event->channel, ZAP_CHANNEL_STATE_CHANGE); + zap_clear_flag_locked(event->channel->span, ZAP_SPAN_STATE_CHANGE); + event->channel->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK] = 0; + } + + zap_channel_rotate_tokens(event->channel); + + if (zap_test_flag(event->channel, ZAP_CHANNEL_HOLD) && event->channel->token_count != 1) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_UP); + } else { + sig.event_id = ZAP_SIGEVENT_FLASH; + zap_span_send_signal(span, &sig); + } + } + break; + case ZAP_OOB_OFFHOOK: + { + if (event->channel->type == ZAP_CHAN_TYPE_FXS) { + if (zap_test_flag(event->channel, ZAP_CHANNEL_INTHREAD)) { + if (zap_test_flag(event->channel, ZAP_CHANNEL_RINGING)) { + zap_channel_command(event->channel, ZAP_COMMAND_GENERATE_RING_OFF, NULL); + } + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_UP); + } else { + if(!analog_data->max_dialstr) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_COLLECT); + } else { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DIALTONE); + } + zap_mutex_unlock(event->channel->mutex); + locked = 0; + zap_thread_create_detached(zap_analog_channel_run, event->channel); + } + } else { + if (!zap_test_flag(event->channel, ZAP_CHANNEL_INTHREAD)) { + if (zap_test_flag(event->channel, ZAP_CHANNEL_OFFHOOK)) { + zap_channel_command(event->channel, ZAP_COMMAND_ONHOOK, NULL); + } + } + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + } + } + case ZAP_OOB_DTMF: + { + const char * digit_str = (const char *)event->data; + if(digit_str) { + if (event->channel->state == ZAP_CHANNEL_STATE_CALLWAITING && (*digit_str == 'D' || *digit_str == 'A')) { + event->channel->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK]++; + } else { + zio_event_cb_t event_callback = NULL; + + zap_channel_queue_dtmf(event->channel, digit_str); + if (span->event_callback) { + event_callback = span->event_callback; + } else if (event->channel->event_callback) { + event_callback = event->channel->event_callback; + } + + if (event_callback) { + event->channel->event_header.channel = event->channel; + event->channel->event_header.e_type = ZAP_EVENT_DTMF; + event->channel->event_header.data = (void *)digit_str; + event_callback(event->channel, &event->channel->event_header); + event->channel->event_header.e_type = ZAP_EVENT_NONE; + event->channel->event_header.data = NULL; + } + + } + zap_safe_free(event->data); + } + } + case ZAP_OOB_ALARM_TRAP: + { + zap_set_flag_locked(event->channel, ZAP_CHANNEL_SUSPENDED); + } + case ZAP_OOB_ALARM_CLEAR: + { + zap_clear_flag_locked(event->channel, ZAP_CHANNEL_SUSPENDED); + } + } + + end: + + if (locked) { + zap_mutex_unlock(event->channel->mutex); + } + return ZAP_SUCCESS; +} + +/** + * \brief Main thread function for analog span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *zap_analog_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_analog_data_t *analog_data = span->signal_data; + int errs = 0; + + zap_log(ZAP_LOG_DEBUG, "ANALOG thread starting.\n"); + + while(zap_running() && zap_test_flag(analog_data, ZAP_ANALOG_RUNNING)) { + int waitms = 1000; + zap_status_t status; + + if ((status = zap_span_poll_event(span, waitms)) != ZAP_FAIL) { + errs = 0; + } + + switch(status) { + case ZAP_SUCCESS: + { + zap_event_t *event; + while (zap_span_next_event(span, &event) == ZAP_SUCCESS) { + if (event->enum_id == ZAP_OOB_NOOP) { + continue; + } + if (process_event(span, event) != ZAP_SUCCESS) { + goto end; + } + } + } + break; + case ZAP_FAIL: + { + zap_log(ZAP_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error); + if (++errs > 300) { + zap_log(ZAP_LOG_CRIT, "Too Many Errors!\n"); + goto end; + } + } + break; + default: + break; + } + + } + + end: + + zap_clear_flag(analog_data, ZAP_ANALOG_RUNNING); + + zap_log(ZAP_LOG_DEBUG, "ANALOG thread ending.\n"); + + return NULL; +} + +/** + * \brief Openzap analog signaling module initialisation + * \return Success + */ +static ZIO_SIG_LOAD_FUNCTION(zap_analog_init) +{ + return ZAP_SUCCESS; +} + +/** + * \brief Openzap analog signaling module definition + */ +EX_DECLARE_DATA zap_module_t zap_module = { + "analog", + NULL, + NULL, + zap_analog_init, + zap_analog_configure_span, + NULL +}; + + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_analog/zap_analog.h b/libs/openzap/src/ozmod/ozmod_analog/zap_analog.h new file mode 100644 index 0000000000..3ffa422ead --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_analog/zap_analog.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 ZAP_ANALOG_H +#define ZAP_ANALOG_H +#include "openzap.h" + +typedef enum { + ZAP_ANALOG_RUNNING = (1 << 0), + ZAP_ANALOG_CALLERID = (1 << 1) +} zap_analog_flag_t; + +#define ZAP_MAX_HOTLINE_STR 20 +#define MAX_DTMF 256 + +struct zap_analog_data { + uint32_t flags; + uint32_t max_dialstr; + uint32_t digit_timeout; + char hotline[ZAP_MAX_HOTLINE_STR]; +}; + + + +static void *zap_analog_run(zap_thread_t *me, void *obj); +typedef struct zap_analog_data zap_analog_data_t; + +#endif + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.2005.vcproj b/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.2005.vcproj new file mode 100644 index 0000000000..bff0716d4c --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.2005.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.2008.vcproj b/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.2008.vcproj new file mode 100644 index 0000000000..35a0d89da0 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.2008.vcproj @@ -0,0 +1,353 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.c b/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.c new file mode 100644 index 0000000000..581c6dd45a --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_analog_em/ozmod_analog_em.c @@ -0,0 +1,707 @@ +/* + * Copyright (c) 2008, Anthony Minessale II + * 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. + * + * Contributor(s): + * + * John Wehle (john@feith.com) + * + */ + +#include "openzap.h" +#include "zap_analog_em.h" + +#ifndef localtime_r +struct tm * localtime_r(const time_t *clock, struct tm *result); +#endif + +static void *zap_analog_em_channel_run(zap_thread_t *me, void *obj); + +/** + * \brief Starts an EM channel thread (outgoing call) + * \param zchan Channel to initiate call on + * \return Success or failure + * + * Initialises state, starts tone progress detection and runs the channel in a new a thread. + */ +static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_em_outgoing_call) +{ + if (!zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_channel_clear_needed_tones(zchan); + zap_channel_clear_detected_tones(zchan); + + zap_set_flag(zchan, ZAP_CHANNEL_OUTBOUND); + + zap_channel_command(zchan, ZAP_COMMAND_OFFHOOK, NULL); + zap_channel_command(zchan, ZAP_COMMAND_ENABLE_PROGRESS_DETECT, NULL); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DIALING); + zap_thread_create_detached(zap_analog_em_channel_run, zchan); + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +/** + * \brief Starts an EM span thread (monitor) + * \param span Span to monitor + * \return Success or failure + */ +static zap_status_t zap_analog_em_start(zap_span_t *span) +{ + zap_analog_em_data_t *analog_data = span->signal_data; + zap_set_flag(analog_data, ZAP_ANALOG_EM_RUNNING); + return zap_thread_create_detached(zap_analog_em_run, span); +} + +/** + * \brief Initialises an EM span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static ZIO_SIG_CONFIGURE_FUNCTION(zap_analog_em_configure_span) +//zap_status_t zap_analog_em_configure_span(zap_span_t *span, char *tonemap, uint32_t digit_timeout, uint32_t max_dialstr, zio_signal_cb_t sig_cb) +{ + zap_analog_em_data_t *analog_data; + const char *tonemap = "us"; + uint32_t digit_timeout = 10; + uint32_t max_dialstr = 11; + const char *var, *val; + int *intval; + + assert(sig_cb != NULL); + + if (span->signal_type) { + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling."); + return ZAP_FAIL; + } + + analog_data = malloc(sizeof(*analog_data)); + assert(analog_data != NULL); + memset(analog_data, 0, sizeof(*analog_data)); + + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "tonemap")) { + if (!(val = va_arg(ap, char *))) { + break; + } + tonemap = val; + } else if (!strcasecmp(var, "digit_timeout")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + digit_timeout = *intval; + } else if (!strcasecmp(var, "max_dialstr")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + max_dialstr = *intval; + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return ZAP_FAIL; + } + } + + + if (digit_timeout < 2000 || digit_timeout > 10000) { + digit_timeout = 2000; + } + + if (max_dialstr < 2 || max_dialstr > MAX_DIALSTRING) { + zap_log(ZAP_LOG_ERROR, "Invalid max_dialstr, setting to %d\n", MAX_DIALSTRING); + max_dialstr = MAX_DIALSTRING; + } + + span->start = zap_analog_em_start; + analog_data->digit_timeout = digit_timeout; + analog_data->max_dialstr = max_dialstr; + span->signal_cb = sig_cb; + span->signal_type = ZAP_SIGTYPE_ANALOG; + span->signal_data = analog_data; + span->outgoing_call = analog_em_outgoing_call; + zap_span_load_tones(span, tonemap); + + return ZAP_SUCCESS; + +} + +/** + * \brief Retrieves tone generation output to be sent + * \param ts Teletone generator + * \param map Tone map + * \return -1 on error, 0 on success + */ +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + zap_buffer_t *dt_buffer = ts->user_data; + int wrote; + + if (!dt_buffer) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + zap_buffer_write(dt_buffer, ts->buffer, wrote * 2); + return 0; +} + +/** + * \brief Main thread function for EM channel (outgoing call) + * \param me Current thread + * \param obj Channel to run in this thread + */ +static void *zap_analog_em_channel_run(zap_thread_t *me, void *obj) +{ + zap_channel_t *zchan = (zap_channel_t *) obj; + zap_buffer_t *dt_buffer = NULL; + teletone_generation_session_t ts; + uint8_t frame[1024]; + zap_size_t len, rlen; + zap_tone_type_t tt = ZAP_TONE_DTMF; + char dtmf[128] = ""; + zap_size_t dtmf_offset = 0; + zap_analog_em_data_t *analog_data = zchan->span->signal_data; + zap_channel_t *closed_chan; + uint32_t state_counter = 0, elapsed = 0, collecting = 0, interval = 0, last_digit = 0, indicate = 0, dial_timeout = 30000; + zap_sigmsg_t sig; + zap_status_t status; + + zap_log(ZAP_LOG_DEBUG, "ANALOG EM CHANNEL thread starting.\n"); + + ts.buffer = NULL; + + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "OPEN ERROR [%s]\n", zchan->last_error); + goto done; + } + + if (zap_buffer_create(&dt_buffer, 1024, 3192, 0) != ZAP_SUCCESS) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "memory error!"); + zap_log(ZAP_LOG_ERROR, "MEM ERROR\n"); + goto done; + } + + if (zap_channel_command(zchan, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "error initilizing tone detector!"); + zap_log(ZAP_LOG_ERROR, "TONE ERROR\n"); + goto done; + } + + zap_set_flag_locked(zchan, ZAP_CHANNEL_INTHREAD); + teletone_init_session(&ts, 0, teletone_handler, dt_buffer); + ts.rate = 8000; +#if 0 + ts.debug = 1; + ts.debug_stream = stdout; +#endif + zap_channel_command(zchan, ZAP_COMMAND_GET_INTERVAL, &interval); + zap_buffer_set_loops(dt_buffer, -1); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + + assert(interval != 0); + + while (zap_running() && zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_wait_flag_t flags = ZAP_READ; + zap_size_t dlen = 0; + + len = sizeof(frame); + + elapsed += interval; + state_counter += interval; + + if (!zap_test_flag(zchan, ZAP_CHANNEL_STATE_CHANGE)) { + switch(zchan->state) { + case ZAP_CHANNEL_STATE_DIALING: + { + if (! zchan->needed_tones[ZAP_TONEMAP_RING] + && zap_test_flag(zchan, ZAP_CHANNEL_WINK)) { + if (zap_strlen_zero(zchan->caller_data.ani.digits)) { + zap_log(ZAP_LOG_ERROR, "No Digits to send!\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + if (zap_channel_command(zchan, ZAP_COMMAND_SEND_DTMF, zchan->caller_data.ani.digits) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Send Digits Failed [%s]\n", zchan->last_error); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + state_counter = 0; + zchan->needed_tones[ZAP_TONEMAP_RING] = 1; + zchan->needed_tones[ZAP_TONEMAP_BUSY] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL1] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL2] = 1; + zchan->needed_tones[ZAP_TONEMAP_FAIL3] = 1; + dial_timeout = ((zchan->dtmf_on + zchan->dtmf_off) * strlen(zchan->caller_data.ani.digits)) + 2000; + } + } + break; + } + if (state_counter > dial_timeout) { + if (!zap_test_flag(zchan, ZAP_CHANNEL_WINK)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + } + } + } + break; + case ZAP_CHANNEL_STATE_DIALTONE: + { + if (state_counter > 10000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } + } + break; + case ZAP_CHANNEL_STATE_BUSY: + { + if (state_counter > 20000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_ATTN); + } + } + break; + case ZAP_CHANNEL_STATE_ATTN: + { + if (state_counter > 20000) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + case ZAP_CHANNEL_STATE_HANGUP: + { + if (state_counter > 500) { + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && + (zchan->last_state == ZAP_CHANNEL_STATE_RING || zchan->last_state == ZAP_CHANNEL_STATE_DIALTONE + || zchan->last_state >= ZAP_CHANNEL_STATE_IDLE)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else { + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CLEARING; + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + } + break; + case ZAP_CHANNEL_STATE_UP: + case ZAP_CHANNEL_STATE_IDLE: + { + zap_sleep(interval); + continue; + } + break; + case ZAP_CHANNEL_STATE_DOWN: + { + goto done; + } + break; + default: + break; + } + } else { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE); + zap_clear_flag_locked(zchan->span, ZAP_SPAN_STATE_CHANGE); + zap_channel_complete_state(zchan); + indicate = 0; + state_counter = 0; + + zap_log(ZAP_LOG_DEBUG, "Executing state handler on %d:%d for %s\n", + zchan->span_id, zchan->chan_id, + zap_channel_state2str(zchan->state)); + switch(zchan->state) { + case ZAP_CHANNEL_STATE_UP: + { + zap_channel_use(zchan); + zap_channel_clear_needed_tones(zchan); + zap_channel_flush_dtmf(zchan); + + if (!zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK)) { + zap_channel_command(zchan, ZAP_COMMAND_OFFHOOK, NULL); + } + + sig.event_id = ZAP_SIGEVENT_UP; + + zap_span_send_signal(zchan->span, &sig); + continue; + } + break; + case ZAP_CHANNEL_STATE_DIALING: + { + zap_channel_use(zchan); + } + break; + case ZAP_CHANNEL_STATE_IDLE: + { + zap_channel_use(zchan); + + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_set_string(zchan->caller_data.dnis.digits, zchan->chan_number); + } else { + zap_set_string(zchan->caller_data.dnis.digits, dtmf); + } + + sig.event_id = ZAP_SIGEVENT_START; + + zap_span_send_signal(zchan->span, &sig); + continue; + } + break; + case ZAP_CHANNEL_STATE_DOWN: + { + sig.event_id = ZAP_SIGEVENT_STOP; + zap_span_send_signal(zchan->span, &sig); + goto done; + } + break; + case ZAP_CHANNEL_STATE_DIALTONE: + { + memset(&zchan->caller_data, 0, sizeof(zchan->caller_data)); + *dtmf = '\0'; + dtmf_offset = 0; + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_DIAL]); + indicate = 1; + + zap_channel_command(zchan, ZAP_COMMAND_WINK, NULL); + } + break; + case ZAP_CHANNEL_STATE_RING: + { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_RING]); + indicate = 1; + } + break; + case ZAP_CHANNEL_STATE_BUSY: + { + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CIRCUIT_CONGESTION; + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_BUSY]); + indicate = 1; + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + case ZAP_CHANNEL_STATE_ATTN: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OFFHOOK) && !zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_ATTN]); + indicate = 1; + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + default: + break; + } + } + + + if (zchan->state == ZAP_CHANNEL_STATE_DIALTONE || zchan->state == ZAP_CHANNEL_STATE_COLLECT) { + if ((dlen = zap_channel_dequeue_dtmf(zchan, dtmf + dtmf_offset, sizeof(dtmf) - strlen(dtmf)))) { + + if (zchan->state == ZAP_CHANNEL_STATE_DIALTONE) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_COLLECT); + collecting = 1; + } + dtmf_offset = strlen(dtmf); + last_digit = elapsed; + sig.event_id = ZAP_SIGEVENT_COLLECTED_DIGIT; + sig.raw_data = dtmf; + if (zap_span_send_signal(zchan->span, &sig) == ZAP_BREAK) { + collecting = 0; + } + } + } + + + if (last_digit && (!collecting || ((elapsed - last_digit > analog_data->digit_timeout) || strlen(dtmf) > analog_data->max_dialstr))) { + zap_log(ZAP_LOG_DEBUG, "Number obtained [%s]\n", dtmf); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_IDLE); + last_digit = 0; + collecting = 0; + } + + if (zap_channel_wait(zchan, &flags, interval * 2) != ZAP_SUCCESS) { + continue; + } + + if (!(flags & ZAP_READ)) { + continue; + } + + if (zap_channel_read(zchan, frame, &len) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "READ ERROR [%s]\n", zchan->last_error); + goto done; + } + + if (zchan->detected_tones[0]) { + zap_sigmsg_t sig; + int i; + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + sig.event_id = ZAP_SIGEVENT_TONE_DETECTED; + + for (i = 1; i < ZAP_TONEMAP_INVALID; i++) { + if (zchan->detected_tones[i]) { + zap_log(ZAP_LOG_DEBUG, "Detected tone %s on %d:%d\n", zap_tonemap2str(i), zchan->span_id, zchan->chan_id); + sig.raw_data = &i; + zap_span_send_signal(zchan->span, &sig); + } + } + + if (zchan->detected_tones[ZAP_TONEMAP_BUSY] || + zchan->detected_tones[ZAP_TONEMAP_FAIL1] || + zchan->detected_tones[ZAP_TONEMAP_FAIL2] || + zchan->detected_tones[ZAP_TONEMAP_FAIL3] || + zchan->detected_tones[ZAP_TONEMAP_ATTN] + ) { + zap_log(ZAP_LOG_ERROR, "Failure indication detected!\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_BUSY); + } else if (zchan->detected_tones[ZAP_TONEMAP_RING]) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + } + + zap_channel_clear_detected_tones(zchan); + } + + if ((zchan->dtmf_buffer && zap_buffer_inuse(zchan->dtmf_buffer))) { + rlen = len; + memset(frame, 0, len); + zap_channel_write(zchan, frame, sizeof(frame), &rlen); + continue; + } + + if (!indicate) { + continue; + } + + if (zchan->effective_codec != ZAP_CODEC_SLIN) { + len *= 2; + } + + rlen = zap_buffer_read_loop(dt_buffer, frame, len); + + if (zchan->effective_codec != ZAP_CODEC_SLIN) { + zio_codec_t codec_func = NULL; + + if (zchan->native_codec == ZAP_CODEC_ULAW) { + codec_func = zio_slin2ulaw; + } else if (zchan->native_codec == ZAP_CODEC_ALAW) { + codec_func = zio_slin2alaw; + } + + if (codec_func) { + status = codec_func(frame, sizeof(frame), &rlen); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "codec error!"); + goto done; + } + } + + zap_channel_write(zchan, frame, sizeof(frame), &rlen); + } + + done: + + zap_channel_command(zchan, ZAP_COMMAND_ONHOOK, NULL); + + closed_chan = zchan; + zap_channel_close(&zchan); + + zap_channel_command(closed_chan, ZAP_COMMAND_SET_NATIVE_CODEC, NULL); + + if (ts.buffer) { + teletone_destroy_session(&ts); + } + + if (dt_buffer) { + zap_buffer_destroy(&dt_buffer); + } + + zap_clear_flag(closed_chan, ZAP_CHANNEL_INTHREAD); + + zap_log(ZAP_LOG_DEBUG, "ANALOG EM CHANNEL thread ended.\n"); + + return NULL; +} + +/** + * \brief Processes EM events coming from zaptel/dahdi + * \param span Span on which the event was fired + * \param event Event to be treated + * \return Success or failure + */ +static __inline__ zap_status_t process_event(zap_span_t *span, zap_event_t *event) +{ + zap_sigmsg_t sig; + int locked = 0; + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = event->channel->chan_id; + sig.span_id = event->channel->span_id; + sig.channel = event->channel; + + + zap_log(ZAP_LOG_DEBUG, "EVENT [%s][%d:%d] STATE [%s]\n", + zap_oob_event2str(event->enum_id), event->channel->span_id, event->channel->chan_id, zap_channel_state2str(event->channel->state)); + + zap_mutex_lock(event->channel->mutex); + locked++; + + switch(event->enum_id) { + case ZAP_OOB_ONHOOK: + { + if (event->channel->state != ZAP_CHANNEL_STATE_DOWN) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + } + + } + break; + case ZAP_OOB_OFFHOOK: + { + if (zap_test_flag(event->channel, ZAP_CHANNEL_INTHREAD)) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_UP); + } else { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DIALTONE); + zap_mutex_unlock(event->channel->mutex); + locked = 0; + zap_thread_create_detached(zap_analog_em_channel_run, event->channel); + } + break; + } + case ZAP_OOB_WINK: + { + if (event->channel->state != ZAP_CHANNEL_STATE_DIALING) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_DOWN); + } else { + zap_set_flag_locked(event->channel, ZAP_CHANNEL_WINK); + } + + } + break; + } + if (locked) { + zap_mutex_unlock(event->channel->mutex); + } + return ZAP_SUCCESS; +} + +/** + * \brief Main thread function for EM span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *zap_analog_em_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_analog_em_data_t *analog_data = span->signal_data; + + zap_log(ZAP_LOG_DEBUG, "ANALOG EM thread starting.\n"); + + while(zap_running() && zap_test_flag(analog_data, ZAP_ANALOG_EM_RUNNING)) { + int waitms = 10; + zap_status_t status; + + status = zap_span_poll_event(span, waitms); + + switch(status) { + case ZAP_SUCCESS: + { + zap_event_t *event; + while (zap_span_next_event(span, &event) == ZAP_SUCCESS) { + if (event->enum_id == ZAP_OOB_NOOP) { + continue; + } + if (process_event(span, event) != ZAP_SUCCESS) { + goto end; + } + } + } + break; + case ZAP_FAIL: + { + zap_log(ZAP_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error); + } + break; + default: + break; + } + + } + + end: + + zap_clear_flag(analog_data, ZAP_ANALOG_EM_RUNNING); + + zap_log(ZAP_LOG_DEBUG, "ANALOG EM thread ending.\n"); + + return NULL; +} + +/** + * \brief Openzap analog EM module initialisation + * \return Success + */ +static ZIO_SIG_LOAD_FUNCTION(zap_analog_em_init) +{ + return ZAP_SUCCESS; +} + +/** + * \brief Openzap analog EM module definition + */ +EX_DECLARE_DATA zap_module_t zap_module = { + "analog_em", + NULL, + NULL, + zap_analog_em_init, + zap_analog_em_configure_span, + NULL +}; + + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_analog_em/zap_analog_em.h b/libs/openzap/src/ozmod/ozmod_analog_em/zap_analog_em.h new file mode 100644 index 0000000000..eb9f9f3d20 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_analog_em/zap_analog_em.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2008, Anthony Minessale II + * 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. + * + * Contributor(s): + * + * John Wehle (john@feith.com) + * + */ + +#ifndef ZAP_ANALOG_EM_H +#define ZAP_ANALOG_EM_H +#include "openzap.h" + +#define MAX_DIALSTRING 256 + +typedef enum { + ZAP_ANALOG_EM_RUNNING = (1 << 0) +} zap_analog_em_flag_t; + + +struct zap_analog_data { + uint32_t flags; + uint32_t max_dialstr; + uint32_t digit_timeout; +}; + +static void *zap_analog_em_run(zap_thread_t *me, void *obj); +typedef struct zap_analog_data zap_analog_em_data_t; + +#endif + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.2005.vcproj b/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.2005.vcproj new file mode 100644 index 0000000000..19dc74fd8f --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.2005.vcproj @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.2008.vcproj b/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.2008.vcproj new file mode 100644 index 0000000000..75815df561 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.2008.vcproj @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.c b/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.c new file mode 100644 index 0000000000..fc998b126f --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_isdn/ozmod_isdn.c @@ -0,0 +1,2420 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + */ + +/** + * Workaround for missing u_int / u_short types on solaris + */ +#if defined(HAVE_LIBPCAP) && defined(__SunOS) +#define __EXTENSIONS__ +#endif + +#include "openzap.h" +#include "Q931.h" +#include "Q921.h" +#ifdef WIN32 +#include +#else +#include +#endif + +#include "zap_isdn.h" + +#define LINE "--------------------------------------------------------------------------------" +//#define IODEBUG + +/* helper macros */ +#define ZAP_SPAN_IS_BRI(x) ((x)->trunk_type == ZAP_TRUNK_BRI || (x)->trunk_type == ZAP_TRUNK_BRI_PTMP) +#define ZAP_SPAN_IS_NT(x) (((zap_isdn_data_t *)(x)->signal_data)->mode == Q921_NT) + + +#ifdef HAVE_LIBPCAP +/*-------------------------------------------------------------------------*/ +/*Q931ToPcap functions*/ +#include +#endif + +#define SNAPLEN 1522 +#define MAX_ETHER_PAYLOAD_SIZE 1500 +#define MIN_ETHER_PAYLOAD_SIZE 42 +#define SIZE_ETHERNET 18 +#define VLANID_OFFSET 15 +#define SIZE_IP 20 +#define SIZE_TCP 20 +#define SIZE_TPKT 4 +#define SIZE_ETHERNET_CRC 4 +#define OVERHEAD SIZE_ETHERNET+SIZE_IP+SIZE_TCP+SIZE_TPKT +#define MAX_Q931_SIZE MAX_ETHER_PAYLOAD_SIZE-SIZE_IP-SIZE_TCP-SIZE_TPKT +#define TPKT_SIZE_OFFSET SIZE_ETHERNET+SIZE_IP+SIZE_TCP+2 +#define IP_SIZE_OFFSET SIZE_ETHERNET+2 +#define TCP_SEQ_OFFSET SIZE_ETHERNET+SIZE_IP+4 + +#ifdef HAVE_LIBPCAP +/*Some globals*/ +unsigned long pcapfilesize = 0; +unsigned long tcp_next_seq_no_send = 0; +unsigned long tcp_next_seq_no_rec = 0; +pcap_dumper_t *pcapfile = NULL; +struct pcap_pkthdr pcaphdr; +pcap_t *pcaphandle = NULL; +char *pcapfn = NULL; +int do_q931ToPcap= 0; + +/*Predefined Ethernet Frame with Q931-over-IP encapsulated - From remote TDM host to FreeSWITCH*/ +L3UCHAR recFrame[SNAPLEN]= { + /*IEEE 802.3 VLAN 802.1q Ethernet Frame Header*/ + 2,0,1,0xAA,0xAA,0xAA,2,0,1,0xBB,0xBB,0xBB,0x81,0,0xE0,0,0x08,0, + /*IPv4 Header (minimal size; no options)*/ + 0x45,0,0,44,0,0,0,0,64,6,0,0,2,2,2,2,1,1,1,1, + /*TCP-Header*/ + 0,0x66,0,0x66,0,0,0,0,0,0,0,0,0x50,0,0,1,0,0,0,0, + /*TPKT-Header RFC 1006*/ + 3,0,0,0 + }; + +/*Predefined Ethernet Frame with Q931-over-IP encapsulated - Frome FreeSWITCH to remote TDM host*/ +L3UCHAR sendFrame[SNAPLEN]= { + /*IEEE 802.3 VLAN 802.1q Ethernet Frame Header*/ + 2,0,1,0xBB,0xBB,0xBB,2,0,1,0xAA,0xAA,0xAA,0x81,0,0xE0,0,0x08,0, + /*IPv4 Header (minimal size; no options)*/ + 0x45,0,0,44,0,0,0,0,64,6,0,0,1,1,1,1,2,2,2,2, + /*TCP-Header*/ + 0,0x66,0,0x66,0,0,0,0,0,0,0,0,0x50,0,0,1,0,0,0,0, + /*TPKT-Header RFC 1006*/ + 3,0,0,0 + }; + +/** + * \brief Opens a pcap file for capture + * \return Success or failure + */ +static zap_status_t openPcapFile(void) +{ + if(!pcaphandle) + { + pcaphandle = pcap_open_dead(DLT_EN10MB, SNAPLEN); + if (!pcaphandle) + { + zap_log(ZAP_LOG_ERROR, "Can't open pcap session: (%s)\n", pcap_geterr(pcaphandle)); + return ZAP_FAIL; + } + } + + if(!pcapfile){ + /* Open the dump file */ + if(!(pcapfile=pcap_dump_open(pcaphandle, pcapfn))){ + zap_log(ZAP_LOG_ERROR, "Error opening output file (%s)\n", pcap_geterr(pcaphandle)); + return ZAP_FAIL; + } + } + else{ + zap_log(ZAP_LOG_WARNING, "Pcap file is already open!\n"); + return ZAP_FAIL; + } + + zap_log(ZAP_LOG_DEBUG, "Pcap file '%s' successfully opened!\n", pcapfn); + + pcaphdr.ts.tv_sec = 0; + pcaphdr.ts.tv_usec = 0; + pcapfilesize = 24; /*current pcap file header seems to be 24 bytes*/ + tcp_next_seq_no_send = 0; + tcp_next_seq_no_rec = 0; + + return ZAP_SUCCESS; +} + +/** + * \brief Closes a pcap file + * \return Success + */ +static zap_status_t closePcapFile(void) +{ + if (pcapfile) { + pcap_dump_close(pcapfile); + if (pcaphandle) pcap_close(pcaphandle); + + zap_log(ZAP_LOG_DEBUG, "Pcap file closed! File size is %lu bytes.\n", pcapfilesize); + + pcaphdr.ts.tv_sec = 0; + pcaphdr.ts.tv_usec = 0; + pcapfile = NULL; + pcaphandle = NULL; + pcapfilesize = 0; + tcp_next_seq_no_send = 0; + tcp_next_seq_no_rec = 0; + } + + /*We have allways success with this? I think so*/ + return ZAP_SUCCESS; +} + +/** + * \brief Writes a Q931 packet to a pcap file + * \return Success or failure + */ +static zap_status_t writeQ931PacketToPcap(L3UCHAR* q931buf, L3USHORT q931size, L3ULONG span_id, L3USHORT direction) +{ + L3UCHAR *frame = NULL; + struct timeval ts; + u_char spanid = (u_char)span_id; + unsigned long *tcp_next_seq_no = NULL; + + spanid=span_id; + + /*The total length of the ethernet frame generated by this function has a min length of 66 + so we don't have to care about padding :)*/ + + + /*FS is sending the packet*/ + if(direction==0){ + frame=sendFrame; + tcp_next_seq_no = &tcp_next_seq_no_send; + } + /*FS is receiving the packet*/ + else{ + frame=recFrame; + tcp_next_seq_no = &tcp_next_seq_no_rec; + } + + /*Set spanid in VLAN-ID tag*/ + frame[VLANID_OFFSET] = spanid; + + /*** Write sent packet ***/ + if(q931size > MAX_Q931_SIZE) + { + /*WARNING*/ + zap_log(ZAP_LOG_WARNING, "Q931 packet size is too big (%u)! Ignoring it!\n", q931size); + return ZAP_FAIL; + } + + /*Copy q931 buffer into frame*/ + memcpy(frame+OVERHEAD,q931buf,q931size); + + /*Store TCP sequence number in TCP header*/ + frame[TCP_SEQ_OFFSET]=(*tcp_next_seq_no>>24)&0xFF; + frame[TCP_SEQ_OFFSET+1]=(*tcp_next_seq_no>>16)&0xFF; + frame[TCP_SEQ_OFFSET+2]=(*tcp_next_seq_no>>8)&0xFF; + frame[TCP_SEQ_OFFSET+3]=*tcp_next_seq_no & 0xFF; + + /*Store size of TPKT packet*/ + q931size+=4; + frame[TPKT_SIZE_OFFSET]=(q931size>>8)&0xFF; + frame[TPKT_SIZE_OFFSET+1]=q931size&0xFF; + + /*Calc next TCP sequence number*/ + *tcp_next_seq_no+=q931size; + + /*Store size of IP packet*/ + q931size+=SIZE_IP+SIZE_TCP; + frame[IP_SIZE_OFFSET]=(q931size>>8)&0xFF; + frame[IP_SIZE_OFFSET+1]=q931size&0xFF; + + pcaphdr.caplen = SIZE_ETHERNET+SIZE_ETHERNET_CRC+q931size; + pcaphdr.len = pcaphdr.caplen; + + /* Set Timestamp */ + /* Get Time in ms. usecs would be better ... */ + gettimeofday(&ts, NULL); + /*Write it into packet header*/ + pcaphdr.ts.tv_sec = ts.tv_sec; + pcaphdr.ts.tv_usec = ts.tv_usec; + + pcap_dump((u_char*)pcapfile, &pcaphdr, frame); + pcap_dump_flush(pcapfile); + + /*Maintain pcap file size*/ + pcapfilesize+=pcaphdr.caplen; + pcapfilesize+=sizeof(struct pcap_pkthdr); + + zap_log(ZAP_LOG_DEBUG, "Added %u bytes to pcap file. File size is now %lu, \n", q931size, pcapfilesize); + + return ZAP_SUCCESS; +} + +#endif + +/** + * \brief Unloads pcap IO + * \return Success or failure + */ +static ZIO_IO_UNLOAD_FUNCTION(close_pcap) +{ +#ifdef HAVE_LIBPCAP + return closePcapFile(); +#else + return ZAP_SUCCESS; +#endif +} + +/*Q931ToPcap functions DONE*/ +/*-------------------------------------------------------------------------*/ + +/** + * \brief Gets current time + * \return Current time (in ms) + */ +static L2ULONG zap_time_now(void) +{ + return (L2ULONG)zap_current_time_in_ms(); +} + +/** + * \brief Initialises an ISDN channel (outgoing call) + * \param zchan Channel to initiate call on + * \return Success or failure + */ +static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) +{ + zap_status_t status = ZAP_SUCCESS; + zap_set_flag(zchan, ZAP_CHANNEL_OUTBOUND); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DIALING); + return status; +} + +/** + * \brief Requests an ISDN channel on a span (outgoing call) + * \param span Span where to get a channel + * \param chan_id Specific channel to get (0 for any) + * \param direction Call direction (inbound, outbound) + * \param caller_data Caller information + * \param zchan Channel to initialise + * \return Success or failure + */ +static ZIO_CHANNEL_REQUEST_FUNCTION(isdn_channel_request) +{ + Q931mes_Generic *gen = (Q931mes_Generic *) caller_data->raw_data; + Q931ie_BearerCap BearerCap; + Q931ie_ChanID ChanID = { 0 }; + Q931ie_CallingNum CallingNum; + Q931ie_CallingNum *ptrCallingNum; + Q931ie_CalledNum CalledNum; + Q931ie_CalledNum *ptrCalledNum; + Q931ie_Display Display, *ptrDisplay; + Q931ie_HLComp HLComp; /* High-Layer Compatibility IE */ + Q931ie_ProgInd Progress; /* Progress Indicator IE */ + zap_status_t status = ZAP_FAIL; + zap_isdn_data_t *isdn_data = span->signal_data; + int sanity = 60000; + int codec = 0; + + /* + * get codec type + */ + zap_channel_command(span->channels[chan_id], ZAP_COMMAND_GET_NATIVE_CODEC, &codec); + + /* + * Q.931 Setup Message + */ + Q931InitMesGeneric(gen); + gen->MesType = Q931mes_SETUP; + gen->CRVFlag = 0; /* outgoing call */ + + /* + * Bearer Capability IE + */ + Q931InitIEBearerCap(&BearerCap); + BearerCap.CodStand = Q931_CODING_ITU; /* ITU-T = 0, ISO/IEC = 1, National = 2, Network = 3 */ + BearerCap.ITC = Q931_ITC_SPEECH; /* Speech */ + BearerCap.TransMode = 0; /* Circuit = 0, Packet = 1 */ + BearerCap.ITR = Q931_ITR_64K; /* 64k */ + BearerCap.Layer1Ident = 1; + BearerCap.UIL1Prot = (codec == ZAP_CODEC_ALAW) ? Q931_UIL1P_G711A : Q931_UIL1P_G711U; /* U-law = 2, A-law = 3 */ + gen->BearerCap = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &BearerCap); + + /* + * Channel ID IE + */ + Q931InitIEChanID(&ChanID); + ChanID.IntType = ZAP_SPAN_IS_BRI(span) ? 0 : 1; /* PRI = 1, BRI = 0 */ + + if(!ZAP_SPAN_IS_NT(span)) { + ChanID.PrefExcl = (isdn_data->opts & ZAP_ISDN_OPT_SUGGEST_CHANNEL) ? 0 : 1; /* 0 = preferred, 1 exclusive */ + } else { + ChanID.PrefExcl = 1; /* always exclusive in NT-mode */ + } + + if(ChanID.IntType) { + ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */ + ChanID.ChanMapType = 3; /* B-Chan */ + ChanID.ChanSlot = (unsigned char)chan_id; + } else { + ChanID.InfoChanSel = (unsigned char)chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */ + } + gen->ChanID = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &ChanID); + + /* + * Progress IE + */ + Q931InitIEProgInd(&Progress); + Progress.CodStand = Q931_CODING_ITU; /* 0 = ITU */ + Progress.Location = 0; /* 0 = User, 1 = Private Network */ + Progress.ProgDesc = 3; /* 1 = Not end-to-end ISDN */ + gen->ProgInd = Q931AppendIE((L3UCHAR *)gen, (L3UCHAR *)&Progress); + + /* + * Display IE + */ + if (!(isdn_data->opts & ZAP_ISDN_OPT_OMIT_DISPLAY_IE)) { + Q931InitIEDisplay(&Display); + Display.Size = Display.Size + (unsigned char)strlen(caller_data->cid_name); + gen->Display = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &Display); + ptrDisplay = Q931GetIEPtr(gen->Display, gen->buf); + zap_copy_string((char *)ptrDisplay->Display, caller_data->cid_name, strlen(caller_data->cid_name)+1); + } + + /* + * Calling Number IE + */ + Q931InitIECallingNum(&CallingNum); + CallingNum.TypNum = Q931_TON_UNKNOWN; + CallingNum.NumPlanID = Q931_NUMPLAN_E164; + CallingNum.PresInd = Q931_PRES_ALLOWED; + CallingNum.ScreenInd = Q931_SCREEN_USER_NOT_SCREENED; + CallingNum.Size = CallingNum.Size + (unsigned char)strlen(caller_data->cid_num.digits); + gen->CallingNum = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &CallingNum); + ptrCallingNum = Q931GetIEPtr(gen->CallingNum, gen->buf); + zap_copy_string((char *)ptrCallingNum->Digit, caller_data->cid_num.digits, strlen(caller_data->cid_num.digits)+1); + + + /* + * Called number IE + */ + Q931InitIECalledNum(&CalledNum); + CalledNum.TypNum = Q931_TON_UNKNOWN; + CalledNum.NumPlanID = Q931_NUMPLAN_E164; + CalledNum.Size = CalledNum.Size + (unsigned char)strlen(caller_data->ani.digits); + gen->CalledNum = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &CalledNum); + ptrCalledNum = Q931GetIEPtr(gen->CalledNum, gen->buf); + zap_copy_string((char *)ptrCalledNum->Digit, caller_data->ani.digits, strlen(caller_data->ani.digits)+1); + + /* + * High-Layer Compatibility IE (Note: Required for AVM FritzBox) + */ + Q931InitIEHLComp(&HLComp); + HLComp.CodStand = Q931_CODING_ITU; /* ITU */ + HLComp.Interpret = 4; /* only possible value */ + HLComp.PresMeth = 1; /* High-layer protocol profile */ + HLComp.HLCharID = 1; /* Telephony = 1, Fax G2+3 = 4, Fax G4 = 65 (Class I)/ 68 (Class II or III) */ + gen->HLComp = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &HLComp); + + caller_data->call_state = ZAP_CALLER_STATE_DIALING; + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + isdn_data->outbound_crv[gen->CRV] = caller_data; + //isdn_data->channels_local_crv[gen->CRV] = zchan; + + while(zap_running() && caller_data->call_state == ZAP_CALLER_STATE_DIALING) { + zap_sleep(1); + + if (!--sanity) { + caller_data->call_state = ZAP_CALLER_STATE_FAIL; + break; + } + } + isdn_data->outbound_crv[gen->CRV] = NULL; + + if (caller_data->call_state == ZAP_CALLER_STATE_SUCCESS) { + zap_channel_t *new_chan = NULL; + int fail = 1; + + new_chan = NULL; + if (caller_data->chan_id < ZAP_MAX_CHANNELS_SPAN && caller_data->chan_id <= span->chan_count) { + new_chan = span->channels[caller_data->chan_id]; + } + + if (new_chan && (status = zap_channel_open_chan(new_chan) == ZAP_SUCCESS)) { + if (zap_test_flag(new_chan, ZAP_CHANNEL_INUSE) || new_chan->state != ZAP_CHANNEL_STATE_DOWN) { + if (new_chan->state == ZAP_CHANNEL_STATE_DOWN || new_chan->state >= ZAP_CHANNEL_STATE_TERMINATING) { + int x = 0; + zap_log(ZAP_LOG_WARNING, "Channel %d:%d ~ %d:%d is already in use waiting for it to become available.\n"); + + for (x = 0; x < 200; x++) { + if (!zap_test_flag(new_chan, ZAP_CHANNEL_INUSE)) { + break; + } + zap_sleep(5); + } + } + if (zap_test_flag(new_chan, ZAP_CHANNEL_INUSE)) { + zap_log(ZAP_LOG_ERROR, "Channel %d:%d ~ %d:%d is already in use.\n", + new_chan->span_id, + new_chan->chan_id, + new_chan->physical_span_id, + new_chan->physical_chan_id + ); + new_chan = NULL; + } + } + + if (new_chan && new_chan->state == ZAP_CHANNEL_STATE_DOWN) { + isdn_data->channels_local_crv[gen->CRV] = new_chan; + memset(&new_chan->caller_data, 0, sizeof(new_chan->caller_data)); + zap_set_flag(new_chan, ZAP_CHANNEL_OUTBOUND); + zap_set_state_locked(new_chan, ZAP_CHANNEL_STATE_DIALING); + switch(gen->MesType) { + case Q931mes_ALERTING: + new_chan->init_state = ZAP_CHANNEL_STATE_PROGRESS_MEDIA; + break; + case Q931mes_CONNECT: + new_chan->init_state = ZAP_CHANNEL_STATE_UP; + break; + default: + new_chan->init_state = ZAP_CHANNEL_STATE_PROGRESS; + break; + } + + fail = 0; + } + } + + if (!fail) { + *zchan = new_chan; + return ZAP_SUCCESS; + } else { + Q931ie_Cause cause; + gen->MesType = Q931mes_DISCONNECT; + cause.IEId = Q931ie_CAUSE; + cause.Size = sizeof(Q931ie_Cause); + cause.CodStand = 0; + cause.Location = 1; + cause.Recom = 1; + //should we be casting here.. or do we need to translate value? + cause.Value = (unsigned char) ZAP_CAUSE_WRONG_CALL_STATE; + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + if (gen->CRV) { + Q931ReleaseCRV(&isdn_data->q931, gen->CRV); + } + + if (new_chan) { + zap_log(ZAP_LOG_CRIT, "Channel is busy\n"); + } else { + zap_log(ZAP_LOG_CRIT, "Failed to open channel for new setup message\n"); + } + } + } + + *zchan = NULL; + return ZAP_FAIL; + +} + +/** + * \brief Handler for Q931 error + * \param pvt Private structure (span?) + * \param id Error number + * \param p1 ?? + * \param p2 ?? + * \return 0 + */ +static L3INT zap_isdn_931_err(void *pvt, L3INT id, L3INT p1, L3INT p2) +{ + zap_log(ZAP_LOG_ERROR, "ERROR: [%s] [%d] [%d]\n", q931_error_to_name(id), p1, p2); + return 0; +} + +/** + * \brief Handler for Q931 event message + * \param pvt Span to handle + * \param msg Message string + * \param mlen Message string length + * \return 0 + */ +static L3INT zap_isdn_931_34(void *pvt, L2UCHAR *msg, L2INT mlen) +{ + zap_span_t *span = (zap_span_t *) pvt; + zap_isdn_data_t *isdn_data = span->signal_data; + Q931mes_Generic *gen = (Q931mes_Generic *) msg; + uint32_t chan_id = 0; + int chan_hunt = 0; + zap_channel_t *zchan = NULL; + zap_caller_data_t *caller_data = NULL; + + if (Q931IsIEPresent(gen->ChanID)) { + Q931ie_ChanID *chanid = Q931GetIEPtr(gen->ChanID, gen->buf); + + if(chanid->IntType) + chan_id = chanid->ChanSlot; + else + chan_id = chanid->InfoChanSel; + + /* "any" channel specified */ + if(chanid->InfoChanSel == 3) { + chan_hunt++; + } + } else if (ZAP_SPAN_IS_NT(span)) { + /* no channel ie */ + chan_hunt++; + } + + assert(span != NULL); + assert(isdn_data != NULL); + + zap_log(ZAP_LOG_DEBUG, "Yay I got an event! Type:[%02x] Size:[%d] CRV: %d (%#hx, CTX: %s)\n", gen->MesType, gen->Size, gen->CRV, gen->CRV, gen->CRVFlag ? "Terminator" : "Originator"); + + if (gen->CRVFlag && (caller_data = isdn_data->outbound_crv[gen->CRV])) { + if (chan_id) { + caller_data->chan_id = chan_id; + } + + switch(gen->MesType) { + case Q931mes_STATUS: + case Q931mes_CALL_PROCEEDING: + break; + case Q931mes_ALERTING: + case Q931mes_PROGRESS: + case Q931mes_CONNECT: + { + caller_data->call_state = ZAP_CALLER_STATE_SUCCESS; + } + break; + default: + caller_data->call_state = ZAP_CALLER_STATE_FAIL; + break; + } + + return 0; + } + + if (gen->CRVFlag) { + zchan = isdn_data->channels_local_crv[gen->CRV]; + } else { + zchan = isdn_data->channels_remote_crv[gen->CRV]; + } + + zap_log(ZAP_LOG_DEBUG, "zchan %x (%d:%d) source isdn_data->channels_%s_crv[%#hx]\n", zchan, zchan ? zchan->span_id : -1, zchan ? zchan->chan_id : -1, gen->CRVFlag ? "local" : "remote", gen->CRV); + + + if (gen->ProtDisc == 3) { + switch(gen->MesType) { + case Q931mes_SERVICE: + { + Q931ie_ChangeStatus *changestatus = Q931GetIEPtr(gen->ChangeStatus, gen->buf); + if (zchan) { + switch (changestatus->NewStatus) { + case 0: /* change status to "in service" */ + { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_SUSPENDED); + zap_log(ZAP_LOG_DEBUG, "Channel %d:%d in service\n", zchan->span_id, zchan->chan_id); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + } + break; + case 1: + { /* change status to "maintenance" */ + zap_set_flag_locked(zchan, ZAP_CHANNEL_SUSPENDED); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_SUSPENDED); + } + break; + case 2: + { /* change status to "out of service" */ + zap_set_flag_locked(zchan, ZAP_CHANNEL_SUSPENDED); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_SUSPENDED); + } + break; + default: /* unknown */ + { + break; + } + } + } + } + break; + default: + break; + } + } else { + switch(gen->MesType) { + case Q931mes_RESTART: + { + if (chan_id) { + zchan = span->channels[chan_id]; + } + if (zchan) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + } else { + uint32_t i; + for (i = 1; i < span->chan_count; i++) { + zap_set_state_locked((span->channels[i]), ZAP_CHANNEL_STATE_RESTART); + } + } + } + break; + case Q931mes_RELEASE: + case Q931mes_RELEASE_COMPLETE: + { + const char *what = gen->MesType == Q931mes_RELEASE ? "Release" : "Release Complete"; + if (zchan) { + if (zchan->state == ZAP_CHANNEL_STATE_TERMINATING || zchan->state == ZAP_CHANNEL_STATE_HANGUP) { + if (gen->MesType == Q931mes_RELEASE) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP_COMPLETE); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + else if((gen->MesType == Q931mes_RELEASE && zchan->state <= ZAP_CHANNEL_STATE_UP) || + (gen->MesType == Q931mes_RELEASE_COMPLETE && zchan->state == ZAP_CHANNEL_STATE_DIALING)) { + + /* + * Don't keep inbound channels open if the remote side hangs up before we answered + */ + Q931ie_Cause *cause = Q931GetIEPtr(gen->Cause, gen->buf); + zap_sigmsg_t sig; + zap_status_t status; + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + sig.channel->caller_data.hangup_cause = (cause) ? cause->Value : ZAP_CAUSE_NORMAL_UNSPECIFIED; + + sig.event_id = ZAP_SIGEVENT_STOP; + status = zap_span_send_signal(zchan->span, &sig); + + zap_log(ZAP_LOG_DEBUG, "Received %s in state %s, requested hangup for channel %d:%d\n", what, zap_channel_state2str(zchan->state), zchan->span_id, chan_id); + } + else { + zap_log(ZAP_LOG_DEBUG, "Ignoring %s on channel %d\n", what, chan_id); + } + } else { + zap_log(ZAP_LOG_CRIT, "Received %s with no matching channel %d\n", what, chan_id); + } + } + break; + case Q931mes_DISCONNECT: + { + if (zchan) { + Q931ie_Cause *cause = Q931GetIEPtr(gen->Cause, gen->buf); + zchan->caller_data.hangup_cause = cause->Value; + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_TERMINATING); + } else { + zap_log(ZAP_LOG_CRIT, "Received Disconnect with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_ALERTING: + { + if (zchan) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); + } else { + zap_log(ZAP_LOG_CRIT, "Received Alerting with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_PROGRESS: + { + if (zchan) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS); + } else { + zap_log(ZAP_LOG_CRIT, "Received Progress with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_CONNECT: + { + if (zchan) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + + gen->MesType = Q931mes_CONNECT_ACKNOWLEDGE; + gen->CRVFlag = 0; /* outbound */ + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + } else { + zap_log(ZAP_LOG_CRIT, "Received Connect with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_SETUP: + { + Q931ie_CallingNum *callingnum = Q931GetIEPtr(gen->CallingNum, gen->buf); + Q931ie_CalledNum *callednum = Q931GetIEPtr(gen->CalledNum, gen->buf); + int fail = 1; + int fail_cause = 0; + int overlap_dial = 0; + uint32_t cplen = mlen; + + if(zchan && zchan == isdn_data->channels_remote_crv[gen->CRV]) { + zap_log(ZAP_LOG_INFO, "Duplicate SETUP message(?) for Channel %d:%d ~ %d:%d in state %s [ignoring]\n", + zchan->span_id, + zchan->chan_id, + zchan->physical_span_id, + zchan->physical_chan_id, + zap_channel_state2str(zchan->state)); + break; + } + + zchan = NULL; + /* + * Channel selection for incoming calls: + */ + if (ZAP_SPAN_IS_NT(span) && chan_hunt) { + uint32_t x; + + /* + * In NT-mode with channel selection "any", + * try to find a free channel + */ + for (x = 1; x <= span->chan_count; x++) { + zap_channel_t *zc = span->channels[x]; + + if (!zap_test_flag(zc, ZAP_CHANNEL_INUSE) && zc->state == ZAP_CHANNEL_STATE_DOWN) { + zchan = zc; + break; + } + } + } + else if (!ZAP_SPAN_IS_NT(span) && chan_hunt) { + /* + * In TE-mode this ("any") is invalid + */ + fail_cause = ZAP_CAUSE_CHANNEL_UNACCEPTABLE; + + zap_log(ZAP_LOG_ERROR, "Invalid channel selection in incoming call (network side didn't specify a channel)\n"); + } + else { + /* + * Otherwise simply try to select the channel we've been told + * + * TODO: NT mode is abled to select a different channel if the one chosen + * by the TE side is already in use + */ + if (chan_id > 0 && chan_id < ZAP_MAX_CHANNELS_SPAN && chan_id <= span->chan_count) { + zchan = span->channels[chan_id]; + } + else { + /* invalid channel id */ + fail_cause = ZAP_CAUSE_CHANNEL_UNACCEPTABLE; + + zap_log(ZAP_LOG_ERROR, "Invalid channel selection in incoming call (none selected or out of bounds)\n"); + } + } + + if (!callednum || !strlen((char *)callednum->Digit)) { + if (ZAP_SPAN_IS_NT(span)) { + zap_log(ZAP_LOG_NOTICE, "No destination number found, assuming overlap dial\n"); + overlap_dial++; + } + else { + zap_log(ZAP_LOG_ERROR, "No destination number found\n"); + zchan = NULL; + } + } + + if (zchan) { + if (zap_test_flag(zchan, ZAP_CHANNEL_INUSE) || zchan->state != ZAP_CHANNEL_STATE_DOWN) { + if (zchan->state == ZAP_CHANNEL_STATE_DOWN || zchan->state >= ZAP_CHANNEL_STATE_TERMINATING) { + int x = 0; + zap_log(ZAP_LOG_WARNING, "Channel %d:%d ~ %d:%d is already in use waiting for it to become available.\n", + zchan->span_id, + zchan->chan_id, + zchan->physical_span_id, + zchan->physical_chan_id); + + for (x = 0; x < 200; x++) { + if (!zap_test_flag(zchan, ZAP_CHANNEL_INUSE)) { + break; + } + zap_sleep(5); + } + } + if (zap_test_flag(zchan, ZAP_CHANNEL_INUSE)) { + zap_log(ZAP_LOG_ERROR, "Channel %d:%d ~ %d:%d is already in use.\n", + zchan->span_id, + zchan->chan_id, + zchan->physical_span_id, + zchan->physical_chan_id + ); + zchan = NULL; + } + } + + if (zchan && zchan->state == ZAP_CHANNEL_STATE_DOWN) { + isdn_data->channels_remote_crv[gen->CRV] = zchan; + memset(&zchan->caller_data, 0, sizeof(zchan->caller_data)); + + if (zchan->mod_data) { + memset(zchan->mod_data, 0, sizeof(zap_isdn_bchan_data_t)); + } + + zap_set_string(zchan->caller_data.cid_num.digits, (char *)callingnum->Digit); + zap_set_string(zchan->caller_data.cid_name, (char *)callingnum->Digit); + zap_set_string(zchan->caller_data.ani.digits, (char *)callingnum->Digit); + if (!overlap_dial) { + zap_set_string(zchan->caller_data.dnis.digits, (char *)callednum->Digit); + } + + zchan->caller_data.CRV = gen->CRV; + if (cplen > sizeof(zchan->caller_data.raw_data)) { + cplen = sizeof(zchan->caller_data.raw_data); + } + gen->CRVFlag = !(gen->CRVFlag); + memcpy(zchan->caller_data.raw_data, msg, cplen); + zchan->caller_data.raw_data_len = cplen; + fail = 0; + } + } + + if (fail) { + Q931ie_Cause cause; + gen->MesType = Q931mes_DISCONNECT; + gen->CRVFlag = 1; /* inbound call */ + cause.IEId = Q931ie_CAUSE; + cause.Size = sizeof(Q931ie_Cause); + cause.CodStand = Q931_CODING_ITU; + cause.Location = 1; + cause.Recom = 1; + //should we be casting here.. or do we need to translate value? + cause.Value = (unsigned char)((fail_cause) ? fail_cause : ZAP_CAUSE_WRONG_CALL_STATE); + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + if (gen->CRV) { + Q931ReleaseCRV(&isdn_data->q931, gen->CRV); + } + + if (zchan) { + zap_log(ZAP_LOG_CRIT, "Channel is busy\n"); + } else { + zap_log(ZAP_LOG_CRIT, "Failed to open channel for new setup message\n"); + } + + } else { + Q931ie_ChanID ChanID; + + /* + * Update Channel ID IE + */ + Q931InitIEChanID(&ChanID); + ChanID.IntType = ZAP_SPAN_IS_BRI(zchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */ + ChanID.PrefExcl = ZAP_SPAN_IS_NT(zchan->span) ? 1 : 0; /* Exclusive in NT-mode = 1, Preferred otherwise = 0 */ + if(ChanID.IntType) { + ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */ + ChanID.ChanMapType = 3; /* B-Chan */ + ChanID.ChanSlot = (unsigned char)zchan->chan_id; + } else { + ChanID.InfoChanSel = (unsigned char)zchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */ + } + gen->ChanID = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &ChanID); + + if (overlap_dial) { + Q931ie_ProgInd progress; + + /* + * Setup Progress indicator + */ + progress.IEId = Q931ie_PROGRESS_INDICATOR; + progress.Size = sizeof(Q931ie_ProgInd); + progress.CodStand = Q931_CODING_ITU; /* ITU */ + progress.Location = 1; /* private network serving the local user */ + progress.ProgDesc = 8; /* call is not end-to-end isdn = 1, in-band information available = 8 */ + gen->ProgInd = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &progress); + + /* + * Send SETUP ACK + */ + gen->MesType = Q931mes_SETUP_ACKNOWLEDGE; + gen->CRVFlag = 1; /* inbound call */ + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DIALTONE); + } else { + /* + * Advance to RING state + */ + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RING); + } + } + } + break; + + case Q931mes_CALL_PROCEEDING: + { + if (zchan) { + zap_log(ZAP_LOG_CRIT, "Received CALL PROCEEDING message for channel %d\n", chan_id); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS); + } else { + zap_log(ZAP_LOG_CRIT, "Received CALL PROCEEDING with no matching channel %d\n", chan_id); + } + } + break; + case Q931mes_CONNECT_ACKNOWLEDGE: + { + if (zchan) { + zap_log(ZAP_LOG_DEBUG, "Received CONNECT_ACK message for channel %d\n", chan_id); + } else { + zap_log(ZAP_LOG_DEBUG, "Received CONNECT_ACK with no matching channel %d\n", chan_id); + } + } + break; + + case Q931mes_INFORMATION: + { + if (zchan) { + zap_log(ZAP_LOG_CRIT, "Received INFORMATION message for channel %d\n", zchan->chan_id); + + if (zchan->state == ZAP_CHANNEL_STATE_DIALTONE) { + char digit = '\0'; + + /* + * overlap dial digit indication + */ + if (Q931IsIEPresent(gen->CalledNum)) { + zap_isdn_bchan_data_t *data = (zap_isdn_bchan_data_t *)zchan->mod_data; + Q931ie_CalledNum *callednum = Q931GetIEPtr(gen->CalledNum, gen->buf); + int pos; + + digit = callednum->Digit[strlen((char *)callednum->Digit) - 1]; + if (digit == '#') { + callednum->Digit[strlen((char *)callednum->Digit) - 1] = '\0'; + } + + /* TODO: make this more safe with strncat() */ + pos = (int)strlen(zchan->caller_data.dnis.digits); + strcat(&zchan->caller_data.dnis.digits[pos], (char *)callednum->Digit); + + /* update timer */ + data->digit_timeout = zap_time_now() + isdn_data->digit_timeout; + + zap_log(ZAP_LOG_DEBUG, "Received new overlap digit (%s), destination number: %s\n", callednum->Digit, zchan->caller_data.dnis.digits); + } + + if (Q931IsIEPresent(gen->SendComplete) || digit == '#') { + zap_log(ZAP_LOG_DEBUG, "Leaving overlap dial mode\n"); + + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RING); + } + } + } else { + zap_log(ZAP_LOG_CRIT, "Received INFORMATION message with no matching channel\n"); + } + } + break; + + case Q931mes_STATUS_ENQUIRY: + { + /* + * !! HACK ALERT !! + * + * Map OpenZAP channel states to Q.931 states + */ + Q931ie_CallState state; + Q931ie_Cause cause; + + gen->MesType = Q931mes_STATUS; + gen->CRVFlag = gen->CRVFlag ? 0 : 1; + + state.CodStand = Q931_CODING_ITU; /* ITU-T */ + state.CallState = Q931_U0; /* Default: Null */ + + cause.IEId = Q931ie_CAUSE; + cause.Size = sizeof(Q931ie_Cause); + cause.CodStand = Q931_CODING_ITU; /* ITU */ + cause.Location = 1; /* private network */ + cause.Recom = 1; /* */ + *cause.Diag = '\0'; + + if(zchan) { + switch(zchan->state) { + case ZAP_CHANNEL_STATE_UP: + state.CallState = Q931_U10; /* Active */ + break; + case ZAP_CHANNEL_STATE_RING: + state.CallState = Q931_U6; /* Call present */ + break; + case ZAP_CHANNEL_STATE_DIALING: + state.CallState = Q931_U1; /* Call initiated */ + break; + case ZAP_CHANNEL_STATE_DIALTONE: + state.CallState = Q931_U25; /* Overlap receiving */ + break; + + /* TODO: map missing states */ + + default: + state.CallState = Q931_U0; + } + + cause.Value = 30; /* response to STATUS ENQUIRY */ + } else { + cause.Value = 98; /* */ + } + + gen->CallState = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &state); + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + } + break; + + default: + zap_log(ZAP_LOG_CRIT, "Received unhandled message %d (%#x)\n", (int)gen->MesType, (int)gen->MesType); + break; + } + } + + return 0; +} + +/** + * \brief Handler for Q921 read event + * \param pvt Span were message is coming from + * \param ind Q921 indication + * \param tei Terminal Endpoint Identifier + * \param msg Message string + * \param mlen Message string length + * \return 0 on success, 1 on failure + */ +static int zap_isdn_921_23(void *pvt, Q921DLMsg_t ind, L2UCHAR tei, L2UCHAR *msg, L2INT mlen) +{ + int ret, offset = (ind == Q921_DL_DATA) ? 4 : 3; + char bb[4096] = ""; + + switch(ind) { + case Q921_DL_DATA: + case Q921_DL_UNIT_DATA: + print_hex_bytes(msg + offset, mlen - offset, bb, sizeof(bb)); +#ifdef HAVE_LIBPCAP + /*Q931ToPcap*/ + if(do_q931ToPcap==1){ + zap_span_t *span = (zap_span_t *) pvt; + if(writeQ931PacketToPcap(msg + offset, mlen - offset, span->span_id, 1) != ZAP_SUCCESS){ + zap_log(ZAP_LOG_WARNING, "Couldn't write Q931 buffer to pcap file!\n"); + } + } + /*Q931ToPcap done*/ +#endif + zap_log(ZAP_LOG_DEBUG, "READ %d\n%s\n%s\n\n\n", (int)mlen - offset, LINE, bb); + + default: + ret = Q931Rx23(pvt, ind, tei, msg, mlen); + if (ret != 0) + zap_log(ZAP_LOG_DEBUG, "931 parse error [%d] [%s]\n", ret, q931_error_to_name(ret)); + break; + } + + return ((ret >= 0) ? 1 : 0); +} + +/** + * \brief Handler for Q921 write event + * \param pvt Span were message is coming from + * \param msg Message string + * \param mlen Message string length + * \return 0 on success, -1 on failure + */ +static int zap_isdn_921_21(void *pvt, L2UCHAR *msg, L2INT mlen) +{ + zap_span_t *span = (zap_span_t *) pvt; + zap_size_t len = (zap_size_t) mlen; + zap_isdn_data_t *isdn_data = span->signal_data; + +#ifdef IODEBUG + char bb[4096] = ""; + print_hex_bytes(msg, len, bb, sizeof(bb)); + print_bits(msg, (int)len, bb, sizeof(bb), ZAP_ENDIAN_LITTLE, 0); + zap_log(ZAP_LOG_DEBUG, "WRITE %d\n%s\n%s\n\n", (int)len, LINE, bb); + +#endif + + assert(span != NULL); + return zap_channel_write(isdn_data->dchan, msg, len, &len) == ZAP_SUCCESS ? 0 : -1; +} + +/** + * \brief Handler for channel state change + * \param zchan Channel to handle + */ +static __inline__ void state_advance(zap_channel_t *zchan) +{ + Q931mes_Generic *gen = (Q931mes_Generic *) zchan->caller_data.raw_data; + zap_isdn_data_t *isdn_data = zchan->span->signal_data; + zap_sigmsg_t sig; + zap_status_t status; + + zap_log(ZAP_LOG_DEBUG, "%d:%d STATE [%s]\n", + zchan->span_id, zchan->chan_id, zap_channel_state2str(zchan->state)); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + + switch (zchan->state) { + case ZAP_CHANNEL_STATE_DOWN: + { + if (gen->CRV) { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + isdn_data->channels_local_crv[gen->CRV] = NULL; + } else { + isdn_data->channels_remote_crv[gen->CRV] = NULL; + } + Q931ReleaseCRV(&isdn_data->q931, gen->CRV); + } + zap_channel_done(zchan); + } + break; + case ZAP_CHANNEL_STATE_PROGRESS: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_PROGRESS; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + gen->MesType = Q931mes_CALL_PROCEEDING; + gen->CRVFlag = 1; /* inbound */ + + if (ZAP_SPAN_IS_NT(zchan->span)) { + Q931ie_ChanID ChanID; + + /* + * Set new Channel ID + */ + Q931InitIEChanID(&ChanID); + ChanID.IntType = ZAP_SPAN_IS_BRI(zchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */ + ChanID.PrefExcl = 1; /* always exclusive in NT-mode */ + + if(ChanID.IntType) { + ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */ + ChanID.ChanMapType = 3; /* B-Chan */ + ChanID.ChanSlot = (unsigned char)zchan->chan_id; + } else { + ChanID.InfoChanSel = (unsigned char)zchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */ + } + gen->ChanID = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &ChanID); + } + + Q931Rx43(&isdn_data->q931, (void *)gen, gen->Size); + } + } + break; + case ZAP_CHANNEL_STATE_DIALTONE: + { + zap_isdn_bchan_data_t *data = (zap_isdn_bchan_data_t *)zchan->mod_data; + + if (data) { + data->digit_timeout = zap_time_now() + isdn_data->digit_timeout; + } + } + break; + case ZAP_CHANNEL_STATE_RING: + { + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_START; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } + } + break; + case ZAP_CHANNEL_STATE_RESTART: + { + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_UNSPECIFIED; + sig.event_id = ZAP_SIGEVENT_RESTART; + status = zap_span_send_signal(zchan->span, &sig); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + break; + case ZAP_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_PROGRESS_MEDIA; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + if (!zap_test_flag(zchan, ZAP_CHANNEL_OPEN)) { + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + return; + } + } + gen->MesType = Q931mes_ALERTING; + gen->CRVFlag = 1; /* inbound call */ + Q931Rx43(&isdn_data->q931, (void *)gen, gen->Size); + } + } + break; + case ZAP_CHANNEL_STATE_UP: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_UP; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + if (!zap_test_flag(zchan, ZAP_CHANNEL_OPEN)) { + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + return; + } + } + gen->MesType = Q931mes_CONNECT; + gen->BearerCap = 0; + gen->CRVFlag = 1; /* inbound call */ + Q931Rx43(&isdn_data->q931, (void *)gen, zchan->caller_data.raw_data_len); + } + } + break; + case ZAP_CHANNEL_STATE_DIALING: + if (!(isdn_data->opts & ZAP_ISDN_OPT_SUGGEST_CHANNEL)) { + Q931ie_BearerCap BearerCap; + Q931ie_ChanID ChanID; + Q931ie_CallingNum CallingNum; + Q931ie_CallingNum *ptrCallingNum; + Q931ie_CalledNum CalledNum; + Q931ie_CalledNum *ptrCalledNum; + Q931ie_Display Display, *ptrDisplay; + Q931ie_HLComp HLComp; /* High-Layer Compatibility IE */ + Q931ie_ProgInd Progress; /* Progress Indicator IE */ + int codec = 0; + + /* + * get codec type + */ + zap_channel_command(zchan->span->channels[zchan->chan_id], ZAP_COMMAND_GET_NATIVE_CODEC, &codec); + + /* + * Q.931 Setup Message + */ + Q931InitMesGeneric(gen); + gen->MesType = Q931mes_SETUP; + gen->CRVFlag = 0; /* outbound(?) */ + + /* + * Bearer Capability IE + */ + Q931InitIEBearerCap(&BearerCap); + BearerCap.CodStand = Q931_CODING_ITU; /* ITU-T = 0, ISO/IEC = 1, National = 2, Network = 3 */ + BearerCap.ITC = Q931_ITC_SPEECH; /* Speech */ + BearerCap.TransMode = 0; /* Circuit = 0, Packet = 1 */ + BearerCap.ITR = Q931_ITR_64K; /* 64k = 16, Packet mode = 0 */ + BearerCap.Layer1Ident = 1; + BearerCap.UIL1Prot = (codec == ZAP_CODEC_ALAW) ? 3 : 2; /* U-law = 2, A-law = 3 */ + gen->BearerCap = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &BearerCap); + + /* + * ChannelID IE + */ + Q931InitIEChanID(&ChanID); + ChanID.IntType = ZAP_SPAN_IS_BRI(zchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */ + ChanID.PrefExcl = ZAP_SPAN_IS_NT(zchan->span) ? 1 : 0; /* Exclusive in NT-mode = 1, Preferred otherwise = 0 */ + if(ChanID.IntType) { + ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */ + ChanID.ChanMapType = 3; /* B-Chan */ + ChanID.ChanSlot = (unsigned char)zchan->chan_id; + } else { + ChanID.InfoChanSel = (unsigned char)zchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */ + } + gen->ChanID = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &ChanID); + + /* + * Progress IE + */ + Q931InitIEProgInd(&Progress); + Progress.CodStand = Q931_CODING_ITU; /* 0 = ITU */ + Progress.Location = 0; /* 0 = User, 1 = Private Network */ + Progress.ProgDesc = 3; /* 1 = Not end-to-end ISDN */ + gen->ProgInd = Q931AppendIE((L3UCHAR *)gen, (L3UCHAR *)&Progress); + + /* + * Display IE + */ + if (!(isdn_data->opts & ZAP_ISDN_OPT_OMIT_DISPLAY_IE)) { + Q931InitIEDisplay(&Display); + Display.Size = Display.Size + (unsigned char)strlen(zchan->caller_data.cid_name); + gen->Display = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &Display); + ptrDisplay = Q931GetIEPtr(gen->Display, gen->buf); + zap_copy_string((char *)ptrDisplay->Display, zchan->caller_data.cid_name, strlen(zchan->caller_data.cid_name)+1); + } + + /* + * CallingNum IE + */ + Q931InitIECallingNum(&CallingNum); + CallingNum.TypNum = zchan->caller_data.ani.type; + CallingNum.NumPlanID = Q931_NUMPLAN_E164; + CallingNum.PresInd = Q931_PRES_ALLOWED; + CallingNum.ScreenInd = Q931_SCREEN_USER_NOT_SCREENED; + CallingNum.Size = CallingNum.Size + (unsigned char)strlen(zchan->caller_data.cid_num.digits); + gen->CallingNum = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &CallingNum); + ptrCallingNum = Q931GetIEPtr(gen->CallingNum, gen->buf); + zap_copy_string((char *)ptrCallingNum->Digit, zchan->caller_data.cid_num.digits, strlen(zchan->caller_data.cid_num.digits)+1); + + /* + * CalledNum IE + */ + Q931InitIECalledNum(&CalledNum); + CalledNum.TypNum = Q931_TON_UNKNOWN; + CalledNum.NumPlanID = Q931_NUMPLAN_E164; + CalledNum.Size = CalledNum.Size + (unsigned char)strlen(zchan->caller_data.ani.digits); + gen->CalledNum = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &CalledNum); + ptrCalledNum = Q931GetIEPtr(gen->CalledNum, gen->buf); + zap_copy_string((char *)ptrCalledNum->Digit, zchan->caller_data.ani.digits, strlen(zchan->caller_data.ani.digits)+1); + + /* + * High-Layer Compatibility IE (Note: Required for AVM FritzBox) + */ + Q931InitIEHLComp(&HLComp); + HLComp.CodStand = Q931_CODING_ITU; /* ITU */ + HLComp.Interpret = 4; /* only possible value */ + HLComp.PresMeth = 1; /* High-layer protocol profile */ + HLComp.HLCharID = Q931_HLCHAR_TELEPHONY; /* Telephony = 1, Fax G2+3 = 4, Fax G4 = 65 (Class I)/ 68 (Class II or III) */ /* TODO: make accessible from user layer */ + gen->HLComp = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &HLComp); + + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + isdn_data->channels_local_crv[gen->CRV] = zchan; + } + break; + case ZAP_CHANNEL_STATE_HANGUP_COMPLETE: + { + /* reply RELEASE with RELEASE_COMPLETE message */ + if(zchan->last_state == ZAP_CHANNEL_STATE_HANGUP) { + gen->MesType = Q931mes_RELEASE_COMPLETE; + + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + } + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + break; + case ZAP_CHANNEL_STATE_HANGUP: + { + Q931ie_Cause cause; + + zap_log(ZAP_LOG_DEBUG, "Hangup: Direction %s\n", zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND) ? "Outbound" : "Inbound"); + + gen->CRVFlag = zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND) ? 0 : 1; + + cause.IEId = Q931ie_CAUSE; + cause.Size = sizeof(Q931ie_Cause); + cause.CodStand = Q931_CODING_ITU; /* ITU */ + cause.Location = 1; /* private network */ + cause.Recom = 1; /* */ + + /* + * BRI PTMP needs special handling here... + * TODO: cleanup / refine (see above) + */ + if (zchan->last_state == ZAP_CHANNEL_STATE_RING) { + /* + * inbound call [was: number unknown (= not found in routing state)] + * (in Q.931 spec terms: Reject request) + */ + gen->MesType = Q931mes_RELEASE_COMPLETE; + + //cause.Value = (unsigned char) ZAP_CAUSE_UNALLOCATED; + cause.Value = (unsigned char) zchan->caller_data.hangup_cause; + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + + /* we're done, release channel */ + //zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP_COMPLETE); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + else if (zchan->last_state <= ZAP_CHANNEL_STATE_PROGRESS) { + /* + * just release all unanswered calls [was: inbound call, remote side hung up before we answered] + */ + gen->MesType = Q931mes_RELEASE; + + cause.Value = (unsigned char) zchan->caller_data.hangup_cause; + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (void *)gen, gen->Size); + + /* this will be triggered by the RELEASE_COMPLETE reply */ + /* zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP_COMPLETE); */ + } + else { + /* + * call connected, hangup + */ + gen->MesType = Q931mes_DISCONNECT; + + cause.Value = (unsigned char) zchan->caller_data.hangup_cause; + *cause.Diag = '\0'; + gen->Cause = Q931AppendIE((L3UCHAR *) gen, (L3UCHAR *) &cause); + Q931Rx43(&isdn_data->q931, (L3UCHAR *) gen, gen->Size); + } + } + break; + case ZAP_CHANNEL_STATE_TERMINATING: + { + zap_log(ZAP_LOG_DEBUG, "Terminating: Direction %s\n", zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND) ? "Outbound" : "Inbound"); + + sig.event_id = ZAP_SIGEVENT_STOP; + status = zap_span_send_signal(zchan->span, &sig); + gen->MesType = Q931mes_RELEASE; + gen->CRVFlag = zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND) ? 0 : 1; + Q931Rx43(&isdn_data->q931, (void *)gen, gen->Size); + } + default: + break; + } +} + +/** + * \brief Checks current state on a span + * \param span Span to check status on + */ +static __inline__ void check_state(zap_span_t *span) +{ + if (zap_test_flag(span, ZAP_SPAN_STATE_CHANGE)) { + uint32_t j; + zap_clear_flag_locked(span, ZAP_SPAN_STATE_CHANGE); + for(j = 1; j <= span->chan_count; j++) { + if (zap_test_flag((span->channels[j]), ZAP_CHANNEL_STATE_CHANGE)) { + zap_mutex_lock(span->channels[j]->mutex); + zap_clear_flag((span->channels[j]), ZAP_CHANNEL_STATE_CHANGE); + state_advance(span->channels[j]); + zap_channel_complete_state(span->channels[j]); + zap_mutex_unlock(span->channels[j]->mutex); + } + } + } +} + +/** + * \brief Processes Openzap event on a span + * \param span Span to process event on + * \param event Event to process + * \return Success or failure + */ +static __inline__ zap_status_t process_event(zap_span_t *span, zap_event_t *event) +{ + zap_log(ZAP_LOG_DEBUG, "EVENT [%s][%d:%d] STATE [%s]\n", + zap_oob_event2str(event->enum_id), event->channel->span_id, event->channel->chan_id, zap_channel_state2str(event->channel->state)); + + switch(event->enum_id) { + case ZAP_OOB_ALARM_TRAP: + { + if (event->channel->state != ZAP_CHANNEL_STATE_DOWN) { + if (event->channel->type == ZAP_CHAN_TYPE_B) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_RESTART); + } + } + + + zap_set_flag(event->channel, ZAP_CHANNEL_SUSPENDED); + + + zap_channel_get_alarms(event->channel); + zap_log(ZAP_LOG_WARNING, "channel %d:%d (%d:%d) has alarms! [%s]\n", + event->channel->span_id, event->channel->chan_id, + event->channel->physical_span_id, event->channel->physical_chan_id, + event->channel->last_error); + } + break; + case ZAP_OOB_ALARM_CLEAR: + { + + zap_log(ZAP_LOG_WARNING, "channel %d:%d (%d:%d) alarms Cleared!\n", event->channel->span_id, event->channel->chan_id, + event->channel->physical_span_id, event->channel->physical_chan_id); + + zap_clear_flag(event->channel, ZAP_CHANNEL_SUSPENDED); + zap_channel_get_alarms(event->channel); + } + break; + } + + return ZAP_SUCCESS; +} + +/** + * \brief Checks for events on a span + * \param span Span to check for events + */ +static __inline__ void check_events(zap_span_t *span) +{ + zap_status_t status; + + status = zap_span_poll_event(span, 5); + + switch(status) { + case ZAP_SUCCESS: + { + zap_event_t *event; + while (zap_span_next_event(span, &event) == ZAP_SUCCESS) { + if (event->enum_id == ZAP_OOB_NOOP) { + continue; + } + if (process_event(span, event) != ZAP_SUCCESS) { + break; + } + } + } + break; + case ZAP_FAIL: + { + zap_log(ZAP_LOG_DEBUG, "Event Failure! %d\n", zap_running()); + } + break; + default: + break; + } +} + +/** + * \brief Retrieves tone generation output to be sent + * \param ts Teletone generator + * \param map Tone map + * \return -1 on error, 0 on success + */ +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + zap_buffer_t *dt_buffer = ts->user_data; + int wrote; + + if (!dt_buffer) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + zap_buffer_write(dt_buffer, ts->buffer, wrote * 2); + return 0; +} + +/** + * \brief Main thread function for tone generation on a span + * \param me Current thread + * \param obj Span to generate tones on + */ +static void *zap_isdn_tones_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_isdn_data_t *isdn_data = span->signal_data; + zap_buffer_t *dt_buffer = NULL; + teletone_generation_session_t ts = {{{{0}}}}; + unsigned char frame[1024]; + uint32_t x; + int interval = 0; + int offset = 0; + + zap_log(ZAP_LOG_DEBUG, "ISDN tones thread starting.\n"); + zap_set_flag(isdn_data, ZAP_ISDN_TONES_RUNNING); + + if (zap_buffer_create(&dt_buffer, 1024, 1024, 0) != ZAP_SUCCESS) { + snprintf(isdn_data->dchan->last_error, sizeof(isdn_data->dchan->last_error), "memory error!"); + zap_log(ZAP_LOG_ERROR, "MEM ERROR\n"); + goto done; + } + zap_buffer_set_loops(dt_buffer, -1); + + /* get a tone generation friendly interval to avoid distortions */ + for (x = 1; x <= span->chan_count; x++) { + if (span->channels[x]->type != ZAP_CHAN_TYPE_DQ921) { + zap_channel_command(span->channels[x], ZAP_COMMAND_GET_INTERVAL, &interval); + break; + } + } + if (!interval) { + interval = 20; + } + zap_log(ZAP_LOG_NOTICE, "Tone generating interval %d\n", interval); + + /* init teletone */ + teletone_init_session(&ts, 0, teletone_handler, dt_buffer); + ts.rate = 8000; + ts.duration = ts.rate; + + /* main loop */ + while(zap_running() && zap_test_flag(isdn_data, ZAP_ISDN_TONES_RUNNING) && !zap_test_flag(isdn_data, ZAP_ISDN_STOP)) { + zap_wait_flag_t flags; + zap_status_t status; + int last_chan_state = 0; + int gated = 0; + L2ULONG now = zap_time_now(); + + /* + * check b-channel states and generate & send tones if neccessary + */ + for (x = 1; x <= span->chan_count; x++) { + zap_channel_t *zchan = span->channels[x]; + zap_size_t len = sizeof(frame), rlen; + + if (zchan->type == ZAP_CHAN_TYPE_DQ921) { + continue; + } + + /* + * Generate tones based on current bchan state + * (Recycle buffer content if succeeding channels share the + * same state, this saves some cpu cycles) + */ + switch (zchan->state) { + case ZAP_CHANNEL_STATE_DIALTONE: + { + zap_isdn_bchan_data_t *data = (zap_isdn_bchan_data_t *)zchan->mod_data; + + /* check overlap dial timeout first before generating tone */ + if (data && data->digit_timeout && data->digit_timeout <= now) { + if (strlen(zchan->caller_data.dnis.digits) > 0) { + zap_log(ZAP_LOG_DEBUG, "Overlap dial timeout, advancing to RING state\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RING); + } else { + /* no digits received, hangup */ + zap_log(ZAP_LOG_DEBUG, "Overlap dial timeout, no digits received, going to HANGUP state\n"); + zchan->caller_data.hangup_cause = ZAP_CAUSE_RECOVERY_ON_TIMER_EXPIRE; /* TODO: probably wrong cause value */ + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + data->digit_timeout = 0; + continue; + } + + if (last_chan_state != zchan->state) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_DIAL]); + last_chan_state = zchan->state; + } + } + break; + + case ZAP_CHANNEL_STATE_RING: + { + if (last_chan_state != zchan->state) { + zap_buffer_zero(dt_buffer); + teletone_run(&ts, zchan->span->tone_map[ZAP_TONEMAP_RING]); + last_chan_state = zchan->state; + } + } + break; + + default: /* Not in a tone generating state, go to next round */ + continue; + } + + if (!zap_test_flag(zchan, ZAP_CHANNEL_OPEN)) { + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + continue; + } + zap_log(ZAP_LOG_NOTICE, "Successfully opened channel %d:%d\n", zchan->span_id, zchan->chan_id); + } + + flags = ZAP_READ; + + status = zap_channel_wait(zchan, &flags, (gated) ? 0 : interval); + switch(status) { + case ZAP_FAIL: + continue; + + case ZAP_TIMEOUT: + gated = 1; + continue; + + default: + if (!(flags & ZAP_READ)) { + continue; + } + } + gated = 1; + + status = zap_channel_read(zchan, frame, &len); + if (status != ZAP_SUCCESS || len <= 0) { + continue; + } + + if (zchan->effective_codec != ZAP_CODEC_SLIN) { + len *= 2; + } + + /* seek to current offset */ + zap_buffer_seek(dt_buffer, offset); + + rlen = zap_buffer_read_loop(dt_buffer, frame, len); + + if (zchan->effective_codec != ZAP_CODEC_SLIN) { + zio_codec_t codec_func = NULL; + + if (zchan->native_codec == ZAP_CODEC_ULAW) { + codec_func = zio_slin2ulaw; + } else if (zchan->native_codec == ZAP_CODEC_ALAW) { + codec_func = zio_slin2alaw; + } + + if (codec_func) { + status = codec_func(frame, sizeof(frame), &rlen); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "codec error!"); + goto done; + } + } + zap_channel_write(zchan, frame, sizeof(frame), &rlen); + } + + /* + * sleep a bit if there was nothing to do + */ + if (!gated) { + zap_sleep(interval); + } + + offset += (ts.rate / (1000 / interval)) << 1; + if (offset >= ts.rate) { + offset = 0; + } + } + +done: + if (ts.buffer) { + teletone_destroy_session(&ts); + } + + if (dt_buffer) { + zap_buffer_destroy(&dt_buffer); + } + + zap_log(ZAP_LOG_DEBUG, "ISDN tone thread ended.\n"); + zap_clear_flag(isdn_data, ZAP_ISDN_TONES_RUNNING); + + return NULL; +} + +/** + * \brief Main thread function for an ISDN span + * \param me Current thread + * \param obj Span to monitor + */ +static void *zap_isdn_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_isdn_data_t *isdn_data = span->signal_data; + unsigned char frame[1024]; + zap_size_t len = sizeof(frame); + int errs = 0; + +#ifdef WIN32 + timeBeginPeriod(1); +#endif + + zap_log(ZAP_LOG_DEBUG, "ISDN thread starting.\n"); + zap_set_flag(isdn_data, ZAP_ISDN_RUNNING); + + Q921Start(&isdn_data->q921); + + while(zap_running() && zap_test_flag(isdn_data, ZAP_ISDN_RUNNING) && !zap_test_flag(isdn_data, ZAP_ISDN_STOP)) { + zap_wait_flag_t flags = ZAP_READ; + zap_status_t status = zap_channel_wait(isdn_data->dchan, &flags, 100); + + Q921TimerTick(&isdn_data->q921); + Q931TimerTick(&isdn_data->q931); + check_state(span); + check_events(span); + + /* + * + */ + switch(status) { + case ZAP_FAIL: + { + zap_log(ZAP_LOG_ERROR, "D-Chan Read Error!\n"); + snprintf(span->last_error, sizeof(span->last_error), "D-Chan Read Error!"); + if (++errs == 10) { + isdn_data->dchan->state = ZAP_CHANNEL_STATE_UP; + goto done; + } + } + break; + case ZAP_TIMEOUT: + { + errs = 0; + } + break; + default: + { + errs = 0; + if (flags & ZAP_READ) { + + if (zap_test_flag(isdn_data->dchan, ZAP_CHANNEL_SUSPENDED)) { + zap_clear_flag_all(span, ZAP_CHANNEL_SUSPENDED); + } + len = sizeof(frame); + if (zap_channel_read(isdn_data->dchan, frame, &len) == ZAP_SUCCESS) { +#ifdef IODEBUG + char bb[4096] = ""; + print_hex_bytes(frame, len, bb, sizeof(bb)); + + print_bits(frame, (int)len, bb, sizeof(bb), ZAP_ENDIAN_LITTLE, 0); + zap_log(ZAP_LOG_DEBUG, "READ %d\n%s\n%s\n\n", (int)len, LINE, bb); +#endif + + Q921QueueHDLCFrame(&isdn_data->q921, frame, (int)len); + Q921Rx12(&isdn_data->q921); + } + } else { + zap_log(ZAP_LOG_DEBUG, "No Read FLAG!\n"); + } + } + break; + } + } + + done: + zap_channel_close(&isdn_data->dchans[0]); + zap_channel_close(&isdn_data->dchans[1]); + zap_clear_flag(isdn_data, ZAP_ISDN_RUNNING); + +#ifdef WIN32 + timeEndPeriod(1); +#endif + + zap_log(ZAP_LOG_DEBUG, "ISDN thread ended.\n"); + return NULL; +} + +/** + * \brief Openzap ISDN signaling module initialisation + * \return Success + */ +static ZIO_SIG_LOAD_FUNCTION(zap_isdn_init) +{ + Q931Initialize(); + + Q921SetGetTimeCB(zap_time_now); + Q931SetGetTimeCB(zap_time_now); + + return ZAP_SUCCESS; +} + +/** + * \brief Receives a Q931 indication message + * \param pvt Span were message is coming from + * \param ind Q931 indication + * \param tei Terminal Endpoint Identifier + * \param msg Message string + * \param mlen Message string length + * \return 0 on success + */ +static int q931_rx_32(void *pvt, Q921DLMsg_t ind, L3UCHAR tei, L3UCHAR *msg, L3INT mlen) +{ + int offset = 4; + char bb[4096] = ""; + + switch(ind) { + case Q921_DL_UNIT_DATA: + offset = 3; + + case Q921_DL_DATA: + print_hex_bytes(msg + offset, mlen - offset, bb, sizeof(bb)); +#ifdef HAVE_LIBPCAP + /*Q931ToPcap*/ + if(do_q931ToPcap==1){ + zap_span_t *span = (zap_span_t *) pvt; + if(writeQ931PacketToPcap(msg + offset, mlen - offset, span->span_id, 0) != ZAP_SUCCESS){ + zap_log(ZAP_LOG_WARNING, "Couldn't write Q931 buffer to pcap file!\n"); + } + } + /*Q931ToPcap done*/ +#endif + zap_log(ZAP_LOG_DEBUG, "WRITE %d\n%s\n%s\n\n", (int)mlen - offset, LINE, bb); + break; + + default: + break; + } + + return Q921Rx32(pvt, ind, tei, msg, mlen); +} + +/** + * \brief Logs Q921 message + * \param pvt Span were message is coming from + * \param level Q921 log level + * \param msg Message string + * \param size Message string length + * \return 0 + */ +static int zap_isdn_q921_log(void *pvt, Q921LogLevel_t level, char *msg, L2INT size) +{ + zap_span_t *span = (zap_span_t *) pvt; + + zap_log("Span", "Q.921", span->span_id, (int)level, "%s", msg); + return 0; +} + +/** + * \brief Logs Q931 message + * \param pvt Span were message is coming from + * \param level Q931 log level + * \param msg Message string + * \param size Message string length + * \return 0 + */ +static L3INT zap_isdn_q931_log(void *pvt, Q931LogLevel_t level, char *msg, L3INT size) +{ + zap_span_t *span = (zap_span_t *) pvt; + + zap_log("Span", "Q.931", span->span_id, (int)level, "%s", msg); + return 0; +} +/** + * \brief ISDN state map + */ +static zap_state_map_t isdn_state_map = { + { + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_ANY_STATE}, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + {ZAP_CHANNEL_STATE_DIALING, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DIALING, ZAP_END}, + {ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_UP, ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_PROGRESS, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_UP, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_UP, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END} + }, + + /****************************************/ + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_ANY_STATE}, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + {ZAP_CHANNEL_STATE_DIALTONE, ZAP_CHANNEL_STATE_RING, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DIALTONE, ZAP_END}, + {ZAP_CHANNEL_STATE_RING, ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_UP, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, + ZAP_CHANNEL_STATE_CANCEL, ZAP_CHANNEL_STATE_UP, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_UP, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + }, + + + } +}; + +/** + * \brief Stops an ISDN span + * \param span Span to halt + * \return Success + * + * Sets a stop flag and waits for the threads to end + */ +static zap_status_t zap_isdn_stop(zap_span_t *span) +{ + zap_isdn_data_t *isdn_data = span->signal_data; + + if (!zap_test_flag(isdn_data, ZAP_ISDN_RUNNING)) { + return ZAP_FAIL; + } + + zap_set_flag(isdn_data, ZAP_ISDN_STOP); + + while(zap_test_flag(isdn_data, ZAP_ISDN_RUNNING)) { + zap_sleep(100); + } + + while(zap_test_flag(isdn_data, ZAP_ISDN_TONES_RUNNING)) { + zap_sleep(100); + } + + return ZAP_SUCCESS; + +} + +/** + * \brief Starts an ISDN span + * \param span Span to halt + * \return Success or failure + * + * Launches a thread to monitor the span and a thread to generate tones on the span + */ +static zap_status_t zap_isdn_start(zap_span_t *span) +{ + zap_status_t ret; + zap_isdn_data_t *isdn_data = span->signal_data; + + if (zap_test_flag(isdn_data, ZAP_ISDN_RUNNING)) { + return ZAP_FAIL; + } + + zap_clear_flag(isdn_data, ZAP_ISDN_STOP); + ret = zap_thread_create_detached(zap_isdn_run, span); + + if (ret != ZAP_SUCCESS) { + return ret; + } + + if (ZAP_SPAN_IS_NT(span) && !(isdn_data->opts & ZAP_ISDN_OPT_DISABLE_TONES)) { + ret = zap_thread_create_detached(zap_isdn_tones_run, span); + } + return ret; +} + +/** + * \brief Parses an option string to flags + * \param in String to parse for configuration options + * \return Flags + */ +static uint32_t parse_opts(const char *in) +{ + uint32_t flags = 0; + + if (!in) { + return 0; + } + + if (strstr(in, "suggest_channel")) { + flags |= ZAP_ISDN_OPT_SUGGEST_CHANNEL; + } + + if (strstr(in, "omit_display")) { + flags |= ZAP_ISDN_OPT_OMIT_DISPLAY_IE; + } + + if (strstr(in, "disable_tones")) { + flags |= ZAP_ISDN_OPT_DISABLE_TONES; + } + + return flags; +} + +/** + * \brief Initialises an ISDN span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static ZIO_SIG_CONFIGURE_FUNCTION(zap_isdn_configure_span) +{ + uint32_t i, x = 0; + zap_channel_t *dchans[2] = {0}; + zap_isdn_data_t *isdn_data; + const char *tonemap = "us"; + char *var, *val; + Q931Dialect_t dialect = Q931_Dialect_National; + int32_t digit_timeout = 0; + int q921loglevel = -1; + int q931loglevel = -1; +#ifdef HAVE_LIBPCAP + int q931topcap = -1; /*Q931ToPcap*/ + int openPcap = 0; /*Flag: open Pcap file please*/ +#endif + + if (span->signal_type) { +#ifdef HAVE_LIBPCAP + /*Q931ToPcap: Get the content of the q931topcap and pcapfilename args given by mod_openzap */ + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "q931topcap")) { + q931topcap = va_arg(ap, int); + if(q931topcap==1) { + /*PCAP on*/; + openPcap=1; + } else if (q931topcap==0) { + /*PCAP off*/ + if (closePcapFile() != ZAP_SUCCESS) return ZAP_FAIL; + do_q931ToPcap=0; + return ZAP_SUCCESS; + } + } + if (!strcasecmp(var, "pcapfilename")) { + /*Put filename into global var*/ + pcapfn = va_arg(ap, char*); + } + } + /*We know now, that user wants to enable Q931ToPcap and what file name he wants, so open it please*/ + if(openPcap==1){ + if(openPcapFile() != ZAP_SUCCESS) return ZAP_FAIL; + do_q931ToPcap=1; + return ZAP_SUCCESS; + } + /*Q931ToPcap done*/ +#endif + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling [%d].", span->signal_type); + return ZAP_FAIL; + } + + if (span->trunk_type >= ZAP_TRUNK_NONE) { + zap_log(ZAP_LOG_WARNING, "Invalid trunk type '%s' defaulting to T1.\n", zap_trunk_type2str(span->trunk_type)); + span->trunk_type = ZAP_TRUNK_T1; + } + + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->type == ZAP_CHAN_TYPE_DQ921) { + if (x > 1) { + snprintf(span->last_error, sizeof(span->last_error), "Span has more than 2 D-Channels!"); + return ZAP_FAIL; + } else { + if (zap_channel_open(span->span_id, i, &dchans[x]) == ZAP_SUCCESS) { + zap_log(ZAP_LOG_DEBUG, "opening d-channel #%d %d:%d\n", x, dchans[x]->span_id, dchans[x]->chan_id); + dchans[x]->state = ZAP_CHANNEL_STATE_UP; + x++; + } + } + } + } + + if (!x) { + snprintf(span->last_error, sizeof(span->last_error), "Span has no D-Channels!"); + return ZAP_FAIL; + } + + isdn_data = malloc(sizeof(*isdn_data)); + assert(isdn_data != NULL); + memset(isdn_data, 0, sizeof(*isdn_data)); + + isdn_data->mode = Q931_TE; + dialect = Q931_Dialect_National; + + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "mode")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->mode = strcasecmp(val, "net") ? Q931_TE : Q931_NT; + } else if (!strcasecmp(var, "dialect")) { + if (!(val = va_arg(ap, char *))) { + break; + } + dialect = q931_str2Q931Dialect_type(val); + if (dialect == Q931_Dialect_Count) { + return ZAP_FAIL; + } + } else if (!strcasecmp(var, "opts")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->opts = parse_opts(val); + } else if (!strcasecmp(var, "tonemap")) { + if (!(val = va_arg(ap, char *))) { + break; + } + tonemap = (const char *)val; + } else if (!strcasecmp(var, "digit_timeout")) { + int *optp; + if (!(optp = va_arg(ap, int *))) { + break; + } + digit_timeout = *optp; + } else if (!strcasecmp(var, "q921loglevel")) { + q921loglevel = va_arg(ap, int); + if (q921loglevel < Q921_LOG_NONE) { + q921loglevel = Q921_LOG_NONE; + } else if (q921loglevel > Q921_LOG_DEBUG) { + q921loglevel = Q921_LOG_DEBUG; + } + } else if (!strcasecmp(var, "q931loglevel")) { + q931loglevel = va_arg(ap, int); + if (q931loglevel < Q931_LOG_NONE) { + q931loglevel = Q931_LOG_NONE; + } else if (q931loglevel > Q931_LOG_DEBUG) { + q931loglevel = Q931_LOG_DEBUG; + } + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return ZAP_FAIL; + } + } + + + if (!digit_timeout) { + digit_timeout = DEFAULT_DIGIT_TIMEOUT; + } + else if (digit_timeout < 3000 || digit_timeout > 30000) { + zap_log(ZAP_LOG_WARNING, "Digit timeout %d ms outside of range (3000 - 30000 ms), using default (10000 ms)\n", digit_timeout); + digit_timeout = DEFAULT_DIGIT_TIMEOUT; + } + + /* allocate per b-chan data */ + if (isdn_data->mode == Q931_NT) { + zap_isdn_bchan_data_t *data; + + data = malloc((span->chan_count - 1) * sizeof(zap_isdn_bchan_data_t)); + if (!data) { + return ZAP_FAIL; + } + + for (i = 1; i <= span->chan_count; i++, data++) { + if (span->channels[i]->type == ZAP_CHAN_TYPE_B) { + span->channels[i]->mod_data = data; + memset(data, 0, sizeof(zap_isdn_bchan_data_t)); + } + } + } + + span->start = zap_isdn_start; + span->stop = zap_isdn_stop; + span->signal_cb = sig_cb; + isdn_data->dchans[0] = dchans[0]; + isdn_data->dchans[1] = dchans[1]; + isdn_data->dchan = isdn_data->dchans[0]; + isdn_data->digit_timeout = digit_timeout; + + Q921_InitTrunk(&isdn_data->q921, + 0, + 0, + isdn_data->mode, + span->trunk_type == ZAP_TRUNK_BRI_PTMP ? Q921_PTMP : Q921_PTP, + 0, + zap_isdn_921_21, + (Q921Tx23CB_t)zap_isdn_921_23, + span, + &isdn_data->q931); + + Q921SetLogCB(&isdn_data->q921, &zap_isdn_q921_log, isdn_data); + Q921SetLogLevel(&isdn_data->q921, (Q921LogLevel_t)q921loglevel); + + Q931Api_InitTrunk(&isdn_data->q931, + dialect, + isdn_data->mode, + span->trunk_type, + zap_isdn_931_34, + (Q931Tx32CB_t)q931_rx_32, + zap_isdn_931_err, + &isdn_data->q921, + span); + + Q931SetLogCB(&isdn_data->q931, &zap_isdn_q931_log, isdn_data); + Q931SetLogLevel(&isdn_data->q931, (Q931LogLevel_t)q931loglevel); + + isdn_data->q931.autoRestartAck = 1; + isdn_data->q931.autoConnectAck = 0; + isdn_data->q931.autoServiceAck = 1; + span->signal_data = isdn_data; + span->signal_type = ZAP_SIGTYPE_ISDN; + span->outgoing_call = isdn_outgoing_call; + + if ((isdn_data->opts & ZAP_ISDN_OPT_SUGGEST_CHANNEL)) { + span->channel_request = isdn_channel_request; + span->suggest_chan_id = 1; + } + span->state_map = &isdn_state_map; + + zap_span_load_tones(span, tonemap); + + return ZAP_SUCCESS; +} + +/** + * \brief Openzap ISDN signaling module definition + */ +EX_DECLARE_DATA zap_module_t zap_module = { + "isdn", + NULL, + close_pcap, + zap_isdn_init, + zap_isdn_configure_span, + NULL +}; + + + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_isdn/zap_isdn.h b/libs/openzap/src/ozmod/ozmod_isdn/zap_isdn.h new file mode 100644 index 0000000000..c5e0e5a711 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_isdn/zap_isdn.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 ZAP_ISDN_H +#define ZAP_ISDN_H +#include "openzap.h" + +#define DEFAULT_DIGIT_TIMEOUT 10000 /* default overlap timeout: 10 seconds */ + + +typedef enum { + ZAP_ISDN_OPT_NONE = 0, + ZAP_ISDN_OPT_SUGGEST_CHANNEL = (1 << 0), + ZAP_ISDN_OPT_OMIT_DISPLAY_IE = (1 << 1), /*!< Do not send Caller name in outgoing SETUP message (= Display IE) */ + ZAP_ISDN_OPT_DISABLE_TONES = (1 << 2), /*!< Disable tone generating thread (NT mode) */ + + ZAP_ISDN_OPT_MAX = (2 << 0) +} zap_isdn_opts_t; + +typedef enum { + ZAP_ISDN_RUNNING = (1 << 0), + ZAP_ISDN_TONES_RUNNING = (1 << 1), + ZAP_ISDN_STOP = (1 << 2) +} zap_isdn_flag_t; + + +struct zap_isdn_data { + Q921Data_t q921; + Q931_TrunkInfo_t q931; + zap_channel_t *dchan; + zap_channel_t *dchans[2]; + struct zap_sigmsg sigmsg; + uint32_t flags; + int32_t mode; + int32_t digit_timeout; + zap_isdn_opts_t opts; + zap_caller_data_t *outbound_crv[32768]; + zap_channel_t *channels_local_crv[32768]; + zap_channel_t *channels_remote_crv[32768]; +}; + +typedef struct zap_isdn_data zap_isdn_data_t; + + +/* b-channel private data */ +struct zap_isdn_bchan_data +{ + L2ULONG digit_timeout; +}; + +typedef struct zap_isdn_bchan_data zap_isdn_bchan_data_t; + + +#endif + +/* 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: + */ + diff --git a/libs/openzap/src/ozmod/ozmod_libpri/lpwrap_pri.c b/libs/openzap/src/ozmod/ozmod_libpri/lpwrap_pri.c new file mode 100644 index 0000000000..3df1d54d89 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_libpri/lpwrap_pri.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2009, Anthony Minessale II + * 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. + */ + +//#define IODEBUG + +#include "openzap.h" +#include "lpwrap_pri.h" +#ifndef HAVE_GETTIMEOFDAY + +#ifdef WIN32 +#include + +static __inline int gettimeofday(struct timeval *tp, void *nothing) +{ +#ifdef WITHOUT_MM_LIB + SYSTEMTIME st; + time_t tt; + struct tm tmtm; + /* mktime converts local to UTC */ + GetLocalTime (&st); + tmtm.tm_sec = st.wSecond; + tmtm.tm_min = st.wMinute; + tmtm.tm_hour = st.wHour; + tmtm.tm_mday = st.wDay; + tmtm.tm_mon = st.wMonth - 1; + tmtm.tm_year = st.wYear - 1900; tmtm.tm_isdst = -1; + tt = mktime (&tmtm); + tp->tv_sec = tt; + tp->tv_usec = st.wMilliseconds * 1000; +#else + /** + ** The earlier time calculations using GetLocalTime + ** had a time resolution of 10ms.The timeGetTime, part + ** of multimedia apis offer a better time resolution + ** of 1ms.Need to link against winmm.lib for this + **/ + unsigned long Ticks = 0; + unsigned long Sec =0; + unsigned long Usec = 0; + Ticks = timeGetTime(); + + Sec = Ticks/1000; + Usec = (Ticks - (Sec*1000))*1000; + tp->tv_sec = Sec; + tp->tv_usec = Usec; +#endif /* WITHOUT_MM_LIB */ + (void)nothing; + return 0; +} +#endif /* WIN32 */ +#endif /* HAVE_GETTIMEOFDAY */ + +static struct lpwrap_pri_event_list LPWRAP_PRI_EVENT_LIST[] = { + {0, LPWRAP_PRI_EVENT_ANY, "ANY"}, + {1, LPWRAP_PRI_EVENT_DCHAN_UP, "DCHAN_UP"}, + {2, LPWRAP_PRI_EVENT_DCHAN_DOWN, "DCHAN_DOWN"}, + {3, LPWRAP_PRI_EVENT_RESTART, "RESTART"}, + {4, LPWRAP_PRI_EVENT_CONFIG_ERR, "CONFIG_ERR"}, + {5, LPWRAP_PRI_EVENT_RING, "RING"}, + {6, LPWRAP_PRI_EVENT_HANGUP, "HANGUP"}, + {7, LPWRAP_PRI_EVENT_RINGING, "RINGING"}, + {8, LPWRAP_PRI_EVENT_ANSWER, "ANSWER"}, + {9, LPWRAP_PRI_EVENT_HANGUP_ACK, "HANGUP_ACK"}, + {10, LPWRAP_PRI_EVENT_RESTART_ACK, "RESTART_ACK"}, + {11, LPWRAP_PRI_EVENT_FACNAME, "FACNAME"}, + {12, LPWRAP_PRI_EVENT_INFO_RECEIVED, "INFO_RECEIVED"}, + {13, LPWRAP_PRI_EVENT_PROCEEDING, "PROCEEDING"}, + {14, LPWRAP_PRI_EVENT_SETUP_ACK, "SETUP_ACK"}, + {15, LPWRAP_PRI_EVENT_HANGUP_REQ, "HANGUP_REQ"}, + {16, LPWRAP_PRI_EVENT_NOTIFY, "NOTIFY"}, + {17, LPWRAP_PRI_EVENT_PROGRESS, "PROGRESS"}, + {18, LPWRAP_PRI_EVENT_KEYPAD_DIGIT, "KEYPAD_DIGIT"}, + {19, LPWRAP_PRI_EVENT_IO_FAIL, "IO_FAIL"}, +}; + +#define LINE "--------------------------------------------------------------------------------" + +const char *lpwrap_pri_event_str(lpwrap_pri_event_t event_id) +{ + return LPWRAP_PRI_EVENT_LIST[event_id].name; +} + +static int __pri_lpwrap_read(struct pri *pri, void *buf, int buflen) +{ + struct lpwrap_pri *spri = (struct lpwrap_pri *) pri_get_userdata(pri); + zap_size_t len = buflen; + int res; + zap_status_t zst; + + if ((zst = zap_channel_read(spri->dchan, buf, &len)) != ZAP_SUCCESS) { + if (zst == ZAP_FAIL) { + zap_log(ZAP_LOG_CRIT, "span %d D-READ FAIL! [%s]\n", spri->span->span_id, spri->dchan->last_error); + spri->errs++; + } else { + zap_log(ZAP_LOG_CRIT, "span %d D-READ TIMEOUT\n", spri->span->span_id); + } + + zap_clear_flag(spri, LPWRAP_PRI_READY); + return -1; + } + spri->errs = 0; + res = (int)len; + memset(&((unsigned char*)buf)[res],0,2); + res+=2; + +#ifdef IODEBUG + { + char bb[2048] = { 0 }; + + print_hex_bytes(buf, res - 2, bb, sizeof(bb)); + zap_log(ZAP_LOG_DEBUG, "READ %d\n", res-2); + } +#endif + + return res; +} + +static int __pri_lpwrap_write(struct pri *pri, void *buf, int buflen) +{ + struct lpwrap_pri *spri = (struct lpwrap_pri *) pri_get_userdata(pri); + zap_size_t len = buflen -2; + + if (zap_channel_write(spri->dchan, buf, buflen, &len) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_CRIT, "span %d D-WRITE FAIL! [%s]\n", spri->span->span_id, spri->dchan->last_error); + zap_clear_flag(spri, LPWRAP_PRI_READY); + return -1; + } + +#ifdef IODEBUG + { + char bb[2048] = { 0 }; + + print_hex_bytes(buf, buflen - 2, bb, sizeof(bb)); + zap_log(ZAP_LOG_DEBUG, "WRITE %d\n", (int)buflen-2); + } +#endif + + return (int) buflen; +} + +int lpwrap_init_pri(struct lpwrap_pri *spri, zap_span_t *span, zap_channel_t *dchan, int swtype, int node, int debug) +{ + int ret = -1; + + memset(spri, 0, sizeof(struct lpwrap_pri)); + + spri->dchan = dchan; + spri->span = span; + + if ((spri->pri = pri_new_cb(spri->dchan->sockfd, node, swtype, __pri_lpwrap_read, __pri_lpwrap_write, spri))){ + unsigned char buf[4] = { 0 }; + size_t buflen = sizeof(buf), len = 0; + pri_set_debug(spri->pri, debug); + ret = 0; + zap_set_flag(spri, LPWRAP_PRI_READY); + zap_channel_write(spri->dchan, buf, buflen, &len); + } else { + fprintf(stderr, "Unable to create PRI\n"); + } + + return ret; +} + + +int lpwrap_one_loop(struct lpwrap_pri *spri) +{ + fd_set rfds, efds; + struct timeval now = {0,0}, *next; + pri_event *event; + event_handler handler; + int sel; + + if (spri->on_loop) { + if ((sel = spri->on_loop(spri)) < 0) { + return sel; + } + } + + if (spri->errs >= 2) { + spri->errs = 0; + return -1; + } + + FD_ZERO(&rfds); + FD_ZERO(&efds); + +#ifdef _MSC_VER + //Windows macro for FD_SET includes a warning C4127: conditional expression is constant +#pragma warning(push) +#pragma warning(disable:4127) +#endif + + FD_SET(pri_fd(spri->pri), &rfds); + FD_SET(pri_fd(spri->pri), &efds); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + now.tv_sec = 0; + now.tv_usec = 100000; + + sel = select(pri_fd(spri->pri) + 1, &rfds, NULL, &efds, &now); + + event = NULL; + + if (!sel) { + if ((next = pri_schedule_next(spri->pri))) { + gettimeofday(&now, NULL); + if (now.tv_sec >= next->tv_sec && (now.tv_usec >= next->tv_usec || next->tv_usec <= 100000)) { + //zap_log(ZAP_LOG_DEBUG, "Check event\n"); + event = pri_schedule_run(spri->pri); + } + } + } else if (sel > 0) { + event = pri_check_event(spri->pri); + } + + if (event) { + /* 0 is catchall event handler */ + if ((handler = spri->eventmap[event->e] ? spri->eventmap[event->e] : spri->eventmap[0] ? spri->eventmap[0] : NULL)) { + handler(spri, event->e, event); + } else { + zap_log(ZAP_LOG_CRIT, "No event handler found for event %d.\n", event->e); + } + } + + + return sel; + + + if ((handler = spri->eventmap[LPWRAP_PRI_EVENT_IO_FAIL] ? spri->eventmap[LPWRAP_PRI_EVENT_IO_FAIL] : spri->eventmap[0] ? spri->eventmap[0] : NULL)) { + handler(spri, LPWRAP_PRI_EVENT_IO_FAIL, NULL); + } + + return -1; +} + +int lpwrap_run_pri(struct lpwrap_pri *spri) +{ + int ret = 0; + + for (;;){ + ret = lpwrap_one_loop(spri); + + if (ret < 0) { + +#ifndef WIN32 //This needs to be adressed fror WIN32 still + if (errno == EINTR){ + /* Igonore an interrupted system call */ + continue; + } +#endif + zap_log(ZAP_LOG_CRIT, "Error = %i [%s]\n", ret, strerror(errno)); + break; + } + } + + return ret; + +} + +/* 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: + */ + diff --git a/libs/openzap/src/ozmod/ozmod_libpri/lpwrap_pri.h b/libs/openzap/src/ozmod/ozmod_libpri/lpwrap_pri.h new file mode 100644 index 0000000000..4c79526b9e --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_libpri/lpwrap_pri.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2009, Anthony Minessale II + * 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 _LPWRAP_PRI_H +#define _LPWRAP_PRI_H +#include +#include + + +#define LPWRAP_MAX_CHAN_PER_SPAN 32 + +typedef enum { + LPWRAP_PRI_EVENT_ANY = 0, + LPWRAP_PRI_EVENT_DCHAN_UP = PRI_EVENT_DCHAN_UP, + LPWRAP_PRI_EVENT_DCHAN_DOWN = PRI_EVENT_DCHAN_DOWN, + LPWRAP_PRI_EVENT_RESTART = PRI_EVENT_RESTART, + LPWRAP_PRI_EVENT_CONFIG_ERR = PRI_EVENT_CONFIG_ERR, + LPWRAP_PRI_EVENT_RING = PRI_EVENT_RING, + LPWRAP_PRI_EVENT_HANGUP = PRI_EVENT_HANGUP, + LPWRAP_PRI_EVENT_RINGING = PRI_EVENT_RINGING, + LPWRAP_PRI_EVENT_ANSWER = PRI_EVENT_ANSWER, + LPWRAP_PRI_EVENT_HANGUP_ACK = PRI_EVENT_HANGUP_ACK, + LPWRAP_PRI_EVENT_RESTART_ACK = PRI_EVENT_RESTART_ACK, + LPWRAP_PRI_EVENT_FACNAME = PRI_EVENT_FACNAME, + LPWRAP_PRI_EVENT_INFO_RECEIVED = PRI_EVENT_INFO_RECEIVED, + LPWRAP_PRI_EVENT_PROCEEDING = PRI_EVENT_PROCEEDING, + LPWRAP_PRI_EVENT_SETUP_ACK = PRI_EVENT_SETUP_ACK, + LPWRAP_PRI_EVENT_HANGUP_REQ = PRI_EVENT_HANGUP_REQ, + LPWRAP_PRI_EVENT_NOTIFY = PRI_EVENT_NOTIFY, + LPWRAP_PRI_EVENT_PROGRESS = PRI_EVENT_PROGRESS, + LPWRAP_PRI_EVENT_KEYPAD_DIGIT = PRI_EVENT_KEYPAD_DIGIT, + LPWRAP_PRI_EVENT_IO_FAIL = 19, + + /* don't touch */ + LPWRAP_PRI_EVENT_MAX +} lpwrap_pri_event_t; + +typedef enum { + LPWRAP_PRI_NETWORK = PRI_NETWORK, + LPWRAP_PRI_CPE = PRI_CPE +} lpwrap_pri_node_t; + +typedef enum { + LPWRAP_PRI_SWITCH_UNKNOWN = PRI_SWITCH_UNKNOWN, + LPWRAP_PRI_SWITCH_NI2 = PRI_SWITCH_NI2, + LPWRAP_PRI_SWITCH_DMS100 = PRI_SWITCH_DMS100, + LPWRAP_PRI_SWITCH_LUCENT5E = PRI_SWITCH_LUCENT5E, + LPWRAP_PRI_SWITCH_ATT4ESS = PRI_SWITCH_ATT4ESS, + LPWRAP_PRI_SWITCH_EUROISDN_E1 = PRI_SWITCH_EUROISDN_E1, + LPWRAP_PRI_SWITCH_EUROISDN_T1 = PRI_SWITCH_EUROISDN_T1, + LPWRAP_PRI_SWITCH_NI1 = PRI_SWITCH_NI1, + LPWRAP_PRI_SWITCH_GR303_EOC = PRI_SWITCH_GR303_EOC, + LPWRAP_PRI_SWITCH_GR303_TMC = PRI_SWITCH_GR303_TMC, + LPWRAP_PRI_SWITCH_QSIG = PRI_SWITCH_QSIG, + + /* don't touch */ + LPWRAP_PRI_SWITCH_MAX +} lpwrap_pri_switch_t; + +typedef enum { + LPWRAP_PRI_READY = (1 << 0) +} lpwrap_pri_flag_t; + +struct lpwrap_pri; +typedef int (*event_handler)(struct lpwrap_pri *, lpwrap_pri_event_t, pri_event *); +typedef int (*loop_handler)(struct lpwrap_pri *); + +struct lpwrap_pri { + struct pri *pri; + zap_span_t *span; + zap_channel_t *dchan; + unsigned int flags; + void *private_info; + event_handler eventmap[LPWRAP_PRI_EVENT_MAX]; + loop_handler on_loop; + int errs; +}; + +typedef struct lpwrap_pri lpwrap_pri_t; + +struct lpwrap_pri_event_list { + int event_id; + int pri_event; + const char *name; +}; + + + +#define LPWRAP_MAP_PRI_EVENT(spri, event, func) spri.eventmap[event] = func; + +const char *lpwrap_pri_event_str(lpwrap_pri_event_t event_id); +int lpwrap_one_loop(struct lpwrap_pri *spri); +int lpwrap_init_pri(struct lpwrap_pri *spri, zap_span_t *span, zap_channel_t *dchan, int swtype, int node, int debug); +int lpwrap_run_pri(struct lpwrap_pri *spri); + +#endif diff --git a/libs/openzap/src/ozmod/ozmod_libpri/ozmod_libpri.c b/libs/openzap/src/ozmod/ozmod_libpri/ozmod_libpri.c new file mode 100644 index 0000000000..f18aa1fc54 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_libpri/ozmod_libpri.c @@ -0,0 +1,1363 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "openzap.h" +#include "ozmod_libpri.h" + +/** + * \brief Unloads libpri IO module + * \return Success + */ +static ZIO_IO_UNLOAD_FUNCTION(zap_libpri_unload) +{ + return ZAP_SUCCESS; +} + +/** + * \brief Starts a libpri channel (outgoing call) + * \param zchan Channel to initiate call on + * \return Success or failure + */ +static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(isdn_outgoing_call) +{ + zap_status_t status = ZAP_SUCCESS; + zap_set_flag(zchan, ZAP_CHANNEL_OUTBOUND); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DIALING); + return status; +} + +/** + * \brief Requests an libpri channel on a span (outgoing call) + * \param span Span where to get a channel (unused) + * \param chan_id Specific channel to get (0 for any) (unused) + * \param direction Call direction (unused) + * \param caller_data Caller information (unused) + * \param zchan Channel to initialise (unused) + * \return Failure + */ +static ZIO_CHANNEL_REQUEST_FUNCTION(isdn_channel_request) +{ + return ZAP_FAIL; +} + +#ifdef WIN32 +/** + * \brief Logs a libpri error + * \param s Error string + */ +static void s_pri_error(char *s) +#else +/** + * \brief Logs a libpri error + * \param pri libpri structure (unused) + * \param s Error string + */ +static void s_pri_error(struct pri *pri, char *s) +#endif +{ + zap_log(ZAP_LOG_ERROR, "%s", s); +} + +#ifdef WIN32 +/** + * \brief Logs a libpri message + * \param s Message string + */ +static void s_pri_message(char *s) +#else +/** + * \brief Logs a libpri message + * \param pri libpri structure (unused) + * \param s Message string + */ +static void s_pri_message(struct pri *pri, char *s) +#endif +{ + zap_log(ZAP_LOG_DEBUG, "%s", s); +} + +/** + * \brief Parses an option string to flags + * \param in String to parse for configuration options + * \return Flags + */ +static uint32_t parse_opts(const char *in) +{ + uint32_t flags = 0; + + if (!in) { + return 0; + } + + if (strstr(in, "suggest_channel")) { + flags |= OZMOD_LIBPRI_OPT_SUGGEST_CHANNEL; + } + + if (strstr(in, "omit_display")) { + flags |= OZMOD_LIBPRI_OPT_OMIT_DISPLAY_IE; + } + + if (strstr(in, "omit_redirecting_number")) { + flags |= OZMOD_LIBPRI_OPT_OMIT_REDIRECTING_NUMBER_IE; + } + + return flags; +} + +/** + * \brief Parses a debug string to flags + * \param in Debug string to parse for + * \return Flags + */ +static int parse_debug(const char *in) +{ + int flags = 0; + + if (!in) { + return 0; + } + + if (strstr(in, "q921_raw")) { + flags |= PRI_DEBUG_Q921_RAW; + } + + if (strstr(in, "q921_dump")) { + flags |= PRI_DEBUG_Q921_DUMP; + } + + if (strstr(in, "q921_state")) { + flags |= PRI_DEBUG_Q921_STATE; + } + + if (strstr(in, "config")) { + flags |= PRI_DEBUG_CONFIG; + } + + if (strstr(in, "q931_dump")) { + flags |= PRI_DEBUG_Q931_DUMP; + } + + if (strstr(in, "q931_state")) { + flags |= PRI_DEBUG_Q931_STATE; + } + + if (strstr(in, "q931_anomaly")) { + flags |= PRI_DEBUG_Q931_ANOMALY; + } + + if (strstr(in, "apdu")) { + flags |= PRI_DEBUG_APDU; + } + + if (strstr(in, "aoc")) { + flags |= PRI_DEBUG_AOC; + } + + if (strstr(in, "all")) { + flags |= PRI_DEBUG_ALL; + } + + if (strstr(in, "none")) { + flags = 0; + } + + return flags; +} + +static zap_io_interface_t zap_libpri_interface; + +static zap_status_t zap_libpri_start(zap_span_t *span); + +/** + * \brief API function to kill or debug a libpri span + * \param stream API stream handler + * \param data String containing argurments + * \return Flags + */ +static ZIO_API_FUNCTION(zap_libpri_api) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + + if (data) { + mycmd = strdup(data); + argc = zap_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (argc == 2) { + if (!strcasecmp(argv[0], "kill")) { + int span_id = atoi(argv[1]); + zap_span_t *span = NULL; + + if (zap_span_find_by_name(argv[1], &span) == ZAP_SUCCESS || zap_span_find(span_id, &span) == ZAP_SUCCESS) { + zap_libpri_data_t *isdn_data = span->signal_data; + + if (span->start != zap_libpri_start) { + stream->write_function(stream, "%s: -ERR invalid span.\n", __FILE__); + goto done; + } + + zap_clear_flag((&isdn_data->spri), LPWRAP_PRI_READY); + stream->write_function(stream, "%s: +OK killed.\n", __FILE__); + goto done; + } else { + stream->write_function(stream, "%s: -ERR invalid span.\n", __FILE__); + goto done; + } + } + } + + if (argc > 2) { + if (!strcasecmp(argv[0], "debug")) { + zap_span_t *span = NULL; + + if (zap_span_find_by_name(argv[1], &span) == ZAP_SUCCESS) { + zap_libpri_data_t *isdn_data = span->signal_data; + if (span->start != zap_libpri_start) { + stream->write_function(stream, "%s: -ERR invalid span.\n", __FILE__); + goto done; + } + + pri_set_debug(isdn_data->spri.pri, parse_debug(argv[2])); + stream->write_function(stream, "%s: +OK debug set.\n", __FILE__); + goto done; + } else { + stream->write_function(stream, "%s: -ERR invalid span.\n", __FILE__); + goto done; + } + } + + } + + stream->write_function(stream, "%s: -ERR invalid command.\n", __FILE__); + + done: + + zap_safe_free(mycmd); + + return ZAP_SUCCESS; +} + +/** + * \brief Loads libpri IO module + * \param zio Openzap IO interface + * \return Success + */ +static ZIO_IO_LOAD_FUNCTION(zap_libpri_io_init) +{ + assert(zio != NULL); + memset(&zap_libpri_interface, 0, sizeof(zap_libpri_interface)); + + zap_libpri_interface.name = "libpri"; + zap_libpri_interface.api = zap_libpri_api; + + *zio = &zap_libpri_interface; + + return ZAP_SUCCESS; +} + +/** + * \brief Loads libpri signaling module + * \param zio Openzap IO interface + * \return Success + */ +static ZIO_SIG_LOAD_FUNCTION(zap_libpri_init) +{ + pri_set_error(s_pri_error); + pri_set_message(s_pri_message); + return ZAP_SUCCESS; +} + +/** + * \brief libpri state map + */ +static zap_state_map_t isdn_state_map = { + { + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_ANY_STATE}, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + {ZAP_CHANNEL_STATE_DIALING, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DIALING, ZAP_END}, + {ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_UP, ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_PROGRESS, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_UP, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_UP, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END} + }, + + /****************************************/ + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_ANY_STATE}, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + {ZAP_CHANNEL_STATE_DIALTONE, ZAP_CHANNEL_STATE_RING, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DIALTONE, ZAP_END}, + {ZAP_CHANNEL_STATE_RING, ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_UP, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, + ZAP_CHANNEL_STATE_CANCEL, ZAP_CHANNEL_STATE_UP, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_UP, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + }, + + + } +}; + +/** + * \brief Handler for channel state change + * \param zchan Channel to handle + */ +static __inline__ void state_advance(zap_channel_t *zchan) +{ + //Q931mes_Generic *gen = (Q931mes_Generic *) zchan->caller_data.raw_data; + zap_libpri_data_t *isdn_data = zchan->span->signal_data; + zap_status_t status; + zap_sigmsg_t sig; + q931_call *call = (q931_call *) zchan->call_data; + + + zap_log(ZAP_LOG_DEBUG, "%d:%d STATE [%s]\n", + zchan->span_id, zchan->chan_id, zap_channel_state2str(zchan->state)); + + +#if 0 + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND) && !call) { + zap_log(ZAP_LOG_WARNING, "NO CALL!!!!\n"); + } +#endif + + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + + + switch (zchan->state) { + case ZAP_CHANNEL_STATE_DOWN: + { + zchan->call_data = NULL; + zap_channel_done(zchan); + } + break; + case ZAP_CHANNEL_STATE_PROGRESS: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_PROGRESS; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else if (call) { + pri_progress(isdn_data->spri.pri, call, zchan->chan_id, 1); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + } + } + break; + case ZAP_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_PROGRESS_MEDIA; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else if (call) { + pri_proceeding(isdn_data->spri.pri, call, zchan->chan_id, 1); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + } + } + break; + case ZAP_CHANNEL_STATE_RING: + { + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + if (call) { + pri_acknowledge(isdn_data->spri.pri, call, zchan->chan_id, 0); + sig.event_id = ZAP_SIGEVENT_START; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + } + } + } + break; + case ZAP_CHANNEL_STATE_RESTART: + { + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_UNSPECIFIED; + sig.event_id = ZAP_SIGEVENT_RESTART; + status = zap_span_send_signal(zchan->span, &sig); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + break; + case ZAP_CHANNEL_STATE_UP: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_UP; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else if (call) { + pri_answer(isdn_data->spri.pri, call, 0, 1); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + } + } + break; + case ZAP_CHANNEL_STATE_DIALING: + if (isdn_data) { + struct pri_sr *sr; + int dp; + + if (!(call = pri_new_call(isdn_data->spri.pri))) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + return; + } + + + dp = zchan->caller_data.ani.type; + switch(dp) { + case ZAP_TON_NATIONAL: + dp = PRI_NATIONAL_ISDN; + break; + case ZAP_TON_INTERNATIONAL: + dp = PRI_INTERNATIONAL_ISDN; + break; + case ZAP_TON_SUBSCRIBER_NUMBER: + dp = PRI_LOCAL_ISDN; + break; + default: + dp = isdn_data->dp; + } + + zchan->call_data = call; + sr = pri_sr_new(); + assert(sr); + pri_sr_set_channel(sr, zchan->chan_id, 0, 0); + pri_sr_set_bearer(sr, 0, isdn_data->l1); + pri_sr_set_called(sr, zchan->caller_data.ani.digits, dp, 1); + pri_sr_set_caller(sr, zchan->caller_data.cid_num.digits, (isdn_data->opts & OZMOD_LIBPRI_OPT_OMIT_DISPLAY_IE ? NULL : zchan->caller_data.cid_name), + dp, (zchan->caller_data.pres != 1 ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_PROHIB_USER_NUMBER_NOT_SCREENED)); + + if (!(isdn_data->opts & OZMOD_LIBPRI_OPT_OMIT_REDIRECTING_NUMBER_IE)) { + pri_sr_set_redirecting(sr, zchan->caller_data.cid_num.digits, dp, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, PRI_REDIR_UNCONDITIONAL); + } + + if (pri_setup(isdn_data->spri.pri, call, sr)) { + zchan->caller_data.hangup_cause = ZAP_CAUSE_DESTINATION_OUT_OF_ORDER; + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + + pri_sr_free(sr); + } + + break; + case ZAP_CHANNEL_STATE_HANGUP: + { + if (call) { + pri_hangup(isdn_data->spri.pri, call, zchan->caller_data.hangup_cause); + pri_destroycall(isdn_data->spri.pri, call); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + } + } + break; + case ZAP_CHANNEL_STATE_HANGUP_COMPLETE: + break; + case ZAP_CHANNEL_STATE_TERMINATING: + { + sig.event_id = ZAP_SIGEVENT_STOP; + status = zap_span_send_signal(zchan->span, &sig); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + + } + default: + break; + } + + + + return; +} + +/** + * \brief Checks current state on a span + * \param span Span to check status on + */ +static __inline__ void check_state(zap_span_t *span) +{ + if (zap_test_flag(span, ZAP_SPAN_STATE_CHANGE)) { + uint32_t j; + zap_clear_flag_locked(span, ZAP_SPAN_STATE_CHANGE); + for(j = 1; j <= span->chan_count; j++) { + if (zap_test_flag((span->channels[j]), ZAP_CHANNEL_STATE_CHANGE)) { + zap_mutex_lock(span->channels[j]->mutex); + zap_clear_flag((span->channels[j]), ZAP_CHANNEL_STATE_CHANGE); + state_advance(span->channels[j]); + zap_channel_complete_state(span->channels[j]); + zap_mutex_unlock(span->channels[j]->mutex); + } + } + } +} + +/** + * \brief Handler for libpri information event (incoming call?) + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_info(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + zap_log(ZAP_LOG_DEBUG, "number is: %s\n", pevent->ring.callednum); + if (strlen(pevent->ring.callednum) > 3) { + zap_log(ZAP_LOG_DEBUG, "final number is: %s\n", pevent->ring.callednum); + pri_answer(spri->pri, pevent->ring.call, 0, 1); + } + return 0; +} + +/** + * \brief Handler for libpri hangup event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_hangup(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + zap_span_t *span = spri->private_info; + zap_channel_t *zchan = NULL; + q931_call *call = NULL; + zchan = span->channels[pevent->hangup.channel]; + + if (zchan) { + call = (q931_call *) zchan->call_data; + zap_log(ZAP_LOG_DEBUG, "-- Hangup on channel %d:%d\n", spri->span->span_id, pevent->hangup.channel); + zchan->caller_data.hangup_cause = pevent->hangup.cause; + pri_release(spri->pri, call, 0); + pri_destroycall(spri->pri, call); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_TERMINATING); + } else { + zap_log(ZAP_LOG_DEBUG, "-- Hangup on channel %d:%d %s but it's not in use?\n", spri->span->span_id, + pevent->hangup.channel, zchan->chan_id); + } + + return 0; +} + +/** + * \brief Handler for libpri answer event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_answer(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + zap_span_t *span = spri->private_info; + zap_channel_t *zchan = NULL; + + zchan = span->channels[pevent->answer.channel]; + + if (zchan) { + zap_log(ZAP_LOG_DEBUG, "-- Answer on channel %d:%d\n", spri->span->span_id, pevent->answer.channel); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + } else { + zap_log(ZAP_LOG_DEBUG, "-- Answer on channel %d:%d %s but it's not in use?\n", spri->span->span_id, pevent->answer.channel, zchan->chan_id); + + } + + return 0; +} + +/** + * \brief Handler for libpri proceed event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_proceed(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + zap_span_t *span = spri->private_info; + zap_channel_t *zchan = NULL; + + zchan = span->channels[pevent->proceeding.channel]; + + if (zchan) { + zap_log(ZAP_LOG_DEBUG, "-- Proceeding on channel %d:%d\n", spri->span->span_id, pevent->proceeding.channel); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); + } else { + zap_log(ZAP_LOG_DEBUG, "-- Proceeding on channel %d:%d %s but it's not in use?\n", spri->span->span_id, + pevent->proceeding.channel, zchan->chan_id); + } + + return 0; +} + +/** + * \brief Handler for libpri ringing event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_ringing(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + zap_span_t *span = spri->private_info; + zap_channel_t *zchan = NULL; + + zchan = span->channels[pevent->ringing.channel]; + + if (zchan) { + zap_log(ZAP_LOG_DEBUG, "-- Ringing on channel %d:%d\n", spri->span->span_id, pevent->ringing.channel); + /* we may get on_ringing even when we're already in ZAP_CHANNEL_STATE_PROGRESS_MEDIA */ + if (zchan->state == ZAP_CHANNEL_STATE_PROGRESS_MEDIA) { + /* dont try to move to STATE_PROGRESS to avoid annoying veto warning */ + return 0; + } + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS); + } else { + zap_log(ZAP_LOG_DEBUG, "-- Ringing on channel %d:%d %s but it's not in use?\n", spri->span->span_id, + pevent->ringing.channel, zchan->chan_id); + } + + return 0; +} + +/** + * \brief Handler for libpri ring event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 on success + */ +static int on_ring(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + zap_span_t *span = spri->private_info; + zap_channel_t *zchan = NULL; + int ret = 0; + + //switch_mutex_lock(globals.channel_mutex); + + zchan = span->channels[pevent->ring.channel]; + if (!zchan || zchan->state != ZAP_CHANNEL_STATE_DOWN || zap_test_flag(zchan, ZAP_CHANNEL_INUSE)) { + zap_log(ZAP_LOG_WARNING, "--Duplicate Ring on channel %d:%d (ignored)\n", spri->span->span_id, pevent->ring.channel); + ret = 0; + goto done; + } + + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_WARNING, "--Failure opening channel %d:%d (ignored)\n", spri->span->span_id, pevent->ring.channel); + ret = 0; + goto done; + } + + + zap_log(ZAP_LOG_NOTICE, "-- Ring on channel %d:%d (from %s to %s)\n", spri->span->span_id, pevent->ring.channel, + pevent->ring.callingnum, pevent->ring.callednum); + + memset(&zchan->caller_data, 0, sizeof(zchan->caller_data)); + + zap_set_string(zchan->caller_data.cid_num.digits, (char *)pevent->ring.callingnum); + if (!zap_strlen_zero((char *)pevent->ring.callingname)) { + zap_set_string(zchan->caller_data.cid_name, (char *)pevent->ring.callingname); + } else { + zap_set_string(zchan->caller_data.cid_name, (char *)pevent->ring.callingnum); + } + zap_set_string(zchan->caller_data.ani.digits, (char *)pevent->ring.callingani); + zap_set_string(zchan->caller_data.dnis.digits, (char *)pevent->ring.callednum); + + if (pevent->ring.ani2 >= 0) { + snprintf(zchan->caller_data.aniII, 5, "%.2d", pevent->ring.ani2); + } + + // scary to trust this pointer, you'd think they would give you a copy of the call data so you own it...... + zchan->call_data = pevent->ring.call; + + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RING); + + done: + //switch_mutex_unlock(globals.channel_mutex); + + return ret; +} + +/** + * \brief Processes openzap event + * \param span Span on which the event was fired + * \param event Event to be treated + * \return Success or failure + */ +static __inline__ zap_status_t process_event(zap_span_t *span, zap_event_t *event) +{ + zap_log(ZAP_LOG_DEBUG, "EVENT [%s][%d][%d:%d] STATE [%s]\n", + zap_oob_event2str(event->enum_id), event->enum_id, event->channel->span_id, event->channel->chan_id, zap_channel_state2str(event->channel->state)); + + switch(event->enum_id) { + case ZAP_OOB_ALARM_TRAP: + { + if (event->channel->state != ZAP_CHANNEL_STATE_DOWN) { + if (event->channel->type == ZAP_CHAN_TYPE_B) { + zap_set_state_locked(event->channel, ZAP_CHANNEL_STATE_RESTART); + } + } + + + zap_set_flag(event->channel, ZAP_CHANNEL_SUSPENDED); + + + zap_channel_get_alarms(event->channel); + zap_log(ZAP_LOG_WARNING, "channel %d:%d (%d:%d) has alarms! [%s]\n", + event->channel->span_id, event->channel->chan_id, + event->channel->physical_span_id, event->channel->physical_chan_id, + event->channel->last_error); + } + break; + case ZAP_OOB_ALARM_CLEAR: + { + zap_log(ZAP_LOG_WARNING, "channel %d:%d (%d:%d) alarms Cleared!\n", event->channel->span_id, event->channel->chan_id, + event->channel->physical_span_id, event->channel->physical_chan_id); + + zap_clear_flag(event->channel, ZAP_CHANNEL_SUSPENDED); + zap_channel_get_alarms(event->channel); + } + break; + } + + return ZAP_SUCCESS; +} + +/** + * \brief Checks for events on a span + * \param span Span to check for events + */ +static __inline__ void check_events(zap_span_t *span) +{ + zap_status_t status; + + status = zap_span_poll_event(span, 5); + + switch(status) { + case ZAP_SUCCESS: + { + zap_event_t *event; + while (zap_span_next_event(span, &event) == ZAP_SUCCESS) { + if (event->enum_id == ZAP_OOB_NOOP) { + continue; + } + if (process_event(span, event) != ZAP_SUCCESS) { + break; + } + } + } + break; + case ZAP_FAIL: + { + zap_log(ZAP_LOG_DEBUG, "Event Failure! %d\n", zap_running()); + zap_sleep(2000); + } + break; + default: + break; + } +} + +/** + * \brief Checks flags on a pri span + * \param spri Pri wrapper structure (libpri, span, dchan) + * \return 0 on success, -1 on error + */ +static int check_flags(lpwrap_pri_t *spri) +{ + zap_span_t *span = spri->private_info; + + if (!zap_running() || zap_test_flag(span, ZAP_SPAN_STOP_THREAD)) { + return -1; + } + + check_state(span); + check_events(span); + + + return 0; +} + +/** + * \brief Handler for libpri restart event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_restart(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + zap_span_t *span = spri->private_info; + zap_channel_t *zchan; + + zap_log(ZAP_LOG_NOTICE, "-- Restarting %d:%d\n", spri->span->span_id, pevent->restart.channel); + + zchan = span->channels[pevent->restart.channel]; + + if (!zchan) { + return 0; + } + + if (pevent->restart.channel < 1) { + zap_set_state_all(zchan->span, ZAP_CHANNEL_STATE_RESTART); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RESTART); + } + + return 0; +} + +/** + * \brief Handler for libpri dchan up event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_dchan_up(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + if (!zap_test_flag(spri, LPWRAP_PRI_READY)) { + zap_log(ZAP_LOG_INFO, "Span %d D-Chan UP!\n", spri->span->span_id); + zap_set_flag(spri, LPWRAP_PRI_READY); + zap_set_state_all(spri->span, ZAP_CHANNEL_STATE_RESTART); + } + + return 0; +} + +/** + * \brief Handler for libpri dchan down event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_dchan_down(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + if (zap_test_flag(spri, LPWRAP_PRI_READY)) { + zap_log(ZAP_LOG_INFO, "Span %d D-Chan DOWN!\n", spri->span->span_id); + zap_clear_flag(spri, LPWRAP_PRI_READY); + zap_set_state_all(spri->span, ZAP_CHANNEL_STATE_RESTART); + + } + + return 0; +} + +/** + * \brief Handler for any libpri event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_anything(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + zap_log(ZAP_LOG_DEBUG, "Caught Event span %d %u (%s)\n", spri->span->span_id, event_type, lpwrap_pri_event_str(event_type)); + return 0; +} + +/** + * \brief Handler for libpri io fail event + * \param spri Pri wrapper structure (libpri, span, dchan) + * \param event_type Event type (unused) + * \param pevent Event + * \return 0 + */ +static int on_io_fail(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent) +{ + + zap_log(ZAP_LOG_DEBUG, "Caught Event span %d %u (%s)\n", spri->span->span_id, event_type, lpwrap_pri_event_str(event_type)); + return 0; +} + +/** + * \brief Main thread function for libpri span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *zap_libpri_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_libpri_data_t *isdn_data = span->signal_data; + int i, x = 0; + int down = 0; + int got_d = 0; + + zap_set_flag(span, ZAP_SPAN_IN_THREAD); + + while(zap_running() && !zap_test_flag(span, ZAP_SPAN_STOP_THREAD)) { + if (!got_d) { + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->type == ZAP_CHAN_TYPE_DQ921) { + if (zap_channel_open(span->span_id, i, &isdn_data->dchan) == ZAP_SUCCESS) { + zap_log(ZAP_LOG_DEBUG, "opening d-channel #%d %d:%d\n", x, isdn_data->dchan->span_id, isdn_data->dchan->chan_id); + isdn_data->dchan->state = ZAP_CHANNEL_STATE_UP; + got_d = 1; + x++; + break; + } + } + } + } + + + if (lpwrap_init_pri(&isdn_data->spri, + span, // span + isdn_data->dchan, // dchan + isdn_data->pswitch, + isdn_data->node, + isdn_data->debug) < 0) { + snprintf(span->last_error, sizeof(span->last_error), "PRI init FAIL!"); + } else { + + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANY, on_anything); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RING, on_ring); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RINGING, on_ringing); + //LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_SETUP_ACK, on_proceed); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_PROCEEDING, on_proceed); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANSWER, on_answer); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_DCHAN_UP, on_dchan_up); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_DCHAN_DOWN, on_dchan_down); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP_REQ, on_hangup); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP, on_hangup); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_INFO_RECEIVED, on_info); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RESTART, on_restart); + LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_IO_FAIL, on_io_fail); + + if (down) { + zap_log(ZAP_LOG_INFO, "PRI back up on span %d\n", isdn_data->spri.span->span_id); + zap_set_state_all(span, ZAP_CHANNEL_STATE_RESTART); + down = 0; + } + + isdn_data->spri.on_loop = check_flags; + isdn_data->spri.private_info = span; + lpwrap_run_pri(&isdn_data->spri); + + } + + if (!zap_running() || zap_test_flag(span, ZAP_SPAN_STOP_THREAD)) { + break; + } + + zap_log(ZAP_LOG_CRIT, "PRI down on span %d\n", isdn_data->spri.span->span_id); + + if (!down) { + zap_set_state_all(span, ZAP_CHANNEL_STATE_RESTART); + check_state(span); + } + + check_state(span); + check_events(span); + + down++; + zap_sleep(5000); + } + + zap_log(ZAP_LOG_DEBUG, "PRI thread ended on span %d\n", isdn_data->spri.span->span_id); + + zap_clear_flag(span, ZAP_SPAN_IN_THREAD); + zap_clear_flag(isdn_data, OZMOD_LIBPRI_RUNNING); + + return NULL; +} + +/** + * \brief Stops a libpri span + * \param span Span to halt + * \return Success + * + * Sets a stop flag and waits for the thread to end + */ +static zap_status_t zap_libpri_stop(zap_span_t *span) +{ + zap_libpri_data_t *isdn_data = span->signal_data; + + if (!zap_test_flag(isdn_data, OZMOD_LIBPRI_RUNNING)) { + return ZAP_FAIL; + } + + zap_set_state_all(span, ZAP_CHANNEL_STATE_RESTART); + check_state(span); + zap_set_flag(span, ZAP_SPAN_STOP_THREAD); + while(zap_test_flag(span, ZAP_SPAN_IN_THREAD)) { + zap_sleep(100); + } + check_state(span); + + return ZAP_SUCCESS; +} + +/** + * \brief Starts a libpri span + * \param span Span to halt + * \return Success or failure + * + * Launches a thread to monitor the span + */ +static zap_status_t zap_libpri_start(zap_span_t *span) +{ + zap_status_t ret; + zap_libpri_data_t *isdn_data = span->signal_data; + + if (zap_test_flag(isdn_data, OZMOD_LIBPRI_RUNNING)) { + return ZAP_FAIL; + } + + zap_clear_flag(span, ZAP_SPAN_STOP_THREAD); + zap_clear_flag(span, ZAP_SPAN_IN_THREAD); + + zap_set_flag(isdn_data, OZMOD_LIBPRI_RUNNING); + ret = zap_thread_create_detached(zap_libpri_run, span); + + if (ret != ZAP_SUCCESS) { + return ret; + } + + return ret; +} + +/** + * \brief Converts a node string to node value + * \param node Node string to convert + * \return -1 on failure, node value on success + */ +static int str2node(char *node) +{ + if (!strcasecmp(node, "cpe") || !strcasecmp(node, "user")) + return PRI_CPE; + if (!strcasecmp(node, "network") || !strcasecmp(node, "net")) + return PRI_NETWORK; + return -1; +} + +/** + * \brief Converts a switch string to switch value + * \param swtype Swtype string to convert + * \return Switch value + */ +static int str2switch(char *swtype) +{ + if (!strcasecmp(swtype, "ni1")) + return PRI_SWITCH_NI1; + if (!strcasecmp(swtype, "ni2")) + return PRI_SWITCH_NI2; + if (!strcasecmp(swtype, "dms100")) + return PRI_SWITCH_DMS100; + if (!strcasecmp(swtype, "lucent5e") || !strcasecmp(swtype, "5ess")) + return PRI_SWITCH_LUCENT5E; + if (!strcasecmp(swtype, "att4ess") || !strcasecmp(swtype, "4ess")) + return PRI_SWITCH_ATT4ESS; + if (!strcasecmp(swtype, "euroisdn")) + return PRI_SWITCH_EUROISDN_E1; + if (!strcasecmp(swtype, "gr303eoc")) + return PRI_SWITCH_GR303_EOC; + if (!strcasecmp(swtype, "gr303tmc")) + return PRI_SWITCH_GR303_TMC; + return PRI_SWITCH_DMS100; +} + +/** + * \brief Converts a L1 string to L1 value + * \param l1 L1 string to convert + * \return L1 value + */ +static int str2l1(char *l1) +{ + if (!strcasecmp(l1, "alaw")) + return PRI_LAYER_1_ALAW; + + return PRI_LAYER_1_ULAW; +} + +/** + * \brief Converts a DP string to DP value + * \param dp DP string to convert + * \return DP value + */ +static int str2dp(char *dp) +{ + if (!strcasecmp(dp, "international")) + return PRI_INTERNATIONAL_ISDN; + if (!strcasecmp(dp, "national")) + return PRI_NATIONAL_ISDN; + if (!strcasecmp(dp, "local")) + return PRI_LOCAL_ISDN; + if (!strcasecmp(dp, "private")) + return PRI_PRIVATE; + if (!strcasecmp(dp, "unknown")) + return PRI_UNKNOWN; + + return PRI_UNKNOWN; +} + +/** + * \brief Initialises a libpri span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static ZIO_SIG_CONFIGURE_FUNCTION(zap_libpri_configure_span) +{ + uint32_t i, x = 0; + //zap_channel_t *dchans[2] = {0}; + zap_libpri_data_t *isdn_data; + char *var, *val; + char *debug = NULL; + + if (span->trunk_type >= ZAP_TRUNK_NONE) { + zap_log(ZAP_LOG_WARNING, "Invalid trunk type '%s' defaulting to T1.\n", zap_trunk_type2str(span->trunk_type)); + span->trunk_type = ZAP_TRUNK_T1; + } + + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->type == ZAP_CHAN_TYPE_DQ921) { + if (x > 1) { + snprintf(span->last_error, sizeof(span->last_error), "Span has more than 2 D-Channels!"); + return ZAP_FAIL; + } else { +#if 0 + if (zap_channel_open(span->span_id, i, &dchans[x]) == ZAP_SUCCESS) { + zap_log(ZAP_LOG_DEBUG, "opening d-channel #%d %d:%d\n", x, dchans[x]->span_id, dchans[x]->chan_id); + dchans[x]->state = ZAP_CHANNEL_STATE_UP; + x++; + } +#endif + } + } + } + +#if 0 + if (!x) { + snprintf(span->last_error, sizeof(span->last_error), "Span has no D-Channels!"); + return ZAP_FAIL; + } +#endif + + isdn_data = malloc(sizeof(*isdn_data)); + assert(isdn_data != NULL); + memset(isdn_data, 0, sizeof(*isdn_data)); + + if (span->trunk_type == ZAP_TRUNK_E1) { + zap_log(ZAP_LOG_NOTICE, "Setting default Layer 1 to ALAW since this is an E1 trunk\n"); + isdn_data->l1 = PRI_LAYER_1_ALAW; + } else if (span->trunk_type == ZAP_TRUNK_T1) { + zap_log(ZAP_LOG_NOTICE, "Setting default Layer 1 to ULAW since this is a T1 trunk\n"); + isdn_data->l1 = PRI_LAYER_1_ULAW; + } + + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "node")) { + int node; + if (!(val = va_arg(ap, char *))) { + break; + } + node = str2node(val); + if (-1 == node) { + zap_log(ZAP_LOG_ERROR, "Unknown node type %s, defaulting to CPE mode\n", val); + node = PRI_CPE; + } + isdn_data->node = node; + } else if (!strcasecmp(var, "switch")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->pswitch = str2switch(val); + } else if (!strcasecmp(var, "opts")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->opts = parse_opts(val); + } else if (!strcasecmp(var, "dp")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->dp = str2dp(val); + } else if (!strcasecmp(var, "l1")) { + if (!(val = va_arg(ap, char *))) { + break; + } + isdn_data->l1 = str2l1(val); + } else if (!strcasecmp(var, "debug")) { + if (!(val = va_arg(ap, char *))) { + break; + } + debug = val; + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return ZAP_FAIL; + } + } + + span->start = zap_libpri_start; + span->stop = zap_libpri_stop; + span->signal_cb = sig_cb; + //isdn_data->dchans[0] = dchans[0]; + //isdn_data->dchans[1] = dchans[1]; + //isdn_data->dchan = isdn_data->dchans[0]; + + isdn_data->debug = parse_debug(debug); + + + span->signal_data = isdn_data; + span->signal_type = ZAP_SIGTYPE_ISDN; + span->outgoing_call = isdn_outgoing_call; + + if ((isdn_data->opts & OZMOD_LIBPRI_OPT_SUGGEST_CHANNEL)) { + span->channel_request = isdn_channel_request; + span->suggest_chan_id = 1; + } + + span->state_map = &isdn_state_map; + + return ZAP_SUCCESS; +} + +/** + * \brief Openzap libpri signaling and IO module definition + */ +zap_module_t zap_module = { + "libpri", + zap_libpri_io_init, + zap_libpri_unload, + zap_libpri_init, + zap_libpri_configure_span, + NULL +}; + + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_libpri/ozmod_libpri.h b/libs/openzap/src/ozmod/ozmod_libpri/ozmod_libpri.h new file mode 100644 index 0000000000..8c929516ba --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_libpri/ozmod_libpri.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2009, Anthony Minessale II + * 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 OZMOD_LIBPRI_H +#define OZMOD_LIBPRI_H +#include "openzap.h" +#include "lpwrap_pri.h" + +typedef enum { + OZMOD_LIBPRI_OPT_NONE = 0, + OZMOD_LIBPRI_OPT_SUGGEST_CHANNEL = (1 << 0), + OZMOD_LIBPRI_OPT_OMIT_DISPLAY_IE = (1 << 1), + OZMOD_LIBPRI_OPT_OMIT_REDIRECTING_NUMBER_IE = (1 << 2), + + OZMOD_LIBPRI_OPT_MAX = (1 << 3) +} zap_isdn_opts_t; + +typedef enum { + OZMOD_LIBPRI_RUNNING = (1 << 0) +} zap_isdn_flag_t; + + +struct zap_libpri_data { + zap_channel_t *dchan; + zap_channel_t *dchans[2]; + struct zap_sigmsg sigmsg; + uint32_t flags; + int32_t mode; + zap_isdn_opts_t opts; + + int node; + int pswitch; + char *dialplan; + unsigned int l1; + unsigned int dp; + + int debug; + + lpwrap_pri_t spri; +}; + +typedef struct zap_libpri_data zap_libpri_data_t; + + +/* b-channel private data */ +struct zap_isdn_bchan_data +{ + int32_t digit_timeout; +}; + +typedef struct zap_isdn_bchan_data zap_isdn_bchan_data_t; + + +#endif + +/* 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: + */ + diff --git a/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.2005.vcproj b/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.2005.vcproj new file mode 100644 index 0000000000..c62eddd423 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.2005.vcproj @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.2008.vcproj b/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.2008.vcproj new file mode 100644 index 0000000000..2e3cfceb13 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.2008.vcproj @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.c b/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.c new file mode 100644 index 0000000000..154c1618ff --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_pika/ozmod_pika.c @@ -0,0 +1,1469 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "openzap.h" +#include "zap_pika.h" + + +#define MAX_NUMBER_OF_TRUNKS 64 +#define PIKA_BLOCK_SIZE 160 +#define PIKA_BLOCK_LEN 20 +#define PIKA_NUM_BUFFERS 8 +#define TRY_OR_DIE(__code, __status, __label) if ((status = __code ) != __status) goto __label +#define pk_atof(__a) (PK_FLOAT) atof(__a) + +PK_VOID PK_CALLBACK media_out_callback(PKH_TPikaEvent *event); + +ZAP_ENUM_NAMES(PIKA_SPAN_NAMES, PIKA_SPAN_STRINGS) +PIKA_STR2ENUM(pika_str2span, pika_span2str, PIKA_TSpanFraming, PIKA_SPAN_NAMES, PIKA_SPAN_INVALID) + +ZAP_ENUM_NAMES(PIKA_SPAN_ENCODING_NAMES, PIKA_SPAN_ENCODING_STRINGS) +PIKA_STR2ENUM(pika_str2span_encoding, pika_span_encoding2str, PIKA_TSpanEncoding, PIKA_SPAN_ENCODING_NAMES, PIKA_SPAN_ENCODING_INVALID) + +ZAP_ENUM_NAMES(PIKA_LL_NAMES, PIKA_LL_STRINGS) +PIKA_STR2ENUM(pika_str2loop_length, pika_loop_length2str, PIKA_TSpanLoopLength, PIKA_LL_NAMES, PIKA_SPAN_LOOP_INVALID) + +ZAP_ENUM_NAMES(PIKA_LBO_NAMES, PIKA_LBO_STRINGS) +PIKA_STR2ENUM(pika_str2lbo, pika_lbo2str, PIKA_TSpanBuildOut, PIKA_LBO_NAMES, PIKA_SPAN_LBO_INVALID) + +ZAP_ENUM_NAMES(PIKA_SPAN_COMPAND_MODE_NAMES, PIKA_SPAN_COMPAND_MODE_STRINGS) +PIKA_STR2ENUM(pika_str2compand_mode, pika_compand_mode2str, PIKA_TSpanCompandMode, PIKA_SPAN_COMPAND_MODE_NAMES, PIKA_SPAN_COMPAND_MODE_INVALID) + + +typedef enum { + PK_FLAG_READY = (1 << 0), + PK_FLAG_LOCKED = (1 << 1) +} pk_flag_t; + +struct general_config { + uint32_t region; +}; +typedef struct general_config general_config_t; + +struct pika_channel_profile { + char name[80]; + PKH_TRecordConfig record_config; + PKH_TPlayConfig play_config; + int ec_enabled; + PKH_TECConfig ec_config; + PKH_TSpanConfig span_config; + general_config_t general_config; + int cust_span; +}; +typedef struct pika_channel_profile pika_channel_profile_t; + +static struct { + PKH_TSystemDeviceList board_list; + TPikaHandle open_boards[MAX_NUMBER_OF_TRUNKS]; + TPikaHandle system_handle; + PKH_TSystemConfig system_config; + PKH_TRecordConfig record_config; + PKH_TPlayConfig play_config; + PKH_TECConfig ec_config; + PKH_TSpanConfig t1_span_config; + PKH_TSpanConfig e1_span_config; + zap_hash_t *profile_hash; + general_config_t general_config; +} globals; + + +struct pika_span_data { + TPikaHandle event_queue; + PKH_TPikaEvent last_oob_event; + uint32_t boardno; + PKH_TSpanConfig span_config; + TPikaHandle handle; + uint32_t flags; +}; +typedef struct pika_span_data pika_span_data_t; + +struct pika_chan_data { + TPikaHandle handle; + TPikaHandle media_in; + TPikaHandle media_out; + TPikaHandle media_in_queue; + TPikaHandle media_out_queue; + PKH_TPikaEvent last_media_event; + PKH_TPikaEvent last_oob_event; + PKH_TRecordConfig record_config; + PKH_TPlayConfig play_config; + int ec_enabled; + PKH_TECConfig ec_config; + PKH_THDLCConfig hdlc_config; + zap_buffer_t *digit_buffer; + zap_mutex_t *digit_mutex; + zap_size_t dtmf_len; + uint32_t flags; + uint32_t hdlc_bytes; +}; +typedef struct pika_chan_data pika_chan_data_t; + +static const char *pika_board_type_string(PK_UINT type) +{ + if (type == PKH_BOARD_TYPE_DIGITAL_GATEWAY) { + return "digital_gateway"; + } + + if (type == PKH_BOARD_TYPE_ANALOG_GATEWAY) { + return "analog_gateway"; + } + + return "unknown"; +} + +/** + * \brief Process configuration variable for a pika profile + * \param category Pika profile name + * \param var Variable name + * \param val Variable value + * \param lineno Line number from configuration file (unused) + * \return Success + */ +static ZIO_CONFIGURE_FUNCTION(pika_configure) +{ + pika_channel_profile_t *profile = NULL; + int ok = 1; + + if (!(profile = (pika_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) { + profile = malloc(sizeof(*profile)); + memset(profile, 0, sizeof(*profile)); + zap_set_string(profile->name, category); + profile->ec_config = globals.ec_config; + profile->record_config = globals.record_config; + profile->play_config = globals.play_config; + hashtable_insert(globals.profile_hash, (void *)profile->name, profile, HASHTABLE_FLAG_NONE); + zap_log(ZAP_LOG_INFO, "creating profile [%s]\n", category); + } + + if (!strcasecmp(var, "rx-gain")) { + profile->record_config.gain = pk_atof(val); + } else if (!strcasecmp(var, "rx-agc-enabled")) { + profile->record_config.AGC.enabled = zap_true(val); + } else if (!strcasecmp(var, "rx-agc-targetPower")) { + profile->record_config.AGC.targetPower = pk_atof(val); + } else if (!strcasecmp(var, "rx-agc-minGain")) { + profile->record_config.AGC.minGain = pk_atof(val); + } else if (!strcasecmp(var, "rx-agc-maxGain")) { + profile->record_config.AGC.maxGain = pk_atof(val); + } else if (!strcasecmp(var, "rx-agc-attackRate")) { + profile->record_config.AGC.attackRate = atoi(val); + } else if (!strcasecmp(var, "rx-agc-decayRate")) { + profile->record_config.AGC.decayRate = atoi(val); + } else if (!strcasecmp(var, "rx-agc-speechThreshold")) { + profile->record_config.AGC.speechThreshold = pk_atof(val); + } else if (!strcasecmp(var, "rx-vad-enabled")) { + profile->record_config.VAD.enabled = zap_true(val); + } else if (!strcasecmp(var, "rx-vad-activationThreshold")) { + profile->record_config.VAD.activationThreshold = pk_atof(val); + } else if (!strcasecmp(var, "rx-vad-activationDebounceTime")) { + profile->record_config.VAD.activationDebounceTime = atoi(val); + } else if (!strcasecmp(var, "rx-vad-deactivationThreshold")) { + profile->record_config.VAD.deactivationThreshold = pk_atof(val); + } else if (!strcasecmp(var, "rx-vad-deactivationDebounceTime")) { + profile->record_config.VAD.deactivationDebounceTime = atoi(val); + } else if (!strcasecmp(var, "rx-vad-preSpeechBufferSize")) { + profile->record_config.VAD.preSpeechBufferSize = atoi(val); + } else if (!strcasecmp(var, "tx-gain")) { + profile->play_config.gain = pk_atof(val); + } else if (!strcasecmp(var, "tx-agc-enabled")) { + profile->play_config.AGC.enabled = zap_true(val); + } else if (!strcasecmp(var, "tx-agc-targetPower")) { + profile->play_config.AGC.targetPower = pk_atof(val); + } else if (!strcasecmp(var, "tx-agc-minGain")) { + profile->play_config.AGC.minGain = pk_atof(val); + } else if (!strcasecmp(var, "tx-agc-maxGain")) { + profile->play_config.AGC.maxGain = pk_atof(val); + } else if (!strcasecmp(var, "tx-agc-attackRate")) { + profile->play_config.AGC.attackRate = atoi(val); + } else if (!strcasecmp(var, "tx-agc-decayRate")) { + profile->play_config.AGC.decayRate = atoi(val); + } else if (!strcasecmp(var, "tx-agc-speechThreshold")) { + profile->play_config.AGC.speechThreshold = pk_atof(val); + } else if (!strcasecmp(var, "ec-enabled")) { + profile->ec_enabled = zap_true(val); + } else if (!strcasecmp(var, "ec-doubleTalkerThreshold")) { + profile->ec_config.doubleTalkerThreshold = pk_atof(val); + } else if (!strcasecmp(var, "ec-speechPresentThreshold")) { + profile->ec_config.speechPresentThreshold = pk_atof(val); + } else if (!strcasecmp(var, "ec-echoSuppressionThreshold")) { + profile->ec_config.echoSuppressionThreshold = pk_atof(val); + } else if (!strcasecmp(var, "ec-echoSuppressionEnabled")) { + profile->ec_config.echoSuppressionEnabled = zap_true(val); + } else if (!strcasecmp(var, "ec-comfortNoiseEnabled")) { + profile->ec_config.comfortNoiseEnabled = zap_true(val); + } else if (!strcasecmp(var, "ec-adaptationModeEnabled")) { + profile->ec_config.adaptationModeEnabled = zap_true(val); + } else if (!strcasecmp(var, "framing")) { + profile->span_config.framing = pika_str2span(val); + profile->cust_span++; + } else if (!strcasecmp(var, "encoding")) { + profile->span_config.encoding = pika_str2span_encoding(val); + profile->cust_span++; + } else if (!strcasecmp(var, "loopLength")) { + profile->span_config.loopLength = pika_str2loop_length(val); + profile->cust_span++; + } else if (!strcasecmp(var, "buildOut")) { + profile->span_config.buildOut = pika_str2lbo(val); + profile->cust_span++; + } else if (!strcasecmp(var, "compandMode")) { + profile->span_config.compandMode = pika_str2compand_mode(val); + profile->cust_span++; + } else if (!strcasecmp(var, "region")) { + if (!strcasecmp(val, "eu")) { + profile->general_config.region = PKH_TRUNK_EU; + } else { + profile->general_config.region = PKH_TRUNK_NA; + } + } else { + ok = 0; + } + + if (ok) { + zap_log(ZAP_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category); + } else { + zap_log(ZAP_LOG_ERROR, "unknown param [%s]\n", var); + } + + return ZAP_SUCCESS; +} + +/** + * \brief Pika event handler + * \param event Pika event + */ +PK_VOID PK_CALLBACK media_out_callback(PKH_TPikaEvent *event) +{ + PK_STATUS pk_status; + zap_channel_t *zchan = event->userData; + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + + //PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + //PKH_EVENT_GetText(event->id, event_text, sizeof(event_text)); + //zap_log(ZAP_LOG_DEBUG, "Event: %s\n", event_text); + + switch (event->id) { + case PKH_EVENT_PLAY_IDLE: + { + while (zap_buffer_inuse(chan_data->digit_buffer)) { + char dtmf[128] = ""; + zap_mutex_lock(chan_data->digit_mutex); + chan_data->dtmf_len = zap_buffer_read(chan_data->digit_buffer, dtmf, sizeof(dtmf)); + pk_status = PKH_TG_PlayDTMF(chan_data->media_out, dtmf); + zap_mutex_unlock(chan_data->digit_mutex); + } + } + break; + case PKH_EVENT_TG_TONE_PLAYED: + { + + if (!event->p1) { + zap_mutex_lock(chan_data->digit_mutex); + PKH_PLAY_Start(chan_data->media_out); + chan_data->dtmf_len = 0; + zap_mutex_unlock(chan_data->digit_mutex); + } + + + } + break; + default: + break; + } + +} + +/** + * \brief Initialises a range of pika channels + * \param span Openzap span + * \param boardno Pika board number + * \param spanno Pika span number + * \param start Initial pika channel number + * \param end Final pika channel number + * \param type Openzap channel type + * \param name Openzap span name + * \param number Openzap span number + * \param profile Pika channel profile + * \return number of spans configured + */ +static unsigned pika_open_range(zap_span_t *span, unsigned boardno, unsigned spanno, unsigned start, unsigned end, + zap_chan_type_t type, char *name, char *number, pika_channel_profile_t *profile) +{ + unsigned configured = 0, x; + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + pika_span_data_t *span_data; + + if (boardno >= globals.board_list.numberOfBoards) { + zap_log(ZAP_LOG_ERROR, "Board %u is not present!\n", boardno); + return 0; + } + + if (!globals.open_boards[boardno]) { + status = PKH_BOARD_Open(globals.board_list.board[boardno].id, + NULL, + &globals.open_boards[boardno]); + if(status != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_BOARD_Open %d failed(%s)!\n", boardno, + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return 0; + } + + zap_log(ZAP_LOG_DEBUG, "Open board %u\n", boardno); + + //PKH_BOARD_SetDebugTrace(globals.open_boards[boardno], 1, 0); + + } + + if (span->mod_data) { + span_data = span->mod_data; + } else { + span_data = malloc(sizeof(*span_data)); + assert(span_data != NULL); + memset(span_data, 0, sizeof(*span_data)); + span_data->boardno = boardno; + + status = PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &span_data->event_queue); + + if (status != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_QUEUE_Create failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + free(span_data); + return 0; + } + + //PKH_QUEUE_Attach(span_data->event_queue, globals.open_boards[boardno], NULL); + + span->mod_data = span_data; + } + + if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO) { + start--; + end--; + } + + for(x = start; x < end; x++) { + zap_channel_t *chan; + pika_chan_data_t *chan_data = NULL; + + chan_data = malloc(sizeof *chan_data); + assert(chan_data); + memset(chan_data, 0, sizeof(*chan_data)); + zap_span_add_channel(span, 0, type, &chan); + chan->mod_data = chan_data; + + if ((type == ZAP_CHAN_TYPE_B || type == ZAP_CHAN_TYPE_DQ921) && !span_data->handle) { + PKH_TBoardConfig boardConfig; + + TRY_OR_DIE(PKH_BOARD_GetConfig(globals.open_boards[boardno], &boardConfig), PK_SUCCESS, error); + if ((profile && profile->general_config.region == PKH_TRUNK_EU) || zap_test_flag(span_data, PK_FLAG_LOCKED)) { + if (span->trunk_type == ZAP_TRUNK_T1) { + zap_log(ZAP_LOG_WARNING, "Changing trunk type to E1 based on previous config.\n"); + } + span->trunk_type = ZAP_TRUNK_E1; + } + + if (span->trunk_type == ZAP_TRUNK_T1) { + if (zap_test_flag(span_data, PK_FLAG_LOCKED)) { + zap_log(ZAP_LOG_WARNING, "Already locked into E1 mode!\n"); + } + } else if (span->trunk_type == ZAP_TRUNK_E1) { + boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_E1; + if ((status = PKH_BOARD_SetConfig(globals.open_boards[boardno], &boardConfig)) != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: [%s]\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + } + zap_set_flag(span_data, PK_FLAG_LOCKED); + } + + TRY_OR_DIE(PKH_SPAN_Open(globals.open_boards[boardno], spanno, NULL, &span_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_SPAN_GetConfig(span_data->handle, &span_data->span_config), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, span_data->handle, (PK_VOID*) span), PK_SUCCESS, error); + } + + if (type == ZAP_CHAN_TYPE_FXO) { + PKH_TTrunkConfig trunkConfig; + + TRY_OR_DIE(PKH_TRUNK_Open(globals.open_boards[boardno], x, &chan_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_TRUNK_Seize(chan_data->handle), PK_SUCCESS, error); + + if (profile && profile->general_config.region == PKH_TRUNK_EU) { + TRY_OR_DIE(PKH_TRUNK_GetConfig(chan_data->handle, &trunkConfig), PK_SUCCESS, error); + trunkConfig.internationalControl = PKH_PHONE_INTERNATIONAL_CONTROL_EU; + trunkConfig.audioFormat = PKH_AUDIO_ALAW; + trunkConfig.compandMode = PKH_PHONE_AUDIO_ALAW; + chan->native_codec = chan->effective_codec = ZAP_CODEC_ALAW; + TRY_OR_DIE(PKH_TRUNK_SetConfig(chan_data->handle, &trunkConfig), PK_SUCCESS, error); + } else { + chan->native_codec = chan->effective_codec = ZAP_CODEC_ULAW; + } + + + TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_TRUNK_GetMediaStreams(chan_data->handle, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_TRUNK_Start(chan_data->handle), PK_SUCCESS, error); + } else if (type == ZAP_CHAN_TYPE_FXS) { + PKH_TPhoneConfig phoneConfig; + + if (profile && profile->general_config.region == PKH_TRUNK_EU) { + TRY_OR_DIE(PKH_PHONE_GetConfig(chan_data->handle, &phoneConfig), PK_SUCCESS, error); + phoneConfig.internationalControl = PKH_PHONE_INTERNATIONAL_CONTROL_EU; + phoneConfig.compandMode = PKH_PHONE_AUDIO_ALAW; + chan->native_codec = chan->effective_codec = ZAP_CODEC_ALAW; + TRY_OR_DIE(PKH_PHONE_SetConfig(chan_data->handle, &phoneConfig), PK_SUCCESS, error); + } else { + chan->native_codec = chan->effective_codec = ZAP_CODEC_ULAW; + } + + TRY_OR_DIE(PKH_PHONE_Open(globals.open_boards[boardno], x, &chan_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_PHONE_Seize(chan_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_PHONE_GetMediaStreams(chan_data->handle, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_PHONE_Start(chan_data->handle), PK_SUCCESS, error); + } else if (type == ZAP_CHAN_TYPE_B) { + TRY_OR_DIE(PKH_SPAN_SeizeChannel(span_data->handle, x), PK_SUCCESS, error); + TRY_OR_DIE(PKH_SPAN_GetMediaStreams(span_data->handle, x, &chan_data->media_in, &chan_data->media_out), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->media_in, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_CALLBACK, &chan_data->media_out_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_SetEventHandler(chan_data->media_out_queue, media_out_callback), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_out_queue, chan_data->media_out, (PK_VOID*) chan), PK_SUCCESS, error); + } else if (type == ZAP_CHAN_TYPE_DQ921) { + TRY_OR_DIE(PKH_SPAN_HDLC_Open(span_data->handle, PKH_SPAN_HDLC_MODE_NORMAL, &chan_data->handle), PK_SUCCESS, error); + TRY_OR_DIE(PKH_SPAN_HDLC_GetConfig(chan_data->handle, &chan_data->hdlc_config), PK_SUCCESS, error); + chan_data->hdlc_config.channelId = x; + TRY_OR_DIE(PKH_SPAN_HDLC_SetConfig(chan_data->handle, &chan_data->hdlc_config), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Create(PKH_QUEUE_TYPE_NORMAL, &chan_data->media_in_queue), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(chan_data->media_in_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error); + TRY_OR_DIE(PKH_QUEUE_Attach(span_data->event_queue, chan_data->handle, (PK_VOID*) chan), PK_SUCCESS, error); + + if (profile) { + if (profile->cust_span) { + span_data->span_config.framing = profile->span_config.framing; + span_data->span_config.encoding = profile->span_config.encoding; + span_data->span_config.loopLength = profile->span_config.loopLength; + span_data->span_config.buildOut = profile->span_config.buildOut; + span_data->span_config.compandMode = profile->span_config.compandMode; + } else { + if (profile->general_config.region == PKH_TRUNK_EU) { + span_data->span_config = globals.e1_span_config; + } else { + span_data->span_config = globals.t1_span_config; + } + } + } else { + if (span->trunk_type == ZAP_TRUNK_E1) { + span_data->span_config = globals.e1_span_config; + } else { + span_data->span_config = globals.t1_span_config; + } + } + + PKH_SPAN_SetConfig(span_data->handle, &span_data->span_config); + TRY_OR_DIE(PKH_SPAN_Start(span_data->handle), PK_SUCCESS, error); + } + + goto ok; + + error: + PKH_ERROR_GetText(status, error_text, sizeof(error_text)); + zap_log(ZAP_LOG_ERROR, "failure configuring device b%ds%dc%d [%s]\n", boardno, spanno, x, error_text); + continue; + ok: + zap_set_flag(chan_data, PK_FLAG_READY); + status = PKH_RECORD_GetConfig(chan_data->media_in, &chan_data->record_config); + chan_data->record_config.encoding = PKH_RECORD_ENCODING_MU_LAW; + chan_data->record_config.samplingRate = PKH_RECORD_SAMPLING_RATE_8KHZ; + chan_data->record_config.bufferSize = PIKA_BLOCK_SIZE; + chan_data->record_config.numberOfBuffers = PIKA_NUM_BUFFERS; + chan_data->record_config.VAD.enabled = PK_FALSE; + //chan_data->record_config.speechSegmentEventsEnabled = PK_FALSE; + //chan_data->record_config.gain = rxgain; + + status = PKH_PLAY_GetConfig(chan_data->media_out, &chan_data->play_config); + chan_data->play_config.encoding = PKH_RECORD_ENCODING_MU_LAW; + chan_data->play_config.samplingRate = PKH_RECORD_SAMPLING_RATE_8KHZ; + chan_data->play_config.AGC.enabled = PK_FALSE; + zap_log(ZAP_LOG_INFO, "configuring device b%ds%dc%d as OpenZAP device %d:%d\n", boardno, spanno, x, chan->span_id, chan->chan_id); + + if (profile) { + zap_log(ZAP_LOG_INFO, "applying config profile %s to device %d:%d\n", profile->name, chan->span_id, chan->chan_id); + chan_data->record_config.gain = profile->record_config.gain; + chan_data->record_config.AGC = profile->record_config.AGC; + chan_data->record_config.VAD = profile->record_config.VAD; + chan_data->play_config.gain = profile->play_config.gain; + chan_data->play_config.AGC = profile->play_config.AGC; + chan_data->ec_enabled = profile->ec_enabled; + chan_data->ec_config = profile->ec_config; + } + + if (type == ZAP_CHAN_TYPE_B) { + if (span_data->span_config.compandMode == PKH_SPAN_COMPAND_MODE_A_LAW) { + chan->native_codec = chan->effective_codec = ZAP_CODEC_ALAW; + } else { + chan->native_codec = chan->effective_codec = ZAP_CODEC_ULAW; + } + } + + status = PKH_RECORD_SetConfig(chan_data->media_in, &chan_data->record_config); + status = PKH_PLAY_SetConfig(chan_data->media_out, &chan_data->play_config); + + chan->physical_span_id = spanno; + chan->physical_chan_id = x; + + chan->rate = 8000; + chan->packet_len = (uint32_t)chan_data->record_config.bufferSize; + chan->effective_interval = chan->native_interval = chan->packet_len / 8; + + PKH_RECORD_Start(chan_data->media_in); + PKH_PLAY_Start(chan_data->media_out); + if (chan_data->ec_enabled) { + PKH_EC_SetConfig(chan_data->media_in, &chan_data->ec_config); + PKH_EC_Start(chan_data->media_in, chan_data->media_in, chan_data->media_out); + } + + if (!zap_strlen_zero(name)) { + zap_copy_string(chan->chan_name, name, sizeof(chan->chan_name)); + } + + if (!zap_strlen_zero(number)) { + zap_copy_string(chan->chan_number, number, sizeof(chan->chan_number)); + } + + zap_channel_set_feature(chan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE); + zap_buffer_create(&chan_data->digit_buffer, 128, 128, 0); + zap_mutex_create(&chan_data->digit_mutex); + + configured++; + } + + + return configured; +} + +/** + * \brief Initialises an openzap pika span from a configuration string + * \param span Openzap span + * \param str Configuration string + * \param type Openzap span type + * \param name Openzap span name + * \param number Openzap span number + * \return Success or failure + */ +static ZIO_CONFIGURE_SPAN_FUNCTION(pika_configure_span) +{ + int items, i; + char *mydata, *item_list[10]; + char *bd, *sp, *ch = NULL, *mx; + int boardno; + int channo; + int spanno; + int top = 0; + unsigned configured = 0; + char *profile_name = NULL; + pika_channel_profile_t *profile = NULL; + + assert(str != NULL); + + mydata = strdup(str); + assert(mydata != NULL); + + if ((profile_name = strchr(mydata, '@'))) { + *profile_name++ = '\0'; + if (!zap_strlen_zero(profile_name)) { + profile = (pika_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)profile_name); + } + } + + items = zap_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0]))); + + for(i = 0; i < items; i++) { + bd = item_list[i]; + if ((sp = strchr(bd, ':'))) { + *sp++ = '\0'; + if ((ch = strchr(sp, ':'))) { + *ch++ = '\0'; + } + } + + if (!(bd && sp && ch)) { + zap_log(ZAP_LOG_ERROR, "Invalid input\n"); + continue; + } + + boardno = atoi(bd); + channo = atoi(ch); + spanno = atoi(sp); + + + if (boardno < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid board number %d\n", boardno); + continue; + } + + if (channo < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid channel number %d\n", channo); + continue; + } + + if (spanno < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid span number %d\n", channo); + continue; + } + + if ((mx = strchr(ch, '-'))) { + mx++; + top = atoi(mx) + 1; + } else { + top = channo + 1; + } + + + if (top < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid range number %d\n", top); + continue; + } + + configured += pika_open_range(span, boardno, spanno, channo, top, type, name, number, profile); + + } + + free(mydata); + + return configured; +} + +/** + * \brief Opens Pika channel + * \param zchan Channel to open + * \return Success or failure + */ +static ZIO_OPEN_FUNCTION(pika_open) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + + if (!chan_data && !zap_test_flag(chan_data, PK_FLAG_READY)) { + return ZAP_FAIL; + } + + if (chan_data->media_in_queue) { + PKH_QUEUE_Flush(chan_data->media_in_queue); + } + + if (zchan->type == ZAP_CHAN_TYPE_FXS || zchan->type == ZAP_CHAN_TYPE_FXO || zchan->type == ZAP_CHAN_TYPE_B) { + PKH_PLAY_Start(chan_data->media_out); + } + return ZAP_SUCCESS; +} + +/** + * \brief Closes Pika channel + * \param zchan Channel to close + * \return Success + */ +static ZIO_CLOSE_FUNCTION(pika_close) +{ + return ZAP_SUCCESS; +} + +/** + * \brief Waits for an event on a Pika channel + * \param zchan Channel to open + * \param flags Type of event to wait for + * \param to Time to wait (in ms) + * \return Success, failure or timeout + */ +static ZIO_WAIT_FUNCTION(pika_wait) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + PK_STATUS status; + zap_wait_flag_t myflags = *flags; + PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + + *flags = ZAP_NO_FLAGS; + + if (myflags & ZAP_READ) { + if (chan_data->hdlc_bytes) { + *flags |= ZAP_READ; + return ZAP_SUCCESS; + } + status = PKH_QUEUE_WaitOnEvent(chan_data->media_in_queue, to, &chan_data->last_media_event); + + if (status == PK_SUCCESS) { + if (chan_data->last_media_event.id == PKH_EVENT_QUEUE_TIMEOUT || chan_data->last_media_event.id == PKH_EVENT_RECORD_BUFFER_OVERFLOW) { + return ZAP_TIMEOUT; + } + + *flags |= ZAP_READ; + return ZAP_SUCCESS; + } + + PKH_EVENT_GetText(chan_data->last_media_event.id, event_text, sizeof(event_text)); + zap_log(ZAP_LOG_DEBUG, "Event: %s\n", event_text); + } + + return ZAP_SUCCESS; +} + +/** + * \brief Reads data from a Pika channel + * \param zchan Channel to read from + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success or failure + */ +static ZIO_READ_FUNCTION(pika_read) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + PK_STATUS status; + PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + uint32_t len; + + if (zchan->type == ZAP_CHAN_TYPE_DQ921) { + if ((status = PKH_SPAN_HDLC_GetMessage(chan_data->handle, data, *datalen)) == PK_SUCCESS) { + *datalen = chan_data->hdlc_bytes; + chan_data->hdlc_bytes = 0; + return ZAP_SUCCESS; + } + return ZAP_FAIL; + } + + if (!(len = chan_data->last_media_event.p0)) { + len = zchan->packet_len; + } + + if (len < *datalen) { + *datalen = len; + } + + if ((status = PKH_RECORD_GetData(chan_data->media_in, data, *datalen)) == PK_SUCCESS) { + return ZAP_SUCCESS; + } + + + PKH_ERROR_GetText(status, event_text, sizeof(event_text)); + zap_log(ZAP_LOG_DEBUG, "ERR: %s\n", event_text); + return ZAP_FAIL; +} + +/** + * \brief Writes data to a Pika channel + * \param zchan Channel to write to + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success or failure + */ +static ZIO_WRITE_FUNCTION(pika_write) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + PK_STATUS status; + + if (zchan->type == ZAP_CHAN_TYPE_DQ921) { + if ((status = PKH_SPAN_HDLC_SendMessage(chan_data->handle, data, *datalen)) == PK_SUCCESS) { + return ZAP_SUCCESS; + } + return ZAP_FAIL; + } + + if (PKH_PLAY_AddData(chan_data->media_out, 0, data, *datalen) == PK_SUCCESS) { + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +/** + * \brief Executes an Openzap command on a Pika channel + * \param zchan Channel to execute command on + * \param command Openzap command to execute + * \param obj Object (unused) + * \return Success or failure + */ +static ZIO_COMMAND_FUNCTION(pika_command) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + //pika_span_data_t *span_data = (pika_span_data_t *) zchan->span->mod_data; + PK_STATUS pk_status; + zap_status_t status = ZAP_SUCCESS; + + switch(command) { + case ZAP_COMMAND_OFFHOOK: + { + if ((pk_status = PKH_TRUNK_SetHookSwitch(chan_data->handle, PKH_TRUNK_OFFHOOK)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } else { + zap_set_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + } + } + break; + case ZAP_COMMAND_ONHOOK: + { + if ((pk_status = PKH_TRUNK_SetHookSwitch(chan_data->handle, PKH_TRUNK_ONHOOK)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } else { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + } + } + break; + case ZAP_COMMAND_GENERATE_RING_ON: + { + if ((pk_status = PKH_PHONE_RingStart(chan_data->handle, 0, 0)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } else { + zap_set_flag_locked(zchan, ZAP_CHANNEL_RINGING); + } + } + break; + case ZAP_COMMAND_GENERATE_RING_OFF: + { + if ((pk_status = PKH_PHONE_RingStop(chan_data->handle)) != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } else { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_RINGING); + } + } + break; + case ZAP_COMMAND_GET_INTERVAL: + { + + ZAP_COMMAND_OBJ_INT = zchan->native_interval; + + } + break; + case ZAP_COMMAND_SET_INTERVAL: + { + int interval = ZAP_COMMAND_OBJ_INT; + int len = interval * 8; + chan_data->record_config.bufferSize = len; + chan_data->record_config.numberOfBuffers = (PK_UINT)chan_data->record_config.bufferSize; + zchan->packet_len = (uint32_t)chan_data->record_config.bufferSize; + zchan->effective_interval = zchan->native_interval = zchan->packet_len / 8; + PKH_RECORD_SetConfig(chan_data->media_in, &chan_data->record_config); + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + case ZAP_COMMAND_GET_DTMF_ON_PERIOD: + { + + ZAP_COMMAND_OBJ_INT = zchan->dtmf_on; + GOTO_STATUS(done, ZAP_SUCCESS); + + } + break; + case ZAP_COMMAND_GET_DTMF_OFF_PERIOD: + { + ZAP_COMMAND_OBJ_INT = zchan->dtmf_on; + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + case ZAP_COMMAND_SET_DTMF_ON_PERIOD: + { + int val = ZAP_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + zchan->dtmf_on = val; + GOTO_STATUS(done, ZAP_SUCCESS); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, ZAP_FAIL); + } + } + break; + case ZAP_COMMAND_SET_DTMF_OFF_PERIOD: + { + int val = ZAP_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + zchan->dtmf_off = val; + GOTO_STATUS(done, ZAP_SUCCESS); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, ZAP_FAIL); + } + } + break; + case ZAP_COMMAND_SEND_DTMF: + { + char *digits = ZAP_COMMAND_OBJ_CHAR_P; + zap_log(ZAP_LOG_DEBUG, "Adding DTMF SEQ [%s]\n", digits); + zap_mutex_lock(chan_data->digit_mutex); + zap_buffer_write(chan_data->digit_buffer, digits, strlen(digits)); + zap_mutex_unlock(chan_data->digit_mutex); + pk_status = PKH_PLAY_Stop(chan_data->media_out); + + if (pk_status != PK_SUCCESS) { + PKH_ERROR_GetText(pk_status, zchan->last_error, sizeof(zchan->last_error)); + GOTO_STATUS(done, ZAP_FAIL); + } + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + default: + break; + }; + + done: + return status; +} + +/** + * \brief Checks for events on a Pika span + * \param span Span to check for events + * \param ms Time to wait for event + * \return Success if event is waiting or failure if not + */ +static ZIO_SPAN_POLL_EVENT_FUNCTION(pika_poll_event) +{ + pika_span_data_t *span_data = (pika_span_data_t *) span->mod_data; + PK_STATUS status; + PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + + status = PKH_QUEUE_WaitOnEvent(span_data->event_queue, ms, &span_data->last_oob_event); + + if (status == PK_SUCCESS) { + zap_channel_t *zchan = NULL; + uint32_t *data = (uint32_t *) span_data->last_oob_event.userData; + zap_data_type_t data_type = ZAP_TYPE_NONE; + + if (span_data->last_oob_event.id == PKH_EVENT_QUEUE_TIMEOUT) { + return ZAP_TIMEOUT; + } + + if (data) { + data_type = *data; + } + + if (data_type == ZAP_TYPE_CHANNEL) { + zchan = span_data->last_oob_event.userData; + } else if (data_type == ZAP_TYPE_SPAN) { + zap_time_t last_event_time = zap_current_time_in_ms(); + uint32_t event_id = 0; + + switch (span_data->last_oob_event.id) { + case PKH_EVENT_SPAN_ALARM_T1_RED: + case PKH_EVENT_SPAN_ALARM_T1_YELLOW: + case PKH_EVENT_SPAN_ALARM_T1_AIS: + case PKH_EVENT_SPAN_ALARM_E1_RED: + case PKH_EVENT_SPAN_ALARM_E1_RAI: + case PKH_EVENT_SPAN_ALARM_E1_AIS: + case PKH_EVENT_SPAN_ALARM_E1_RMAI: + case PKH_EVENT_SPAN_ALARM_E1_TS16AIS: + case PKH_EVENT_SPAN_ALARM_E1_TS16LOS: + case PKH_EVENT_SPAN_OUT_OF_SYNC: + case PKH_EVENT_SPAN_FRAMING_ERROR: + case PKH_EVENT_SPAN_LOSS_OF_SIGNAL: + case PKH_EVENT_SPAN_OUT_OF_CRC_MF_SYNC: + case PKH_EVENT_SPAN_OUT_OF_CAS_MF_SYNC: + event_id = ZAP_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_T1_RED_CLEAR: + case PKH_EVENT_SPAN_ALARM_T1_YELLOW_CLEAR: + case PKH_EVENT_SPAN_ALARM_T1_AIS_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_RED_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_RAI_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_AIS_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_RMAI_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_TS16AIS_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_TS16LOS_CLEAR: + case PKH_EVENT_SPAN_IN_SYNC: + case PKH_EVENT_SPAN_LOSS_OF_SIGNAL_CLEAR: + case PKH_EVENT_SPAN_IN_CRC_MF_SYNC: + case PKH_EVENT_SPAN_IN_CAS_MF_SYNC: + event_id = ZAP_OOB_ALARM_CLEAR; + break; + case PKH_EVENT_SPAN_MESSAGE: + case PKH_EVENT_SPAN_ABCD_SIGNAL_CHANGE: + break; + } + + if (event_id) { + uint32_t x = 0; + zap_channel_t *zchan; + pika_chan_data_t *chan_data; + for(x = 1; x <= span->chan_count; x++) { + zchan = span->channels[x]; + assert(zchan != NULL); + chan_data = (pika_chan_data_t *) zchan->mod_data; + assert(chan_data != NULL); + + + zap_set_flag(zchan, ZAP_CHANNEL_EVENT); + zchan->last_event_time = last_event_time; + chan_data->last_oob_event = span_data->last_oob_event; + } + + } + + } + + PKH_EVENT_GetText(span_data->last_oob_event.id, event_text, sizeof(event_text)); + //zap_log(ZAP_LOG_DEBUG, "Event: %s\n", event_text); + + if (zchan) { + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + + assert(chan_data != NULL); + zap_set_flag(zchan, ZAP_CHANNEL_EVENT); + zchan->last_event_time = zap_current_time_in_ms(); + chan_data->last_oob_event = span_data->last_oob_event; + } + + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +/** + * \brief Retrieves an event from a Pika span + * \param span Span to retrieve event from + * \param event Openzap event to return + * \return Success or failure + */ +static ZIO_SPAN_NEXT_EVENT_FUNCTION(pika_next_event) +{ + uint32_t i, event_id = 0; + + for(i = 1; i <= span->chan_count; i++) { + if (zap_test_flag(span->channels[i], ZAP_CHANNEL_EVENT)) { + pika_chan_data_t *chan_data = (pika_chan_data_t *) span->channels[i]->mod_data; + PK_CHAR event_text[PKH_EVENT_MAX_NAME_LENGTH]; + + zap_clear_flag(span->channels[i], ZAP_CHANNEL_EVENT); + + PKH_EVENT_GetText(chan_data->last_oob_event.id, event_text, sizeof(event_text)); + + switch(chan_data->last_oob_event.id) { + case PKH_EVENT_HDLC_MESSAGE: + chan_data->hdlc_bytes = chan_data->last_oob_event.p2; + continue; + case PKH_EVENT_TRUNK_HOOKFLASH: + event_id = ZAP_OOB_FLASH; + break; + case PKH_EVENT_TRUNK_RING_OFF: + event_id = ZAP_OOB_RING_STOP; + break; + case PKH_EVENT_TRUNK_RING_ON: + event_id = ZAP_OOB_RING_START; + break; + + case PKH_EVENT_PHONE_OFFHOOK: + zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK); + event_id = ZAP_OOB_OFFHOOK; + break; + + case PKH_EVENT_TRUNK_BELOW_THRESHOLD: + case PKH_EVENT_TRUNK_ABOVE_THRESHOLD: + case PKH_EVENT_PHONE_ONHOOK: + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK); + event_id = ZAP_OOB_ONHOOK; + break; + + + + case PKH_EVENT_SPAN_ALARM_T1_RED: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_RED); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RED ALARM"); + event_id = ZAP_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_T1_YELLOW: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_YELLOW); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "YELLOW ALARM"); + event_id = ZAP_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_T1_AIS: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_AIS); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "AIS ALARM"); + event_id = ZAP_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_E1_RED: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_RED); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RED ALARM"); + event_id = ZAP_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_E1_RAI: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_RAI); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "RAI ALARM"); + event_id = ZAP_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_E1_AIS: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_AIS); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "AIS ALARM"); + event_id = ZAP_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_E1_RMAI: + case PKH_EVENT_SPAN_ALARM_E1_TS16AIS: + case PKH_EVENT_SPAN_ALARM_E1_TS16LOS: + case PKH_EVENT_SPAN_OUT_OF_SYNC: + case PKH_EVENT_SPAN_FRAMING_ERROR: + case PKH_EVENT_SPAN_LOSS_OF_SIGNAL: + case PKH_EVENT_SPAN_OUT_OF_CRC_MF_SYNC: + case PKH_EVENT_SPAN_OUT_OF_CAS_MF_SYNC: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_GENERAL); + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "GENERAL ALARM"); + event_id = ZAP_OOB_ALARM_TRAP; + break; + case PKH_EVENT_SPAN_ALARM_T1_RED_CLEAR: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_RED); + case PKH_EVENT_SPAN_ALARM_T1_YELLOW_CLEAR: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_YELLOW); + case PKH_EVENT_SPAN_ALARM_T1_AIS_CLEAR: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_AIS); + case PKH_EVENT_SPAN_ALARM_E1_RED_CLEAR: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_RED); + case PKH_EVENT_SPAN_ALARM_E1_RAI_CLEAR: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_RAI); + case PKH_EVENT_SPAN_ALARM_E1_AIS_CLEAR: + zap_set_alarm_flag(span->channels[i], ZAP_ALARM_AIS); + case PKH_EVENT_SPAN_ALARM_E1_RMAI_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_TS16AIS_CLEAR: + case PKH_EVENT_SPAN_ALARM_E1_TS16LOS_CLEAR: + case PKH_EVENT_SPAN_IN_SYNC: + case PKH_EVENT_SPAN_LOSS_OF_SIGNAL_CLEAR: + case PKH_EVENT_SPAN_IN_CRC_MF_SYNC: + case PKH_EVENT_SPAN_IN_CAS_MF_SYNC: + zap_clear_alarm_flag(span->channels[i], ZAP_ALARM_GENERAL); + event_id = ZAP_OOB_ALARM_CLEAR; + break; + case PKH_EVENT_SPAN_MESSAGE: + case PKH_EVENT_SPAN_ABCD_SIGNAL_CHANGE: + break; + + + + + case PKH_EVENT_TRUNK_ONHOOK: + case PKH_EVENT_TRUNK_OFFHOOK: + case PKH_EVENT_TRUNK_DIALED : + case PKH_EVENT_TRUNK_REVERSAL: + case PKH_EVENT_TRUNK_LCSO: + case PKH_EVENT_TRUNK_DROPOUT: + case PKH_EVENT_TRUNK_LOF: + case PKH_EVENT_TRUNK_RX_OVERLOAD: + default: + zap_log(ZAP_LOG_DEBUG, "Unhandled event %d on channel %d [%s]\n", chan_data->last_oob_event.id, i, event_text); + event_id = ZAP_OOB_INVALID; + break; + } + + span->channels[i]->last_event_time = 0; + span->event_header.e_type = ZAP_EVENT_OOB; + span->event_header.enum_id = event_id; + span->event_header.channel = span->channels[i]; + *event = &span->event_header; + return ZAP_SUCCESS; + } + } + + return ZAP_FAIL; +} + +/** + * \brief Destroys a Pika Span + * \param span Span to destroy + * \return Success + */ +static ZIO_SPAN_DESTROY_FUNCTION(pika_span_destroy) +{ + pika_span_data_t *span_data = (pika_span_data_t *) span->mod_data; + + if (span_data) { + PKH_QUEUE_Destroy(span_data->event_queue); + free(span_data); + } + + return ZAP_SUCCESS; +} + +/** + * \brief Destroys a Pika Channel + * \param zchan Channel to destroy + * \return Success or failure + */ +static ZIO_CHANNEL_DESTROY_FUNCTION(pika_channel_destroy) +{ + pika_chan_data_t *chan_data = (pika_chan_data_t *) zchan->mod_data; + pika_span_data_t *span_data = (pika_span_data_t *) zchan->span->mod_data; + + if (!chan_data) { + return ZAP_FAIL; + } + + if (!zap_test_flag(chan_data, PK_FLAG_READY)) { + goto end; + } + + PKH_RECORD_Stop(chan_data->media_in); + PKH_PLAY_Stop(chan_data->media_out); + PKH_QUEUE_Destroy(chan_data->media_in_queue); + PKH_QUEUE_Destroy(chan_data->media_out_queue); + + switch(zchan->type) { + case ZAP_CHAN_TYPE_FXS: + PKH_QUEUE_Detach(span_data->event_queue, chan_data->handle); + PKH_PHONE_Close(chan_data->handle); + break; + case ZAP_CHAN_TYPE_FXO: + PKH_QUEUE_Detach(span_data->event_queue, chan_data->handle); + PKH_TRUNK_Close(chan_data->handle); + break; + case ZAP_CHAN_TYPE_DQ921: + PKH_SPAN_Stop(span_data->handle); + break; + default: + break; + } + + + zap_mutex_destroy(&chan_data->digit_mutex); + zap_buffer_destroy(&chan_data->digit_buffer); + + end: + zap_safe_free(chan_data); + + return ZAP_SUCCESS; +} + +/** + * \brief Gets alarms from a Pika Channel (does nothing) + * \param zchan Channel to get alarms from + * \return Failure + */ +static ZIO_GET_ALARMS_FUNCTION(pika_get_alarms) +{ + return ZAP_FAIL; +} + +static zap_io_interface_t pika_interface; + +/** + * \brief Loads Pika IO module + * \param zio Openzap IO interface + * \return Success or failure + */ +static ZIO_IO_LOAD_FUNCTION(pika_init) +{ + + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + uint32_t i; + int ok = 0; + PKH_TLogMasks m; + TPikaHandle tmpHandle; + + assert(zio != NULL); + memset(&pika_interface, 0, sizeof(pika_interface)); + memset(&globals, 0, sizeof(globals)); + globals.general_config.region = PKH_TRUNK_NA; + + globals.profile_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys); + + // Open the system object, to enumerate boards configured for this system + if ((status = PKH_SYSTEM_Open(&globals.system_handle)) != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_SYSTEM_Open failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return ZAP_FAIL; + } + + // Retrieves a list of all boards in this system, existing, + // or listed in pika.cfg + if ((status = PKH_SYSTEM_Detect(globals.system_handle, &globals.board_list)) != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_SYSTEM_Detect failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + return ZAP_FAIL; + } + + PKH_SYSTEM_GetConfig(globals.system_handle, &globals.system_config); + globals.system_config.maxAudioProcessBlockSize = PIKA_BLOCK_LEN; + globals.system_config.playBufferSize = PIKA_BLOCK_SIZE; + globals.system_config.recordBufferSize = PIKA_BLOCK_SIZE; + globals.system_config.recordNumberOfBuffers = PIKA_NUM_BUFFERS; + PKH_SYSTEM_SetConfig(globals.system_handle, &globals.system_config); + + status = PKH_MEDIA_STREAM_Create(&tmpHandle); + status = PKH_RECORD_GetConfig(tmpHandle, &globals.record_config); + status = PKH_PLAY_GetConfig(tmpHandle, &globals.play_config); + status = PKH_EC_GetConfig(tmpHandle, &globals.ec_config); + status = PKH_MEDIA_STREAM_Destroy(tmpHandle); + + + + zap_log(ZAP_LOG_DEBUG, "Found %u board%s\n", globals.board_list.numberOfBoards, globals.board_list.numberOfBoards == 1 ? "" : "s"); + for(i = 0; i < globals.board_list.numberOfBoards; ++i) { + zap_log(ZAP_LOG_INFO, "Found PIKA board type:[%s] id:[%u] serno:[%u]\n", + pika_board_type_string(globals.board_list.board[i].type), globals.board_list.board[i].id, (uint32_t) + globals.board_list.board[i].serialNumber); + + if (globals.board_list.board[i].type == PKH_BOARD_TYPE_DIGITAL_GATEWAY) { + TPikaHandle board_handle, span_handle; + PKH_TBoardConfig boardConfig; + PKH_BOARD_GetConfig(board_handle, &boardConfig); + PKH_BOARD_Open(globals.board_list.board[i].id, NULL, &board_handle); + PKH_SPAN_Open(board_handle, 0, NULL, &span_handle); + PKH_SPAN_GetConfig(span_handle, &globals.t1_span_config); + PKH_SPAN_Close(span_handle); + boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_E1; + PKH_BOARD_SetConfig(board_handle, &boardConfig); + PKH_SPAN_Open(board_handle, 0, NULL, &span_handle); + PKH_SPAN_GetConfig(span_handle, &globals.e1_span_config); + PKH_SPAN_Close(span_handle); + boardConfig.specific.DigitalGateway.interfaceType = PKH_BOARD_INTERFACE_TYPE_T1; + PKH_BOARD_SetConfig(board_handle, &boardConfig); + PKH_BOARD_Close(board_handle); + } + ok++; + + } + + if (!ok) { + return ZAP_FAIL; + } + + pika_interface.name = "pika"; + pika_interface.configure = pika_configure; + pika_interface.configure_span = pika_configure_span; + pika_interface.open = pika_open; + pika_interface.close = pika_close; + pika_interface.wait = pika_wait; + pika_interface.read = pika_read; + pika_interface.write = pika_write; + pika_interface.command = pika_command; + pika_interface.poll_event = pika_poll_event; + pika_interface.next_event = pika_next_event; + pika_interface.channel_destroy = pika_channel_destroy; + pika_interface.span_destroy = pika_span_destroy; + pika_interface.get_alarms = pika_get_alarms; + *zio = &pika_interface; + + + zap_log(ZAP_LOG_INFO, "Dumping Default configs:\n"); + zap_log(ZAP_LOG_INFO, "rx-gain => %0.2f\n", (float)globals.record_config.gain); + zap_log(ZAP_LOG_INFO, "rx-agc-enabled => %s\n", globals.record_config.AGC.enabled ? "true" : "false"); + zap_log(ZAP_LOG_INFO, "rx-agc-targetPower => %0.2f\n", (float)globals.record_config.AGC.targetPower); + zap_log(ZAP_LOG_INFO, "rx-agc-minGain => %0.2f\n", (float)globals.record_config.AGC.minGain); + zap_log(ZAP_LOG_INFO, "rx-agc-maxGain => %0.2f\n", (float)globals.record_config.AGC.maxGain); + zap_log(ZAP_LOG_INFO, "rx-agc-attackRate => %d\n", (int)globals.record_config.AGC.attackRate); + zap_log(ZAP_LOG_INFO, "rx-agc-decayRate => %d\n", (int)globals.record_config.AGC.decayRate); + zap_log(ZAP_LOG_INFO, "rx-agc-speechThreshold => %0.2f\n", (float)globals.record_config.AGC.speechThreshold); + zap_log(ZAP_LOG_INFO, "rx-vad-enabled => %s\n", globals.record_config.VAD.enabled ? "true" : "false"); + zap_log(ZAP_LOG_INFO, "rx-vad-activationThreshold => %0.2f\n", (float)globals.record_config.VAD.activationThreshold); + zap_log(ZAP_LOG_INFO, "rx-vad-activationDebounceTime => %d\n", (int)globals.record_config.VAD.activationDebounceTime); + zap_log(ZAP_LOG_INFO, "rx-vad-deactivationThreshold => %0.2f\n", (float)globals.record_config.VAD.deactivationThreshold); + zap_log(ZAP_LOG_INFO, "rx-vad-deactivationDebounceTime => %d\n", (int)globals.record_config.VAD.deactivationDebounceTime); + zap_log(ZAP_LOG_INFO, "rx-vad-preSpeechBufferSize => %d\n", (int)globals.record_config.VAD.preSpeechBufferSize); + zap_log(ZAP_LOG_INFO, "tx-gain => %0.2f\n", (float)globals.play_config.gain); + zap_log(ZAP_LOG_INFO, "tx-agc-enabled => %s\n", globals.play_config.AGC.enabled ? "true" : "false"); + zap_log(ZAP_LOG_INFO, "tx-agc-targetPower => %0.2f\n", (float)globals.play_config.AGC.targetPower); + zap_log(ZAP_LOG_INFO, "tx-agc-minGain => %0.2f\n", (float)globals.play_config.AGC.minGain); + zap_log(ZAP_LOG_INFO, "tx-agc-maxGain => %0.2f\n", (float)globals.play_config.AGC.maxGain); + zap_log(ZAP_LOG_INFO, "tx-agc-attackRate => %d\n", (int)globals.play_config.AGC.attackRate); + zap_log(ZAP_LOG_INFO, "tx-agc-decayRate => %d\n", (int)globals.play_config.AGC.decayRate); + zap_log(ZAP_LOG_INFO, "tx-agc-speechThreshold => %0.2f\n", (float)globals.play_config.AGC.speechThreshold); + zap_log(ZAP_LOG_INFO, "ec-doubleTalkerThreshold => %0.2f\n", (float)globals.ec_config.doubleTalkerThreshold); + zap_log(ZAP_LOG_INFO, "ec-speechPresentThreshold => %0.2f\n", (float)globals.ec_config.speechPresentThreshold); + zap_log(ZAP_LOG_INFO, "ec-echoSuppressionThreshold => %0.2f\n", (float)globals.ec_config.echoSuppressionThreshold); + zap_log(ZAP_LOG_INFO, "ec-echoSuppressionEnabled => %s\n", globals.ec_config.echoSuppressionEnabled ? "true" : "false"); + zap_log(ZAP_LOG_INFO, "ec-comfortNoiseEnabled => %s\n", globals.ec_config.comfortNoiseEnabled ? "true" : "false"); + zap_log(ZAP_LOG_INFO, "ec-adaptationModeEnabled => %s\n", globals.ec_config.adaptationModeEnabled ? "true" : "false"); + + + + memset(&m, 0, sizeof(m)); + //m.apiMask = 0xffffffff; + //PKH_LOG_SetMasks(&m); + + return ZAP_SUCCESS; +} + +/** + * \brief Unloads Pika IO module + * \return Success + */ +static ZIO_IO_UNLOAD_FUNCTION(pika_destroy) +{ + uint32_t x; + PK_STATUS status; + PK_CHAR error_text[PKH_ERROR_MAX_NAME_LENGTH]; + + for (x = 0; x < MAX_NUMBER_OF_TRUNKS; x++) { + if (globals.open_boards[x]) { + zap_log(ZAP_LOG_INFO, "Closing board %u\n", x); + PKH_BOARD_Close(globals.open_boards[x]); + } + } + + // The system can now be closed. + if ((status = PKH_SYSTEM_Close(globals.system_handle)) != PK_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error: PKH_SYSTEM_Close failed(%s)!\n", + PKH_ERROR_GetText(status, error_text, sizeof(error_text))); + } else { + zap_log(ZAP_LOG_INFO, "Closing system handle\n"); + } + + hashtable_destroy(globals.profile_hash); + + return ZAP_SUCCESS; +} + +/** + * \brief Pika IO module definition + */ +EX_DECLARE_DATA zap_module_t zap_module = { + "pika", + pika_init, + pika_destroy, +}; + +/* 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: + */ + diff --git a/libs/openzap/src/ozmod/ozmod_pika/zap_pika.h b/libs/openzap/src/ozmod/ozmod_pika/zap_pika.h new file mode 100644 index 0000000000..27c455a429 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_pika/zap_pika.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 ZAP_PIKA_H +#define ZAP_PIKA_H +#include "openzap.h" +#include "pikahmpapi.h" + + + +#define PIKA_STR2ENUM_P(_FUNC1, _FUNC2, _TYPE) _TYPE _FUNC1 (const char *name); const char * _FUNC2 (_TYPE type); +#define PIKA_STR2ENUM(_FUNC1, _FUNC2, _TYPE, _STRINGS, _MAX) \ + _TYPE _FUNC1 (const char *name) \ + { \ + int i; \ + _TYPE t = _MAX ; \ + \ + for (i = 0; i < _MAX ; i++) { \ + if (!strcasecmp(name, _STRINGS[i])) { \ + t = (_TYPE) i; \ + break; \ + } \ + } \ + \ + return t; \ + } \ + const char * _FUNC2 (_TYPE type) \ + { \ + if (type > _MAX) { \ + type = _MAX; \ + } \ + return _STRINGS[(int)type]; \ + } + + +typedef enum { + PIKA_SPAN_FRAMING_T1_D4, + PIKA_SPAN_FRAMING_T1_ESF, + PIKA_SPAN_FRAMING_E1_BASIC, + PIKA_SPAN_FRAMING_E1_CRC4, + PIKA_SPAN_INVALID +} PIKA_TSpanFraming; +#define PIKA_SPAN_STRINGS "T1_D4", "T1_ESF", "E1_BASIC", "E1_CRC4" +PIKA_STR2ENUM_P(pika_str2span, pika_span2str, PIKA_TSpanFraming) + +typedef enum { + PIKA_SPAN_ENCODING_T1_AMI_ZS_NONE, + PIKA_SPAN_ENCODING_T1_AMI_ZS_GTE, + PIKA_SPAN_ENCODING_T1_AMI_ZS_BELL, + PIKA_SPAN_ENCODING_T1_AMI_ZS_JAM8, + PIKA_SPAN_ENCODING_T1_B8ZS, + PIKA_SPAN_ENCODING_E1_AMI, + PIKA_SPAN_ENCODING_E1_HDB3, + PIKA_SPAN_ENCODING_INVALID +} PIKA_TSpanEncoding; +#define PIKA_SPAN_ENCODING_STRINGS "T1_AMI_ZS_NONE", "T1_AMI_ZS_GTE", "T1_AMI_ZS_BELL", "T1_AMI_ZS_JAM8", "T1_B8ZS", "E1_AMI", "E1_HDB3" +PIKA_STR2ENUM_P(pika_str2span_encoding, pika_span_encoding2str, PIKA_TSpanEncoding) + +typedef enum { + PIKA_SPAN_LOOP_LENGTH_SHORT_HAUL, + PIKA_SPAN_LOOP_LENGTH_LONG_HAUL, + PIKA_SPAN_LOOP_INVALID +} PIKA_TSpanLoopLength; +#define PIKA_LL_STRINGS "SHORT_HAUL", "LONG_HAUL" +PIKA_STR2ENUM_P(pika_str2loop_length, pika_loop_length2str, PIKA_TSpanLoopLength) + +typedef enum { + PIKA_SPAN_LBO_T1_LONG_0_DB, + PIKA_SPAN_LBO_T1_LONG_7_DB, + PIKA_SPAN_LBO_T1_LONG_15_DB, + PIKA_SPAN_LBO_T1_LONG_22_DB, + PIKA_SPAN_LBO_T1_SHORT_133_FT, + PIKA_SPAN_LBO_T1_SHORT_266_FT, + PIKA_SPAN_LBO_T1_SHORT_399_FT, + PIKA_SPAN_LBO_T1_SHORT_533_FT, + PIKA_SPAN_LBO_T1_SHORT_655_FT, + PIKA_SPAN_LBO_E1_WAVEFORM_120_OHM, + PIKA_SPAN_LBO_INVALID +} PIKA_TSpanBuildOut; +#define PIKA_LBO_STRINGS "T1_LONG_0_DB", "T1_LONG_7_DB", "T1_LONG_15_DB", "T1_LONG_22_DB", "T1_SHORT_133_FT", "T1_SHORT_266_FT", "T1_SHORT_399_FT", "T1_SHORT_533_FT", "T1_SHORT_655_FT", "E1_WAVEFORM_120_OHM" +PIKA_STR2ENUM_P(pika_str2lbo, pika_lbo2str, PIKA_TSpanBuildOut) + +typedef enum { + PIKA_SPAN_COMPAND_MODE_MU_LAW = 1, + PIKA_SPAN_COMPAND_MODE_A_LAW, + PIKA_SPAN_COMPAND_MODE_INVALID +} PIKA_TSpanCompandMode; +#define PIKA_SPAN_COMPAND_MODE_STRINGS "MU_LAW", "A_LAW" +PIKA_STR2ENUM_P(pika_str2compand_mode, pika_compand_mode2str, PIKA_TSpanCompandMode) + +#endif + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_r2/ozmod_r2.c b/libs/openzap/src/ozmod/ozmod_r2/ozmod_r2.c new file mode 100644 index 0000000000..64a2d8aa0a --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_r2/ozmod_r2.c @@ -0,0 +1,1364 @@ +/* + * Copyright (c) 2009, Moises Silva + * 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 +#include +#include +#include "openzap.h" + +/* debug thread count for r2 legs */ +static zap_mutex_t* g_thread_count_mutex; +static int32_t g_thread_count = 0; + +/* when the users kills a span we clear this flag to kill the signaling thread */ +/* FIXME: what about the calls that are already up-and-running? */ +typedef enum { + ZAP_R2_RUNNING = (1 << 0), +} zap_r2_flag_t; + +/* private call information stored in zchan->call_data void* ptr */ +#define R2CALL(zchan) ((zap_r2_call_t*)((zchan)->call_data)) +typedef struct zap_r2_call_t { + openr2_chan_t *r2chan; + int accepted:1; + int answer_pending:1; + int state_ack_pending:1; + int disconnect_rcvd:1; + int zap_started:1; + zap_channel_state_t chanstate; + zap_size_t dnis_index; + zap_size_t ani_index; + char name[10]; +} zap_r2_call_t; + +/* this is just used as place holder in the stack when configuring the span to avoid using bunch of locals */ +typedef struct oz_r2_conf_s { + /* openr2 types */ + openr2_variant_t variant; + openr2_calling_party_category_t category; + openr2_log_level_t loglevel; + + /* strings */ + char *logdir; + char *advanced_protocol_file; + + /* ints */ + int32_t max_ani; + int32_t max_dnis; + int32_t mfback_timeout; + int32_t metering_pulse_timeout; + + /* booleans */ + int immediate_accept; + int skip_category; + int get_ani_first; + int call_files; + int double_answer; + int charge_calls; + int forced_release; + int allow_collect_calls; +} oz_r2_conf_t; + +/* r2 configuration stored in span->signal_data */ +typedef struct zap_r2_data_s { + /* span flags */ + zap_r2_flag_t flags; + /* openr2 handle for the R2 variant context */ + openr2_context_t *r2context; + /* category to use when making calls */ + openr2_calling_party_category_t category; + /* whether to use OR2_CALL_WITH_CHARGE or OR2_CALL_NO_CHARGE when accepting a call */ + int charge_calls:1; + /* allow or reject collect calls */ + int allow_collect_calls:1; + /* whether to use forced release when hanging up */ + int forced_release:1; + /* whether accept the call when offered, or wait until the user decides to accept */ + int accept_on_offer:1; +} zap_r2_data_t; + +/* one element per span will be stored in g_mod_data_hash global var to keep track of them + and destroy them on module unload */ +typedef struct zap_r2_span_pvt_s { + openr2_context_t *r2context; /* r2 context allocated for this span */ + zap_hash_t *r2calls; /* hash table of allocated call data per channel for this span */ +} zap_r2_span_pvt_t; + +/* span monitor thread */ +static void *zap_r2_run(zap_thread_t *me, void *obj); + +/* channel monitor thread */ +static void *zap_r2_channel_run(zap_thread_t *me, void *obj); + +/* hash of all the private span allocations + we need to keep track of them to destroy them when unloading the module + since openzap does not notify signaling modules when destroying a span + span -> zap_r2_mod_allocs_t */ +static zap_hash_t *g_mod_data_hash; + +/* IO interface for the command API */ +static zap_io_interface_t g_zap_r2_interface; + +static void oz_r2_clean_call(zap_r2_call_t *call) +{ + openr2_chan_t *r2chan = call->r2chan; + memset(call, 0, sizeof(*call)); + call->r2chan = r2chan; +} + +static void oz_r2_accept_call(zap_channel_t *zchan) +{ + openr2_chan_t *r2chan = R2CALL(zchan)->r2chan; + // FIXME: not always accept as no charge, let the user decide that + // also we should check the return code from openr2_chan_accept_call and handle error condition + // hanging up the call with protocol error as the reason, this openr2 API will fail only when there something + // wrong at the I/O layer or the library itself + openr2_chan_accept_call(r2chan, OR2_CALL_NO_CHARGE); + R2CALL(zchan)->accepted = 1; +} + +static void oz_r2_answer_call(zap_channel_t *zchan) +{ + openr2_chan_t *r2chan = R2CALL(zchan)->r2chan; + // FIXME + // 1. check openr2_chan_answer_call return code + // 2. The openr2_chan_answer_call_with_mode should be used depending on user settings + // openr2_chan_answer_call_with_mode(r2chan, OR2_ANSWER_SIMPLE); + openr2_chan_answer_call(r2chan); + R2CALL(zchan)->answer_pending = 0; +} + +static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call) +{ + zap_status_t status; + zap_mutex_lock(zchan->mutex); + + /* the channel may be down but the thread not quite done */ + zap_wait_for_flag_cleared(zchan, ZAP_CHANNEL_INTHREAD, 200); + + if (zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_log(ZAP_LOG_ERROR, "%d:%d Yay! R2 outgoing call in channel that is already in thread.\n", + zchan->span_id, zchan->chan_id); + zap_mutex_unlock(zchan->mutex); + return ZAP_FAIL; + } + + oz_r2_clean_call(zchan->call_data); + R2CALL(zchan)->chanstate = ZAP_CHANNEL_STATE_DOWN; + zap_channel_set_state(zchan, ZAP_CHANNEL_STATE_DIALING, 0); + zap_set_flag(zchan, ZAP_CHANNEL_OUTBOUND); + R2CALL(zchan)->zap_started = 1; + zap_mutex_unlock(zchan->mutex); + + status = zap_thread_create_detached(zap_r2_channel_run, zchan); + if (status == ZAP_FAIL) { + zap_log(ZAP_LOG_ERROR, "%d:%d Cannot handle request to start call in channel, failed to create thread!\n", + zchan->span_id, zchan->chan_id); + zap_channel_done(zchan); + return ZAP_FAIL; + } + + return ZAP_SUCCESS; +} + +static zap_status_t zap_r2_start(zap_span_t *span) +{ + zap_r2_data_t *r2_data = span->signal_data; + zap_set_flag(r2_data, ZAP_R2_RUNNING); + return zap_thread_create_detached(zap_r2_run, span); +} + +/* always called from the monitor thread */ +static void zap_r2_on_call_init(openr2_chan_t *r2chan) +{ + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + zap_status_t status; + zap_log(ZAP_LOG_NOTICE, "Received request to start call on chan %d\n", openr2_chan_get_number(r2chan)); + + zap_mutex_lock(zchan->mutex); + + if (zchan->state != ZAP_CHANNEL_STATE_DOWN) { + zap_log(ZAP_LOG_ERROR, "Cannot handle request to start call in channel %d, invalid state (%d)\n", + openr2_chan_get_number(r2chan), zchan->state); + zap_mutex_unlock(zchan->mutex); + return; + } + + /* the channel may be down but the thread not quite done */ + zap_wait_for_flag_cleared(zchan, ZAP_CHANNEL_INTHREAD, 200); + + if (zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_log(ZAP_LOG_ERROR, "Cannot handle request to start call in channel %d, already in thread!\n", + openr2_chan_get_number(r2chan)); + zap_mutex_unlock(zchan->mutex); + return; + } + oz_r2_clean_call(zchan->call_data); + R2CALL(zchan)->chanstate = ZAP_CHANNEL_STATE_DOWN; + zap_channel_set_state(zchan, ZAP_CHANNEL_STATE_COLLECT, 0); + zap_mutex_unlock(zchan->mutex); + + status = zap_thread_create_detached(zap_r2_channel_run, zchan); + if (status == ZAP_FAIL) { + zap_log(ZAP_LOG_ERROR, "Cannot handle request to start call in channel %d, failed to create thread!\n", + openr2_chan_get_number(r2chan)); + } +} + +/* 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 */ +static void zap_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category) +{ + zap_sigmsg_t sigev; + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + + zap_log(ZAP_LOG_NOTICE, "Call offered on chan %d, ANI = %s, DNIS = %s, Category = %s\n", openr2_chan_get_number(r2chan), + ani, dnis, openr2_proto_get_category_string(category)); + + /* notify the user about the new call */ + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = zchan->chan_id; + sigev.span_id = zchan->span_id; + sigev.channel = zchan; + sigev.event_id = ZAP_SIGEVENT_START; + + if (zap_span_send_signal(zchan->span, &sigev) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_NOTICE, "Failed to handle call offered on chan %d\n", openr2_chan_get_number(r2chan)); + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_CANCEL); + return; + } + zap_channel_use(zchan); + R2CALL(zchan)->zap_started = 1; +} + +static void zap_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode) +{ + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + zap_log(ZAP_LOG_NOTICE, "Call accepted on chan %d\n", openr2_chan_get_number(r2chan)); + /* at this point the MF signaling has ended and there is no point on keep reading */ + openr2_chan_disable_read(r2chan); + if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) { + R2CALL(zchan)->state_ack_pending = 1; + if (R2CALL(zchan)->answer_pending) { + zap_log(ZAP_LOG_DEBUG, "Answer was pending on chan %d, answering now.\n", openr2_chan_get_number(r2chan)); + oz_r2_answer_call(zchan); + return; + } + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS); + } +} + +static void zap_r2_on_call_answered(openr2_chan_t *r2chan) +{ + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + zap_log(ZAP_LOG_NOTICE, "Call answered on chan %d\n", openr2_chan_get_number(r2chan)); + /* notify the upper layer of progress in the outbound call */ + if (OR2_DIR_FORWARD == openr2_chan_get_direction(r2chan)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP); + } +} + +/* may be called in the signaling or media thread depending on whether the hangup is product of MF or CAS signaling */ +static void zap_r2_on_call_disconnect(openr2_chan_t *r2chan, openr2_call_disconnect_cause_t cause) +{ + zap_sigmsg_t sigev; + zap_r2_data_t *r2data; + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + zap_log(ZAP_LOG_NOTICE, "Call disconnected on chan %d\n", openr2_chan_get_number(r2chan)); + + zap_log(ZAP_LOG_DEBUG, "Got openr2 disconnection, clearing call on channel %d\n", zchan->physical_chan_id); + + R2CALL(zchan)->disconnect_rcvd = 1; + + /* acknowledge the hangup, cause will be ignored. From here to -> HANGUP once the openzap side hangs up as well */ + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING); + + /* if the call has not been started yet we must go to HANGUP right here */ + if (!R2CALL(zchan)->zap_started) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + return; + } + + /* FIXME: use the cause received from openr2 and map it to zap cause */ + zchan->caller_data.hangup_cause = ZAP_CAUSE_NORMAL_CLEARING; + + /* notify the user of the call terminating */ + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = zchan->chan_id; + sigev.span_id = zchan->span_id; + sigev.channel = zchan; + sigev.event_id = ZAP_SIGEVENT_STOP; + r2data = zchan->span->signal_data; + + zap_span_send_signal(zchan->span, &sigev); +} + +static void zap_r2_on_call_end(openr2_chan_t *r2chan) +{ + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + zap_log(ZAP_LOG_NOTICE, "Call finished on chan %d\n", openr2_chan_get_number(r2chan)); + /* this means the openzap side disconnected the call, therefore we must move to DOWN here */ + if (!R2CALL(zchan)->disconnect_rcvd) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + return; + } +} + +static void zap_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen) +{ + zap_log(ZAP_LOG_NOTICE, "Call read data on chan %d\n", openr2_chan_get_number(r2chan)); +} + +static void zap_r2_on_hardware_alarm(openr2_chan_t *r2chan, int alarm) +{ + zap_log(ZAP_LOG_NOTICE, "Alarm on chan %d (%d)\n", openr2_chan_get_number(r2chan), alarm); +} + +static void zap_r2_on_os_error(openr2_chan_t *r2chan, int errorcode) +{ + zap_log(ZAP_LOG_ERROR, "OS error on chan %d: %s\n", openr2_chan_get_number(r2chan), strerror(errorcode)); +} + +static void zap_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_error_t reason) +{ + zap_sigmsg_t sigev; + zap_r2_data_t *r2data; + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + + zap_log(ZAP_LOG_ERROR, "Protocol error on chan %d\n", openr2_chan_get_number(r2chan)); + + R2CALL(zchan)->disconnect_rcvd = 1; + + if (!R2CALL(zchan)->zap_started) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + return; + } + + zchan->caller_data.hangup_cause = ZAP_CAUSE_PROTOCOL_ERROR; + + /* notify the user of the call terminating */ + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = zchan->chan_id; + sigev.span_id = zchan->span_id; + sigev.channel = zchan; + sigev.event_id = ZAP_SIGEVENT_STOP; + r2data = zchan->span->signal_data; + + zap_span_send_signal(zchan->span, &sigev); +} + +static void zap_r2_on_line_blocked(openr2_chan_t *r2chan) +{ + zap_log(ZAP_LOG_NOTICE, "Far end blocked on chan %d\n", openr2_chan_get_number(r2chan)); +} + +static void zap_r2_on_line_idle(openr2_chan_t *r2chan) +{ + zap_log(ZAP_LOG_NOTICE, "Far end unblocked on chan %d\n", openr2_chan_get_number(r2chan)); +} + +static void zap_r2_write_log(openr2_log_level_t level, const char *message) +{ + switch (level) { + case OR2_LOG_NOTICE: + zap_log(ZAP_LOG_NOTICE, "%s", message); + break; + case OR2_LOG_WARNING: + zap_log(ZAP_LOG_WARNING, "%s", message); + break; + case OR2_LOG_ERROR: + zap_log(ZAP_LOG_ERROR, "%s", message); + break; + case OR2_LOG_STACK_TRACE: + case OR2_LOG_MF_TRACE: + case OR2_LOG_CAS_TRACE: + case OR2_LOG_DEBUG: + case OR2_LOG_EX_DEBUG: + zap_log(ZAP_LOG_DEBUG, "%s", message); + break; + default: + zap_log(ZAP_LOG_WARNING, "We should handle logging level %d here.\n", level); + zap_log(ZAP_LOG_DEBUG, "%s", message); + break; + } +} + +static void zap_r2_on_context_log(openr2_context_t *r2context, openr2_log_level_t level, const char *fmt, va_list ap) +{ +#define CONTEXT_TAG "Context -" + char logmsg[256]; + char completemsg[sizeof(logmsg) + sizeof(CONTEXT_TAG) - 1]; + vsnprintf(logmsg, sizeof(logmsg), fmt, ap); + snprintf(completemsg, sizeof(completemsg), CONTEXT_TAG "%s", logmsg); + zap_r2_write_log(level, completemsg); +#undef CONTEXT_TAG +} + +static void zap_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level, const char *fmt, va_list ap) +{ +#define CHAN_TAG "Chan " + char logmsg[256]; + char completemsg[sizeof(logmsg) + sizeof(CHAN_TAG) - 1]; + vsnprintf(logmsg, sizeof(logmsg), fmt, ap); + snprintf(completemsg, sizeof(completemsg), CHAN_TAG "%d: %s", openr2_chan_get_number(r2chan), logmsg); + zap_r2_write_log(level, completemsg); +#undef CHAN_TAG +} + +static int zap_r2_on_dnis_digit_received(openr2_chan_t *r2chan, char digit) +{ + zap_sigmsg_t sigev; + zap_r2_data_t *r2data; + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + zap_size_t collected_len = R2CALL(zchan)->dnis_index; + + zap_log(ZAP_LOG_DEBUG, "DNIS digit %d received chan %d\n", digit, openr2_chan_get_number(r2chan)); + + /* save the digit we just received */ + zchan->caller_data.dnis.digits[collected_len] = digit; + collected_len++; + zchan->caller_data.dnis.digits[collected_len] = '\0'; + R2CALL(zchan)->dnis_index = collected_len; + + /* notify the user about the new digit and check if we should stop requesting more DNIS */ + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = zchan->chan_id; + sigev.span_id = zchan->span_id; + sigev.channel = zchan; + sigev.event_id = ZAP_SIGEVENT_COLLECTED_DIGIT; + r2data = zchan->span->signal_data; + if (zap_span_send_signal(zchan->span, &sigev) == ZAP_BREAK) { + zap_log(ZAP_LOG_NOTICE, "Requested to stop getting DNIS. Current DNIS = %s on chan %d\n", zchan->caller_data.dnis.digits, openr2_chan_get_number(r2chan)); + return OR2_STOP_DNIS_REQUEST; + } + + /* the only other reason to stop requesting DNIS is that there is no more room to save it */ + if (collected_len == (sizeof(zchan->caller_data.dnis.digits) - 1)) { + zap_log(ZAP_LOG_NOTICE, "No more room for DNIS. Current DNIS = %s on chan %d\n", zchan->caller_data.dnis.digits, openr2_chan_get_number(r2chan)); + return OR2_STOP_DNIS_REQUEST; + } + + return OR2_CONTINUE_DNIS_REQUEST; +} + +static void zap_r2_on_ani_digit_received(openr2_chan_t *r2chan, char digit) +{ + zap_channel_t *zchan = openr2_chan_get_client_data(r2chan); + zap_size_t collected_len = R2CALL(zchan)->ani_index; + + /* check if we should drop ANI */ + if (collected_len == (sizeof(zchan->caller_data.ani.digits) - 1)) { + zap_log(ZAP_LOG_NOTICE, "No more room for ANI %c on chan %d, digit dropped.\n", digit, openr2_chan_get_number(r2chan)); + return; + } + zap_log(ZAP_LOG_DEBUG, "ANI digit %c received chan %d\n", digit, openr2_chan_get_number(r2chan)); + + /* save the digit we just received */ + zchan->caller_data.ani.digits[collected_len++] = digit; + zchan->caller_data.ani.digits[collected_len] = '\0'; +} + +static openr2_event_interface_t zap_r2_event_iface = { + .on_call_init = zap_r2_on_call_init, + .on_call_offered = zap_r2_on_call_offered, + .on_call_accepted = zap_r2_on_call_accepted, + .on_call_answered = zap_r2_on_call_answered, + .on_call_disconnect = zap_r2_on_call_disconnect, + .on_call_end = zap_r2_on_call_end, + .on_call_read = zap_r2_on_call_read, + .on_hardware_alarm = zap_r2_on_hardware_alarm, + .on_os_error = zap_r2_on_os_error, + .on_protocol_error = zap_r2_on_protocol_error, + .on_line_blocked = zap_r2_on_line_blocked, + .on_line_idle = zap_r2_on_line_idle, + /* cast seems to be needed to get rid of the annoying warning regarding format attribute */ + .on_context_log = (openr2_handle_context_logging_func)zap_r2_on_context_log, + .on_dnis_digit_received = zap_r2_on_dnis_digit_received, + .on_ani_digit_received = zap_r2_on_ani_digit_received, + /* so far we do nothing with billing pulses */ + .on_billing_pulse_received = NULL +}; + +static int zap_r2_io_set_cas(openr2_chan_t *r2chan, int cas) +{ + zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan); + zap_status_t status = zap_channel_command(zap_chan, ZAP_COMMAND_SET_CAS_BITS, &cas); + if (ZAP_FAIL == status) { + return -1; + } + return 0; +} + +static int zap_r2_io_get_cas(openr2_chan_t *r2chan, int *cas) +{ + zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan); + zap_status_t status = zap_channel_command(zap_chan, ZAP_COMMAND_GET_CAS_BITS, cas); + if (ZAP_FAIL == status) { + return -1; + } + return 0; +} + +static int zap_r2_io_flush_write_buffers(openr2_chan_t *r2chan) +{ + zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan); + zap_status_t status = zap_channel_command(zap_chan, ZAP_COMMAND_FLUSH_TX_BUFFERS, NULL); + if (ZAP_FAIL == status) { + return -1; + } + return 0; +} + +static int zap_r2_io_write(openr2_chan_t *r2chan, const void *buf, int size) +{ + zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan); + zap_size_t outsize = size; + zap_status_t status = zap_channel_write(zap_chan, (void *)buf, size, &outsize); + if (ZAP_FAIL == status) { + return -1; + } + return outsize; +} + +static int zap_r2_io_read(openr2_chan_t *r2chan, const void *buf, int size) +{ + zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan); + zap_size_t outsize = size; + zap_status_t status = zap_channel_read(zap_chan, (void *)buf, &outsize); + if (ZAP_FAIL == status) { + return -1; + } + return outsize; +} + +static int zap_r2_io_wait(openr2_chan_t *r2chan, int *flags, int block) +{ + zap_status_t status; + zap_wait_flag_t zapflags = 0; + + zap_channel_t *zap_chan = openr2_chan_get_fd(r2chan); + int32_t timeout = block ? -1 : 0; + + if (*flags & OR2_IO_READ) { + zapflags |= ZAP_READ; + } + if (*flags & OR2_IO_WRITE) { + zapflags |= ZAP_WRITE; + } + if (*flags & OR2_IO_OOB_EVENT) { + zapflags |= ZAP_EVENTS; + } + + status = zap_channel_wait(zap_chan, &zapflags, timeout); + + if (ZAP_SUCCESS != status) { + return -1; + } + + *flags = 0; + if (zapflags & ZAP_READ) { + *flags |= OR2_IO_READ; + } + if (zapflags & ZAP_WRITE) { + *flags |= OR2_IO_WRITE; + } + if (zapflags & ZAP_EVENTS) { + *flags |= OR2_IO_OOB_EVENT; + } + + return 0; +} + +/* The following openr2 hooks never get called, read on for reasoning ... */ +/* since openzap takes care of opening the file descriptor and using openr2_chan_new_from_fd, openr2 should never call this hook */ +static openr2_io_fd_t zap_r2_io_open(openr2_context_t *r2context, int channo) +{ + zap_log(ZAP_LOG_ERROR, "I should not be called (I/O open)!!\n"); + return NULL; +} + +/* since openzap takes care of closing the file descriptor and uses openr2_chan_new_from_fd, openr2 should never call this hook */ +static int zap_r2_io_close(openr2_chan_t *r2chan) +{ + zap_log(ZAP_LOG_ERROR, "I should not be called (I/O close)!!\n"); + return 0; +} + +/* since openzap takes care of opening the file descriptor and using openr2_chan_new_from_fd, openr2 should never call this hook */ +static int zap_r2_io_setup(openr2_chan_t *r2chan) +{ + zap_log(ZAP_LOG_ERROR, "I should not be called (I/O Setup)!!\n"); + return 0; +} + +/* since the signaling thread calls openr2_chan_process_cas_signaling directly, openr2 should never call this hook */ +static int zap_r2_io_get_oob_event(openr2_chan_t *r2chan, openr2_oob_event_t *event) +{ + *event = 0; + zap_log(ZAP_LOG_ERROR, "I should not be called (I/O get oob event)!!\n"); + return 0; +} + +static openr2_io_interface_t zap_r2_io_iface = { + .open = zap_r2_io_open, /* never called */ + .close = zap_r2_io_close, /* never called */ + .set_cas = zap_r2_io_set_cas, + .get_cas = zap_r2_io_get_cas, + .flush_write_buffers = zap_r2_io_flush_write_buffers, + .write = zap_r2_io_write, + .read = zap_r2_io_read, + .setup = zap_r2_io_setup, /* never called */ + .wait = zap_r2_io_wait, + .get_oob_event = zap_r2_io_get_oob_event /* never called */ +}; + +static ZIO_SIG_CONFIGURE_FUNCTION(zap_r2_configure_span) + //zap_status_t (zap_span_t *span, zio_signal_cb_t sig_cb, va_list ap) +{ + int i = 0; + int conf_failure = 0; + char *var = NULL; + char *val = NULL; + zap_r2_data_t *r2data = NULL; + zap_r2_span_pvt_t *spanpvt = NULL; + zap_r2_call_t *r2call = NULL; + openr2_chan_t *r2chan = NULL; + + assert(sig_cb != NULL); + + oz_r2_conf_t r2conf = + { + .variant = OR2_VAR_ITU, + .category = OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER, + .loglevel = OR2_LOG_ERROR | OR2_LOG_WARNING, + .max_ani = 10, + .max_dnis = 4, + .mfback_timeout = -1, + .metering_pulse_timeout = -1, + .allow_collect_calls = -1, + .immediate_accept = -1, + .skip_category = -1, + .forced_release = -1, + .charge_calls = -1, + .get_ani_first = -1, + .call_files = -1, + .logdir = NULL, + .advanced_protocol_file = NULL + }; + + + if (span->signal_type) { + snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling."); + return ZAP_FAIL; + } + + while ((var = va_arg(ap, char *))) { + zap_log(ZAP_LOG_DEBUG, "Reading R2 parameter %s for span %d\n", var, span->span_id); + if (!strcasecmp(var, "variant")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (zap_strlen_zero_buf(val)) { + zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 variant parameter\n"); + continue; + } + r2conf.variant = openr2_proto_get_variant(val); + if (r2conf.variant == OR2_VAR_UNKNOWN) { + zap_log(ZAP_LOG_ERROR, "Unknown R2 variant %s\n", val); + conf_failure = 1; + break; + } + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d for variant %s\n", span->span_id, val); + } else if (!strcasecmp(var, "category")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (zap_strlen_zero_buf(val)) { + zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 category parameter\n"); + continue; + } + r2conf.category = openr2_proto_get_category(val); + if (r2conf.category == OR2_CALLING_PARTY_CATEGORY_UNKNOWN) { + zap_log(ZAP_LOG_ERROR, "Unknown R2 caller category %s\n", val); + conf_failure = 1; + break; + } + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with default category %s\n", span->span_id, val); + } else if (!strcasecmp(var, "logdir")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (zap_strlen_zero_buf(val)) { + zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 logdir parameter\n"); + continue; + } + r2conf.logdir = val; + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with logdir %s\n", span->span_id, val); + } else if (!strcasecmp(var, "logging")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (zap_strlen_zero_buf(val)) { + zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 logging parameter\n"); + continue; + } + openr2_log_level_t tmplevel; + char *clevel; + char *logval = malloc(strlen(val)+1); /* alloca man page scared me, so better to use good ol' malloc */ + if (!logval) { + zap_log(ZAP_LOG_WARNING, "Ignoring R2 logging parameter: '%s', failed to alloc memory\n", val); + continue; + } + strcpy(logval, val); + while (logval) { + clevel = strsep(&logval, ","); + if (-1 == (tmplevel = openr2_log_get_level(clevel))) { + zap_log(ZAP_LOG_WARNING, "Ignoring invalid R2 logging level: '%s'\n", clevel); + continue; + } + r2conf.loglevel |= tmplevel; + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with loglevel %s\n", span->span_id, clevel); + } + free(logval); + } else if (!strcasecmp(var, "advanced_protocol_file")) { + if (!(val = va_arg(ap, char *))) { + break; + } + if (zap_strlen_zero_buf(val)) { + zap_log(ZAP_LOG_NOTICE, "Ignoring empty R2 advanced_protocol_file parameter\n"); + continue; + } + r2conf.advanced_protocol_file = val; + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with advanced protocol file %s\n", span->span_id, val); + } else if (!strcasecmp(var, "allow_collect_calls")) { + r2conf.allow_collect_calls = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with allow collect calls max ani = %d\n", span->span_id, r2conf.allow_collect_calls); + } else if (!strcasecmp(var, "double_answer")) { + r2conf.double_answer = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with double answer = %d\n", span->span_id, r2conf.double_answer); + } else if (!strcasecmp(var, "immediate_accept")) { + r2conf.immediate_accept = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with immediate accept = %d\n", span->span_id, r2conf.immediate_accept); + } else if (!strcasecmp(var, "skip_category")) { + r2conf.skip_category = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with skip category = %d\n", span->span_id, r2conf.skip_category); + } else if (!strcasecmp(var, "forced_release")) { + r2conf.forced_release = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with forced release = %d\n", span->span_id, r2conf.forced_release); + } else if (!strcasecmp(var, "charge_calls")) { + r2conf.charge_calls = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with charge calls = %d\n", span->span_id, r2conf.charge_calls); + } else if (!strcasecmp(var, "get_ani_first")) { + r2conf.get_ani_first = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with get ani first = %d\n", span->span_id, r2conf.get_ani_first); + } else if (!strcasecmp(var, "call_files")) { + r2conf.call_files = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with call files = %d\n", span->span_id, r2conf.call_files); + } else if (!strcasecmp(var, "mfback_timeout")) { + r2conf.mfback_timeout = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with MF backward timeout = %dms\n", span->span_id, r2conf.mfback_timeout); + } else if (!strcasecmp(var, "metering_pulse_timeout")) { + r2conf.metering_pulse_timeout = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with metering pulse timeout = %dms\n", span->span_id, r2conf.metering_pulse_timeout); + } else if (!strcasecmp(var, "max_ani")) { + r2conf.max_ani = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with max ani = %d\n", span->span_id, r2conf.max_ani); + } else if (!strcasecmp(var, "max_dnis")) { + r2conf.max_dnis = va_arg(ap, int); + zap_log(ZAP_LOG_DEBUG, "Configuring R2 span %d with max dnis = %d\n", span->span_id, r2conf.max_dnis); + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var); + return ZAP_FAIL; + } + } + + if (conf_failure) { + snprintf(span->last_error, sizeof(span->last_error), "R2 configuration error"); + return ZAP_FAIL; + } + + r2data = malloc(sizeof(*r2data)); + if (!r2data) { + snprintf(span->last_error, sizeof(span->last_error), "Failed to allocate R2 data."); + return ZAP_FAIL; + } + memset(r2data, 0, sizeof(*r2data)); + + spanpvt = malloc(sizeof(*spanpvt)); + if (!spanpvt) { + snprintf(span->last_error, sizeof(span->last_error), "Failed to allocate private span data container."); + goto fail; + } + memset(spanpvt, 0, sizeof(*spanpvt)); + + r2data->r2context = openr2_context_new(r2conf.variant, &zap_r2_event_iface, r2conf.max_ani, r2conf.max_dnis); + if (!r2data->r2context) { + snprintf(span->last_error, sizeof(span->last_error), "Cannot create openr2 context for span."); + goto fail; + } + openr2_context_set_io_type(r2data->r2context, OR2_IO_CUSTOM, &zap_r2_io_iface); + openr2_context_set_log_level(r2data->r2context, r2conf.loglevel); + openr2_context_set_ani_first(r2data->r2context, r2conf.get_ani_first); + openr2_context_set_skip_category_request(r2data->r2context, r2conf.skip_category); + openr2_context_set_mf_back_timeout(r2data->r2context, r2conf.mfback_timeout); + openr2_context_set_metering_pulse_timeout(r2data->r2context, r2conf.metering_pulse_timeout); + openr2_context_set_double_answer(r2data->r2context, r2conf.double_answer); + openr2_context_set_immediate_accept(r2data->r2context, r2conf.immediate_accept); + if (r2conf.logdir) { + openr2_context_set_log_directory(r2data->r2context, r2conf.logdir); + } + if (r2conf.advanced_protocol_file) { + openr2_context_configure_from_advanced_file(r2data->r2context, r2conf.advanced_protocol_file); + } + + spanpvt->r2calls = create_hashtable(ZAP_MAX_CHANNELS_SPAN, zap_hash_hashfromstring, zap_hash_equalkeys); + if (!spanpvt->r2calls) { + snprintf(span->last_error, sizeof(span->last_error), "Cannot create channel calls hash for span."); + goto fail; + } + + for (i = 1; (i <= span->chan_count) && (i <= ZAP_MAX_CHANNELS_SPAN); i++) { + r2chan = openr2_chan_new_from_fd(r2data->r2context, span->channels[i], span->channels[i]->physical_chan_id); + if (!r2chan) { + snprintf(span->last_error, sizeof(span->last_error), "Cannot create all openr2 channels for span."); + goto fail; + } + if (r2conf.call_files) { + openr2_chan_enable_call_files(r2chan); + openr2_chan_set_log_level(r2chan, r2conf.loglevel); + } + + r2call = malloc(sizeof(*r2call)); + if (!r2call) { + snprintf(span->last_error, sizeof(span->last_error), "Cannot create all R2 call data structures for the span."); + zap_safe_free(r2chan); + goto fail; + } + memset(r2call, 0, sizeof(*r2call)); + openr2_chan_set_logging_func(r2chan, zap_r2_on_chan_log); + openr2_chan_set_client_data(r2chan, span->channels[i]); + r2call->r2chan = r2chan; + span->channels[i]->call_data = r2call; + /* value and key are the same so just free one of them */ + snprintf(r2call->name, sizeof(r2call->name), "chancall%d", i); + hashtable_insert(spanpvt->r2calls, (void *)r2call->name, r2call, HASHTABLE_FLAG_FREE_VALUE); + + } + spanpvt->r2context = r2data->r2context; + + /* just the value must be freed by the hash */ + hashtable_insert(g_mod_data_hash, (void *)span->name, spanpvt, HASHTABLE_FLAG_FREE_VALUE); + + span->start = zap_r2_start; + r2data->flags = 0; + span->signal_cb = sig_cb; + span->signal_type = ZAP_SIGTYPE_R2; + span->signal_data = r2data; + span->outgoing_call = r2_outgoing_call; + + return ZAP_SUCCESS; + +fail: + + if (r2data && r2data->r2context) { + openr2_context_delete(r2data->r2context); + } + if (spanpvt && spanpvt->r2calls) { + hashtable_destroy(spanpvt->r2calls); + } + zap_safe_free(r2data); + zap_safe_free(spanpvt); + return ZAP_FAIL; + +} + +static void *zap_r2_channel_run(zap_thread_t *me, void *obj) +{ + zap_channel_t *closed_chan; + uint32_t interval = 0; + zap_sigmsg_t sigev; + zap_channel_t *zchan = (zap_channel_t *)obj; + openr2_chan_t *r2chan = R2CALL(zchan)->r2chan; + + zap_set_flag_locked(zchan, ZAP_CHANNEL_INTHREAD); + + zap_mutex_lock(g_thread_count_mutex); + g_thread_count++; + zap_mutex_unlock(g_thread_count_mutex); + + zap_log(ZAP_LOG_DEBUG, "R2 CHANNEL thread starting on %d in state %s.\n", + zchan->physical_chan_id, + zap_channel_state2str(zchan->state)); + + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "OPEN ERROR [%s]\n", zchan->last_error); + goto endthread; + } + + zap_channel_command(zchan, ZAP_COMMAND_GET_INTERVAL, &interval); + + assert(interval != 0); + zap_log(ZAP_LOG_DEBUG, "Got %d interval for chan %d\n", interval, zchan->physical_chan_id); + + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + /* FIXME: is this needed? */ + memset(zchan->caller_data.dnis.digits, 0, sizeof(zchan->caller_data.collected)); + memset(zchan->caller_data.ani.digits, 0, sizeof(zchan->caller_data.collected)); + } + + memset(&sigev, 0, sizeof(sigev)); + sigev.chan_id = zchan->chan_id; + sigev.span_id = zchan->span_id; + sigev.channel = zchan; + + while (zap_running()) { + int32_t read_enabled = openr2_chan_get_read_enabled(r2chan); + zap_wait_flag_t flags = read_enabled ? ( ZAP_READ | ZAP_WRITE ) : 0; + + if (zap_test_flag(zchan, ZAP_CHANNEL_STATE_CHANGE) && (R2CALL(zchan)->chanstate != zchan->state)) { + + zap_log(ZAP_LOG_DEBUG, "Executing state handler on %d:%d for %s\n", zchan->span_id, zchan->chan_id, zap_channel_state2str(zchan->state)); + R2CALL(zchan)->chanstate = zchan->state; + + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND) && !R2CALL(zchan)->accepted && + (zchan->state == ZAP_CHANNEL_STATE_PROGRESS || + zchan->state == ZAP_CHANNEL_STATE_PROGRESS_MEDIA || + zchan->state == ZAP_CHANNEL_STATE_UP) ) { + /* if an accept ack will be required we should not acknowledge the state change just yet, + it will be done below after processing the MF signals, otherwise we have a race condition between openzap calling + openr2_chan_answer_call and openr2 accepting the call first, if openzap calls openr2_chan_answer_call before the accept cycle + completes, openr2 will fail to answer the call */ + zap_log(ZAP_LOG_DEBUG, "State ack in chan %d:%d for state %s will have to wait a bit\n", zchan->span_id, zchan->chan_id, zap_channel_state2str(zchan->state)); + } else if (zchan->state != ZAP_CHANNEL_STATE_DOWN){ + /* the down state will be completed in zap_channel_done below */ + zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE); + zap_channel_complete_state(zchan); + } + + switch (zchan->state) { + + /* starting an incoming call */ + case ZAP_CHANNEL_STATE_COLLECT: + { + zap_log(ZAP_LOG_DEBUG, "COLLECT: Starting processing of incoming call in channel %d with interval %d\n", zchan->physical_chan_id, interval); + } + break; + + /* starting an outgoing call */ + case ZAP_CHANNEL_STATE_DIALING: + { + // FIXME: use user defined calling party + zap_channel_use(zchan); + zap_log(ZAP_LOG_DEBUG, "DIALING: Starting processing of outgoing call in channel %d with interval %d\n", zchan->physical_chan_id, interval); + if (openr2_chan_make_call(r2chan, zchan->caller_data.cid_num.digits, zchan->caller_data.ani.digits, OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER)) { + zap_log(ZAP_LOG_ERROR, "%d:%d Failed to make call in R2 channel, openr2_chan_make_call failed\n", zchan->span_id, zchan->chan_id); + zchan->caller_data.hangup_cause = ZAP_CAUSE_DESTINATION_OUT_OF_ORDER; + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } + break; + + /* the call is ringing */ + case ZAP_CHANNEL_STATE_PROGRESS: + case ZAP_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + if (!R2CALL(zchan)->accepted) { + zap_log(ZAP_LOG_DEBUG, "PROGRESS: Accepting call on channel %d\n", zchan->physical_chan_id); + oz_r2_accept_call(zchan); + } + } else { + zap_log(ZAP_LOG_DEBUG, "PROGRESS: Notifying progress in channel %d\n", zchan->physical_chan_id); + sigev.event_id = ZAP_SIGEVENT_PROGRESS; + if (zap_span_send_signal(zchan->span, &sigev) != ZAP_SUCCESS) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } + } + break; + + /* the call was answered */ + case ZAP_CHANNEL_STATE_UP: + { + zap_log(ZAP_LOG_DEBUG, "UP: Call was answered on channel %d\n", zchan->physical_chan_id); + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + if (!R2CALL(zchan)->accepted) { + zap_log(ZAP_LOG_DEBUG, "UP: Call has not been accepted, need to accept first\n"); + // the answering will be done in the on_call_accepted handler + oz_r2_accept_call(zchan); + R2CALL(zchan)->answer_pending = 1; + } else { + oz_r2_answer_call(zchan); + } + } else { + zap_log(ZAP_LOG_DEBUG, "UP: Notifying of call answered in channel %d\n", zchan->physical_chan_id); + sigev.event_id = ZAP_SIGEVENT_UP; + if (zap_span_send_signal(zchan->span, &sigev) != ZAP_SUCCESS) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } + } + break; + + /* just got hangup */ + case ZAP_CHANNEL_STATE_HANGUP: + { + /* FIXME: the cause should be retrieved from zchan->caller_data.hangup_cause and translated from Q931 to R2 cause */ + zap_log(ZAP_LOG_DEBUG, "HANGUP: Clearing call on channel %d\n", zchan->physical_chan_id); + if (!R2CALL(zchan)->disconnect_rcvd) { + /* this will disconnect the call, but need to wait for the call end before moving to DOWN */ + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING); + } else { + /* at this point on_call_end possibly was already called, + * but we needed to wait for the openzap confirmation before moving to DOWN */ + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + + /* just got hangup from the openzap side due to abnormal failure */ + case ZAP_CHANNEL_STATE_CANCEL: + { + zap_log(ZAP_LOG_DEBUG, "CANCEL: Unable to receive call on channel %d\n", zchan->physical_chan_id); + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_OUT_OF_ORDER); + } + break; + + /* finished call for good */ + case ZAP_CHANNEL_STATE_DOWN: + { + zap_log(ZAP_LOG_DEBUG, "DOWN: Placing channel %d back to the pool of available channels\n", zchan->physical_chan_id); + zap_channel_done(zchan); + goto endthread; + } + break; + + default: + { + zap_log(ZAP_LOG_ERROR, "%s: Unhandled channel state change in channel %d\n", zap_channel_state2str(zchan->state), zchan->physical_chan_id); + } + break; + + } + } + + if (flags) { + if (zap_channel_wait(zchan, &flags, interval * 2) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_DEBUG, "zap_channel_wait did not return ZAP_SUCCESS\n"); + continue; + } + + /* handle timeout events first if any */ + openr2_chan_run_schedule(r2chan); + + /* openr2 will now try to detect MF tones, make sense out of them, reply if necessary with another tone and trigger + * telephony events via the call event interface we provided when creating the R2 context. + * openr2 will also call our I/O callbacks to retrieve audio from the channel and call our wait poll I/O registered callback + * and will not return from this function until the I/O poll callback returns no pending events + * */ + openr2_chan_process_mf_signaling(r2chan); + if (R2CALL(zchan)->state_ack_pending) { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE); + zap_channel_complete_state(zchan); + R2CALL(zchan)->state_ack_pending = 0; + } + } else { + /* once the MF signaling has end we just loop here waiting for state changes */ + zap_sleep(interval); + } + + } + +endthread: + + closed_chan = zchan; + zap_channel_close(&closed_chan); + zap_clear_flag(zchan, ZAP_CHANNEL_INTHREAD); + zap_log(ZAP_LOG_DEBUG, "R2 channel %d thread ended.\n", zchan->physical_chan_id); + + zap_mutex_lock(g_thread_count_mutex); + g_thread_count--; + zap_mutex_unlock(g_thread_count_mutex); + + return NULL; +} + +static void *zap_r2_run(zap_thread_t *me, void *obj) +{ + openr2_chan_t *r2chan; + zap_status_t status; + zap_span_t *span = (zap_span_t *) obj; + zap_r2_data_t *r2data = span->signal_data; + int waitms = 1000; + int i; + + zap_log(ZAP_LOG_DEBUG, "OpenR2 monitor thread started.\n"); + r2chan = NULL; + for (i = 1; i <= span->chan_count; i++) { + r2chan = R2CALL(span->channels[i])->r2chan; + openr2_chan_set_idle(r2chan); + openr2_chan_process_cas_signaling(r2chan); + } + + while (zap_running() && zap_test_flag(r2data, ZAP_R2_RUNNING)) { + status = zap_span_poll_event(span, waitms); + if (ZAP_FAIL == status) { + zap_log(ZAP_LOG_ERROR, "Failure Polling event! [%s]\n", span->last_error); + continue; + } + if (ZAP_SUCCESS == status) { + zap_event_t *event; + while (zap_span_next_event(span, &event) == ZAP_SUCCESS) { + if (event->enum_id == ZAP_OOB_CAS_BITS_CHANGE) { + r2chan = R2CALL(event->channel)->r2chan; + zap_log(ZAP_LOG_DEBUG, "Handling CAS on channel %d.\n", openr2_chan_get_number(r2chan)); + // we only expect CAS and other OOB events on this thread/loop, once a call is started + // the MF events (in-band signaling) are handled in the call thread + openr2_chan_process_cas_signaling(r2chan); + } else { + zap_log(ZAP_LOG_DEBUG, "Ignoring event %d on channel %d.\n", event->enum_id, openr2_chan_get_number(r2chan)); + // XXX TODO: handle alarms here XXX + } + } + } else if (status != ZAP_TIMEOUT) { + zap_log(ZAP_LOG_ERROR, "zap_span_poll_event returned %d.\n", status); + } else { + //zap_log(ZAP_LOG_DEBUG, "timed out waiting for event on span %d\n", span->span_id); + } + } + + /* + FIXME: we should set BLOCKED but at this point I/O routines of openzap caused segfault + for (i = 1; i <= span->chan_count; i++) { + r2chan = R2CALL(span->channels[i])->r2chan; + openr2_chan_set_blocked(r2chan); + } + */ + + zap_clear_flag(r2data, ZAP_R2_RUNNING); + zap_log(ZAP_LOG_DEBUG, "R2 thread ending.\n"); + + return NULL; + +} + +static ZIO_API_FUNCTION(zap_r2_api) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + + if (data) { + mycmd = strdup(data); + argc = zap_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (argc == 2) { + if (!strcasecmp(argv[0], "kill")) { + int span_id = atoi(argv[1]); + zap_span_t *span = NULL; + + if (zap_span_find_by_name(argv[1], &span) == ZAP_SUCCESS || zap_span_find(span_id, &span) == ZAP_SUCCESS) { + zap_r2_data_t *r2data = span->signal_data; + + if (span->start != zap_r2_start) { + stream->write_function(stream, "-ERR invalid span.\n"); + goto done; + } + + zap_clear_flag(r2data, ZAP_R2_RUNNING); + stream->write_function(stream, "+OK killed.\n"); + goto done; + } else { + stream->write_function(stream, "-ERR invalid span.\n"); + goto done; + } + } + + if (!strcasecmp(argv[0], "status")) { + int span_id = atoi(argv[1]); + zap_r2_data_t *r2data = NULL; + zap_span_t *span = NULL; + openr2_chan_t *r2chan = NULL; + openr2_context_t *r2context = NULL; + int i = 0; + + if (zap_span_find_by_name(argv[1], &span) == ZAP_SUCCESS || zap_span_find(span_id, &span) == ZAP_SUCCESS) { + if (span->start != zap_r2_start) { + stream->write_function(stream, "-ERR not an R2 span.\n"); + goto done; + } + if (!(r2data = span->signal_data)) { + stream->write_function(stream, "-ERR invalid span. No R2 singal data in span.\n"); + goto done; + } + r2context = r2data->r2context; + openr2_variant_t r2variant = openr2_context_get_variant(r2context); + stream->write_function(stream, + "Variant: %s\n" + "Max ANI: %d\n" + "Max DNIS: %d\n" + "ANI First: %s\n" + "Immediate Accept: %s\n", + openr2_proto_get_variant_string(r2variant), + openr2_context_get_max_ani(r2context), + openr2_context_get_max_dnis(r2context), + openr2_context_get_ani_first(r2context) ? "Yes" : "No", + openr2_context_get_immediate_accept(r2context) ? "Yes" : "No"); + stream->write_function(stream, "\n"); + stream->write_function(stream, "%4s %-12.12s %-12.12s\n", "Channel", "Tx CAS", "Rx CAS"); + for (i = 1; i <= span->chan_count; i++) { + if (i == 16) continue; + r2chan = R2CALL(span->channels[i])->r2chan; + stream->write_function(stream, "%4d %-12.12s %-12.12s\n", + span->channels[i]->physical_chan_id, + openr2_chan_get_tx_cas_string(r2chan), + openr2_chan_get_rx_cas_string(r2chan)); + } + stream->write_function(stream, "\n"); + stream->write_function(stream, "+OK.\n"); + goto done; + } else { + stream->write_function(stream, "-ERR invalid span.\n"); + goto done; + } + } + + } + + if (argc == 1) { + if (!strcasecmp(argv[0], "threads")) { + zap_mutex_lock(g_thread_count_mutex); + stream->write_function(stream, "%d R2 channel threads up\n", g_thread_count); + zap_mutex_unlock(g_thread_count_mutex); + stream->write_function(stream, "+OK.\n"); + goto done; + } + + if (!strcasecmp(argv[0], "version")) { + stream->write_function(stream, "OpenR2 version: %s, revision: %s\n", openr2_get_version(), openr2_get_revision()); + stream->write_function(stream, "+OK.\n"); + goto done; + } + + if (!strcasecmp(argv[0], "variants")) { + int32_t numvariants = 0; + const openr2_variant_entry_t *variants = openr2_proto_get_variant_list(&numvariants); + if (!variants) { + stream->write_function(stream, "-ERR failed to retrieve openr2 variant list.\n"); + goto done; + } +#define VARIANT_FORMAT "%4s %40s\n" + stream->write_function(stream, VARIANT_FORMAT, "Variant Code", "Country"); + numvariants--; + for (; numvariants; numvariants--) { + stream->write_function(stream, VARIANT_FORMAT, variants[numvariants].name, variants[numvariants].country); + } + stream->write_function(stream, "+OK.\n"); +#undef VARIANT_FORMAT + goto done; + } + } + + stream->write_function(stream, "-ERR invalid command.\n"); + +done: + + zap_safe_free(mycmd); + + return ZAP_SUCCESS; + +} + +static ZIO_IO_LOAD_FUNCTION(zap_r2_io_init) +{ + assert(zio != NULL); + memset(&g_zap_r2_interface, 0, sizeof(g_zap_r2_interface)); + + g_zap_r2_interface.name = "r2"; + g_zap_r2_interface.api = zap_r2_api; + + *zio = &g_zap_r2_interface; + + return ZAP_SUCCESS; +} + +static ZIO_SIG_LOAD_FUNCTION(zap_r2_init) +{ + g_mod_data_hash = create_hashtable(10, zap_hash_hashfromstring, zap_hash_equalkeys); + if (!g_mod_data_hash) { + return ZAP_FAIL; + } + zap_mutex_create(&g_thread_count_mutex); + return ZAP_SUCCESS; +} + +static ZIO_SIG_UNLOAD_FUNCTION(zap_r2_destroy) +{ + zap_hash_iterator_t *i = NULL; + zap_r2_span_pvt_t *spanpvt = NULL; + const void *key = NULL; + void *val = NULL; + for (i = hashtable_first(g_mod_data_hash); i; i = hashtable_next(i)) { + hashtable_this(i, &key, NULL, &val); + if (key && val) { + spanpvt = val; + openr2_context_delete(spanpvt->r2context); + hashtable_destroy(spanpvt->r2calls); + } + } + hashtable_destroy(g_mod_data_hash); + zap_mutex_destroy(&g_thread_count_mutex); + return ZAP_SUCCESS; +} + +zap_module_t zap_module = { + "r2", + zap_r2_io_init, + NULL, + zap_r2_init, + zap_r2_configure_span, + zap_r2_destroy +}; + + +/* 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 + */ diff --git a/libs/openzap/src/ozmod/ozmod_sangoma_boost/ozmod_sangoma_boost.c b/libs/openzap/src/ozmod/ozmod_sangoma_boost/ozmod_sangoma_boost.c new file mode 100644 index 0000000000..5e0e32d9ee --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_sangoma_boost/ozmod_sangoma_boost.c @@ -0,0 +1,1768 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "openzap.h" +#include "sangoma_boost_client.h" +#include "zap_sangoma_boost.h" +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#define MAX_TRUNK_GROUPS 64 +static time_t congestion_timeouts[MAX_TRUNK_GROUPS]; + +/** + * \brief Strange flag + */ +typedef enum { + SFLAG_FREE_REQ_ID = (1 << 0), + SFLAG_SENT_FINAL_MSG = (1 << 1), + SFLAG_SENT_ACK = (1 << 2), + SFLAG_RECVD_ACK = (1 << 3), + SFLAG_HANGUP = (1 << 4), + SFLAG_TERMINATING = (1 << 5) +} sflag_t; + +typedef uint16_t sangoma_boost_request_id_t; + +/** + * \brief SANGOMA boost request status + */ +typedef enum { + BST_FREE, + BST_WAITING, + BST_ACK, + BST_READY, + BST_FAIL +} sangoma_boost_request_status_t; + +/** + * \brief SANGOMA boost request structure + */ +typedef struct { + sangoma_boost_request_status_t status; + sangomabc_short_event_t event; + zap_span_t *span; + zap_channel_t *zchan; + int hangup_cause; + int flags; +} sangoma_boost_request_t; + +//#define MAX_REQ_ID ZAP_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN * ZAP_MAX_CHANNELS_PHYSICAL_SPAN +#define MAX_REQ_ID 6000 + +static uint16_t SETUP_GRID[ZAP_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN+1][ZAP_MAX_CHANNELS_PHYSICAL_SPAN+1] = {{ 0 }}; + +static sangoma_boost_request_t OUTBOUND_REQUESTS[MAX_REQ_ID+1] = {{ 0 }}; + +static zap_mutex_t *request_mutex = NULL; + +static uint8_t req_map[MAX_REQ_ID+1] = { 0 }; +static uint8_t nack_map[MAX_REQ_ID+1] = { 0 }; + +/** + * \brief Releases span and channel from setup grid + * \param span Span number + * \param chan Channel number + * \param func Calling function + * \param line Line number on request + * \return NULL if not found, channel otherwise + */ +static void __release_request_id_span_chan(int span, int chan, const char *func, int line) +{ + int id; + + zap_mutex_lock(request_mutex); + if ((id = SETUP_GRID[span][chan])) { + assert(id <= MAX_REQ_ID); + req_map[id] = 0; + SETUP_GRID[span][chan] = 0; + } + zap_mutex_unlock(request_mutex); +} +#define release_request_id_span_chan(s, c) __release_request_id_span_chan(s, c, __FUNCTION__, __LINE__) + +/** + * \brief Releases request ID + * \param func Calling function + * \param line Line number on request + * \return NULL if not found, channel otherwise + */ +static void __release_request_id(sangoma_boost_request_id_t r, const char *func, int line) +{ + assert(r <= MAX_REQ_ID); + zap_mutex_lock(request_mutex); + req_map[r] = 0; + zap_mutex_unlock(request_mutex); +} +#define release_request_id(r) __release_request_id(r, __FUNCTION__, __LINE__) + +static sangoma_boost_request_id_t last_req = 0; + +/** + * \brief Gets the first available tank request ID + * \param func Calling function + * \param line Line number on request + * \return 0 on failure, request ID on success + */ +static sangoma_boost_request_id_t __next_request_id(const char *func, int line) +{ + sangoma_boost_request_id_t r = 0, i = 0; + int found=0; + + zap_mutex_lock(request_mutex); + //r = ++last_req; + //while(!r || req_map[r]) { + + for (i=1; i<= MAX_REQ_ID; i++){ + r = ++last_req; + + if (r >= MAX_REQ_ID) { + r = i = last_req = 1; + } + + if (req_map[r]) { + /* Busy find another */ + continue; + + } + + req_map[r] = 1; + found=1; + break; + + } + + zap_mutex_unlock(request_mutex); + + if (!found) { + return 0; + } + + return r; +} +#define next_request_id() __next_request_id(__FUNCTION__, __LINE__) + +/** + * \brief Finds the channel that triggered an event + * \param span Span where to search the channel + * \param event SANGOMA event + * \param force Do not wait for the channel to be available if in use + * \return NULL if not found, channel otherwise + */ +static zap_channel_t *find_zchan(zap_span_t *span, sangomabc_short_event_t *event, int force) +{ + int i; + zap_channel_t *zchan = NULL; + + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->physical_span_id == event->span+1 && span->channels[i]->physical_chan_id == event->chan+1) { + zchan = span->channels[i]; + if (force || (zchan->state == ZAP_CHANNEL_STATE_DOWN && !zap_test_flag(zchan, ZAP_CHANNEL_INUSE))) { + break; + } else { + zchan = NULL; + zap_log(ZAP_LOG_DEBUG, "Channel %d:%d ~ %d:%d is already in use.\n", + span->channels[i]->span_id, + span->channels[i]->chan_id, + span->channels[i]->physical_span_id, + span->channels[i]->physical_chan_id + ); + break; + } + } + } + + return zchan; +} + +static int check_congestion(int trunk_group) +{ + if (congestion_timeouts[trunk_group]) { + time_t now = time(NULL); + + if (now >= congestion_timeouts[trunk_group]) { + congestion_timeouts[trunk_group] = 0; + } else { + return 1; + } + } + + return 0; +} + + +/** + * \brief Requests an sangoma boost channel on a span (outgoing call) + * \param span Span where to get a channel + * \param chan_id Specific channel to get (0 for any) + * \param direction Call direction + * \param caller_data Caller information + * \param zchan Channel to initialise + * \return Success or failure + */ +static ZIO_CHANNEL_REQUEST_FUNCTION(sangoma_boost_channel_request) +{ + zap_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + zap_status_t status = ZAP_FAIL; + sangoma_boost_request_id_t r; + sangomabc_event_t event = {0}; + + /* sanity has to be more than 8 seconds. + * In PRI specs, timeout is 4 seconds for remote switch to respond to a SETUP, + * and PRI stack will retransmit a second SETUP after the first timeout, so + * we should allow for at least 8 seconds */ + + int sanity = 10000; + sangoma_boost_request_status_t st; + char ani[128] = ""; + char *gr = NULL; + uint32_t count = 0; + int tg=0; + + if (zap_test_flag(span, ZAP_SPAN_SUSPENDED)) { + zap_log(ZAP_LOG_CRIT, "SPAN is not online.\n"); + *zchan = NULL; + return ZAP_FAIL; + } + + zap_set_string(ani, caller_data->ani.digits); + + if ((gr = strchr(ani, '@'))) { + *gr++ = '\0'; + } + + if (gr && *(gr+1)) { + tg = atoi(gr+1); + if (tg > 0) { + tg--; + } + } + event.trunk_group = tg; + + if (check_congestion(tg)) { + zap_log(ZAP_LOG_CRIT, "All circuits are busy. Trunk Group=%i (BOOST REQUESTED BACK OFF)\n",tg+1); + *zchan = NULL; + return ZAP_FAIL; + } + + zap_span_channel_use_count(span, &count); + + if (count >= span->chan_count) { + zap_log(ZAP_LOG_CRIT, "All circuits are busy.\n"); + *zchan = NULL; + return ZAP_FAIL; + } + + r = next_request_id(); + if (r == 0) { + zap_log(ZAP_LOG_CRIT, "All tanks ids are busy.\n"); + *zchan = NULL; + return ZAP_FAIL; + } + + /* sangomabc_call_init (event, calling, called, setup_id) */ + sangomabc_call_init(&event, caller_data->cid_num.digits, ani, r); + //sangoma_bc_call_init will clear the trunk_group val so we need to set it again + event.trunk_group=tg; + + if (gr && *(gr+1)) { + + switch(*gr) { + case 'g': + event.hunt_group = SIGBOOST_HUNTGRP_SEQ_ASC; + break; + case 'G': + event.hunt_group = SIGBOOST_HUNTGRP_SEQ_DESC; + break; + case 'r': + event.hunt_group = SIGBOOST_HUNTGRP_RR_ASC; + break; + case 'R': + event.hunt_group = SIGBOOST_HUNTGRP_RR_DESC; + break; + default: + zap_log(ZAP_LOG_WARNING, "Failed to determine huntgroup (%s)\n", gr); + event.hunt_group = SIGBOOST_HUNTGRP_SEQ_ASC; + } + } + + zap_set_string(event.calling_name, caller_data->cid_name); + zap_set_string(event.rdnis.digits, caller_data->rdnis.digits); + + if (strlen(caller_data->rdnis.digits)) { + event.rdnis.digits_count = strlen(caller_data->rdnis.digits)+1; + event.rdnis.ton = caller_data->rdnis.type; + event.rdnis.npi = caller_data->rdnis.plan; + } + + event.calling.screening_ind = caller_data->screen; + event.calling.presentation_ind = caller_data->pres; + + event.calling.ton = caller_data->cid_num.type; + event.calling.npi = caller_data->cid_num.plan; + + event.called.ton = caller_data->ani.type; + event.called.npi = caller_data->ani.plan; + + OUTBOUND_REQUESTS[r].status = BST_WAITING; + OUTBOUND_REQUESTS[r].span = span; + + if (sangomabc_connection_write(&sangoma_boost_data->mcon, &event) <= 0) { + zap_log(ZAP_LOG_CRIT, "Failed to tx on ISUP socket [%s]\n", strerror(errno)); + status = ZAP_FAIL; + *zchan = NULL; + goto done; + } + + while(zap_running() && OUTBOUND_REQUESTS[r].status == BST_WAITING) { + zap_sleep(1); + if (--sanity <= 0) { + status = ZAP_FAIL; + *zchan = NULL; + goto done; + } + } + + if (OUTBOUND_REQUESTS[r].status == BST_READY && OUTBOUND_REQUESTS[r].zchan) { + *zchan = OUTBOUND_REQUESTS[r].zchan; + status = ZAP_SUCCESS; + } else { + status = ZAP_FAIL; + *zchan = NULL; + } + + done: + + st = OUTBOUND_REQUESTS[r].status; + OUTBOUND_REQUESTS[r].status = BST_FREE; + + if (status == ZAP_FAIL) { + if (st == BST_FAIL) { + caller_data->hangup_cause = OUTBOUND_REQUESTS[r].hangup_cause; + } else { + caller_data->hangup_cause = ZAP_CAUSE_RECOVERY_ON_TIMER_EXPIRE; + } + } + + if (st == BST_FAIL) { + release_request_id(r); + } else if (st != BST_READY) { + assert(r <= MAX_REQ_ID); + nack_map[r] = 1; + sangomabc_exec_command(&sangoma_boost_data->mcon, + 0, + 0, + r, + SIGBOOST_EVENT_CALL_START_NACK, + 0, + 0); + } + + return status; +} + +/** + * \brief Starts an sangoma boost channel (outgoing call) + * \param zchan Channel to initiate call on + * \return Success + */ +static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(sangoma_boost_outgoing_call) +{ + zap_status_t status = ZAP_SUCCESS; + + return status; +} + +/** + * \brief Handler for call start ack no media event + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_progress(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_channel_t *zchan; + + + if ((zchan = find_zchan(span, event, 1))) { + zap_mutex_lock(zchan->mutex); + if (zchan->state == ZAP_CHANNEL_STATE_HOLD) { + if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { + zchan->init_state = ZAP_CHANNEL_STATE_PROGRESS_MEDIA; + zap_log(ZAP_LOG_DEBUG, "Channel init state updated to PROGRESS_MEDIA [Csid:%d]\n", event->call_setup_id); + } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { + zchan->init_state = ZAP_CHANNEL_STATE_PROGRESS; + zap_log(ZAP_LOG_DEBUG, "Channel init state updated to PROGRESS [Csid:%d]\n", event->call_setup_id); + } else { + zchan->init_state = ZAP_CHANNEL_STATE_IDLE; + zap_log(ZAP_LOG_DEBUG, "Channel init state updated to IDLE [Csid:%d]\n", event->call_setup_id); + } + } else { + if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS_MEDIA); + } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_PROGRESS); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_IDLE); + } + } + zap_mutex_unlock(zchan->mutex); + } +} + +/** + * \brief Handler for call start ack event + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_start_ack(sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_channel_t *zchan; + + if (nack_map[event->call_setup_id]) { + return; + } + + OUTBOUND_REQUESTS[event->call_setup_id].event = *event; + SETUP_GRID[event->span][event->chan] = event->call_setup_id; + + if ((zchan = find_zchan(OUTBOUND_REQUESTS[event->call_setup_id].span, event, 0))) { + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "OPEN ERROR [%s]\n", zchan->last_error); + } else { + zap_set_flag(zchan, ZAP_CHANNEL_OUTBOUND); + zap_set_flag_locked(zchan, ZAP_CHANNEL_INUSE); + zchan->extra_id = event->call_setup_id; + zap_log(ZAP_LOG_DEBUG, "Assign chan %d:%d (%d:%d) CSid=%d\n", zchan->span_id, zchan->chan_id, event->span+1,event->chan+1, event->call_setup_id); + zchan->sflags = SFLAG_RECVD_ACK; + + if ((event->flags & SIGBOOST_PROGRESS_MEDIA)) { + zchan->init_state = ZAP_CHANNEL_STATE_PROGRESS_MEDIA; + zap_log(ZAP_LOG_DEBUG, "Channel init state changed to PROGRESS_MEDIA [Csid:%d]\n", event->call_setup_id); + } else if ((event->flags & SIGBOOST_PROGRESS_RING)) { + zchan->init_state = ZAP_CHANNEL_STATE_PROGRESS; + zap_log(ZAP_LOG_DEBUG, "Channel init state changed to PROGRESS [Csid:%d]\n", event->call_setup_id); + } else { + zchan->init_state = ZAP_CHANNEL_STATE_IDLE; + zap_log(ZAP_LOG_DEBUG, "Channel init state changed to IDLE [Csid:%d]\n", event->call_setup_id); + } + + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HOLD); + + OUTBOUND_REQUESTS[event->call_setup_id].flags = event->flags; + OUTBOUND_REQUESTS[event->call_setup_id].zchan = zchan; + OUTBOUND_REQUESTS[event->call_setup_id].status = BST_READY; + return; + } + } + + //printf("WTF BAD ACK CSid=%d span=%d chan=%d\n", event->call_setup_id, event->span+1,event->chan+1); + if ((zchan = find_zchan(OUTBOUND_REQUESTS[event->call_setup_id].span, event, 1))) { + //printf("WTF BAD ACK2 %d:%d (%d:%d) CSid=%d xtra_id=%d out=%d state=%s\n", zchan->span_id, zchan->chan_id, event->span+1,event->chan+1, event->call_setup_id, zchan->extra_id, zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND), zap_channel_state2str(zchan->state)); + } + + zap_set_sflag(zchan, SFLAG_SENT_FINAL_MSG); + zap_log(ZAP_LOG_CRIT, "START ACK CANT FIND A CHAN %d:%d\n", event->span+1,event->chan+1); + sangomabc_exec_command(mcon, + event->span, + event->chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_STOPPED, + ZAP_CAUSE_DESTINATION_OUT_OF_ORDER, 0); + OUTBOUND_REQUESTS[event->call_setup_id].status = BST_FAIL; + OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = ZAP_CAUSE_DESTINATION_OUT_OF_ORDER; + +} + +/** + * \brief Handler for call done event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_done(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_channel_t *zchan; + int r = 0; + + if ((zchan = find_zchan(span, event, 1))) { + zap_mutex_lock(zchan->mutex); + + if (zchan->state == ZAP_CHANNEL_STATE_DOWN || zchan->state == ZAP_CHANNEL_STATE_HANGUP_COMPLETE || zap_test_sflag(zchan, SFLAG_TERMINATING)) { + goto done; + } + + zap_set_state_r(zchan, ZAP_CHANNEL_STATE_HANGUP_COMPLETE, 0, r); + if (r) { + zap_set_sflag(zchan, SFLAG_FREE_REQ_ID); + zap_mutex_unlock(zchan->mutex); + return; + } + } + + done: + + if (zchan) { + zap_mutex_unlock(zchan->mutex); + } + + if (event->call_setup_id) { + release_request_id(event->call_setup_id); + } else { + release_request_id_span_chan(event->span, event->chan); + } +} + +/** + * \brief Handler for call start nack event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_start_nack(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_channel_t *zchan; + + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + uint32_t count = 0; + int delay = 0; + int tg=event->trunk_group; + + zap_span_channel_use_count(span, &count); + + delay = (int) (count / 100) * 2; + + if (delay > 10) { + delay = 10; + } else if (delay < 1) { + delay = 1; + } + + if (tg < 0 || tg >= MAX_TRUNK_GROUPS) { + zap_log(ZAP_LOG_CRIT, "Invalid All Ckt Busy trunk group number %i\n", tg); + tg=0; + } + + congestion_timeouts[tg] = time(NULL) + delay; + event->release_cause = 17; + + } else if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { + event->release_cause = 17; + } + + if (event->call_setup_id) { + + sangomabc_exec_command(mcon, + 0, + 0, + event->call_setup_id, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0, 0); + + OUTBOUND_REQUESTS[event->call_setup_id].event = *event; + OUTBOUND_REQUESTS[event->call_setup_id].status = BST_FAIL; + OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = event->release_cause; + return; + } else { + if ((zchan = find_zchan(span, event, 1))) { + int r = 0; + assert(!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)); + + zchan->call_data = (void*)(intptr_t)event->event_id; + + zap_mutex_lock(zchan->mutex); + zap_set_state_r(zchan, ZAP_CHANNEL_STATE_TERMINATING, 0, r); + if (r == ZAP_STATE_CHANGE_SUCCESS) { + zchan->caller_data.hangup_cause = event->release_cause; + } + zap_mutex_unlock(zchan->mutex); + if (r) { + return; + } + } + } + + if (zchan) { + zap_set_sflag_locked(zchan, SFLAG_SENT_FINAL_MSG); + } + + /* nobody else will do it so we have to do it ourselves */ + sangomabc_exec_command(mcon, + event->span, + event->chan, + 0, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0, 0); +} + +/** + * \brief Handler for call stop event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_stop(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_channel_t *zchan; + + if ((zchan = find_zchan(span, event, 1))) { + int r = 0; + + zap_mutex_lock(zchan->mutex); + + if (zap_test_sflag(zchan, SFLAG_HANGUP)) { + /* racing condition where both sides initiated a hangup + * Do not change current state as channel is already clearing + * itself through local initiated hangup */ + + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0, 0); + zap_mutex_unlock(zchan->mutex); + return; + } else { + if (zchan->state == ZAP_CHANNEL_STATE_HOLD) { + zchan->init_state = ZAP_CHANNEL_STATE_TERMINATING; + zap_log(ZAP_LOG_DEBUG, "Channel init state updated to TERMINATING [Csid:%d]\n", event->call_setup_id); + OUTBOUND_REQUESTS[event->call_setup_id].hangup_cause = event->release_cause; + zchan->caller_data.hangup_cause = event->release_cause; + zap_mutex_unlock(zchan->mutex); + return; + } else { + zap_set_state_r(zchan, ZAP_CHANNEL_STATE_TERMINATING, 0, r); + } + } + + if (r == ZAP_STATE_CHANGE_SUCCESS) { + zchan->caller_data.hangup_cause = event->release_cause; + } + + if (r) { + zap_set_sflag(zchan, SFLAG_FREE_REQ_ID); + } + + zap_mutex_unlock(zchan->mutex); + + if (r) { + return; + } + } /* else we have to do it ourselves.... */ + + zap_log(ZAP_LOG_WARNING, "We could not find chan: s%dc%d\n", event->span, event->chan); + release_request_id_span_chan(event->span, event->chan); +} + +/** + * \brief Handler for call answer event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_answer(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_channel_t *zchan; + + if ((zchan = find_zchan(span, event, 1))) { + zap_mutex_lock(zchan->mutex); + if (zchan->state == ZAP_CHANNEL_STATE_HOLD) { + zchan->init_state = ZAP_CHANNEL_STATE_UP; + } else { + int r = 0; + zap_set_state_r(zchan, ZAP_CHANNEL_STATE_UP, 0, r); + } + zap_mutex_unlock(zchan->mutex); + } else { + zap_log(ZAP_LOG_CRIT, "ANSWER CANT FIND A CHAN %d:%d\n", event->span+1,event->chan+1); + sangomabc_exec_command(mcon, + event->span, + event->chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_STOPPED, + ZAP_CAUSE_DESTINATION_OUT_OF_ORDER, 0); + } +} + +static __inline__ void advance_chan_states(zap_channel_t *zchan); + +/** + * \brief Handler for call start event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_call_start(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_event_t *event) +{ + zap_channel_t *zchan; + int hangup_cause = ZAP_CAUSE_CALL_REJECTED; + + if (!(zchan = find_zchan(span, (sangomabc_short_event_t*)event, 0))) { + if ((zchan = find_zchan(span, (sangomabc_short_event_t*)event, 1))) { + int r; + if (zchan->state == ZAP_CHANNEL_STATE_UP) { + zap_log(ZAP_LOG_CRIT, "ZCHAN STATE UP -> Changed to TERMINATING %d:%d\n", event->span+1,event->chan+1); + zap_set_state_r(zchan, ZAP_CHANNEL_STATE_TERMINATING, 0, r); + } else if (zap_test_sflag(zchan, SFLAG_HANGUP)) { + zap_log(ZAP_LOG_CRIT, "ZCHAN STATE HANGUP -> Changed to HANGUP COMPLETE %d:%d\n", event->span+1,event->chan+1); + zap_set_state_r(zchan, ZAP_CHANNEL_STATE_HANGUP_COMPLETE, 0, r); + } else { + zap_log(ZAP_LOG_CRIT, "ZCHAN STATE INVALID %s on IN CALL %d:%d\n", zap_channel_state2str(zchan->state),event->span+1,event->chan+1); + + } + zap_set_sflag(zchan, SFLAG_SENT_FINAL_MSG); + zchan=NULL; + } + zap_log(ZAP_LOG_CRIT, "START CANT FIND CHAN %d:%d\n", event->span+1,event->chan+1); + goto error; + } + + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_CRIT, "START CANT OPEN CHAN %d:%d\n", event->span+1,event->chan+1); + goto error; + } + + zchan->sflags = 0; + zap_set_string(zchan->caller_data.cid_num.digits, (char *)event->calling.digits); + zap_set_string(zchan->caller_data.cid_name, (char *)event->calling.digits); + zap_set_string(zchan->caller_data.ani.digits, (char *)event->calling.digits); + zap_set_string(zchan->caller_data.dnis.digits, (char *)event->called.digits); + zap_set_string(zchan->caller_data.rdnis.digits, (char *)event->rdnis.digits); + + if (strlen(event->calling_name)) { + zap_set_string(zchan->caller_data.cid_name, (char *)event->calling_name); + } + + zchan->caller_data.cid_num.plan = event->calling.npi; + zchan->caller_data.cid_num.type = event->calling.ton; + + zchan->caller_data.ani.plan = event->calling.npi; + zchan->caller_data.ani.type = event->calling.ton; + + zchan->caller_data.dnis.plan = event->called.npi; + zchan->caller_data.dnis.type = event->called.ton; + + zchan->caller_data.rdnis.plan = event->rdnis.npi; + zchan->caller_data.rdnis.type = event->rdnis.ton; + + zchan->caller_data.screen = event->calling.screening_ind; + zchan->caller_data.pres = event->calling.presentation_ind; + + if (event->custom_data_size) { + char* p = NULL; + + p = strstr((char*)event->custom_data,"PRI001-ANI2-"); + if (p!=NULL) { + int ani2 = 0; + sscanf(p, "PRI001-ANI2-%d", &ani2); + snprintf(zchan->caller_data.aniII, 5, "%.2d", ani2); + } + } + + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RING); + return; + + error: + if (zchan) { + hangup_cause = zchan->caller_data.hangup_cause; + } else { + zap_log(ZAP_LOG_CRIT, "START CANT FIND A CHAN %d:%d\n", event->span+1,event->chan+1); + hangup_cause = ZAP_CAUSE_REQUESTED_CHAN_UNAVAIL; + } + sangomabc_exec_command(mcon, + event->span, + event->chan, + 0, + SIGBOOST_EVENT_CALL_START_NACK, + hangup_cause, 0); + +} + +static void handle_call_loop_start(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_status_t res = ZAP_FAIL; + zap_channel_t *zchan; + + if (!(zchan = find_zchan(span, (sangomabc_short_event_t*)event, 0))) { + zap_log(ZAP_LOG_CRIT, "CANNOT START LOOP, CHAN NOT AVAILABLE %d:%d\n", event->span+1,event->chan+1); + return; + } + + if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_CRIT, "CANNOT START LOOP, CANT OPEN CHAN %d:%d\n", event->span+1,event->chan+1); + return; + } + + zap_set_state_r(zchan, ZAP_CHANNEL_STATE_IN_LOOP, 0, res); + if (res != ZAP_SUCCESS) { + zap_log(ZAP_LOG_CRIT, "yay, could not set the state of the channel to IN_LOOP, loop will fail\n"); + zap_channel_done(zchan); + return; + } + zap_channel_command(zchan, ZAP_COMMAND_ENABLE_LOOP, NULL); +} + +static void handle_call_loop_stop(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_channel_t *zchan; + zap_status_t res = ZAP_FAIL; + if (!(zchan = find_zchan(span, (sangomabc_short_event_t*)event, 1))) { + zap_log(ZAP_LOG_CRIT, "CANNOT STOP LOOP, INVALID CHAN REQUESTED %d:%d\n", event->span+1,event->chan+1); + return; + } + if (zchan->state != ZAP_CHANNEL_STATE_IN_LOOP) { + zap_log(ZAP_LOG_ERROR, "Got stop loop request in a channel that is not in loop, ignoring ...\n"); + return; + } + zap_channel_command(zchan, ZAP_COMMAND_DISABLE_LOOP, NULL); + /* even when we did not sent a msg we set this flag to avoid sending call stop in the DOWN state handler */ + zap_set_flag(zchan, SFLAG_SENT_FINAL_MSG); + zap_set_state_r(zchan, ZAP_CHANNEL_STATE_DOWN, 0, res); +} + +/** + * \brief Handler for heartbeat event + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static void handle_heartbeat(sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + int err; + + err = sangomabc_connection_writep(mcon, (sangomabc_event_t*)event); + + if (err <= 0) { + zap_log(ZAP_LOG_CRIT, "Failed to tx on ISUP socket [%s]: %s\n", strerror(errno)); + } + + mcon->hb_elapsed = 0; + + return; +} + +/** + * \brief Handler for restart ack event + * \param mcon sangoma boost connection + * \param span Span where event was fired + * \param event Event to handle + */ +static void handle_restart_ack(sangomabc_connection_t *mcon, zap_span_t *span, sangomabc_short_event_t *event) +{ + zap_log(ZAP_LOG_DEBUG, "RECV RESTART ACK\n"); +} + +/** + * \brief Handler for restart event + * \param mcon sangoma boost connection + * \param span Span where event was fired + * \param event Event to handle + */ +static void handle_restart(sangomabc_connection_t *mcon, zap_span_t *span, sangomabc_short_event_t *event) +{ + zap_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + + mcon->rxseq_reset = 0; + zap_set_flag((&sangoma_boost_data->mcon), MSU_FLAG_DOWN); + zap_set_flag_locked(span, ZAP_SPAN_SUSPENDED); + zap_set_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RESTARTING); + + mcon->hb_elapsed = 0; +} + +/** + * \brief Handler for incoming digit event + * \param mcon sangoma boost connection + * \param span Span where event was fired + * \param event Event to handle + */ +static void handle_incoming_digit(sangomabc_connection_t *mcon, zap_span_t *span, sangomabc_event_t *event) +{ + zap_channel_t *zchan = NULL; + char digits[MAX_DIALED_DIGITS + 2] = ""; + + if (!(zchan = find_zchan(span, (sangomabc_short_event_t *)event, 1))) { + zap_log(ZAP_LOG_ERROR, "Invalid channel\n"); + return; + } + + if (event->called_number_digits_count == 0) { + zap_log(ZAP_LOG_WARNING, "Error Incoming digit with len %s %d [w%dg%d]\n", + event->called_number_digits, + event->called_number_digits_count, + event->span+1, event->chan+1); + return; + } + + zap_log(ZAP_LOG_WARNING, "Incoming digit with len %s %d [w%dg%d]\n", + event->called_number_digits, + event->called_number_digits_count, + event->span+1, event->chan+1); + + memcpy(digits, event->called_number_digits, event->called_number_digits_count); + zap_channel_queue_dtmf(zchan, digits); + + return; +} + + +/** + * \brief Checks if span has state changes pending and processes + * \param span Span where event was fired + * \param event Event to handle + */ +static zap_channel_t* event_process_states(zap_span_t *span, sangomabc_short_event_t *event) +{ + zap_channel_t *zchan = NULL; + + switch (event->event_id) { + case SIGBOOST_EVENT_CALL_START_NACK: + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + if (event->call_setup_id) { + return NULL; + } + //if event->span and event->chan is valid, fall-through + case SIGBOOST_EVENT_CALL_START: + case SIGBOOST_EVENT_CALL_START_ACK: + case SIGBOOST_EVENT_CALL_STOPPED: + case SIGBOOST_EVENT_CALL_PROGRESS: + case SIGBOOST_EVENT_CALL_ANSWERED: + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + case SIGBOOST_EVENT_DIGIT_IN: + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + if (!(zchan = find_zchan(span, (sangomabc_short_event_t*)event, 1))) { + zap_log(ZAP_LOG_DEBUG, "PROCESS STATES CANT FIND CHAN %d:%d\n", event->span+1,event->chan+1); + return NULL; + } + break; + case SIGBOOST_EVENT_HEARTBEAT: + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + case SIGBOOST_EVENT_SYSTEM_RESTART: + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + return NULL; + default: + zap_log(ZAP_LOG_CRIT, "Unhandled event id:%d\n", event->event_id); + return NULL; + } + + zap_mutex_lock(zchan->mutex); + advance_chan_states(zchan); + return zchan; +} + +/** + * \brief Handler for sangoma boost event + * \param span Span where event was fired + * \param mcon sangoma boost connection + * \param event Event to handle + */ +static int parse_sangoma_event(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event) +{ + zap_channel_t* zchan = NULL; + + if (!zap_running()) { + zap_log(ZAP_LOG_WARNING, "System is shutting down.\n"); + goto end; + } + + assert(event->call_setup_id <= MAX_REQ_ID); + + /* process all pending state changes for that channel before + processing the new boost event */ + zchan = event_process_states(span, event); + + switch(event->event_id) { + case SIGBOOST_EVENT_CALL_START: + handle_call_start(span, mcon, (sangomabc_event_t*)event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + handle_call_stop(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + handle_call_start_ack(mcon, event); + break; + case SIGBOOST_EVENT_CALL_PROGRESS: + handle_call_progress(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + handle_call_start_nack(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + handle_call_answer(span, mcon, event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + handle_heartbeat(mcon, event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + handle_call_done(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + handle_call_done(span, mcon, event); + nack_map[event->call_setup_id] = 0; + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + handle_call_loop_start(span, mcon, event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + handle_call_loop_stop(span, mcon, event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + handle_restart_ack(mcon, span, event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART: + handle_restart(mcon, span, event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + //handle_gap_abate(event); + break; + case SIGBOOST_EVENT_DIGIT_IN: + handle_incoming_digit(mcon, span, (sangomabc_event_t*)event); + break; + default: + zap_log(ZAP_LOG_WARNING, "No handler implemented for [%s]\n", sangomabc_event_id_name(event->event_id)); + break; + } + + end: + if(zchan != NULL) { + advance_chan_states(zchan); + zap_mutex_unlock(zchan->mutex); + } + + return 0; +} + +/** + * \brief Handler for channel state change + * \param zchan Channel to handle + */ +static __inline__ void state_advance(zap_channel_t *zchan) +{ + + zap_sangoma_boost_data_t *sangoma_boost_data = zchan->span->signal_data; + sangomabc_connection_t *mcon = &sangoma_boost_data->mcon; + zap_sigmsg_t sig; + zap_status_t status; + + zap_log(ZAP_LOG_DEBUG, "%d:%d STATE [%s]\n", zchan->span_id, zchan->chan_id, zap_channel_state2str(zchan->state)); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + + switch (zchan->state) { + case ZAP_CHANNEL_STATE_DOWN: + { + if (zap_test_sflag(zchan, SFLAG_FREE_REQ_ID)) { + release_request_id_span_chan(zchan->physical_span_id-1, zchan->physical_chan_id-1); + } + + if (!zap_test_sflag(zchan, SFLAG_SENT_FINAL_MSG)) { + zap_set_sflag_locked(zchan, SFLAG_SENT_FINAL_MSG); + + if (zchan->call_data && ((uint32_t)(intptr_t)zchan->call_data == SIGBOOST_EVENT_CALL_START_NACK)) { + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0, 0); + + } else { + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0, 0); + } + } + + if (zchan->extra_id) { + zchan->extra_id = 0; + } + zchan->sflags = 0; + zchan->call_data = NULL; + zap_channel_done(zchan); + } + break; + case ZAP_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_PROGRESS_MEDIA; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + if (!zap_test_sflag(zchan, SFLAG_SENT_ACK)) { + zap_set_sflag(zchan, SFLAG_SENT_ACK); + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0, + SIGBOOST_PROGRESS_MEDIA); + } + } + } + break; + case ZAP_CHANNEL_STATE_PROGRESS: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_PROGRESS; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + if (!zap_test_sflag(zchan, SFLAG_SENT_ACK)) { + zap_set_sflag(zchan, SFLAG_SENT_ACK); + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0, + SIGBOOST_PROGRESS_RING); + } + } + } + break; + case ZAP_CHANNEL_STATE_IDLE: + case ZAP_CHANNEL_STATE_HOLD: + /* twiddle */ + break; + case ZAP_CHANNEL_STATE_RING: + { + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_START; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } + } + break; + case ZAP_CHANNEL_STATE_RESTART: + { + sig.event_id = ZAP_SIGEVENT_RESTART; + status = zap_span_send_signal(zchan->span, &sig); + zap_set_sflag_locked(zchan, SFLAG_SENT_FINAL_MSG); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + break; + case ZAP_CHANNEL_STATE_UP: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_UP; + if ((status = zap_span_send_signal(zchan->span, &sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + if (!(zap_test_flag(zchan, ZAP_CHANNEL_PROGRESS) || zap_test_flag(zchan, ZAP_CHANNEL_MEDIA))) { + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0, 0); + } + + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_ANSWERED, + 0, 0); + } + } + break; + case ZAP_CHANNEL_STATE_DIALING: + { + } + break; + case ZAP_CHANNEL_STATE_HANGUP_COMPLETE: + { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + break; + case ZAP_CHANNEL_STATE_HANGUP: + { + zap_set_sflag_locked(zchan, SFLAG_HANGUP); + + if (zap_test_sflag(zchan, SFLAG_SENT_FINAL_MSG) || zap_test_sflag(zchan, SFLAG_TERMINATING)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP_COMPLETE); + } else { + zap_set_sflag_locked(zchan, SFLAG_SENT_FINAL_MSG); + + if (zap_test_flag(zchan, ZAP_CHANNEL_ANSWERED) || + zap_test_flag(zchan, ZAP_CHANNEL_PROGRESS) || + zap_test_flag(zchan, ZAP_CHANNEL_MEDIA) || zap_test_sflag(zchan, SFLAG_RECVD_ACK)) { + + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_STOPPED, + zchan->caller_data.hangup_cause, 0); + } else { + sangomabc_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_NACK, + zchan->caller_data.hangup_cause, 0); + } + } + } + break; + case ZAP_CHANNEL_STATE_TERMINATING: + { + zap_set_sflag_locked(zchan, SFLAG_TERMINATING); + sig.event_id = ZAP_SIGEVENT_STOP; + status = zap_span_send_signal(zchan->span, &sig); + } + break; + case ZAP_CHANNEL_STATE_IN_LOOP: + { + /* nothing to do, we sent the ZAP_COMMAND_ENABLE_LOOP command in handle_call_loop_start() right away */ + } + break; + default: + break; + } +} + +static __inline__ void advance_chan_states(zap_channel_t *zchan) +{ + while (zap_test_flag(zchan, ZAP_CHANNEL_STATE_CHANGE)) { + zap_clear_flag(zchan, ZAP_CHANNEL_STATE_CHANGE); + state_advance(zchan); + zap_channel_complete_state(zchan); + } +} + +/** + * \brief Initialises outgoing requests array + */ +static __inline__ void init_outgoing_array(void) +{ + memset(&OUTBOUND_REQUESTS, 0, sizeof(OUTBOUND_REQUESTS)); + +} + +/** + * \brief Checks current state on a span + * \param span Span to check status on + */ +static __inline__ void check_state(zap_span_t *span) +{ + zap_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + int susp = zap_test_flag(span, ZAP_SPAN_SUSPENDED); + + if (susp && zap_check_state_all(span, ZAP_CHANNEL_STATE_DOWN)) { + susp = 0; + } + + if (zap_test_flag(span, ZAP_SPAN_STATE_CHANGE) || susp) { + uint32_t j; + zap_clear_flag_locked(span, ZAP_SPAN_STATE_CHANGE); + for(j = 1; j <= span->chan_count; j++) { + if (zap_test_flag((span->channels[j]), ZAP_CHANNEL_STATE_CHANGE) || susp) { + zap_mutex_lock(span->channels[j]->mutex); + zap_clear_flag((span->channels[j]), ZAP_CHANNEL_STATE_CHANGE); + if (susp && span->channels[j]->state != ZAP_CHANNEL_STATE_DOWN) { + zap_channel_set_state(span->channels[j], ZAP_CHANNEL_STATE_RESTART, 0); + } + state_advance(span->channels[j]); + zap_channel_complete_state(span->channels[j]); + zap_mutex_unlock(span->channels[j]->mutex); + } + } + } + + if (zap_test_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RESTARTING)) { + if (zap_check_state_all(span, ZAP_CHANNEL_STATE_DOWN)) { + sangomabc_exec_command(&sangoma_boost_data->mcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART_ACK, + 0, 0); + zap_clear_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RESTARTING); + zap_clear_flag_locked(span, ZAP_SPAN_SUSPENDED); + zap_clear_flag((&sangoma_boost_data->mcon), MSU_FLAG_DOWN); + sangoma_boost_data->mcon.hb_elapsed = 0; + init_outgoing_array(); + } + } +} + + +/** + * \brief Checks for events on a span + * \param span Span to check for events + */ +static __inline__ void check_events(zap_span_t *span, int ms_timeout) +{ + zap_status_t status; + + status = zap_span_poll_event(span, ms_timeout); + + switch(status) { + case ZAP_SUCCESS: + { + zap_event_t *event; + while (zap_span_next_event(span, &event) == ZAP_SUCCESS) { + // for now we do nothing with events, this is here + // just to have the hardware layer to get any HW DTMF + // events and enqueue the DTMF on the channel (done during zap_span_next_event()) + } + } + break; + case ZAP_FAIL: + { + zap_log(ZAP_LOG_DEBUG, "Boost Check Event Failure Failure! %d\n", zap_running()); + } + break; + default: + break; + } + + return; +} + +/** + * \brief Main thread function for sangoma boost span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *zap_sangoma_events_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + + while (zap_test_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RUNNING) && zap_running()) { + check_events(span,100); + } + + return NULL; +} + +/** + * \brief Main thread function for sangoma boost span (monitor) + * \param me Current thread + * \param obj Span to run in this thread + */ +static void *zap_sangoma_boost_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + zap_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + sangomabc_connection_t *mcon, *pcon; + uint32_t ms = 10; //, too_long = 20000; + + + sangoma_boost_data->pcon = sangoma_boost_data->mcon; + + if (sangomabc_connection_open(&sangoma_boost_data->mcon, + sangoma_boost_data->mcon.cfg.local_ip, + sangoma_boost_data->mcon.cfg.local_port, + sangoma_boost_data->mcon.cfg.remote_ip, + sangoma_boost_data->mcon.cfg.remote_port) < 0) { + zap_log(ZAP_LOG_ERROR, "Error: Opening MCON Socket [%d] %s\n", sangoma_boost_data->mcon.socket, strerror(errno)); + goto end; + } + + if (sangomabc_connection_open(&sangoma_boost_data->pcon, + sangoma_boost_data->pcon.cfg.local_ip, + ++sangoma_boost_data->pcon.cfg.local_port, + sangoma_boost_data->pcon.cfg.remote_ip, + ++sangoma_boost_data->pcon.cfg.remote_port) < 0) { + zap_log(ZAP_LOG_ERROR, "Error: Opening PCON Socket [%d] %s\n", sangoma_boost_data->pcon.socket, strerror(errno)); + goto end; + } + + mcon = &sangoma_boost_data->mcon; + pcon = &sangoma_boost_data->pcon; + + init_outgoing_array(); + + sangomabc_exec_commandp(pcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + zap_set_flag(mcon, MSU_FLAG_DOWN); + + while (zap_test_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RUNNING)) { + fd_set rfds, efds; + struct timeval tv = { 0, ms * 1000 }; + int max, activity, i = 0; + sangomabc_event_t *event = NULL; + + if (!zap_running()) { + sangomabc_exec_commandp(pcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + zap_set_flag(mcon, MSU_FLAG_DOWN); + break; + } + + FD_ZERO(&rfds); + FD_ZERO(&efds); + FD_SET(mcon->socket, &rfds); + FD_SET(mcon->socket, &efds); + FD_SET(pcon->socket, &rfds); + FD_SET(pcon->socket, &efds); + + max = ((pcon->socket > mcon->socket) ? pcon->socket : mcon->socket) + 1; + + if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) { + goto error; + } + + if (activity) { + if (FD_ISSET(pcon->socket, &efds) || FD_ISSET(mcon->socket, &efds)) { + goto error; + } + + if (FD_ISSET(pcon->socket, &rfds)) { + while ((event = sangomabc_connection_readp(pcon, i))) { + parse_sangoma_event(span, pcon, (sangomabc_short_event_t*)event); + i++; + } + } + i=0; + + if (FD_ISSET(mcon->socket, &rfds)) { + if ((event = sangomabc_connection_read(mcon, i))) { + parse_sangoma_event(span, mcon, (sangomabc_short_event_t*)event); + i++; + } + } + + } + + + pcon->hb_elapsed += ms; + + if (zap_test_flag(span, ZAP_SPAN_SUSPENDED) || zap_test_flag(mcon, MSU_FLAG_DOWN)) { + pcon->hb_elapsed = 0; + } + + +#if 0 + if (pcon->hb_elapsed >= too_long) { + zap_log(ZAP_LOG_CRIT, "Lost Heartbeat!\n"); + zap_set_flag_locked(span, ZAP_SPAN_SUSPENDED); + zap_set_flag(mcon, MSU_FLAG_DOWN); + sangomabc_exec_commandp(pcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + } +#endif + + if (zap_running()) { + check_state(span); + } + } + + goto end; + + error: + zap_log(ZAP_LOG_CRIT, "Socket Error!\n"); + + end: + + sangomabc_connection_close(&sangoma_boost_data->mcon); + sangomabc_connection_close(&sangoma_boost_data->pcon); + + zap_clear_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RUNNING); + + zap_log(ZAP_LOG_DEBUG, "SANGOMA_BOOST thread ended.\n"); + return NULL; +} + +/** + * \brief Loads sangoma boost signaling module + * \param zio Openzap IO interface + * \return Success + */ +static ZIO_SIG_LOAD_FUNCTION(zap_sangoma_boost_init) +{ + zap_mutex_create(&request_mutex); + + return ZAP_SUCCESS; +} + +static zap_status_t zap_sangoma_boost_start(zap_span_t *span) +{ + int err; + zap_sangoma_boost_data_t *sangoma_boost_data = span->signal_data; + zap_set_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RUNNING); + err=zap_thread_create_detached(zap_sangoma_boost_run, span); + if (err) { + zap_clear_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RUNNING); + return err; + } + // launch the events thread to handle HW DTMF and possibly + // other events in the future + err=zap_thread_create_detached(zap_sangoma_events_run, span); + if (err) { + zap_clear_flag(sangoma_boost_data, ZAP_SANGOMA_BOOST_RUNNING); + } + return err; +} + +static zap_state_map_t boost_state_map = { + { + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_ANY_STATE}, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + {ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_IDLE, ZAP_CHANNEL_STATE_HOLD, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HOLD, ZAP_END}, + {ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_PROGRESS, + ZAP_CHANNEL_STATE_IDLE, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_UP, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_IDLE, ZAP_END}, + {ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_PROGRESS, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_IDLE, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_UP, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_CHANNEL_STATE_HANGUP, ZAP_END} + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + }, + { + ZSD_OUTBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_UP, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END} + }, + + /****************************************/ + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_ANY_STATE}, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RESTART, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DOWN}, + {ZAP_CHANNEL_STATE_IN_LOOP, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_IN_LOOP}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + {ZAP_CHANNEL_STATE_RING, ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_RING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_PROGRESS_MEDIA,ZAP_END} + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_CHANNEL_STATE_HANGUP, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_HANGUP_COMPLETE, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + {ZAP_CHANNEL_STATE_DOWN, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_PROGRESS, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_CHANNEL_STATE_UP, ZAP_CHANNEL_STATE_PROGRESS_MEDIA, ZAP_END}, + }, + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {ZAP_CHANNEL_STATE_UP, ZAP_END}, + {ZAP_CHANNEL_STATE_HANGUP, ZAP_CHANNEL_STATE_TERMINATING, ZAP_END}, + }, + + + } +}; + +/** + * \brief Initialises an sangoma boost span from configuration variables + * \param span Span to configure + * \param sig_cb Callback function for event signals + * \param ap List of configuration variables + * \return Success or failure + */ +static ZIO_SIG_CONFIGURE_FUNCTION(zap_sangoma_boost_configure_span) +{ + zap_sangoma_boost_data_t *sangoma_boost_data = NULL; + const char *local_ip = "127.0.0.65", *remote_ip = "127.0.0.66"; + int local_port = 53000, remote_port = 53000; + char *var, *val; + int *intval; + + while((var = va_arg(ap, char *))) { + if (!strcasecmp(var, "local_ip")) { + if (!(val = va_arg(ap, char *))) { + break; + } + local_ip = val; + } else if (!strcasecmp(var, "remote_ip")) { + if (!(val = va_arg(ap, char *))) { + break; + } + remote_ip = val; + } else if (!strcasecmp(var, "local_port")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + local_port = *intval; + } else if (!strcasecmp(var, "remote_port")) { + if (!(intval = va_arg(ap, int *))) { + break; + } + remote_port = *intval; + } else { + snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var); + return ZAP_FAIL; + } + } + + + if (!local_ip && local_port && remote_ip && remote_port && sig_cb) { + zap_set_string(span->last_error, "missing params"); + return ZAP_FAIL; + } + + sangoma_boost_data = malloc(sizeof(*sangoma_boost_data)); + assert(sangoma_boost_data); + memset(sangoma_boost_data, 0, sizeof(*sangoma_boost_data)); + + zap_set_string(sangoma_boost_data->mcon.cfg.local_ip, local_ip); + sangoma_boost_data->mcon.cfg.local_port = local_port; + zap_set_string(sangoma_boost_data->mcon.cfg.remote_ip, remote_ip); + sangoma_boost_data->mcon.cfg.remote_port = remote_port; + span->signal_cb = sig_cb; + span->start = zap_sangoma_boost_start; + span->signal_data = sangoma_boost_data; + span->signal_type = ZAP_SIGTYPE_SANGOMABOOST; + span->outgoing_call = sangoma_boost_outgoing_call; + span->channel_request = sangoma_boost_channel_request; + span->state_map = &boost_state_map; + zap_set_flag_locked(span, ZAP_SPAN_SUSPENDED); + + return ZAP_SUCCESS; +} + +/** + * \brief Openzap sangoma boost signaling module definition + */ +zap_module_t zap_module = { + "sangoma_boost", + NULL, + NULL, + zap_sangoma_boost_init, + zap_sangoma_boost_configure_span, + NULL +}; + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.c b/libs/openzap/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.c new file mode 100644 index 0000000000..5b3e5b87ad --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * 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. + */ + +#if HAVE_NETDB_H +#include +#endif + +#include "openzap.h" +#include + +#ifndef HAVE_GETHOSTBYNAME_R +extern int gethostbyname_r (const char *__name, + struct hostent *__result_buf, + char *__buf, size_t __buflen, + struct hostent **__result, + int *__h_errnop); +#endif + +struct sangomabc_map { + uint32_t event_id; + const char *name; +}; + +static struct sangomabc_map sangomabc_table[] = { + {SIGBOOST_EVENT_CALL_START, "CALL_START"}, + {SIGBOOST_EVENT_CALL_START_ACK, "CALL_START_ACK"}, + {SIGBOOST_EVENT_CALL_START_NACK, "CALL_START_NACK"}, + {SIGBOOST_EVENT_CALL_PROGRESS, "CALL PROGRESS"}, + {SIGBOOST_EVENT_CALL_START_NACK_ACK, "CALL_START_NACK_ACK"}, + {SIGBOOST_EVENT_CALL_ANSWERED, "CALL_ANSWERED"}, + {SIGBOOST_EVENT_CALL_STOPPED, "CALL_STOPPED"}, + {SIGBOOST_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"}, + {SIGBOOST_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"}, + {SIGBOOST_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"}, + {SIGBOOST_EVENT_HEARTBEAT, "HEARTBEAT"}, + {SIGBOOST_EVENT_INSERT_CHECK_LOOP, "LOOP START"}, + {SIGBOOST_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"}, + {SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE, "AUTO_CALL_GAP_ABATE"}, + {SIGBOOST_EVENT_DIGIT_IN, "DIGIT_IN"} +}; + + + +static void sangomabc_print_event_call(sangomabc_connection_t *mcon, sangomabc_event_t *event, int priority, int dir, const char *file, const char *func, int line) +{ + if (event->event_id == SIGBOOST_EVENT_HEARTBEAT) + return; + + zap_log(file, func, line, ZAP_LOG_LEVEL_WARNING, "%s EVENT: %s:(%X) [w%dg%d] CSid=%i Seq=%i Cn=[%s] Cd=[%s] Ci=[%s] Rdnis=[%s]\n", + dir ? "TX":"RX", + sangomabc_event_id_name(event->event_id), + event->event_id, + event->span+1, + event->chan+1, + event->call_setup_id, + event->fseqno, + strlen(event->calling_name)?event->calling_name:"N/A", + (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), + (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A"), + event->isup_in_rdnis); + +} +static void sangomabc_print_event_short(sangomabc_connection_t *mcon, sangomabc_short_event_t *event, int priority, int dir, const char *file, const char *func, int line) +{ + if (event->event_id == SIGBOOST_EVENT_HEARTBEAT) + return; + zap_log(file, func, line, ZAP_LOG_LEVEL_WARNING, "%s EVENT (%s): %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i \n", + dir ? "TX":"RX", + priority ? "P":"N", + sangomabc_event_id_name(event->event_id), + event->event_id, + event->span+1, + event->chan+1, + event->release_cause, + event->call_setup_id, + event->fseqno); +} + + +static int create_conn_socket(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) +{ + int rc; + struct hostent *result, *local_result; + char buf[512], local_buf[512]; + int err = 0, local_err = 0; + + memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp)); + memset(&mcon->local_hp, 0, sizeof(mcon->local_hp)); +#ifdef HAVE_NETINET_SCTP_H + zap_log(ZAP_LOG_DEBUG, "Creating SCTP socket L=%s:%d R=%s:%d\n", + local_ip, local_port, ip, port); + mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); +#else + zap_log(ZAP_LOG_DEBUG, "Creating UDP socket L=%s:%d R=%s:%d\n", + local_ip, local_port, ip, port); + mcon->socket = socket(AF_INET, SOCK_DGRAM, 0); +#endif + + if (mcon->socket >= 0) { + int flag; + + flag = 1; +#ifdef HAVE_GETHOSTBYNAME_R_FIVE + gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &err); + gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_err); + if (!err && !local_err) { +#else + gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_result, &local_err); + if (result && local_result) { +#endif + mcon->remote_addr.sin_family = mcon->remote_hp.h_addrtype; + memcpy((char *) &mcon->remote_addr.sin_addr.s_addr, mcon->remote_hp.h_addr_list[0], mcon->remote_hp.h_length); + mcon->remote_addr.sin_port = htons(port); + + mcon->local_addr.sin_family = mcon->local_hp.h_addrtype; + memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length); + mcon->local_addr.sin_port = htons(local_port); + +#ifdef HAVE_NETINET_SCTP_H + setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY, + (char *)&flag, sizeof(int)); +#endif + + if ((rc = bind(mcon->socket, + (struct sockaddr *) &mcon->local_addr, + sizeof(mcon->local_addr))) < 0) { + close(mcon->socket); + mcon->socket = -1; + } else { +#ifdef HAVE_NETINET_SCTP_H + rc=listen(mcon->socket, 100); + if (rc) { + close(mcon->socket); + mcon->socket = -1; + } +#endif + } + } + } + + zap_mutex_create(&mcon->mutex); + + return mcon->socket; +} + +int sangomabc_connection_close(sangomabc_connection_t *mcon) +{ + if (mcon->socket > -1) { + close(mcon->socket); + } + + if (mcon->mutex) { + zap_mutex_lock(mcon->mutex); + zap_mutex_unlock(mcon->mutex); + zap_mutex_destroy(&mcon->mutex); + } + memset(mcon, 0, sizeof(*mcon)); + mcon->socket = -1; + + return 0; +} + +int sangomabc_connection_open(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) +{ + create_conn_socket(mcon, local_ip, local_port, ip, port); + return mcon->socket; +} + + +int sangomabc_exec_command(sangomabc_connection_t *mcon, int span, int chan, int id, int cmd, int cause, int flags) +{ + sangomabc_short_event_t oevent; + int retry = 5; + + sangomabc_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + oevent.flags = flags; + + if (cmd == SIGBOOST_EVENT_SYSTEM_RESTART || cmd == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { + mcon->rxseq_reset = 1; + mcon->txseq = 0; + mcon->rxseq = 0; + mcon->txwindow = 0; + } + + if (id >= 0) { + oevent.call_setup_id = id; + } + + while (sangomabc_connection_write(mcon, (sangomabc_event_t*)&oevent) <= 0) { + if (--retry <= 0) { + zap_log(ZAP_LOG_CRIT, "Failed to tx on ISUP socket: %s\n", strerror(errno)); + return -1; + } else { + zap_log(ZAP_LOG_WARNING, "Failed to tx on ISUP socket: %s :retry %i\n", strerror(errno), retry); + zap_sleep(1); + } + } + + return 0; +} + + +int sangomabc_exec_commandp(sangomabc_connection_t *pcon, int span, int chan, int id, int cmd, int cause) +{ + sangomabc_short_event_t oevent; + int retry = 5; + + sangomabc_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } + + while (sangomabc_connection_writep(pcon, (sangomabc_event_t*)&oevent) <= 0) { + if (--retry <= 0) { + zap_log(ZAP_LOG_CRIT, "Failed to tx on ISUP socket: %s\n", strerror(errno)); + return -1; + } else { + zap_log(ZAP_LOG_WARNING, "Failed to tx on ISUP socket: %s :retry %i\n", strerror(errno), retry); + zap_sleep(1); + } + } + + return 0; +} + +sangomabc_event_t *__sangomabc_connection_read(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line) +{ + unsigned int fromlen = sizeof(struct sockaddr_in); + int bytes = 0; + int msg_ok = 0; + + bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, + (struct sockaddr *) &mcon->local_addr, &fromlen); + + if (bytes <= 0) { + return NULL; + } + + if (mcon->event.version != SIGBOOST_VERSION) { + zap_log(ZAP_LOG_CRIT, "Invalid Boost Version %i Expecting %i\n",mcon->event.version, SIGBOOST_VERSION); + } + + /* Must check for < 0 cannot rely on bytes > MIN_SIZE_... compiler issue */ + if (bytes < 0) { + msg_ok=0; + + } else if ((bytes >= MIN_SIZE_CALLSTART_MSG) && boost_full_event(mcon->event.event_id)) { + msg_ok=1; + + } else if (bytes == sizeof(sangomabc_short_event_t)) { + msg_ok=1; + + } else { + msg_ok=0; + } + + if (msg_ok){ + + if (sangomabc_test_flag(mcon, MSU_FLAG_DOWN)) { + if (mcon->event.event_id != SIGBOOST_EVENT_SYSTEM_RESTART && + mcon->event.event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && + mcon->event.event_id != SIGBOOST_EVENT_HEARTBEAT) { + zap_log(file, func, line, ZAP_LOG_LEVEL_WARNING, "Not reading packets when connection is down. [%s]\n", + sangomabc_event_id_name(mcon->event.event_id)); + return NULL; + } + } + + if (boost_full_event(mcon->event.event_id)) { + sangomabc_print_event_call(mcon, &mcon->event, 0, 0, file, func, line); + } else { + sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)&mcon->event, 0, 0, file, func, line); + } + +#if 0 +/* NC: NOT USED ANY MORE */ + if (mcon->rxseq_reset) { + //if (mcon->event.event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { + zap_log(ZAP_LOG_DEBUG, "Rx sync ok\n"); + mcon->rxseq = mcon->event.fseqno; + return &mcon->event; + //} + errno=EAGAIN; + zap_log(ZAP_LOG_DEBUG, "Waiting for rx sync...\n"); + return NULL; + } +#endif + + mcon->txwindow = mcon->txseq - mcon->event.bseqno; + mcon->rxseq++; + +#if 0 + if (mcon->rxseq != mcon->event.fseqno) { + zap_log(ZAP_LOG_CRIT, "Invalid Sequence Number Expect=%i Rx=%i\n", mcon->rxseq, mcon->event.fseqno); + return NULL; + } +#endif + + return &mcon->event; + } else { + if (iteration == 0) { + zap_log(ZAP_LOG_CRIT, "NC - Invalid Event length from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); + return NULL; + } + } + + return NULL; +} + +sangomabc_event_t *__sangomabc_connection_readp(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line) +{ + unsigned int fromlen = sizeof(struct sockaddr_in); + int bytes = 0; + + bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, (struct sockaddr *) &mcon->local_addr, &fromlen); + + if (bytes <= 0) { + return NULL; + } + + if (mcon->event.version != SIGBOOST_VERSION) { + zap_log(ZAP_LOG_CRIT, "Invalid Boost Version %i Expecting %i\n",mcon->event.version, SIGBOOST_VERSION); + } + + if (bytes == sizeof(sangomabc_short_event_t)) { + + if (boost_full_event(mcon->event.event_id)) { + sangomabc_print_event_call(mcon, &mcon->event, 1, 0, file, func, line); + } else { + sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)&mcon->event, 1, 0, file, func, line); + } + + return &mcon->event; + } else { + if (iteration == 0) { + zap_log(ZAP_LOG_CRIT, "Critical Error: PQ Invalid Event lenght from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); + return NULL; + } + } + + return NULL; +} + + +int __sangomabc_connection_write(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line) +{ + int err; + int event_size=MIN_SIZE_CALLSTART_MSG+event->isup_in_rdnis_size; + + if (!event || mcon->socket < 0 || !mcon->mutex) { + zap_log(file, func, line, ZAP_LOG_LEVEL_CRIT, "Critical Error: No Event Device\n"); + return -EINVAL; + abort(); + } + + if (event->span >= ZAP_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN || event->chan >= ZAP_MAX_CHANNELS_PHYSICAL_SPAN ) { + zap_log(file, func, line, ZAP_LOG_LEVEL_CRIT, "Critical Error: TX Cmd=%s Invalid Span=%i Chan=%i\n", sangomabc_event_id_name(event->event_id), event->span, event->chan); + abort(); + return -1; + } + + if (!boost_full_event(event->event_id)) { + event_size=sizeof(sangomabc_short_event_t); + } + + if (sangomabc_test_flag(mcon, MSU_FLAG_DOWN)) { + if (event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART && + event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && + event->event_id != SIGBOOST_EVENT_HEARTBEAT) { + zap_log(file, func, line, ZAP_LOG_LEVEL_WARNING, "Not writing packets when connection is down. [%s]\n", + sangomabc_event_id_name(event->event_id)); + return 0; + } + } + + zap_mutex_lock(mcon->mutex); + if (event->event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { + mcon->txseq=0; + mcon->rxseq=0; + event->fseqno=0; + } else { + event->fseqno = mcon->txseq++; + } + event->bseqno = mcon->rxseq; + event->version = SIGBOOST_VERSION; + err = sendto(mcon->socket, event, event_size, 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); + + zap_mutex_unlock(mcon->mutex); + + if (err != event_size) { + err = -1; + abort(); + } + + if (boost_full_event(event->event_id)) { + sangomabc_print_event_call(mcon, event, 0, 1, file, func, line); + } else { + sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)event, 0, 1, file, func, line); + } + + return err; +} + + +int __sangomabc_connection_writep(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line) +{ + int err; + int event_size=sizeof(sangomabc_event_t); + + if (!event || mcon->socket < 0 || !mcon->mutex) { + zap_log(file, func, line, ZAP_LOG_LEVEL_CRIT, "Critical Error: No Event Device\n"); + return -EINVAL; + abort(); + } + + if (!boost_full_event(event->event_id)) { + event_size=sizeof(sangomabc_short_event_t); + } + + zap_mutex_lock(mcon->mutex); + event->version = SIGBOOST_VERSION; + err = sendto(mcon->socket, event, event_size, 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); + zap_mutex_unlock(mcon->mutex); + + if (err != event_size) { + err = -1; + abort(); + } + + if (boost_full_event(event->event_id)) { + sangomabc_print_event_call(mcon, event, 1, 1, file, func, line); + } else { + sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)event, 1, 1, file, func, line); + } + + return err; +} + + +void sangomabc_call_init(sangomabc_event_t *event, const char *calling, const char *called, int setup_id) +{ + memset(event, 0, sizeof(sangomabc_event_t)); + event->event_id = SIGBOOST_EVENT_CALL_START; + + if (calling) { + strncpy((char*)event->calling_number_digits, calling, sizeof(event->calling_number_digits)-1); + event->calling_number_digits_count = strlen(calling); + } + + if (called) { + strncpy((char*)event->called_number_digits, called, sizeof(event->called_number_digits)-1); + event->called_number_digits_count = strlen(called); + } + + event->call_setup_id = setup_id; + +} + +void sangomabc_event_init(sangomabc_short_event_t *event, sangomabc_event_id_t event_id, int chan, int span) +{ + memset(event, 0, sizeof(sangomabc_short_event_t)); + event->event_id = event_id; + event->chan = chan; + event->span = span; +} + +const char *sangomabc_event_id_name(uint32_t event_id) +{ + unsigned int x; + const char *ret = NULL; + + for (x = 0 ; x < sizeof(sangomabc_table)/sizeof(struct sangomabc_map); x++) { + if (sangomabc_table[x].event_id == event_id) { + ret = sangomabc_table[x].name; + break; + } + } + + return ret; +} + + +/* 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: + */ + diff --git a/libs/openzap/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.h b/libs/openzap/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.h new file mode 100644 index 0000000000..861f941ceb --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic + * 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 _SANGOMABC_H +#define _SANGOMABC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_NETINET_SCTP_H +#include +#endif +#include +#include +#include +#include +#include + +#define sangomabc_test_flag(p,flag) ({ \ + ((p)->flags & (flag)); \ + }) + +#define sangomabc_set_flag(p,flag) do { \ + ((p)->flags |= (flag)); \ + } while (0) + +#define sangomabc_clear_flag(p,flag) do { \ + ((p)->flags &= ~(flag)); \ + } while (0) + +#define sangomabc_copy_flags(dest,src,flagz) do { \ + (dest)->flags &= ~(flagz); \ + (dest)->flags |= ((src)->flags & (flagz)); \ + } while (0) + +typedef t_sigboost_callstart sangomabc_event_t; +typedef t_sigboost_short sangomabc_short_event_t; +typedef uint32_t sangomabc_event_id_t; + +typedef struct sangomabc_ip_cfg +{ + char local_ip[25]; + int local_port; + char remote_ip[25]; + int remote_port; +}sangomabc_ip_cfg_t; + +typedef enum { + MSU_FLAG_EVENT = (1 << 0), + MSU_FLAG_DOWN = (1 << 1) +} sangomabc_flag_t; + + +struct sangomabc_connection { + zap_socket_t socket; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + sangomabc_event_t event; + struct hostent remote_hp; + struct hostent local_hp; + unsigned int flags; + zap_mutex_t *mutex; + FILE *log; + unsigned int txseq; + unsigned int rxseq; + unsigned int txwindow; + unsigned int rxseq_reset; + sangomabc_ip_cfg_t cfg; + uint32_t hb_elapsed; +}; + +typedef struct sangomabc_connection sangomabc_connection_t; + +/* disable nagle's algorythm */ +static inline void sctp_no_nagle(int socket) +{ +#ifdef HAVE_NETINET_SCTP_H + int flag = 1; + setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int)); +#endif +} + +int sangomabc_connection_close(sangomabc_connection_t *mcon); +int sangomabc_connection_open(sangomabc_connection_t *mcon, char *local_ip, int local_port, char *ip, int port); +sangomabc_event_t *__sangomabc_connection_read(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line); +sangomabc_event_t *__sangomabc_connection_readp(sangomabc_connection_t *mcon, int iteration, const char *file, const char *func, int line); +int __sangomabc_connection_write(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line); +int __sangomabc_connection_writep(sangomabc_connection_t *mcon, sangomabc_event_t *event, const char *file, const char *func, int line); +#define sangomabc_connection_write(_m,_e) __sangomabc_connection_write(_m, _e, __FILE__, __func__, __LINE__) +#define sangomabc_connection_writep(_m,_e) __sangomabc_connection_writep(_m, _e, __FILE__, __func__, __LINE__) +#define sangomabc_connection_read(_m,_e) __sangomabc_connection_read(_m, _e, __FILE__, __func__, __LINE__) +#define sangomabc_connection_readp(_m,_e) __sangomabc_connection_readp(_m, _e, __FILE__, __func__, __LINE__) +void sangomabc_event_init(sangomabc_short_event_t *event, sangomabc_event_id_t event_id, int chan, int span); +void sangomabc_call_init(sangomabc_event_t *event, const char *calling, const char *called, int setup_id); +const char *sangomabc_event_id_name(uint32_t event_id); +int sangomabc_exec_command(sangomabc_connection_t *mcon, int span, int chan, int id, int cmd, int cause, int flags); +int sangomabc_exec_commandp(sangomabc_connection_t *pcon, int span, int chan, int id, int cmd, int cause); + +#endif + + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_sangoma_boost/sigboost.h b/libs/openzap/src/ozmod/ozmod_sangoma_boost/sigboost.h new file mode 100644 index 0000000000..1281069536 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_sangoma_boost/sigboost.h @@ -0,0 +1,209 @@ +/**************************************************************************** + * sigboost.h $Revision: 1.13 $ + * + * Definitions for the sigboost interface. + * + * WARNING WARNING WARNING + * + * This file is used by sangoma_mgd and perhaps other programs. Any changes + * to this file must be coordinated with other user programs, + * + * Copyright (C) 2005 Xygnada Technology, Inc. + * +****************************************************************************/ +#ifndef _SIGBOOST_H_ +#define _SIGBOOST_H_ + +#define SIGBOOST_VERSION 103 + +#include +#include + +#ifdef HAVE_FREETDM +#include +#endif + +enum e_sigboost_event_id_values +{ + SIGBOOST_EVENT_CALL_START = 0x80, /*128*/ + SIGBOOST_EVENT_CALL_START_ACK = 0x81, /*129*/ + SIGBOOST_EVENT_CALL_START_NACK = 0x82, /*130*/ + SIGBOOST_EVENT_CALL_START_NACK_ACK = 0x83, /*131*/ + SIGBOOST_EVENT_CALL_ANSWERED = 0x84, /*132*/ + SIGBOOST_EVENT_CALL_STOPPED = 0x85, /*133*/ + SIGBOOST_EVENT_CALL_STOPPED_ACK = 0x86, /*134*/ + SIGBOOST_EVENT_SYSTEM_RESTART = 0x87, /*135*/ + SIGBOOST_EVENT_SYSTEM_RESTART_ACK = 0x88, /*136*/ + /* CALL_RELEASED is aimed to fix a race condition that became obvious + * when the boost socket was replaced by direct function calls + * and the channel hunting was moved to freetdm, the problem is + * we can get CALL_STOPPED msg and reply with CALL_STOPPED_ACK + * but the signaling module will still (in PRI) send RELEASE and + * wait for RELEASE_COMPLETE from the isdn network before + * marking the channel as available, therefore freetdm should + * also not mark the channel as available until CALL_RELEASED + * is received, for socket mode we can continue working as usual + * with CALL_STOPPED being the last step because the hunting is + * done in the signaling module. + * + * CALL_RELEASED is only used in queue mode + * */ + SIGBOOST_EVENT_CALL_RELEASED = 0x51, /* 81 */ + SIGBOOST_EVENT_CALL_PROGRESS = 0x50, /*decimal 80*/ + /* Following IDs are ss7boost to sangoma_mgd only. */ + SIGBOOST_EVENT_HEARTBEAT = 0x89, /*137*/ + SIGBOOST_EVENT_INSERT_CHECK_LOOP = 0x8a, /*138*/ + SIGBOOST_EVENT_REMOVE_CHECK_LOOP = 0x8b, /*139*/ + SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE = 0x8c, /*140*/ + SIGBOOST_EVENT_DIGIT_IN = 0x8d, /*141*/ +}; +enum e_sigboost_release_cause_values +{ + SIGBOOST_RELEASE_CAUSE_UNDEFINED = 0, + SIGBOOST_RELEASE_CAUSE_NORMAL = 16, + /* probable elimination */ + //SIGBOOST_RELEASE_CAUSE_BUSY = 0x91, /* 145 */ + //SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST = 0x92, /* 146 */ + //SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET = 0x93, /* 147 */ + //SIGBOOST_RELEASE_CAUSE_NOANSWER = 0x94, /* 148 */ +}; + +enum e_sigboost_call_setup_ack_nack_cause_values +{ + //SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 34, /* Q.850 value - don't use */ + SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 117, /* non Q.850 value indicates local all ckt busy + causing sangoma_mgd to perform automatic call + gapping*/ + SIGBOOST_CALL_SETUP_NACK_TEST_CKT_BUSY = 17, /* Q.850 value */ + SIGBOOST_CALL_SETUP_NACK_INVALID_NUMBER = 28, /* Q.850 value */ + SIGBOOST_CALL_SETUP_CSUPID_DBL_USE = 200, /* unused Q.850 value */ +}; + + +enum e_sigboost_huntgroup_values +{ + SIGBOOST_HUNTGRP_SEQ_ASC = 0x00, /* sequential with lowest available first */ + SIGBOOST_HUNTGRP_SEQ_DESC = 0x01, /* sequential with highest available first */ + SIGBOOST_HUNTGRP_RR_ASC = 0x02, /* round-robin with lowest available first */ + SIGBOOST_HUNTGRP_RR_DESC = 0x03, /* round-robin with highest available first */ +}; + +enum e_sigboost_event_info_par_values +{ + SIGBOOST_EVI_SPARE = 0x00, + SIGBOOST_EVI_ALERTING = 0x01, + SIGBOOST_EVI_PROGRESS = 0x02, +}; + +enum e_sigboost_progress_flags +{ + SIGBOOST_PROGRESS_RING = (1 << 0), + SIGBOOST_PROGRESS_MEDIA = (1 << 1) +}; + +#define MAX_DIALED_DIGITS 31 + +/* Next two defines are used to create the range of values for call_setup_id + * in the t_sigboost structure. + * 0..((CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN) - 1) */ +#define CORE_MAX_SPANS 200 +#define CORE_MAX_CHAN_PER_SPAN 32 +#define MAX_PENDING_CALLS CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN +/* 0..(MAX_PENDING_CALLS-1) is range of call_setup_id below */ + +/* Should only be used by server */ +#define MAX_CALL_SETUP_ID 0xFFFF + +#define SIZE_CUSTOM 900 +#define SIZE_RDNIS SIZE_CUSTOM + + +#pragma pack(1) + +typedef struct +{ + uint8_t capability; + uint8_t uil1p; +} t_sigboost_bearer; + +typedef struct +{ + uint8_t digits_count; + char digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ + uint8_t npi; + uint8_t ton; + uint8_t screening_ind; + uint8_t presentation_ind; +}t_sigboost_digits; + +typedef struct +{ + uint16_t version; + uint32_t event_id; + /* delete sequence numbers - SCTP does not need them */ + uint32_t fseqno; + uint32_t bseqno; + uint16_t call_setup_id; + uint32_t trunk_group; + uint8_t span; + uint8_t chan; + uint32_t flags; + /* struct timeval tv; */ + t_sigboost_digits called; + t_sigboost_digits calling; + t_sigboost_digits rdnis; + /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ + char calling_name[MAX_DIALED_DIGITS + 1]; + t_sigboost_bearer bearer; + uint8_t hunt_group; + uint16_t custom_data_size; + char custom_data[SIZE_CUSTOM]; /* it's a null terminated string */ + +} t_sigboost_callstart; + +#define called_number_digits_count called.digits_count +#define called_number_digits called.digits +#define calling_number_digits_count calling.digits_count +#define calling_number_digits calling.digits +#define calling_number_screening_ind calling.screening_ind +#define calling_number_presentation calling.presentation_ind + +#define isup_in_rdnis_size custom_data_size +#define isup_in_rdnis custom_data + + +#define MIN_SIZE_CALLSTART_MSG sizeof(t_sigboost_callstart) - SIZE_CUSTOM + +typedef struct +{ + uint16_t version; + uint32_t event_id; + /* delete sequence numbers - SCTP does not need them */ + uint32_t fseqno; + uint32_t bseqno; + uint16_t call_setup_id; + uint32_t trunk_group; + uint8_t span; + uint8_t chan; + uint32_t flags; + /* struct timeval tv; */ + uint8_t release_cause; +} t_sigboost_short; +#pragma pack() + + +static inline int boost_full_event(int event_id) +{ + switch (event_id) { + case SIGBOOST_EVENT_CALL_START: + case SIGBOOST_EVENT_DIGIT_IN: + case SIGBOOST_EVENT_CALL_PROGRESS: + return 1; + default: + break; + } + + return 0; +} + +#endif diff --git a/libs/openzap/src/ozmod/ozmod_sangoma_boost/zap_sangoma_boost.h b/libs/openzap/src/ozmod/ozmod_sangoma_boost/zap_sangoma_boost.h new file mode 100644 index 0000000000..a48060812b --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_sangoma_boost/zap_sangoma_boost.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 ZAP_SANGOMA_BOOST_H +#define ZAP_SANGOMA_BOOST_H +#include "sangoma_boost_client.h" +#include "openzap.h" + +typedef enum { + ZAP_SANGOMA_BOOST_RUNNING = (1 << 0), + ZAP_SANGOMA_BOOST_RESTARTING = (1 << 1) +} zap_sangoma_boost_flag_t; + +typedef struct zap_sangoma_boost_data { + sangomabc_connection_t mcon; + sangomabc_connection_t pcon; + uint32_t flags; +} zap_sangoma_boost_data_t; + +#endif + +/* 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: + */ + diff --git a/libs/openzap/src/ozmod/ozmod_skel/ozmod_skel.c b/libs/openzap/src/ozmod/ozmod_skel/ozmod_skel.c new file mode 100644 index 0000000000..949e0fbf31 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_skel/ozmod_skel.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "openzap.h" +//#include "zap_skel.h" + +static ZIO_CONFIGURE_FUNCTION(skel_configure) +{ + return ZAP_FAIL; +} + +static ZIO_CONFIGURE_SPAN_FUNCTION(skel_configure_span) +{ + return ZAP_FAIL; +} + +static ZIO_OPEN_FUNCTION(skel_open) +{ + return ZAP_FAIL; +} + +static ZIO_CLOSE_FUNCTION(skel_close) +{ + return ZAP_FAIL; +} + +static ZIO_WAIT_FUNCTION(skel_wait) +{ + return ZAP_FAIL; +} + +static ZIO_READ_FUNCTION(skel_read) +{ + return ZAP_FAIL; +} + +static ZIO_WRITE_FUNCTION(skel_write) +{ + return ZAP_FAIL; +} + +static ZIO_COMMAND_FUNCTION(skel_command) +{ + return ZAP_FAIL; +} + +static ZIO_SPAN_POLL_EVENT_FUNCTION(skel_poll_event) +{ + return ZAP_FAIL; +} + +static ZIO_SPAN_NEXT_EVENT_FUNCTION(skel_next_event) +{ + return ZAP_FAIL; +} + +static ZIO_CHANNEL_DESTROY_FUNCTION(skel_channel_destroy) +{ + return ZAP_FAIL; +} + +static ZIO_SPAN_DESTROY_FUNCTION(skel_span_destroy) +{ + return ZAP_FAIL; +} + +static ZIO_GET_ALARMS_FUNCTION(skel_get_alarms) +{ + return ZAP_FAIL; +} + +static zap_io_interface_t skel_interface; + +static ZIO_IO_LOAD_FUNCTION(skel_init) +{ + assert(zio != NULL); + memset(&skel_interface, 0, sizeof(skel_interface)); + + skel_interface.name = "skel"; + skel_interface.configure = skel_configure; + skel_interface.configure_span = skel_configure_span; + skel_interface.open = skel_open; + skel_interface.close = skel_close; + skel_interface.wait = skel_wait; + skel_interface.read = skel_read; + skel_interface.write = skel_write; + skel_interface.command = skel_command; + skel_interface.poll_event = skel_poll_event; + skel_interface.next_event = skel_next_event; + skel_interface.channel_destroy = skel_channel_destroy; + skel_interface.span_destroy = skel_span_destroy; + skel_interface.get_alarms = skel_get_alarms; + *zio = &skel_interface; + + return ZAP_SUCCESS; +} + +static ZIO_IO_UNLOAD_FUNCTION(skel_destroy) +{ + return ZAP_SUCCESS; +} + + +zap_module_t zap_module = { + "skel", + skel_init, + skel_destroy, +}; + + +/* 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: + */ + diff --git a/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.2005.vcproj b/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.2005.vcproj new file mode 100644 index 0000000000..da21a76b7a --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.2005.vcproj @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.2008.vcproj b/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.2008.vcproj new file mode 100644 index 0000000000..c66eb3becb --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.2008.vcproj @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c b/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c new file mode 100644 index 0000000000..87aa0d0079 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_wanpipe/ozmod_wanpipe.c @@ -0,0 +1,1235 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + */ + +#ifdef __sun +#include +#include +#endif +#include "openzap.h" +#ifndef __WINDOWS__ +#include +#include +#endif +#include "libsangoma.h" + +#if defined(__WINDOWS__) +/* remove this when http://jira.freeswitch.org/browse/FSBUILD-259 wanpipe issue is fixed */ +#define WINDOWS_BUILD_BROKEN 1 +/*! Backward compatible defines - current code is all using the old names*/ +#define sangoma_open_tdmapi_span_chan sangoma_open_api_span_chan +#define sangoma_open_tdmapi_span sangoma_open_api_span +#define sangoma_open_tdmapi_ctrl sangoma_open_api_ctrl +#define sangoma_tdm_get_fe_status sangoma_get_fe_status +#define sangoma_socket_close sangoma_close +#define sangoma_tdm_get_hw_coding sangoma_get_hw_coding +#define sangoma_tdm_set_fe_status sangoma_set_fe_status +#define sangoma_tdm_get_link_status sangoma_get_link_status +#define sangoma_tdm_flush_bufs sangoma_flush_bufs +#define sangoma_tdm_cmd_exec sangoma_cmd_exec +#define sangoma_tdm_read_event sangoma_read_event +#define sangoma_readmsg_tdm sangoma_readmsg +#define sangoma_readmsg_socket sangoma_readmsg +#define sangoma_sendmsg_socket sangoma_writemsg +#define sangoma_writemsg_tdm sangoma_writemsg +#define sangoma_create_socket_intr sangoma_open_api_span_chan +#endif + +/*! Starting with libsangoma 3 we can use the new libsangoma waitable API, the poor souls of those using a release were LIBSANGOMA version + * is defined but the version is not higher or equal to 3.0.0 will be forced to upgrade + * */ +#ifdef LIBSANGOMA_VERSION +#if LIBSANGOMA_VERSION_CODE < LIBSANGOMA_VERSION(3,0,0) +#undef LIBSANGOMA_VERSION +#endif +#endif + +/** + * \brief Wanpipe flags + */ +typedef enum { + WP_RINGING = (1 << 0) +} wp_flag_t; + +/** + * \brief Wanpipe globals + */ +static struct { + uint32_t codec_ms; + uint32_t wink_ms; + uint32_t flash_ms; + uint32_t ring_on_ms; + uint32_t ring_off_ms; +} wp_globals; + +/* a bunch of this stuff should go into the wanpipe_tdm_api_iface.h */ + +ZIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event); +ZIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event); + +#define WP_INVALID_SOCKET -1 + +/** + * \brief Poll for event on a wanpipe socket + * \param fd Wanpipe socket descriptor + * \param timeout Time to wait for event + * \param flags Sangoma event flags + * \return -1 on failure, wanpipe event flags on success + * + * a cross platform way to poll on an actual pollset (span and/or list of spans) will probably also be needed for analog + * so we can have one analong handler thread that will deal with all the idle analog channels for events + * the alternative would be for the driver to provide one socket for all of the oob events for all analog channels + */ +static __inline__ int tdmv_api_wait_socket(zap_channel_t *zchan, int timeout, int *flags) +{ + +#ifdef LIBSANGOMA_VERSION + int err; + uint32_t inflags = *flags; + uint32_t outflags = 0; + sangoma_wait_obj_t *sangoma_wait_obj = zchan->mod_data; + + err = sangoma_waitfor(sangoma_wait_obj, inflags, &outflags, timeout); + *flags = 0; + if (err == SANG_STATUS_SUCCESS) { + *flags = outflags; + err = 1; /* ideally should be the number of file descriptors with something to read */ + } + if (err == SANG_STATUS_APIPOLL_TIMEOUT) { + err = 0; + } + return err; +#else + struct pollfd pfds[1]; + int res; + + memset(&pfds[0], 0, sizeof(pfds[0])); + pfds[0].fd = zchan->sockfd; + pfds[0].events = *flags; + res = poll(pfds, 1, timeout); + *flags = 0; + + if (pfds[0].revents & POLLERR) { + res = -1; + } + + if (res > 0) { + *flags = pfds[0].revents; + } + + return res; +#endif + +} + +/** + * \brief Opens a sangoma channel socket (TDM API) + * \param span Span number + * \param chan Channel number + * \return 0 on success, wanpipe error code on failure + */ +static __inline__ sng_fd_t tdmv_api_open_span_chan(int span, int chan) +{ + return sangoma_open_tdmapi_span_chan(span, chan); +} + +#ifdef LIBSANGOMA_VERSION +static __inline__ sng_fd_t __tdmv_api_open_span_chan(int span, int chan) +{ + return __sangoma_open_tdmapi_span_chan(span, chan); +} +#endif + +static zap_io_interface_t wanpipe_interface; + +/** + * \brief Inverts bit string + * \param cas_bits CAS bit string + * \return Swapped bits + */ +static unsigned char wanpipe_swap_bits(unsigned char cas_bits) +{ + unsigned char swapped_bits = 0x0; + if (cas_bits & 0x8) { + swapped_bits |= 0x1; + } + if (cas_bits & 0x4) { + swapped_bits |= 0x2; + } + if (cas_bits & 0x2) { + swapped_bits |= 0x4; + } + if (cas_bits & 0x1) { + swapped_bits |= 0x8; + } + return swapped_bits; +} + +/** + * \brief Initialises a range of wanpipe channels + * \param span Openzap span + * \param spanno Wanpipe span number + * \param start Initial wanpipe channel number + * \param end Final wanpipe channel number + * \param type Openzap channel type + * \param name Openzap span name + * \param number Openzap span number + * \param cas_bits CAS bits + * \return number of spans configured + */ +static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start, unsigned end, zap_chan_type_t type, char *name, char *number, unsigned char cas_bits) +{ + unsigned configured = 0, x; +#ifdef LIBSANGOMA_VERSION + sangoma_status_t sangstatus; + sangoma_wait_obj_t *sangoma_wait_obj; +#endif + + if (type == ZAP_CHAN_TYPE_CAS) { + zap_log(ZAP_LOG_DEBUG, "Configuring Wanpipe CAS channels with abcd == 0x%X\n", cas_bits); + } + for(x = start; x < end; x++) { + zap_channel_t *chan; + zap_socket_t sockfd = WP_INVALID_SOCKET; + const char *dtmf = "none"; + if (!strncasecmp(span->name, "smg_prid_nfas", 8) && span->trunk_type == ZAP_TRUNK_T1 && x == 24) { +#ifdef LIBSANGOMA_VERSION + sockfd = __tdmv_api_open_span_chan(spanno, x); +#else + zap_log(ZAP_LOG_ERROR, "span %d channel %d cannot be configured as smg_prid_nfas, you need to compile openzap with newer libsangoma\n", spanno, x); +#endif + } else { + sockfd = tdmv_api_open_span_chan(spanno, x); + } + + if (sockfd == WP_INVALID_SOCKET) { + zap_log(ZAP_LOG_ERROR, "Failed to open wanpipe device span %d channel %d\n", spanno, x); + continue; + } + + if (zap_span_add_channel(span, sockfd, type, &chan) == ZAP_SUCCESS) { + wanpipe_tdm_api_t tdm_api; + memset(&tdm_api, 0, sizeof(tdm_api)); +#ifdef LIBSANGOMA_VERSION + sangstatus = sangoma_wait_obj_create(&sangoma_wait_obj, sockfd, SANGOMA_DEVICE_WAIT_OBJ); + if (sangstatus != SANG_STATUS_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "failure create waitable object for s%dc%d\n", spanno, x); + continue; + } + chan->mod_data = sangoma_wait_obj; +#endif + + chan->physical_span_id = spanno; + chan->physical_chan_id = x; + chan->rate = 8000; + + if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO || type == ZAP_CHAN_TYPE_B) { + int err; + + dtmf = "software"; + + /* FIXME: Handle Error Condition Check for return code */ + err = sangoma_tdm_get_hw_coding(chan->sockfd, &tdm_api); + + if (tdm_api.wp_tdm_cmd.hw_tdm_coding) { + chan->native_codec = chan->effective_codec = ZAP_CODEC_ALAW; + } else { + chan->native_codec = chan->effective_codec = ZAP_CODEC_ULAW; + } + + err = sangoma_tdm_get_hw_dtmf(chan->sockfd, &tdm_api); + if (err > 0) { + err = sangoma_tdm_enable_dtmf_events(chan->sockfd, &tdm_api); + if (err == 0) { + zap_channel_set_feature(chan, ZAP_CHANNEL_FEATURE_DTMF_DETECT); + dtmf = "hardware"; + } + } + } + +#ifdef LIBSANGOMA_VERSION + if (type == ZAP_CHAN_TYPE_FXS) { + if (sangoma_tdm_disable_ring_trip_detect_events(chan->sockfd, &tdm_api)) { + /* we had problems of on-hook/off-hook detection due to how ring trip events were handled + * if this fails, I believe we will still work ok as long as we dont handle them incorrectly */ + zap_log(ZAP_LOG_WARNING, "Failed to disable ring trip events in channel s%dc%d\n", spanno, x); + } + } +#endif +#if 0 + if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO) { + /* Enable FLASH/Wink Events */ + int err=sangoma_set_rm_rxflashtime(chan->sockfd, &tdm_api, wp_globals.flash_ms); + if (err == 0) { + zap_log(ZAP_LOG_ERROR, "flash enabled s%dc%d\n", spanno, x); + } else { + zap_log(ZAP_LOG_ERROR, "flash disabled s%dc%d\n", spanno, x); + } + } +#endif + + if (type == ZAP_CHAN_TYPE_CAS || type == ZAP_CHAN_TYPE_EM) { +#ifdef LIBSANGOMA_VERSION + sangoma_tdm_write_rbs(chan->sockfd,&tdm_api,chan->physical_chan_id, wanpipe_swap_bits(cas_bits)); + + /* this should probably be done for old libsangoma but I am not sure if the API is available and I'm lazy to check, + The poll rate is hard coded to 100 per second (done in the driver, is the max rate of polling allowed by wanpipe) + */ + if (sangoma_tdm_enable_rbs_events(chan->sockfd, &tdm_api, 100)) { + zap_log(ZAP_LOG_ERROR, "Failed to enable RBS/CAS events in device %d:%d fd:%d\n", chan->span_id, chan->chan_id, sockfd); + continue; + } + /* probably done by the driver but lets write defensive code this time */ + sangoma_flush_bufs(chan->sockfd, &tdm_api); +#else + /* + * With wanpipe 3.4.4.2 I get failure even though the events are enabled, /var/log/messages said: + * wanpipe4: WARNING: Event type 9 is already pending! + * wanpipe4: Failed to add new fe event 09 ch_map=FFFFFFFF! + * may be we should not send an error until that is fixed in the driver + */ + if (sangoma_tdm_enable_rbs_events(chan->sockfd, &tdm_api, 100)) { + zap_log(ZAP_LOG_ERROR, "Failed to enable RBS/CAS events in device %d:%d fd:%d\n", chan->span_id, chan->chan_id, sockfd); + } + /* probably done by the driver but lets write defensive code this time */ + sangoma_tdm_flush_bufs(chan->sockfd, &tdm_api); + sangoma_tdm_write_rbs(chan->sockfd,&tdm_api, wanpipe_swap_bits(cas_bits)); +#endif + } + + if (!zap_strlen_zero(name)) { + zap_copy_string(chan->chan_name, name, sizeof(chan->chan_name)); + } + + if (!zap_strlen_zero(number)) { + zap_copy_string(chan->chan_number, number, sizeof(chan->chan_number)); + } + configured++; + zap_log(ZAP_LOG_INFO, "configuring device s%dc%d as OpenZAP device %d:%d fd:%d DTMF: %s\n", + spanno, x, chan->span_id, chan->chan_id, sockfd, dtmf); + + } else { + zap_log(ZAP_LOG_ERROR, "zap_span_add_channel failed for wanpipe span %d channel %d\n", spanno, x); + } + } + + return configured; +} + +/** + * \brief Process configuration variable for a Wanpipe profile + * \param category Wanpipe profile name + * \param var Variable name + * \param val Variable value + * \param lineno Line number from configuration file + * \return Success + */ +static ZIO_CONFIGURE_FUNCTION(wanpipe_configure) +{ + int num; + + if (!strcasecmp(category, "defaults")) { + if (!strcasecmp(var, "codec_ms")) { + num = atoi(val); + if (num < 10 || num > 60) { + zap_log(ZAP_LOG_WARNING, "invalid codec ms at line %d\n", lineno); + } else { + wp_globals.codec_ms = num; + } + } else if (!strcasecmp(var, "wink_ms")) { + num = atoi(val); + if (num < 50 || num > 3000) { + zap_log(ZAP_LOG_WARNING, "invalid wink ms at line %d\n", lineno); + } else { + wp_globals.wink_ms = num; + } + } else if (!strcasecmp(var, "flash_ms")) { + num = atoi(val); + if (num < 50 || num > 3000) { + zap_log(ZAP_LOG_WARNING, "invalid flash ms at line %d\n", lineno); + } else { + wp_globals.flash_ms = num; + } + } else if (!strcasecmp(var, "ring_on_ms")) { + num = atoi(val); + if (num < 500 || num > 5000) { + zap_log(ZAP_LOG_WARNING, "invalid ring_on_ms at line %d (valid range 500 to 5000)\n", lineno); + } else { + wp_globals.ring_on_ms = num; + } + } else if (!strcasecmp(var, "ring_off_ms")) { + num = atoi(val); + if (num < 500 || num > 5000) { + zap_log(ZAP_LOG_WARNING, "invalid ring_off_ms at line %d (valid range 500 to 5000)\n", lineno); + } else { + wp_globals.ring_off_ms = num; + } + } + } + + return ZAP_SUCCESS; +} + +/** + * \brief Initialises an openzap Wanpipe span from a configuration string + * \param span Openzap span + * \param str Configuration string + * \param type Openzap span type + * \param name Openzap span name + * \param number Openzap span number + * \return Success or failure + */ +static ZIO_CONFIGURE_SPAN_FUNCTION(wanpipe_configure_span) +{ + int items, i; + char *mydata, *item_list[10]; + char *sp, *ch, *mx; + unsigned char cas_bits = 0; + int channo; + int spanno; + int top = 0; + unsigned configured = 0; + + assert(str != NULL); + + + mydata = strdup(str); + assert(mydata != NULL); + + + items = zap_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0]))); + + for(i = 0; i < items; i++) { + sp = item_list[i]; + if ((ch = strchr(sp, ':'))) { + *ch++ = '\0'; + } + + if (!(sp && ch)) { + zap_log(ZAP_LOG_ERROR, "No valid wanpipe span and channel was specified\n"); + continue; + } + + channo = atoi(ch); + spanno = atoi(sp); + + if (channo < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid channel number %d\n", channo); + continue; + } + + if (spanno < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid span number %d\n", channo); + continue; + } + + if ((mx = strchr(ch, '-'))) { + mx++; + top = atoi(mx) + 1; + } else { + top = channo + 1; + } + + + if (top < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid range number %d\n", top); + continue; + } + if (ZAP_CHAN_TYPE_CAS == type && zap_config_get_cas_bits(ch, &cas_bits)) { + zap_log(ZAP_LOG_ERROR, "Failed to get CAS bits in CAS channel\n"); + continue; + } + configured += wp_open_range(span, spanno, channo, top, type, name, number, cas_bits); + + } + + free(mydata); + + return configured; +} + +/** + * \brief Opens Wanpipe channel + * \param zchan Channel to open + * \return Success or failure + */ +static ZIO_OPEN_FUNCTION(wanpipe_open) +{ + + wanpipe_tdm_api_t tdm_api; + + memset(&tdm_api,0,sizeof(tdm_api)); + sangoma_tdm_flush_bufs(zchan->sockfd, &tdm_api); +#ifdef LIBSANGOMA_VERSION + sangoma_flush_event_bufs(zchan->sockfd, &tdm_api); +#endif + + if (zchan->type == ZAP_CHAN_TYPE_DQ921 || zchan->type == ZAP_CHAN_TYPE_DQ931) { + zchan->native_codec = zchan->effective_codec = ZAP_CODEC_NONE; + } else { + zchan->effective_codec = zchan->native_codec; + + sangoma_tdm_set_usr_period(zchan->sockfd, &tdm_api, wp_globals.codec_ms); + + zap_channel_set_feature(zchan, ZAP_CHANNEL_FEATURE_INTERVAL); + zchan->effective_interval = zchan->native_interval = wp_globals.codec_ms; + zchan->packet_len = zchan->native_interval * 8; + } + + return ZAP_SUCCESS; +} + +/** + * \brief Closes Wanpipe channel + * \param zchan Channel to close + * \return Success + */ +static ZIO_CLOSE_FUNCTION(wanpipe_close) +{ + return ZAP_SUCCESS; +} + +/** + * \brief Executes an Openzap command on a Wanpipe channel + * \param zchan Channel to execute command on + * \param command Openzap command to execute + * \param obj Object (unused) + * \return Success or failure + */ +static ZIO_COMMAND_FUNCTION(wanpipe_command) +{ + wanpipe_tdm_api_t tdm_api; + int err = 0; + + memset(&tdm_api, 0, sizeof(tdm_api)); + + switch(command) { + case ZAP_COMMAND_OFFHOOK: + { + err=sangoma_tdm_txsig_offhook(zchan->sockfd,&tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "OFFHOOK Failed"); + return ZAP_FAIL; + } + zap_set_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + } + break; + case ZAP_COMMAND_ONHOOK: + { + err=sangoma_tdm_txsig_onhook(zchan->sockfd,&tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "ONHOOK Failed"); + return ZAP_FAIL; + } + zap_clear_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + } + break; + case ZAP_COMMAND_GENERATE_RING_ON: + { + err=sangoma_tdm_txsig_start(zchan->sockfd,&tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring Failed"); + return ZAP_FAIL; + } + zap_set_flag_locked(zchan, ZAP_CHANNEL_RINGING); + zap_set_pflag_locked(zchan, WP_RINGING); + zchan->ring_time = zap_current_time_in_ms() + wp_globals.ring_on_ms; + } + break; + case ZAP_COMMAND_GENERATE_RING_OFF: + { + err=sangoma_tdm_txsig_offhook(zchan->sockfd,&tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring-off Failed"); + return ZAP_FAIL; + } + zap_clear_pflag_locked(zchan, WP_RINGING); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_RINGING); + } + break; + case ZAP_COMMAND_GET_INTERVAL: + { + err=sangoma_tdm_get_usr_period(zchan->sockfd, &tdm_api); + if (err > 0 ) { + ZAP_COMMAND_OBJ_INT = err; + err=0; + } + } + break; + case ZAP_COMMAND_ENABLE_ECHOCANCEL: + { +#ifndef WINDOWS_BUILD_BROKEN + err=sangoma_tdm_enable_hwec(zchan->sockfd, &tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "HWEC Enable Failed"); + return ZAP_FAIL; + } +#endif /* WINDOWS_BUILD_BROKEN */ + } + break; + case ZAP_COMMAND_DISABLE_ECHOCANCEL: + { +#ifndef WINDOWS_BUILD_BROKEN + err=sangoma_tdm_disable_hwec(zchan->sockfd, &tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "HWEC Disable Failed"); + return ZAP_FAIL; + } +#endif /* WINDOWS_BUILD_BROKEN */ + } + break; + case ZAP_COMMAND_ENABLE_LOOP: + { +#ifndef WINDOWS_BUILD_BROKEN +#ifdef WP_API_FEATURE_LOOP + err=sangoma_tdm_enable_loop(zchan->sockfd, &tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Loop Enable Failed"); + return ZAP_FAIL; + } +#endif +#endif /* WINDOWS_BUILD_BROKEN */ + } + case ZAP_COMMAND_DISABLE_LOOP: + { +#ifndef WINDOWS_BUILD_BROKEN +#ifdef WP_API_FEATURE_LOOP + err=sangoma_tdm_disable_loop(zchan->sockfd, &tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Loop Disable Failed"); + return ZAP_FAIL; + } +#endif +#endif /* WINDOWS_BUILD_BROKEN */ + } + case ZAP_COMMAND_SET_INTERVAL: + { + err=sangoma_tdm_set_usr_period(zchan->sockfd, &tdm_api, ZAP_COMMAND_OBJ_INT); + zchan->packet_len = zchan->native_interval * (zchan->effective_codec == ZAP_CODEC_SLIN ? 16 : 8); + } + break; + case ZAP_COMMAND_SET_CAS_BITS: + { +#ifdef LIBSANGOMA_VERSION + err = sangoma_tdm_write_rbs(zchan->sockfd,&tdm_api, zchan->physical_chan_id, wanpipe_swap_bits(ZAP_COMMAND_OBJ_INT)); +#else + err = sangoma_tdm_write_rbs(zchan->sockfd, &tdm_api, wanpipe_swap_bits(ZAP_COMMAND_OBJ_INT)); +#endif + } + break; + case ZAP_COMMAND_GET_CAS_BITS: + { +#ifdef LIBSANGOMA_VERSION + unsigned char rbsbits; + err = sangoma_tdm_read_rbs(zchan->sockfd, &tdm_api, zchan->physical_chan_id, &rbsbits); + if (!err) { + ZAP_COMMAND_OBJ_INT = wanpipe_swap_bits(rbsbits); + } +#else + // does sangoma_tdm_read_rbs is available here? + ZAP_COMMAND_OBJ_INT = zchan->rx_cas_bits; +#endif + } + break; + default: + break; + }; + + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + + return ZAP_SUCCESS; +} + +/** + * \brief Reads data from a Wanpipe channel + * \param zchan Channel to read from + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success, failure or timeout + */ +static ZIO_READ_FUNCTION(wanpipe_read) +{ + int rx_len = 0; + wp_tdm_api_rx_hdr_t hdrframe; + + memset(&hdrframe, 0, sizeof(hdrframe)); + + rx_len = sangoma_readmsg_tdm(zchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (int)*datalen,0); + *datalen = rx_len; + + if (rx_len == 0 || rx_len == -17) { + return ZAP_TIMEOUT; + } + + if (rx_len < 0) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + + return ZAP_SUCCESS; +} + +/** + * \brief Writes data to a Wanpipe channel + * \param zchan Channel to write to + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success or failure + */ +static ZIO_WRITE_FUNCTION(wanpipe_write) +{ + int bsent; + wp_tdm_api_tx_hdr_t hdrframe; + + /* Do we even need the headerframe here? on windows, we don't even pass it to the driver */ + memset(&hdrframe, 0, sizeof(hdrframe)); + if (*datalen == 0) { + return ZAP_SUCCESS; + } + bsent = sangoma_writemsg_tdm(zchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (unsigned short)(*datalen),0); + + /* should we be checking if bsent == *datalen here? */ + if (bsent > 0) { + *datalen = bsent; + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +/** + * \brief Waits for an event on a Wanpipe channel + * \param zchan Channel to open + * \param flags Type of event to wait for + * \param to Time to wait (in ms) + * \return Success, failure or timeout + */ + +static ZIO_WAIT_FUNCTION(wanpipe_wait) +{ + int32_t inflags = 0; + int result; + + if (*flags & ZAP_READ) { + inflags |= POLLIN; + } + + if (*flags & ZAP_WRITE) { + inflags |= POLLOUT; + } + + if (*flags & ZAP_EVENTS) { + inflags |= POLLPRI; + } + + result = tdmv_api_wait_socket(zchan, to, &inflags); + + *flags = ZAP_NO_FLAGS; + + if (result < 0){ + snprintf(zchan->last_error, sizeof(zchan->last_error), "Poll failed"); + return ZAP_FAIL; + } + + if (result == 0) { + return ZAP_TIMEOUT; + } + + if (inflags & POLLIN) { + *flags |= ZAP_READ; + } + + if (inflags & POLLOUT) { + *flags |= ZAP_WRITE; + } + + if (inflags & POLLPRI) { + *flags |= ZAP_EVENTS; + } + + return ZAP_SUCCESS; +} + +/** + * \brief Checks for events on a Wanpipe span + * \param span Span to check for events + * \param ms Time to wait for event + * \return Success if event is waiting or failure if not + */ +ZIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event) +{ +#ifdef LIBSANGOMA_VERSION + sangoma_status_t sangstatus; + sangoma_wait_obj_t *pfds[ZAP_MAX_CHANNELS_SPAN] = { 0 }; + uint32_t inflags[ZAP_MAX_CHANNELS_SPAN]; + uint32_t outflags[ZAP_MAX_CHANNELS_SPAN]; +#else + struct pollfd pfds[ZAP_MAX_CHANNELS_SPAN]; +#endif + + uint32_t i, j = 0, k = 0, l = 0; + int r; + + for(i = 1; i <= span->chan_count; i++) { + zap_channel_t *zchan = span->channels[i]; + + if (!strncasecmp(span->name, "smg_prid_nfas", 8) && span->trunk_type == ZAP_TRUNK_T1 && zchan->physical_chan_id == 24) { + continue; + } + +#ifdef LIBSANGOMA_VERSION + if (!zchan->mod_data) { + continue; /* should never happen but happens when shutting down */ + } + pfds[j] = zchan->mod_data; + inflags[j] = POLLPRI; +#else + memset(&pfds[j], 0, sizeof(pfds[j])); + pfds[j].fd = span->channels[i]->sockfd; + pfds[j].events = POLLPRI; +#endif + + /* The driver probably should be able to do this wink/flash/ringing by itself this is sort of a hack to make it work! */ + + if (zap_test_flag(zchan, ZAP_CHANNEL_WINK) || zap_test_flag(zchan, ZAP_CHANNEL_FLASH)) { + l = 5; + } + + j++; + + if (zap_test_flag(zchan, ZAP_CHANNEL_RINGING)) { + l = 5; + } + + if (zap_test_flag(zchan, ZAP_CHANNEL_RINGING) && zap_current_time_in_ms() >= zchan->ring_time) { + wanpipe_tdm_api_t tdm_api; + int err; + memset(&tdm_api, 0, sizeof(tdm_api)); + if (zap_test_pflag(zchan, WP_RINGING)) { + err=sangoma_tdm_txsig_offhook(zchan->sockfd,&tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring-off Failed"); + return ZAP_FAIL; + } + zap_clear_pflag_locked(zchan, WP_RINGING); + zchan->ring_time = zap_current_time_in_ms() + wp_globals.ring_off_ms; + } else { + err=sangoma_tdm_txsig_start(zchan->sockfd,&tdm_api); + if (err) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring Failed"); + return ZAP_FAIL; + } + zap_set_pflag_locked(zchan, WP_RINGING); + zchan->ring_time = zap_current_time_in_ms() + wp_globals.ring_on_ms; + } + } + } + + if (l) { + ms = l; + } +#ifdef LIBSANGOMA_VERSION + sangstatus = sangoma_waitfor_many(pfds, inflags, outflags, j, ms); + if (SANG_STATUS_APIPOLL_TIMEOUT == sangstatus) { + r = 0; + } else if (SANG_STATUS_SUCCESS == sangstatus) { + r = 1; /* hopefully we never need how many changed -_- */ + } else { + r = -1; + } +#else + r = poll(pfds, j, ms); +#endif + + if (r == 0) { + return l ? ZAP_SUCCESS : ZAP_TIMEOUT; + } else if (r < 0) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + for(i = 1; i <= span->chan_count; i++) { + zap_channel_t *zchan = span->channels[i]; + +#ifdef LIBSANGOMA_VERSION + if (outflags[i-1] & POLLPRI) { +#else + if (pfds[i-1].revents & POLLPRI) { +#endif + zap_set_flag(zchan, ZAP_CHANNEL_EVENT); + zchan->last_event_time = zap_current_time_in_ms(); + k++; + } + } + + + return k ? ZAP_SUCCESS : ZAP_FAIL; +} + +/** + * \brief Gets alarms from a Wanpipe Channel + * \param zchan Channel to get alarms from + * \return Success or failure + */ +static ZIO_GET_ALARMS_FUNCTION(wanpipe_get_alarms) +{ + wanpipe_tdm_api_t tdm_api; + unsigned int alarms = 0; + int err; + + memset(&tdm_api,0,sizeof(tdm_api)); + +#ifdef LIBSANGOMA_VERSION + if ((err = sangoma_tdm_get_fe_alarms(zchan->sockfd, &tdm_api, &alarms))) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "ioctl failed (%s)", strerror(errno)); + snprintf(zchan->span->last_error, sizeof(zchan->span->last_error), "ioctl failed (%s)", strerror(errno)); + return ZAP_FAIL; + } +#else + if ((err = sangoma_tdm_get_fe_alarms(zchan->sockfd, &tdm_api)) < 0){ + snprintf(zchan->last_error, sizeof(zchan->last_error), "ioctl failed (%s)", strerror(errno)); + snprintf(zchan->span->last_error, sizeof(zchan->span->last_error), "ioctl failed (%s)", strerror(errno)); + return ZAP_FAIL; + } + alarms = tdm_api.wp_tdm_cmd.fe_alarms; +#endif + zchan->alarm_flags = ZAP_ALARM_NONE; + + if (alarms & WAN_TE_BIT_ALARM_RED) { + zchan->alarm_flags |= ZAP_ALARM_RED; + alarms &= ~WAN_TE_BIT_ALARM_RED; + } + + if (alarms & WAN_TE_BIT_ALARM_AIS) { + zchan->alarm_flags |= ZAP_ALARM_AIS; + zchan->alarm_flags |= ZAP_ALARM_BLUE; + alarms &= ~WAN_TE_BIT_ALARM_AIS; + } + + if (alarms & WAN_TE_BIT_ALARM_RAI) { + zchan->alarm_flags |= ZAP_ALARM_RAI; + zchan->alarm_flags |= ZAP_ALARM_YELLOW; + alarms &= ~WAN_TE_BIT_ALARM_RAI; + } + + /* still missing to map: + * ZAP_ALARM_RECOVER + * ZAP_ALARM_LOOPBACK + * ZAP_ALARM_NOTOPEN + * */ + + /* if we still have alarms that we did not map, set the general alarm */ + if (alarms) { + zap_log(ZAP_LOG_DEBUG, "Unmapped wanpipe alarms: %d\n", alarms); + zchan->alarm_flags |= ZAP_ALARM_GENERAL; + } + + return ZAP_SUCCESS; +} + +/** + * \brief Retrieves an event from a wanpipe span + * \param span Span to retrieve event from + * \param event Openzap event to return + * \return Success or failure + */ +ZIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event) +{ + uint32_t i,err; + zap_oob_event_t event_id; + for(i = 1; i <= span->chan_count; i++) { + if (span->channels[i]->last_event_time && !zap_test_flag(span->channels[i], ZAP_CHANNEL_EVENT)) { + uint32_t diff = (uint32_t)(zap_current_time_in_ms() - span->channels[i]->last_event_time); + /* XX printf("%u %u %u\n", diff, (unsigned)zap_current_time_in_ms(), (unsigned)span->channels[i]->last_event_time); */ + if (zap_test_flag(span->channels[i], ZAP_CHANNEL_WINK)) { + if (diff > wp_globals.wink_ms) { + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_WINK); + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH); + zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK); + event_id = ZAP_OOB_OFFHOOK; + goto event; + } + } + + if (zap_test_flag(span->channels[i], ZAP_CHANNEL_FLASH)) { + if (diff > wp_globals.flash_ms) { + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH); + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_WINK); + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK); + event_id = ZAP_OOB_ONHOOK; + + if (span->channels[i]->type == ZAP_CHAN_TYPE_FXO) { + zap_channel_t *zchan = span->channels[i]; + wanpipe_tdm_api_t tdm_api; + memset(&tdm_api, 0, sizeof(tdm_api)); + + sangoma_tdm_txsig_onhook(zchan->sockfd,&tdm_api); + } + goto event; + } + } + } + if (zap_test_flag(span->channels[i], ZAP_CHANNEL_EVENT)) { + wanpipe_tdm_api_t tdm_api; + zap_channel_t *zchan = span->channels[i]; + memset(&tdm_api, 0, sizeof(tdm_api)); + zap_clear_flag(span->channels[i], ZAP_CHANNEL_EVENT); + + err = sangoma_tdm_read_event(zchan->sockfd, &tdm_api); + if (err != ZAP_SUCCESS) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) { + + case WP_TDMAPI_EVENT_LINK_STATUS: + { + zap_sigmsg_t sigmsg; + memset(&sigmsg, 0, sizeof(sigmsg)); + switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) { + case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED: + event_id = ZAP_OOB_ALARM_CLEAR; + break; + default: + event_id = ZAP_OOB_ALARM_TRAP; + break; + }; + sigmsg.chan_id = zchan->chan_id; + sigmsg.span_id = zchan->span_id; + sigmsg.channel = zchan; + sigmsg.event_id = (event_id == ZAP_OOB_ALARM_CLEAR) ? ZAP_SIGEVENT_ALARM_CLEAR : ZAP_SIGEVENT_ALARM_TRAP; + zap_span_send_signal(zchan->span, &sigmsg); + } + break; + + case WP_TDMAPI_EVENT_RXHOOK: + { + if (span->channels[i]->type == ZAP_CHAN_TYPE_FXS) { + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? ZAP_OOB_OFFHOOK : ZAP_OOB_ONHOOK; + if (event_id == ZAP_OOB_OFFHOOK) { + if (zap_test_flag(span->channels[i], ZAP_CHANNEL_FLASH)) { + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH); + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_WINK); + event_id = ZAP_OOB_FLASH; + goto event; + } else { + zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_WINK); + } + } else { + if (zap_test_flag(span->channels[i], ZAP_CHANNEL_WINK)) { + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_WINK); + zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH); + event_id = ZAP_OOB_WINK; + goto event; + } else { + zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH); + } + } + continue; + } else { + int err; + zap_channel_t *zchan = span->channels[i]; + err=sangoma_tdm_txsig_onhook(zchan->sockfd,&tdm_api); + if (err) { + snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "ONHOOK Failed"); + return ZAP_FAIL; + } + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? ZAP_OOB_ONHOOK : ZAP_OOB_NOOP; + } + } + break; + case WP_TDMAPI_EVENT_RING_DETECT: + { + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? ZAP_OOB_RING_START : ZAP_OOB_RING_STOP; + } + break; + /* + disabled this ones when configuring, we don't need them, do we? + case WP_TDMAPI_EVENT_RING_TRIP_DETECT: + { + event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? ZAP_OOB_ONHOOK : ZAP_OOB_OFFHOOK; + } + break; + */ + case WP_TDMAPI_EVENT_RBS: + { + event_id = ZAP_OOB_CAS_BITS_CHANGE; + span->channels[i]->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits); + } + break; + case WP_TDMAPI_EVENT_DTMF: + { + char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 }; + event_id = ZAP_OOB_NOOP; + + //zap_log(ZAP_LOG_WARNING, "%d:%d queue hardware dtmf %s %s\n", zchan->span_id, zchan->chan_id, tmp_dtmf, + //tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT ? "on" : "off"); + if (tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT) { + zap_set_flag_locked(zchan, ZAP_CHANNEL_MUTE); + } + + if (tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_type == WAN_EC_TONE_STOP) { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_MUTE); + if (zap_test_flag(zchan, ZAP_CHANNEL_INUSE)) { + zap_channel_queue_dtmf(zchan, tmp_dtmf); + } + } + } + break; + case WP_TDMAPI_EVENT_ALARM: + { + zap_sigmsg_t sigmsg; + zap_log(ZAP_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm); + memset(&sigmsg, 0, sizeof(sigmsg)); + event_id = ZAP_OOB_ALARM_TRAP; + sigmsg.chan_id = zchan->chan_id; + sigmsg.span_id = zchan->span_id; + sigmsg.channel = zchan; + sigmsg.event_id = (event_id == ZAP_OOB_ALARM_CLEAR) ? ZAP_SIGEVENT_ALARM_CLEAR : ZAP_SIGEVENT_ALARM_TRAP; + zap_span_send_signal(zchan->span, &sigmsg); + } + break; + default: + { + zap_log(ZAP_LOG_WARNING, "Unhandled event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type); + event_id = ZAP_OOB_INVALID; + } + break; + } + + event: + + span->channels[i]->last_event_time = 0; + span->event_header.e_type = ZAP_EVENT_OOB; + span->event_header.enum_id = event_id; + span->event_header.channel = span->channels[i]; + *event = &span->event_header; + return ZAP_SUCCESS; + } + } + + return ZAP_FAIL; + +} + +/** + * \brief Destroys a Wanpipe Channel + * \param zchan Channel to destroy + * \return Success + */ +static ZIO_CHANNEL_DESTROY_FUNCTION(wanpipe_channel_destroy) +{ +#ifdef LIBSANGOMA_VERSION + if (zchan->mod_data) { + sangoma_wait_obj_t *sangoma_wait_obj; + sangoma_wait_obj = zchan->mod_data; + zchan->mod_data = NULL; + sangoma_wait_obj_delete(&sangoma_wait_obj); + } +#endif + + if (zchan->sockfd > -1) { + close(zchan->sockfd); + zchan->sockfd = WP_INVALID_SOCKET; + } + + return ZAP_SUCCESS; +} + +/** + * \brief Loads wanpipe IO module + * \param zio Openzap IO interface + * \return Success + */ +static ZIO_IO_LOAD_FUNCTION(wanpipe_init) +{ + assert(zio != NULL); + memset(&wanpipe_interface, 0, sizeof(wanpipe_interface)); + + wp_globals.codec_ms = 20; + wp_globals.wink_ms = 150; + wp_globals.flash_ms = 750; + wp_globals.ring_on_ms = 2000; + wp_globals.ring_off_ms = 4000; + wanpipe_interface.name = "wanpipe"; + wanpipe_interface.configure_span = wanpipe_configure_span; + wanpipe_interface.configure = wanpipe_configure; + wanpipe_interface.open = wanpipe_open; + wanpipe_interface.close = wanpipe_close; + wanpipe_interface.command = wanpipe_command; + wanpipe_interface.wait = wanpipe_wait; + wanpipe_interface.read = wanpipe_read; + wanpipe_interface.write = wanpipe_write; + wanpipe_interface.poll_event = wanpipe_poll_event; + wanpipe_interface.next_event = wanpipe_next_event; + wanpipe_interface.channel_destroy = wanpipe_channel_destroy; + wanpipe_interface.get_alarms = wanpipe_get_alarms; + *zio = &wanpipe_interface; + + return ZAP_SUCCESS; +} + +/** + * \brief Unloads wanpipe IO module + * \return Success + */ +static ZIO_IO_UNLOAD_FUNCTION(wanpipe_destroy) +{ + memset(&wanpipe_interface, 0, sizeof(wanpipe_interface)); + return ZAP_SUCCESS; +} + +/** + * \brief Openzap wanpipe IO module definition + */ +EX_DECLARE_DATA zap_module_t zap_module = { + "wanpipe", + wanpipe_init, + wanpipe_destroy, +}; + +/* 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 + */ diff --git a/libs/openzap/src/ozmod/ozmod_wanpipe/wanpipe_tdm_api_iface.h b/libs/openzap/src/ozmod/ozmod_wanpipe/wanpipe_tdm_api_iface.h new file mode 100644 index 0000000000..3801ff9c8c --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_wanpipe/wanpipe_tdm_api_iface.h @@ -0,0 +1,351 @@ +/***************************************************************************** +* wanpipe_tdm_api_iface.h +* +* WANPIPE(tm) AFT TE1 Hardware Support +* +* Authors: Nenad Corbic +* +* Copyright (c) 2007 - 08, Sangoma Technologies +* 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 nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY ``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 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. + +* ============================================================================ +* Oct 04, 2005 Nenad Corbic Initial version. +* +* Jul 25, 2006 David Rokhvarg Ported to Windows. +*****************************************************************************/ + +#ifndef __WANPIPE_TDM_API_IFACE_H_ +#define __WANPIPE_TDM_API_IFACE_H_ + + +#if defined(__WINDOWS__) +typedef HANDLE sng_fd_t; +#else +typedef int sng_fd_t; +#endif + +/* Indicate to library that new features exist */ +#define WP_TDM_FEATURE_DTMF_EVENTS 1 +#define WP_TDM_FEATURE_FE_ALARM 1 +#define WP_TDM_FEATURE_EVENTS 1 +#define WP_TDM_FEATURE_LINK_STATUS 1 + +enum wanpipe_tdm_api_cmds { + + SIOC_WP_TDM_GET_USR_MTU_MRU, /* 0x00 */ + + SIOC_WP_TDM_SET_USR_PERIOD, /* 0x01 */ + SIOC_WP_TDM_GET_USR_PERIOD, /* 0x02 */ + + SIOC_WP_TDM_SET_HW_MTU_MRU, /* 0x03 */ + SIOC_WP_TDM_GET_HW_MTU_MRU, /* 0x04 */ + + SIOC_WP_TDM_SET_CODEC, /* 0x05 */ + SIOC_WP_TDM_GET_CODEC, /* 0x06 */ + + SIOC_WP_TDM_SET_POWER_LEVEL, /* 0x07 */ + SIOC_WP_TDM_GET_POWER_LEVEL, /* 0x08 */ + + SIOC_WP_TDM_TOGGLE_RX, /* 0x09 */ + SIOC_WP_TDM_TOGGLE_TX, /* 0x0A */ + + SIOC_WP_TDM_GET_HW_CODING, /* 0x0B */ + SIOC_WP_TDM_SET_HW_CODING, /* 0x0C */ + + SIOC_WP_TDM_GET_FULL_CFG, /* 0x0D */ + + SIOC_WP_TDM_SET_EC_TAP, /* 0x0E */ + SIOC_WP_TDM_GET_EC_TAP, /* 0x0F */ + + SIOC_WP_TDM_ENABLE_RBS_EVENTS, /* 0x10 */ + SIOC_WP_TDM_DISABLE_RBS_EVENTS, /* 0x11 */ + SIOC_WP_TDM_WRITE_RBS_BITS, /* 0x12 */ + + SIOC_WP_TDM_GET_STATS, /* 0x13 */ + SIOC_WP_TDM_FLUSH_BUFFERS, /* 0x14 */ + + SIOC_WP_TDM_READ_EVENT, /* 0x15 */ + + SIOC_WP_TDM_SET_EVENT, /* 0x16 */ + + SIOC_WP_TDM_SET_RX_GAINS, /* 0x17 */ + SIOC_WP_TDM_SET_TX_GAINS, /* 0x18 */ + SIOC_WP_TDM_CLEAR_RX_GAINS, /* 0x19 */ + SIOC_WP_TDM_CLEAR_TX_GAINS, /* 0x1A */ + + SIOC_WP_TDM_GET_FE_ALARMS, /* 0x1B */ + + SIOC_WP_TDM_ENABLE_HWEC, /* 0x1C */ + SIOC_WP_TDM_DISABLE_HWEC, /* 0x1D */ + + SIOC_WP_TDM_SET_FE_STATUS, /* 0x1E */ + SIOC_WP_TDM_GET_FE_STATUS, /* 0x1F */ + + SIOC_WP_TDM_GET_HW_DTMF, /* 0x20 */ + + SIOC_WP_TDM_NOTSUPP /* */ + +}; + +#define SIOC_WP_TDM_GET_LINK_STATUS SIOC_WP_TDM_GET_FE_STATUS + +enum wanpipe_tdm_api_events { + WP_TDMAPI_EVENT_NONE, + WP_TDMAPI_EVENT_RBS, + WP_TDMAPI_EVENT_ALARM, + WP_TDMAPI_EVENT_DTMF, + WP_TDMAPI_EVENT_RM_DTMF, + WP_TDMAPI_EVENT_RXHOOK, + WP_TDMAPI_EVENT_RING, + WP_TDMAPI_EVENT_RING_DETECT, + WP_TDMAPI_EVENT_RING_TRIP_DETECT, + WP_TDMAPI_EVENT_TONE, + WP_TDMAPI_EVENT_TXSIG_KEWL, + WP_TDMAPI_EVENT_TXSIG_START, + WP_TDMAPI_EVENT_TXSIG_OFFHOOK, + WP_TDMAPI_EVENT_TXSIG_ONHOOK, + WP_TDMAPI_EVENT_ONHOOKTRANSFER, + WP_TDMAPI_EVENT_SETPOLARITY, + WP_TDMAPI_EVENT_BRI_CHAN_LOOPBACK, + WP_TDMAPI_EVENT_LINK_STATUS +}; + +#define WP_TDMAPI_EVENT_FE_ALARM WP_TDMAPI_EVENT_ALARM + + +#define WP_TDMAPI_EVENT_ENABLE 0x01 +#define WP_TDMAPI_EVENT_DISABLE 0x02 +#define WP_TDMAPI_EVENT_MODE_DECODE(mode) \ + ((mode) == WP_TDMAPI_EVENT_ENABLE) ? "Enable" : \ + ((mode) == WP_TDMAPI_EVENT_DISABLE) ? "Disable" : \ + "(Unknown mode)" + +#define WPTDM_A_BIT WAN_RBS_SIG_A +#define WPTDM_B_BIT WAN_RBS_SIG_B +#define WPTDM_C_BIT WAN_RBS_SIG_C +#define WPTDM_D_BIT WAN_RBS_SIG_D + +#define WP_TDMAPI_EVENT_RXHOOK_OFF 0x01 +#define WP_TDMAPI_EVENT_RXHOOK_ON 0x02 +#define WP_TDMAPI_EVENT_RXHOOK_DECODE(state) \ + ((state) == WP_TDMAPI_EVENT_RXHOOK_OFF) ? "Off-hook" : \ + ((state) == WP_TDMAPI_EVENT_RXHOOK_ON) ? "On-hook" : \ + "(Unknown state)" + +#define WP_TDMAPI_EVENT_RING_PRESENT 0x01 +#define WP_TDMAPI_EVENT_RING_STOP 0x02 +#define WP_TDMAPI_EVENT_RING_DECODE(state) \ + ((state) == WP_TDMAPI_EVENT_RING_PRESENT) ? "Ring Present" : \ + ((state) == WP_TDMAPI_EVENT_RING_STOP) ? "Ring Stop" : \ + "(Unknown state)" + +#define WP_TDMAPI_EVENT_RING_TRIP_PRESENT 0x01 +#define WP_TDMAPI_EVENT_RING_TRIP_STOP 0x02 +#define WP_TDMAPI_EVENT_RING_TRIP_DECODE(state) \ + ((state) == WP_TDMAPI_EVENT_RING_TRIP_PRESENT) ? "Ring Present" : \ + ((state) == WP_TDMAPI_EVENT_RING_TRIP_STOP) ? "Ring Stop" : \ + "(Unknown state)" +/*Link Status */ +#define WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED 0x01 +#define WP_TDMAPI_EVENT_LINK_STATUS_DISCONNECTED 0x02 +#define WP_TDMAPI_EVENT_LINK_STATUS_DECODE(status) \ + ((status) == WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED) ? "Connected" : \ + ((status) == WP_TDMAPI_EVENT_LINK_STATUS_DISCONNECTED) ? "Disconnected" : \ + "Unknown" +#define WP_TDMAPI_EVENT_TONE_DIAL 0x01 +#define WP_TDMAPI_EVENT_TONE_BUSY 0x02 +#define WP_TDMAPI_EVENT_TONE_RING 0x03 +#define WP_TDMAPI_EVENT_TONE_CONGESTION 0x04 + +/* BRI channels list */ +#define WAN_BRI_BCHAN1 0x01 +#define WAN_BRI_BCHAN2 0x02 +#define WAN_BRI_DCHAN 0x03 + + +typedef struct { + + u_int8_t type; + u_int8_t mode; + u_int32_t time_stamp; + u_int8_t channel; + u_int32_t chan_map; + u_int8_t span; + union { + struct { + u_int8_t alarm; + } te1_alarm; + struct { + u_int8_t rbs_bits; + } te1_rbs; + struct { + u_int8_t state; + u_int8_t sig; + } rm_hook; + struct { + u_int8_t state; + } rm_ring; + struct { + u_int8_t type; + } rm_tone; + struct { + u_int8_t digit; /* DTMF: digit */ + u_int8_t port; /* DTMF: SOUT/ROUT */ + u_int8_t type; /* DTMF: PRESET/STOP */ + } dtmf; + struct { + u_int16_t polarity; + u_int16_t ohttimer; + } rm_common; + struct{ + u_int16_t status; + } linkstatus; + } wp_tdm_api_event_u; +#define wp_tdm_api_event_type type +#define wp_tdm_api_event_mode mode +#define wp_tdm_api_event_alarm wp_tdm_api_event_u.te1_alarm.alarm +#define wp_tdm_api_event_alarm wp_tdm_api_event_u.te1_alarm.alarm +#define wp_tdm_api_event_rbs_bits wp_tdm_api_event_u.te1_rbs.rbs_bits +#define wp_tdm_api_event_hook_state wp_tdm_api_event_u.rm_hook.state +#define wp_tdm_api_event_hook_sig wp_tdm_api_event_u.rm_hook.sig +#define wp_tdm_api_event_ring_state wp_tdm_api_event_u.rm_ring.state +#define wp_tdm_api_event_tone_type wp_tdm_api_event_u.rm_tone.type +#define wp_tdm_api_event_dtmf_digit wp_tdm_api_event_u.dtmf.digit +#define wp_tdm_api_event_dtmf_type wp_tdm_api_event_u.dtmf.type +#define wp_tdm_api_event_dtmf_port wp_tdm_api_event_u.dtmf.port +#define wp_tdm_api_event_ohttimer wp_tdm_api_event_u.rm_common.ohttimer +#define wp_tdm_api_event_polarity wp_tdm_api_event_u.rm_common.polarity +#define wp_tdm_api_event_link_status wp_tdm_api_event_u.linkstatus.status +} wp_tdm_api_event_t; + +typedef struct { + union { + unsigned char reserved[16]; + }wp_rx_hdr_u; +} wp_tdm_api_rx_hdr_t; + +typedef struct { + wp_tdm_api_rx_hdr_t hdr; + unsigned char data[1]; +} wp_tdm_api_rx_element_t; + +typedef struct { + union { + struct { + unsigned char _rbs_rx_bits; + unsigned int _time_stamp; + }wp_tx; + unsigned char reserved[16]; + }wp_tx_hdr_u; +#define wp_api_time_stamp wp_tx_hdr_u.wp_tx._time_stamp +} wp_tdm_api_tx_hdr_t; + +typedef struct { + wp_tdm_api_tx_hdr_t hdr; + unsigned char data[1]; +} wp_tdm_api_tx_element_t; + + + +typedef struct wp_tdm_chan_stats +{ + unsigned int rx_packets; /* total packets received */ + unsigned int tx_packets; /* total packets transmitted */ + unsigned int rx_bytes; /* total bytes received */ + unsigned int tx_bytes; /* total bytes transmitted */ + unsigned int rx_errors; /* bad packets received */ + unsigned int tx_errors; /* packet transmit problems */ + unsigned int rx_dropped; /* no space in linux buffers */ + unsigned int tx_dropped; /* no space available in linux */ + unsigned int multicast; /* multicast packets received */ +#if !defined(__WINDOWS__) + unsigned int collisions; +#endif + /* detailed rx_errors: */ + unsigned int rx_length_errors; + unsigned int rx_over_errors; /* receiver ring buff overflow */ + unsigned int rx_crc_errors; /* recved pkt with crc error */ + unsigned int rx_frame_errors; /* recv'd frame alignment error */ +#if !defined(__WINDOWS__) + unsigned int rx_fifo_errors; /* recv'r fifo overrun */ +#endif + unsigned int rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ +#if !defined(__WINDOWS__) + unsigned int tx_aborted_errors; + unsigned int tx_carrier_errors; +#endif + unsigned int tx_fifo_errors; + unsigned int tx_heartbeat_errors; + unsigned int tx_window_errors; + +}wp_tdm_chan_stats_t; + + + +typedef struct wanpipe_tdm_api_cmd{ + unsigned int cmd; + unsigned int hw_tdm_coding; /* Set/Get HW TDM coding: uLaw muLaw */ + unsigned int hw_mtu_mru; /* Set/Get HW TDM MTU/MRU */ + unsigned int usr_period; /* Set/Get User Period in ms */ + unsigned int tdm_codec; /* Set/Get TDM Codec: SLinear */ + unsigned int power_level; /* Set/Get Power level treshold */ + unsigned int rx_disable; /* Enable/Disable Rx */ + unsigned int tx_disable; /* Enable/Disable Tx */ + unsigned int usr_mtu_mru; /* Set/Get User TDM MTU/MRU */ + unsigned int ec_tap; /* Echo Cancellation Tap */ + unsigned int rbs_poll; /* Enable/Disable RBS Polling */ + unsigned int rbs_rx_bits; /* Rx RBS Bits */ + unsigned int rbs_tx_bits; /* Tx RBS Bits */ + unsigned int hdlc; /* HDLC based device */ + unsigned int idle_flag; /* IDLE flag to Tx */ + unsigned int fe_alarms; /* FE Alarms detected */ + wp_tdm_chan_stats_t stats; /* TDM Statistics */ + /* Do NOT add anything above this! Important for binary backward compatibility. */ + wp_tdm_api_event_t event; /* TDM Event */ + unsigned int data_len; + void *data; + unsigned char fe_status; /* FE status - Connected or Disconnected */ + unsigned int hw_dtmf; /* HW DTMF enabled */ +}wanpipe_tdm_api_cmd_t; + +typedef struct wanpipe_tdm_api_event{ + int (*wp_rbs_event)(sng_fd_t fd, unsigned char rbs_bits); + int (*wp_dtmf_event)(sng_fd_t fd, unsigned char dtmf, unsigned char type, unsigned char port); + int (*wp_rxhook_event)(sng_fd_t fd, unsigned char hook_state); + int (*wp_ring_detect_event)(sng_fd_t fd, unsigned char ring_state); + int (*wp_ring_trip_detect_event)(sng_fd_t fd, unsigned char ring_state); + int (*wp_fe_alarm_event)(sng_fd_t fd, unsigned char fe_alarm_event); + int (*wp_link_status_event)(sng_fd_t fd, unsigned char link_status_event); +}wanpipe_tdm_api_event_t; + +typedef struct wanpipe_tdm_api{ + wanpipe_tdm_api_cmd_t wp_tdm_cmd; + wanpipe_tdm_api_event_t wp_tdm_event; +}wanpipe_tdm_api_t; + + +#endif diff --git a/libs/openzap/src/ozmod/ozmod_zt/ozmod_zt.c b/libs/openzap/src/ozmod/ozmod_zt/ozmod_zt.c new file mode 100644 index 0000000000..15803017b5 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_zt/ozmod_zt.c @@ -0,0 +1,1251 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "openzap.h" +#include "ozmod_zt.h" + +/** + * \brief Zaptel globals + */ +static struct { + uint32_t codec_ms; + uint32_t wink_ms; + uint32_t flash_ms; + uint32_t eclevel; + uint32_t etlevel; + float rxgain; + float txgain; +} zt_globals; + +/** + * \brief General IOCTL codes + */ +struct ioctl_codes { + int GET_BLOCKSIZE; + int SET_BLOCKSIZE; + int FLUSH; + int SYNC; + int GET_PARAMS; + int SET_PARAMS; + int HOOK; + int GETEVENT; + int IOMUX; + int SPANSTAT; + int MAINT; + int GETCONF; + int SETCONF; + int CONFLINK; + int CONFDIAG; + int GETGAINS; + int SETGAINS; + int SPANCONFIG; + int CHANCONFIG; + int SET_BUFINFO; + int GET_BUFINFO; + int AUDIOMODE; + int ECHOCANCEL; + int HDLCRAWMODE; + int HDLCFCSMODE; + int SPECIFY; + int SETLAW; + int SETLINEAR; + int GETCONFMUTE; + int ECHOTRAIN; + int SETTXBITS; + int GETRXBITS; +}; + +/** + * \brief Zaptel IOCTL codes + */ +static struct ioctl_codes zt_ioctl_codes = { + .GET_BLOCKSIZE = ZT_GET_BLOCKSIZE, + .SET_BLOCKSIZE = ZT_SET_BLOCKSIZE, + .FLUSH = ZT_FLUSH, + .SYNC = ZT_SYNC, + .GET_PARAMS = ZT_GET_PARAMS, + .SET_PARAMS = ZT_SET_PARAMS, + .HOOK = ZT_HOOK, + .GETEVENT = ZT_GETEVENT, + .IOMUX = ZT_IOMUX, + .SPANSTAT = ZT_SPANSTAT, + .MAINT = ZT_MAINT, + .GETCONF = ZT_GETCONF, + .SETCONF = ZT_SETCONF, + .CONFLINK = ZT_CONFLINK, + .CONFDIAG = ZT_CONFDIAG, + .GETGAINS = ZT_GETGAINS, + .SETGAINS = ZT_SETGAINS, + .SPANCONFIG = ZT_SPANCONFIG, + .CHANCONFIG = ZT_CHANCONFIG, + .SET_BUFINFO = ZT_SET_BUFINFO, + .GET_BUFINFO = ZT_GET_BUFINFO, + .AUDIOMODE = ZT_AUDIOMODE, + .ECHOCANCEL = ZT_ECHOCANCEL, + .HDLCRAWMODE = ZT_HDLCRAWMODE, + .HDLCFCSMODE = ZT_HDLCFCSMODE, + .SPECIFY = ZT_SPECIFY, + .SETLAW = ZT_SETLAW, + .SETLINEAR = ZT_SETLINEAR, + .GETCONFMUTE = ZT_GETCONFMUTE, + .ECHOTRAIN = ZT_ECHOTRAIN, + .SETTXBITS = ZT_SETTXBITS, + .GETRXBITS = ZT_GETRXBITS +}; + +/** + * \brief Dahdi IOCTL codes + */ +static struct ioctl_codes dahdi_ioctl_codes = { + .GET_BLOCKSIZE = DAHDI_GET_BLOCKSIZE, + .SET_BLOCKSIZE = DAHDI_SET_BLOCKSIZE, + .FLUSH = DAHDI_FLUSH, + .SYNC = DAHDI_SYNC, + .GET_PARAMS = DAHDI_GET_PARAMS, + .SET_PARAMS = DAHDI_SET_PARAMS, + .HOOK = DAHDI_HOOK, + .GETEVENT = DAHDI_GETEVENT, + .IOMUX = DAHDI_IOMUX, + .SPANSTAT = DAHDI_SPANSTAT, + .MAINT = DAHDI_MAINT, + .GETCONF = DAHDI_GETCONF, + .SETCONF = DAHDI_SETCONF, + .CONFLINK = DAHDI_CONFLINK, + .CONFDIAG = DAHDI_CONFDIAG, + .GETGAINS = DAHDI_GETGAINS, + .SETGAINS = DAHDI_SETGAINS, + .SPANCONFIG = DAHDI_SPANCONFIG, + .CHANCONFIG = DAHDI_CHANCONFIG, + .SET_BUFINFO = DAHDI_SET_BUFINFO, + .GET_BUFINFO = DAHDI_GET_BUFINFO, + .AUDIOMODE = DAHDI_AUDIOMODE, + .ECHOCANCEL = DAHDI_ECHOCANCEL, + .HDLCRAWMODE = DAHDI_HDLCRAWMODE, + .HDLCFCSMODE = DAHDI_HDLCFCSMODE, + .SPECIFY = DAHDI_SPECIFY, + .SETLAW = DAHDI_SETLAW, + .SETLINEAR = DAHDI_SETLINEAR, + .GETCONFMUTE = DAHDI_GETCONFMUTE, + .ECHOTRAIN = DAHDI_ECHOTRAIN, + .SETTXBITS = DAHDI_SETTXBITS, + .GETRXBITS = DAHDI_GETRXBITS +}; + +#define ZT_INVALID_SOCKET -1 +static struct ioctl_codes codes; +static const char *ctlpath = NULL; +static const char *chanpath = NULL; + +static const char dahdi_ctlpath[] = "/dev/dahdi/ctl"; +static const char dahdi_chanpath[] = "/dev/dahdi/channel"; + +static const char zt_ctlpath[] = "/dev/zap/ctl"; +static const char zt_chanpath[] = "/dev/zap/channel"; + +static zap_socket_t CONTROL_FD = ZT_INVALID_SOCKET; + +ZIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event); +ZIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event); + +/** + * \brief Initialises codec, and rx/tx gains + * \param g Structure for gains to be initialised + * \param rxgain RX gain value + * \param txgain TX gain value + * \param codec Codec + */ +static void zt_build_gains(struct zt_gains *g, float rxgain, float txgain, int codec) +{ + int j; + int k; + float linear_rxgain = pow(10.0, rxgain / 20.0); + float linear_txgain = pow(10.0, txgain / 20.0); + + switch (codec) { + case ZAP_CODEC_ALAW: + for (j = 0; j < (sizeof(g->receive_gain) / sizeof(g->receive_gain[0])); j++) { + if (rxgain) { + k = (int) (((float) alaw_to_linear(j)) * linear_rxgain); + if (k > 32767) k = 32767; + if (k < -32767) k = -32767; + g->receive_gain[j] = linear_to_alaw(k); + } else { + g->receive_gain[j] = j; + } + if (txgain) { + k = (int) (((float) alaw_to_linear(j)) * linear_txgain); + if (k > 32767) k = 32767; + if (k < -32767) k = -32767; + g->transmit_gain[j] = linear_to_alaw(k); + } else { + g->transmit_gain[j] = j; + } + } + break; + case ZAP_CODEC_ULAW: + for (j = 0; j < (sizeof(g->receive_gain) / sizeof(g->receive_gain[0])); j++) { + if (rxgain) { + k = (int) (((float) ulaw_to_linear(j)) * linear_rxgain); + if (k > 32767) k = 32767; + if (k < -32767) k = -32767; + g->receive_gain[j] = linear_to_ulaw(k); + } else { + g->receive_gain[j] = j; + } + if (txgain) { + k = (int) (((float) ulaw_to_linear(j)) * linear_txgain); + if (k > 32767) k = 32767; + if (k < -32767) k = -32767; + g->transmit_gain[j] = linear_to_ulaw(k); + } else { + g->transmit_gain[j] = j; + } + } + break; + } +} + +/** + * \brief Initialises a range of zaptel channels + * \param span Openzap span + * \param start Initial wanpipe channel number + * \param end Final wanpipe channel number + * \param type Openzap channel type + * \param name Openzap span name + * \param number Openzap span number + * \param cas_bits CAS bits + * \return number of spans configured + */ +static unsigned zt_open_range(zap_span_t *span, unsigned start, unsigned end, zap_chan_type_t type, char *name, char *number, unsigned char cas_bits) +{ + unsigned configured = 0, x; + zt_params_t ztp; + + memset(&ztp, 0, sizeof(ztp)); + + if (type == ZAP_CHAN_TYPE_CAS) { + zap_log(ZAP_LOG_DEBUG, "Configuring CAS channels with abcd == 0x%X\n", cas_bits); + } + for(x = start; x < end; x++) { + zap_channel_t *zchan; + zap_socket_t sockfd = ZT_INVALID_SOCKET; + int len; + + sockfd = open(chanpath, O_RDWR); + if (sockfd != ZT_INVALID_SOCKET && zap_span_add_channel(span, sockfd, type, &zchan) == ZAP_SUCCESS) { + + if (ioctl(sockfd, codes.SPECIFY, &x)) { + zap_log(ZAP_LOG_ERROR, "failure configuring device %s chan %d fd %d (%s)\n", chanpath, x, sockfd, strerror(errno)); + close(sockfd); + continue; + } + + if (zchan->type == ZAP_CHAN_TYPE_DQ921) { + struct zt_bufferinfo binfo; + memset(&binfo, 0, sizeof(binfo)); + binfo.txbufpolicy = 0; + binfo.rxbufpolicy = 0; + binfo.numbufs = 32; + binfo.bufsize = 1024; + if (ioctl(sockfd, codes.SET_BUFINFO, &binfo)) { + zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d\n", chanpath, zchan->span_id, zchan->chan_id, sockfd); + close(sockfd); + continue; + } + } + + if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO) { + struct zt_chanconfig cc; + memset(&cc, 0, sizeof(cc)); + cc.chan = cc.master = x; + + switch(type) { + case ZAP_CHAN_TYPE_FXS: + { + switch(span->start_type) { + case ZAP_ANALOG_START_KEWL: + cc.sigtype = ZT_SIG_FXOKS; + break; + case ZAP_ANALOG_START_LOOP: + cc.sigtype = ZT_SIG_FXOLS; + break; + case ZAP_ANALOG_START_GROUND: + cc.sigtype = ZT_SIG_FXOGS; + break; + default: + break; + } + } + break; + case ZAP_CHAN_TYPE_FXO: + { + switch(span->start_type) { + case ZAP_ANALOG_START_KEWL: + cc.sigtype = ZT_SIG_FXSKS; + break; + case ZAP_ANALOG_START_LOOP: + cc.sigtype = ZT_SIG_FXSLS; + break; + case ZAP_ANALOG_START_GROUND: + cc.sigtype = ZT_SIG_FXSGS; + break; + default: + break; + } + } + break; + default: + break; + } + + if (ioctl(CONTROL_FD, codes.CHANCONFIG, &cc)) { + zap_log(ZAP_LOG_WARNING, "this ioctl fails on older zaptel but is harmless if you used ztcfg\n[device %s chan %d fd %d (%s)]\n", chanpath, x, CONTROL_FD, strerror(errno)); + } + } + + if (type == ZAP_CHAN_TYPE_CAS) { + struct zt_chanconfig cc; + memset(&cc, 0, sizeof(cc)); + cc.chan = cc.master = x; + cc.sigtype = ZT_SIG_CAS; + cc.idlebits = cas_bits; + if (ioctl(CONTROL_FD, codes.CHANCONFIG, &cc)) { + zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d err:%s", chanpath, zchan->span_id, zchan->chan_id, sockfd, strerror(errno)); + close(sockfd); + continue; + } + } + + if (zchan->type != ZAP_CHAN_TYPE_DQ921 && zchan->type != ZAP_CHAN_TYPE_DQ931) { + len = zt_globals.codec_ms * 8; + if (ioctl(zchan->sockfd, codes.SET_BLOCKSIZE, &len)) { + zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d err:%s\n", + chanpath, zchan->span_id, zchan->chan_id, sockfd, strerror(errno)); + close(sockfd); + continue; + } + + zchan->packet_len = len; + zchan->effective_interval = zchan->native_interval = zchan->packet_len / 8; + + if (zchan->effective_codec == ZAP_CODEC_SLIN) { + zchan->packet_len *= 2; + } + } + + if (ioctl(sockfd, codes.GET_PARAMS, &ztp) < 0) { + zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d\n", chanpath, zchan->span_id, zchan->chan_id, sockfd); + close(sockfd); + continue; + } + + if (zchan->type == ZAP_CHAN_TYPE_DQ921) { + if ( + (ztp.sig_type != ZT_SIG_HDLCRAW) && + (ztp.sig_type != ZT_SIG_HDLCFCS) && + (ztp.sig_type != ZT_SIG_HARDHDLC) + ) { + zap_log(ZAP_LOG_ERROR, "Failure configuring device %s as OpenZAP device %d:%d fd:%d, hardware signaling is not HDLC, fix your Zap/DAHDI configuration!\n", chanpath, zchan->span_id, zchan->chan_id, sockfd); + close(sockfd); + continue; + } + } + + zap_log(ZAP_LOG_INFO, "configuring device %s channel %d as OpenZAP device %d:%d fd:%d\n", chanpath, x, zchan->span_id, zchan->chan_id, sockfd); + + zchan->rate = 8000; + zchan->physical_span_id = ztp.span_no; + zchan->physical_chan_id = ztp.chan_no; + + if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO || type == ZAP_CHAN_TYPE_EM || type == ZAP_CHAN_TYPE_B) { + if (ztp.g711_type == ZT_G711_ALAW) { + zchan->native_codec = zchan->effective_codec = ZAP_CODEC_ALAW; + } else if (ztp.g711_type == ZT_G711_MULAW) { + zchan->native_codec = zchan->effective_codec = ZAP_CODEC_ULAW; + } else { + int type; + + if (zchan->span->trunk_type == ZAP_TRUNK_E1) { + type = ZAP_CODEC_ALAW; + } else { + type = ZAP_CODEC_ULAW; + } + + zchan->native_codec = zchan->effective_codec = type; + + } + } + + ztp.wink_time = zt_globals.wink_ms; + ztp.flash_time = zt_globals.flash_ms; + + if (ioctl(sockfd, codes.SET_PARAMS, &ztp) < 0) { + zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d\n", chanpath, zchan->span_id, zchan->chan_id, sockfd); + close(sockfd); + continue; + } + + if (!zap_strlen_zero(name)) { + zap_copy_string(zchan->chan_name, name, sizeof(zchan->chan_name)); + } + if (!zap_strlen_zero(number)) { + zap_copy_string(zchan->chan_number, number, sizeof(zchan->chan_number)); + } + configured++; + } else { + zap_log(ZAP_LOG_ERROR, "failure configuring device %s\n", chanpath); + } + } + + + + return configured; +} + +/** + * \brief Initialises an openzap zaptel span from a configuration string + * \param span Openzap span + * \param str Configuration string + * \param type Openzap span type + * \param name Openzap span name + * \param number Openzap span number + * \return Success or failure + */ +static ZIO_CONFIGURE_SPAN_FUNCTION(zt_configure_span) +{ + + int items, i; + char *mydata, *item_list[10]; + char *ch, *mx; + unsigned char cas_bits = 0; + int channo; + int top = 0; + unsigned configured = 0; + + assert(str != NULL); + + + mydata = strdup(str); + assert(mydata != NULL); + + + items = zap_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0]))); + + for(i = 0; i < items; i++) { + ch = item_list[i]; + + if (!(ch)) { + zap_log(ZAP_LOG_ERROR, "Invalid input\n"); + continue; + } + + channo = atoi(ch); + + if (channo < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid channel number %d\n", channo); + continue; + } + + if ((mx = strchr(ch, '-'))) { + mx++; + top = atoi(mx) + 1; + } else { + top = channo + 1; + } + + + if (top < 0) { + zap_log(ZAP_LOG_ERROR, "Invalid range number %d\n", top); + continue; + } + if (ZAP_CHAN_TYPE_CAS == type && zap_config_get_cas_bits(ch, &cas_bits)) { + zap_log(ZAP_LOG_ERROR, "Failed to get CAS bits in CAS channel\n"); + continue; + } + configured += zt_open_range(span, channo, top, type, name, number, cas_bits); + + } + + free(mydata); + + return configured; + +} + +/** + * \brief Process configuration variable for a zaptel profile + * \param category Wanpipe profile name + * \param var Variable name + * \param val Variable value + * \param lineno Line number from configuration file + * \return Success + */ +static ZIO_CONFIGURE_FUNCTION(zt_configure) +{ + + int num; + float fnum; + + if (!strcasecmp(category, "defaults")) { + if (!strcasecmp(var, "codec_ms")) { + num = atoi(val); + if (num < 10 || num > 60) { + zap_log(ZAP_LOG_WARNING, "invalid codec ms at line %d\n", lineno); + } else { + zt_globals.codec_ms = num; + } + } else if (!strcasecmp(var, "wink_ms")) { + num = atoi(val); + if (num < 50 || num > 3000) { + zap_log(ZAP_LOG_WARNING, "invalid wink ms at line %d\n", lineno); + } else { + zt_globals.wink_ms = num; + } + } else if (!strcasecmp(var, "flash_ms")) { + num = atoi(val); + if (num < 50 || num > 3000) { + zap_log(ZAP_LOG_WARNING, "invalid flash ms at line %d\n", lineno); + } else { + zt_globals.flash_ms = num; + } + } else if (!strcasecmp(var, "echo_cancel_level")) { + num = atoi(val); + if (num < 0 || num > 256) { + zap_log(ZAP_LOG_WARNING, "invalid echo can val at line %d\n", lineno); + } else { + zt_globals.eclevel = num; + } + } else if (!strcasecmp(var, "echo_train_level")) { + if (zt_globals.eclevel < 1) { + zap_log(ZAP_LOG_WARNING, "can't set echo train level without setting echo cancel level first at line %d\n", lineno); + } else { + num = atoi(val); + if (num < 0 || num > 256) { + zap_log(ZAP_LOG_WARNING, "invalid echo can val at line %d\n", lineno); + } else { + zt_globals.etlevel = num; + } + } + } else if (!strcasecmp(var, "rxgain")) { + fnum = (float)atof(val); + if (fnum < -100.0 || fnum > 100.0) { + zap_log(ZAP_LOG_WARNING, "invalid rxgain val at line %d\n", lineno); + } else { + zt_globals.rxgain = fnum; + zap_log(ZAP_LOG_INFO, "Setting rxgain val to %f\n", fnum); + } + + } else if (!strcasecmp(var, "txgain")) { + fnum = (float)atof(val); + if (fnum < -100.0 || fnum > 100.0) { + zap_log(ZAP_LOG_WARNING, "invalid txgain val at line %d\n", lineno); + } else { + zt_globals.txgain = fnum; + zap_log(ZAP_LOG_INFO, "Setting txgain val to %f\n", fnum); + } + + } + } + + return ZAP_SUCCESS; +} + +/** + * \brief Opens a zaptel channel + * \param zchan Channel to open + * \return Success or failure + */ +static ZIO_OPEN_FUNCTION(zt_open) +{ + zap_channel_set_feature(zchan, ZAP_CHANNEL_FEATURE_INTERVAL); + + if (zchan->type == ZAP_CHAN_TYPE_DQ921 || zchan->type == ZAP_CHAN_TYPE_DQ931) { + zchan->native_codec = zchan->effective_codec = ZAP_CODEC_NONE; + } else { + int blocksize = zt_globals.codec_ms * (zchan->rate / 1000); + int err; + if ((err = ioctl(zchan->sockfd, codes.SET_BLOCKSIZE, &blocksize))) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } else { + zchan->effective_interval = zchan->native_interval; + zchan->packet_len = blocksize; + zchan->native_codec = zchan->effective_codec; + } + + if (zchan->type == ZAP_CHAN_TYPE_B) { + int one = 1; + if (ioctl(zchan->sockfd, codes.AUDIOMODE, &one)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno)); + zap_log(ZAP_LOG_ERROR, "%s\n", zchan->last_error); + return ZAP_FAIL; + } + } + + if (zt_globals.rxgain || zt_globals.txgain) { + struct zt_gains gains; + memset(&gains, 0, sizeof(gains)); + + gains.chan_no = zchan->physical_chan_id; + zt_build_gains(&gains, zt_globals.rxgain, zt_globals.txgain, zchan->native_codec); + + if(zt_globals.rxgain) + zap_log(ZAP_LOG_INFO, "Setting rxgain to %f on channel %d\n", zt_globals.rxgain, gains.chan_no); + + if(zt_globals.txgain) + zap_log(ZAP_LOG_INFO, "Setting txgain to %f on channel %d\n", zt_globals.txgain, gains.chan_no); + + if (ioctl(zchan->sockfd, codes.SETGAINS, &gains) < 0) { + zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d\n", chanpath, zchan->span_id, zchan->chan_id, zchan->sockfd); + } + } + + if (zt_globals.eclevel >= 0) { + int len = zt_globals.eclevel; + + if (len) { + zap_log(ZAP_LOG_INFO, "Setting echo cancel to %d taps for %d:%d\n", len, zchan->span_id, zchan->chan_id); + } else { + zap_log(ZAP_LOG_INFO, "Disable echo cancel for %d:%d\n", zchan->span_id, zchan->chan_id); + } + + if (ioctl(zchan->sockfd, codes.ECHOCANCEL, &len)) { + zap_log(ZAP_LOG_WARNING, "Echo cancel not available for %d:%d\n", zchan->span_id, zchan->chan_id); + } else if (zt_globals.etlevel >= 0) { + len = zt_globals.etlevel; + if (ioctl(zchan->sockfd, codes.ECHOTRAIN, &len)) { + zap_log(ZAP_LOG_WARNING, "Echo training not available for %d:%d\n", zchan->span_id, zchan->chan_id); + } + } + } + + } + return ZAP_SUCCESS; +} + +/** + * \brief Closes zaptel channel + * \param zchan Channel to close + * \return Success + */ +static ZIO_CLOSE_FUNCTION(zt_close) +{ + return ZAP_SUCCESS; +} + +/** + * \brief Executes an Openzap command on a zaptel channel + * \param zchan Channel to execute command on + * \param command Openzap command to execute + * \param obj Object (unused) + * \return Success or failure + */ +static ZIO_COMMAND_FUNCTION(zt_command) +{ + zt_params_t ztp; + int err = 0; + + memset(&ztp, 0, sizeof(ztp)); + + switch(command) { + case ZAP_COMMAND_ENABLE_ECHOCANCEL: + { + int level = ZAP_COMMAND_OBJ_INT; + err = ioctl(zchan->sockfd, codes.ECHOCANCEL, &level); + ZAP_COMMAND_OBJ_INT = level; + } + case ZAP_COMMAND_DISABLE_ECHOCANCEL: + { + int level = 0; + err = ioctl(zchan->sockfd, codes.ECHOCANCEL, &level); + ZAP_COMMAND_OBJ_INT = level; + } + break; + case ZAP_COMMAND_ENABLE_ECHOTRAIN: + { + int level = ZAP_COMMAND_OBJ_INT; + err = ioctl(zchan->sockfd, codes.ECHOTRAIN, &level); + ZAP_COMMAND_OBJ_INT = level; + } + case ZAP_COMMAND_DISABLE_ECHOTRAIN: + { + int level = 0; + err = ioctl(zchan->sockfd, codes.ECHOTRAIN, &level); + ZAP_COMMAND_OBJ_INT = level; + } + break; + case ZAP_COMMAND_OFFHOOK: + { + int command = ZT_OFFHOOK; + if (ioctl(zchan->sockfd, codes.HOOK, &command)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "OFFHOOK Failed"); + return ZAP_FAIL; + } + zap_set_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + } + break; + case ZAP_COMMAND_ONHOOK: + { + int command = ZT_ONHOOK; + if (ioctl(zchan->sockfd, codes.HOOK, &command)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "ONHOOK Failed"); + return ZAP_FAIL; + } + zap_clear_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + } + break; + case ZAP_COMMAND_FLASH: + { + int command = ZT_FLASH; + if (ioctl(zchan->sockfd, codes.HOOK, &command)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "FLASH Failed"); + return ZAP_FAIL; + } + } + break; + case ZAP_COMMAND_WINK: + { + int command = ZT_WINK; + if (ioctl(zchan->sockfd, codes.HOOK, &command)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "WINK Failed"); + return ZAP_FAIL; + } + } + break; + case ZAP_COMMAND_GENERATE_RING_ON: + { + int command = ZT_RING; + if (ioctl(zchan->sockfd, codes.HOOK, &command)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring Failed"); + return ZAP_FAIL; + } + zap_set_flag_locked(zchan, ZAP_CHANNEL_RINGING); + } + break; + case ZAP_COMMAND_GENERATE_RING_OFF: + { + int command = ZT_RINGOFF; + if (ioctl(zchan->sockfd, codes.HOOK, &command)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring-off failed"); + return ZAP_FAIL; + } + zap_clear_flag_locked(zchan, ZAP_CHANNEL_RINGING); + } + break; + case ZAP_COMMAND_GET_INTERVAL: + { + + if (!(err = ioctl(zchan->sockfd, codes.GET_BLOCKSIZE, &zchan->packet_len))) { + zchan->native_interval = zchan->packet_len / 8; + if (zchan->effective_codec == ZAP_CODEC_SLIN) { + zchan->packet_len *= 2; + } + ZAP_COMMAND_OBJ_INT = zchan->native_interval; + } + } + break; + case ZAP_COMMAND_SET_INTERVAL: + { + int interval = ZAP_COMMAND_OBJ_INT; + int len = interval * 8; + + if (!(err = ioctl(zchan->sockfd, codes.SET_BLOCKSIZE, &len))) { + zchan->packet_len = len; + zchan->effective_interval = zchan->native_interval = zchan->packet_len / 8; + + if (zchan->effective_codec == ZAP_CODEC_SLIN) { + zchan->packet_len *= 2; + } + } + } + break; + case ZAP_COMMAND_SET_CAS_BITS: + { + int bits = ZAP_COMMAND_OBJ_INT; + err = ioctl(zchan->sockfd, codes.SETTXBITS, &bits); + } + break; + case ZAP_COMMAND_GET_CAS_BITS: + { + err = ioctl(zchan->sockfd, codes.GETRXBITS, &zchan->rx_cas_bits); + if (!err) { + ZAP_COMMAND_OBJ_INT = zchan->rx_cas_bits; + } + } + break; + case ZAP_COMMAND_FLUSH_TX_BUFFERS: + { + int flushmode = ZT_FLUSH_WRITE; + err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode); + } + break; + case ZAP_COMMAND_FLUSH_RX_BUFFERS: + { + int flushmode = ZT_FLUSH_READ; + err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode); + } + break; + case ZAP_COMMAND_FLUSH_BUFFERS: + { + int flushmode = ZT_FLUSH_BOTH; + err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode); + } + break; + default: + err = ZAP_NOTIMPL; + break; + }; + + if (err && err != ZAP_NOTIMPL) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + + return err == 0 ? ZAP_SUCCESS : err; +} + +/** + * \brief Gets alarms from a zaptel Channel + * \param zchan Channel to get alarms from + * \return Success or failure + */ +static ZIO_GET_ALARMS_FUNCTION(zt_get_alarms) +{ + struct zt_spaninfo info; + + memset(&info, 0, sizeof(info)); + info.span_no = zchan->physical_span_id; + + if (ioctl(CONTROL_FD, codes.SPANSTAT, &info)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "ioctl failed (%s)", strerror(errno)); + snprintf(zchan->span->last_error, sizeof(zchan->span->last_error), "ioctl failed (%s)", strerror(errno)); + return ZAP_FAIL; + } + + zchan->alarm_flags = info.alarms; + + return ZAP_SUCCESS; +} + +/** + * \brief Waits for an event on a zaptel channel + * \param zchan Channel to open + * \param flags Type of event to wait for + * \param to Time to wait (in ms) + * \return Success, failure or timeout + */ +static ZIO_WAIT_FUNCTION(zt_wait) +{ + int32_t inflags = 0; + int result; + struct pollfd pfds[1]; + + if (*flags & ZAP_READ) { + inflags |= POLLIN; + } + + if (*flags & ZAP_WRITE) { + inflags |= POLLOUT; + } + + if (*flags & ZAP_EVENTS) { + inflags |= POLLPRI; + } + + + memset(&pfds[0], 0, sizeof(pfds[0])); + pfds[0].fd = zchan->sockfd; + pfds[0].events = inflags; + result = poll(pfds, 1, to); + *flags = 0; + + if (pfds[0].revents & POLLERR) { + result = -1; + } + + if (result > 0) { + inflags = pfds[0].revents; + } + + *flags = ZAP_NO_FLAGS; + + if (result < 0){ + snprintf(zchan->last_error, sizeof(zchan->last_error), "Poll failed"); + return ZAP_FAIL; + } + + if (result == 0) { + return ZAP_TIMEOUT; + } + + if (inflags & POLLIN) { + *flags |= ZAP_READ; + } + + if (inflags & POLLOUT) { + *flags |= ZAP_WRITE; + } + + if (inflags & POLLPRI) { + *flags |= ZAP_EVENTS; + } + + return ZAP_SUCCESS; + +} + +/** + * \brief Checks for events on a zaptel span + * \param span Span to check for events + * \param ms Time to wait for event + * \return Success if event is waiting or failure if not + */ +ZIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event) +{ + struct pollfd pfds[ZAP_MAX_CHANNELS_SPAN]; + uint32_t i, j = 0, k = 0; + int r; + + for(i = 1; i <= span->chan_count; i++) { + memset(&pfds[j], 0, sizeof(pfds[j])); + pfds[j].fd = span->channels[i]->sockfd; + pfds[j].events = POLLPRI; + j++; + } + + r = poll(pfds, j, ms); + + if (r == 0) { + return ZAP_TIMEOUT; + } else if (r < 0 || (pfds[i-1].revents & POLLERR)) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + for(i = 1; i <= span->chan_count; i++) { + if (pfds[i-1].revents & POLLPRI) { + zap_set_flag(span->channels[i], ZAP_CHANNEL_EVENT); + span->channels[i]->last_event_time = zap_current_time_in_ms(); + k++; + } + } + + if (!k) { + snprintf(span->last_error, sizeof(span->last_error), "no matching descriptor"); + } + + return k ? ZAP_SUCCESS : ZAP_FAIL; +} + +/** + * \brief Retrieves an event from a zaptel span + * \param span Span to retrieve event from + * \param event Openzap event to return + * \return Success or failure + */ +ZIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event) +{ + uint32_t i, event_id = 0; + zap_oob_event_t zt_event_id = 0; + + for(i = 1; i <= span->chan_count; i++) { + if (zap_test_flag(span->channels[i], ZAP_CHANNEL_EVENT)) { + zap_clear_flag(span->channels[i], ZAP_CHANNEL_EVENT); + if (ioctl(span->channels[i]->sockfd, codes.GETEVENT, &zt_event_id) == -1) { + snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); + return ZAP_FAIL; + } + + switch(zt_event_id) { + case ZT_EVENT_RINGEROFF: + { + return ZAP_FAIL; + } + break; + case ZT_EVENT_RINGERON: + { + return ZAP_FAIL; + } + break; + case ZT_EVENT_RINGBEGIN: + { + event_id = ZAP_OOB_RING_START; + } + break; + case ZT_EVENT_ONHOOK: + { + event_id = ZAP_OOB_ONHOOK; + } + break; + case ZT_EVENT_WINKFLASH: + { + if (span->channels[i]->state == ZAP_CHANNEL_STATE_DOWN || span->channels[i]->state == ZAP_CHANNEL_STATE_DIALING) { + event_id = ZAP_OOB_WINK; + } else { + event_id = ZAP_OOB_FLASH; + } + } + break; + case ZT_EVENT_RINGOFFHOOK: + { + if (span->channels[i]->type == ZAP_CHAN_TYPE_FXS || (span->channels[i]->type == ZAP_CHAN_TYPE_EM && span->channels[i]->state != ZAP_CHANNEL_STATE_UP)) { + zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK); + event_id = ZAP_OOB_OFFHOOK; + } else if (span->channels[i]->type == ZAP_CHAN_TYPE_FXO) { + event_id = ZAP_OOB_RING_START; + } + } + break; + case ZT_EVENT_ALARM: + { + zap_sigmsg_t sigmsg; + zap_channel_t *zchan = span->channels[i]; + event_id = ZAP_OOB_ALARM_TRAP; + memset(&sigmsg, 0, sizeof(sigmsg)); + sigmsg.chan_id = zchan->chan_id; + sigmsg.span_id = zchan->span_id; + sigmsg.channel = zchan; + sigmsg.event_id = ZAP_SIGEVENT_ALARM_TRAP; + zap_span_send_signal(zchan->span, &sigmsg); + } + break; + case ZT_EVENT_NOALARM: + { + zap_sigmsg_t sigmsg; + zap_channel_t *zchan = span->channels[i]; + event_id = ZAP_OOB_ALARM_CLEAR; + memset(&sigmsg, 0, sizeof(sigmsg)); + sigmsg.chan_id = zchan->chan_id; + sigmsg.span_id = zchan->span_id; + sigmsg.channel = zchan; + sigmsg.event_id = ZAP_SIGEVENT_ALARM_CLEAR; + zap_span_send_signal(zchan->span, &sigmsg); + } + break; + case ZT_EVENT_BITSCHANGED: + { + event_id = ZAP_OOB_CAS_BITS_CHANGE; + int bits = 0; + int err = ioctl(span->channels[i]->sockfd, codes.GETRXBITS, &bits); + if (err) { + return ZAP_FAIL; + } + span->channels[i]->rx_cas_bits = bits; + } + break; + default: + { + zap_log(ZAP_LOG_WARNING, "Unhandled event %d for %d:%d\n", zt_event_id, span->span_id, i); + event_id = ZAP_OOB_INVALID; + } + break; + } + + span->channels[i]->last_event_time = 0; + span->event_header.e_type = ZAP_EVENT_OOB; + span->event_header.enum_id = event_id; + span->event_header.channel = span->channels[i]; + *event = &span->event_header; + return ZAP_SUCCESS; + } + } + + return ZAP_FAIL; + +} + +/** + * \brief Reads data from a zaptel channel + * \param zchan Channel to read from + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success, failure or timeout + */ +static ZIO_READ_FUNCTION(zt_read) +{ + zap_ssize_t r = 0; + int errs = 0; + + while (errs++ < 30) { + if ((r = read(zchan->sockfd, data, *datalen)) > 0) { + break; + } + zap_sleep(10); + if (r == 0) { + errs--; + } + } + + if (r > 0) { + *datalen = r; + if (zchan->type == ZAP_CHAN_TYPE_DQ921) { + *datalen -= 2; + } + return ZAP_SUCCESS; + } + + return r == 0 ? ZAP_TIMEOUT : ZAP_FAIL; +} + +/** + * \brief Writes data to a zaptel channel + * \param zchan Channel to write to + * \param data Data buffer + * \param datalen Size of data buffer + * \return Success or failure + */ +static ZIO_WRITE_FUNCTION(zt_write) +{ + zap_ssize_t w = 0; + zap_size_t bytes = *datalen; + + if (zchan->type == ZAP_CHAN_TYPE_DQ921) { + memset(data+bytes, 0, 2); + bytes += 2; + } + + w = write(zchan->sockfd, data, bytes); + + if (w >= 0) { + *datalen = w; + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +/** + * \brief Destroys a zaptel Channel + * \param zchan Channel to destroy + * \return Success + */ +static ZIO_CHANNEL_DESTROY_FUNCTION(zt_channel_destroy) +{ + close(zchan->sockfd); + zchan->sockfd = ZT_INVALID_SOCKET; + + return ZAP_SUCCESS; +} + +/** + * \brief Global Openzap IO interface for zaptel + */ +static zap_io_interface_t zt_interface; + +/** + * \brief Loads zaptel IO module + * \param zio Openzap IO interface + * \return Success or failure + */ +static ZIO_IO_LOAD_FUNCTION(zt_init) +{ + assert(zio != NULL); + struct stat statbuf; + memset(&zt_interface, 0, sizeof(zt_interface)); + memset(&zt_globals, 0, sizeof(zt_globals)); + + if (!stat(zt_ctlpath, &statbuf)) { + zap_log(ZAP_LOG_NOTICE, "Using Zaptel control device\n"); + ctlpath = zt_ctlpath; + chanpath = zt_chanpath; + memcpy(&codes, &zt_ioctl_codes, sizeof(codes)); + } else if (!stat(dahdi_ctlpath, &statbuf)) { + zap_log(ZAP_LOG_NOTICE, "Using DAHDI control device\n"); + ctlpath = dahdi_ctlpath; + chanpath = dahdi_chanpath; + memcpy(&codes, &dahdi_ioctl_codes, sizeof(codes)); + } else { + zap_log(ZAP_LOG_ERROR, "No DAHDI or Zap control device found in /dev/\n"); + return ZAP_FAIL; + } + if ((CONTROL_FD = open(ctlpath, O_RDWR)) < 0) { + zap_log(ZAP_LOG_ERROR, "Cannot open control device %s: %s\n", ctlpath, strerror(errno)); + return ZAP_FAIL; + } + + zt_globals.codec_ms = 20; + zt_globals.wink_ms = 150; + zt_globals.flash_ms = 750; + zt_globals.eclevel = 0; + zt_globals.etlevel = 0; + + zt_interface.name = "zt"; + zt_interface.configure = zt_configure; + zt_interface.configure_span = zt_configure_span; + zt_interface.open = zt_open; + zt_interface.close = zt_close; + zt_interface.command = zt_command; + zt_interface.wait = zt_wait; + zt_interface.read = zt_read; + zt_interface.write = zt_write; + zt_interface.poll_event = zt_poll_event; + zt_interface.next_event = zt_next_event; + zt_interface.channel_destroy = zt_channel_destroy; + zt_interface.get_alarms = zt_get_alarms; + *zio = &zt_interface; + + return ZAP_SUCCESS; +} + +/** + * \brief Unloads zaptel IO module + * \return Success + */ +static ZIO_IO_UNLOAD_FUNCTION(zt_destroy) +{ + close(CONTROL_FD); + memset(&zt_interface, 0, sizeof(zt_interface)); + return ZAP_SUCCESS; +} + +/** + * \brief Openzap zaptel IO module definition + */ +zap_module_t zap_module = { + "zt", + zt_init, + zt_destroy, +}; + +/* 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: + */ diff --git a/libs/openzap/src/ozmod/ozmod_zt/ozmod_zt.h b/libs/openzap/src/ozmod/ozmod_zt/ozmod_zt.h new file mode 100644 index 0000000000..687269a983 --- /dev/null +++ b/libs/openzap/src/ozmod/ozmod_zt/ozmod_zt.h @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 ZAP_ZT_H +#define ZAP_ZT_H +#include "openzap.h" +#include +#include + +#ifdef __sun +#include +#include +#include +#endif + +/* Hardware interface structures and defines */ +/* Based on documentation of the structures required for the hardware interface */ +/* from http://wiki.freeswitch.org/wiki/Zapata_zaptel_interface */ + +/* Structures */ + +/* Used with ioctl: ZT_GET_PARAMS and ZT_SET_PARAMS */ +struct zt_params { + int chan_no; /* Channel Number */ + int span_no; /* Span Number */ + int chan_position; /* Channel Position */ + int sig_type; /* Signal Type (read-only) */ + int sig_cap; /* Signal Cap (read-only) */ + int receive_offhook; /* Receive is offhook (read-only) */ + int receive_bits; /* Number of bits in receive (read-only) */ + int transmit_bits; /* Number of bits in transmit (read-only) */ + int transmit_hook_sig; /* Transmit Hook Signal (read-only) */ + int receive_hook_sig; /* Receive Hook Signal (read-only) */ + int g711_type; /* Member of zt_g711_t (read-only) */ + int idlebits; /* bits for the idle state (read-only) */ + char chan_name[40]; /* Channel Name */ + int prewink_time; + int preflash_time; + int wink_time; + int flash_time; + int start_time; + int receive_wink_time; + int receive_flash_time; + int debounce_time; + int pulse_break_time; + int pulse_make_time; + int pulse_after_time; + /* latest version of this struct include chan_alarms field */ + uint32_t chan_alarms; +}; + +typedef struct zt_params zt_params_t; + +/* Used with ioctl: ZT_CONFLINK, ZT_GETCONF and ZT_SETCONF */ +struct zt_confinfo { + int chan_no; /* Channel Number, 0 for current */ + int conference_number; + int conference_mode; +}; + +/* Used with ioctl: ZT_GETGAINS and ZT_SETGAINS */ +struct zt_gains { + int chan_no; /* Channel Number, 0 for current */ + unsigned char receive_gain[256]; /* Receive gain table */ + unsigned char transmit_gain[256]; /* Transmit gain table */ +}; + +/* Used with ioctl: ZT_SPANSTAT */ +struct zt_spaninfo { + int span_no; /* span number (-1 to use name) */ + char name[20]; /* Name of span */ + char description[40]; /* Description of span */ + int alarms; /* alarms status */ + int transmit_level; /* Transmit level */ + int receive_level; /* Receive level */ + int bpv_count; /* Current BPV count */ + int crc4_count; /* Current CRC4 error count */ + int ebit_count; /* Current E-bit error count */ + int fas_count; /* Current FAS error count */ + int irq_misses; /* Current IRQ misses */ + int sync_src; /* Span # of sync source (0 = free run) */ + int configured_chan_count; /* Count of channels configured on the span */ + int channel_count; /* Total count of channels on the span */ + int span_count; /* Total count of zaptel spans on the system*/ + /* end v1 of the struct */ + /* as long as we don't use the fields below we should be ok regardless of the zaptel/dahdi version */ + int lbo; /* Line Build Out */ + int lineconfig; /* framing/coding */ + /* end of v2 of the struct */ + char lboname[40]; /* Line Build Out in text form */ + char location[40]; /* span's device location in system */ + char manufacturer[40]; /* manufacturer of span's device */ + char devicetype[40]; /* span's device type */ + int irq; /* span's device IRQ */ + int linecompat; /* signaling modes possible on this span */ + char spantype[6]; /* type of span in text form */ +}; + +struct zt_maintinfo { + int span_no; /* span number */ + int command; /* Maintenance mode to set (from zt_maintenance_mode_t) */ +}; + +struct zt_lineconfig { +/* Used in ZT_SPANCONFIG */ + int span; /* Which span number (0 to use name) */ + char name[20]; /* Name of span to use */ + int lbo; /* line build-outs */ + int lineconfig; /* line config parameters (framing, coding) */ + int sync; /* what level of sync source we are */ +}; + +struct zt_chanconfig { +/* Used in ZT_CHANCONFIG */ + int chan; /* Channel we're applying this to (0 to use name) */ + char name[40]; /* Name of channel to use */ + int sigtype; /* Signal type */ + int deflaw; /* Default law (ZT_LAW_DEFAULT, ZT_LAW_MULAW, or ZT_LAW_ALAW */ + int master; /* Master channel if sigtype is ZT_SLAVE */ + int idlebits; /* Idle bits (if this is a CAS channel) or channel to monitor (if this is DACS channel) */ + char netdev_name[16]; /* name for the hdlc network device */ +}; + +struct zt_bufferinfo { +/* used in ZT_SET_BUFINFO and ZT_GET_BUFINFO */ + int txbufpolicy; /* Policy for handling receive buffers */ + int rxbufpolicy; /* Policy for handling receive buffers */ + int numbufs; /* How many buffers to use */ + int bufsize; /* How big each buffer is */ + int readbufs; /* How many read buffers are full (read-only) */ + int writebufs; /* How many write buffers are full (read-only) */ +}; + +/* Enumerations */ + +/* Values in zt_params structure for member g711_type */ +typedef enum { + ZT_G711_DEFAULT = 0, /* Default mulaw/alaw from the span */ + ZT_G711_MULAW = 1, + ZT_G711_ALAW = 2 +} zt_g711_t; + +typedef enum { + ZT_EVENT_NONE = 0, + ZT_EVENT_ONHOOK = 1, + ZT_EVENT_RINGOFFHOOK = 2, + ZT_EVENT_WINKFLASH = 3, + ZT_EVENT_ALARM = 4, + ZT_EVENT_NOALARM = 5, + ZT_EVENT_ABORT = 6, + ZT_EVENT_OVERRUN = 7, + ZT_EVENT_BADFCS = 8, + ZT_EVENT_DIALCOMPLETE = 9, + ZT_EVENT_RINGERON = 10, + ZT_EVENT_RINGEROFF = 11, + ZT_EVENT_HOOKCOMPLETE = 12, + ZT_EVENT_BITSCHANGED = 13, + ZT_EVENT_PULSE_START = 14, + ZT_EVENT_TIMER_EXPIRED = 15, + ZT_EVENT_TIMER_PING = 16, + ZT_EVENT_POLARITY = 17, + ZT_EVENT_RINGBEGIN = 18 +} zt_event_t; + +typedef enum { + ZT_FLUSH_READ = 1, + ZT_FLUSH_WRITE = 2, + ZT_FLUSH_BOTH = (ZT_FLUSH_READ | ZT_FLUSH_WRITE), + ZT_FLUSH_EVENT = 4, + ZT_FLUSH_ALL = (ZT_FLUSH_READ | ZT_FLUSH_WRITE | ZT_FLUSH_EVENT) +} zt_flush_t; + +typedef enum { + ZT_IOMUX_READ = 1, + ZT_IOMUX_WRITE = 2, + ZT_IOMUX_WRITEEMPTY = 4, + ZT_IOMUX_SIGEVENT = 8, + ZT_IOMUX_NOWAIT = 256 +} zt_iomux_t; + +typedef enum { + ZT_ONHOOK = 0, + ZT_OFFHOOK = 1, + ZT_WINK = 2, + ZT_FLASH = 3, + ZT_START = 4, + ZT_RING = 5, + ZT_RINGOFF = 6 +} zt_hookstate_t; + +typedef enum { + ZT_MAINT_NONE = 0, /* Normal Mode */ + ZT_MAINT_LOCALLOOP = 1, /* Local Loopback */ + ZT_MAINT_REMOTELOOP = 2, /* Remote Loopback */ + ZT_MAINT_LOOPUP = 3, /* Send Loopup Code */ + ZT_MAINT_LOOPDOWN = 4, /* Send Loopdown Code */ + ZT_MAINT_LOOPSTOP = 5 /* Stop Sending Loop Codes */ +} zt_maintenance_mode_t; + +typedef enum { +/* Signalling type */ +ZT_SIG_NONE = 0, /* chan not configured. */ + +ZT_SIG_FXSLS = ((1 << 0) | (1 << 13)), /* FXS, Loopstart */ +ZT_SIG_FXSGS = ((1 << 1) | (1 << 13)), /* FXS, Groundstart */ +ZT_SIG_FXSKS = ((1 << 2) | (1 << 13)), /* FXS, Kewlstart */ +ZT_SIG_FXOLS = ((1 << 3) | (1 << 12)), /* FXO, Loopstart */ +ZT_SIG_FXOGS = ((1 << 4) | (1 << 12)), /* FXO, Groupstart */ +ZT_SIG_FXOKS = ((1 << 5) | (1 << 12)), /* FXO, Kewlstart */ +ZT_SIG_EM = (1 << 6), /* E&M */ +ZT_SIG_CLEAR = (1 << 7), +ZT_SIG_HDLCRAW = ((1 << 8) | ZT_SIG_CLEAR), +ZT_SIG_HDLCFCS = ((1 << 9) | ZT_SIG_HDLCRAW), +ZT_SIG_CAS = (1 << 15), +ZT_SIG_HARDHDLC = ((1 << 19) | ZT_SIG_CLEAR), +} zt_sigtype_t; + +typedef enum { +ZT_DBIT = 1, +ZT_CBIT = 2, +ZT_BBIT = 4, +ZT_ABIT = 8 +} zt_cas_bit_t; + +/* Defines */ + +#define ZT_MAX_BLOCKSIZE 8192 +#define ZT_DEFAULT_MTU_MRU 2048 + +/* ioctl defines */ + +#define ZT_CODE 'J' +#define DAHDI_CODE 0xDA + + +#define ZT_GET_BLOCKSIZE _IOR (ZT_CODE, 1, int) /* Get Transfer Block Size. */ +#define ZT_SET_BLOCKSIZE _IOW (ZT_CODE, 2, int) /* Set Transfer Block Size. */ +#define ZT_FLUSH _IOW (ZT_CODE, 3, int) /* Flush Buffer(s) and stop I/O */ +#define ZT_SYNC _IOW (ZT_CODE, 4, int) /* Wait for Write to Finish */ +#define ZT_GET_PARAMS _IOR (ZT_CODE, 5, struct zt_params) /* Get channel parameters */ +#define ZT_SET_PARAMS _IOW (ZT_CODE, 6, struct zt_params) /* Set channel parameters */ +#define ZT_HOOK _IOW (ZT_CODE, 7, int) /* Set Hookswitch Status */ +#define ZT_GETEVENT _IOR (ZT_CODE, 8, int) /* Get Signalling Event */ +#define ZT_IOMUX _IOWR (ZT_CODE, 9, int) /* Wait for something to happen (IO Mux) */ +#define ZT_SPANSTAT _IOWR (ZT_CODE, 10, struct zt_spaninfo) /* Get Span Status */ +#define ZT_MAINT _IOW (ZT_CODE, 11, struct zt_maintinfo)/* Set Maintenance Mode for a span */ +#define ZT_GETCONF _IOWR (ZT_CODE, 12, struct zt_confinfo) /* Get Conference Mode */ +#define ZT_SETCONF _IOWR (ZT_CODE, 13, struct zt_confinfo) /* Set Conference Mode */ +#define ZT_CONFLINK _IOW (ZT_CODE, 14, struct zt_confinfo) /* Setup or Remove Conference Link */ +#define ZT_CONFDIAG _IOR (ZT_CODE, 15, int) /* Display Conference Diagnostic Information on Console */ + +#define ZT_GETGAINS _IOWR (ZT_CODE, 16, struct zt_gains) /* Get Channel audio gains */ +#define ZT_SETGAINS _IOWR (ZT_CODE, 17, struct zt_gains) /* Set Channel audio gains */ +#define ZT_SPANCONFIG _IOW (ZT_CODE, 18, struct zt_lineconfig)/* Set Line (T1) Configurations and start system */ +#define ZT_CHANCONFIG _IOW (ZT_CODE, 19, struct zt_chanconfig)/* Set Channel Configuration */ +#define ZT_SET_BUFINFO _IOW (ZT_CODE, 27, struct zt_bufferinfo)/* Set buffer policy */ +#define ZT_GET_BUFINFO _IOR (ZT_CODE, 28, struct zt_bufferinfo)/* Get current buffer info */ +#define ZT_AUDIOMODE _IOW (ZT_CODE, 32, int) /* Set a clear channel into audio mode */ +#define ZT_ECHOCANCEL _IOW (ZT_CODE, 33, int) /* Control Echo Canceller */ +#define ZT_HDLCRAWMODE _IOW (ZT_CODE, 36, int) /* Set a clear channel into HDLC w/out FCS checking/calculation mode */ +#define ZT_HDLCFCSMODE _IOW (ZT_CODE, 37, int) /* Set a clear channel into HDLC w/ FCS mode */ + +/* Specify a channel on /dev/zap/chan -- must be done before any other ioctl's and is only valid on /dev/zap/chan */ +#define ZT_SPECIFY _IOW (ZT_CODE, 38, int) + +/* Temporarily set the law on a channel to ZT_LAW_DEFAULT, ZT_LAW_ALAW, or ZT_LAW_MULAW. Is reset on close. */ +#define ZT_SETLAW _IOW (ZT_CODE, 39, int) + +/* Temporarily set the channel to operate in linear mode when non-zero or default law if 0 */ +#define ZT_SETLINEAR _IOW (ZT_CODE, 40, int) + +#define ZT_GETCONFMUTE _IOR (ZT_CODE, 49, int) /* Get Conference to mute mode */ +#define ZT_ECHOTRAIN _IOW (ZT_CODE, 50, int) /* Control Echo Trainer */ + +/* Set/Get CAS bits */ +#define ZT_SETTXBITS _IOW (ZT_CODE, 43, int) +#define ZT_GETRXBITS _IOR (ZT_CODE, 45, int) + +#define DAHDI_GET_BLOCKSIZE _IOR (DAHDI_CODE, 1, int) /* Get Transfer Block Size. */ +#define DAHDI_SET_BLOCKSIZE _IOW (DAHDI_CODE, 1, int) /* Set Transfer Block Size. */ +#define DAHDI_FLUSH _IOW (DAHDI_CODE, 3, int) /* Flush Buffer(s) and stop I/O */ +#define DAHDI_SYNC _IO (DAHDI_CODE, 4) /* Wait for Write to Finish */ +#define DAHDI_GET_PARAMS _IOR (DAHDI_CODE, 5, struct zt_params) /* Get channel parameters */ +#define DAHDI_SET_PARAMS _IOW (DAHDI_CODE, 5, struct zt_params) /* Set channel parameters */ +#define DAHDI_HOOK _IOW (DAHDI_CODE, 7, int) /* Set Hookswitch Status */ +#define DAHDI_GETEVENT _IOR (DAHDI_CODE, 8, int) /* Get Signalling Event */ +#define DAHDI_IOMUX _IOWR (DAHDI_CODE, 9, int) /* Wait for something to happen (IO Mux) */ +#define DAHDI_SPANSTAT _IOWR (DAHDI_CODE, 10, struct zt_spaninfo) /* Get Span Status */ +#define DAHDI_MAINT _IOW (DAHDI_CODE, 11, struct zt_maintinfo) /* Set Maintenance Mode for a span */ +#define DAHDI_GETCONF _IOR (DAHDI_CODE, 12, struct zt_confinfo) /* Get Conference Mode */ +#define DAHDI_SETCONF _IOW (DAHDI_CODE, 12, struct zt_confinfo) /* Set Conference Mode */ +#define DAHDI_CONFLINK _IOW (DAHDI_CODE, 14, struct zt_confinfo) /* Setup or Remove Conference Link */ +#define DAHDI_CONFDIAG _IOR (DAHDI_CODE, 15, int) /* Display Conference Diagnostic Information on Console */ + +#define DAHDI_GETGAINS _IOR (DAHDI_CODE, 16, struct zt_gains) /* Get Channel audio gains */ +#define DAHDI_SETGAINS _IOW (DAHDI_CODE, 16, struct zt_gains) /* Set Channel audio gains */ +#define DAHDI_SPANCONFIG _IOW (DAHDI_CODE, 18, struct zt_lineconfig)/* Set Line (T1) Configurations and start system */ +#define DAHDI_CHANCONFIG _IOW (DAHDI_CODE, 19, struct zt_chanconfig)/* Set Channel Configuration */ +#define DAHDI_SET_BUFINFO _IOW (DAHDI_CODE, 27, struct zt_bufferinfo)/* Set buffer policy */ +#define DAHDI_GET_BUFINFO _IOR (DAHDI_CODE, 27, struct zt_bufferinfo)/* Get current buffer info */ +#define DAHDI_AUDIOMODE _IOW (DAHDI_CODE, 32, int) /* Set a clear channel into audio mode */ +#define DAHDI_ECHOCANCEL _IOW (DAHDI_CODE, 33, int) /* Control Echo Canceller */ +#define DAHDI_HDLCRAWMODE _IOW (DAHDI_CODE, 36, int) /* Set a clear channel into HDLC w/out FCS checking/calculation mode */ +#define DAHDI_HDLCFCSMODE _IOW (DAHDI_CODE, 37, int) /* Set a clear channel into HDLC w/ FCS mode */ + +/* Specify a channel on /dev/dahdi/chan -- must be done before any other ioctl's and is only valid on /dev/dahdi/chan */ +#define DAHDI_SPECIFY _IOW (DAHDI_CODE, 38, int) + +/* Temporarily set the law on a channel to DAHDI_LAW_DEFAULT, DAHDI_LAW_ALAW, or DAHDI_LAW_MULAW. Is reset on close. */ +#define DAHDI_SETLAW _IOW (DAHDI_CODE, 39, int) + +/* Temporarily set the channel to operate in linear mode when non-zero or default law if 0 */ +#define DAHDI_SETLINEAR _IOW (DAHDI_CODE, 40, int) + +#define DAHDI_GETCONFMUTE _IOR (DAHDI_CODE, 49, int) /* Get Conference to mute mode */ +#define DAHDI_ECHOTRAIN _IOW (DAHDI_CODE, 50, int) /* Control Echo Trainer */ + +/* Set/Get CAS bits */ +#define DAHDI_SETTXBITS _IOW (DAHDI_CODE, 43, int) +#define DAHDI_GETRXBITS _IOR (DAHDI_CODE, 43, int) + + +#endif + +/* 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: + */ diff --git a/libs/openzap/src/priserver.c b/libs/openzap/src/priserver.c new file mode 100644 index 0000000000..7a37357083 --- /dev/null +++ b/libs/openzap/src/priserver.c @@ -0,0 +1,328 @@ +/***************************************************************************** + * priserver.c Refactoring of pritest.c + * + * Author(s): Anthony Minessale II + * Nenad Corbic + * + * Copyright: (c) 2005 Anthony Minessale II + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * ============================================================================ + */ + +#include "openzap.h" +#include +#include +#include +#include +#include +#include + +typedef struct { + int pid; + q931_call call; + void *pri; + int ready; +}call_info_t; + + +#define SANGOMA_MAX_CHAN_PER_SPAN 32 + +static call_info_t pidmap[SANGOMA_MAX_CHAN_PER_SPAN]; + +ZIO_EVENT_CB_FUNCTION(my_zap_event_handler) +{ + if (event->e_type = ZAP_EVENT_DTMF) { + char *dtmf = event->data; + printf("DTMF %s\n", dtmf); + } +} + +/* Stupid runtime process to play a file to a b channel*/ +#define BYTES 320 +#define MAX_BYTES 1000 + +static int ready = 1; + +static void handle_SIGINT(int sig) +{ + if (sig) { + ready = 0; + } + + return; +} + +static void launch_channel(struct sangoma_pri *spri, int channo) +{ + pid_t pid; + int fd = 0, file = 0, inlen = 0, outlen = 0; + unsigned char inframe[MAX_BYTES], outframe[MAX_BYTES]; + fd_set readfds; + int mtu_mru=BYTES / 2; + int err; + zap_channel_t *chan; + zap_codec_t codec = ZAP_CODEC_SLIN; + unsigned ms = 20; + unsigned int lead = 50; + int ifd = -1; + zap_tone_type_t tt = ZAP_TONE_DTMF; + char dtmf[] = "1234567890"; + int loops = 0; + + pid = fork(); + + if (pid) { + pidmap[channo-1].pid = pid; + printf("-- Launching process %d to handle channel %d\n", pid, channo); + return; + } + + signal(SIGINT, handle_SIGINT); + + //ifd = open("/nfs/sounds/ptest.raw", O_WRONLY|O_CREAT|O_TRUNC, 777); + + memset(inframe, 0, MAX_BYTES); + memset(outframe, 0, MAX_BYTES); + + if (zap_channel_open(spri->span, channo, &chan) != ZAP_SUCCESS) { + printf("DEBUG cant open fd!\n"); + } + + + +#if 1 + if (zap_channel_command(chan, ZAP_COMMAND_SET_CODEC, &codec) != ZAP_SUCCESS) { + printf("Critical Error: Failed to set driver codec!\n"); + zap_channel_close(&chan); + exit(-1); + } +#endif + +#if 1 + if (zap_channel_command(chan, ZAP_COMMAND_ENABLE_DTMF_DETECT, &tt) != ZAP_SUCCESS) { + printf("Critical Error: Failed to set dtmf detect!\n"); + zap_channel_close(&chan); + exit(-1); + } + zap_channel_set_event_callback(chan, my_zap_event_handler); +#endif + + + if (zap_channel_command(chan, ZAP_COMMAND_SET_INTERVAL, &ms) != ZAP_SUCCESS) { + printf("Critical Error: Failed to set codec interval!\n"); + zap_channel_close(&chan); + exit(-1); + } + + file = open("sound.raw", O_RDONLY); + if (file < 0){ + printf("Critical Error: Failed to open sound file!\n"); + zap_channel_close(&chan); + exit(-1); + } + + + while(ready) { + zap_wait_flag_t flags = ZAP_READ; + zap_size_t len; + loops++; + + if (lead) { + lead--; + } + + if (!lead && loops == 300) { +#if 1 + if (zap_channel_command(chan, ZAP_COMMAND_SEND_DTMF, dtmf) != ZAP_SUCCESS) { + printf("Critical Error: Failed to send dtmf\n"); + zap_channel_close(&chan); + exit(-1); + } +#endif + + } + + if (zap_channel_wait(chan, &flags, 2000) != ZAP_SUCCESS) { + printf("wait FAIL! [%s]\n", chan->last_error); + break; + } + + if (flags & ZAP_READ) { + len = MAX_BYTES; + if (zap_channel_read(chan, inframe, &len) == ZAP_SUCCESS) { + //printf("READ: %d\n", len); + //write(ifd, inframe, len); + if(!lead && (outlen = read(file, outframe, len)) <= 0) { + break; + } + + } else { + printf("READ FAIL! %d [%s]\n", len, chan->last_error); + break; + } + if (lead) { + continue; + } + zap_channel_write(chan, outframe, sizeof(outframe), &len); + } else { + printf("BREAK"); + break; + } + } + + printf("loop done\n"); + + //sangoma_get_full_cfg(fd, &tdm_api); + close(file); + //close(ifd); + + pri_hangup(spri->pri, channo, 16); + if (zap_channel_close(&chan) != ZAP_SUCCESS) { + printf("Critical Error: Failed to close channel [%s]\n", chan->last_error); + } + + printf("Call Handler: Process Finished\n"); + exit(0); +} + + +/* Event Handlers */ + +static int on_info(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + printf( "number is: %s\n", event->ring.callednum); + if(strlen(event->ring.callednum) > 3) { + printf( "final number is: %s\n", event->ring.callednum); + pri_answer(spri->pri, event->ring.call, 0, 1); + } + return 0; +} + +static int on_hangup(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + //pri_hangup(spri->pri, event->hangup.call, event->hangup.cause); + printf("-- Hanging up channel %d\n", event->hangup.channel); + if(pidmap[event->hangup.channel-1].pid) { + pri_hangup(spri->pri, event->hangup.call, 16); + pri_destroycall(spri->pri, event->hangup.call); + kill(pidmap[event->hangup.channel-1].pid, SIGINT); + pidmap[event->hangup.channel-1].pid = 0; + } + return 0; +} + +static int on_ring(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + printf("-- Ring on channel %d (from %s to %s), answering...\n", event->ring.channel, event->ring.callingnum, event->ring.callednum); + pri_answer(spri->pri, event->ring.call, event->ring.channel, 1); + memcpy(&pidmap[event->ring.channel-1].call, event->ring.call, sizeof(q931_call)); + pidmap[event->ring.channel-1].pri=spri->pri; + pidmap[event->ring.channel-1].call = *event->ring.call; + launch_channel(spri, event->ring.channel); + return 0; +} + +static int on_restart(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + printf("-- Restarting channel %d\n", event->restart.channel); + return 0; +} + +static int on_anything(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event) +{ + printf("%s: Caught Event %d (%s)\n", __FUNCTION__, event_type, sangoma_pri_event_str(event_type)); + return 0; +} + +/* Generic Reaper */ +static void chan_ended(int sig) +{ + int status; + int x; + struct rusage rusage; + pid_t pid; + pid = wait4(-1, &status, WNOHANG, &rusage); + + printf("-- PID %d ended\n", pid); + + for (x=0;x -1) { + fprintf(stderr, "--!! Unknown PID %d exited\n", pid); + signal(SIGCHLD, chan_ended); + return; + } +} + +/* Our Program */ +int main(int argc, char *argv[]) +{ + struct sangoma_pri spri; + int debug = 0; + if (argv[1]) { + debug = atoi(argv[1]); + } + + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + if (zap_global_init() != ZAP_SUCCESS) { + fprintf(stderr, "Error loading OpenZAP\n"); + exit(-1); + } + + printf("OpenZAP loaded\n"); + + + debug = PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE; + printf("WTF %d\n", debug); + + if (sangoma_init_pri(&spri, + 1, // span + 24, // dchan + SANGOMA_PRI_SWITCH_DMS100, + SANGOMA_PRI_CPE, + debug) < 0) { + return -1; + } + //spri.pri->debug = (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE); + + //pri_set_debug(&spri.pri, (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE)); + + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_ANY, on_anything); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_RING, on_ring); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_HANGUP, on_hangup); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_HANGUP_REQ, on_hangup); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_INFO_RECEIVED, on_info); + SANGOMA_MAP_PRI_EVENT(spri, SANGOMA_PRI_EVENT_RESTART, on_restart); + + signal(SIGCHLD, chan_ended); + sangoma_run_pri(&spri); + return 0; +} + +/* 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: + */ diff --git a/libs/openzap/src/sangoma_pri.c b/libs/openzap/src/sangoma_pri.c new file mode 100644 index 0000000000..5cf6a5cd10 --- /dev/null +++ b/libs/openzap/src/sangoma_pri.c @@ -0,0 +1,251 @@ +/***************************************************************************** + * sangoma_pri.c libpri Sangoma integration + * + * Author(s): Anthony Minessale II + * Nenad Corbic + * + * Copyright: (c) 2005 Anthony Minessale II + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * ============================================================================ + */ + +#include "openzap.h" +#include +#ifndef HAVE_GETTIMEOFDAY + +#ifdef WIN32 +#include + +static __inline int gettimeofday(struct timeval *tp, void *nothing) +{ +#ifdef WITHOUT_MM_LIB + SYSTEMTIME st; + time_t tt; + struct tm tmtm; + /* mktime converts local to UTC */ + GetLocalTime (&st); + tmtm.tm_sec = st.wSecond; + tmtm.tm_min = st.wMinute; + tmtm.tm_hour = st.wHour; + tmtm.tm_mday = st.wDay; + tmtm.tm_mon = st.wMonth - 1; + tmtm.tm_year = st.wYear - 1900; tmtm.tm_isdst = -1; + tt = mktime (&tmtm); + tp->tv_sec = tt; + tp->tv_usec = st.wMilliseconds * 1000; +#else + /** + ** The earlier time calculations using GetLocalTime + ** had a time resolution of 10ms.The timeGetTime, part + ** of multimedia apis offer a better time resolution + ** of 1ms.Need to link against winmm.lib for this + **/ + unsigned long Ticks = 0; + unsigned long Sec =0; + unsigned long Usec = 0; + Ticks = timeGetTime(); + + Sec = Ticks/1000; + Usec = (Ticks - (Sec*1000))*1000; + tp->tv_sec = Sec; + tp->tv_usec = Usec; +#endif /* WITHOUT_MM_LIB */ + (void)nothing; + return 0; +} +#endif /* WIN32 */ +#endif /* HAVE_GETTIMEOFDAY */ + +static struct sangoma_pri_event_list SANGOMA_PRI_EVENT_LIST[] = { + {0, SANGOMA_PRI_EVENT_ANY, "ANY"}, + {1, SANGOMA_PRI_EVENT_DCHAN_UP, "DCHAN_UP"}, + {2, SANGOMA_PRI_EVENT_DCHAN_DOWN, "DCHAN_DOWN"}, + {3, SANGOMA_PRI_EVENT_RESTART, "RESTART"}, + {4, SANGOMA_PRI_EVENT_CONFIG_ERR, "CONFIG_ERR"}, + {5, SANGOMA_PRI_EVENT_RING, "RING"}, + {6, SANGOMA_PRI_EVENT_HANGUP, "HANGUP"}, + {7, SANGOMA_PRI_EVENT_RINGING, "RINGING"}, + {8, SANGOMA_PRI_EVENT_ANSWER, "ANSWER"}, + {9, SANGOMA_PRI_EVENT_HANGUP_ACK, "HANGUP_ACK"}, + {10, SANGOMA_PRI_EVENT_RESTART_ACK, "RESTART_ACK"}, + {11, SANGOMA_PRI_EVENT_FACNAME, "FACNAME"}, + {12, SANGOMA_PRI_EVENT_INFO_RECEIVED, "INFO_RECEIVED"}, + {13, SANGOMA_PRI_EVENT_PROCEEDING, "PROCEEDING"}, + {14, SANGOMA_PRI_EVENT_SETUP_ACK, "SETUP_ACK"}, + {15, SANGOMA_PRI_EVENT_HANGUP_REQ, "HANGUP_REQ"}, + {16, SANGOMA_PRI_EVENT_NOTIFY, "NOTIFY"}, + {17, SANGOMA_PRI_EVENT_PROGRESS, "PROGRESS"}, + {18, SANGOMA_PRI_EVENT_KEYPAD_DIGIT, "KEYPAD_DIGIT"} +}; + +#define LINE "--------------------------------------------------------------------------------" + +char *sangoma_pri_event_str(sangoma_pri_event_t event_id) +{ + return SANGOMA_PRI_EVENT_LIST[event_id].name; +} + +static int __pri_sangoma_read(struct pri *pri, void *buf, int buflen) +{ + struct sangoma_pri *spri = (struct sangoma_pri *) pri->userdata; + zap_size_t len = buflen; + int res; + char bb[4096] = ""; + + + if (zap_channel_read(spri->zdchan, buf, &len) != ZAP_SUCCESS) { + printf("D-READ FAIL! [%s]\n", spri->zdchan->last_error); + return 0; + } + res = (int)len; + memset(&((unsigned char*)buf)[res],0,2); + res+=2; + + //print_bits(buf, res-2, bb, sizeof(bb), 1, 0); + //zap_log(ZAP_LOG_DEBUG, "READ %d\n%s\n%s\n\n", res-2, LINE, bb); + + return res; +} + +static int __pri_sangoma_write(struct pri *pri, void *buf, int buflen) +{ + struct sangoma_pri *spri = (struct sangoma_pri *) pri->userdata; + int res; + zap_size_t len = buflen -2; + char bb[4096] = ""; + + if (zap_channel_write(spri->zdchan, buf, buflen, &len) != ZAP_SUCCESS) { + printf("D-WRITE FAIL! [%s]\n", spri->zdchan->last_error); + return 0; + } + + //print_bits(buf, (int)buflen-2, bb, sizeof(bb), 1, 0); + //zap_log(ZAP_LOG_DEBUG, "WRITE %d\n%s\n%s\n\n", (int)buflen-2, LINE, bb); + + return (int) buflen; +} + +int sangoma_init_pri(struct sangoma_pri *spri, int span, int dchan, int swtype, int node, int debug) +{ + int ret = -1; + zap_socket_t dfd = 0; + + memset(spri, 0, sizeof(struct sangoma_pri)); + + if (zap_channel_open(span, dchan, &spri->zdchan) != ZAP_SUCCESS) { + fprintf(stderr, "Unable to open DCHAN %d for span %d (%s)\n", dchan, span, strerror(errno)); + } else { + if ((spri->pri = pri_new_cb(spri->zdchan->sockfd, node, swtype, __pri_sangoma_read, __pri_sangoma_write, spri))){ + spri->span = span; + pri_set_debug(spri->pri, debug); + ret = 0; + } else { + fprintf(stderr, "Unable to create PRI\n"); + } + } + return ret; +} + + +int sangoma_one_loop(struct sangoma_pri *spri) +{ + fd_set rfds, efds; + struct timeval now = {0,0}, *next; + pri_event *event; + int sel; + + if (spri->on_loop) { + spri->on_loop(spri); + } + + FD_ZERO(&rfds); + FD_ZERO(&efds); + +#ifdef _MSC_VER + //Windows macro for FD_SET includes a warning C4127: conditional expression is constant +#pragma warning(push) +#pragma warning(disable:4127) +#endif + + FD_SET(spri->pri->fd, &rfds); + FD_SET(spri->pri->fd, &efds); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + if ((next = pri_schedule_next(spri->pri))) { + gettimeofday(&now, NULL); + now.tv_sec = next->tv_sec - now.tv_sec; + now.tv_usec = next->tv_usec - now.tv_usec; + if (now.tv_usec < 0) { + now.tv_usec += 1000000; + now.tv_sec -= 1; + } + if (now.tv_sec < 0) { + now.tv_sec = 0; + now.tv_usec = 0; + } + } + + sel = select(spri->pri->fd + 1, &rfds, NULL, &efds, next ? &now : NULL); + event = NULL; + + if (!sel) { + event = pri_schedule_run(spri->pri); + } else if (sel > 0) { + event = pri_check_event(spri->pri); + } + + if (event) { + event_handler handler; + /* 0 is catchall event handler */ + if ((handler = spri->eventmap[event->e] ? spri->eventmap[event->e] : spri->eventmap[0] ? spri->eventmap[0] : NULL)) { + handler(spri, event->e, event); + } else { + fprintf(stderr,"No event handler found for event %d.\n", event->e); + } + } + + return sel; +} + +int sangoma_run_pri(struct sangoma_pri *spri) +{ + int ret = 0; + + for (;;){ + ret=sangoma_one_loop(spri); + if (ret < 0){ + +#ifndef WIN32 //This needs to be adressed fror WIN32 still + if (errno == EINTR){ + /* Igonore an interrupted system call */ + continue; + } +#endif + printf("Error = %i\n",ret); + perror("Sangoma Run Pri: "); + break; + } + } + + return ret; + +} + +/* 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: + */ + diff --git a/libs/openzap/src/sangoma_pri.h b/libs/openzap/src/sangoma_pri.h new file mode 100644 index 0000000000..5ea21eb7b5 --- /dev/null +++ b/libs/openzap/src/sangoma_pri.h @@ -0,0 +1,100 @@ +/***************************************************************************** + * libsangoma.c AFT T1/E1: HDLC API Code Library + * + * Author(s): Anthony Minessale II + * Nenad Corbic + * + * Copyright: (c) 2005 Anthony Minessale II + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * ============================================================================ + */ + +#ifndef _SANGOMA_PRI_H +#define _SANGOMA_PRI_H +#include +#include + + +#define SANGOMA_MAX_CHAN_PER_SPAN 32 + +typedef enum { + SANGOMA_PRI_EVENT_ANY = 0, + SANGOMA_PRI_EVENT_DCHAN_UP = PRI_EVENT_DCHAN_UP, + SANGOMA_PRI_EVENT_DCHAN_DOWN = PRI_EVENT_DCHAN_DOWN, + SANGOMA_PRI_EVENT_RESTART = PRI_EVENT_RESTART, + SANGOMA_PRI_EVENT_CONFIG_ERR = PRI_EVENT_CONFIG_ERR, + SANGOMA_PRI_EVENT_RING = PRI_EVENT_RING, + SANGOMA_PRI_EVENT_HANGUP = PRI_EVENT_HANGUP, + SANGOMA_PRI_EVENT_RINGING = PRI_EVENT_RINGING, + SANGOMA_PRI_EVENT_ANSWER = PRI_EVENT_ANSWER, + SANGOMA_PRI_EVENT_HANGUP_ACK = PRI_EVENT_HANGUP_ACK, + SANGOMA_PRI_EVENT_RESTART_ACK = PRI_EVENT_RESTART_ACK, + SANGOMA_PRI_EVENT_FACNAME = PRI_EVENT_FACNAME, + SANGOMA_PRI_EVENT_INFO_RECEIVED = PRI_EVENT_INFO_RECEIVED, + SANGOMA_PRI_EVENT_PROCEEDING = PRI_EVENT_PROCEEDING, + SANGOMA_PRI_EVENT_SETUP_ACK = PRI_EVENT_SETUP_ACK, + SANGOMA_PRI_EVENT_HANGUP_REQ = PRI_EVENT_HANGUP_REQ, + SANGOMA_PRI_EVENT_NOTIFY = PRI_EVENT_NOTIFY, + SANGOMA_PRI_EVENT_PROGRESS = PRI_EVENT_PROGRESS, + SANGOMA_PRI_EVENT_KEYPAD_DIGIT = PRI_EVENT_KEYPAD_DIGIT +} sangoma_pri_event_t; + +typedef enum { + SANGOMA_PRI_NETWORK = PRI_NETWORK, + SANGOMA_PRI_CPE = PRI_CPE +} sangoma_pri_node_t; + +typedef enum { + SANGOMA_PRI_SWITCH_UNKNOWN = PRI_SWITCH_UNKNOWN, + SANGOMA_PRI_SWITCH_NI2 = PRI_SWITCH_NI2, + SANGOMA_PRI_SWITCH_DMS100 = PRI_SWITCH_DMS100, + SANGOMA_PRI_SWITCH_LUCENT5E = PRI_SWITCH_LUCENT5E, + SANGOMA_PRI_SWITCH_ATT4ESS = PRI_SWITCH_ATT4ESS, + SANGOMA_PRI_SWITCH_EUROISDN_E1 = PRI_SWITCH_EUROISDN_E1, + SANGOMA_PRI_SWITCH_EUROISDN_T1 = PRI_SWITCH_EUROISDN_T1, + SANGOMA_PRI_SWITCH_NI1 = PRI_SWITCH_NI1, + SANGOMA_PRI_SWITCH_GR303_EOC = PRI_SWITCH_GR303_EOC, + SANGOMA_PRI_SWITCH_GR303_TMC = PRI_SWITCH_GR303_TMC, + SANGOMA_PRI_SWITCH_QSIG = PRI_SWITCH_QSIG +} sangoma_pri_switch_t; + +typedef enum { + SANGOMA_PRI_READY = (1 << 0) +} sangoma_pri_flag_t; + +struct sangoma_pri; +typedef int (*event_handler)(struct sangoma_pri *, sangoma_pri_event_t, pri_event *); +typedef int (*loop_handler)(struct sangoma_pri *); +#define MAX_EVENT 18 + +struct sangoma_pri { + struct pri *pri; + int span; + int dchan; + unsigned int flags; + void *private_info; + event_handler eventmap[MAX_EVENT+1]; + loop_handler on_loop; + zap_channel_t *zdchan; +}; + +struct sangoma_pri_event_list { + int event_id; + int pri_event; + char *name; +}; + + + +#define SANGOMA_MAP_PRI_EVENT(spri, event, func) spri.eventmap[event] = func; + +char *sangoma_pri_event_str(sangoma_pri_event_t event_id); +int sangoma_one_loop(struct sangoma_pri *spri); +int sangoma_init_pri(struct sangoma_pri *spri, int span, int dchan, int swtype, int node, int debug); +int sangoma_run_pri(struct sangoma_pri *spri); + +#endif diff --git a/libs/openzap/src/ss7/README b/libs/openzap/src/ss7/README new file mode 100644 index 0000000000..0efe94c94a --- /dev/null +++ b/libs/openzap/src/ss7/README @@ -0,0 +1,3 @@ +SS7 Coming soon + +-Shane Burrell- diff --git a/libs/openzap/src/testanalog.c b/libs/openzap/src/testanalog.c new file mode 100644 index 0000000000..1a9542be4f --- /dev/null +++ b/libs/openzap/src/testanalog.c @@ -0,0 +1,133 @@ +#include "openzap.h" + + +static void *test_call(zap_thread_t *me, void *obj) +{ + zap_channel_t *chan = (zap_channel_t *) obj; + uint8_t frame[1024]; + zap_size_t len; + char *number = strdup("5551212"); + + zap_sleep(10 * 1000); + + zap_log(ZAP_LOG_DEBUG, "answer call and start echo test\n"); + + zap_set_state_locked(chan, ZAP_CHANNEL_STATE_UP); + zap_channel_command(chan, ZAP_COMMAND_SEND_DTMF, number); + + while (chan->state == ZAP_CHANNEL_STATE_UP) { + zap_wait_flag_t flags = ZAP_READ; + + if (zap_channel_wait(chan, &flags, -1) == ZAP_FAIL) { + break; + } + len = sizeof(frame); + if (flags & ZAP_READ) { + if (zap_channel_read(chan, frame, &len) == ZAP_SUCCESS) { + //zap_log(ZAP_LOG_DEBUG, "WRITE %d\n", len); + zap_channel_write(chan, frame, sizeof(frame), &len); + } else { + break; + } + } + } + + if (chan->state == ZAP_CHANNEL_STATE_UP) { + zap_set_state_locked(chan, ZAP_CHANNEL_STATE_BUSY); + } + + zap_log(ZAP_LOG_DEBUG, "call over\n"); + free(number); + return NULL; +} + +static ZIO_SIGNAL_CB_FUNCTION(on_signal) +{ + zap_log(ZAP_LOG_DEBUG, "got sig [%s]\n", zap_signal_event2str(sigmsg->event_id)); + + switch(sigmsg->event_id) { + case ZAP_SIGEVENT_START: + zap_set_state_locked(sigmsg->channel, ZAP_CHANNEL_STATE_RING); + zap_log(ZAP_LOG_DEBUG, "launching thread and indicating ring\n"); + zap_thread_create_detached(test_call, sigmsg->channel); + break; + default: + break; + } + + return ZAP_SUCCESS; +} + +static int R = 0; +#if 0 +static void handle_SIGINT(int sig) +{ + if (sig); + R = 0; + return; +} +#endif + +int main(int argc, char *argv[]) +{ + zap_span_t *span; + int span_id; + int digit_timeout = 2000; + int max_dialstr = 11; + + if (argc < 2) { + printf("usage %s \n", argv[0]); + exit(-1); + } + + span_id = atoi(argv[1]); + + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + + if (zap_global_init() != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error loading OpenZAP\n"); + exit(-1); + } + + zap_log(ZAP_LOG_DEBUG, "OpenZAP loaded\n"); + + if (zap_span_find(span_id, &span) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span\n"); + goto done; + } + + + if (zap_configure_span("analog", span, on_signal, + "tonemap", "us", + "digit_timeout", &digit_timeout, + "max_dialstr", &max_dialstr, + TAG_END + ) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error configuring OpenZAP span\n"); + goto done; + } + zap_span_start(span); + + R = 1; + + while(zap_running() && R) { + zap_sleep(1 * 1000); + } + +done: + + zap_global_destroy(); + return 0; +} + +/* 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: + */ + diff --git a/libs/openzap/src/testapp.c b/libs/openzap/src/testapp.c new file mode 100644 index 0000000000..cfa27c9296 --- /dev/null +++ b/libs/openzap/src/testapp.c @@ -0,0 +1,83 @@ +#include "openzap.h" + +int main(int argc, char *argv[]) +{ + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + zap_channel_t *chan; + unsigned ms = 20; + zap_codec_t codec = ZAP_CODEC_SLIN; + unsigned runs = 1; + + + if (zap_global_init() != ZAP_SUCCESS) { + fprintf(stderr, "Error loading OpenZAP\n"); + exit(-1); + } + + printf("OpenZAP loaded\n"); + + top: + //if (zap_channel_open_any("wanpipe", 0, ZAP_TOP_DOWN, &chan) == ZAP_SUCCESS) { + if (zap_channel_open(1, 1, &chan) == ZAP_SUCCESS) { + int x = 0; + printf("opened channel %d:%d\n", chan->span_id, chan->chan_id); + +#if 1 + if (zap_channel_command(chan, ZAP_COMMAND_SET_INTERVAL, &ms) == ZAP_SUCCESS) { + ms = 0; + zap_channel_command(chan, ZAP_COMMAND_GET_INTERVAL, &ms); + printf("interval set to %u\n", ms); + } else { + printf("set interval failed [%s]\n", chan->last_error); + } +#endif + if (zap_channel_command(chan, ZAP_COMMAND_SET_CODEC, &codec) == ZAP_SUCCESS) { + codec = 1; + zap_channel_command(chan, ZAP_COMMAND_GET_CODEC, &codec); + printf("codec set to %u\n", codec); + } else { + printf("set codec failed [%s]\n", chan->last_error); + } + + for(x = 0; x < 25; x++) { + unsigned char buf[2048]; + zap_size_t len = sizeof(buf); + zap_wait_flag_t flags = ZAP_READ; + + if (zap_channel_wait(chan, &flags, -1) == ZAP_FAIL) { + printf("wait FAIL! %u [%s]\n", (unsigned)len, chan->last_error); + } + if (flags & ZAP_READ) { + if (zap_channel_read(chan, buf, &len) == ZAP_SUCCESS) { + printf("READ: %u\n", (unsigned)len); + } else { + printf("READ FAIL! %u [%s]\n", (unsigned)len, chan->last_error); + break; + } + } else { + printf("wait fail [%s]\n", chan->last_error); + } + } + zap_channel_close(&chan); + } else { + printf("open fail [%s]\n", chan->last_error); + } + + if(--runs) { + goto top; + } + + zap_global_destroy(); + return 0; +} + +/* 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: + */ diff --git a/libs/openzap/src/testboost.c b/libs/openzap/src/testboost.c new file mode 100644 index 0000000000..cec952219b --- /dev/null +++ b/libs/openzap/src/testboost.c @@ -0,0 +1,75 @@ +#include "openzap.h" + +static ZIO_SIGNAL_CB_FUNCTION(on_signal) +{ + return ZAP_FAIL; +} + +static int R = 0; +#if 0 +static void handle_SIGINT(int sig) +{ + if (sig); + R = 0; + return; +} +#endif +int main(int argc, char *argv[]) +{ + zap_span_t *span; + int local_port, remote_port; + + local_port = remote_port = 53000; + + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + + if (argc < 2) { + printf("umm no\n"); + exit(-1); + } + + if (zap_global_init() != ZAP_SUCCESS) { + fprintf(stderr, "Error loading OpenZAP\n"); + exit(-1); + } + + printf("OpenZAP loaded\n"); + + if (zap_span_find(atoi(argv[1]), &span) != ZAP_SUCCESS) { + fprintf(stderr, "Error finding OpenZAP span\n"); + goto done; + } + + if (zap_configure_span("ss7_boost", span, on_signal, + "local_ip", "127.0.0.65", + "local_port", &local_port, + "remote_ip", "127.0.0.66", + "remote_port", &remote_port, + TAG_END) == ZAP_SUCCESS) { + zap_span_start(span); + } else { + fprintf(stderr, "Error starting SS7_BOOST\n"); + goto done; + } + + while(zap_running() && R) { + zap_sleep(1 * 1000); + } + + done: + + zap_global_destroy(); + + return 0; +} + +/* 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: + */ diff --git a/libs/openzap/src/testcid.c b/libs/openzap/src/testcid.c new file mode 100644 index 0000000000..0018bb9a5d --- /dev/null +++ b/libs/openzap/src/testcid.c @@ -0,0 +1,108 @@ +#include "openzap.h" +zap_status_t my_write_sample(int16_t *buf, zap_size_t buflen, void *user_data); + +struct helper { + int fd; + int wrote; +}; + +zap_status_t my_write_sample(int16_t *buf, zap_size_t buflen, void *user_data) +{ + struct helper *foo = (struct helper *) user_data; + size_t len; + len = write(foo->fd, buf, buflen * 2); + if (!len) return ZAP_FAIL; + foo->wrote += buflen * 2; + return ZAP_SUCCESS; +} + +int main(int argc, char *argv[]) +{ + struct zap_fsk_modulator fsk_trans; + zap_fsk_data_state_t fsk_data = {0}; + int fd = -1; + int16_t buf[160] = {0}; + ssize_t len = 0; + size_t type, mlen; + char *sp; + char str[128] = ""; + char fbuf[256]; + uint8_t databuf[1024] = ""; + struct helper foo = {0}; + // int x, bytes, start_bits = 180, stop_bits = 5, sbits = 300; + char time_str[9]; + struct tm tm; + time_t now; + + if (argc < 2) { + int x; + const char *url = "sip:cool@rad.com"; + + if ((fd = open("tone.raw", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + fprintf(stderr, "File Error! [%s]\n", strerror(errno)); + exit(-1); + } + + + time(&now); + localtime_r(&now, &tm); + strftime(time_str, sizeof(time_str), "%m%d%H%M", &tm); + + zap_fsk_data_init(&fsk_data, databuf, sizeof(databuf)); +#if 1 + + zap_fsk_data_add_mdmf(&fsk_data, MDMF_DATETIME, (uint8_t *)time_str, strlen(time_str)); + //zap_fsk_data_add_mdmf(&fsk_data, MDMF_DATETIME, "06091213", 8); + zap_fsk_data_add_mdmf(&fsk_data, MDMF_PHONE_NUM, (uint8_t *)"14149361212", 7); + zap_fsk_data_add_mdmf(&fsk_data, MDMF_PHONE_NAME, (uint8_t *)"Fred Smith", 10); + for(x = 0; x < 0; x++) + zap_fsk_data_add_mdmf(&fsk_data, MDMF_ALT_ROUTE, (uint8_t *)url, strlen(url)); +#else + zap_fsk_data_add_sdmf(&fsk_data, "06061234", "0"); + //zap_fsk_data_add_sdmf(&state, "06061234", "5551212"); +#endif + zap_fsk_data_add_checksum(&fsk_data); + + foo.fd = fd; + + + zap_fsk_modulator_init(&fsk_trans, FSK_BELL202, 8000, &fsk_data, -14, 180, 5, 300, my_write_sample, &foo); + zap_fsk_modulator_send_all((&fsk_trans)); + + printf("%u %d %d\n", (unsigned) fsk_data.dlen, foo.wrote, fsk_trans.est_bytes); + + if (fd > -1) { + close (fd); + } + + return 0; + } + + if (zap_fsk_demod_init(&fsk_data, 8000, (uint8_t *)fbuf, sizeof(fbuf))) { + printf("wtf\n"); + return 0; + } + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "cant open file %s\n", argv[1]); + exit (-1); + } + + while((len = read(fd, buf, sizeof(buf))) > 0) { + if (zap_fsk_demod_feed(&fsk_data, buf, len / 2) != ZAP_SUCCESS) { + break; + } + } + + while(zap_fsk_data_parse(&fsk_data, &type, &sp, &mlen) == ZAP_SUCCESS) { + zap_copy_string(str, sp, mlen+1); + *(str+mlen) = '\0'; + zap_clean_string(str); + printf("TYPE %u (%s) LEN %u VAL [%s]\n", (unsigned)type, zap_mdmf_type2str(type), (unsigned)mlen, str); + } + + zap_fsk_demod_destroy(&fsk_data); + + close(fd); + return 0; +} diff --git a/libs/openzap/src/testisdn.c b/libs/openzap/src/testisdn.c new file mode 100644 index 0000000000..f32f47a3f0 --- /dev/null +++ b/libs/openzap/src/testisdn.c @@ -0,0 +1,74 @@ +#include "openzap.h" +#include + + +static ZIO_SIGNAL_CB_FUNCTION(on_signal) +{ + return ZAP_FAIL; +} + +static int R = 0; +static void handle_SIGINT(int sig) +{ + if (sig); + R = 0; + return; +} + +int main(int argc, char *argv[]) +{ + zap_span_t *span; + + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + + if (argc < 2) { + printf("umm no\n"); + exit(-1); + } + + if (zap_global_init() != ZAP_SUCCESS) { + fprintf(stderr, "Error loading OpenZAP\n"); + exit(-1); + } + + printf("OpenZAP loaded\n"); + + if (zap_span_find(atoi(argv[1]), &span) != ZAP_SUCCESS) { + fprintf(stderr, "Error finding OpenZAP span\n"); + goto done; + } + + if (zap_configure_span("isdn", span, on_signal, + "mode", "te", + "dialect", "national", + TAG_END) == ZAP_SUCCESS) { + zap_span_start(span); + } else { + fprintf(stderr, "Error starting ISDN D-Channel\n"); + goto done; + } + + signal(SIGINT, handle_SIGINT); + R = 1; + while(R) { + zap_sleep(1 * 1000); + } + + done: + + zap_global_destroy(); + + return 1; + +} + +/* 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: + */ diff --git a/libs/openzap/src/testm3ua.c b/libs/openzap/src/testm3ua.c new file mode 100644 index 0000000000..16377cd067 --- /dev/null +++ b/libs/openzap/src/testm3ua.c @@ -0,0 +1,60 @@ +/* + * testm3ua.c + * openzap + * + * Created by Shane Burrell on 4/8/08. + * Copyright 2008 __MyCompanyName__. All rights reserved. + * + */ + +#include "testm3ua.h" +#include "openzap.h" +#include "zap_m3ua.h" + +static ZIO_SIGNAL_CB_FUNCTION(on_signal) +{ + return ZAP_FAIL; +} + +int main(int argc, char *argv[]) +{ + zap_span_t *span; + //m3ua_data_t *data; + + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + + if (argc < 5) { + printf("more args needed\n"); + exit(-1); + } + + if (zap_global_init() != ZAP_SUCCESS) { + fprintf(stderr, "Error loading OpenZAP\n"); + exit(-1); + } + + printf("OpenZAP loaded\n"); + + if (zap_span_find(atoi(argv[1]), &span) != ZAP_SUCCESS) { + fprintf(stderr, "Error finding OpenZAP span\n"); + goto done; + } + + + if (zap_m3ua_configure_span(span) == ZAP_SUCCESS) { + //data = span->signal_data; + zap_m3ua_start(span); + } else { + fprintf(stderr, "Error starting M3UA\n"); + goto done; + } + + //while(zap_test_flag(data, ZAP_M3UA_RUNNING)) { + // zap_sleep(1 * 1000); + //} + + done: + + zap_global_destroy(); + +} diff --git a/libs/openzap/src/testpri.c b/libs/openzap/src/testpri.c new file mode 100644 index 0000000000..1d203eae55 --- /dev/null +++ b/libs/openzap/src/testpri.c @@ -0,0 +1,162 @@ +#include "openzap.h" +#include + +static int THREADS[4][31] = { {0} }; +static int R = 0; +static int T = 0; +static zap_mutex_t *mutex = NULL; + + +static void *channel_run(zap_thread_t *me, void *obj) +{ + zap_channel_t *zchan = obj; + int fd = -1; + short buf[160]; + + zap_mutex_lock(mutex); + T++; + zap_mutex_unlock(mutex); + + zap_set_state_locked_wait(zchan, ZAP_CHANNEL_STATE_UP); + + if ((fd = open("test.raw", O_RDONLY, 0)) < 0) { + goto end; + } + + while(R == 1 && THREADS[zchan->span_id][zchan->chan_id] == 1) { + ssize_t bytes = read(fd, buf, sizeof(buf)); + size_t bbytes; + + if (bytes <= 0) { + break; + } + + bbytes = (size_t) bytes; + + zio_slin2alaw(buf, sizeof(buf), &bbytes); + + if (zap_channel_write(zchan, buf, sizeof(buf), &bbytes) != ZAP_SUCCESS) { + break; + } + } + + close(fd); + + end: + + zap_set_state_locked_wait(zchan, ZAP_CHANNEL_STATE_HANGUP); + + THREADS[zchan->span_id][zchan->chan_id] = 0; + + zap_mutex_lock(mutex); + T = 0; + zap_mutex_unlock(mutex); + + return NULL; +} + +static ZIO_SIGNAL_CB_FUNCTION(on_signal) +{ + zap_log(ZAP_LOG_DEBUG, "got sig %d:%d [%s]\n", sigmsg->channel->span_id, sigmsg->channel->chan_id, zap_signal_event2str(sigmsg->event_id)); + + switch(sigmsg->event_id) { + + case ZAP_SIGEVENT_STOP: + THREADS[sigmsg->channel->span_id][sigmsg->channel->chan_id] = -1; + break; + + case ZAP_SIGEVENT_START: + if (!THREADS[sigmsg->channel->span_id][sigmsg->channel->chan_id]) { + THREADS[sigmsg->channel->span_id][sigmsg->channel->chan_id] = 1; + zap_thread_create_detached(channel_run, sigmsg->channel); + } + + break; + default: + break; + } + + return ZAP_SUCCESS; +} + + +static void handle_SIGINT(int sig) +{ + if (sig); + + zap_mutex_lock(mutex); + R = 0; + zap_mutex_unlock(mutex); + + return; +} + +int main(int argc, char *argv[]) +{ + zap_span_t *span; + zap_mutex_create(&mutex); + + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + + if (argc < 2) { + printf("umm no\n"); + exit(-1); + } + + if (zap_global_init() != ZAP_SUCCESS) { + fprintf(stderr, "Error loading OpenZAP\n"); + exit(-1); + } + + printf("OpenZAP loaded\n"); + + if (zap_span_find(atoi(argv[1]), &span) != ZAP_SUCCESS) { + fprintf(stderr, "Error finding OpenZAP span\n"); + goto done; + } + + + + if (zap_configure_span( + "libpri", span, on_signal, + "node", "cpe", + "switch", "euroisdn", + "dp", "unknown", + "l1", "alaw", + "debug", NULL, + "opts", 0, + TAG_END) == ZAP_SUCCESS) { + + + zap_span_start(span); + } else { + fprintf(stderr, "Error starting ISDN D-Channel\n"); + goto done; + } + + signal(SIGINT, handle_SIGINT); + zap_mutex_lock(mutex); + R = 1; + zap_mutex_unlock(mutex); + while(R || T) { + zap_sleep(1 * 1000); + } + + done: + + zap_global_destroy(); + + return 1; + +} + +/* 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: + */ diff --git a/libs/openzap/src/testr2.c b/libs/openzap/src/testr2.c new file mode 100644 index 0000000000..83a950cff0 --- /dev/null +++ b/libs/openzap/src/testr2.c @@ -0,0 +1,86 @@ +#include "openzap.h" +#include + +static int R = 0; +static zap_mutex_t *mutex = NULL; + +static ZIO_SIGNAL_CB_FUNCTION(on_r2_signal) +{ + zap_log(ZAP_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", zap_signal_event2str(sigmsg->event_id), sigmsg->channel->physical_chan_id); + return ZAP_SUCCESS; +} + +static void handle_SIGINT(int sig) +{ + zap_mutex_lock(mutex); + R = 0; + zap_mutex_unlock(mutex); + return; +} + +int main(int argc, char *argv[]) +{ + zap_span_t *span; + zap_mutex_create(&mutex); + + zap_global_set_default_logger(ZAP_LOG_LEVEL_DEBUG); + + if (argc < 2) { + printf("umm no\n"); + exit(-1); + } + + if (zap_global_init() != ZAP_SUCCESS) { + fprintf(stderr, "Error loading OpenZAP\n"); + exit(-1); + } + + printf("OpenZAP loaded\n"); + + if (zap_span_find(atoi(argv[1]), &span) != ZAP_SUCCESS) { + fprintf(stderr, "Error finding OpenZAP span\n"); + goto done; + } + + + + if (zap_configure_span("r2", span, on_r2_signal, + "variant", "mx", + "max_ani", 10, + "max_dnis", 4, + "logging", "all", + TAG_END) == ZAP_SUCCESS) { + + + zap_span_start(span); + } else { + fprintf(stderr, "Error starting R2 span\n"); + goto done; + } + + signal(SIGINT, handle_SIGINT); + zap_mutex_lock(mutex); + R = 1; + zap_mutex_unlock(mutex); + while(R) { + zap_sleep(1 * 1000); + } + + done: + + zap_global_destroy(); + + return 1; + +} + +/* 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: + */ diff --git a/libs/openzap/src/testtones.c b/libs/openzap/src/testtones.c new file mode 100644 index 0000000000..273b793acf --- /dev/null +++ b/libs/openzap/src/testtones.c @@ -0,0 +1,76 @@ +#include "openzap.h" + +struct ttmp { + int fd; +}; + +static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) +{ + struct ttmp *tmp = ts->user_data; + int wrote; + size_t len; + + wrote = teletone_mux_tones(ts, map); + len = write(tmp->fd, ts->buffer, wrote * 2); + + if (!len) return -1; + + return 0; +} + +#if 1 +int main(int argc, char *argv[]) +{ + teletone_generation_session_t ts; + struct ttmp tmp; + + if (argc < 3) { + fprintf(stderr, "Arg Error! \n"); + exit(-1); + } + + if ((tmp.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + fprintf(stderr, "File Error! [%s]\n", strerror(errno)); + exit(-1); + } + + teletone_init_session(&ts, 0, teletone_handler, &tmp); + ts.rate = 8000; + ts.debug = 1; + ts.debug_stream = stdout; + teletone_run(&ts, argv[2]); + close(tmp.fd); + + return 0; +} +#else +int32_t main(int argc, char *argv[]) +{ + int32_t j, i, fd = -1; + int32_t rate = 8000; + /* SIT tones and durations */ + float tones[] = { 913.8, 1370.6, 1776.7, 0 }; + int32_t durations[] = {274, 274, 380, 0}; + teletone_dds_state_t dds = {0}; + int16_t sample; + size_t len = 1; + + if (argc < 2 || (fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + fprintf(stderr, "File Error!\n", strerror(errno)); + exit(-1); + } + + for (j = 0; tones[j] && durations[j]; j++) { + + teletone_dds_state_set_tone(&dds, tones[j], rate, -50); + + for(i = 0; (i < durations[j] * rate / 1000) && len != 0; i++) { + sample = teletone_dds_modulate_sample(&dds) * 20; + len = write(fd, &sample, sizeof(sample)); + } + + } + + close(fd); +} +#endif diff --git a/libs/openzap/src/uart.c b/libs/openzap/src/uart.c new file mode 100644 index 0000000000..5e0dd85350 --- /dev/null +++ b/libs/openzap/src/uart.c @@ -0,0 +1,124 @@ + +/* + * uart.c + * + * Copyright (c) 2005 Robert Krten. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This module contains a simple 8-bit UART, which performs a callback + * with the decoded byte value. + * + * 2005 06 11 R. Krten created +*/ + +#include +#include +#include +#include + +#include "uart.h" + +/* + * dsp_uart_attr_init + * + * Initializes the attributes structure; this must be done before the + * attributes structure is used. +*/ + +void dsp_uart_attr_init (dsp_uart_attr_t *attr) +{ + memset (attr, 0, sizeof (*attr)); +} + +/* + * dsp_uart_attr_get_bytehandler + * dsp_uart_attr_set_bytehandler + * + * These functions get and set their respective elements from the + * attributes structure. If an error code is returned, it is just + * zero == ok, -1 == fail. +*/ + +bytehandler_func_t dsp_uart_attr_get_bytehandler(dsp_uart_attr_t *attr, void **bytehandler_arg) +{ + *bytehandler_arg = attr->bytehandler_arg; + return attr->bytehandler; +} + +void dsp_uart_attr_set_bytehandler(dsp_uart_attr_t *attr, bytehandler_func_t bytehandler, void *bytehandler_arg) +{ + attr->bytehandler = bytehandler; + attr->bytehandler_arg = bytehandler_arg; +} + +dsp_uart_handle_t *dsp_uart_create(dsp_uart_attr_t *attr) +{ + dsp_uart_handle_t *handle; + + handle = malloc(sizeof (*handle)); + if (handle) { + memset(handle, 0, sizeof (*handle)); + + /* fill the attributes member */ + memcpy(&handle->attr, attr, sizeof (*attr)); + } + return handle; +} + +void dsp_uart_destroy(dsp_uart_handle_t **handle) +{ + if (*handle) { + free(*handle); + *handle = NULL; + } +} + + +void dsp_uart_bit_handler(void *x, int bit) +{ + dsp_uart_handle_t *handle = (dsp_uart_handle_t *) x; + + if (!handle->have_start) { + if (bit) { + return; /* waiting for start bit (0) */ + } + handle->have_start = 1; + handle->data = 0; + handle->nbits = 0; + return; + } + + handle->data >>= 1; + handle->data |= 0x80 * !!bit; + handle->nbits++; + + if (handle->nbits == 8) { + handle->attr.bytehandler(handle->attr.bytehandler_arg, handle->data); + handle->nbits = 0; + handle->data = 0; + handle->have_start = 0; + } +/* might consider handling errors in the future... */ +} + diff --git a/libs/openzap/src/zap_buffer.c b/libs/openzap/src/zap_buffer.c new file mode 100644 index 0000000000..0e2279d7e3 --- /dev/null +++ b/libs/openzap/src/zap_buffer.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "openzap.h" +#include "zap_buffer.h" + +static unsigned buffer_id = 0; + +struct zap_buffer { + unsigned char *data; + unsigned char *head; + zap_size_t used; + zap_size_t actually_used; + zap_size_t datalen; + zap_size_t max_len; + zap_size_t blocksize; + unsigned id; + int loops; +}; + + +OZ_DECLARE(zap_status_t) zap_buffer_create(zap_buffer_t **buffer, zap_size_t blocksize, zap_size_t start_len, zap_size_t max_len) +{ + zap_buffer_t *new_buffer; + + new_buffer = malloc(sizeof(*new_buffer)); + if (new_buffer) { + memset(new_buffer, 0, sizeof(*new_buffer)); + + if (start_len) { + new_buffer->data = malloc(start_len); + if (!new_buffer->data) { + free(new_buffer); + return ZAP_MEMERR; + } + memset(new_buffer->data, 0, start_len); + } + + new_buffer->max_len = max_len; + new_buffer->datalen = start_len; + new_buffer->id = buffer_id++; + new_buffer->blocksize = blocksize; + new_buffer->head = new_buffer->data; + + *buffer = new_buffer; + return ZAP_SUCCESS; + } + + return ZAP_MEMERR; +} + +OZ_DECLARE(zap_size_t) zap_buffer_len(zap_buffer_t *buffer) +{ + + assert(buffer != NULL); + + return buffer->datalen; + +} + + +OZ_DECLARE(zap_size_t) zap_buffer_freespace(zap_buffer_t *buffer) +{ + assert(buffer != NULL); + + + if (buffer->max_len) { + return (zap_size_t) (buffer->max_len - buffer->used); + } + return 1000000; + +} + +OZ_DECLARE(zap_size_t) zap_buffer_inuse(zap_buffer_t *buffer) +{ + assert(buffer != NULL); + + return buffer->used; +} + +OZ_DECLARE(zap_size_t) zap_buffer_seek(zap_buffer_t *buffer, zap_size_t datalen) +{ + zap_size_t reading = 0; + + assert(buffer != NULL); + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + buffer->used = buffer->actually_used - reading; + buffer->head = buffer->data + reading; + + return reading; +} + +OZ_DECLARE(zap_size_t) zap_buffer_toss(zap_buffer_t *buffer, zap_size_t datalen) +{ + zap_size_t reading = 0; + + assert(buffer != NULL); + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + buffer->used -= reading; + buffer->head += reading; + + return buffer->used; +} + +OZ_DECLARE(void) zap_buffer_set_loops(zap_buffer_t *buffer, int loops) +{ + buffer->loops = loops; +} + +OZ_DECLARE(zap_size_t) zap_buffer_read_loop(zap_buffer_t *buffer, void *data, zap_size_t datalen) +{ + zap_size_t len; + if ((len = zap_buffer_read(buffer, data, datalen)) < datalen) { + if (buffer->loops == 0) { + return len; + } + buffer->head = buffer->data; + buffer->used = buffer->actually_used; + len = zap_buffer_read(buffer, (char*)data + len, datalen - len); + buffer->loops--; + } + return len; +} + +OZ_DECLARE(zap_size_t) zap_buffer_read(zap_buffer_t *buffer, void *data, zap_size_t datalen) +{ + zap_size_t reading = 0; + + assert(buffer != NULL); + assert(data != NULL); + + + if (buffer->used < 1) { + buffer->used = 0; + return 0; + } else if (buffer->used >= datalen) { + reading = datalen; + } else { + reading = buffer->used; + } + + memcpy(data, buffer->head, reading); + buffer->used -= reading; + buffer->head += reading; + + /* if (buffer->id == 4) printf("%u o %d = %d\n", buffer->id, (unsigned)reading, (unsigned)buffer->used); */ + return reading; +} + +OZ_DECLARE(zap_size_t) zap_buffer_write(zap_buffer_t *buffer, const void *data, zap_size_t datalen) +{ + zap_size_t freespace, actual_freespace; + + assert(buffer != NULL); + assert(data != NULL); + assert(buffer->data != NULL); + + if (!datalen) { + return buffer->used; + } + + actual_freespace = buffer->datalen - buffer->actually_used; + if (actual_freespace < datalen && (!buffer->max_len || (buffer->used + datalen <= buffer->max_len))) { + memmove(buffer->data, buffer->head, buffer->used); + buffer->head = buffer->data; + buffer->actually_used = buffer->used; + } + + freespace = buffer->datalen - buffer->used; + + /* + if (buffer->data != buffer->head) { + memmove(buffer->data, buffer->head, buffer->used); + buffer->head = buffer->data; + } + */ + + if (freespace < datalen) { + zap_size_t new_size, new_block_size; + void *data; + + new_size = buffer->datalen + datalen; + new_block_size = buffer->datalen + buffer->blocksize; + + if (new_block_size > new_size) { + new_size = new_block_size; + } + buffer->head = buffer->data; + data = realloc(buffer->data, new_size); + if (!data) { + return 0; + } + buffer->data = data; + buffer->head = buffer->data; + buffer->datalen = new_size; + } + + + freespace = buffer->datalen - buffer->used; + + if (freespace < datalen) { + return 0; + } else { + memcpy(buffer->head + buffer->used, data, datalen); + buffer->used += datalen; + buffer->actually_used += datalen; + } + /* if (buffer->id == 4) printf("%u i %d = %d\n", buffer->id, (unsigned)datalen, (unsigned)buffer->used); */ + + return buffer->used; +} + +OZ_DECLARE(void) zap_buffer_zero(zap_buffer_t *buffer) +{ + assert(buffer != NULL); + assert(buffer->data != NULL); + + buffer->used = 0; + buffer->actually_used = 0; + buffer->head = buffer->data; +} + +OZ_DECLARE(zap_size_t) zap_buffer_zwrite(zap_buffer_t *buffer, const void *data, zap_size_t datalen) +{ + zap_size_t w; + + if (!(w = zap_buffer_write(buffer, data, datalen))) { + zap_buffer_zero(buffer); + return zap_buffer_write(buffer, data, datalen); + } + + return w; +} + +OZ_DECLARE(void) zap_buffer_destroy(zap_buffer_t **buffer) +{ + if (*buffer) { + free((*buffer)->data); + free(*buffer); + } + + *buffer = NULL; +} + +/* 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: + */ diff --git a/libs/openzap/src/zap_callerid.c b/libs/openzap/src/zap_callerid.c new file mode 100644 index 0000000000..7a6e658261 --- /dev/null +++ b/libs/openzap/src/zap_callerid.c @@ -0,0 +1,307 @@ +#include "openzap.h" +#include "fsk.h" +#include "uart.h" + + + +static void fsk_byte_handler (void *x, int data) +{ + zap_fsk_data_state_t *state = (zap_fsk_data_state_t *) x; + uint8_t byte = (uint8_t)data; + + top: + + if (state->init == 3) { + return; + } + + if (state->dlen) { + goto add_byte; + } + + if (state->bpos == 1) { + state->blen = byte; + + if ((uint32_t)(state->dlen = state->bpos + byte + 2) > state->bufsize) { + state->dlen = state->bufsize; + } + goto top; + } + + add_byte: + + if (state->bpos <= state->dlen) { + state->buf[state->bpos++] = byte; + } else { + state->init = 3; + } +} + +OZ_DECLARE(zap_status_t) zap_fsk_data_init(zap_fsk_data_state_t *state, uint8_t *data, uint32_t datalen) +{ + memset(state, 0, sizeof(*state)); + state->buf = data; + state->bufsize = datalen; + state->bpos = 2; + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_fsk_data_add_sdmf(zap_fsk_data_state_t *state, const char *date, char *number) +{ + size_t dlen = strlen(date); + size_t nlen = strlen(number); + + state->buf[0] = ZAP_CID_TYPE_SDMF; + memcpy(&state->buf[state->bpos], date, dlen); + state->bpos += dlen; + memcpy(&state->buf[state->bpos], number, nlen); + state->bpos += nlen; + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_fsk_data_add_mdmf(zap_fsk_data_state_t *state, zap_mdmf_type_t type, const uint8_t *data, uint32_t datalen) +{ + state->buf[0] = ZAP_CID_TYPE_MDMF; + state->buf[state->bpos++] = type; + state->buf[state->bpos++] = (uint8_t)datalen; + memcpy(&state->buf[state->bpos], data, datalen); + state->bpos += datalen; + return ZAP_SUCCESS; +} + + +OZ_DECLARE(zap_status_t) zap_fsk_data_add_checksum(zap_fsk_data_state_t *state) +{ + uint32_t i; + uint8_t check = 0; + + state->buf[1] = (uint8_t)(state->bpos - 2); + + for (i = 0; i < state->bpos; i++) { + check = check + state->buf[i]; + } + + state->checksum = state->buf[state->bpos] = (uint8_t)(256 - check); + state->bpos++; + + state->dlen = state->bpos; + state->blen = state->buf[1]; + + return ZAP_SUCCESS; +} + + +OZ_DECLARE(zap_status_t) zap_fsk_data_parse(zap_fsk_data_state_t *state, zap_size_t *type, char **data, zap_size_t *len) +{ + + zap_size_t i; + int sum = 0; + + top: + + if (state->checksum != 0 || state->ppos >= state->dlen - 1) { + return ZAP_FAIL; + } + + if (!state->ppos) { + for(i = 0; i < state->bpos; i++) { + sum += state->buf[i]; + } + state->checksum = sum % 256; + state->ppos = 2; + + if (state->buf[0] != ZAP_CID_TYPE_MDMF && state->buf[0] != ZAP_CID_TYPE_SDMF) { + state->checksum = -1; + } + goto top; + } + + if (state->buf[0] == ZAP_CID_TYPE_SDMF) { + /* convert sdmf to mdmf so we don't need 2 parsers */ + if (state->ppos == 2) { + *type = MDMF_DATETIME; + *len = 8; + } else { + if (state->buf[state->ppos] == 'P' || state->buf[state->ppos] == 'O') { + *type = MDMF_NO_NUM; + *len = 1; + } else { + *type = MDMF_PHONE_NUM; + *len = state->blen - 8; + } + } + *data = (char *)&state->buf[state->ppos]; + state->ppos += *len; + return ZAP_SUCCESS; + } else if (state->buf[0] == ZAP_CID_TYPE_MDMF) { + *type = state->buf[state->ppos++]; + *len = state->buf[state->ppos++]; + *data = (char *)&state->buf[state->ppos]; + state->ppos += *len; + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +OZ_DECLARE(zap_status_t) zap_fsk_demod_feed(zap_fsk_data_state_t *state, int16_t *data, zap_size_t samples) +{ + uint32_t x; + int16_t *sp = data; + + if (state->init == 3) { + return ZAP_FAIL; + } + + for (x = 0; x < samples; x++) { + dsp_fsk_sample (state->fsk1200_handle, (double) *sp++ / 32767.0); + if (state->dlen && state->bpos >= state->dlen) { + state->init = 3; + return ZAP_FAIL; + } + } + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_fsk_demod_destroy(zap_fsk_data_state_t *state) +{ + dsp_fsk_destroy(&state->fsk1200_handle); + memset(state, 0, sizeof(*state)); + return ZAP_SUCCESS; +} + +OZ_DECLARE(int) zap_fsk_demod_init(zap_fsk_data_state_t *state, int rate, uint8_t *buf, zap_size_t bufsize) +{ + + dsp_fsk_attr_t fsk1200_attr; + + if (state->fsk1200_handle) { + dsp_fsk_destroy(&state->fsk1200_handle); + } + + memset(state, 0, sizeof(*state)); + memset(buf, 0, bufsize); + state->buf = buf; + state->bufsize = bufsize; + + dsp_fsk_attr_init (&fsk1200_attr); + dsp_fsk_attr_set_samplerate (&fsk1200_attr, rate); + dsp_fsk_attr_set_bytehandler (&fsk1200_attr, fsk_byte_handler, state); + state->fsk1200_handle = dsp_fsk_create (&fsk1200_attr); + + if (state->fsk1200_handle == NULL) { + return ZAP_FAIL; + } + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_size_t) zap_fsk_modulator_generate_bit(zap_fsk_modulator_t *fsk_trans, int8_t bit, int16_t *buf, zap_size_t buflen) +{ + zap_size_t i; + + for(i = 0 ; i < buflen; i++) { + fsk_trans->bit_accum += fsk_trans->bit_factor; + if (fsk_trans->bit_accum >= ZAP_FSK_MOD_FACTOR) { + fsk_trans->bit_accum -= (ZAP_FSK_MOD_FACTOR + fsk_trans->bit_factor); + break; + } + + buf[i] = teletone_dds_state_modulate_sample(&fsk_trans->dds, bit); + } + + return i; +} + + +OZ_DECLARE(int32_t) zap_fsk_modulator_generate_carrier_bits(zap_fsk_modulator_t *fsk_trans, uint32_t bits) +{ + uint32_t i = 0; + zap_size_t r = 0; + int8_t bit = 1; + + for (i = 0; i < bits; i++) { + if ((r = zap_fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) { + if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != ZAP_SUCCESS) { + break; + } + } else { + break; + } + } + + return i; +} + + +OZ_DECLARE(void) zap_fsk_modulator_generate_chan_sieze(zap_fsk_modulator_t *fsk_trans) +{ + uint32_t i = 0; + zap_size_t r = 0; + int8_t bit = 0; + + for (i = 0; i < fsk_trans->chan_sieze_bits; i++) { + if ((r = zap_fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) { + if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != ZAP_SUCCESS) { + break; + } + } else { + break; + } + bit = !bit; + } + + +} + + +OZ_DECLARE(void) zap_fsk_modulator_send_data(zap_fsk_modulator_t *fsk_trans) +{ + zap_size_t r = 0; + int8_t bit = 0; + + while((bit = zap_bitstream_get_bit(&fsk_trans->bs)) > -1) { + if ((r = zap_fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) { + if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != ZAP_SUCCESS) { + break; + } + } else { + break; + } + } +} + + +OZ_DECLARE(zap_status_t) zap_fsk_modulator_init(zap_fsk_modulator_t *fsk_trans, + fsk_modem_types_t modem_type, + uint32_t sample_rate, + zap_fsk_data_state_t *fsk_data, + float db_level, + uint32_t carrier_bits_start, + uint32_t carrier_bits_stop, + uint32_t chan_sieze_bits, + zap_fsk_write_sample_t write_sample_callback, + void *user_data) +{ + memset(fsk_trans, 0, sizeof(*fsk_trans)); + fsk_trans->modem_type = modem_type; + teletone_dds_state_set_tone(&fsk_trans->dds, fsk_modem_definitions[fsk_trans->modem_type].freq_space, sample_rate, 0); + teletone_dds_state_set_tone(&fsk_trans->dds, fsk_modem_definitions[fsk_trans->modem_type].freq_mark, sample_rate, 1); + fsk_trans->bit_factor = (uint32_t)((fsk_modem_definitions[fsk_trans->modem_type].baud_rate * ZAP_FSK_MOD_FACTOR) / (float)sample_rate); + fsk_trans->samples_per_bit = (uint32_t) (sample_rate / fsk_modem_definitions[fsk_trans->modem_type].baud_rate); + fsk_trans->est_bytes = (int32_t)(((fsk_data->dlen * 10) + carrier_bits_start + carrier_bits_stop + chan_sieze_bits) * ((fsk_trans->samples_per_bit + 1) * 2)); + fsk_trans->bit_accum = 0; + fsk_trans->fsk_data = fsk_data; + teletone_dds_state_set_tx_level(&fsk_trans->dds, db_level); + zap_bitstream_init(&fsk_trans->bs, fsk_trans->fsk_data->buf, (uint32_t)fsk_trans->fsk_data->dlen, ZAP_ENDIAN_BIG, 1); + fsk_trans->carrier_bits_start = carrier_bits_start; + fsk_trans->carrier_bits_stop = carrier_bits_stop; + fsk_trans->chan_sieze_bits = chan_sieze_bits; + fsk_trans->write_sample_callback = write_sample_callback; + fsk_trans->user_data = user_data; + return ZAP_SUCCESS; +} + diff --git a/libs/openzap/src/zap_config.c b/libs/openzap/src/zap_config.c new file mode 100644 index 0000000000..ca0315594b --- /dev/null +++ b/libs/openzap/src/zap_config.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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 "openzap.h" +#include "zap_config.h" + +int zap_config_open_file(zap_config_t *cfg, const char *file_path) +{ + FILE *f; + const char *path = NULL; + char path_buf[1024]; + + if (file_path[0] == '/') { + path = file_path; + } else { + snprintf(path_buf, sizeof(path_buf), "%s%s%s", ZAP_CONFIG_DIR, ZAP_PATH_SEPARATOR, file_path); + path = path_buf; + } + + if (!path) { + return 0; + } + + memset(cfg, 0, sizeof(*cfg)); + cfg->lockto = -1; + zap_log(ZAP_LOG_DEBUG, "Configuration file is %s.\n", path); + f = fopen(path, "r"); + + if (!f) { + if (file_path[0] != '/') { + int last = -1; + char *var, *val; + + snprintf(path_buf, sizeof(path_buf), "%s%sopenzap.conf", ZAP_CONFIG_DIR, ZAP_PATH_SEPARATOR); + path = path_buf; + + if ((f = fopen(path, "r")) == 0) { + return 0; + } + + cfg->file = f; + zap_set_string(cfg->path, path); + + while (zap_config_next_pair(cfg, &var, &val)) { + if ((cfg->sectno != last) && !strcmp(cfg->section, file_path)) { + cfg->lockto = cfg->sectno; + return 1; + } + } + + zap_config_close_file(cfg); + memset(cfg, 0, sizeof(*cfg)); + return 0; + } + + return 0; + } else { + cfg->file = f; + zap_set_string(cfg->path, path); + return 1; + } +} + +void zap_config_close_file(zap_config_t *cfg) +{ + + if (cfg->file) { + fclose(cfg->file); + } + + memset(cfg, 0, sizeof(*cfg)); +} + + + +int zap_config_next_pair(zap_config_t *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + if (!cfg->path) { + return 0; + } + + for (;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']')) != 0) { + *end = '\0'; + (*var)++; + if (**var == '+') { + (*var)++; + zap_copy_string(cfg->section, *var, sizeof(cfg->section)); + cfg->sectno++; + + if (cfg->lockto > -1 && cfg->sectno != cfg->lockto) { + break; + } + cfg->catno = 0; + cfg->lineno = 0; + *var = (char *) ""; + *val = (char *) ""; + return 1; + } else { + zap_copy_string(cfg->category, *var, sizeof(cfg->category)); + cfg->catno++; + } + continue; + } + + + + if (**var == '#' || **var == ';' || **var == '\n' || **var == '\r') { + continue; + } + + if (!strncmp(*var, "__END__", 7)) { + break; + } + + + if ((end = strchr(*var, ';')) && *(end+1) == *end) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n')) != 0) { + if (*(end - 1) == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + + if ((*val = strchr(*var, '=')) == 0) { + ret = -1; + /* log_printf(0, server.log, "Invalid syntax on %s: line %d\n", cfg->path, cfg->lineno); */ + continue; + } else { + p = *val - 1; + *(*val) = '\0'; + (*val)++; + if (*(*val) == '>') { + *(*val) = '\0'; + (*val)++; + } + + while ((*p == ' ' || *p == '\t') && p != *var) { + *p = '\0'; + p--; + } + + p = *val; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *val = p; + ret = 1; + break; + } + } + + + return ret; + +} + +OZ_DECLARE (int) zap_config_get_cas_bits(char *strvalue, unsigned char *outbits) +{ + char cas_bits[5]; + unsigned char bit = 0x8; + int x = 0; + char *double_colon = strchr(strvalue, ':'); + if (!double_colon) { + zap_log(ZAP_LOG_ERROR, "No CAS bits specified: %s, :xxxx definition expected, where x is 1 or 0\n", strvalue); + return -1; + } + double_colon++; + *outbits = 0; + cas_bits[4] = 0; + if (sscanf(double_colon, "%c%c%c%c", &cas_bits[0], &cas_bits[1], &cas_bits[2], &cas_bits[3]) != 4) { + zap_log(ZAP_LOG_ERROR, "Invalid CAS bits specified: '%s', :xxxx definition expected, where x is 1 or 0\n", double_colon); + return -1; + } + zap_log(ZAP_LOG_DEBUG, "CAS bits specification found: %s\n", cas_bits); + for (; cas_bits[x]; x++) { + if ('1' == cas_bits[x]) { + *outbits |= bit; + } else if ('0' != cas_bits[x]) { + zap_log(ZAP_LOG_ERROR, "Invalid CAS pattern specified: %s, just 0 or 1 allowed for each bit\n"); + return -1; + } + bit >>= 1; + } + return 0; +} + +/* 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: + */ diff --git a/libs/openzap/src/zap_cpu_monitor.c b/libs/openzap/src/zap_cpu_monitor.c new file mode 100644 index 0000000000..548fd176d3 --- /dev/null +++ b/libs/openzap/src/zap_cpu_monitor.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2009, Sangoma Technologies + * Moises Silva + * 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. + * + * Contributors: + * David Yat Sin + * + */ + +#ifdef WIN32 +#define _WIN32_WINNT 0x0501 // To make GetSystemTimes visible in windows.h +#include +#else /* LINUX */ + +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "openzap.h" +#include "zap_cpu_monitor.h" +struct zap_cpu_monitor_stats +{ + /* bool, just used to retrieve the values for the first time and not calculate the percentage of idle time */ + int valid_last_times; + + /* last calculated percentage of idle time */ + double last_percentage_of_idle_time; + +#ifdef __linux__ + /* all of these are the Linux jiffies last retrieved count */ + unsigned long long last_user_time; + unsigned long long last_system_time; + unsigned long long last_idle_time; + + unsigned long long last_nice_time; + unsigned long long last_irq_time; + unsigned long long last_soft_irq_time; + unsigned long long last_io_wait_time; + unsigned long long last_steal_time; + + /* /proc/stat file descriptor used to retrieve the counters */ + int procfd; + int initd; +#elif defined (WIN32) || defined (WIN64) + __int64 i64LastUserTime; + __int64 i64LastKernelTime; + __int64 i64LastIdleTime; +#else +/* Unsupported */ +#endif +}; + +#ifdef __linux__ +static zap_status_t zap_cpu_read_stats(struct zap_cpu_monitor_stats *p, + unsigned long long *user, + unsigned long long *nice, + unsigned long long *system, + unsigned long long *idle, + unsigned long long *iowait, + unsigned long long *irq, + unsigned long long *softirq, + unsigned long long *steal) +{ +// the output of proc should not change that often from one kernel to other +// see fs/proc/proc_misc.c or fs/proc/stat.c in the Linux kernel for more details +// also man 5 proc is useful +#define CPU_ELEMENTS 8 // change this if you change the format string +#define CPU_INFO_FORMAT "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu" + static const char procfile[] = "/proc/stat"; + int rc = 0; + int myerrno = 0; + int elements = 0; + const char *cpustr = NULL; + char statbuff[1024]; + + if (!p->initd) { + p->procfd = open(procfile, O_RDONLY, 0); + if(p->procfd == -1) { + zap_log(ZAP_LOG_ERROR, "Failed to open CPU statistics file %s: %s\n", procfile, strerror(myerrno)); + return ZAP_FAIL; + } + p->initd = 1; + } else { + lseek(p->procfd, 0L, SEEK_SET); + } + + rc = read(p->procfd, statbuff, sizeof(statbuff) - 1); + if (rc <= 0) { + myerrno = errno; + zap_log(ZAP_LOG_ERROR, "Failed to read CPU statistics file %s: %s\n", procfile, strerror(myerrno)); + return ZAP_FAIL; + } + + cpustr = strstr(statbuff, "cpu "); + if (!cpustr) { + zap_log(ZAP_LOG_ERROR, "wrong format for Linux proc cpu statistics: missing cpu string\n"); + return ZAP_FAIL; + } + + elements = sscanf(cpustr, CPU_INFO_FORMAT, user, nice, system, idle, iowait, irq, softirq, steal); + if (elements != CPU_ELEMENTS) { + zap_log(ZAP_LOG_ERROR, "wrong format for Linux proc cpu statistics: expected %d elements, but just found %d\n", CPU_ELEMENTS, elements); + return ZAP_FAIL; + } + return ZAP_SUCCESS; +} +#endif + +#ifdef __linux__ +OZ_DECLARE(zap_status_t) zap_cpu_get_system_idle_time (struct zap_cpu_monitor_stats *p, double *idle_percentage) +{ + unsigned long long user, nice, system, idle, iowait, irq, softirq, steal; + unsigned long long usertime, kerneltime, idletime, totaltime, halftime; + + if (zap_cpu_read_stats(p, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal)) { + zap_log(ZAP_LOG_ERROR, "Failed to retrieve Linux CPU statistics\n"); + return ZAP_FAIL; + } + + if (!p->valid_last_times) { + // we dont strictly need to save all of them but I feel code is more clear if we do + p->valid_last_times = 1; + p->last_user_time = user; + p->last_nice_time = nice; + p->last_system_time = system; + p->last_irq_time = irq; + p->last_soft_irq_time = softirq; + p->last_io_wait_time = iowait; + p->last_steal_time = steal; + p->last_idle_time = idle; + p->last_percentage_of_idle_time = 100.0; + *idle_percentage = p->last_percentage_of_idle_time; + return ZAP_SUCCESS; + } + + usertime = (user - p->last_user_time) + (nice - p->last_nice_time); + kerneltime = (system - p->last_system_time) + (irq - p->last_irq_time) + (softirq - p->last_soft_irq_time); + kerneltime += (iowait - p->last_io_wait_time); + kerneltime += (steal - p->last_steal_time); + idletime = (idle - p->last_idle_time); + + totaltime = usertime + kerneltime + idletime; + + if (totaltime <= 0) { + // this may happen if not enough time has elapsed and the jiffies counters are the same than the last time we checked + // jiffies depend on timer interrupts which depend on the number of HZ compile time setting of the kernel + // typical configs set HZ to 100 (that means, 100 jiffies updates per second, that is one each 10ms) + // avoid an arithmetic exception and return the same values + *idle_percentage = p->last_percentage_of_idle_time; + return ZAP_SUCCESS; + } + + halftime = totaltime / 2UL; + + p->last_percentage_of_idle_time = ((100 * idletime + halftime) / totaltime); + *idle_percentage = p->last_percentage_of_idle_time; + + p->last_user_time = user; + p->last_nice_time = nice; + p->last_system_time = system; + p->last_irq_time = irq; + p->last_soft_irq_time = softirq; + p->last_io_wait_time = iowait; + p->last_steal_time = steal; + p->last_idle_time = idle; + + return ZAP_SUCCESS; +} + +#elif defined (WIN32) || defined (WIN64) +OZ_DECLARE(zap_status_t) zap_cpu_get_system_idle_time(struct zap_cpu_monitor_stats *p, double *idle_percentage) +{ + FILETIME idleTime; + FILETIME kernelTime; + FILETIME userTime; + + if (!::GetSystemTimes(&idleTime, &kernelTime, &userTime)) { + return false; + } + + __int64 i64UserTime = (__int64)userTime.dwLowDateTime | ((__int64)userTime.dwHighDateTime << 32); + + __int64 i64KernelTime = (__int64)kernelTime.dwLowDateTime | ((__int64)kernelTime.dwHighDateTime << 32); + + __int64 i64IdleTime = (__int64)idleTime.dwLowDateTime | ((__int64)idleTime.dwHighDateTime << 32); + + if (p->valid_last_times) { + __int64 i64User = i64UserTime - p->i64LastUserTime; + __int64 i64Kernel = i64KernelTime - p->i64LastKernelTime; + __int64 i64Idle = i64IdleTime - p->i64LastIdleTime; + __int64 i64System = i64User + i64Kernel; + *idle_percentage = 100.0 * i64Idle / i64System; + } else { + *idle_percentage = 100.0; + p->valid_last_times = 1; + } + + /* Remember current value for the next call */ + p->i64LastUserTime = i64UserTime; + p->i64LastKernelTime = i64KernelTime; + p->i64LastIdleTime = i64IdleTime; + + /* Success */ + return ZAP_SUCCESS; +} +#else +/* Unsupported */ +OZ_DECLARE(zap_status_t) zap_cpu_get_system_idle_time(struct zap_cpu_monitor_stats *p, double *idle_percentage) +{ + return ZAP_FAIL; +} +#endif + +OZ_DECLARE(struct zap_cpu_monitor_stats*) zap_new_cpu_monitor(void) +{ + return calloc(1, sizeof(struct zap_cpu_monitor_stats)); +} + +OZ_DECLARE(void) zap_delete_cpu_monitor(struct zap_cpu_monitor_stats *p) +{ +#ifdef __linux__ + close(p->procfd); +#endif + free(p); +} + + +/* 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: + */ diff --git a/libs/openzap/src/zap_dso.c b/libs/openzap/src/zap_dso.c new file mode 100644 index 0000000000..ead40e7c4b --- /dev/null +++ b/libs/openzap/src/zap_dso.c @@ -0,0 +1,115 @@ +/* + * Cross Platform dso/dll load abstraction + * Copyright(C) 2008 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + +#include "zap_dso.h" +#include +#include + +#ifdef WIN32 +#include +#include + + +void zap_dso_destroy(zap_dso_lib_t *lib) { + if (lib && *lib) { + FreeLibrary(*lib); + *lib = NULL; + } +} + +zap_dso_lib_t zap_dso_open(const char *path, char **err) { + HINSTANCE lib; + + lib = LoadLibraryEx(path, NULL, 0); + + if (!lib) { + LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + } + + if (!lib) { + DWORD error = GetLastError(); + char tmp[80]; + sprintf(tmp, "dll open error [%ul]\n", error); + *err = _strdup(tmp); + } + + return lib; +} + +void* zap_dso_func_sym(zap_dso_lib_t lib, const char *sym, char **err) { + FARPROC func = GetProcAddress(lib, sym); + if (!func) { + DWORD error = GetLastError(); + char tmp[80]; + sprintf(tmp, "dll sym error [%ul]\n", error); + *err = _strdup(tmp); + } + return (void *)(intptr_t)func; // this should really be addr - zap_dso_func_data +} + +#else + +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + + +#include + +void zap_dso_destroy(zap_dso_lib_t *lib) { + if (lib && *lib) { + dlclose(*lib); + *lib = NULL; + } +} + +zap_dso_lib_t zap_dso_open(const char *path, char **err) { + void *lib = dlopen(path, RTLD_NOW | RTLD_LOCAL); + if (lib == NULL) { + *err = strdup(dlerror()); + } + return lib; +} + +void *zap_dso_func_sym(zap_dso_lib_t lib, const char *sym, char **err) { + void *func = dlsym(lib, sym); + if (!func) { + *err = strdup(dlerror()); + } + return func; +} +#endif + +/* }====================================================== */ + +/* 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 + */ diff --git a/libs/openzap/src/zap_io.c b/libs/openzap/src/zap_io.c new file mode 100644 index 0000000000..dc747d9429 --- /dev/null +++ b/libs/openzap/src/zap_io.c @@ -0,0 +1,3526 @@ +/* + * Copyright (c) 2007, Anthony Minessale II + * 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. + */ + +#define _GNU_SOURCE +#ifndef WIN32 +#endif +#include "openzap.h" +#include +#ifdef WIN32 +#include +#endif +#ifdef ZAP_PIKA_SUPPORT +#include "zap_pika.h" +#endif +#include "zap_cpu_monitor.h" + +static int time_is_init = 0; + +static void time_init(void) +{ +#ifdef WIN32 + timeBeginPeriod(1); +#endif + time_is_init = 1; +} + +static void time_end(void) +{ +#ifdef WIN32 + timeEndPeriod(1); +#endif + time_is_init = 0; +} + + +OZ_DECLARE(zap_time_t) zap_current_time_in_ms(void) +{ +#ifdef WIN32 + return timeGetTime(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); +#endif +} + +typedef struct { + uint8_t running; + uint8_t alarm; + uint32_t interval; + uint8_t alarm_action_flags; + uint8_t set_alarm_threshold; + uint8_t reset_alarm_threshold; + zap_interrupt_t *interrupt; +} cpu_monitor_t; + +static struct { + zap_hash_t *interface_hash; + zap_hash_t *module_hash; + zap_hash_t *span_hash; + zap_mutex_t *mutex; + zap_mutex_t *span_mutex; + uint32_t span_index; + uint32_t running; + zap_span_t *spans; + cpu_monitor_t cpu_monitor; +} globals; + +static uint8_t zap_cpu_monitor_disabled = 0; + +enum zap_enum_cpu_alarm_action_flags +{ + ZAP_CPU_ALARM_ACTION_WARN = (1 << 0), + ZAP_CPU_ALARM_ACTION_REJECT = (1 << 1) +}; + +/* enum lookup funcs */ +ZAP_ENUM_NAMES(TONEMAP_NAMES, TONEMAP_STRINGS) +ZAP_STR2ENUM(zap_str2zap_tonemap, zap_tonemap2str, zap_tonemap_t, TONEMAP_NAMES, ZAP_TONEMAP_INVALID) + +ZAP_ENUM_NAMES(OOB_NAMES, OOB_STRINGS) +ZAP_STR2ENUM(zap_str2zap_oob_event, zap_oob_event2str, zap_oob_event_t, OOB_NAMES, ZAP_OOB_INVALID) + +ZAP_ENUM_NAMES(TRUNK_TYPE_NAMES, TRUNK_STRINGS) +ZAP_STR2ENUM(zap_str2zap_trunk_type, zap_trunk_type2str, zap_trunk_type_t, TRUNK_TYPE_NAMES, ZAP_TRUNK_NONE) + +ZAP_ENUM_NAMES(START_TYPE_NAMES, START_TYPE_STRINGS) +ZAP_STR2ENUM(zap_str2zap_analog_start_type, zap_analog_start_type2str, zap_analog_start_type_t, START_TYPE_NAMES, ZAP_ANALOG_START_NA) + +ZAP_ENUM_NAMES(SIGNAL_NAMES, SIGNAL_STRINGS) +ZAP_STR2ENUM(zap_str2zap_signal_event, zap_signal_event2str, zap_signal_event_t, SIGNAL_NAMES, ZAP_SIGEVENT_INVALID) + +ZAP_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS) +ZAP_STR2ENUM(zap_str2zap_channel_state, zap_channel_state2str, zap_channel_state_t, CHANNEL_STATE_NAMES, ZAP_CHANNEL_STATE_INVALID) + +ZAP_ENUM_NAMES(MDMF_TYPE_NAMES, MDMF_STRINGS) +ZAP_STR2ENUM(zap_str2zap_mdmf_type, zap_mdmf_type2str, zap_mdmf_type_t, MDMF_TYPE_NAMES, MDMF_INVALID) + +ZAP_ENUM_NAMES(CHAN_TYPE_NAMES, CHAN_TYPE_STRINGS) +ZAP_STR2ENUM(zap_str2zap_chan_type, zap_chan_type2str, zap_chan_type_t, CHAN_TYPE_NAMES, ZAP_CHAN_TYPE_COUNT) + +static zap_status_t zap_cpu_monitor_start(void); +static void zap_cpu_monitor_stop(void); + +static const char *cut_path(const char *in) +{ + const char *p, *ret = in; + char delims[] = "/\\"; + char *i; + + for (i = delims; *i; i++) { + p = in; + while ((p = strchr(p, *i)) != 0) { + ret = ++p; + } + } + return ret; +} + +static void null_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + if (file && func && line && level && fmt) { + return; + } + return; +} + + +static const char *LEVEL_NAMES[] = { + "EMERG", + "ALERT", + "CRIT", + "ERROR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG", + NULL +}; + + +static int zap_log_level = 7; + +/* Cpu monitor thread */ +static void *zap_cpu_monitor_run(zap_thread_t *me, void *obj); + +static void default_logger(const char *file, const char *func, int line, int level, const char *fmt, ...) +{ + const char *fp; + char data[1024]; + va_list ap; + + if (level < 0 || level > 7) { + level = 7; + } + if (level > zap_log_level) { + return; + } + + fp = cut_path(file); + + va_start(ap, fmt); + + vsnprintf(data, sizeof(data), fmt, ap); + + + fprintf(stderr, "[%s] %s:%d %s() %s", LEVEL_NAMES[level], file, line, func, data); + + va_end(ap); + +} + +static zap_status_t zap_set_caller_data(zap_span_t *span, zap_caller_data_t *caller_data) +{ + if (!caller_data) { + zap_log(ZAP_LOG_CRIT, "Error: trying to set caller data, but no caller_data!\n"); + return ZAP_FAIL; + } + + if (caller_data->cid_num.plan == ZAP_NPI_INVALID) { + caller_data->cid_num.plan = span->default_caller_data.cid_num.plan; + } + + if (caller_data->cid_num.type == ZAP_TON_INVALID) { + caller_data->cid_num.type = span->default_caller_data.cid_num.type; + } + + if (caller_data->ani.plan == ZAP_NPI_INVALID) { + caller_data->ani.plan = span->default_caller_data.ani.plan; + } + + if (caller_data->ani.type == ZAP_TON_INVALID) { + caller_data->ani.type = span->default_caller_data.ani.type; + } + + if (caller_data->rdnis.plan == ZAP_NPI_INVALID) { + caller_data->rdnis.plan = span->default_caller_data.rdnis.plan; + } + + if (caller_data->rdnis.type == ZAP_NPI_INVALID) { + caller_data->rdnis.type = span->default_caller_data.rdnis.type; + } + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_set_caller_data(zap_channel_t *zchan, zap_caller_data_t *caller_data) +{ + zap_status_t err = ZAP_SUCCESS; + if (!zchan) { + zap_log(ZAP_LOG_CRIT, "Error: trying to set caller data, but no zchan!\n"); + return ZAP_FAIL; + } + if ((err = zap_set_caller_data(zchan->span, caller_data)) != ZAP_SUCCESS) { + return err; + } + zchan->caller_data = *caller_data; + return ZAP_SUCCESS; +} + +OZ_DECLARE_DATA zap_logger_t zap_log = null_logger; + +OZ_DECLARE(void) zap_global_set_logger(zap_logger_t logger) +{ + if (logger) { + zap_log = logger; + } else { + zap_log = null_logger; + } +} + +OZ_DECLARE(void) zap_global_set_default_logger(int level) +{ + if (level < 0 || level > 7) { + level = 7; + } + + zap_log = default_logger; + zap_log_level = level; +} + +OZ_DECLARE_NONSTD(int) zap_hash_equalkeys(void *k1, void *k2) +{ + return strcmp((char *) k1, (char *) k2) ? 0 : 1; +} + +OZ_DECLARE_NONSTD(uint32_t) zap_hash_hashfromstring(void *ky) +{ + unsigned char *str = (unsigned char *) ky; + uint32_t hash = 0; + int c; + + while ((c = *str++)) { + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + + +static zap_status_t zap_span_destroy(zap_span_t *span) +{ + zap_status_t status = ZAP_FAIL; + + if (zap_test_flag(span, ZAP_SPAN_CONFIGURED)) { + if (span->stop) { + span->stop(span); + } + if (span->zio && span->zio->span_destroy) { + zap_log(ZAP_LOG_INFO, "Destroying span %u type (%s)\n", span->span_id, span->type); + status = span->zio->span_destroy(span); + zap_safe_free(span->type); + zap_safe_free(span->dtmf_hangup); + } + } + + return status; +} + +static zap_status_t zap_channel_destroy(zap_channel_t *zchan) +{ + + if (zap_test_flag(zchan, ZAP_CHANNEL_CONFIGURED)) { + + while (zap_test_flag(zchan, ZAP_CHANNEL_INTHREAD)) { + zap_log(ZAP_LOG_INFO, "Waiting for thread to exit on channel %u:%u\n", zchan->span_id, zchan->chan_id); + zap_sleep(500); + } + + zap_mutex_lock(zchan->pre_buffer_mutex); + zap_buffer_destroy(&zchan->pre_buffer); + zap_mutex_unlock(zchan->pre_buffer_mutex); + + zap_buffer_destroy(&zchan->digit_buffer); + zap_buffer_destroy(&zchan->gen_dtmf_buffer); + zap_buffer_destroy(&zchan->dtmf_buffer); + zap_buffer_destroy(&zchan->fsk_buffer); + zchan->pre_buffer_size = 0; + + hashtable_destroy(zchan->variable_hash); + + zap_safe_free(zchan->dtmf_hangup_buf); + + if (zchan->tone_session.buffer) { + teletone_destroy_session(&zchan->tone_session); + memset(&zchan->tone_session, 0, sizeof(zchan->tone_session)); + } + + + if (zchan->span->zio->channel_destroy) { + zap_log(ZAP_LOG_INFO, "Closing channel %s:%u:%u fd:%d\n", zchan->span->type, zchan->span_id, zchan->chan_id, zchan->sockfd); + if (zchan->span->zio->channel_destroy(zchan) == ZAP_SUCCESS) { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_CONFIGURED); + } else { + zap_log(ZAP_LOG_ERROR, "Error Closing channel %u:%u fd:%d\n", zchan->span_id, zchan->chan_id, zchan->sockfd); + } + } + + zap_mutex_destroy(&zchan->mutex); + zap_mutex_destroy(&zchan->pre_buffer_mutex); + } + + return ZAP_SUCCESS; +} + + +OZ_DECLARE(zap_status_t) zap_channel_get_alarms(zap_channel_t *zchan) +{ + zap_status_t status = ZAP_FAIL; + + if (zap_test_flag(zchan, ZAP_CHANNEL_CONFIGURED)) { + if (zchan->span->zio->get_alarms) { + if ((status = zchan->span->zio->get_alarms(zchan)) == ZAP_SUCCESS) { + *zchan->last_error = '\0'; + if (zap_test_alarm_flag(zchan, ZAP_ALARM_RED)) { + snprintf(zchan->last_error + strlen(zchan->last_error), sizeof(zchan->last_error) - strlen(zchan->last_error), "RED/"); + } + if (zap_test_alarm_flag(zchan, ZAP_ALARM_YELLOW)) { + snprintf(zchan->last_error + strlen(zchan->last_error), sizeof(zchan->last_error) - strlen(zchan->last_error), "YELLOW/"); + } + if (zap_test_alarm_flag(zchan, ZAP_ALARM_BLUE)) { + snprintf(zchan->last_error + strlen(zchan->last_error), sizeof(zchan->last_error) - strlen(zchan->last_error), "BLUE/"); + } + if (zap_test_alarm_flag(zchan, ZAP_ALARM_LOOPBACK)) { + snprintf(zchan->last_error + strlen(zchan->last_error), sizeof(zchan->last_error) - strlen(zchan->last_error), "LOOP/"); + } + if (zap_test_alarm_flag(zchan, ZAP_ALARM_RECOVER)) { + snprintf(zchan->last_error + strlen(zchan->last_error), sizeof(zchan->last_error) - strlen(zchan->last_error), "RECOVER/"); + } + *(zchan->last_error + strlen(zchan->last_error) - 1) = '\0'; + + } + } else { + status = ZAP_NOTIMPL; + } + } + + return status; +} + +static void zap_span_add(zap_span_t *span) +{ + zap_span_t *sp; + zap_mutex_lock(globals.span_mutex); + for (sp = globals.spans; sp && sp->next; sp = sp->next); + if (sp) { + sp->next = span; + } else { + globals.spans = span; + } + hashtable_insert(globals.span_hash, (void *)span->name, span, HASHTABLE_FLAG_NONE); + zap_mutex_unlock(globals.span_mutex); +} + +#if 0 +static void zap_span_del(zap_span_t *span) +{ + zap_span_t *last = NULL, *sp; + + zap_mutex_lock(globals.span_mutex); + for (sp = globals.spans; sp; sp = sp->next) { + + if (sp == span) { + if (last) { + last->next = sp->next; + } else { + globals.spans = sp->next; + } + hashtable_remove(globals.span_hash, (void *)sp->name); + break; + } + + last = sp; + } + zap_mutex_unlock(globals.span_mutex); +} +#endif + +OZ_DECLARE(zap_status_t) zap_span_stop(zap_span_t *span) +{ + if (span->stop) { + span->stop(span); + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +OZ_DECLARE(zap_status_t) zap_span_create(zap_io_interface_t *zio, zap_span_t **span, const char *name) +{ + zap_span_t *new_span = NULL; + zap_status_t status = ZAP_FAIL; + + assert(zio != NULL); + + zap_mutex_lock(globals.mutex); + + if (globals.span_index < ZAP_MAX_SPANS_INTERFACE) { + new_span = malloc(sizeof(*new_span)); + assert(new_span); + memset(new_span, 0, sizeof(*new_span)); + status = zap_mutex_create(&new_span->mutex); + assert(status == ZAP_SUCCESS); + + zap_set_flag(new_span, ZAP_SPAN_CONFIGURED); + new_span->span_id = ++globals.span_index; + new_span->zio = zio; + zap_copy_string(new_span->tone_map[ZAP_TONEMAP_DIAL], "%(1000,0,350,440)", ZAP_TONEMAP_LEN); + zap_copy_string(new_span->tone_map[ZAP_TONEMAP_RING], "%(2000,4000,440,480)", ZAP_TONEMAP_LEN); + zap_copy_string(new_span->tone_map[ZAP_TONEMAP_BUSY], "%(500,500,480,620)", ZAP_TONEMAP_LEN); + zap_copy_string(new_span->tone_map[ZAP_TONEMAP_ATTN], "%(100,100,1400,2060,2450,2600)", ZAP_TONEMAP_LEN); + new_span->trunk_type = ZAP_TRUNK_NONE; + new_span->data_type = ZAP_TYPE_SPAN; + + zap_mutex_lock(globals.span_mutex); + if (!zap_strlen_zero(name) && hashtable_search(globals.span_hash, (void *)name)) { + zap_log(ZAP_LOG_WARNING, "name %s is already used, substituting 'span%d' as the name\n", name, new_span->span_id); + name = NULL; + } + zap_mutex_unlock(globals.span_mutex); + + if (!name) { + char buf[128] = ""; + snprintf(buf, sizeof(buf), "span%d", new_span->span_id); + name = buf; + } + new_span->name = strdup(name); + zap_span_add(new_span); + *span = new_span; + status = ZAP_SUCCESS; + } + zap_mutex_unlock(globals.mutex); + return status; +} + +OZ_DECLARE(zap_status_t) zap_span_close_all(void) +{ + zap_span_t *span; + uint32_t i = 0, j; + + zap_mutex_lock(globals.span_mutex); + for (span = globals.spans; span; span = span->next) { + if (zap_test_flag(span, ZAP_SPAN_CONFIGURED)) { + for(j = 1; j <= span->chan_count && span->channels[j]; j++) { + zap_channel_destroy(span->channels[j]); + i++; + } + } + } + zap_mutex_unlock(globals.span_mutex); + + return i ? ZAP_SUCCESS : ZAP_FAIL; +} + +OZ_DECLARE(zap_status_t) zap_span_load_tones(zap_span_t *span, const char *mapname) +{ + zap_config_t cfg; + char *var, *val; + int x = 0; + + if (!zap_config_open_file(&cfg, "tones.conf")) { + snprintf(span->last_error, sizeof(span->last_error), "error loading tones."); + return ZAP_FAIL; + } + + while (zap_config_next_pair(&cfg, &var, &val)) { + int detect = 0; + + if (!strcasecmp(cfg.category, mapname) && var && val) { + uint32_t index; + char *name = NULL; + + if (!strncasecmp(var, "detect-", 7)) { + name = var + 7; + detect = 1; + } else if (!strncasecmp(var, "generate-", 9)) { + name = var + 9; + } else { + zap_log(ZAP_LOG_WARNING, "Unknown tone name %s\n", var); + continue; + } + + index = zap_str2zap_tonemap(name); + + if (index >= ZAP_TONEMAP_INVALID || index == ZAP_TONEMAP_NONE) { + zap_log(ZAP_LOG_WARNING, "Unknown tone name %s\n", name); + } else { + if (detect) { + char *p = val, *next; + int i = 0; + do { + teletone_process_t this; + next = strchr(p, ','); + this = (teletone_process_t)atof(p); + span->tone_detect_map[index].freqs[i++] = this; + if (next) { + p = next + 1; + } + } while (next); + zap_log(ZAP_LOG_DEBUG, "added tone detect [%s] = [%s]\n", name, val); + } else { + zap_log(ZAP_LOG_DEBUG, "added tone generation [%s] = [%s]\n", name, val); + zap_copy_string(span->tone_map[index], val, sizeof(span->tone_map[index])); + } + x++; + } + } + } + + zap_config_close_file(&cfg); + + if (!x) { + snprintf(span->last_error, sizeof(span->last_error), "error loading tones."); + return ZAP_FAIL; + } + + return ZAP_SUCCESS; + +} + +#define ZAP_SLINEAR_MAX_VALUE 32767 +#define ZAP_SLINEAR_MIN_VALUE -32767 +static void reset_gain_table(unsigned char *gain_table, float new_gain, zap_codec_t codec_gain) +{ + /* sample value */ + uint8_t sv = 0; + /* linear gain factor */ + float lingain = 0; + /* linear value for each table sample */ + float linvalue = 0; + /* amplified (or attenuated in case of negative amplification) sample value */ + int ampvalue = 0; + + /* gain tables are only for alaw and ulaw */ + if (codec_gain != ZAP_CODEC_ALAW && codec_gain != ZAP_CODEC_ULAW) { + zap_log(ZAP_LOG_WARNING, "Not resetting gain table because codec is not ALAW or ULAW but %d\n", codec_gain); + return; + } + + if (!new_gain) { + /* for a 0.0db gain table, each alaw/ulaw sample value is left untouched (0 ==0, 1 == 1, 2 == 2 etc)*/ + sv = 0; + while (1) { + gain_table[sv] = (unsigned char)sv; + if (sv == (ZAP_GAINS_TABLE_SIZE - 1)) { + break; + } + sv++; + } + return; + } + + /* use the 20log rule to increase the gain: http://en.wikipedia.org/wiki/Gain, http:/en.wikipedia.org/wiki/20_log_rule#Definitions */ + lingain = (float)pow(10.0f, new_gain/20.0f); + sv = 0; + while (1) { + /* get the linear value for this alaw/ulaw sample value */ + linvalue = codec_gain == ZAP_CODEC_ALAW ? (float)alaw_to_linear(sv) : (float)ulaw_to_linear(sv); + + /* multiply the linear value and the previously calculated linear gain */ + ampvalue = (int)(linvalue * lingain); + + /* chop it if goes beyond the limits */ + if (ampvalue > ZAP_SLINEAR_MAX_VALUE) { + ampvalue = ZAP_SLINEAR_MAX_VALUE; + } + + if (ampvalue < ZAP_SLINEAR_MIN_VALUE) { + ampvalue = ZAP_SLINEAR_MIN_VALUE; + } + gain_table[sv] = codec_gain == ZAP_CODEC_ALAW ? linear_to_alaw(ampvalue) : linear_to_ulaw(ampvalue); + if (sv == (ZAP_GAINS_TABLE_SIZE-1)) { + break; + } + sv++; + } +} + +OZ_DECLARE(zap_status_t) zap_span_add_channel(zap_span_t *span, zap_socket_t sockfd, zap_chan_type_t type, zap_channel_t **chan) +{ + unsigned i = 0; + if (span->chan_count < ZAP_MAX_CHANNELS_SPAN) { + zap_channel_t *new_chan = span->channels[++span->chan_count]; + + if (!new_chan) { + if (!(new_chan = malloc(sizeof(*new_chan)))) { + return ZAP_FAIL; + } + span->channels[span->chan_count] = new_chan; + memset(new_chan, 0, sizeof(*new_chan)); + } + + new_chan->type = type; + new_chan->sockfd = sockfd; + new_chan->zio = span->zio; + new_chan->span_id = span->span_id; + new_chan->chan_id = span->chan_count; + new_chan->span = span; + new_chan->fds[0] = -1; + new_chan->fds[1] = -1; + new_chan->data_type = ZAP_TYPE_CHANNEL; + if (!new_chan->dtmf_on) { + new_chan->dtmf_on = ZAP_DEFAULT_DTMF_ON; + } + + if (!new_chan->dtmf_off) { + new_chan->dtmf_off = ZAP_DEFAULT_DTMF_OFF; + } + + zap_mutex_create(&new_chan->mutex); + zap_mutex_create(&new_chan->pre_buffer_mutex); + + zap_buffer_create(&new_chan->digit_buffer, 128, 128, 0); + zap_buffer_create(&new_chan->gen_dtmf_buffer, 128, 128, 0); + new_chan->variable_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys); + + new_chan->dtmf_hangup_buf = calloc (span->dtmf_hangup_len + 1, sizeof (char)); + + /* set 0.0db gain table */ + i = 0; + while (1) { + new_chan->txgain_table[i] = (unsigned char)i; + new_chan->rxgain_table[i] = (unsigned char)i; + if (i == (sizeof(new_chan->txgain_table)-1)) { + break; + } + i++; + } + + zap_set_flag(new_chan, ZAP_CHANNEL_CONFIGURED | ZAP_CHANNEL_READY); + *chan = new_chan; + return ZAP_SUCCESS; + } + + return ZAP_FAIL; +} + +OZ_DECLARE(zap_status_t) zap_span_find_by_name(const char *name, zap_span_t **span) +{ + zap_status_t status = ZAP_FAIL; + + zap_mutex_lock(globals.span_mutex); + if (!zap_strlen_zero(name)) { + if ((*span = hashtable_search(globals.span_hash, (void *)name))) { + status = ZAP_SUCCESS; + } else { + int span_id = atoi(name); + + zap_span_find(span_id, span); + if (*span) { + status = ZAP_SUCCESS; + } + } + } + zap_mutex_unlock(globals.span_mutex); + + return status; +} + +OZ_DECLARE(zap_status_t) zap_span_find(uint32_t id, zap_span_t **span) +{ + zap_span_t *fspan = NULL, *sp; + + if (id > ZAP_MAX_SPANS_INTERFACE) { + return ZAP_FAIL; + } + + zap_mutex_lock(globals.span_mutex); + for (sp = globals.spans; sp; sp = sp->next) { + if (sp->span_id == id) { + fspan = sp; + break; + } + } + zap_mutex_unlock(globals.span_mutex); + + if (!fspan || !zap_test_flag(fspan, ZAP_SPAN_CONFIGURED)) { + return ZAP_FAIL; + } + + *span = fspan; + + return ZAP_SUCCESS; + +} + +OZ_DECLARE(zap_status_t) zap_span_set_event_callback(zap_span_t *span, zio_event_cb_t event_callback) +{ + zap_mutex_lock(span->mutex); + span->event_callback = event_callback; + zap_mutex_unlock(span->mutex); + return ZAP_SUCCESS; +} + + +OZ_DECLARE(zap_status_t) zap_span_poll_event(zap_span_t *span, uint32_t ms) +{ + assert(span->zio != NULL); + + if (span->zio->poll_event) { + return span->zio->poll_event(span, ms); + } else { + zap_log(ZAP_LOG_ERROR, "poll_event method not implemented in module %s!", span->zio->name); + } + + return ZAP_NOTIMPL; +} + +OZ_DECLARE(zap_status_t) zap_span_next_event(zap_span_t *span, zap_event_t **event) +{ + assert(span->zio != NULL); + + if (span->zio->next_event) { + return span->zio->next_event(span, event); + } else { + zap_log(ZAP_LOG_ERROR, "next_event method not implemented in module %s!", span->zio->name); + } + + return ZAP_NOTIMPL; +} + +static zap_status_t zchan_fsk_write_sample(int16_t *buf, zap_size_t buflen, void *user_data) +{ + zap_channel_t *zchan = (zap_channel_t *) user_data; + zap_buffer_write(zchan->fsk_buffer, buf, buflen * 2); + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_send_fsk_data(zap_channel_t *zchan, zap_fsk_data_state_t *fsk_data, float db_level) +{ + struct zap_fsk_modulator fsk_trans; + + if (!zchan->fsk_buffer) { + zap_buffer_create(&zchan->fsk_buffer, 128, 128, 0); + } else { + zap_buffer_zero(zchan->fsk_buffer); + } + + if (zchan->token_count > 1) { + zap_fsk_modulator_init(&fsk_trans, FSK_BELL202, zchan->rate, fsk_data, db_level, 80, 5, 0, zchan_fsk_write_sample, zchan); + zap_fsk_modulator_send_all((&fsk_trans)); + } else { + zap_fsk_modulator_init(&fsk_trans, FSK_BELL202, zchan->rate, fsk_data, db_level, 180, 5, 300, zchan_fsk_write_sample, zchan); + zap_fsk_modulator_send_all((&fsk_trans)); + zchan->buffer_delay = 3500 / zchan->effective_interval; + } + + return ZAP_SUCCESS; +} + + +OZ_DECLARE(zap_status_t) zap_channel_set_event_callback(zap_channel_t *zchan, zio_event_cb_t event_callback) +{ + zap_mutex_lock(zchan->mutex); + zchan->event_callback = event_callback; + zap_mutex_unlock(zchan->mutex); + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_clear_token(zap_channel_t *zchan, const char *token) +{ + zap_status_t status = ZAP_FAIL; + + zap_mutex_lock(zchan->mutex); + if (token == NULL) { + memset(zchan->tokens, 0, sizeof(zchan->tokens)); + zchan->token_count = 0; + } else if (*token != '\0') { + char tokens[ZAP_MAX_TOKENS][ZAP_TOKEN_STRLEN]; + int32_t i, count = zchan->token_count; + memcpy(tokens, zchan->tokens, sizeof(tokens)); + memset(zchan->tokens, 0, sizeof(zchan->tokens)); + zchan->token_count = 0; + + for (i = 0; i < count; i++) { + if (strcmp(tokens[i], token)) { + zap_copy_string(zchan->tokens[zchan->token_count], tokens[i], sizeof(zchan->tokens[zchan->token_count])); + zchan->token_count++; + } + } + + status = ZAP_SUCCESS; + } + zap_mutex_unlock(zchan->mutex); + + return status; +} + +OZ_DECLARE(void) zap_channel_rotate_tokens(zap_channel_t *zchan) +{ + if (zchan->token_count) { + memmove(zchan->tokens[1], zchan->tokens[0], zchan->token_count * ZAP_TOKEN_STRLEN); + zap_copy_string(zchan->tokens[0], zchan->tokens[zchan->token_count], ZAP_TOKEN_STRLEN); + *zchan->tokens[zchan->token_count] = '\0'; + } +} + +OZ_DECLARE(void) zap_channel_replace_token(zap_channel_t *zchan, const char *old_token, const char *new_token) +{ + unsigned int i; + + if (zchan->token_count) { + for(i = 0; i < zchan->token_count; i++) { + if (!strcmp(zchan->tokens[i], old_token)) { + zap_copy_string(zchan->tokens[i], new_token, ZAP_TOKEN_STRLEN); + break; + } + } + } +} + +OZ_DECLARE(zap_status_t) zap_channel_add_token(zap_channel_t *zchan, char *token, int end) +{ + zap_status_t status = ZAP_FAIL; + + zap_mutex_lock(zchan->mutex); + if (zchan->token_count < ZAP_MAX_TOKENS) { + if (end) { + zap_copy_string(zchan->tokens[zchan->token_count++], token, ZAP_TOKEN_STRLEN); + } else { + memmove(zchan->tokens[1], zchan->tokens[0], zchan->token_count * ZAP_TOKEN_STRLEN); + zap_copy_string(zchan->tokens[0], token, ZAP_TOKEN_STRLEN); + zchan->token_count++; + } + status = ZAP_SUCCESS; + } + zap_mutex_unlock(zchan->mutex); + + return status; +} + + +OZ_DECLARE(zap_status_t) zap_channel_complete_state(zap_channel_t *zchan) +{ + zap_channel_state_t state = zchan->state; + + if (state == ZAP_CHANNEL_STATE_PROGRESS) { + zap_set_flag(zchan, ZAP_CHANNEL_PROGRESS); + } else if (state == ZAP_CHANNEL_STATE_UP) { + zap_set_flag(zchan, ZAP_CHANNEL_PROGRESS); + zap_set_flag(zchan, ZAP_CHANNEL_MEDIA); + zap_set_flag(zchan, ZAP_CHANNEL_ANSWERED); + } else if (state == ZAP_CHANNEL_STATE_PROGRESS_MEDIA) { + zap_set_flag(zchan, ZAP_CHANNEL_PROGRESS); + zap_set_flag(zchan, ZAP_CHANNEL_MEDIA); + } + + return ZAP_SUCCESS; +} + +static int zap_parse_state_map(zap_channel_t *zchan, zap_channel_state_t state, zap_state_map_t *state_map) +{ + int x = 0, ok = 0; + zap_state_direction_t direction = zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND; + + for(x = 0; x < ZAP_MAP_NODE_SIZE; x++) { + int i = 0, proceed = 0; + if (!state_map->nodes[x].type) { + break; + } + + if (state_map->nodes[x].direction != direction) { + continue; + } + + if (state_map->nodes[x].check_states[0] == ZAP_ANY_STATE) { + proceed = 1; + } else { + for(i = 0; i < ZAP_MAP_MAX; i++) { + if (state_map->nodes[x].check_states[i] == zchan->state) { + proceed = 1; + break; + } + } + } + + if (!proceed) { + continue; + } + + for(i = 0; i < ZAP_MAP_MAX; i++) { + ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE); + if (state_map->nodes[x].states[i] == ZAP_END) { + break; + } + if (state_map->nodes[x].states[i] == state) { + ok = !ok; + goto end; + } + } + } + end: + + return ok; +} + +OZ_DECLARE(zap_status_t) zap_channel_set_state(zap_channel_t *zchan, zap_channel_state_t state, int lock) +{ + int ok = 1; + + if (!zap_test_flag(zchan, ZAP_CHANNEL_READY)) { + return ZAP_FAIL; + } + + if (zap_test_flag(zchan->span, ZAP_SPAN_SUSPENDED)) { + if (state != ZAP_CHANNEL_STATE_RESTART && state != ZAP_CHANNEL_STATE_DOWN) { + return ZAP_FAIL; + } + } + + if (lock) { + zap_mutex_lock(zchan->mutex); + } + + if (zchan->span->state_map) { + ok = zap_parse_state_map(zchan, state, zchan->span->state_map); + goto end; + } + + switch(zchan->state) { + case ZAP_CHANNEL_STATE_HANGUP: + case ZAP_CHANNEL_STATE_TERMINATING: + { + ok = 0; + switch(state) { + case ZAP_CHANNEL_STATE_DOWN: + case ZAP_CHANNEL_STATE_BUSY: + case ZAP_CHANNEL_STATE_RESTART: + ok = 1; + break; + default: + break; + } + } + break; + case ZAP_CHANNEL_STATE_UP: + { + ok = 1; + switch(state) { + case ZAP_CHANNEL_STATE_PROGRESS: + case ZAP_CHANNEL_STATE_PROGRESS_MEDIA: + case ZAP_CHANNEL_STATE_RING: + ok = 0; + break; + default: + break; + } + } + break; + case ZAP_CHANNEL_STATE_DOWN: + { + ok = 0; + + switch(state) { + case ZAP_CHANNEL_STATE_DIALTONE: + case ZAP_CHANNEL_STATE_COLLECT: + case ZAP_CHANNEL_STATE_DIALING: + case ZAP_CHANNEL_STATE_RING: + case ZAP_CHANNEL_STATE_PROGRESS_MEDIA: + case ZAP_CHANNEL_STATE_PROGRESS: + case ZAP_CHANNEL_STATE_GET_CALLERID: + case ZAP_CHANNEL_STATE_GENRING: + ok = 1; + break; + default: + break; + } + } + break; + case ZAP_CHANNEL_STATE_BUSY: + { + switch(state) { + case ZAP_CHANNEL_STATE_UP: + ok = 0; + break; + default: + break; + } + } + break; + case ZAP_CHANNEL_STATE_RING: + { + switch(state) { + case ZAP_CHANNEL_STATE_UP: + ok = 1; + break; + default: + break; + } + } + break; + default: + break; + } + + end: + + if (state == zchan->state) { + ok = 0; + } + + + if (ok) { + zap_set_flag(zchan, ZAP_CHANNEL_STATE_CHANGE); + zap_set_flag_locked(zchan->span, ZAP_SPAN_STATE_CHANGE); + zchan->last_state = zchan->state; + zchan->state = state; + } + + if (lock) { + zap_mutex_unlock(zchan->mutex); + } + + return ok ? ZAP_SUCCESS : ZAP_FAIL; +} + +OZ_DECLARE(zap_status_t) zap_span_channel_use_count(zap_span_t *span, uint32_t *count) +{ + uint32_t j; + + *count = 0; + + if (!span || !zap_test_flag(span, ZAP_SPAN_CONFIGURED)) { + return ZAP_FAIL; + } + + for(j = 1; j <= span->chan_count && span->channels[j]; j++) { + if (span->channels[j]) { + if (zap_test_flag(span->channels[j], ZAP_CHANNEL_INUSE)) { + (*count)++; + } + } + } + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_open_any(uint32_t span_id, zap_direction_t direction, zap_caller_data_t *caller_data, zap_channel_t **zchan) +{ + zap_status_t status = ZAP_FAIL; + zap_channel_t *check; + uint32_t i, j, count; + zap_span_t *span = NULL; + uint32_t span_max; + + if (span_id) { + zap_span_find(span_id, &span); + + if (!span || !zap_test_flag(span, ZAP_SPAN_CONFIGURED)) { + zap_log(ZAP_LOG_CRIT, "SPAN NOT DEFINED!\n"); + *zchan = NULL; + return ZAP_FAIL; + } + + zap_span_channel_use_count(span, &count); + + if (count >= span->chan_count) { + zap_log(ZAP_LOG_CRIT, "All circuits are busy.\n"); + *zchan = NULL; + return ZAP_FAIL; + } + + if (span->channel_request && !span->suggest_chan_id) { + zap_set_caller_data(span, caller_data); + return span->channel_request(span, 0, direction, caller_data, zchan); + } + + span_max = span_id; + j = span_id; + } else { + zap_log(ZAP_LOG_CRIT, "No span supplied\n"); + *zchan = NULL; + return ZAP_FAIL; + } + + zap_mutex_lock(span->mutex); + + if (direction == ZAP_TOP_DOWN) { + i = 1; + } else { + i = span->chan_count; + } + + for(;;) { + + if (direction == ZAP_TOP_DOWN) { + if (i > span->chan_count) { + break; + } + } else { + if (i == 0) { + break; + } + } + + if (!(check = span->channels[i])) { + status = ZAP_FAIL; + break; + } + + if (zap_test_flag(check, ZAP_CHANNEL_READY) && + !zap_test_flag(check, ZAP_CHANNEL_INUSE) && + !zap_test_flag(check, ZAP_CHANNEL_SUSPENDED) && + check->state == ZAP_CHANNEL_STATE_DOWN && + check->type != ZAP_CHAN_TYPE_DQ921 && + check->type != ZAP_CHAN_TYPE_DQ931 + + ) { + + if (span && span->channel_request) { + zap_set_caller_data(span, caller_data); + status = span->channel_request(span, i, direction, caller_data, zchan); + break; + } + + status = check->zio->open(check); + + if (status == ZAP_SUCCESS) { + zap_set_flag(check, ZAP_CHANNEL_INUSE); + zap_channel_open_chan(check); + *zchan = check; + break; + } + } + + if (direction == ZAP_TOP_DOWN) { + i++; + } else { + i--; + } + } + + zap_mutex_unlock(span->mutex); + + return status; +} + +static zap_status_t zap_channel_reset(zap_channel_t *zchan) +{ + zap_clear_flag(zchan, ZAP_CHANNEL_OPEN); + zchan->event_callback = NULL; + zap_clear_flag(zchan, ZAP_CHANNEL_DTMF_DETECT); + zap_clear_flag(zchan, ZAP_CHANNEL_SUPRESS_DTMF); + zap_channel_done(zchan); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_HOLD); + + memset(zchan->tokens, 0, sizeof(zchan->tokens)); + zchan->token_count = 0; + + if (zchan->dtmf_buffer) { + zap_buffer_zero(zchan->dtmf_buffer); + } + + if (zchan->gen_dtmf_buffer) { + zap_buffer_zero(zchan->gen_dtmf_buffer); + } + + if (zchan->digit_buffer) { + zap_buffer_zero(zchan->digit_buffer); + } + + if (!zchan->dtmf_on) { + zchan->dtmf_on = ZAP_DEFAULT_DTMF_ON; + } + + if (!zchan->dtmf_off) { + zchan->dtmf_off = ZAP_DEFAULT_DTMF_OFF; + } + + memset(zchan->dtmf_hangup_buf, '\0', zchan->span->dtmf_hangup_len); + + if (zap_test_flag(zchan, ZAP_CHANNEL_TRANSCODE)) { + zchan->effective_codec = zchan->native_codec; + zchan->packet_len = zchan->native_interval * (zchan->effective_codec == ZAP_CODEC_SLIN ? 16 : 8); + zap_clear_flag(zchan, ZAP_CHANNEL_TRANSCODE); + } + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_init(zap_channel_t *zchan) +{ + + if (zchan->init_state != ZAP_CHANNEL_STATE_DOWN) { + zap_set_state_locked(zchan, zchan->init_state); + zchan->init_state = ZAP_CHANNEL_STATE_DOWN; + } + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_open_chan(zap_channel_t *zchan) +{ + zap_status_t status = ZAP_FAIL; + + assert(zchan != NULL); + + if (zap_test_flag(zchan, ZAP_CHANNEL_SUSPENDED)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", "Channel is suspended"); + return ZAP_FAIL; + } + if (globals.cpu_monitor.alarm && + globals.cpu_monitor.alarm_action_flags & ZAP_CPU_ALARM_ACTION_REJECT) { + + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", "CPU usage alarm is on - refusing to open channel\n"); + zap_log(ZAP_LOG_WARNING, "CPU usage alarm is on - refusing to open channel\n"); + zchan->caller_data.hangup_cause = ZAP_CAUSE_SWITCH_CONGESTION; + return ZAP_FAIL; + } + + if (!zap_test_flag(zchan, ZAP_CHANNEL_READY) || (status = zap_mutex_trylock(zchan->mutex)) != ZAP_SUCCESS) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "Channel is not ready or is in use %d %d", zap_test_flag(zchan, ZAP_CHANNEL_READY), status); + return status; + } + + status = ZAP_FAIL; + + if (zap_test_flag(zchan, ZAP_CHANNEL_READY)) { + status = zchan->span->zio->open(zchan); + if (status == ZAP_SUCCESS) { + zap_set_flag(zchan, ZAP_CHANNEL_OPEN | ZAP_CHANNEL_INUSE); + } + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", "Channel is not ready"); + } + + zap_mutex_unlock(zchan->mutex); + return status; +} + +OZ_DECLARE(zap_status_t) zap_channel_open(uint32_t span_id, uint32_t chan_id, zap_channel_t **zchan) +{ + zap_channel_t *check; + zap_status_t status = ZAP_FAIL; + zap_span_t *span = NULL; + + zap_mutex_unlock(globals.mutex); + zap_span_find(span_id, &span); + + if (!span || !zap_test_flag(span, ZAP_SPAN_CONFIGURED) || chan_id >= ZAP_MAX_CHANNELS_SPAN) { + zap_log(ZAP_LOG_CRIT, "SPAN NOT DEFINED!\n"); + *zchan = NULL; + goto done; + } + + if (span->channel_request) { + zap_log(ZAP_LOG_ERROR, "Individual channel selection not implemented on this span.\n"); + *zchan = NULL; + goto done; + } + + if (!(check = span->channels[chan_id])) { + zap_log(ZAP_LOG_ERROR, "Invalid Channel %d\n", chan_id); + *zchan = NULL; + goto done; + } + + if (zap_test_flag(check, ZAP_CHANNEL_SUSPENDED) || + !zap_test_flag(check, ZAP_CHANNEL_READY) || (status = zap_mutex_trylock(check->mutex)) != ZAP_SUCCESS) { + *zchan = NULL; + goto done; + } + + status = ZAP_FAIL; + + if (zap_test_flag(check, ZAP_CHANNEL_READY) && (!zap_test_flag(check, ZAP_CHANNEL_INUSE) || + (check->type == ZAP_CHAN_TYPE_FXS && check->token_count == 1))) { + if (!zap_test_flag(check, ZAP_CHANNEL_OPEN)) { + status = check->zio->open(check); + if (status == ZAP_SUCCESS) { + zap_set_flag(check, ZAP_CHANNEL_OPEN); + } + } else { + status = ZAP_SUCCESS; + } + zap_set_flag(check, ZAP_CHANNEL_INUSE); + *zchan = check; + } + zap_mutex_unlock(check->mutex); + + done: + zap_mutex_unlock(globals.mutex); + + return status; +} + +OZ_DECLARE(zap_status_t) zap_channel_outgoing_call(zap_channel_t *zchan) +{ + zap_status_t status; + + assert(zchan != NULL); + + if (zchan->span->outgoing_call) { + if ((status = zchan->span->outgoing_call(zchan)) == ZAP_SUCCESS) { + zap_set_flag(zchan, ZAP_CHANNEL_OUTBOUND); + } + return status; + } else { + zap_log(ZAP_LOG_ERROR, "outgoing_call method not implemented!\n"); + } + + return ZAP_FAIL; +} + +OZ_DECLARE(zap_status_t) zap_channel_done(zap_channel_t *zchan) +{ + assert(zchan != NULL); + + memset(&zchan->caller_data, 0, sizeof(zchan->caller_data)); + + zap_clear_flag_locked(zchan, ZAP_CHANNEL_INUSE); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_OUTBOUND); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_WINK); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_FLASH); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_HOLD); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_RINGING); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_PROGRESS_DETECT); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_CALLERID_DETECT); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_3WAY); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_PROGRESS); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_MEDIA); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_ANSWERED); + zap_mutex_lock(zchan->pre_buffer_mutex); + zap_buffer_destroy(&zchan->pre_buffer); + zchan->pre_buffer_size = 0; + zap_mutex_unlock(zchan->pre_buffer_mutex); + + zap_channel_flush_dtmf(zchan); + + zchan->init_state = ZAP_CHANNEL_STATE_DOWN; + zchan->state = ZAP_CHANNEL_STATE_DOWN; + zap_log(ZAP_LOG_DEBUG, "channel done %u:%u\n", zchan->span_id, zchan->chan_id); + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_use(zap_channel_t *zchan) +{ + + assert(zchan != NULL); + + zap_set_flag_locked(zchan, ZAP_CHANNEL_INUSE); + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_close(zap_channel_t **zchan) +{ + zap_channel_t *check; + zap_status_t status = ZAP_FAIL; + + assert(zchan != NULL); + check = *zchan; + *zchan = NULL; + + if (!check) { + return ZAP_FAIL; + } + + if (zap_test_flag(check, ZAP_CHANNEL_CONFIGURED)) { + zap_mutex_lock(check->mutex); + if (zap_test_flag(check, ZAP_CHANNEL_OPEN)) { + status = check->zio->close(check); + if (status == ZAP_SUCCESS) { + zap_channel_reset(check); + *zchan = NULL; + } + } + check->ring_count = 0; + zap_mutex_unlock(check->mutex); + } + + return status; +} + + +static zap_status_t zchan_activate_dtmf_buffer(zap_channel_t *zchan) +{ + + if (!zchan->dtmf_buffer) { + if (zap_buffer_create(&zchan->dtmf_buffer, 1024, 3192, 0) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Failed to allocate DTMF Buffer!\n"); + snprintf(zchan->last_error, sizeof(zchan->last_error), "buffer error"); + return ZAP_FAIL; + } else { + zap_log(ZAP_LOG_DEBUG, "Created DTMF Buffer!\n"); + } + } + + + if (!zchan->tone_session.buffer) { + memset(&zchan->tone_session, 0, sizeof(zchan->tone_session)); + teletone_init_session(&zchan->tone_session, 0, NULL, NULL); + } + + zchan->tone_session.rate = zchan->rate; + zchan->tone_session.duration = zchan->dtmf_on * (zchan->tone_session.rate / 1000); + zchan->tone_session.wait = zchan->dtmf_off * (zchan->tone_session.rate / 1000); + zchan->tone_session.volume = -7; + + /* + zchan->tone_session.debug = 1; + zchan->tone_session.debug_stream = stdout; + */ + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_command(zap_channel_t *zchan, zap_command_t command, void *obj) +{ + zap_status_t status = ZAP_FAIL; + + assert(zchan != NULL); + assert(zchan->zio != NULL); + + zap_mutex_lock(zchan->mutex); + + switch(command) { + + case ZAP_COMMAND_ENABLE_CALLERID_DETECT: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_CALLERID)) { + if (zap_fsk_demod_init(&zchan->fsk, zchan->rate, zchan->fsk_buf, sizeof(zchan->fsk_buf)) != ZAP_SUCCESS) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno)); + GOTO_STATUS(done, ZAP_FAIL); + } + zap_set_flag_locked(zchan, ZAP_CHANNEL_CALLERID_DETECT); + } + } + break; + case ZAP_COMMAND_DISABLE_CALLERID_DETECT: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_CALLERID)) { + zap_fsk_demod_destroy(&zchan->fsk); + zap_clear_flag_locked(zchan, ZAP_CHANNEL_CALLERID_DETECT); + } + } + break; + case ZAP_COMMAND_TRACE_INPUT: + { + char *path = (char *) obj; + if (zchan->fds[0] > 0) { + close(zchan->fds[0]); + zchan->fds[0] = -1; + } + if ((zchan->fds[0] = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) > -1) { + zap_log(ZAP_LOG_DEBUG, "Tracing channel %u:%u to [%s]\n", zchan->span_id, zchan->chan_id, path); + GOTO_STATUS(done, ZAP_SUCCESS); + } + + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno)); + GOTO_STATUS(done, ZAP_FAIL); + } + break; + case ZAP_COMMAND_TRACE_OUTPUT: + { + char *path = (char *) obj; + if (zchan->fds[1] > 0) { + close(zchan->fds[1]); + zchan->fds[1] = -1; + } + if ((zchan->fds[1] = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) > -1) { + zap_log(ZAP_LOG_DEBUG, "Tracing channel %u:%u to [%s]\n", zchan->span_id, zchan->chan_id, path); + GOTO_STATUS(done, ZAP_SUCCESS); + } + + snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno)); + GOTO_STATUS(done, ZAP_FAIL); + } + break; + case ZAP_COMMAND_SET_INTERVAL: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_INTERVAL)) { + zchan->effective_interval = ZAP_COMMAND_OBJ_INT; + if (zchan->effective_interval == zchan->native_interval) { + zap_clear_flag(zchan, ZAP_CHANNEL_BUFFER); + } else { + zap_set_flag(zchan, ZAP_CHANNEL_BUFFER); + } + zchan->packet_len = zchan->native_interval * (zchan->effective_codec == ZAP_CODEC_SLIN ? 16 : 8); + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + case ZAP_COMMAND_GET_INTERVAL: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_INTERVAL)) { + ZAP_COMMAND_OBJ_INT = zchan->effective_interval; + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + case ZAP_COMMAND_SET_CODEC: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_CODECS)) { + zchan->effective_codec = ZAP_COMMAND_OBJ_INT; + + if (zchan->effective_codec == zchan->native_codec) { + zap_clear_flag(zchan, ZAP_CHANNEL_TRANSCODE); + } else { + zap_set_flag(zchan, ZAP_CHANNEL_TRANSCODE); + } + zchan->packet_len = zchan->native_interval * (zchan->effective_codec == ZAP_CODEC_SLIN ? 16 : 8); + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + + case ZAP_COMMAND_SET_NATIVE_CODEC: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_CODECS)) { + zchan->effective_codec = zchan->native_codec; + zap_clear_flag(zchan, ZAP_CHANNEL_TRANSCODE); + zchan->packet_len = zchan->native_interval * (zchan->effective_codec == ZAP_CODEC_SLIN ? 16 : 8); + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + + case ZAP_COMMAND_GET_CODEC: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_CODECS)) { + ZAP_COMMAND_OBJ_INT = zchan->effective_codec; + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + case ZAP_COMMAND_GET_NATIVE_CODEC: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_CODECS)) { + ZAP_COMMAND_OBJ_INT = zchan->native_codec; + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + case ZAP_COMMAND_ENABLE_PROGRESS_DETECT: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_PROGRESS)) { + /* if they don't have thier own, use ours */ + zap_channel_clear_detected_tones(zchan); + zap_channel_clear_needed_tones(zchan); + teletone_multi_tone_init(&zchan->span->tone_finder[ZAP_TONEMAP_DIAL], &zchan->span->tone_detect_map[ZAP_TONEMAP_DIAL]); + teletone_multi_tone_init(&zchan->span->tone_finder[ZAP_TONEMAP_RING], &zchan->span->tone_detect_map[ZAP_TONEMAP_RING]); + teletone_multi_tone_init(&zchan->span->tone_finder[ZAP_TONEMAP_BUSY], &zchan->span->tone_detect_map[ZAP_TONEMAP_BUSY]); + zap_set_flag(zchan, ZAP_CHANNEL_PROGRESS_DETECT); + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + case ZAP_COMMAND_DISABLE_PROGRESS_DETECT: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_PROGRESS)) { + zap_clear_flag_locked(zchan, ZAP_CHANNEL_PROGRESS_DETECT); + zap_channel_clear_detected_tones(zchan); + zap_channel_clear_needed_tones(zchan); + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + case ZAP_COMMAND_ENABLE_DTMF_DETECT: + { + /* if they don't have thier own, use ours */ + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_DETECT)) { + zap_tone_type_t tt = ZAP_COMMAND_OBJ_INT; + if (tt == ZAP_TONE_DTMF) { + teletone_dtmf_detect_init (&zchan->dtmf_detect, zchan->rate); + zap_set_flag_locked(zchan, ZAP_CHANNEL_DTMF_DETECT); + zap_set_flag_locked(zchan, ZAP_CHANNEL_SUPRESS_DTMF); + GOTO_STATUS(done, ZAP_SUCCESS); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "invalid command"); + GOTO_STATUS(done, ZAP_FAIL); + } + } + } + break; + case ZAP_COMMAND_DISABLE_DTMF_DETECT: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_DETECT)) { + zap_tone_type_t tt = ZAP_COMMAND_OBJ_INT; + if (tt == ZAP_TONE_DTMF) { + teletone_dtmf_detect_init (&zchan->dtmf_detect, zchan->rate); + zap_clear_flag(zchan, ZAP_CHANNEL_DTMF_DETECT); + zap_clear_flag(zchan, ZAP_CHANNEL_SUPRESS_DTMF); + GOTO_STATUS(done, ZAP_SUCCESS); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "invalid command"); + GOTO_STATUS(done, ZAP_FAIL); + } + } + } + + case ZAP_COMMAND_SET_PRE_BUFFER_SIZE: + { + int val = ZAP_COMMAND_OBJ_INT; + + if (val < 0) { + val = 0; + } + + zchan->pre_buffer_size = val * 8; + + zap_mutex_lock(zchan->pre_buffer_mutex); + if (!zchan->pre_buffer_size) { + zap_buffer_destroy(&zchan->pre_buffer); + } else if (!zchan->pre_buffer) { + zap_buffer_create(&zchan->pre_buffer, 1024, zchan->pre_buffer_size, 0); + } + zap_mutex_unlock(zchan->pre_buffer_mutex); + + GOTO_STATUS(done, ZAP_SUCCESS); + + } + break; + case ZAP_COMMAND_GET_DTMF_ON_PERIOD: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { + ZAP_COMMAND_OBJ_INT = zchan->dtmf_on; + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + case ZAP_COMMAND_GET_DTMF_OFF_PERIOD: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { + ZAP_COMMAND_OBJ_INT = zchan->dtmf_on; + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + case ZAP_COMMAND_SET_DTMF_ON_PERIOD: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { + int val = ZAP_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + zchan->dtmf_on = val; + GOTO_STATUS(done, ZAP_SUCCESS); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, ZAP_FAIL); + } + } + } + break; + case ZAP_COMMAND_SET_DTMF_OFF_PERIOD: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { + int val = ZAP_COMMAND_OBJ_INT; + if (val > 10 && val < 1000) { + zchan->dtmf_off = val; + GOTO_STATUS(done, ZAP_SUCCESS); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "invalid value %d range 10-1000", val); + GOTO_STATUS(done, ZAP_FAIL); + } + } + } + break; + case ZAP_COMMAND_SEND_DTMF: + { + if (!zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_GENERATE)) { + char *digits = ZAP_COMMAND_OBJ_CHAR_P; + + if ((status = zchan_activate_dtmf_buffer(zchan)) != ZAP_SUCCESS) { + GOTO_STATUS(done, status); + } + + zap_buffer_write(zchan->gen_dtmf_buffer, digits, strlen(digits)); + + GOTO_STATUS(done, ZAP_SUCCESS); + } + } + break; + + case ZAP_COMMAND_DISABLE_ECHOCANCEL: + { + zap_mutex_lock(zchan->pre_buffer_mutex); + zap_buffer_destroy(&zchan->pre_buffer); + zchan->pre_buffer_size = 0; + zap_mutex_unlock(zchan->pre_buffer_mutex); + } + break; + + case ZAP_COMMAND_SET_RX_GAIN: + { + zchan->rxgain = ZAP_COMMAND_OBJ_FLOAT; + reset_gain_table(zchan->rxgain_table, zchan->rxgain, zchan->native_codec); + if (zchan->rxgain == 0.0) { + zap_clear_flag(zchan, ZAP_CHANNEL_USE_RX_GAIN); + } else { + zap_set_flag(zchan, ZAP_CHANNEL_USE_RX_GAIN); + } + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + case ZAP_COMMAND_GET_RX_GAIN: + { + ZAP_COMMAND_OBJ_FLOAT = zchan->rxgain; + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + case ZAP_COMMAND_SET_TX_GAIN: + { + zchan->txgain = ZAP_COMMAND_OBJ_FLOAT; + reset_gain_table(zchan->txgain_table, zchan->txgain, zchan->native_codec); + if (zchan->txgain == 0.0) { + zap_clear_flag(zchan, ZAP_CHANNEL_USE_TX_GAIN); + } else { + zap_set_flag(zchan, ZAP_CHANNEL_USE_TX_GAIN); + } + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + case ZAP_COMMAND_GET_TX_GAIN: + { + ZAP_COMMAND_OBJ_FLOAT = zchan->txgain; + GOTO_STATUS(done, ZAP_SUCCESS); + } + break; + default: + break; + } + + if (!zchan->zio->command) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "method not implemented"); + zap_log(ZAP_LOG_ERROR, "no command function defined by the I/O openzap module!\n"); + GOTO_STATUS(done, ZAP_FAIL); + } + + status = zchan->zio->command(zchan, command, obj); + + if (status == ZAP_NOTIMPL) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "I/O command %d not implemented in backend", command); + zap_log(ZAP_LOG_ERROR, "I/O backend does not support command %d!\n", command); + } +done: + zap_mutex_unlock(zchan->mutex); + return status; + +} + +OZ_DECLARE(zap_status_t) zap_channel_wait(zap_channel_t *zchan, zap_wait_flag_t *flags, int32_t to) +{ + assert(zchan != NULL); + assert(zchan->zio != NULL); + + if (!zap_test_flag(zchan, ZAP_CHANNEL_OPEN)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "channel not open"); + return ZAP_FAIL; + } + + if (!zchan->zio->wait) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "method not implemented"); + return ZAP_FAIL; + } + + return zchan->zio->wait(zchan, flags, to); + +} + +/*******************************/ +ZIO_CODEC_FUNCTION(zio_slin2ulaw) +{ + int16_t sln_buf[512] = {0}, *sln = sln_buf; + uint8_t *lp = data; + uint32_t i; + zap_size_t len = *datalen; + + if (max > len) { + max = len; + } + + memcpy(sln, data, max); + + for(i = 0; i < max; i++) { + *lp++ = linear_to_ulaw(*sln++); + } + + *datalen = max / 2; + + return ZAP_SUCCESS; + +} + + +ZIO_CODEC_FUNCTION(zio_ulaw2slin) +{ + int16_t *sln = data; + uint8_t law[1024] = {0}, *lp = law; + uint32_t i; + zap_size_t len = *datalen; + + if (max > len) { + max = len; + } + + memcpy(law, data, max); + + for(i = 0; i < max; i++) { + *sln++ = ulaw_to_linear(*lp++); + } + + *datalen = max * 2; + + return ZAP_SUCCESS; +} + +ZIO_CODEC_FUNCTION(zio_slin2alaw) +{ + int16_t sln_buf[512] = {0}, *sln = sln_buf; + uint8_t *lp = data; + uint32_t i; + zap_size_t len = *datalen; + + if (max > len) { + max = len; + } + + memcpy(sln, data, max); + + for(i = 0; i < max; i++) { + *lp++ = linear_to_alaw(*sln++); + } + + *datalen = max / 2; + + return ZAP_SUCCESS; + +} + + +ZIO_CODEC_FUNCTION(zio_alaw2slin) +{ + int16_t *sln = data; + uint8_t law[1024] = {0}, *lp = law; + uint32_t i; + zap_size_t len = *datalen; + + if (max > len) { + max = len; + } + + memcpy(law, data, max); + + for(i = 0; i < max; i++) { + *sln++ = alaw_to_linear(*lp++); + } + + *datalen = max * 2; + + return ZAP_SUCCESS; +} + +ZIO_CODEC_FUNCTION(zio_ulaw2alaw) +{ + zap_size_t len = *datalen; + uint32_t i; + uint8_t *lp = data; + + if (max > len) { + max = len; + } + + for(i = 0; i < max; i++) { + *lp = ulaw_to_alaw(*lp); + lp++; + } + + return ZAP_SUCCESS; +} + +ZIO_CODEC_FUNCTION(zio_alaw2ulaw) +{ + zap_size_t len = *datalen; + uint32_t i; + uint8_t *lp = data; + + if (max > len) { + max = len; + } + + for(i = 0; i < max; i++) { + *lp = alaw_to_ulaw(*lp); + lp++; + } + + return ZAP_SUCCESS; +} + +/******************************/ + +OZ_DECLARE(void) zap_channel_clear_detected_tones(zap_channel_t *zchan) +{ + uint32_t i; + + memset(zchan->detected_tones, 0, sizeof(zchan->detected_tones[0]) * ZAP_TONEMAP_INVALID); + + for (i = 1; i < ZAP_TONEMAP_INVALID; i++) { + zchan->span->tone_finder[i].tone_count = 0; + } +} + +OZ_DECLARE(void) zap_channel_clear_needed_tones(zap_channel_t *zchan) +{ + memset(zchan->needed_tones, 0, sizeof(zchan->needed_tones[0]) * ZAP_TONEMAP_INVALID); +} + +OZ_DECLARE(zap_size_t) zap_channel_dequeue_dtmf(zap_channel_t *zchan, char *dtmf, zap_size_t len) +{ + zap_size_t bytes = 0; + + assert(zchan != NULL); + + if (!zap_test_flag(zchan, ZAP_CHANNEL_READY)) { + return ZAP_FAIL; + } + + if (zchan->digit_buffer && zap_buffer_inuse(zchan->digit_buffer)) { + zap_mutex_lock(zchan->mutex); + if ((bytes = zap_buffer_read(zchan->digit_buffer, dtmf, len)) > 0) { + *(dtmf + bytes) = '\0'; + } + zap_mutex_unlock(zchan->mutex); + } + + return bytes; +} + +OZ_DECLARE(void) zap_channel_flush_dtmf(zap_channel_t *zchan) +{ + if (zchan->digit_buffer && zap_buffer_inuse(zchan->digit_buffer)) { + zap_mutex_lock(zchan->mutex); + zap_buffer_zero(zchan->digit_buffer); + zap_mutex_unlock(zchan->mutex); + } +} + +OZ_DECLARE(zap_status_t) zap_channel_queue_dtmf(zap_channel_t *zchan, const char *dtmf) +{ + zap_status_t status; + register zap_size_t len, inuse; + zap_size_t wr = 0; + const char *p; + + assert(zchan != NULL); + + if (zchan->pre_buffer) { + zap_buffer_zero(zchan->pre_buffer); + } + + zap_mutex_lock(zchan->mutex); + + inuse = zap_buffer_inuse(zchan->digit_buffer); + len = strlen(dtmf); + + if (len + inuse > zap_buffer_len(zchan->digit_buffer)) { + zap_buffer_toss(zchan->digit_buffer, strlen(dtmf)); + } + + if (zchan->span->dtmf_hangup_len) { + for (p = dtmf; zap_is_dtmf(*p); p++) { + memmove (zchan->dtmf_hangup_buf, zchan->dtmf_hangup_buf + 1, zchan->span->dtmf_hangup_len - 1); + zchan->dtmf_hangup_buf[zchan->span->dtmf_hangup_len - 1] = *p; + if (!strcmp(zchan->dtmf_hangup_buf, zchan->span->dtmf_hangup)) { + zap_log(ZAP_LOG_DEBUG, "DTMF hangup detected.\n"); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + break; + } + } + } + + p = dtmf; + while (wr < len && p) { + if (zap_is_dtmf(*p)) { + wr++; + } else { + break; + } + p++; + } + + status = zap_buffer_write(zchan->digit_buffer, dtmf, wr) ? ZAP_SUCCESS : ZAP_FAIL; + zap_mutex_unlock(zchan->mutex); + + return status; +} + + +static zap_status_t handle_dtmf(zap_channel_t *zchan, zap_size_t datalen) +{ + zap_buffer_t *buffer = NULL; + zap_size_t dblen = 0; + int wrote = 0; + + if (zchan->gen_dtmf_buffer && (dblen = zap_buffer_inuse(zchan->gen_dtmf_buffer))) { + char digits[128] = ""; + char *cur; + int x = 0; + + if (dblen > sizeof(digits) - 1) { + dblen = sizeof(digits) - 1; + } + + if (zap_buffer_read(zchan->gen_dtmf_buffer, digits, dblen) && !zap_strlen_zero_buf(digits)) { + zap_log(ZAP_LOG_DEBUG, "%d:%d GENERATE DTMF [%s]\n", zchan->span_id, zchan->chan_id, digits); + + cur = digits; + + if (*cur == 'F') { + zap_channel_command(zchan, ZAP_COMMAND_FLASH, NULL); + cur++; + } + + for (; *cur; cur++) { + if ((wrote = teletone_mux_tones(&zchan->tone_session, &zchan->tone_session.TONES[(int)*cur]))) { + zap_buffer_write(zchan->dtmf_buffer, zchan->tone_session.buffer, wrote * 2); + x++; + } else { + zap_log(ZAP_LOG_ERROR, "%d:%d Problem Adding DTMF SEQ [%s]\n", zchan->span_id, zchan->chan_id, digits); + return ZAP_FAIL; + } + } + + if (x) { + zchan->skip_read_frames = (wrote / (zchan->effective_interval * 8)) + 4; + } + } + } + + + if (!zchan->buffer_delay || --zchan->buffer_delay == 0) { + if (zchan->dtmf_buffer && (dblen = zap_buffer_inuse(zchan->dtmf_buffer))) { + buffer = zchan->dtmf_buffer; + } else if (zchan->fsk_buffer && (dblen = zap_buffer_inuse(zchan->fsk_buffer))) { + buffer = zchan->fsk_buffer; + } + } + + if (buffer) { + zap_size_t dlen = datalen; + uint8_t auxbuf[1024]; + zap_size_t len, br, max = sizeof(auxbuf); + + if (zchan->native_codec != ZAP_CODEC_SLIN) { + dlen *= 2; + } + + len = dblen > dlen ? dlen : dblen; + + br = zap_buffer_read(buffer, auxbuf, len); + if (br < dlen) { + memset(auxbuf + br, 0, dlen - br); + } + + if (zchan->native_codec != ZAP_CODEC_SLIN) { + if (zchan->native_codec == ZAP_CODEC_ULAW) { + zio_slin2ulaw(auxbuf, max, &dlen); + } else if (zchan->native_codec == ZAP_CODEC_ALAW) { + zio_slin2alaw(auxbuf, max, &dlen); + } + } + + return zchan->zio->write(zchan, auxbuf, &dlen); + } + + return ZAP_SUCCESS; + +} + + +OZ_DECLARE(void) zap_generate_sln_silence(int16_t *data, uint32_t samples, uint32_t divisor) +{ + int16_t x; + uint32_t i; + int sum_rnd = 0; + int16_t rnd2 = (int16_t) zap_current_time_in_ms() * (int16_t) (intptr_t) data; + + assert(divisor); + + for (i = 0; i < samples; i++, sum_rnd = 0) { + for (x = 0; x < 6; x++) { + rnd2 = rnd2 * 31821U + 13849U; + sum_rnd += rnd2 ; + } + //switch_normalize_to_16bit(sum_rnd); + *data = (int16_t) ((int16_t) sum_rnd / (int) divisor); + + data++; + } +} + + + +OZ_DECLARE(zap_status_t) zap_channel_read(zap_channel_t *zchan, void *data, zap_size_t *datalen) +{ + zap_status_t status = ZAP_FAIL; + zio_codec_t codec_func = NULL; + zap_size_t max = *datalen; + unsigned i = 0; + + assert(zchan != NULL); + assert(zchan->zio != NULL); + + if (!zap_test_flag(zchan, ZAP_CHANNEL_OPEN)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "channel not open"); + return ZAP_FAIL; + } + + if (!zchan->zio->read) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "method not implemented"); + return ZAP_FAIL; + } + + status = zchan->zio->read(zchan, data, datalen); + if (zchan->fds[0] > -1) { + int dlen = (int) *datalen; + if (write(zchan->fds[0], data, dlen) != dlen) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "file write error!"); + return ZAP_FAIL; + } + } + + if (status == ZAP_SUCCESS) { + if (zap_test_flag(zchan, ZAP_CHANNEL_USE_RX_GAIN) + && (zchan->native_codec == ZAP_CODEC_ALAW || zchan->native_codec == ZAP_CODEC_ULAW)) { + unsigned char *rdata = data; + for (i = 0; i < *datalen; i++) { + rdata[i] = zchan->rxgain_table[rdata[i]]; + } + } + handle_dtmf(zchan, *datalen); + } + + if (status == ZAP_SUCCESS && zap_test_flag(zchan, ZAP_CHANNEL_TRANSCODE) && zchan->effective_codec != zchan->native_codec) { + if (zchan->native_codec == ZAP_CODEC_ULAW && zchan->effective_codec == ZAP_CODEC_SLIN) { + codec_func = zio_ulaw2slin; + } else if (zchan->native_codec == ZAP_CODEC_ULAW && zchan->effective_codec == ZAP_CODEC_ALAW) { + codec_func = zio_ulaw2alaw; + } else if (zchan->native_codec == ZAP_CODEC_ALAW && zchan->effective_codec == ZAP_CODEC_SLIN) { + codec_func = zio_alaw2slin; + } else if (zchan->native_codec == ZAP_CODEC_ALAW && zchan->effective_codec == ZAP_CODEC_ULAW) { + codec_func = zio_alaw2ulaw; + } + + if (codec_func) { + status = codec_func(data, max, datalen); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "codec error!"); + status = ZAP_FAIL; + } + } + + if (zap_test_flag(zchan, ZAP_CHANNEL_DTMF_DETECT) || zap_test_flag(zchan, ZAP_CHANNEL_PROGRESS_DETECT) || + zap_test_flag(zchan, ZAP_CHANNEL_CALLERID_DETECT)) { + uint8_t sln_buf[1024] = {0}; + int16_t *sln; + zap_size_t slen = 0; + char digit_str[80] = ""; + + if (zchan->effective_codec == ZAP_CODEC_SLIN) { + sln = data; + slen = *datalen / 2; + } else { + zap_size_t len = *datalen; + uint32_t i; + uint8_t *lp = data; + + slen = sizeof(sln_buf) / 2; + if (len > slen) { + len = slen; + } + + sln = (int16_t *) sln_buf; + for(i = 0; i < len; i++) { + if (zchan->effective_codec == ZAP_CODEC_ULAW) { + *sln++ = ulaw_to_linear(*lp++); + } else if (zchan->effective_codec == ZAP_CODEC_ALAW) { + *sln++ = alaw_to_linear(*lp++); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "codec error!"); + return ZAP_FAIL; + } + } + sln = (int16_t *) sln_buf; + slen = len; + } + + if (zap_test_flag(zchan, ZAP_CHANNEL_CALLERID_DETECT)) { + if (zap_fsk_demod_feed(&zchan->fsk, sln, slen) != ZAP_SUCCESS) { + zap_size_t type, mlen; + char str[128], *sp; + + while(zap_fsk_data_parse(&zchan->fsk, &type, &sp, &mlen) == ZAP_SUCCESS) { + *(str+mlen) = '\0'; + zap_copy_string(str, sp, ++mlen); + zap_clean_string(str); + zap_log(ZAP_LOG_DEBUG, "FSK: TYPE %s LEN %d VAL [%s]\n", zap_mdmf_type2str(type), mlen-1, str); + + switch(type) { + case MDMF_DDN: + case MDMF_PHONE_NUM: + { + if (mlen > sizeof(zchan->caller_data.ani)) { + mlen = sizeof(zchan->caller_data.ani); + } + zap_set_string(zchan->caller_data.ani.digits, str); + zap_set_string(zchan->caller_data.cid_num.digits, zchan->caller_data.ani.digits); + } + break; + case MDMF_NO_NUM: + { + zap_set_string(zchan->caller_data.ani.digits, *str == 'P' ? "private" : "unknown"); + zap_set_string(zchan->caller_data.cid_name, zchan->caller_data.ani.digits); + } + break; + case MDMF_PHONE_NAME: + { + if (mlen > sizeof(zchan->caller_data.cid_name)) { + mlen = sizeof(zchan->caller_data.cid_name); + } + zap_set_string(zchan->caller_data.cid_name, str); + } + break; + case MDMF_NO_NAME: + { + zap_set_string(zchan->caller_data.cid_name, *str == 'P' ? "private" : "unknown"); + } + case MDMF_DATETIME: + { + if (mlen > sizeof(zchan->caller_data.cid_date)) { + mlen = sizeof(zchan->caller_data.cid_date); + } + zap_set_string(zchan->caller_data.cid_date, str); + } + break; + } + } + zap_channel_command(zchan, ZAP_COMMAND_DISABLE_CALLERID_DETECT, NULL); + } + } + + if (zap_test_flag(zchan, ZAP_CHANNEL_PROGRESS_DETECT) && !zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_PROGRESS)) { + uint32_t i; + + for (i = 1; i < ZAP_TONEMAP_INVALID; i++) { + if (zchan->span->tone_finder[i].tone_count) { + if (zchan->needed_tones[i] && teletone_multi_tone_detect(&zchan->span->tone_finder[i], sln, (int)slen)) { + if (++zchan->detected_tones[i]) { + zchan->needed_tones[i] = 0; + zchan->detected_tones[0]++; + } + } + } + } + } + + + if (zap_test_flag(zchan, ZAP_CHANNEL_DTMF_DETECT) && !zap_channel_test_feature(zchan, ZAP_CHANNEL_FEATURE_DTMF_DETECT)) { + teletone_dtmf_detect(&zchan->dtmf_detect, sln, (int)slen); + teletone_dtmf_get(&zchan->dtmf_detect, digit_str, sizeof(digit_str)); + + if(*digit_str) { + zio_event_cb_t event_callback = NULL; + + if (zchan->state == ZAP_CHANNEL_STATE_CALLWAITING && (*digit_str == 'D' || *digit_str == 'A')) { + zchan->detected_tones[ZAP_TONEMAP_CALLWAITING_ACK]++; + } else { + zap_channel_queue_dtmf(zchan, digit_str); + + if (zchan->span->event_callback) { + event_callback = zchan->span->event_callback; + } else if (zchan->event_callback) { + event_callback = zchan->event_callback; + } + + if (event_callback) { + zchan->event_header.channel = zchan; + zchan->event_header.e_type = ZAP_EVENT_DTMF; + zchan->event_header.data = digit_str; + event_callback(zchan, &zchan->event_header); + zchan->event_header.e_type = ZAP_EVENT_NONE; + zchan->event_header.data = NULL; + } + if (zap_test_flag(zchan, ZAP_CHANNEL_SUPRESS_DTMF)) { + zchan->skip_read_frames = 20; + } + } + } + } + } + + if (zchan->skip_read_frames > 0 || zap_test_flag(zchan, ZAP_CHANNEL_MUTE)) { + + zap_mutex_lock(zchan->pre_buffer_mutex); + if (zchan->pre_buffer && zap_buffer_inuse(zchan->pre_buffer)) { + zap_buffer_zero(zchan->pre_buffer); + } + zap_mutex_unlock(zchan->pre_buffer_mutex); + + + memset(data, 255, *datalen); + + if (zchan->skip_read_frames > 0) { + zchan->skip_read_frames--; + } + } else { + zap_mutex_lock(zchan->pre_buffer_mutex); + if (zchan->pre_buffer_size && zchan->pre_buffer) { + zap_buffer_write(zchan->pre_buffer, data, *datalen); + if (zap_buffer_inuse(zchan->pre_buffer) >= zchan->pre_buffer_size) { + zap_buffer_read(zchan->pre_buffer, data, *datalen); + } else { + memset(data, 255, *datalen); + } + } + zap_mutex_unlock(zchan->pre_buffer_mutex); + } + + + return status; +} + + +OZ_DECLARE(zap_status_t) zap_channel_write(zap_channel_t *zchan, void *data, zap_size_t datasize, zap_size_t *datalen) +{ + zap_status_t status = ZAP_FAIL; + zio_codec_t codec_func = NULL; + zap_size_t max = datasize; + unsigned i = 0; + + assert(zchan != NULL); + assert(zchan->zio != NULL); + + if (!zchan->buffer_delay && + ((zchan->dtmf_buffer && zap_buffer_inuse(zchan->dtmf_buffer)) || + (zchan->fsk_buffer && zap_buffer_inuse(zchan->fsk_buffer)))) { + /* read size writing DTMF ATM */ + return ZAP_SUCCESS; + } + + + if (!zap_test_flag(zchan, ZAP_CHANNEL_OPEN)) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "channel not open"); + return ZAP_FAIL; + } + + if (!zchan->zio->write) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "method not implemented"); + return ZAP_FAIL; + } + + if (zap_test_flag(zchan, ZAP_CHANNEL_TRANSCODE) && zchan->effective_codec != zchan->native_codec) { + if (zchan->native_codec == ZAP_CODEC_ULAW && zchan->effective_codec == ZAP_CODEC_SLIN) { + codec_func = zio_slin2ulaw; + } else if (zchan->native_codec == ZAP_CODEC_ULAW && zchan->effective_codec == ZAP_CODEC_ALAW) { + codec_func = zio_alaw2ulaw; + } else if (zchan->native_codec == ZAP_CODEC_ALAW && zchan->effective_codec == ZAP_CODEC_SLIN) { + codec_func = zio_slin2alaw; + } else if (zchan->native_codec == ZAP_CODEC_ALAW && zchan->effective_codec == ZAP_CODEC_ULAW) { + codec_func = zio_ulaw2alaw; + } + + if (codec_func) { + status = codec_func(data, max, datalen); + } else { + snprintf(zchan->last_error, sizeof(zchan->last_error), "codec error!"); + status = ZAP_FAIL; + } + } + + if (zchan->fds[1] > -1) { + int dlen = (int) *datalen; + if ((write(zchan->fds[1], data, dlen)) != dlen) { + snprintf(zchan->last_error, sizeof(zchan->last_error), "file write error!"); + return ZAP_FAIL; + } + } + + if (zap_test_flag(zchan, ZAP_CHANNEL_USE_TX_GAIN) + && (zchan->native_codec == ZAP_CODEC_ALAW || zchan->native_codec == ZAP_CODEC_ULAW)) { + unsigned char *wdata = data; + for (i = 0; i < *datalen; i++) { + wdata[i] = zchan->txgain_table[wdata[i]]; + } + } + status = zchan->zio->write(zchan, data, datalen); + + return status; +} + +OZ_DECLARE(zap_status_t) zap_channel_clear_vars(zap_channel_t *zchan) +{ + if(zchan->variable_hash) { + hashtable_destroy(zchan->variable_hash); + } + zchan->variable_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys); + + if(!zchan->variable_hash) + return ZAP_FAIL; + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_channel_add_var(zap_channel_t *zchan, const char *var_name, const char *value) +{ + char *t_name = 0, *t_val = 0; + + if(!zchan->variable_hash || !var_name || !value) + { + return ZAP_FAIL; + } + + t_name = strdup(var_name); + t_val = strdup(value); + + if(hashtable_insert(zchan->variable_hash, t_name, t_val, HASHTABLE_FLAG_FREE_KEY | HASHTABLE_FLAG_FREE_VALUE)) { + return ZAP_SUCCESS; + } + return ZAP_FAIL; +} + +OZ_DECLARE(const char *) zap_channel_get_var(zap_channel_t *zchan, const char *var_name) +{ + if(!zchan->variable_hash || !var_name) + { + return NULL; + } + return (const char *) hashtable_search(zchan->variable_hash, (void *)var_name); +} + +static struct { + zap_io_interface_t *pika_interface; +} interfaces; + + +OZ_DECLARE(char *) zap_api_execute(const char *type, const char *cmd) +{ + zap_io_interface_t *zio = NULL; + char *dup = NULL, *p; + char *rval = NULL; + + if (type && !cmd) { + dup = strdup(type); + if ((p = strchr(dup, ' '))) { + *p++ = '\0'; + cmd = p; + } + + type = dup; + } + + zap_mutex_lock(globals.mutex); + if (!(zio = (zap_io_interface_t *) hashtable_search(globals.interface_hash, (void *)type))) { + zap_load_module_assume(type); + if ((zio = (zap_io_interface_t *) hashtable_search(globals.interface_hash, (void *)type))) { + zap_log(ZAP_LOG_INFO, "auto-loaded '%s'\n", type); + } + } + zap_mutex_unlock(globals.mutex); + + if (zio && zio->api) { + zap_stream_handle_t stream = { 0 }; + zap_status_t status; + ZAP_STANDARD_STREAM(stream); + status = zio->api(&stream, cmd); + + if (status != ZAP_SUCCESS) { + zap_safe_free(stream.data); + } else { + rval = (char *) stream.data; + } + } + + zap_safe_free(dup); + + return rval; +} + +static void zap_set_channels_gains(zap_span_t *span, int currindex, float rxgain, float txgain) +{ + unsigned chan_index = 0; + + if (!span->chan_count) { + return; + } + + for (chan_index = currindex+1; chan_index <= span->chan_count; chan_index++) { + if (!ZAP_IS_VOICE_CHANNEL(span->channels[chan_index])) { + continue; + } + zap_channel_command(span->channels[chan_index], ZAP_COMMAND_SET_RX_GAIN, &rxgain); + zap_channel_command(span->channels[chan_index], ZAP_COMMAND_SET_TX_GAIN, &txgain); + } +} + +static zap_status_t load_config(void) +{ + char cfg_name[] = "openzap.conf"; + zap_config_t cfg; + char *var, *val; + int catno = -1; + zap_span_t *span = NULL; + unsigned configured = 0, d = 0; + char name[80] = ""; + char number[25] = ""; + zap_io_interface_t *zio = NULL; + zap_analog_start_type_t tmp; + float rxgain = 0.0; + float txgain = 0.0; + int chanindex = 0; + + if (!zap_config_open_file(&cfg, cfg_name)) { + return ZAP_FAIL; + } + + while (zap_config_next_pair(&cfg, &var, &val)) { + if (*cfg.category == '#') { + if (cfg.catno != catno) { + zap_log(ZAP_LOG_DEBUG, "Skipping %s\n", cfg.category); + catno = cfg.catno; + } + } else if (!strncasecmp(cfg.category, "span", 4)) { + if (cfg.catno != catno) { + char *type = cfg.category + 4; + char *name; + + if (*type == ' ') { + type++; + } + + zap_log(ZAP_LOG_DEBUG, "found config for span\n"); + catno = cfg.catno; + + if (zap_strlen_zero(type)) { + zap_log(ZAP_LOG_CRIT, "failure creating span, no type specified.\n"); + span = NULL; + continue; + } + + if ((name = strchr(type, ' '))) { + *name++ = '\0'; + } + + zap_mutex_lock(globals.mutex); + if (!(zio = (zap_io_interface_t *) hashtable_search(globals.interface_hash, type))) { + zap_load_module_assume(type); + if ((zio = (zap_io_interface_t *) hashtable_search(globals.interface_hash, type))) { + zap_log(ZAP_LOG_INFO, "auto-loaded '%s'\n", type); + } + } + zap_mutex_unlock(globals.mutex); + + if (!zio) { + zap_log(ZAP_LOG_CRIT, "failure creating span, no such type '%s'\n", type); + span = NULL; + continue; + } + + if (!zio->configure_span) { + zap_log(ZAP_LOG_CRIT, "failure creating span, no configure_span method for '%s'\n", type); + span = NULL; + continue; + } + + if (zap_span_create(zio, &span, name) == ZAP_SUCCESS) { + span->type = strdup(type); + d = 0; + + zap_log(ZAP_LOG_DEBUG, "created span %d (%s) of type %s\n", span->span_id, span->name, type); + + } else { + zap_log(ZAP_LOG_CRIT, "failure creating span of type %s\n", type); + span = NULL; + continue; + } + } + + if (!span) { + continue; + } + + zap_log(ZAP_LOG_DEBUG, "span %d [%s]=[%s]\n", span->span_id, var, val); + + if (!strcasecmp(var, "trunk_type")) { + span->trunk_type = zap_str2zap_trunk_type(val); + zap_log(ZAP_LOG_DEBUG, "setting trunk type to '%s'\n", zap_trunk_type2str(span->trunk_type)); + } else if (!strcasecmp(var, "name")) { + if (!strcasecmp(val, "undef")) { + *name = '\0'; + } else { + zap_copy_string(name, val, sizeof(name)); + } + } else if (!strcasecmp(var, "number")) { + if (!strcasecmp(val, "undef")) { + *number = '\0'; + } else { + zap_copy_string(number, val, sizeof(number)); + } + } else if (!strcasecmp(var, "analog-start-type")) { + if (span->trunk_type == ZAP_TRUNK_FXS || span->trunk_type == ZAP_TRUNK_FXO || span->trunk_type == ZAP_TRUNK_EM) { + if ((tmp = zap_str2zap_analog_start_type(val)) != ZAP_ANALOG_START_NA) { + span->start_type = tmp; + zap_log(ZAP_LOG_DEBUG, "changing start type to '%s'\n", zap_analog_start_type2str(span->start_type)); + } + } else { + zap_log(ZAP_LOG_ERROR, "This option is only valid on analog trunks!\n"); + } + } else if (!strcasecmp(var, "fxo-channel")) { + if (span->trunk_type == ZAP_TRUNK_NONE) { + span->trunk_type = ZAP_TRUNK_FXO; + zap_log(ZAP_LOG_DEBUG, "setting trunk type to '%s' start(%s)\n", zap_trunk_type2str(span->trunk_type), + zap_analog_start_type2str(span->start_type)); + } + if (span->trunk_type == ZAP_TRUNK_FXO) { + chanindex = span->chan_count; + configured += zio->configure_span(span, val, ZAP_CHAN_TYPE_FXO, name, number); + zap_set_channels_gains(span, chanindex, rxgain, txgain); + } else { + zap_log(ZAP_LOG_WARNING, "Cannot add FXO channels to an FXS trunk!\n"); + } + } else if (!strcasecmp(var, "fxs-channel")) { + if (span->trunk_type == ZAP_TRUNK_NONE) { + span->trunk_type = ZAP_TRUNK_FXS; + zap_log(ZAP_LOG_DEBUG, "setting trunk type to '%s' start(%s)\n", zap_trunk_type2str(span->trunk_type), + zap_analog_start_type2str(span->start_type)); + } + if (span->trunk_type == ZAP_TRUNK_FXS) { + chanindex = span->chan_count; + configured += zio->configure_span(span, val, ZAP_CHAN_TYPE_FXS, name, number); + zap_set_channels_gains(span, chanindex, rxgain, txgain); + } else { + zap_log(ZAP_LOG_WARNING, "Cannot add FXS channels to an FXO trunk!\n"); + } + } else if (!strcasecmp(var, "em-channel")) { + if (span->trunk_type == ZAP_TRUNK_NONE) { + span->trunk_type = ZAP_TRUNK_EM; + zap_log(ZAP_LOG_DEBUG, "setting trunk type to '%s' start(%s)\n", zap_trunk_type2str(span->trunk_type), + zap_analog_start_type2str(span->start_type)); + } + if (span->trunk_type == ZAP_TRUNK_EM) { + chanindex = span->chan_count; + configured += zio->configure_span(span, val, ZAP_CHAN_TYPE_EM, name, number); + zap_set_channels_gains(span, chanindex, rxgain, txgain); + } else { + zap_log(ZAP_LOG_WARNING, "Cannot add EM channels to a non-EM trunk!\n"); + } + } else if (!strcasecmp(var, "b-channel")) { + chanindex = span->chan_count; + configured += zio->configure_span(span, val, ZAP_CHAN_TYPE_B, name, number); + zap_set_channels_gains(span, chanindex, rxgain, txgain); + } else if (!strcasecmp(var, "d-channel")) { + if (d) { + zap_log(ZAP_LOG_WARNING, "ignoring extra d-channel\n"); + } else { + zap_chan_type_t qtype; + if (!strncasecmp(val, "lapd:", 5)) { + qtype = ZAP_CHAN_TYPE_DQ931; + val += 5; + } else { + qtype = ZAP_CHAN_TYPE_DQ921; + } + configured += zio->configure_span(span, val, qtype, name, number); + d++; + } + } else if (!strcasecmp(var, "cas-channel")) { + chanindex = span->chan_count; + configured += zio->configure_span(span, val, ZAP_CHAN_TYPE_CAS, name, number); + zap_set_channels_gains(span, chanindex, rxgain, txgain); + } else if (!strcasecmp(var, "dtmf_hangup")) { + span->dtmf_hangup = strdup(val); + span->dtmf_hangup_len = strlen(val); + } else if (!strcasecmp(var, "txgain")) { + if (sscanf(val, "%f", &txgain) != 1) { + zap_log(ZAP_LOG_ERROR, "invalid txgain: '%s'\n", val); + } + } else if (!strcasecmp(var, "rxgain")) { + if (sscanf(val, "%f", &rxgain) != 1) { + zap_log(ZAP_LOG_ERROR, "invalid rxgain: '%s'\n", val); + } + } else { + zap_log(ZAP_LOG_ERROR, "unknown span variable '%s'\n", var); + } + } else if (!strncasecmp(cfg.category, "general", 7)) { + if (!strncasecmp(var, "cpu_monitoring_interval", 24)) { + if (atoi(val) > 0) { + globals.cpu_monitor.interval = atoi(val); + } else { + zap_log(ZAP_LOG_ERROR, "Invalid cpu monitoring interval %s\n", val); + } + } else if (!strncasecmp(var, "cpu_set_alarm_threshold", 22)) { + if (atoi(val) > 0 && atoi(val) < 100) { + globals.cpu_monitor.set_alarm_threshold = (uint8_t)atoi(val); + } else { + zap_log(ZAP_LOG_ERROR, "Invalid cpu alarm set threshold %s\n", val); + } + } else if (!strncasecmp(var, "cpu_reset_alarm_threshold", 22)) { + if (atoi(val) > 0 && atoi(val) < 100) { + globals.cpu_monitor.reset_alarm_threshold = (uint8_t)atoi(val); + if (globals.cpu_monitor.reset_alarm_threshold > globals.cpu_monitor.set_alarm_threshold) { + globals.cpu_monitor.reset_alarm_threshold = globals.cpu_monitor.set_alarm_threshold-10; + zap_log(ZAP_LOG_ERROR, "Cpu alarm reset threshold must be lower than set threshold, set threshold to %d\n", globals.cpu_monitor.reset_alarm_threshold); + } + } else { + zap_log(ZAP_LOG_ERROR, "Invalid cpu alarm reset threshold %s\n", val); + } + } else if (!strncasecmp(var, "cpu_alarm_action", 16)) { + char* p = val; + do { + if (!strncasecmp(p, "reject", 6)) { + globals.cpu_monitor.alarm_action_flags |= ZAP_CPU_ALARM_ACTION_REJECT; + } else if (!strncasecmp(p, "warn", 4)) { + globals.cpu_monitor.alarm_action_flags |= ZAP_CPU_ALARM_ACTION_WARN; + } + p = strchr(p, ','); + if (p) { + while(++p) if (*p != 0x20) break; + } + } while (p); + } + } else { + zap_log(ZAP_LOG_ERROR, "unknown param [%s] '%s' / '%s'\n", cfg.category, var, val); + } + } + zap_config_close_file(&cfg); + + zap_log(ZAP_LOG_INFO, "Configured %u channel(s)\n", configured); + return configured ? ZAP_SUCCESS : ZAP_FAIL; +} + +static zap_status_t process_module_config(zap_io_interface_t *zio) +{ + zap_config_t cfg; + char *var, *val; + char filename[256] = ""; + assert(zio != NULL); + + snprintf(filename, sizeof(filename), "%s.conf", zio->name); + + if (!zio->configure) { + zap_log(ZAP_LOG_DEBUG, "Module %s does not support configuration.\n", zio->name); + return ZAP_FAIL; + } + + if (!zap_config_open_file(&cfg, filename)) { + zap_log(ZAP_LOG_ERROR, "Cannot open %s\n", filename); + return ZAP_FAIL; + } + + while (zap_config_next_pair(&cfg, &var, &val)) { + zio->configure(cfg.category, var, val, cfg.lineno); + } + + zap_config_close_file(&cfg); + + return ZAP_SUCCESS; +} + +OZ_DECLARE(int) zap_load_module(const char *name) +{ + zap_dso_lib_t lib; + int count = 0, x = 0; + char path[128] = ""; + char *err; + zap_module_t *mod; + +#ifdef WIN32 + const char *ext = ".dll"; + //const char *EXT = ".DLL"; +#define ZAP_MOD_DIR "." //todo +#elif defined (MACOSX) || defined (DARWIN) + const char *ext = ".dylib"; + //const char *EXT = ".DYLIB"; +#else + const char *ext = ".so"; + //const char *EXT = ".SO"; +#endif + + if (*name == *ZAP_PATH_SEPARATOR) { + snprintf(path, sizeof(path), "%s%s", name, ext); + } else { + snprintf(path, sizeof(path), "%s%s%s%s", ZAP_MOD_DIR, ZAP_PATH_SEPARATOR, name, ext); + } + + if (!(lib = zap_dso_open(path, &err))) { + zap_log(ZAP_LOG_ERROR, "Error loading %s [%s]\n", path, err); + zap_safe_free(err); + return 0; + } + + if (!(mod = (zap_module_t *) zap_dso_func_sym(lib, "zap_module", &err))) { + zap_log(ZAP_LOG_ERROR, "Error loading %s [%s]\n", path, err); + zap_safe_free(err); + return 0; + } + + if (mod->io_load) { + zap_io_interface_t *interface1 = NULL; /* name conflict w/windows here */ + + if (mod->io_load(&interface1) != ZAP_SUCCESS || !interface1 || !interface1->name) { + zap_log(ZAP_LOG_ERROR, "Error loading %s\n", path); + } else { + zap_log(ZAP_LOG_INFO, "Loading IO from %s [%s]\n", path, interface1->name); + zap_mutex_lock(globals.mutex); + if (hashtable_search(globals.interface_hash, (void *)interface1->name)) { + zap_log(ZAP_LOG_ERROR, "Interface %s already loaded!\n", interface1->name); + } else { + hashtable_insert(globals.interface_hash, (void *)interface1->name, interface1, HASHTABLE_FLAG_NONE); + process_module_config(interface1); + x++; + } + zap_mutex_unlock(globals.mutex); + } + } + + if (mod->sig_load) { + if (mod->sig_load() != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "Error loading %s\n", path); + } else { + zap_log(ZAP_LOG_INFO, "Loading SIG from %s\n", path); + x++; + } + } + + if (x) { + char *p; + mod->lib = lib; + zap_set_string(mod->path, path); + if (mod->name[0] == '\0') { + if (!(p = strrchr(path, *ZAP_PATH_SEPARATOR))) { + p = path; + } + zap_set_string(mod->name, p); + } + + zap_mutex_lock(globals.mutex); + if (hashtable_search(globals.module_hash, (void *)mod->name)) { + zap_log(ZAP_LOG_ERROR, "Module %s already loaded!\n", mod->name); + zap_dso_destroy(&lib); + } else { + hashtable_insert(globals.module_hash, (void *)mod->name, mod, HASHTABLE_FLAG_NONE); + count++; + } + zap_mutex_unlock(globals.mutex); + } else { + zap_log(ZAP_LOG_ERROR, "Unloading %s\n", path); + zap_dso_destroy(&lib); + } + + return count; +} + +OZ_DECLARE(int) zap_load_module_assume(const char *name) +{ + char buf[256] = ""; + + snprintf(buf, sizeof(buf), "ozmod_%s", name); + return zap_load_module(buf); +} + +OZ_DECLARE(int) zap_load_modules(void) +{ + char cfg_name[] = "modules.conf"; + zap_config_t cfg; + char *var, *val; + int count = 0; + + if (!zap_config_open_file(&cfg, cfg_name)) { + return ZAP_FAIL; + } + + while (zap_config_next_pair(&cfg, &var, &val)) { + if (!strcasecmp(cfg.category, "modules")) { + if (!strcasecmp(var, "load")) { + count += zap_load_module(val); + } + } + } + + return count; +} + +OZ_DECLARE(zap_status_t) zap_unload_modules(void) +{ + zap_hash_iterator_t *i; + zap_dso_lib_t lib; + + for (i = hashtable_first(globals.module_hash); i; i = hashtable_next(i)) { + const void *key; + void *val; + + hashtable_this(i, &key, NULL, &val); + + if (key && val) { + zap_module_t *mod = (zap_module_t *) val; + + if (!mod) { + continue; + } + + if (mod->io_unload) { + if (mod->io_unload() == ZAP_SUCCESS) { + zap_log(ZAP_LOG_INFO, "Unloading IO %s\n", mod->name); + } else { + zap_log(ZAP_LOG_ERROR, "Error unloading IO %s\n", mod->name); + } + } + + if (mod->sig_unload) { + if (mod->sig_unload() == ZAP_SUCCESS) { + zap_log(ZAP_LOG_INFO, "Unloading SIG %s\n", mod->name); + } else { + zap_log(ZAP_LOG_ERROR, "Error unloading SIG %s\n", mod->name); + } + } + + + zap_log(ZAP_LOG_INFO, "Unloading %s\n", mod->path); + lib = mod->lib; + zap_dso_destroy(&lib); + + } + } + + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_configure_span(const char *type, zap_span_t *span, zio_signal_cb_t sig_cb, ...) +{ + zap_module_t *mod = (zap_module_t *) hashtable_search(globals.module_hash, (void *)type); + zap_status_t status = ZAP_FAIL; + + if (!mod) { + zap_load_module_assume(type); + if ((mod = (zap_module_t *) hashtable_search(globals.module_hash, (void *)type))) { + zap_log(ZAP_LOG_INFO, "auto-loaded '%s'\n", type); + } + } + + if (mod && mod->sig_configure) { + va_list ap; + va_start(ap, sig_cb); + status = mod->sig_configure(span, sig_cb, ap); + va_end(ap); + } else { + zap_log(ZAP_LOG_ERROR, "can't find '%s'\n", type); + status = ZAP_FAIL; + } + + return status; +} + +OZ_DECLARE(zap_status_t) zap_span_start(zap_span_t *span) +{ + if (span->start) { + return span->start(span); + } + + return ZAP_FAIL; +} + +OZ_DECLARE(zap_status_t) zap_span_send_signal(zap_span_t *span, zap_sigmsg_t *sigmsg) +{ + zap_status_t status = ZAP_FAIL; + + if (span->signal_cb) { + + if (sigmsg->channel) { + zap_mutex_lock(sigmsg->channel->mutex); + } + + status = span->signal_cb(sigmsg); + + if (sigmsg->channel) { + zap_mutex_unlock(sigmsg->channel->mutex); + } + } + + return status; +} + + +OZ_DECLARE(zap_status_t) zap_global_init(void) +{ + int modcount; + + memset(&globals, 0, sizeof(globals)); + + time_init(); + + zap_thread_override_default_stacksize(ZAP_THREAD_STACKSIZE); + + memset(&interfaces, 0, sizeof(interfaces)); + globals.interface_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys); + globals.module_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys); + globals.span_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys); + modcount = 0; + zap_mutex_create(&globals.mutex); + zap_mutex_create(&globals.span_mutex); + + modcount = zap_load_modules(); + zap_log(ZAP_LOG_NOTICE, "Modules configured: %d \n", modcount); + + globals.cpu_monitor.interval = 1000; + globals.cpu_monitor.alarm_action_flags = ZAP_CPU_ALARM_ACTION_WARN | ZAP_CPU_ALARM_ACTION_REJECT; + globals.cpu_monitor.set_alarm_threshold = 80; + globals.cpu_monitor.reset_alarm_threshold = 70; + + if (load_config() != ZAP_SUCCESS) { + zap_log(ZAP_LOG_ERROR, "No modules configured!\n"); + return ZAP_FAIL; + } + + globals.running = 1; + if (!zap_cpu_monitor_disabled) { + if (zap_cpu_monitor_start() != ZAP_SUCCESS) { + return ZAP_FAIL; + } + } + return ZAP_SUCCESS; +} + +OZ_DECLARE(uint32_t) zap_running(void) +{ + return globals.running; +} + + +OZ_DECLARE(zap_status_t) zap_global_destroy(void) +{ + unsigned int j; + zap_span_t *sp; + + time_end(); + + globals.running = 0; + zap_cpu_monitor_stop(); + zap_span_close_all(); + zap_sleep(1000); + + zap_mutex_lock(globals.span_mutex); + for (sp = globals.spans; sp;) { + zap_span_t *cur_span = sp; + sp = sp->next; + + if (cur_span) { + if (zap_test_flag(cur_span, ZAP_SPAN_CONFIGURED)) { + zap_mutex_lock(cur_span->mutex); + zap_clear_flag(cur_span, ZAP_SPAN_CONFIGURED); + for(j = 1; j <= cur_span->chan_count && cur_span->channels[j]; j++) { + zap_channel_t *cur_chan = cur_span->channels[j]; + if (cur_chan) { + if (zap_test_flag(cur_chan, ZAP_CHANNEL_CONFIGURED)) { + zap_channel_destroy(cur_chan); + } + free(cur_chan); + cur_chan = NULL; + } + } + zap_mutex_unlock(cur_span->mutex); + + if (cur_span->mutex) { + zap_mutex_destroy(&cur_span->mutex); + } + + zap_safe_free(cur_span->signal_data); + zap_span_destroy(cur_span); + } + + hashtable_remove(globals.span_hash, (void *)cur_span->name); + zap_safe_free(cur_span->type); + zap_safe_free(cur_span->name); + free(cur_span); + cur_span = NULL; + } + } + globals.spans = NULL; + zap_mutex_unlock(globals.span_mutex); + + globals.span_index = 0; + + zap_unload_modules(); + + zap_mutex_lock(globals.mutex); + hashtable_destroy(globals.interface_hash); + hashtable_destroy(globals.module_hash); + hashtable_destroy(globals.span_hash); + zap_mutex_unlock(globals.mutex); + zap_mutex_destroy(&globals.mutex); + zap_mutex_destroy(&globals.span_mutex); + memset(&globals, 0, sizeof(globals)); + return ZAP_SUCCESS; +} + + +OZ_DECLARE(uint32_t) zap_separate_string(char *buf, char delim, char **array, int arraylen) +{ + int argc; + char *ptr; + int quot = 0; + char qc = '\''; + int x; + + if (!buf || !array || !arraylen) { + return 0; + } + + memset(array, 0, arraylen * sizeof(*array)); + + ptr = buf; + + for (argc = 0; *ptr && (argc < arraylen - 1); argc++) { + array[argc] = ptr; + for (; *ptr; ptr++) { + if (*ptr == qc) { + if (quot) { + quot--; + } else { + quot++; + } + } else if ((*ptr == delim) && !quot) { + *ptr++ = '\0'; + break; + } + } + } + + if (*ptr) { + array[argc++] = ptr; + } + + /* strip quotes and leading / trailing spaces */ + for (x = 0; x < argc; x++) { + char *p; + + while(*(array[x]) == ' ') { + (array[x])++; + } + p = array[x]; + while((p = strchr(array[x], qc))) { + memmove(p, p+1, strlen(p)); + p++; + } + p = array[x] + (strlen(array[x]) - 1); + while(*p == ' ') { + *p-- = '\0'; + } + } + + return argc; +} + +OZ_DECLARE(void) zap_bitstream_init(zap_bitstream_t *bsp, uint8_t *data, uint32_t datalen, zap_endian_t endian, uint8_t ss) +{ + memset(bsp, 0, sizeof(*bsp)); + bsp->data = data; + bsp->datalen = datalen; + bsp->endian = endian; + bsp->ss = ss; + + if (endian < 0) { + bsp->top = bsp->bit_index = 7; + bsp->bot = 0; + } else { + bsp->top = bsp->bit_index = 0; + bsp->bot = 7; + } + +} + +OZ_DECLARE(int8_t) zap_bitstream_get_bit(zap_bitstream_t *bsp) +{ + int8_t bit = -1; + + + if (bsp->byte_index >= bsp->datalen) { + goto done; + } + + if (bsp->ss) { + if (!bsp->ssv) { + bsp->ssv = 1; + return 0; + } else if (bsp->ssv == 2) { + bsp->byte_index++; + bsp->ssv = 0; + return 1; + } + } + + + + + bit = (bsp->data[bsp->byte_index] >> (bsp->bit_index)) & 1; + + if (bsp->bit_index == bsp->bot) { + bsp->bit_index = bsp->top; + if (bsp->ss) { + bsp->ssv = 2; + goto done; + } + + if (++bsp->byte_index > bsp->datalen) { + bit = -1; + goto done; + } + + } else { + bsp->bit_index = bsp->bit_index + bsp->endian; + } + + + done: + return bit; +} + +OZ_DECLARE(void) print_hex_bytes(uint8_t *data, zap_size_t dlen, char *buf, zap_size_t blen) +{ + char *bp = buf; + uint8_t *byte = data; + uint32_t i, j = 0; + + if (blen < (dlen * 3) + 2) { + return; + } + + *bp++ = '['; + j++; + + for(i = 0; i < dlen; i++) { + snprintf(bp, blen-j, "%02x ", *byte++); + bp += 3; + j += 3; + } + + *--bp = ']'; + +} + +OZ_DECLARE(void) print_bits(uint8_t *b, int bl, char *buf, int blen, zap_endian_t e, uint8_t ss) +{ + zap_bitstream_t bs; + int j = 0, c = 0; + int8_t bit; + uint32_t last; + + if (blen < (bl * 10) + 2) { + return; + } + + zap_bitstream_init(&bs, b, bl, e, ss); + last = bs.byte_index; + while((bit = zap_bitstream_get_bit(&bs)) > -1) { + buf[j++] = bit ? '1' : '0'; + if (bs.byte_index != last) { + buf[j++] = ' '; + last = bs.byte_index; + if (++c == 8) { + buf[j++] = '\n'; + c = 0; + } + } + } + +} + + + +OZ_DECLARE_NONSTD(zap_status_t) zap_console_stream_raw_write(zap_stream_handle_t *handle, uint8_t *data, zap_size_t datalen) +{ + zap_size_t need = handle->data_len + datalen; + + if (need >= handle->data_size) { + void *new_data; + need += handle->alloc_chunk; + + if (!(new_data = realloc(handle->data, need))) { + return ZAP_MEMERR; + } + + handle->data = new_data; + handle->data_size = need; + } + + memcpy((uint8_t *) (handle->data) + handle->data_len, data, datalen); + handle->data_len += datalen; + handle->end = (uint8_t *) (handle->data) + handle->data_len; + *(uint8_t *)handle->end = '\0'; + + return ZAP_SUCCESS; +} + +OZ_DECLARE(int) zap_vasprintf(char **ret, const char *fmt, va_list ap) /* code from switch_apr.c */ +{ +#ifdef HAVE_VASPRINTF + return vasprintf(ret, fmt, ap); +#else + char *buf; + int len; + size_t buflen; + va_list ap2; + char *tmp = NULL; + +#ifdef _MSC_VER +#if _MSC_VER >= 1500 + /* hack for incorrect assumption in msvc header files for code analysis */ + __analysis_assume(tmp); +#endif + ap2 = ap; +#else + va_copy(ap2, ap); +#endif + + len = vsnprintf(tmp, 0, fmt, ap2); + + if (len > 0 && (buf = malloc((buflen = (size_t) (len + 1)))) != NULL) { + len = vsnprintf(buf, buflen, fmt, ap); + *ret = buf; + } else { + *ret = NULL; + len = -1; + } + + va_end(ap2); + return len; +#endif +} + +OZ_DECLARE_NONSTD(zap_status_t) zap_console_stream_write(zap_stream_handle_t *handle, const char *fmt, ...) +{ + va_list ap; + char *buf = handle->data; + char *end = handle->end; + int ret = 0; + char *data = NULL; + + if (handle->data_len >= handle->data_size) { + return ZAP_FAIL; + } + + va_start(ap, fmt); + ret = zap_vasprintf(&data, fmt, ap); + va_end(ap); + + if (data) { + zap_size_t remaining = handle->data_size - handle->data_len; + zap_size_t need = strlen(data) + 1; + + if ((remaining < need) && handle->alloc_len) { + zap_size_t new_len; + void *new_data; + + new_len = handle->data_size + need + handle->alloc_chunk; + if ((new_data = realloc(handle->data, new_len))) { + handle->data_size = handle->alloc_len = new_len; + handle->data = new_data; + buf = handle->data; + remaining = handle->data_size - handle->data_len; + handle->end = (uint8_t *) (handle->data) + handle->data_len; + end = handle->end; + } else { + zap_log(ZAP_LOG_CRIT, "Memory Error!\n"); + free(data); + return ZAP_FAIL; + } + } + + if (remaining < need) { + ret = -1; + } else { + ret = 0; + snprintf(end, remaining, "%s", data); + handle->data_len = strlen(buf); + handle->end = (uint8_t *) (handle->data) + handle->data_len; + } + free(data); + } + + return ret ? ZAP_FAIL : ZAP_SUCCESS; +} + +static void *zap_cpu_monitor_run(zap_thread_t *me, void *obj) +{ +#ifndef WIN32 + cpu_monitor_t *monitor = (cpu_monitor_t *)obj; + struct zap_cpu_monitor_stats *cpu_stats = zap_new_cpu_monitor(); + if (!cpu_stats) { + return NULL; + } + monitor->running = 1; + + while(zap_running()) { + double time; + if (zap_cpu_get_system_idle_time(cpu_stats, &time)) { + break; + } + + if (monitor->alarm) { + if ((int)time >= (100-monitor->set_alarm_threshold)) { + zap_log(ZAP_LOG_DEBUG, "CPU alarm OFF (idle:%d)\n", (int) time); + monitor->alarm = 0; + } + if (monitor->alarm_action_flags & ZAP_CPU_ALARM_ACTION_WARN) { + zap_log(ZAP_LOG_WARNING, "CPU alarm is ON (cpu usage:%d)\n", (int) (100-time)); + } + } else { + if ((int)time <= (100-monitor->reset_alarm_threshold)) { + zap_log(ZAP_LOG_DEBUG, "CPU alarm ON (idle:%d)\n", (int) time); + monitor->alarm = 1; + } + } + zap_interrupt_wait(monitor->interrupt, monitor->interval); + } + zap_delete_cpu_monitor(cpu_stats); + monitor->running = 0; +#else + UNREFERENCED_PARAMETER(me); + UNREFERENCED_PARAMETER(obj); +#endif + + return NULL; +} + + +static zap_status_t zap_cpu_monitor_start(void) +{ + if (zap_interrupt_create(&globals.cpu_monitor.interrupt, ZAP_INVALID_SOCKET) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_CRIT, "Failed to create CPU monitor interrupt\n"); + return ZAP_FAIL; + } + + if (zap_thread_create_detached(zap_cpu_monitor_run, &globals.cpu_monitor) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_CRIT, "Failed to create cpu monitor thread!!\n"); + return ZAP_FAIL; + } + return ZAP_SUCCESS; +} + +static void zap_cpu_monitor_stop(void) +{ + if (!globals.cpu_monitor.interrupt) { + return; + } + + if (!globals.cpu_monitor.running) { + return; + } + + if (zap_interrupt_signal(globals.cpu_monitor.interrupt) != ZAP_SUCCESS) { + zap_log(ZAP_LOG_CRIT, "Failed to stop CPU monitor\n"); + return; + } + + while(globals.cpu_monitor.running) { + zap_sleep(10); + } + + zap_interrupt_destroy(&globals.cpu_monitor.interrupt); +} + +OZ_DECLARE(void) zap_cpu_monitor_disable(void) +{ + zap_cpu_monitor_disabled = 1; +} + + +/* 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: + */ diff --git a/libs/openzap/src/zap_m3ua.c b/libs/openzap/src/zap_m3ua.c new file mode 100644 index 0000000000..50736b75e4 --- /dev/null +++ b/libs/openzap/src/zap_m3ua.c @@ -0,0 +1,692 @@ +/* + * zap_m3ua.c + * openzap + * + * Created by Shane Burrell on 4/3/08. + * Copyright 2008 Shane Burrell. All rights reserved. + * + * + * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic * + * + * 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 "openzap.h" +#include "m3ua_client.h" +#include "zap_m3ua.h" + +#define MAX_REQ_ID MAX_PENDING_CALLS +typedef uint16_t m3ua_request_id_t; + +typedef enum { + BST_FREE, + BST_WAITING, + BST_READY, + BST_FAIL +} m3ua_request_status_t; + +typedef struct { + m3ua_request_status_t status; + m3uac_event_t event; + zap_span_t *span; + zap_channel_t *zchan; +} m3ua_request_t; + + +struct general_config { + uint32_t region; +}; +typedef struct general_config general_config_t; + + +struct m3ua_channel_profile { + char name[80]; + int cust_span; + unsigned char opc[3]; + unsigned char dpc[3]; + int local_ip[4]; + int local_port; + int remote_ip[4]; + int remote_port; + int m3ua_mode; +}; +typedef struct m3ua_channel_profile m3ua_channel_profile_t; + +static struct { + zap_hash_t *profile_hash; + general_config_t general_config; +} globals; + +struct m3ua_span_data { + uint32_t boardno; + uint32_t flags; +}; +typedef struct m3ua_span_data m3ua_span_data_t; + +struct m3ua_chan_data { + zap_buffer_t *digit_buffer; + zap_mutex_t *digit_mutex; + zap_size_t dtmf_len; + uint32_t flags; + uint32_t hdlc_bytes; +}; +typedef struct m3ua_chan_data m3ua_chan_data_t; + +static zap_mutex_t *request_mutex = NULL; +static zap_mutex_t *signal_mutex = NULL; + +static uint8_t req_map[MAX_REQ_ID+1] = { 0 }; + +static void release_request_id(m3ua_request_id_t r) +{ + zap_mutex_lock(request_mutex); + req_map[r] = 0; + zap_mutex_unlock(request_mutex); +} + +/*static m3ua_request_id_t next_request_id(void) +{ + m3ua_request_id_t r = 0; + int ok = 0; + + while(!ok) { + zap_mutex_lock(request_mutex); + for (r = 1; r <= MAX_REQ_ID; r++) { + if (!req_map[r]) { + ok = 1; + req_map[r] = 1; + break; + } + } + zap_mutex_unlock(request_mutex); + if (!ok) { + zap_sleep(5); + } + } + return r; +} +*/ + +static __inline__ void state_advance(zap_channel_t *zchan) +{ + + m3ua_data_t *m3ua_data = zchan->span->signal_data; + m3uac_connection_t *mcon = &m3ua_data->mcon; + zap_sigmsg_t sig; + zap_status_t status; + + zap_log(ZAP_LOG_DEBUG, "%d:%d STATE [%s]\n", zchan->span_id, zchan->chan_id, zap_channel_state2str(zchan->state)); + + memset(&sig, 0, sizeof(sig)); + sig.chan_id = zchan->chan_id; + sig.span_id = zchan->span_id; + sig.channel = zchan; + + switch (zchan->state) { + case ZAP_CHANNEL_STATE_DOWN: + { + if (zchan->extra_id) { + release_request_id((m3ua_request_id_t)zchan->extra_id); + zchan->extra_id = 0; + } + zap_channel_done(zchan); + } + break; + case ZAP_CHANNEL_STATE_PROGRESS_MEDIA: + case ZAP_CHANNEL_STATE_PROGRESS: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_PROGRESS_MEDIA; + if ((status = m3ua_data->signal_cb(&sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + m3uac_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + } + break; + case ZAP_CHANNEL_STATE_RING: + { + if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_START; + if ((status = m3ua_data->signal_cb(&sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } + + } + break; + case ZAP_CHANNEL_STATE_RESTART: + { + if (zchan->last_state != ZAP_CHANNEL_STATE_HANGUP && zchan->last_state != ZAP_CHANNEL_STATE_DOWN) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } else { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + } + break; + case ZAP_CHANNEL_STATE_UP: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) { + sig.event_id = ZAP_SIGEVENT_UP; + if ((status = m3ua_data->signal_cb(&sig) != ZAP_SUCCESS)) { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP); + } + } else { + if (!(zap_test_flag(zchan, ZAP_CHANNEL_PROGRESS) || zap_test_flag(zchan, ZAP_CHANNEL_MEDIA))) { + m3uac_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + m3uac_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_ANSWERED, + 0); + } + } + break; + case ZAP_CHANNEL_STATE_DIALING: + { + } + break; + case ZAP_CHANNEL_STATE_HANGUP_COMPLETE: + { + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + } + break; + case ZAP_CHANNEL_STATE_HANGUP: + { + if (zap_test_flag(zchan, ZAP_CHANNEL_ANSWERED) || zap_test_flag(zchan, ZAP_CHANNEL_PROGRESS) || zap_test_flag(zchan, ZAP_CHANNEL_MEDIA)) { + m3uac_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_STOPPED, + zchan->caller_data.hangup_cause); + } else { + m3uac_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_NACK, + zchan->caller_data.hangup_cause); + } + } + break; + case ZAP_CHANNEL_STATE_CANCEL: + { + sig.event_id = ZAP_SIGEVENT_STOP; + status = m3ua_data->signal_cb(&sig); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + m3uac_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0); + } + break; + case ZAP_CHANNEL_STATE_TERMINATING: + { + sig.event_id = ZAP_SIGEVENT_STOP; + status = m3ua_data->signal_cb(&sig); + zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN); + m3uac_exec_command(mcon, + zchan->physical_span_id-1, + zchan->physical_chan_id-1, + 0, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0); + } + break; + default: + break; + } +} + + +static __inline__ void check_state(zap_span_t *span) +{ + if (zap_test_flag(span, ZAP_SPAN_STATE_CHANGE)) { + uint32_t j; + zap_clear_flag_locked(span, ZAP_SPAN_STATE_CHANGE); + for(j = 1; j <= span->chan_count; j++) { + if (zap_test_flag((&span->channels[j]), ZAP_CHANNEL_STATE_CHANGE)) { + zap_clear_flag_locked((&span->channels[j]), ZAP_CHANNEL_STATE_CHANGE); + state_advance(&span->channels[j]); + zap_channel_complete_state(&span->channels[j]); + } + } + } +} + + +static int parse_ss7_event(zap_span_t *span, m3uac_connection_t *mcon, m3uac_event_t *event) +{ + zap_mutex_lock(signal_mutex); + + if (!zap_running()) { + zap_log(ZAP_LOG_WARNING, "System is shutting down.\n"); + goto end; + } + + + if (zap_test_flag(span, ZAP_SPAN_SUSPENDED) && + event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && event->event_id != SIGBOOST_EVENT_HEARTBEAT) { + + zap_log(ZAP_LOG_WARNING, + "INVALID EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", + m3uac_event_id_name(event->event_id), + event->event_id, + event->span+1, + event->chan+1, + event->release_cause, + event->call_setup_id, + event->fseqno, + (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), + (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") + ); + + goto end; + } + + + zap_log(ZAP_LOG_DEBUG, + "RX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", + m3uac_event_id_name(event->event_id), + event->event_id, + event->span+1, + event->chan+1, + event->release_cause, + event->call_setup_id, + event->fseqno, + (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), + (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") + ); + + + + switch(event->event_id) { + + case SIGBOOST_EVENT_CALL_START: + //handle_call_start(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + //handle_call_stop(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + //handle_call_start_ack(mcon, event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + //handle_call_start_nack(span, mcon, event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + //handle_call_answer(span, mcon, event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + //handle_heartbeat(mcon, event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + //handle_call_done(span, mcon, event); + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + //handle_call_loop_start(event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + //handle_call_stop(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + //handle_restart_ack(mcon, span, event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + //handle_gap_abate(event); + break; + default: + zap_log(ZAP_LOG_WARNING, "No handler implemented for [%s]\n", m3uac_event_id_name(event->event_id)); + break; + } + + end: + + zap_mutex_unlock(signal_mutex); + + return 0; +} + +static ZIO_CONFIGURE_FUNCTION(m3ua_configure) +{ + m3ua_channel_profile_t *profile = NULL; + + int ok = 1; + + if (!(profile = (m3ua_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) { + profile = malloc(sizeof(*profile)); + memset(profile, 0, sizeof(*profile)); + zap_set_string(profile->name, category); + hashtable_insert(globals.profile_hash, (void *)profile->name, profile); + zap_log(ZAP_LOG_INFO, "creating profile [%s]\n", category); + } + +// zap_set_string(m3ua_data->mcon. cfg.local_ip, local_ip); + if (!strcasecmp(var, "local_sctp_port")) { + profile->local_port = 30000 ; + profile->remote_port = 30000; + profile->cust_span++; + } + ok = 1; + + + if (ok) { + zap_log(ZAP_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category); + } else { + zap_log(ZAP_LOG_ERROR, "unknown param [%s]\n", var); + } + + return ZAP_SUCCESS; +} + +static ZIO_CONFIGURE_SPAN_FUNCTION(m3ua_configure_span) +{ + + return ZAP_FAIL; +} + +static ZIO_OPEN_FUNCTION(m3ua_open) +{ + + return ZAP_FAIL; +} + +static ZIO_CLOSE_FUNCTION(m3ua_close) +{ + + return ZAP_FAIL; +} + +/*static ZIO_SET_INTERVAL_FUNCTION(m3ua_set_interval) +{ + + return 0; +}*/ + +static ZIO_WAIT_FUNCTION(m3ua_wait) +{ + + return ZAP_FAIL; +} + +static ZIO_READ_FUNCTION(m3ua_read) +{ + + return ZAP_FAIL; +} + +static ZIO_WRITE_FUNCTION(m3ua_write) +{ + + return ZAP_FAIL; +} + +static ZIO_COMMAND_FUNCTION(m3ua_command) +{ + return ZAP_FAIL; +} + +static ZIO_SPAN_POLL_EVENT_FUNCTION(m3ua_poll_event) +{ + return ZAP_FAIL; +} + +static ZIO_SPAN_NEXT_EVENT_FUNCTION(m3ua_next_event) +{ + return ZAP_FAIL; +} + + +static ZIO_SPAN_DESTROY_FUNCTION(m3ua_span_destroy) +{ + m3ua_span_data_t *span_data = (m3ua_span_data_t *) span->mod_data; + + if (span_data) { + free(span_data); + } + + return ZAP_SUCCESS; +} +static ZIO_CHANNEL_DESTROY_FUNCTION(m3ua_channel_destroy) +{ + m3ua_chan_data_t *chan_data = (m3ua_chan_data_t *) zchan->mod_data; + m3ua_span_data_t *span_data = (m3ua_span_data_t *) zchan->span->mod_data; + + if (!chan_data) { + return ZAP_FAIL; + } + + + + + + + zap_mutex_destroy(&chan_data->digit_mutex); + zap_buffer_destroy(&chan_data->digit_buffer); + + + zap_safe_free(chan_data); + + if (span_data) { + free(span_data); + } + + + return ZAP_SUCCESS; +} + + + +static ZIO_GET_ALARMS_FUNCTION(m3ua_get_alarms) +{ + return ZAP_FAIL; +} + +static zap_io_interface_t m3ua_interface; + +zap_status_t m3ua_init(zap_io_interface_t **zint) +{ + assert(zint != NULL); + memset(&m3ua_interface, 0, sizeof(m3ua_interface)); + + m3ua_interface.name = "m3ua"; + m3ua_interface.configure = m3ua_configure; + m3ua_interface.configure_span = m3ua_configure_span; + m3ua_interface.open = m3ua_open; + m3ua_interface.close = m3ua_close; + m3ua_interface.wait = m3ua_wait; + m3ua_interface.read = m3ua_read; + m3ua_interface.write = m3ua_write; + m3ua_interface.command = m3ua_command; + m3ua_interface.poll_event = m3ua_poll_event; + m3ua_interface.next_event = m3ua_next_event; + m3ua_interface.channel_destroy = m3ua_channel_destroy; + m3ua_interface.span_destroy = m3ua_span_destroy; + m3ua_interface.get_alarms = m3ua_get_alarms; + *zint = &m3ua_interface; + + return ZAP_FAIL; +} + +zap_status_t m3ua_destroy(void) +{ + return ZAP_FAIL; +} + + +static void *m3ua_run(zap_thread_t *me, void *obj) +{ + zap_span_t *span = (zap_span_t *) obj; + m3ua_data_t *m3ua_data = span->signal_data; + m3uac_connection_t *mcon, *pcon; + uint32_t ms = 10, too_long = 60000; + + + m3ua_data->pcon = m3ua_data->mcon; + + if (m3uac_connection_open(&m3ua_data->mcon, + m3ua_data->mcon.cfg.local_ip, + m3ua_data->mcon.cfg.local_port, + m3ua_data->mcon.cfg.remote_ip, + m3ua_data->mcon.cfg.remote_port) < 0) { + zap_log(ZAP_LOG_DEBUG, "Error: Opening MCON Socket [%d] %s\n", m3ua_data->mcon.socket, strerror(errno)); + goto end; + } + + if (m3uac_connection_open(&m3ua_data->pcon, + m3ua_data->pcon.cfg.local_ip, + ++m3ua_data->pcon.cfg.local_port, + m3ua_data->pcon.cfg.remote_ip, + m3ua_data->pcon.cfg.remote_port) < 0) { + zap_log(ZAP_LOG_DEBUG, "Error: Opening PCON Socket [%d] %s\n", m3ua_data->pcon.socket, strerror(errno)); + goto end; + } + + mcon = &m3ua_data->mcon; + pcon = &m3ua_data->pcon; + + top: + + //init_outgoing_array(); + + m3uac_exec_command(mcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + + while (zap_test_flag(m3ua_data, ZAP_M3UA_RUNNING)) { + fd_set rfds, efds; + struct timeval tv = { 0, ms * 1000 }; + int max, activity, i = 0; + m3uac_event_t *event = NULL; + + if (!zap_running()) { + m3uac_exec_command(mcon, + 0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + break; + } + + FD_ZERO(&rfds); + FD_ZERO(&efds); + FD_SET(mcon->socket, &rfds); + FD_SET(mcon->socket, &efds); + FD_SET(pcon->socket, &rfds); + FD_SET(pcon->socket, &efds); + + max = ((pcon->socket > mcon->socket) ? pcon->socket : mcon->socket) + 1; + + if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) { + goto error; + } + + if (activity) { + if (FD_ISSET(pcon->socket, &efds) || FD_ISSET(mcon->socket, &efds)) { + goto error; + } + + if (FD_ISSET(pcon->socket, &rfds)) { + if ((event = m3uac_connection_readp(pcon, i))) { + parse_ss7_event(span, mcon, event); + } else goto top; + } + + if (FD_ISSET(mcon->socket, &rfds)) { + if ((event = m3uac_connection_read(mcon, i))) { + parse_ss7_event(span, mcon, event); + } else goto top; + } + } + + check_state(span); + mcon->hb_elapsed += ms; + + if (mcon->hb_elapsed >= too_long && (mcon->up || !zap_test_flag(span, ZAP_SPAN_SUSPENDED))) { + zap_set_state_all(span, ZAP_CHANNEL_STATE_RESTART); + zap_set_flag_locked(span, ZAP_SPAN_SUSPENDED); + mcon->up = 0; + zap_log(ZAP_LOG_CRIT, "Lost Heartbeat!\n"); + } + + } + + goto end; + + error: + zap_log(ZAP_LOG_CRIT, "Socket Error!\n"); + + end: + + m3uac_connection_close(&m3ua_data->mcon); + m3uac_connection_close(&m3ua_data->pcon); + + zap_clear_flag(m3ua_data, ZAP_M3UA_RUNNING); + + zap_log(ZAP_LOG_DEBUG, "M3UA thread ended.\n"); + return NULL; +} +zap_status_t m3ua_start(zap_span_t *span) +{ + m3ua_data_t *m3ua_data = span->signal_data; + zap_set_flag(m3ua_data, ZAP_M3UA_RUNNING); + return zap_thread_create_detached(m3ua_run, span); +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: +*/ diff --git a/libs/openzap/src/zap_threadmutex.c b/libs/openzap/src/zap_threadmutex.c new file mode 100644 index 0000000000..4c2f8ac312 --- /dev/null +++ b/libs/openzap/src/zap_threadmutex.c @@ -0,0 +1,454 @@ +/* + * Cross Platform Thread/Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Soozware, and permit persons to whom the Soozware is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + * Contributors: + * + * Moises Silva + * + */ + +#ifdef WIN32 +/* required for TryEnterCriticalSection definition. Must be defined before windows.h include */ +#define _WIN32_WINNT 0x0400 +#endif + +#include "openzap.h" +#include "zap_threadmutex.h" + +#ifdef WIN32 +#include + +#define ZAP_THREAD_CALLING_CONVENTION __stdcall + +struct zap_mutex { + CRITICAL_SECTION mutex; +}; + +#else +#include +#include + +#define ZAP_THREAD_CALLING_CONVENTION + +struct zap_mutex { + pthread_mutex_t mutex; +}; + +#endif + +struct zap_interrupt { + zap_socket_t device; +#ifdef WIN32 + /* for generic interruption */ + HANDLE event; +#else + /* for generic interruption */ + int readfd; + int writefd; +#endif +}; + +struct zap_thread { +#ifdef WIN32 + void *handle; +#else + pthread_t handle; +#endif + void *private_data; + zap_thread_function_t function; + zap_size_t stack_size; +#ifndef WIN32 + pthread_attr_t attribute; +#endif +}; + +zap_size_t thread_default_stacksize = 0; + +OZ_DECLARE(void) zap_thread_override_default_stacksize(zap_size_t size) +{ + thread_default_stacksize = size; +} + +static void * ZAP_THREAD_CALLING_CONVENTION thread_launch(void *args) +{ + void *exit_val; + zap_thread_t *thread = (zap_thread_t *)args; + exit_val = thread->function(thread, thread->private_data); +#ifndef WIN32 + pthread_attr_destroy(&thread->attribute); +#endif + zap_safe_free(thread); + + return exit_val; +} + +OZ_DECLARE(zap_status_t) zap_thread_create_detached(zap_thread_function_t func, void *data) +{ + return zap_thread_create_detached_ex(func, data, thread_default_stacksize); +} + +OZ_DECLARE(zap_status_t) zap_thread_create_detached_ex(zap_thread_function_t func, void *data, zap_size_t stack_size) +{ + zap_thread_t *thread = NULL; + zap_status_t status = ZAP_FAIL; + + if (!func || !(thread = (zap_thread_t *)malloc(sizeof(zap_thread_t)))) { + goto done; + } + + thread->private_data = data; + thread->function = func; + thread->stack_size = stack_size; + +#if defined(WIN32) + thread->handle = (void *)_beginthreadex(NULL, (unsigned)thread->stack_size, (unsigned int (__stdcall *)(void *))thread_launch, thread, 0, NULL); + if (!thread->handle) { + goto fail; + } + CloseHandle(thread->handle); + + status = ZAP_SUCCESS; + goto done; +#else + + if (pthread_attr_init(&thread->attribute) != 0) goto fail; + + if (pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0) goto failpthread; + + if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0) goto failpthread; + + if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) goto failpthread; + + status = ZAP_SUCCESS; + goto done; + failpthread: + pthread_attr_destroy(&thread->attribute); +#endif + + fail: + if (thread) { + zap_safe_free(thread); + } + done: + return status; +} + + +OZ_DECLARE(zap_status_t) zap_mutex_create(zap_mutex_t **mutex) +{ + zap_status_t status = ZAP_FAIL; +#ifndef WIN32 + pthread_mutexattr_t attr; +#endif + zap_mutex_t *check = NULL; + + check = (zap_mutex_t *)malloc(sizeof(**mutex)); + if (!check) + goto done; +#ifdef WIN32 + InitializeCriticalSection(&check->mutex); +#else + if (pthread_mutexattr_init(&attr)) + goto done; + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) + goto fail; + + if (pthread_mutex_init(&check->mutex, &attr)) + goto fail; + + goto success; + + fail: + pthread_mutexattr_destroy(&attr); + goto done; + + success: +#endif + *mutex = check; + status = ZAP_SUCCESS; + + done: + return status; +} + +OZ_DECLARE(zap_status_t) zap_mutex_destroy(zap_mutex_t **mutex) +{ + zap_mutex_t *mp = *mutex; + *mutex = NULL; + if (!mp) { + return ZAP_FAIL; + } +#ifdef WIN32 + DeleteCriticalSection(&mp->mutex); +#else + if (pthread_mutex_destroy(&mp->mutex)) + return ZAP_FAIL; +#endif + zap_safe_free(mp); + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) _zap_mutex_lock(zap_mutex_t *mutex) +{ +#ifdef WIN32 + EnterCriticalSection(&mutex->mutex); +#else + int err; + if ((err = pthread_mutex_lock(&mutex->mutex))) { + zap_log(ZAP_LOG_ERROR, "Failed to lock mutex %d:%s\n", err, strerror(err)); + return ZAP_FAIL; + } +#endif + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) _zap_mutex_trylock(zap_mutex_t *mutex) +{ +#ifdef WIN32 + if (!TryEnterCriticalSection(&mutex->mutex)) + return ZAP_FAIL; +#else + if (pthread_mutex_trylock(&mutex->mutex)) + return ZAP_FAIL; +#endif + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) _zap_mutex_unlock(zap_mutex_t *mutex) +{ +#ifdef WIN32 + LeaveCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_unlock(&mutex->mutex)) + return ZAP_FAIL; +#endif + return ZAP_SUCCESS; +} + + +OZ_DECLARE(zap_status_t) zap_interrupt_create(zap_interrupt_t **ininterrupt, zap_socket_t device) +{ + zap_interrupt_t *interrupt = NULL; +#ifndef WIN32 + int fds[2]; +#endif + + interrupt = calloc(1, sizeof(*interrupt)); + if (!interrupt) { + zap_log(ZAP_LOG_ERROR, "Failed to allocate interrupt memory\n"); + return ZAP_FAIL; + } + + interrupt->device = device; +#ifdef WIN32 + interrupt->event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!interrupt->event) { + zap_log(ZAP_LOG_ERROR, "Failed to allocate interrupt event\n"); + goto failed; + } +#else + if (pipe(fds)) { + zap_log(ZAP_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno)); + goto failed; + } + + interrupt->readfd = fds[0]; + interrupt->writefd = fds[1]; +#endif + + *ininterrupt = interrupt; + return ZAP_SUCCESS; + +failed: + if (interrupt) { +#ifndef WIN32 + if (interrupt->readfd) { + close(interrupt->readfd); + close(interrupt->writefd); + interrupt->readfd = -1; + interrupt->writefd = -1; + } +#endif + zap_safe_free(interrupt); + } + return ZAP_FAIL; +} + +#define ONE_BILLION 1000000000 + +OZ_DECLARE(zap_status_t) zap_interrupt_wait(zap_interrupt_t *interrupt, int ms) +{ + int num = 1; +#ifdef WIN32 + DWORD res = 0; + HANDLE ints[2]; +#else + int res = 0; + struct pollfd ints[2]; + char pipebuf[255]; +#endif + + /* start implementation */ +#ifdef WIN32 + ints[0] = interrupt->event; + if (interrupt->device != ZAP_INVALID_SOCKET) { + num++; + ints[1] = interrupt->device; + } + res = WaitForMultipleObjects(num, ints, FALSE, ms >= 0 ? ms : INFINITE); + switch (res) { + case WAIT_TIMEOUT: + return ZAP_TIMEOUT; + case WAIT_FAILED: + case WAIT_ABANDONED: /* is it right to fail with abandoned? */ + return ZAP_FAIL; + default: + if (res >= (sizeof(ints)/sizeof(ints[0]))) { + zap_log(ZAP_LOG_ERROR, "Error waiting for openzap interrupt event (WaitForSingleObject returned %d)\n", res); + return ZAP_FAIL; + } + return ZAP_SUCCESS; + } +#else + ints[0].fd = interrupt->readfd; + ints[0].events = POLLIN; + ints[0].revents = 0; + + if (interrupt->device != ZAP_INVALID_SOCKET) { + num++; + ints[1].fd = interrupt->device; + ints[1].events = POLLIN; + ints[1].revents = 0; + } + + res = poll(ints, num, ms); + if (res == -1) { + zap_log(ZAP_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno)); + return ZAP_FAIL; + } + + if (res == 0) { + return ZAP_TIMEOUT; + } + + if (ints[0].revents & POLLIN) { + res = read(ints[0].fd, pipebuf, sizeof(pipebuf)); + if (res == -1) { + zap_log(ZAP_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno)); + } + } + + return ZAP_SUCCESS; +#endif +} + +OZ_DECLARE(zap_status_t) zap_interrupt_signal(zap_interrupt_t *interrupt) +{ +#ifdef WIN32 + if (!SetEvent(interrupt->event)) { + zap_log(ZAP_LOG_ERROR, "Failed to signal interrupt\n"); + return ZAP_FAIL; + } +#else + int err; + if ((err = write(interrupt->writefd, "w", 1)) != 1) { + zap_log(ZAP_LOG_ERROR, "Failed to signal interrupt: %s\n", errno, strerror(errno)); + return ZAP_FAIL; + } +#endif + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_interrupt_destroy(zap_interrupt_t **ininterrupt) +{ + zap_interrupt_t *interrupt = NULL; + interrupt = *ininterrupt; +#ifdef WIN32 + CloseHandle(interrupt->event); +#else + close(interrupt->readfd); + close(interrupt->writefd); + interrupt->readfd = -1; + interrupt->writefd = -1; +#endif + zap_safe_free(interrupt); + *ininterrupt = NULL; + return ZAP_SUCCESS; +} + +OZ_DECLARE(zap_status_t) zap_interrupt_multiple_wait(zap_interrupt_t *interrupts[], zap_size_t size, int ms) +{ +#ifndef WIN32 + int i; + int res = 0; + int numdevices = 0; + char pipebuf[255]; + struct pollfd ints[size*2]; + + memset(&ints, 0, sizeof(ints)); + + for (i = 0; i < size; i++) { + ints[i].events = POLLIN; + ints[i].revents = 0; + ints[i].fd = interrupts[i]->readfd; + if (interrupts[i]->device != ZAP_INVALID_SOCKET) { + ints[i+numdevices].events = POLLIN; + ints[i+numdevices].revents = 0; + ints[i+numdevices].fd = interrupts[i]->device; + numdevices++; + } + } + + res = poll(ints, size + numdevices, ms); + + if (res == -1) { + zap_log(ZAP_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno)); + return ZAP_FAIL; + } + + if (res == 0) { + return ZAP_TIMEOUT; + } + + for (i = size; i < zap_array_len(ints); i++) { + if (ints[i].revents & POLLIN) { + res = read(ints[0].fd, pipebuf, sizeof(pipebuf)); + if (res == -1) { + zap_log(ZAP_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno)); + } + } + } + +#elif defined(__WINDOWS__) + UNREFERENCED_PARAMETER(interrupts); + UNREFERENCED_PARAMETER(size); + UNREFERENCED_PARAMETER(ms); +#endif + return ZAP_SUCCESS; +} + +/* 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: + */